#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "ip.h" #include "libsec.h" typedef struct Esphdr Esphdr;typedef struct Esptail Esptail;typedef struct Userhdr Userhdr;typedef struct Esppriv Esppriv;typedef struct Espcb Espcb;typedef struct Algorithm Algorithm;typedef struct Esprc4 Esprc4; #define DPRINT if(0)print enum{ IP_ESPPROTO = 50, EsphdrSize = 28, // includes IP header IphdrSize = 20, // options have been striped EsptailSize = 2, // does not include pad or auth data UserhdrSize = 4, // user visable header size - if enabled}; struct Esphdr{ /* ip 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 */ /* esp header */ 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);}; enum { RC4forward = 10*1024*1024, // maximum skip forward RC4back = 100*1024, // maximum look back}; struct Esprc4{ ulong cseq; // current byte sequence number RC4state current; int ovalid; // old is valid ulong lgseq; // last good sequence ulong oseq; // old byte sequence number RC4state old;}; static Conv* convlookup(Proto *esp, ulong spi);static char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg);static void nullespinit(Espcb*, char*, uchar *key, int keylen);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 void desespinit(Espcb *ecb, char *name, uchar *k, int n);static void rc4espinit(Espcb *ecb, char *name, uchar *k, int n);static void espkick(void *x); static Algorithm espalg[] ={ "null", 0, nullespinit, "des_56_cbc", 64, desespinit, "rc4_128", 128, rc4espinit, nil, 0, nil,}; static Algorithm ahalg[] ={ "null", 0, nullahinit, "hmac_sha1_96", 128, shaahinit, "hmac_md5_96", 128, md5ahinit, 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 voidespkick(void *x){ Conv *c = x; Esphdr *eh; Esptail *et; Userhdr *uh; Espcb *ecb; Block *bp; int nexthdr; int payload; int pad; int align; uchar *auth; 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, UserhdrSize); if(bp == nil) { qunlock(c); return; } uh = (Userhdr*)bp->rp; nexthdr = uh->nexthdr; bp->rp += UserhdrSize; } else { nexthdr = 0; // what should this be? } payload = BLEN(bp) + ecb->espivlen; /* Make space to fit ip header */ bp = padblock(bp, EsphdrSize + 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 + EsptailSize-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+EsptailSize+ecb->ahlen)); bp->wp += pad+EsptailSize+ecb->ahlen; eh = (Esphdr *)(bp->rp); et = (Esptail*)(bp->rp + EsphdrSize + payload + pad); // fill in tail et->pad = pad; et->nexthdr = nexthdr; ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize); auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; // fill in head eh->vihl = IP_VER4; hnputl(eh->espspi, ecb->spi); hnputl(eh->espseq, ++ecb->seq); v6tov4(eh->espsrc, c->laddr); v6tov4(eh->espdst, c->raddr); eh->espproto = IP_ESPPROTO; eh->frag[0] = 0; eh->frag[1] = 0; ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); qunlock(c); //print("esp: pass down: %uld\n", BLEN(bp)); ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c);} voidespiput(Proto *esp, Ipifc*, Block *bp){ Esphdr *eh; Esptail *et; Userhdr *uh; Conv *c; Espcb *ecb; uchar raddr[IPaddrlen], laddr[IPaddrlen]; Fs *f; uchar *auth; ulong spi; int payload, nexthdr; f = esp->f; bp = pullupblock(bp, EsphdrSize+EsptailSize); if(bp == nil) { netlog(f, Logesp, "esp: short packet\n"); return; } eh = (Esphdr*)(bp->rp); spi = nhgetl(eh->espspi); v4tov6(raddr, eh->espsrc); v4tov6(laddr, eh->espdst); 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) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) { qunlock(c); netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr, laddr, spi); freeb(bp); return; } eh = (Esphdr*)(bp->rp); auth = bp->wp - ecb->ahlen; if(!ecb->auth(ecb, eh->espspi, auth-eh->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)-EsphdrSize-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+EsphdrSize, payload)) { qunlock(c);print("esp: cipher failed %I -> %I!%ld: %r\n", raddr, laddr, spi); netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %r\n", raddr, laddr, spi); freeb(bp); return; } payload -= EsptailSize; et = (Esptail*)(bp->rp + EsphdrSize + 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 += EsphdrSize + ecb->espivlen; bp->wp = bp->rp + payload; if(ecb->header) { // assume UserhdrSize < EsphdrSize bp->rp -= UserhdrSize; uh = (Userhdr*)bp->rp; memset(uh, 0, UserhdrSize); 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) ecb->header = 1; else if(strcmp(f[0], "noheader") == 0) ecb->header = 0; else e = "unknown control request"; return e;} voidespadvise(Proto *esp, Block *bp, char *msg){ Esphdr *h; Conv *c; ulong spi; h = (Esphdr*)(bp->rp); spi = nhgets(h->espspi); qlock(esp); c = convlookup(esp, spi); if(c != nil) { qhangup(c->rq, msg); qhangup(c->wq, msg); } qunlock(esp); freeblist(bp);} intespstats(Proto *esp, char *buf, int len){ Esppriv *upriv; upriv = esp->priv; return snprint(buf, len, "%lud %lud\n", upriv->in, upriv->inerrors);} static intesplocal(Conv *c, char *buf, int len){ Espcb *ecb = c->ptcl; int n; qlock(c); if(ecb->incoming) n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi); else n = snprint(buf, len, "%I\n", c->laddr); qunlock(c); return n;} static intespremote(Conv *c, char *buf, int len){ Espcb *ecb = c->ptcl; int n; qlock(c); if(ecb->incoming) n = snprint(buf, len, "%I\n", c->raddr); else n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi); qunlock(c);Lines 1–500 of 866. Load more
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ip.h" #include "libsec.h" typedef struct Esphdr Esphdr; typedef struct Esptail Esptail; typedef struct Userhdr Userhdr; typedef struct Esppriv Esppriv; typedef struct Espcb Espcb; typedef struct Algorithm Algorithm; typedef struct Esprc4 Esprc4; #define DPRINT if(0)print enum { IP_ESPPROTO = 50, EsphdrSize = 28, // includes IP header IphdrSize = 20, // options have been striped EsptailSize = 2, // does not include pad or auth data UserhdrSize = 4, // user visable header size - if enabled }; struct Esphdr { /* ip 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 */ /* esp header */ 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); }; enum { RC4forward = 10*1024*1024, // maximum skip forward RC4back = 100*1024, // maximum look back }; struct Esprc4 { ulong cseq; // current byte sequence number RC4state current; int ovalid; // old is valid ulong lgseq; // last good sequence ulong oseq; // old byte sequence number RC4state old; }; static Conv* convlookup(Proto *esp, ulong spi); static char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg); static void nullespinit(Espcb*, char*, uchar *key, int keylen); 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 void desespinit(Espcb *ecb, char *name, uchar *k, int n); static void rc4espinit(Espcb *ecb, char *name, uchar *k, int n); static void espkick(void *x); static Algorithm espalg[] = { "null", 0, nullespinit, "des_56_cbc", 64, desespinit, "rc4_128", 128, rc4espinit, nil, 0, nil, }; static Algorithm ahalg[] = { "null", 0, nullahinit, "hmac_sha1_96", 128, shaahinit, "hmac_md5_96", 128, md5ahinit, 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 int espstate(Conv *c, char *state, int n) { return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n"); } static void espcreate(Conv *c) { c->rq = qopen(64*1024, Qmsg, 0, 0); c->wq = qopen(64*1024, Qkick, espkick, c); } static void espclose(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 void espkick(void *x) { Conv *c = x; Esphdr *eh; Esptail *et; Userhdr *uh; Espcb *ecb; Block *bp; int nexthdr; int payload; int pad; int align; uchar *auth; 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, UserhdrSize); if(bp == nil) { qunlock(c); return; } uh = (Userhdr*)bp->rp; nexthdr = uh->nexthdr; bp->rp += UserhdrSize; } else { nexthdr = 0; // what should this be? } payload = BLEN(bp) + ecb->espivlen; /* Make space to fit ip header */ bp = padblock(bp, EsphdrSize + 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 + EsptailSize-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+EsptailSize+ecb->ahlen)); bp->wp += pad+EsptailSize+ecb->ahlen; eh = (Esphdr *)(bp->rp); et = (Esptail*)(bp->rp + EsphdrSize + payload + pad); // fill in tail et->pad = pad; et->nexthdr = nexthdr; ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize); auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; // fill in head eh->vihl = IP_VER4; hnputl(eh->espspi, ecb->spi); hnputl(eh->espseq, ++ecb->seq); v6tov4(eh->espsrc, c->laddr); v6tov4(eh->espdst, c->raddr); eh->espproto = IP_ESPPROTO; eh->frag[0] = 0; eh->frag[1] = 0; ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); qunlock(c); //print("esp: pass down: %uld\n", BLEN(bp)); ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c); } void espiput(Proto *esp, Ipifc*, Block *bp) { Esphdr *eh; Esptail *et; Userhdr *uh; Conv *c; Espcb *ecb; uchar raddr[IPaddrlen], laddr[IPaddrlen]; Fs *f; uchar *auth; ulong spi; int payload, nexthdr; f = esp->f; bp = pullupblock(bp, EsphdrSize+EsptailSize); if(bp == nil) { netlog(f, Logesp, "esp: short packet\n"); return; } eh = (Esphdr*)(bp->rp); spi = nhgetl(eh->espspi); v4tov6(raddr, eh->espsrc); v4tov6(laddr, eh->espdst); 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) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) { qunlock(c); netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr, laddr, spi); freeb(bp); return; } eh = (Esphdr*)(bp->rp); auth = bp->wp - ecb->ahlen; if(!ecb->auth(ecb, eh->espspi, auth-eh->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)-EsphdrSize-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+EsphdrSize, payload)) { qunlock(c); print("esp: cipher failed %I -> %I!%ld: %r\n", raddr, laddr, spi); netlog(f, Logesp, "esp: cipher failed %I -> %I!%d: %r\n", raddr, laddr, spi); freeb(bp); return; } payload -= EsptailSize; et = (Esptail*)(bp->rp + EsphdrSize + 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 += EsphdrSize + ecb->espivlen; bp->wp = bp->rp + payload; if(ecb->header) { // assume UserhdrSize < EsphdrSize bp->rp -= UserhdrSize; uh = (Userhdr*)bp->rp; memset(uh, 0, UserhdrSize); 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) ecb->header = 1; else if(strcmp(f[0], "noheader") == 0) ecb->header = 0; else e = "unknown control request"; return e; } void espadvise(Proto *esp, Block *bp, char *msg) { Esphdr *h; Conv *c; ulong spi; h = (Esphdr*)(bp->rp); spi = nhgets(h->espspi); qlock(esp); c = convlookup(esp, spi); if(c != nil) { qhangup(c->rq, msg); qhangup(c->wq, msg); } qunlock(esp); freeblist(bp); } int espstats(Proto *esp, char *buf, int len) { Esppriv *upriv; upriv = esp->priv; return snprint(buf, len, "%lud %lud\n", upriv->in, upriv->inerrors); } static int esplocal(Conv *c, char *buf, int len) { Espcb *ecb = c->ptcl; int n; qlock(c); if(ecb->incoming) n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi); else n = snprint(buf, len, "%I\n", c->laddr); qunlock(c); return n; } static int espremote(Conv *c, char *buf, int len) { Espcb *ecb = c->ptcl; int n; qlock(c); if(ecb->incoming) n = snprint(buf, len, "%I\n", c->raddr); else n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi); qunlock(c);