951 lines · 19.2 KB · C
RawDownload
/* * Encapsulating Security Payload for IPsec for IPv4, rfc1827. *	currently only implements tunnel mode. * TODO: update to match rfc4303. */#include	"u.h"#include	"lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"error.h" #include	"ip.h"#include	"ipv6.h"#include	"libsec.h" typedef struct Esphdr Esphdr;typedef struct Esp4hdr Esp4hdr;typedef struct Esp6hdr Esp6hdr;typedef struct Esptail Esptail;typedef struct Userhdr Userhdr;typedef struct Esppriv Esppriv;typedef struct Espcb Espcb;typedef struct Algorithm Algorithm; enum{	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 */}; struct Esphdr{	uchar	espspi[4];	/* Security parameter index */	uchar	espseq[4];	/* Sequence number */}; /* * tunnel-mode layout:		IP | ESP | TCP/UDP | user data. * transport-mode layout is:	ESP | IP | TCP/UDP | user data. */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; */	uchar	espspi[4];	/* Security parameter index */	uchar	espseq[4];	/* Sequence number */}; /* tunnel-mode layout */struct Esp6hdr{	/* Ip6hdr; */	uchar	vcf[4];		/* version:4, traffic class:8, flow label:20 */	uchar	ploadlen[2];	/* payload length: packet length - 40 */	uchar	proto;		/* next header type */	uchar	ttl;		/* hop limit */	uchar	src[IPaddrlen];	uchar	dst[IPaddrlen]; 	/* Esphdr; */	uchar	espspi[4];	/* Security parameter index */	uchar	espseq[4];	/* Sequence number */}; struct Esptail{	uchar	pad;	uchar	nexthdr;}; /* header as seen by the user */struct Userhdr{	uchar	nexthdr;	/* next protocol */	uchar	unused[3];}; struct Esppriv{	ulong	in;	ulong	inerrors;}; /* *  protocol specific part of Conv */struct Espcb{	int	incoming;	int	header;		/* user user level header */	ulong	spi;	ulong	seq;		/* last seq sent */	ulong	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);}; struct Algorithm{	char 	*name;	int	keylen;		/* in bits */	void	(*init)(Espcb*, char* name, uchar *key, int 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, int keylen);static	void desespinit(Espcb *ecb, char *name, uchar *k, int n); static	void nullahinit(Espcb*, char*, uchar *key, int keylen);static	void shaahinit(Espcb*, char*, uchar *key, int keylen);static	void md5ahinit(Espcb*, char*, uchar *key, int keylen); static Algorithm espalg[] ={	"null",			0,	nullespinit,//	"des3_cbc",		192,	des3espinit,	/* rfc2451 *///	"aes_128_cbc",		128,	aescbcespinit,	/* rfc3602 *///	"aes_ctr",		128,	aesctrespinit,	/* rfc3686 */	"des_56_cbc",		64,	desespinit,	/* rfc2405, deprecated *///	"rc4_128",		128,	rc4espinit,	/* gone in rfc4305 */	nil,			0,	nil,}; static Algorithm ahalg[] ={	"null",			0,	nullahinit,	"hmac_sha1_96",		128,	shaahinit,	/* rfc2404 *///	"aes_xcbc_mac_96",	128,	aesahinit,	/* rfc3566 */	"hmac_md5_96",		128,	md5ahinit,	/* rfc2403 */	nil,			0,	nil,}; static char*espconnect(Conv *c, char **argv, int argc){	char *p, *pp;	char *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;		parseip(c->raddr, argv[1]);		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 intipvers(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 voidespkick(void *x){	Conv *c = x;	Esp4hdr *eh4;	Esp6hdr *eh6;	Esptail *et;	Userhdr *uh;	Espcb *ecb;	Block *bp;	int nexthdr, payload, pad, align, version, hdrlen, iphdrlen;	uchar *auth; 	version = ipvers(c);	iphdrlen = version == V4? IP4HDR: IP6HDR;	hdrlen =   version == V4? Esp4hdrlen: Esp6hdrlen; 	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? */	} 	payload = BLEN(bp) + ecb->espivlen; 	/* Make space to fit ip header */	bp = padblock(bp, hdrlen + ecb->espivlen); 	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; 	eh4 = (Esp4hdr *)bp->rp;	eh6 = (Esp6hdr *)bp->rp;	et = (Esptail*)(bp->rp + hdrlen + payload + pad); 	/* fill in tail */	et->pad = pad;	et->nexthdr = nexthdr; 	ecb->cipher(ecb, bp->rp + hdrlen, payload + pad + Esptaillen);	auth = bp->rp + hdrlen + payload + pad + Esptaillen; 	/* fill in head */	if (version == V4) {		eh4->vihl = IP_VER4;		hnputl(eh4->espspi, ecb->spi);		hnputl(eh4->espseq, ++ecb->seq);		v6tov4(eh4->espsrc, c->laddr);		v6tov4(eh4->espdst, c->raddr);		eh4->espproto = IP_ESPPROTO;		eh4->frag[0] = 0;		eh4->frag[1] = 0;	} else {		eh6->vcf[0] = IP_VER6;		hnputl(eh6->espspi, ecb->spi);		hnputl(eh6->espseq, ++ecb->seq);		ipmove(eh6->src, c->laddr);		ipmove(eh6->dst, c->raddr);		eh6->proto = IP_ESPPROTO;	} 	ecb->auth(ecb, bp->rp + iphdrlen, (hdrlen - iphdrlen) +		payload + pad + Esptaillen, auth); 	QUNLOCK(c);	/* print("esp: pass down: %uld\n", BLEN(bp)); */	if (version == V4)		ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c);	else		ipoput6(c->p->f, bp, 0, c->ttl, c->tos, c);} voidespiput(Proto *esp, Ipifc* _, Block *bp){	Esp4hdr *eh4;	Esp6hdr *eh6;	Esptail *et;	Userhdr *uh;	Conv *c;	Espcb *ecb;	uchar raddr[IPaddrlen], laddr[IPaddrlen];	Fs *f;	uchar *auth, *espspi;	ulong spi;	int payload, nexthdr, version, hdrlen; 	f = esp->f;	if (bp == nil || BLEN(bp) == 0) {		/* get enough to identify the IP version */		bp = pullupblock(bp, IP4HDR);		if(bp == nil) {			netlog(f, Logesp, "esp: short packet\n");			return;		}	}	eh4 = (Esp4hdr*)bp->rp;	version = ((eh4->vihl & 0xf0) == IP_VER4? V4: V6);	hdrlen = version == V4? Esp4hdrlen: Esp6hdrlen; 	bp = pullupblock(bp, hdrlen + Esptaillen);	if(bp == nil) {		netlog(f, Logesp, "esp: short packet\n");		return;	} 	if (version == V4) {		eh4 = (Esp4hdr*)bp->rp;		spi = nhgetl(eh4->espspi);		v4tov6(raddr, eh4->espsrc);		v4tov6(laddr, eh4->espdst);	} else {		eh6 = (Esp6hdr*)bp->rp;		spi = nhgetl(eh6->espspi);		ipmove(raddr, eh6->src);		ipmove(laddr, eh6->dst);	} 	QLOCK(esp);	/* Look for a conversation structure for this port */	c = convlookup(esp, spi);	if(c == nil) {		QUNLOCK(esp);		netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr,			laddr, spi);		icmpnoconv(f, bp);		freeblist(bp);		return;	} 	QLOCK(c);	QUNLOCK(esp); 	ecb = c->ptcl;	/* too hard to do decryption/authentication on block lists */	if(bp->next)		bp = concatblock(bp); 	if(BLEN(bp) < hdrlen + ecb->espivlen + Esptaillen + ecb->ahlen) {		QUNLOCK(c);		netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr,			laddr, spi);		freeb(bp);		return;	} 	auth = bp->wp - ecb->ahlen;	espspi = version == V4? ((Esp4hdr*)bp->rp)->espspi:				((Esp6hdr*)bp->rp)->espspi;	if(!ecb->auth(ecb, espspi, auth - espspi, auth)) {		QUNLOCK(c);print("esp: bad auth %I -> %I!%ld\n", raddr, laddr, spi);		netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr,			laddr, spi);		freeb(bp);		return;	} 	payload = BLEN(bp) - hdrlen - ecb->ahlen;	if(payload <= 0 || payload % 4 != 0 || payload % ecb->espblklen != 0) {		QUNLOCK(c);		netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n",			raddr, laddr, spi, payload, BLEN(bp));		freeb(bp);		return;	}	if(!ecb->cipher(ecb, bp->rp + hdrlen, payload)) {		QUNLOCK(c);print("esp: cipher failed %I -> %I!%ld: %s\n", raddr, laddr, spi, up->errstr);		netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %s\n", raddr,			laddr, spi, up->errstr);		freeb(bp);		return;	} 	payload -= Esptaillen;	et = (Esptail*)(bp->rp + hdrlen + payload);	payload -= et->pad + ecb->espivlen;	nexthdr = et->nexthdr;	if(payload <= 0) {		QUNLOCK(c);		netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n",			raddr, laddr, spi);		freeb(bp);		return;	} 	/* trim packet */	bp->rp += hdrlen + ecb->espivlen;	bp->wp = bp->rp + payload;	if(ecb->header) {		/* assume Userhdrlen < Esp4hdrlen < Esp6hdrlen */		bp->rp -= Userhdrlen;		uh = (Userhdr*)bp->rp;		memset(uh, 0, Userhdrlen);		uh->nexthdr = nexthdr;	} 	if(qfull(c->rq)){		netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr,			laddr, spi);		freeblist(bp);	}else {//		print("esp: pass up: %uld\n", BLEN(bp));		qpass(c->rq, bp);	} 	QUNLOCK(c);} char*espctl(Conv *c, char **f, int n){	Espcb *ecb = c->ptcl;	char *e = nil; 	if(strcmp(f[0], "esp") == 0)		e = setalg(ecb, f, n, espalg);	else if(strcmp(f[0], "ah") == 0)		e = setalg(ecb, f, n, ahalg);	else if(strcmp(f[0], "header") == 0)

Lines 1–500 of 951.