1232 lines · 25.2 KB · C
RawDownload
/* * Encapsulating Security Payload for IPsec for IPv4, rfc1827. * rfc2104 defines hmac computation. *	currently only implements tunnel mode. * TODO: verify aes algorithms; *	transport mode (host-to-host) */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h" #include	"ip.h"#include	"ipv6.h"#include	"libsec.h" #define BITS2BYTES(bi) (((bi) + BI2BY - 1) / BI2BY)#define BYTES2BITS(by)  ((by) * BI2BY) typedef struct Algorithm Algorithm;typedef struct Esp4hdr Esp4hdr;typedef struct Esp6hdr Esp6hdr;typedef struct Espcb Espcb;typedef struct Esphdr Esphdr;typedef struct Esppriv Esppriv;typedef struct Esptail Esptail;typedef struct Userhdr Userhdr;typedef struct Window Window; enum {	Encrypt,	Decrypt, 	IP_ESPPROTO	= 50,	/* IP v4 and v6 protocol number */	Esp4hdrlen	= IP4HDR + 8,	Esp6hdrlen	= IP6HDR + 8, 	Esptaillen	= 2,	/* does not include pad or auth data */	Userhdrlen	= 4,	/* user-visible header size - if enabled */ 	Desblk	 = BITS2BYTES(64),	Des3keysz = BITS2BYTES(192), 	Aesblk	 = BITS2BYTES(128),	Aeskeysz = BITS2BYTES(128),}; struct Esphdr{	uchar	espspi[4];	/* Security parameter index */	uchar	espseq[4];	/* Sequence number */	uchar	payload[];}; /* * tunnel-mode (network-to-network, etc.) layout is: * new IP hdrs | ESP hdr | *	 enc { orig IP hdrs | TCP/UDP hdr | user data | ESP trailer } | ESP ICV * * transport-mode (host-to-host) layout would be: *	orig IP hdrs | ESP hdr | *			enc { TCP/UDP hdr | user data | ESP trailer } | ESP ICV */struct Esp4hdr{	/* ipv4 header */	uchar	vihl;		/* Version and header length */	uchar	tos;		/* Type of service */	uchar	length[2];	/* packet length */	uchar	id[2];		/* Identification */	uchar	frag[2];	/* Fragment information */	uchar	Unused;	uchar	espproto;	/* Protocol */	uchar	espplen[2];	/* Header plus data length */	uchar	espsrc[4];	/* Ip source */	uchar	espdst[4];	/* Ip destination */ 	Esphdr;}; /* tunnel-mode layout */struct Esp6hdr{	IPV6HDR;	Esphdr;}; struct Esptail{	uchar	pad;	uchar	nexthdr;}; /* IP-version-dependent data */typedef struct Versdep Versdep;struct Versdep{	ulong	version;	ulong	iphdrlen;	ulong	hdrlen;		/* iphdrlen + esp hdr len */	ulong	spi;	ulong	seq;	uchar	laddr[IPaddrlen];	uchar	raddr[IPaddrlen];}; /* header as seen by the user */struct Userhdr{	uchar	nexthdr;	/* next protocol */	uchar	unused[3];}; /* MIB II counters; http://www.icir.org/fenner/mibs/htmllint/IPSEC-SA-MON-MIB.html */enum {	/* ipsecSaEsp ... */	InUserOctets,	InPackets,		/* just the payload */	InDecryptErrors,	InAuthErrors,	InReplayErrors,	InPolicyErrors,	InPadErrors,	InOtherRxErr, 	OutUserOctets,	OutPackets,	OutSendErrors, 	/* non-MIB? */	InSpiUnknown,	InSeqFastfwd,	InDropQpass, 	Nstats,}; static char *statnames[Nstats] = {[InUserOctets]	"InUserOctets",[InPackets]	"InPackets",[InDecryptErrors]	"InDecryptErrors",[InAuthErrors]	"InAuthErrors",[InReplayErrors]	"InReplayErrors",[InPolicyErrors]	"InPolicyErrors",[InPadErrors]	"InPadErrors",[InOtherRxErr]	"InOtherRxErr", [OutUserOctets]	"OutUserOctets",[OutPackets]	"OutPackets",[OutSendErrors]	"OutSendErrors", [InSpiUnknown]	"InSpiUnknown",[InSeqFastfwd]	"InSeqFastfwd",[InDropQpass]	"InDropQpass", }; struct Esppriv{	uvlong	stats[Nstats];}; struct Window{	uchar	w0set;		/* unknown how this is properly set */	ulong	w0;	uvlong	w;}; /* *  protocol specific part of Conv */struct Espcb{	int	incoming;	int	header;		/* user-level header */	ulong	spi;	ulong	seq;		/* last seq sent */	Window;			/* for replay attacks */ 	char	*espalg;	void	*espstate;	/* other state for esp */	int	espivlen;	/* in bytes */	int	espblklen;	int	(*cipher)(Espcb*, uchar *buf, int len); 	char	*ahalg;	void	*ahstate;	/* other state for esp */	int	ahlen;		/* auth data length in bytes */	int	ahblklen;	int	(*auth)(Espcb*, uchar *buf, int len, uchar *hash);	DigestState *ds;}; struct Algorithm{	char 	*name;	int	keylen;		/* in bits */	void	(*init)(Espcb*, char* name, uchar *key, unsigned keylen);}; static	Conv* convlookup(Proto *esp, ulong spi);static	char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg);static	void espkick(void *x); static	void nullespinit(Espcb*, char*, uchar *key, unsigned keylen);static	void des3espinit(Espcb*, char*, uchar *key, unsigned keylen);static	void aescbcespinit(Espcb*, char*, uchar *key, unsigned keylen);static	void aesctrespinit(Espcb*, char*, uchar *key, unsigned keylen);static	void desespinit(Espcb *ecb, char *name, uchar *k, unsigned n); static	void nullahinit(Espcb*, char*, uchar *key, unsigned keylen);static	void shaahinit(Espcb*, char*, uchar *key, unsigned keylen);static	void aesahinit(Espcb*, char*, uchar *key, unsigned keylen);static	void md5ahinit(Espcb*, char*, uchar *key, unsigned keylen); static Algorithm espalg[] ={	"null",		0,	nullespinit,	"des3_cbc",	192,	des3espinit,	/* new rfc2451, des-ede3 */	"aes_128_cbc",	128,	aescbcespinit,	/* new rfc3602 */	"aes_ctr",	128,	aesctrespinit,	/* new rfc3686 */	"des_56_cbc",	64,	desespinit,	/* rfc2405, deprecated */	/* rc4 was never required, was used in original bandt *///	"rc4_128",	128,	rc4espinit,	nil,		0,	nil,}; static Algorithm ahalg[] ={	"null",		0,	nullahinit,	"hmac_sha1_96",	128,	shaahinit,	/* rfc2404 */	"aes_xcbc_mac_96", 128,	aesahinit,	/* new rfc3566 */	"hmac_md5_96",	128,	md5ahinit,	/* rfc2403 */	nil,		0,	nil,}; voidespstat(Proto *esp, int stat, uint n){	Esppriv *p; 	p = esp->priv;	p->stats[stat] += n;} static intckwindow(Proto *esp, Window *w, ulong seq){	long δ; 	if(w->w0set == 0){		w->w0set = 1;		w->w0 = seq - 1;	}	δ = seq - w->w0 - 1; 	if(δ < 0 || w->w & 1ull << 63 - δ)		return -1;	if(δ > 63){		netlog(esp->f, Logesp, "long drop seq %lud %lud\n", seq, w->w0);		espstat(esp, InSeqFastfwd, 1);		w->w <<= δ - 32;		w->w0 += δ - 32;		δ = 32;	}	w->w |= 1ull<<63 - δ;	while(w->w & 1ull<<63 || δ >= 32 && δ <= 63){		w->w0++;		w->w <<= 1;		δ--;	}	return 0;} static char*espconnect(Conv *c, char **argv, int argc){	char *p, *pp, *e = nil;	ulong spi;	Espcb *ecb = (Espcb*)c->ptcl; 	switch(argc) {	default:		e = "bad args to connect";		break;	case 2:		p = strchr(argv[1], '!');		if(p == nil){			e = "malformed address";			break;		}		*p++ = 0;		if (parseip(c->raddr, argv[1]) == -1) {			e = Ebadip;			break;		}		findlocalip(c->p->f, c->laddr, c->raddr);		ecb->incoming = 0;		ecb->seq = 0;		if(strcmp(p, "*") == 0) {			qlock(c->p);			for(;;) {				spi = nrand(1<<16) + 256;				if(convlookup(c->p, spi) == nil)					break;			}			qunlock(c->p);			ecb->spi = spi;			ecb->incoming = 1;			qhangup(c->wq, nil);		} else {			spi = strtoul(p, &pp, 10);			if(pp == p) {				e = "malformed address";				break;			}			ecb->spi = spi;			qhangup(c->rq, nil);		}		nullespinit(ecb, "null", nil, 0);		nullahinit(ecb, "null", nil, 0);	}	Fsconnected(c, e); 	return e;}  static intespstate(Conv *c, char *state, int n){	return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n");} static voidespcreate(Conv *c){	c->rq = qopen(64*1024, Qmsg, 0, 0);	c->wq = qopen(64*1024, Qkick, espkick, c);} static voidespclose(Conv *c){	Espcb *ecb; 	qclose(c->rq);	qclose(c->wq);	qclose(c->eq);	ipmove(c->laddr, IPnoaddr);	ipmove(c->raddr, IPnoaddr); 	ecb = (Espcb*)c->ptcl;	free(ecb->espstate);	free(ecb->ahstate);	memset(ecb, 0, sizeof(Espcb));} static intconvipvers(Conv *c){	if((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&	    memcmp(c->laddr, v4prefix, IPv4off) == 0) ||	    ipcmp(c->raddr, IPnoaddr) == 0)		return V4;	else		return V6;} static intpktipvers(Fs *f, Block **bpp){	if (*bpp == nil || BLEN(*bpp) == 0) {		/* get enough to identify the IP version */		*bpp = pullupblock(*bpp, IP4HDR);		if(*bpp == nil) {			netlog(f, Logesp, "esp: short packet\n");			return 0;		}	}	return (((Esp4hdr*)(*bpp)->rp)->vihl & 0xf0) == IP_VER4? V4: V6;} static voidgetverslens(int version, Versdep *vp){	vp->version = version;	switch(vp->version) {	case V4:		vp->iphdrlen = IP4HDR;		vp->hdrlen   = Esp4hdrlen;		break;	case V6:		vp->iphdrlen = IP6HDR;		vp->hdrlen   = Esp6hdrlen;		break;	default:		panic("esp: getverslens version %d wrong", version);	}} static voidgetpktspiaddrs(uchar *pkt, Versdep *vp){	Esp4hdr *eh4;	Esp6hdr *eh6; 	switch(vp->version) {	case V4:		eh4 = (Esp4hdr*)pkt;		v4tov6(vp->raddr, eh4->espsrc);		v4tov6(vp->laddr, eh4->espdst);		vp->spi = nhgetl(eh4->espspi);		vp->seq = nhgetl(eh4->espseq);		break;	case V6:		eh6 = (Esp6hdr*)pkt;		ipmove(vp->raddr, eh6->src);		ipmove(vp->laddr, eh6->dst);		vp->spi = nhgetl(eh6->espspi);		vp->seq = nhgetl(eh6->espseq);		break;	default:		panic("esp: getpktspiaddrs vp->version %ld wrong", vp->version);	}} /* * encapsulate next IP packet on x's write queue in IP/ESP packet * and initiate output of the result. */static voidespkick(void *x){	int nexthdr, payload, pad, align, ulen;	uchar *auth;	Block *bp;	Conv *c = x;	Esp4hdr *eh4;	Esp6hdr *eh6;	Espcb *ecb;	Esptail *et;	Userhdr *uh;	Versdep vers; 	getverslens(convipvers(c), &vers);	bp = qget(c->wq);	if(bp == nil)		return; 	qlock(c);	ecb = c->ptcl; 	if(ecb->header) {		/* make sure the message has a User header */		bp = pullupblock(bp, Userhdrlen);		if(bp == nil) {			qunlock(c);			return;		}		uh = (Userhdr*)bp->rp;		nexthdr = uh->nexthdr;		bp->rp += Userhdrlen;	} else {		nexthdr = 0;	/* what should this be? */	} 	ulen = BLEN(bp);	payload = ulen + ecb->espivlen; 	/* Make space to fit ip header */	bp = padblock(bp, vers.hdrlen + ecb->espivlen);	getpktspiaddrs(bp->rp, &vers); 	align = 4;	if(ecb->espblklen > align)		align = ecb->espblklen;	if(align % ecb->ahblklen != 0)		panic("espkick: ahblklen is important after all");	pad = (align-1) - (payload + Esptaillen-1)%align; 	/*	 * Make space for tail	 * this is done by calling padblock with a negative size	 * Padblock does not change bp->wp!	 */	bp = padblock(bp, -(pad+Esptaillen+ecb->ahlen));	bp->wp += pad+Esptaillen+ecb->ahlen; 	et = (Esptail*)(bp->rp + vers.hdrlen + payload + pad); 	/* fill in tail */	et->pad = pad;	et->nexthdr = nexthdr; 	/* encrypt the payload */	ecb->cipher(ecb, bp->rp + vers.hdrlen, payload + pad + Esptaillen);

Lines 1–500 of 1232.