## diffname port/devtls.c 2001/0208 ## diff -e /dev/null /n/emeliedump/2001/0208/sys/src/9/port/devtls.c 0a /* * devtls - transport layer security 1.0 and secure sockets layer 3.0 record layer */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include #define NOSPOOKS 1 typedef struct OneWay OneWay; struct OneWay { QLock q; QLock ctlq; void *state; /* encryption state */ int slen; /* hash data length */ uchar *secret; /* secret */ ulong mid; /* message id */ }; enum { /* connection states */ Sincomplete= 0, Sclear= 1, Sencrypting= 2, Sdigesting= 4, Sdigenc= Sencrypting|Sdigesting, /* encryption algorithms */ Noencryption= 0, DESCBC= 1, DESECB= 2, RC4= 3 }; typedef struct Dstate Dstate; struct Dstate { Chan *c; /* io channel */ uchar state; /* state of connection */ int ref; /* serialized by dslock for atomic destroy */ uchar encryptalg; /* encryption algorithm */ ushort blocklen; /* blocking length */ ushort diglen; /* length of digest */ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ /* for SSL format */ int max; /* maximum unpadded data per msg */ int maxpad; /* maximum padded data per msg */ /* input side */ OneWay in; Block *processed; Block *unprocessed; /* output side */ OneWay out; /* protections */ char user[NAMELEN]; int perm; }; static Lock dslock; static int dshiwat; static int maxdstate = 128; static Dstate** dstate; static char *encalgs; static char *hashalgs; enum { Maxdmsg= 1<<16, Maxdstate= 64 }; enum{ Qtopdir = 1, /* top level directory */ Qprotodir, Qclonus, Qconvdir, /* directory for a conversation */ Qdata, Qctl, Qsecretin, Qsecretout, Qencalgs, Qhashalgs, }; #define TYPE(x) ((x).path & 0xf) #define CONV(x) (((x).path >> 5)&(Maxdstate-1)) #define QID(c, y) (((c)<<5) | (y)) static void ensure(Dstate*, Block**, int); static void consume(Block**, uchar*, int); static void setsecret(OneWay*, uchar*, int); static Block* encryptb(Dstate*, Block*, int); static Block* decryptb(Dstate*, Block*); static Block* digestb(Dstate*, Block*, int); static void checkdigestb(Dstate*, Block*); static Chan* buftochan(char*); static void tlshangup(Dstate*); static Dstate* dsclone(Chan *c); static void dsnew(Chan *c, Dstate **); static char *tlsnames[] = { [Qclonus] "clone", [Qdata] "data", [Qctl] "ctl", [Qsecretin] "secretin", [Qsecretout] "secretout", [Qencalgs] "encalgs", [Qhashalgs] "hashalgs", }; static int tlsgen(Chan *c, Dirtab *d, int nd, int s, Dir *dp) { Qid q; Dstate *ds; char name[16], *p, *nm; USED(nd); USED(d); q.vers = 0; switch(TYPE(c->qid)) { case Qtopdir: if(s == DEVDOTDOT){ q.path = QID(0, Qtopdir)|CHDIR; devdir(c, q, "#D", 0, eve, CHDIR|0555, dp); return 1; } if(s > 0) return -1; q.path = QID(0, Qprotodir)|CHDIR; devdir(c, q, "tls", 0, eve, CHDIR|0555, dp); return 1; case Qprotodir: if(s == DEVDOTDOT){ q.path = QID(0, Qtopdir)|CHDIR; devdir(c, q, ".", 0, eve, CHDIR|0555, dp); return 1; } if(s < dshiwat) { sprint(name, "%d", s); q.path = QID(s, Qconvdir)|CHDIR; ds = dstate[s]; if(ds != 0) nm = ds->user; else nm = eve; devdir(c, q, name, 0, nm, CHDIR|0555, dp); return 1; } if(s > dshiwat) return -1; q.path = QID(0, Qclonus); devdir(c, q, "clone", 0, eve, 0555, dp); return 1; case Qconvdir: if(s == DEVDOTDOT){ q.path = QID(0, Qprotodir)|CHDIR; devdir(c, q, "tls", 0, eve, CHDIR|0555, dp); return 1; } ds = dstate[CONV(c->qid)]; if(ds != 0) nm = ds->user; else nm = eve; switch(s) { default: return -1; case 0: q.path = QID(CONV(c->qid), Qctl); p = "ctl"; break; case 1: q.path = QID(CONV(c->qid), Qdata); p = "data"; break; case 2: q.path = QID(CONV(c->qid), Qsecretin); p = "secretin"; break; case 3: q.path = QID(CONV(c->qid), Qsecretout); p = "secretout"; break; case 4: q.path = QID(CONV(c->qid), Qencalgs); p = "encalgs"; break; case 5: q.path = QID(CONV(c->qid), Qhashalgs); p = "hashalgs"; break; } devdir(c, q, p, 0, nm, 0660, dp); return 1; case Qclonus: devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, eve, 0555, dp); return 1; default: ds = dstate[CONV(c->qid)]; devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, ds->user, 0660, dp); return 1; } return -1; } static Chan* tlsattach(char *spec) { Chan *c; c = devattach('D', spec); c->qid.path = QID(0, Qtopdir)|CHDIR; c->qid.vers = 0; return c; } static int tlswalk(Chan *c, char *name) { return devwalk(c, name, 0, 0, tlsgen); } static void tlsstat(Chan *c, char *db) { devstat(c, db, 0, 0, tlsgen); } static Chan* tlsopen(Chan *c, int omode) { Dstate *s, **pp; int perm; perm = 0; omode &= 3; switch(omode) { case OREAD: perm = 4; break; case OWRITE: perm = 2; break; case ORDWR: perm = 6; break; } switch(TYPE(c->qid)) { default: panic("tlsopen"); case Qtopdir: case Qprotodir: case Qconvdir: if(omode != OREAD) error(Eperm); break; case Qclonus: s = dsclone(c); if(s == 0) error(Enodev); break; case Qctl: case Qdata: case Qsecretin: case Qsecretout: if(waserror()) { unlock(&dslock); nexterror(); } lock(&dslock); pp = &dstate[CONV(c->qid)]; s = *pp; if(s == 0) dsnew(c, pp); else { if((perm & (s->perm>>6)) != perm && (strcmp(up->user, s->user) != 0 || (perm & s->perm) != perm)) error(Eperm); s->ref++; } unlock(&dslock); poperror(); break; case Qencalgs: case Qhashalgs: if(omode != OREAD) error(Eperm); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void tlswstat(Chan *c, char *dp) { Dir d; Dstate *s; convM2D(dp, &d); s = dstate[CONV(c->qid)]; if(s == nil) error(Ebadusefd); if(strcmp(s->user, up->user) != 0) error(Eperm); memmove(s->user, d.uid, NAMELEN); s->perm = d.mode; } static void tlsclose(Chan *c) { Dstate *s; switch(TYPE(c->qid)) { case Qctl: case Qdata: case Qsecretin: case Qsecretout: if((c->flag & COPEN) == 0) break; s = dstate[CONV(c->qid)]; if(s == nil) break; lock(&dslock); if(--s->ref > 0) { unlock(&dslock); break; } dstate[CONV(c->qid)] = nil; unlock(&dslock); tlshangup(s); if(s->c) cclose(s->c); free(s->in.secret); free(s->out.secret); free(s->in.state); free(s->out.state); free(s); } } /* * make sure we have at least 'n' bytes in list 'l' */ static void ensure(Dstate *s, Block **l, int n) { int sofar, i; Block *b, *bl; sofar = 0; for(b = *l; b; b = b->next){ sofar += BLEN(b); if(sofar >= n) return; l = &b->next; } while(sofar < n){ bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); if(bl == 0) error(Ehungup); *l = bl; i = 0; for(b = bl; b; b = b->next){ i += BLEN(b); l = &b->next; } if(i == 0) error(Ehungup); sofar += i; } } /* * copy 'n' bytes from 'l' into 'p' and free * the bytes in 'l' */ static void consume(Block **l, uchar *p, int n) { Block *b; int i; for(; *l && n > 0; n -= i){ b = *l; i = BLEN(b); if(i > n) i = n; memmove(p, b->rp, i); b->rp += i; p += i; if(BLEN(b) < 0) panic("consume"); if(BLEN(b)) break; *l = b->next; freeb(b); } } /* * give back n bytes */ static void regurgitate(Dstate *s, uchar *p, int n) { Block *b; if(n <= 0) return; b = s->unprocessed; if(s->unprocessed == nil || b->rp - b->base < n) { b = allocb(n); memmove(b->wp, p, n); b->wp += n; b->next = s->unprocessed; s->unprocessed = b; } else { b->rp -= n; memmove(b->rp, p, n); } } /* * remove at most n bytes from the queue, if discard is set * dump the remainder */ static Block* qremove(Block **l, int n, int discard) { Block *nb, *b, *first; int i; first = *l; for(b = first; b; b = b->next){ i = BLEN(b); if(i == n){ if(discard){ freeblist(b->next); *l = 0; } else *l = b->next; b->next = 0; return first; } else if(i > n){ i -= n; if(discard){ freeblist(b->next); b->wp -= i; *l = 0; } else { nb = allocb(i); memmove(nb->wp, b->rp+n, i); nb->wp += i; b->wp -= i; nb->next = b->next; *l = nb; } b->next = 0; if(BLEN(b) < 0) panic("qremove"); return first; } else n -= i; if(BLEN(b) < 0) panic("qremove"); } *l = 0; return first; } /* * We can't let Eintr's lose data since the program * doing the read may be able to handle it. The only * places Eintr is possible is during the read's in consume. * Therefore, we make sure we can always put back the bytes * consumed before the last ensure. */ static Block* tlsbread(Chan *c, long n, ulong) { Dstate *volatile s; Block *b; uchar consumed[3]; int nconsumed; int len, pad; s = dstate[CONV(c->qid)]; if(s == nil) panic("tlsbread"); if(s->state == Sincomplete) error(Ebadusefd); nconsumed = 0; if(waserror()){ if(strcmp(up->error, Eintr) != 0) regurgitate(s, consumed, nconsumed); qunlock(&s->in.q); nexterror(); } qlock(&s->in.q); if(s->processed == 0){ /* read in the whole message */ ensure(s, &s->unprocessed, 2); consume(&s->unprocessed, consumed, 2); nconsumed = 2; if(consumed[0] & 0x80){ len = ((consumed[0] & 0x7f)<<8) | consumed[1]; ensure(s, &s->unprocessed, len); pad = 0; } else { len = ((consumed[0] & 0x3f)<<8) | consumed[1]; ensure(s, &s->unprocessed, len+1); consume(&s->unprocessed, &consumed[2], 1); pad = consumed[2]; if(pad > len){ print("pad %d buf len %d\n", pad, len); error("bad pad in tls message"); } } USED(nconsumed); nconsumed = 0; /* if an Eintr happens after this, we're screwed. Make * sure nothing we call can sleep. Luckily, allocb * won't sleep, it'll just error out. */ /* grab the next message and decode/decrypt it */ b = qremove(&s->unprocessed, len, 0); if(waserror()){ qunlock(&s->in.ctlq); if(b != nil) freeb(b); nexterror(); } qlock(&s->in.ctlq); switch(s->state){ case Sencrypting: b = decryptb(s, b); break; case Sdigesting: b = pullupblock(b, s->diglen); if(b == nil) error("tls message too short"); checkdigestb(s, b); b->rp += s->diglen; break; case Sdigenc: b = decryptb(s, b); b = pullupblock(b, s->diglen); if(b == nil) error("tls message too short"); checkdigestb(s, b); b->rp += s->diglen; len -= s->diglen; break; } /* remove pad */ if(pad) s->processed = qremove(&b, len - pad, 1); else s->processed = b; b = nil; s->in.mid++; qunlock(&s->in.ctlq); poperror(); USED(nconsumed); } /* return at most what was asked for */ b = qremove(&s->processed, n, 0); qunlock(&s->in.q); poperror(); return b; } static long tlsread(Chan *c, void *a, long n, vlong off) { Block *volatile b; Block *nb; uchar *va; int i; char buf[128]; ulong offset = off; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, tlsgen); switch(TYPE(c->qid)) { default: error(Ebadusefd); case Qctl: sprint(buf, "%lud", CONV(c->qid)); return readstr(offset, a, n, buf); case Qdata: b = tlsbread(c, n, offset); break; case Qencalgs: return readstr(offset, a, n, encalgs); case Qhashalgs: return readstr(offset, a, n, hashalgs); } if(waserror()){ freeblist(b); nexterror(); } n = 0; va = a; for(nb = b; nb; nb = nb->next){ i = BLEN(nb); memmove(va+n, nb->rp, i); n += i; } freeblist(b); poperror(); return n; } /* * this algorithm doesn't have to be great since we're just * trying to obscure the block fill */ static void randfill(uchar *buf, int len) { while(len-- > 0) *buf++ = nrand(256); } /* * use SSL record format, add in count, digest and/or encrypt. * the write is interruptable. if it is interrupted, we'll * get out of sync with the far side. not much we can do about * it since we don't know if any bytes have been written. */ static long tlsbwrite(Chan *c, Block *b, ulong offset) { Dstate *volatile s; Block *volatile bb; Block *nb; int h, n, m, pad, rv; uchar *p; bb = b; s = dstate[CONV(c->qid)]; if(s == nil) panic("tlsbwrite"); if(s->state == Sincomplete){ freeb(b); error(Ebadusefd); } if(waserror()){ qunlock(&s->out.q); if(bb != nil) freeb(bb); nexterror(); } qlock(&s->out.q); rv = 0; while(bb){ m = n = BLEN(bb); h = s->diglen + 2; /* trim to maximum block size */ pad = 0; if(m > s->max){ m = s->max; } else if(s->blocklen != 1){ pad = (m + s->diglen)%s->blocklen; if(pad){ if(m > s->maxpad){ pad = 0; m = s->maxpad; } else { pad = s->blocklen - pad; h++; } } } rv += m; if(m != n){ nb = allocb(m + h + pad); memmove(nb->wp + h, bb->rp, m); nb->wp += m + h; bb->rp += m; } else { /* add header space */ nb = padblock(bb, h); bb = nil; } m += s->diglen; /* SSL style count */ if(pad){ nb = padblock(nb, -pad); randfill(nb->wp, pad); nb->wp += pad; m += pad; p = nb->rp; p[0] = (m>>8); p[1] = m; p[2] = pad; offset = 3; } else { p = nb->rp; p[0] = (m>>8) | 0x80; p[1] = m; offset = 2; } switch(s->state){ case Sencrypting: nb = encryptb(s, nb, offset); break; case Sdigesting: nb = digestb(s, nb, offset); break; case Sdigenc: nb = digestb(s, nb, offset); nb = encryptb(s, nb, offset); break; } s->out.mid++; m = BLEN(nb); devtab[s->c->type]->bwrite(s->c, nb, s->c->offset); s->c->offset += m; } qunlock(&s->out.q); poperror(); return rv; } static void setsecret(OneWay *w, uchar *secret, int n) { if(w->secret) free(w->secret); w->secret = smalloc(n); memmove(w->secret, secret, n); w->slen = n; } static void initDESkey(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } w->state = smalloc(sizeof(DESstate)); if(w->slen >= 16) setupDESstate(w->state, w->secret, w->secret+8); else if(w->slen >= 8) setupDESstate(w->state, w->secret, 0); else error("secret too short"); } /* * 40 bit DES is the same as 56 bit DES. However, * 16 bits of the key are masked to zero. */ static void initDESkey_40(OneWay *w) { uchar key[8]; if(w->state){ free(w->state); w->state = 0; } if(w->slen >= 8){ memmove(key, w->secret, 8); key[0] &= 0x0f; key[2] &= 0x0f; key[4] &= 0x0f; key[6] &= 0x0f; } w->state = malloc(sizeof(DESstate)); if(w->slen >= 16) setupDESstate(w->state, key, w->secret+8); else if(w->slen >= 8) setupDESstate(w->state, key, 0); else error("secret too short"); } static void initRC4key(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } w->state = smalloc(sizeof(RC4state)); setupRC4state(w->state, w->secret, w->slen); } /* * 40 bit RC4 is the same as n-bit RC4. However, * we ignore all but the first 40 bits of the key. */ static void initRC4key_40(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } if(w->slen > 5) w->slen = 5; w->state = malloc(sizeof(RC4state)); setupRC4state(w->state, w->secret, w->slen); } /* * 128 bit RC4 is the same as n-bit RC4. However, * we ignore all but the first 128 bits of the key. */ static void initRC4key_128(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } if(w->slen > 16) w->slen = 16; w->state = malloc(sizeof(RC4state)); setupRC4state(w->state, w->secret, w->slen); } typedef struct Hashalg Hashalg; struct Hashalg { char *name; int diglen; DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); }; Hashalg hashtab[] = { { "md5", MD5dlen, md5, }, { "sha1", SHA1dlen, sha1, }, { 0 } }; static int parsehashalg(char *p, Dstate *s) { Hashalg *ha; for(ha = hashtab; ha->name; ha++){ if(strcmp(p, ha->name) == 0){ s->hf = ha->hf; s->diglen = ha->diglen; s->state &= ~Sclear; s->state |= Sdigesting; return 0; } } return -1; } typedef struct Encalg Encalg; struct Encalg { char *name; int blocklen; int alg; void (*keyinit)(OneWay*); }; #ifdef NOSPOOKS Encalg encrypttab[] = { { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ { "des_56_cbc", 8, DESCBC, initDESkey, }, { "des_56_ecb", 8, DESECB, initDESkey, }, { "des_40_cbc", 8, DESCBC, initDESkey_40, }, { "des_40_ecb", 8, DESECB, initDESkey_40, }, { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ { "rc4_256", 1, RC4, initRC4key, }, { "rc4_128", 1, RC4, initRC4key_128, }, { "rc4_40", 1, RC4, initRC4key_40, }, { 0 } }; #else Encalg encrypttab[] = { { "des_40_cbc", 8, DESCBC, initDESkey_40, }, { "des_40_ecb", 8, DESECB, initDESkey_40, }, { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ { "rc4_40", 1, RC4, initRC4key_40, }, { 0 } }; #endif NOSPOOKS static int parseencryptalg(char *p, Dstate *s) { Encalg *ea; for(ea = encrypttab; ea->name; ea++){ if(strcmp(p, ea->name) == 0){ s->encryptalg = ea->alg; s->blocklen = ea->blocklen; (*ea->keyinit)(&s->in); (*ea->keyinit)(&s->out); s->state &= ~Sclear; s->state |= Sencrypting; return 0; } } return -1; } static long tlswrite(Chan *c, void *a, long n, vlong off) { Dstate *volatile s; Block *volatile b; int m, t; char *p, *np, *e, buf[128]; uchar *x; ulong offset = off; s = dstate[CONV(c->qid)]; if(s == nil) panic("tlswrite"); t = TYPE(c->qid); if(t == Qdata){ if(s->state == Sincomplete) error(Ebadusefd); p = a; e = p + n; do { m = e - p; if(m > s->max) m = s->max; b = allocb(m); if(waserror()){ freeb(b); nexterror(); } memmove(b->wp, p, m); poperror(); b->wp += m; tlsbwrite(c, b, offset); p += m; } while(p < e); return n; } /* mutex with operations using what we're about to change */ if(waserror()){ qunlock(&s->in.ctlq); qunlock(&s->out.q); nexterror(); } qlock(&s->in.ctlq); qlock(&s->out.q); switch(t){ default: panic("tlswrite"); case Qsecretin: setsecret(&s->in, a, n); goto out; case Qsecretout: setsecret(&s->out, a, n); goto out; case Qctl: break; } if(n >= sizeof(buf)) error("arg too long"); strncpy(buf, a, n); buf[n] = 0; p = strchr(buf, '\n'); if(p) *p = 0; p = strchr(buf, ' '); if(p) *p++ = 0; if(strcmp(buf, "fd") == 0){ s->c = buftochan(p); /* default is clear (msg delimiters only) */ s->state = Sclear; s->blocklen = 1; s->diglen = 0; s->maxpad = s->max = (1<<15) - s->diglen - 1; s->in.mid = 0; s->out.mid = 0; } else if(strcmp(buf, "alg") == 0 && p != 0){ s->blocklen = 1; s->diglen = 0; if(s->c == 0) error("must set fd before algorithm"); s->state = Sclear; s->maxpad = s->max = (1<<15) - s->diglen - 1; if(strcmp(p, "clear") == 0){ goto out; } if(s->in.secret && s->out.secret == 0) setsecret(&s->out, s->in.secret, s->in.slen); if(s->out.secret && s->in.secret == 0) setsecret(&s->in, s->out.secret, s->out.slen); if(s->in.secret == 0 || s->out.secret == 0) error("algorithm but no secret"); s->hf = 0; s->encryptalg = Noencryption; s->blocklen = 1; for(;;){ np = strchr(p, ' '); if(np) *np++ = 0; if(parsehashalg(p, s) < 0) if(parseencryptalg(p, s) < 0) error("bad algorithm"); if(np == 0) break; p = np; } if(s->hf == 0 && s->encryptalg == Noencryption) error("bad algorithm"); if(s->blocklen != 1){ s->max = (1<<15) - s->diglen - 1; s->max -= s->max % s->blocklen; s->maxpad = (1<<14) - s->diglen - 1; s->maxpad -= s->maxpad % s->blocklen; } else s->maxpad = s->max = (1<<15) - s->diglen - 1; } else if(strcmp(buf, "secretin") == 0 && p != 0) { m = (strlen(p)*3)/2; x = smalloc(m); n = dec64(x, m, p, strlen(p)); setsecret(&s->in, x, n); free(x); } else if(strcmp(buf, "secretout") == 0 && p != 0) { m = (strlen(p)*3)/2 + 1; x = smalloc(m); n = dec64(x, m, p, strlen(p)); setsecret(&s->out, x, n); free(x); } else error(Ebadarg); out: qunlock(&s->in.ctlq); qunlock(&s->out.q); poperror(); return n; } static void tlsinit(void) { struct Encalg *e; struct Hashalg *h; int n; char *cp; if((dstate = smalloc(sizeof(Dstate*) * maxdstate)) == 0) panic("tlsinit"); n = 1; for(e = encrypttab; e->name != nil; e++) n += strlen(e->name) + 1; cp = encalgs = smalloc(n); for(e = encrypttab;;){ strcpy(cp, e->name); cp += strlen(e->name); e++; if(e->name == nil) break; *cp++ = ' '; } *cp = 0; n = 1; for(h = hashtab; h->name != nil; h++) n += strlen(h->name) + 1; cp = hashalgs = smalloc(n); for(h = hashtab;;){ strcpy(cp, h->name); cp += strlen(h->name); h++; if(h->name == nil) break; *cp++ = ' '; } *cp = 0; } Dev tlsdevtab = { 'D', "tls", devreset, tlsinit, tlsattach, devclone, tlswalk, tlsstat, tlsopen, devcreate, tlsclose, tlsread, tlsbread, tlswrite, tlsbwrite, devremove, tlswstat, }; static Block* encryptb(Dstate *s, Block *b, int offset) { uchar *p, *ep, *p2, *ip, *eip; DESstate *ds; switch(s->encryptalg){ case DESECB: ds = s->out.state; ep = b->rp + BLEN(b); for(p = b->rp + offset; p < ep; p += 8) block_cipher(ds->expanded, p, 0); break; case DESCBC: ds = s->out.state; ep = b->rp + BLEN(b); for(p = b->rp + offset; p < ep; p += 8){ p2 = p; ip = ds->ivec; for(eip = ip+8; ip < eip; ) *p2++ ^= *ip++; block_cipher(ds->expanded, p, 0); memmove(ds->ivec, p, 8); } break; case RC4: rc4(s->out.state, b->rp + offset, BLEN(b) - offset); break; } return b; } static Block* decryptb(Dstate *s, Block *bin) { Block *b, **l; uchar *p, *ep, *tp, *ip, *eip; DESstate *ds; uchar tmp[8]; int i; l = &bin; for(b = bin; b; b = b->next){ /* make sure we have a multiple of s->blocklen */ if(s->blocklen > 1){ i = BLEN(b); if(i % s->blocklen){ *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); if(b == 0) error("tls encrypted message too short"); } } l = &b->next; /* decrypt */ switch(s->encryptalg){ case DESECB: ds = s->in.state; ep = b->rp + BLEN(b); for(p = b->rp; p < ep; p += 8) block_cipher(ds->expanded, p, 1); break; case DESCBC: ds = s->in.state; ep = b->rp + BLEN(b); for(p = b->rp; p < ep;){ memmove(tmp, p, 8); block_cipher(ds->expanded, p, 1); tp = tmp; ip = ds->ivec; for(eip = ip+8; ip < eip; ){ *p++ ^= *ip; *ip++ = *tp++; } } break; case RC4: rc4(s->in.state, b->rp, BLEN(b)); break; } } return bin; } static Block* digestb(Dstate *s, Block *b, int offset) { uchar *p; DigestState ss; uchar msgid[4]; ulong n, h; OneWay *w; w = &s->out; memset(&ss, 0, sizeof(ss)); h = s->diglen + offset; n = BLEN(b) - h; /* hash secret + message */ (*s->hf)(w->secret, w->slen, 0, &ss); (*s->hf)(b->rp + h, n, 0, &ss); /* hash message id */ p = msgid; n = w->mid; *p++ = n>>24; *p++ = n>>16; *p++ = n>>8; *p = n; (*s->hf)(msgid, 4, b->rp + offset, &ss); return b; } static void checkdigestb(Dstate *s, Block *bin) { uchar *p; DigestState ss; uchar msgid[4]; int n, h; OneWay *w; uchar digest[128]; Block *b; w = &s->in; memset(&ss, 0, sizeof(ss)); /* hash secret */ (*s->hf)(w->secret, w->slen, 0, &ss); /* hash message */ h = s->diglen; for(b = bin; b; b = b->next){ n = BLEN(b) - h; if(n < 0) panic("checkdigestb"); (*s->hf)(b->rp + h, n, 0, &ss); h = 0; } /* hash message id */ p = msgid; n = w->mid; *p++ = n>>24; *p++ = n>>16; *p++ = n>>8; *p = n; (*s->hf)(msgid, 4, digest, &ss); if(memcmp(digest, bin->rp, s->diglen) != 0) error("bad digest"); } /* get channel associated with an fd */ static Chan* buftochan(char *p) { Chan *c; int fd; if(p == 0) error(Ebadarg); fd = strtoul(p, 0, 0); if(fd < 0) error(Ebadarg); c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ return c; } /* hand up a digest connection */ static void tlshangup(Dstate *s) { Block *b; qlock(&s->in.q); for(b = s->processed; b; b = s->processed){ s->processed = b->next; freeb(b); } if(s->unprocessed){ freeb(s->unprocessed); s->unprocessed = 0; } s->state = Sincomplete; qunlock(&s->in.q); } static Dstate* dsclone(Chan *ch) { Dstate **pp, **ep, **np; int newmax; if(waserror()) { unlock(&dslock); nexterror(); } lock(&dslock); ep = &dstate[maxdstate]; for(pp = dstate; pp < ep; pp++) { if(*pp == 0) { dsnew(ch, pp); break; } } if(pp >= ep) { if(maxdstate >= Maxdstate) { unlock(&dslock); poperror(); return 0; } newmax = 2 * maxdstate; if(newmax > Maxdstate) newmax = Maxdstate; np = smalloc(sizeof(Dstate*) * newmax); if(np == 0) error(Enomem); memmove(np, dstate, sizeof(Dstate*) * maxdstate); dstate = np; pp = &dstate[maxdstate]; memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate)); maxdstate = newmax; dsnew(ch, pp); } unlock(&dslock); poperror(); return *pp; } static void dsnew(Chan *ch, Dstate **pp) { Dstate *s; int t; *pp = s = malloc(sizeof(*s)); if(!s) error(Enomem); if(pp - dstate >= dshiwat) dshiwat++; memset(s, 0, sizeof(*s)); s->state = Sincomplete; s->ref = 1; strncpy(s->user, up->user, sizeof(s->user)); s->perm = 0660; t = TYPE(ch->qid); if(t == Qclonus) t = Qctl; ch->qid.path = QID(pp - dstate, t); ch->qid.vers = 0; } . ## diffname port/devtls.c 2001/0331 ## diff -e /n/emeliedump/2001/0208/sys/src/9/port/devtls.c /n/emeliedump/2001/0331/sys/src/9/port/devtls.c 1444a } static void put32(uchar *p, u32int x) { p[0] = x>>24; p[1] = x>>16; p[2] = x>>8; p[3] = x; } static void put64(uchar *p, vlong x) { put32(p, (u32int)(x >> 32)); put32(p+4, (u32int)x); } static void put16(uchar *p, int x) { p[0] = x>>8; p[1] = x; } static u32int get32(uchar *p) { return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; } static int get16(uchar *p) { return (p[0]<<8)|p[1]; . 1427c TlsRec *s; . 1425c dsnew(Chan *ch, TlsRec **pp) . 1415c memset(pp, 0, sizeof(TlsRec*)*(newmax - maxdstate)); . 1412c memmove(np, dstate, sizeof(TlsRec*) * maxdstate); . 1409,1410c np = smalloc(sizeof(TlsRec*) * newmax); if(np == nil) . 1404c return nil; . 1395c if(*pp == nil) { . 1385c TlsRec **pp, **ep, **np; . 1382c static TlsRec* . 1376c s->unprocessed = nil; . 1374c if(s->unprocessed != nil){ . 1365c tlshangup(TlsRec *s) . 1307c checkdigestb(TlsRec *s, Block *bin) . 1276c digestb(TlsRec *s, Block *b, int offset) . 1224c decryptb(TlsRec *s, Block *bin) . 1192c encryptb(TlsRec *s, Block *b, int offset) . 1171c 'a', . 1138c if((dstate = smalloc(sizeof(TlsRec*) * maxdstate)) == 0) . 1057d 1050a if(s->c != nil) error(Einuse); . 1028,1034c error(Ebadusefd); . 998,999c if(m > MaxRecLen) m = MaxRecLen; . 990,993c if(t == Qdata || t == Qhand){ . 985,986c tr = dstate[CONV(c->qid)]; if(tr == nil) . 978c TlsRec *volatile tr; . 957c parseencryptalg(char *p, TlsRec *s) . 945,954d 942d 933,940d 930d 905c parsehashalg(char *p, TlsRec *s) . 900d 851,869d 846,847c w->secret = smalloc(n); memmove(w->secret, secret, n); w->slen = n; . 841,844c if(w->secret) free(w->secret); . 839c setsecret(OneWay *w, uchar *secret, int n) . 821,835c return n; . 798,818c switch(TYPE(c->qid)) { default: return devbwrite(c, b, offset); case Qhand: //ZZZ race setting state if(tr->state != SHandshake && tr->state != SOpen) error(Ebadusefd); tlsrecwrite(tr, RHandshake, b); break; case Qdata: //ZZZ race setting state if(tr->state != SOpen) error(Ebadusefd); tlsrecwrite(tr, RApplication, b); break; . 790,796c tr = dstate[CONV(c->qid)]; if(tr == nil) panic("tlsbread"); . 785,788c n = BLEN(b); . 782,783c TlsRec *tr; ulong n; . 779,780c static long tlsbwrite(Chan *c, Block *b, ulong offset) . 775,776d 773c qunlock(&out->q); . 767,771c /* * if bwrite error's, we assume the block is queued. * if not, we're out of sync with the receiver and will not recover. */ devtab[tr->c->type]->bwrite(tr->c, nb, 0); . 765a qunlock(&out->ctlq); poperror(); . 754,764c /* update length */ put16(p+3, n); /* encrypt */ rc4(&out->rc4, p+RecHdrLen, n); . 742,752c if(out->protected){ put64(seq, out->seq); out->seq++; (*tr->packMac)(tr, out->mackey, seq, p, p + RecHdrLen, n, nb->wp); b->wp += maclen; n += maclen; . 735,740c p = nb->rp; p[0] = type; put16(p+1, tr->version); put16(p+3, n); . 733d 721,730c qlock(&out->ctlq); maclen = 0; if(out->protected) maclen = tr->maclen; n = BLEN(bb); if(n > MaxRecLen){ n = MaxRecLen; nb = allocb(n + RecHdrLen + maclen); memmove(nb->wp + RecHdrLen, bb->rp, n); nb->wp += n + RecHdrLen; bb->rp += n; }else{ /* * carefully reuse bb so it will get freed if we're out of memory */ bb = padblock(bb, RecHdrLen); if(maclen) nb = padblock(bb, -maclen); else nb = bb; . 705,719c /* * get at most one maximal record's input, * with padding on the front for header and back for mac */ if(waserror()){ qunlock(&out->ctlq); nexterror(); . 700,703c while(bb != nil){ //ZZZ race on state if(tr->state != SHandshake && tr->state != SOpen) error(Ebadusefd); . 698c qlock(&out->q); . 693c qunlock(&out->q); . 684,691d 682a out = &tr->out; . 680,681c uchar *p, seq[8]; OneWay *volatile out; int n, maclen; . 677d 674,675c static void tlsrecwrite(TlsRec *tr, int type, Block *b) . 669,672c * write a block in tls records . 601,606d 598c }else{ //ZZZ race setting state while(tr->state == SHandshake && !qcanread(tr->handq)) tlsrecread(tr); qunlock(&tr->in.q); poperror(); b = qbread(tr->handq, n); . 530,596c /* return at most what was asked for */ b = qremove(&tr->processed, n, 0); qunlock(&tr->in.q); . 528c qlock(&tr->in.q); if(TYPE(c->qid) == Qdata){ //ZZZ race setting state if(tr->state != SOpen) error(Ebadusefd); while(tr->processed == nil) tlsrecread(tr); . 523,525c qunlock(&tr->in.q); . 521d 518,519d 515,516c switch(TYPE(c->qid)) { default: return devbread(c, n, offset); case Qhand: case Qdata: break; } tr = dstate[CONV(c->qid)]; if(tr == nil) . 511,513d 509c TlsRec *volatile tr; . 507c tlsbread(Chan *c, long n, ulong offset) . 499a * read and process one tls record layer message * must be called with tr->in.q held */ static void tlsrecread(TlsRec *tr) { OneWay *volatile in; Block *volatile b; uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen]; int volatile nconsumed; int len, type, ver; nconsumed = 0; if(waserror()){ if(strcmp(up->error, Eintr) == 0) regurgitate(tr, header, nconsumed); nexterror(); } ensure(tr, &tr->unprocessed, RecHdrLen); consume(&tr->unprocessed, header, RecHdrLen); nconsumed = RecHdrLen; type = header[0]; ver = get16(header+1); len = get16(header+3); if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion)) tlsError(tr, EProtocolVersion, "invalid version in record layer"); if(len <= 0) tlsError(tr, EIllegalParameter, "invalid length in record layer"); if(len > MaxRecLen) tlsError(tr, ERecordOverflow, "record message too long"); ensure(tr, &tr->unprocessed, len); nconsumed = 0; /* * if an Eintr happens after this, we're screwed. Make * sure nothing we call can sleep. Luckily, allocb * won't sleep, it'll just error out. * grab the next message and decode/decrypt it */ b = qremove(&tr->unprocessed, len, 0); in = &tr->in; if(waserror()){ qunlock(&in->ctlq); if(b != nil) freeb(b); nexterror(); } qlock(&in->ctlq); b = pullupblock(b, len); p = b->rp; if(in->protected) { if(len <= tr->maclen) tlsError(tr, EDecodeError, "record message too short for mac"); rc4(&in->rc4, p, len); len -= tr->maclen; /* update length */ put16(header+3, len); put64(seq, in->seq); in->seq++; (*tr->packMac)(tr, in->mackey, seq, header, p, len, hmac); if(memcmp(hmac, p+len, tr->maclen) != 0) tlsError(tr, EBadRecordMac, "record mac mismatch"); } qunlock(&tr->in.ctlq); poperror(); if(len <= 0) tlsError(tr, EDecodeError, "runt record message"); switch(type) { default: tlsError(tr, EIllegalParameter, "invalid record message 0x%x", type); return; case RChangeCipherSpec: if(len != 1 || p[0] != 1) tlsError(tr, EHandshakeFailure, "invalid change cipher spec"); qlock(&in->ctlq); if(!tr->in.keyed){ qunlock(&in->ctlq); tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec"); } tr->in.keyed = 0; tr->in.protected = 1; tr->in.seq = 0; qunlock(&in->ctlq); break; case RAlert: if(len != 2) tlsError(tr, EDecodeError, "invalid alert"); if(p[0] == 1) { if(p[1] == ECloseNotify) { tlsError(tr, ECloseNotify, "remote close"); tlsSetState(tr, SRemoteClosed); } } else { tlsSetState(tr, SError); tlsAlert(tr, p[1]); } break; case RHandshake: /* * don't worry about dropping the block * qbwrite always queue it even if flow controlled and interrupted. */ if(tr->handq != nil){ qbwrite(tr->handq, b); b = nil; } break; case RApplication: //ZZZ race on state if(tr->state != SOpen) tlsError(tr, EUnexpectedMessage, "application message received before handshake completed"); tr->processed = b; b = nil; break; } if(b != nil) freeb(b); } /* . 432c regurgitate(TlsRec *s, uchar *p, int n) . 372c ensure(TlsRec *s, Block **l, int n) . 360,363d 340,341c case Qhand: . 335c TlsRec *s; . 318c TlsRec *s; . 280,281c case Qhand: . 247c TlsRec *s, **pp; . 226c c = devattach('a', spec); . 203c case 4: . 196,199d 192,193c q.path = QID(CONV(c->qid), Qhand); p = "hand"; . 129c TlsRec *ds; . 119,120c [Qhand] "hand", . 111,113c static void tlshangup(TlsRec*); static TlsRec* dsclone(Chan *c); static void dsnew(Chan *c, TlsRec **); static void put64(uchar *p, vlong x); static void put32(uchar *p, u32int); static void put24(uchar *p, int); static void put16(uchar *p, int); static u32int get32(uchar *p); static int get16(uchar *p); static void tlsAlert(TlsRec *tr, int err); static void tlsError(TlsRec *tr, int err, char *msg, ...); #pragma varargck argpos tlsError 3 . 106,109c static Block* encryptb(TlsRec*, Block*, int); static Block* decryptb(TlsRec*, Block*); static Block* digestb(TlsRec*, Block*, int); static void checkdigestb(TlsRec*, Block*); . 103c static void ensure(TlsRec*, Block**, int); . 93,94c Qhand, . 76c static TlsRec** dstate; . 62,63c Queue *handq; /* queue of handshake messages */ Block *processed; /* next bunch of application data */ Block *unprocessed; /* data read from c but not parsed into records */ . 60c typedef struct TlsRec TlsRec; struct TlsRec { Chan *c; /* io channel */ int ref; /* serialized by dslock for atomic destroy */ int version; /* version of the protocol we are speaking */ int verset; /* version has been set */ Lock statelk; int state; /* must be set using setstate */ /* record layer mac functions for different protocol versions */ void (*packMac)(TlsRec*, uchar*, uchar*, uchar*, uchar*, int, uchar*); DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); int maclen; /* input side -- protected by in.q */ . 56,58d 53,54c int protected; /* cipher is enabled */ RC4state rc4; uchar mackey[64]; }; . 50,51c ulong seq; int keyed; /* have key, waiting for cipher enable */ . 46,48c QLock q; /* locks io access */ QLock ctlq; /* locks one-way paramaters */ . 43,44c enum { MaxRecLen = 1<<14, /* max payload length of a record layer message */ RecHdrLen = 5, TLSVersion = 0x0301, SSL3Version = 0x0300, ProtocolVersion = 0x0301, /* maximum version we speak */ MinProtoVersion = 0x0300, /* limits on version we accept */ MaxProtoVersion = 0x03ff, }; /* connection states */ enum { SHandshake, // doing handshake SOpen, // application data can be sent SRemoteClosed, // remote side has closed down SError, // some sort of error has occured SClosed, // it is all over }; /* record types */ enum { RChangeCipherSpec = 20, RAlert, RHandshake, RApplication, }; /* alerts */ enum { ECloseNotify = 0, EUnexpectedMessage = 10, EBadRecordMac = 20, EDecryptionFailed = 21, ERecordOverflow = 22, EDecompressionFailure = 30, EHandshakeFailure = 40, ENoCertificate = 41, EBadCertificate = 42, EUnsupportedCertificate = 43, ECertificateRevoked = 44, ECertificateExpired = 45, ECertificateUnknown = 46, EIllegalParameter = 47, EUnknownCa = 48, EAccessDenied = 49, EDecodeError = 50, EDecryptError = 51, EExportRestriction = 60, EProtocolVersion = 70, EInsufficientSecurity = 71, EInternalError = 80, EUserCanceled = 90, ENoRenegotiation = 100, EMAX = 256 }; struct OneWay . 29,34d 16,26d 2c * devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0 . ## diffname port/devtls.c 2001/0403 ## diff -e /n/emeliedump/2001/0331/sys/src/9/port/devtls.c /n/emeliedump/2001/0403/sys/src/9/port/devtls.c 1461a } /* * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac. */ static DigestState* sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s, DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen) { int i; uchar pad[48], innerdigest[20]; if(xlen > sizeof(innerdigest) || padlen > sizeof(pad)) return nil; if(klen>64) return nil; /* first time through */ if(s == nil){ for(i=0; imac)(buf, 11, mackey, sec->maclen, 0, 0); (*sec->mac)(body, len, mackey, sec->maclen, mac, s); } static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) { DigestState *s; uchar buf[13]; memmove(buf, seq, 8); memmove(&buf[8], header, 5); s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0); (*sec->mac)(body, len, mackey, sec->maclen, mac, s); . 1453c s->state = SClosed; . 1395,1396c s->state = SClosed; qunlock(&s->in.io); . 1386c qlock(&s->in.io); . 1208,1363d 1143a free(cb); poperror(); . 1140,1142c qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); . 1131,1136c poperror(); . 1115,1129d 1113a if(ea->initkey){ (*ea->initkey)(ea, tr->in.new, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); (*ea->initkey)(ea, tr->out.new, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); } . 1110,1112c tr->out.new = smalloc(sizeof(Secret)); tr->in.new = smalloc(sizeof(Secret)); if(ha->initkey){ (*ha->initkey)(ha, tr->version, tr->in.new, &x[0]); (*ha->initkey)(ha, tr->version, tr->out.new, &x[ha->maclen]); . 1106,1108c m = (strlen(cb->f[3])*3)/2; x = smalloc(m); if(waserror()){ free(x); poperror(); } m = dec64(x, m, cb->f[3], strlen(cb->f[3])); if(m != 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen) error("bad secret data length"); . 1101,1104c ha = parsehashalg(cb->f[1]); ea = parseencalg(cb->f[2]); . 1097,1099c if(tr->in.new != nil){ free(tr->in.new); tr->in.new = nil; } if(tr->out.new != nil){ free(tr->out.new); tr->out.new = nil; } . 1090,1095c free(tr->out.sec); tr->out.sec = tr->out.new; tr->out.new = nil; }else if(strcmp(cb->f[0], "secret") == 0){ if(cb->nf != 4) error("usage: secret hashalg encalg secretdata"); if(tr->c == nil || !tr->verset) error("must set fd and version before secrets"); . 1084,1088c b = allocb(1); *b->wp++ = 1; tlsrecwrite(tr, RChangeCipherSpec, b); freeb(b); . 1081,1082c tlsError(tr, n); b = allocb(2); //ZZZ need to check for fatal error *b->wp++ = 1; *b->wp++ = n; tlsrecwrite(tr, RAlert, b); freeb(b); //ZZZ race on state tlsSetState(tr, SError); }else if(strcmp(cb->f[0], "changecipher") == 0){ if(cb->nf != 1) error("usage: changecipher"); if(tr->out.new == nil) error("can't change cipher spec without setting secret"); . 1071,1079c n = strtol(cb->f[1], nil, 0); . 1069c n = strtol(cb->f[2], nil, 0); if(n < MinProtoVersion || n > MinProtoVersion) error("unsupported version"); tr->c = buftochan(cb->f[1]); tr->version = n; }else if(strcmp(cb->f[0], "version") == 0){ if(cb->nf != 2) error("usage: version n"); if(tr->c == nil) error("must set fd before version"); if(tr->verset) error("version already set"); n = strtol(cb->f[1], nil, 0); if(n == SSL3Version) tr->packMac = sslPackMac; else if(n == TLSVersion) tr->packMac = tlsPackMac; else error("unsupported version"); tr->verset = 1; tr->version = n; }else if(strcmp(cb->f[0], "alert") == 0){ if(cb->nf != 2) error("usage: alert n"); if(tr->c == nil) error("must set fd before sending alerts"); . 1055,1067c if(strcmp(cb->f[0], "fd") == 0){ if(cb->nf != 3) error("usage: fd n version"); if(tr->c != nil) . 1053a qlock(&tr->in.seclock); qlock(&tr->out.seclock); . 1048,1052c /* mutex with operations using what we're about to change */ //ZZZ check this locking if(waserror()){ qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); nexterror(); . 1045,1046c if(cb->nf < 1) error("short control request"); . 1041,1042c free(cb); . 1039c cb = parsecmd(buf, n); . 1036a case Qctl: break; default: error(Ebadusefd); return -1; . 1035c }while(p < e); . 1018c do{ . 1014,1015c switch(TYPE(c->qid)){ case Qdata: case Qhand: . 1005,1006c Cmdbuf *volatile cb; int m; char *p, *e, buf[128]; . 1002a Encalg *ea; Hashalg *ha; . 986,997c for(ea = encrypttab; ea->name; ea++) if(strcmp(p, ea->name) == 0) return ea; error("unsupported encryption algorithm"); return nil; . 981,982c static Encalg* parseencalg(char *p) . 977c { "clear" }, { "rc4_128", 128/8, 0, initRC4key, }, . 974a static void initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *) { setupRC4state(&s->rc4, p, ea->keylen); } . 970,972c int keylen; int ivlen; void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*); . 954,963c for(ha = hashtab; ha->name; ha++) if(strcmp(p, ha->name) == 0) return ha; error("unsupported hash algorithm"); return nil; . 949,950c static Hashalg* parsehashalg(char *p) . 945c { "clear" }, { "md5", MD5dlen, initmd5key, }, . 942a static void initmd5key(Hashalg *ha, int version, Secret *s, uchar *p) { s->maclen = ha->maclen; if(version == SSL3Version) s->mac = sslmac_md5; else s->mac = hmac_md5; memmove(s->mackey, p, ha->maclen); } . 939,940c int maclen; void (*initkey)(Hashalg *, int, Secret *, uchar*); . 904,934d 868c qunlock(&out->io); . 859c qunlock(&out->seclock); . 857c rc4(&out->sec->rc4, p+RecHdrLen, n); . 849c (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp); . 846c if(out->sec != nil){ . 820,821c if(out->sec != nil) maclen = out->sec->maclen; . 818c qlock(&out->seclock); . 815c qunlock(&out->seclock); . 803c qlock(&out->io); . 798c qunlock(&out->io); . 742c snprint(buf, sizeof(buf), "%lud", CONV(c->qid)); . 732c char buf[16]; . 717c qunlock(&tr->in.io); . 711c qunlock(&tr->in.io); . 701c qlock(&tr->in.io); . 698c qunlock(&tr->in.io); . 635c qunlock(&in->seclock); . 632,633c free(tr->in.sec); tr->in.sec = tr->in.new; tr->in.new = nil; . 627,629c qlock(&in->seclock); if(in->new == nil){ qunlock(&in->seclock); . 615c qunlock(&tr->in.seclock); . 611,612c (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac); if(memcmp(hmac, p+len, in->sec->maclen) != 0) . 604,605c rc4(&in->sec->rc4, p, len); len -= in->sec->maclen; . 601,602c if(in->sec != nil) { if(len <= in->sec->maclen) . 598c qlock(&in->seclock); . 593c //ZZZ kill the connection qunlock(&in->seclock); . 584,587c * If an Eintr happens after this, we'll get out of sync. * Make sure nothing we call can sleep. * Errors are ok, as they kill the connection. * Luckily, allocb won't sleep, it'll just error out. . 551c * must be called with tr->in.io held . 408,414c closedown(c, tr); break; . 403,406c if(t == Qhand){ qlock(&tr->in.io); if(tr->handq != nil){ qfree(tr->handq); tr->handq = nil; } qunlock(&tr->in.io); . 399,400c tr = dstate[CONV(c->qid)]; if(tr == nil) . 392c t = TYPE(c->qid); switch(t) { . 390c TlsRec *tr; int t; . 383,384c memmove(tr->user, d.uid, NAMELEN); tr->perm = d.mode; . 380c if(strcmp(tr->user, up->user) != 0) . 377,378c tr = dstate[CONV(c->qid)]; if(tr == nil) . 373c TlsRec *tr; . 355a if(t == Qhand){ if(waserror()){ qunlock(&tr->in.io); closedown(c, tr); } qlock(&tr->in.io); if(tr->handq != nil) error(Einuse); tr->handq = qopen(MaxRecLen, 0, nil, nil); if(tr->handq == nil) error("can't allocate handshake queue"); qunlock(&tr->in.io); poperror(); } . 352c tr->ref++; . 347,349c if((perm & (tr->perm>>6)) != perm && (strcmp(up->user, tr->user) != 0 || (perm & tr->perm) != perm)) . 343,344c tr = *pp; if(tr == nil) . 330,331c tr = dsclone(c); if(tr == nil) . 320c t = TYPE(c->qid); switch(t) { . 303,304c TlsRec *tr, **pp; int t, perm; . 299a static void closedown(Chan *c, TlsRec *tr) { lock(&dslock); if(--tr->ref > 0) { unlock(&dslock); return; } dstate[CONV(c->qid)] = nil; unlock(&dslock); tlshangup(tr); if(tr->c) cclose(tr->c); free(tr->in.sec); free(tr->in.new); free(tr->out.sec); free(tr->out.new); free(tr); } . 165a static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac); . 157,161d 108,110c void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); . 94a struct OneWay { QLock io; /* locks io access */ QLock seclock; /* locks secret paramaters */ ulong seq; Secret *sec; /* cipher in use */ Secret *new; /* cipher waiting for enable */ }; . 87,90c struct Secret { DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); int maclen; . 82,85d 19,25d 16,17c typedef struct Secret Secret; . 13,14d ## diffname port/devtls.c 2001/0404 ## diff -e /n/emeliedump/2001/0403/sys/src/9/port/devtls.c /n/emeliedump/2001/0404/sys/src/9/port/devtls.c 1254a } static void sendAlert(TlsRec *tr, int err) { Block *b; int i, fatal; fatal = 1; for(i=0; i < nelem(tlserrs); i++) { if(tlserrs[i].err == err) { if(tr->version == SSL3Version) err = tlserrs[i].sslerr; else err = tlserrs[i].tlserr; fatal = tlserrs[i].fatal; break; } } b = allocb(2); *b->wp++ = fatal + 1; *b->wp++ = err; tlsrecwrite(tr, RAlert, b); //ZZZ race on state if(fatal) tlsSetState(tr, SError); } static void rcvAlert(TlsRec *tr, int err) { char *s; int i, fatal; s = "unknown error"; fatal = 1; for(i=0; i < nelem(tlserrs); i++){ if(tlserrs[i].err == err){ s = tlserrs[i].msg; fatal = tlserrs[i].fatal; break; } } //ZZZ need to kill session if fatal error if(fatal) tlsSetState(tr, SError); error(s); } static void rcvError(TlsRec *tr, int err, char *fmt, ...) { char msg[ERRLEN]; va_list arg; sendAlert(tr, err); va_start(arg, fmt); strcpy(msg, "tls local %s"); doprint(strchr(msg, '\0'), msg+sizeof(msg), fmt, arg); va_end(arg); error(msg); } static void tlsSetState(TlsRec *tr, int newstate) { lock(&tr->statelk); tr->state = newstate; unlock(&tr->statelk); . 1164a if(strtol(cb->f[3], nil, 0) == 0){ tr->in.new = tos; tr->out.new = toc; }else{ tr->in.new = toc; tr->out.new = tos; } . 1162,1163c (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); . 1158,1159c (*ha->initkey)(ha, tr->version, tos, &x[0]); (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]); . 1155,1156c tos = smalloc(sizeof(Secret)); toc = smalloc(sizeof(Secret)); . 1151,1153c m = dec64(x, m, p, strlen(p)); if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen) error("not enough secret data provided"); . 1149c nexterror(); . 1145c p = cb->f[4]; m = (strlen(p)*3)/2; . 1128,1129c if(cb->nf != 5) error("usage: secret hashalg encalg isclient secretdata"); . 1125,1126c tr->out.sec = toc; qunlock(&tr->out.seclock); return n; . 1123a qlock(&tr->out.seclock); . 1122d 1118a toc = tr->out.new; tr->out.new = nil; //ZZZ minor race; worth fixing? qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); poperror(); free(cb); poperror(); . 1104,1112c sendAlert(tr, m); return n; . 1102c qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); poperror(); free(cb); poperror(); . 1100a m = strtol(cb->f[1], nil, 0); . 1095c tr->version = m; }else if(strcmp(cb->f[0], "opened") == 0){ if(cb->nf != 1) error("usage: opened"); tlsSetState(tr, SOpen); . 1090c else if(m == TLSVersion) . 1087,1088c m = strtol(cb->f[1], nil, 0); if(m == SSL3Version) . 1079c tr->version = m; tlsSetState(tr, SHandshake); . 1075,1076c m = strtol(cb->f[2], nil, 0); if(m < MinProtoVersion || m > MaxProtoVersion) . 1052c cb = parsecmd(a, n); . 1013,1014c char *p, *e; uchar *volatile x; . 1009a Secret *tos, *toc; . 985c static Encalg encrypttab[] = . 951c static Hashalg hashtab[] = . 917,918c // if(tr->state != SHandshake && tr->state != SOpen) // error(Ebadusefd); . 772a case Qhand: . 691c rcvError(tr, EUnexpectedMessage, "application message received before handshake completed"); . 686c }else if(tr->verset && tr->version != SSL3Version) sendAlert(tr, ENoRenegotiation); . 681c * qbwrite always queues even if flow controlled and interrupted. * * if there isn't any handshaker, ignore the request, * but notify the other side we are doing so. . 674,675c rcvAlert(tr, p[1]); . 672a /* * ignore EUserCancelled, it's meaningless * need to handle ENoRenegotiation */ . 670c rcvError(tr, ECloseNotify, "remote close"); . 667c rcvError(tr, EDecodeError, "invalid alert"); . 659,662c free(in->sec); in->sec = in->new; in->new = nil; in->seq = 0; . 657c rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec"); . 653c rcvError(tr, EDecodeError, "invalid change cipher spec"); . 649c rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type); . 645c rcvError(tr, EDecodeError, "runt record message"); . 642c qunlock(&in->seclock); . 640c rcvError(tr, EBadRecordMac, "record mac mismatch"); . 630c rcvError(tr, EDecodeError, "record message too short for mac"); . 619c //ZZZ kill the connection? . 607a poperror(); . 605c rcvError(tr, ERecordOverflow, "record message too long"); . 603c rcvError(tr, EIllegalParameter, "invalid length in record layer"); . 601c rcvError(tr, EProtocolVersion, "invalid version in record layer"); . 376c //ZZZ what is the correct buffering here? tr->handq = qopen(2 * MaxRecLen, 0, nil, nil); . 162,164c static void tlsSetState(TlsRec *tr, int newstate); static void rcvAlert(TlsRec *tr, int err); static void sendAlert(TlsRec *tr, int err); static void rcvError(TlsRec *tr, int err, char *msg, ...); #pragma varargck argpos rcvError 3 . 126a //ZZZ . 117a typedef struct TlsErrs TlsErrs; struct TlsErrs{ int err; int sslerr; int tlserr; int fatal; char *msg; }; static TlsErrs tlserrs[] = { {ECloseNotify, ECloseNotify, ECloseNotify, 0, "remote close"}, {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"}, {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record MAC"}, {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"}, {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"}, {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"}, {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security paramters"}, {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"}, {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"}, {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"}, {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"}, {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"}, {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"}, {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"}, {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"}, {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"}, {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"}, {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"}, {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"}, {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"}, {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"}, {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"}, {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "renegotiation not supported"}, {-1}, }; . ## diffname port/devtls.c 2001/0405 ## diff -e /n/emeliedump/2001/0404/sys/src/9/port/devtls.c /n/emeliedump/2001/0405/sys/src/9/port/devtls.c 1440,1441c qunlock(&tr->in.io); tlsrecwrite(tr, RAlert, ECloseNotify); tlsSetState(tr, SClosed); . 1436,1438c if(tr->unprocessed != nil){ freeb(tr->unprocessed); tr->unprocessed = nil; . 1431,1433c qlock(&tr->in.io); for(b = tr->processed; b; b = tr->processed){ tr->processed = b->next; . 1427c tlshangup(TlsRec *tr) . 1398,1399c // handclose(tr, err); tlshangup(tr); tlsSetState(tr, SError); . 1393d 1389d 1386c int i; . 1381a /* * got a fatal alert message */ . 1211,1215d 1207c /* * the real work is done as the message is written * so the stream is encrypted in sync. */ . 1198,1201d 1175c lock(&tr->statelk); if(tr->state != SHandshake && tr->state != SOpen){ unlock(&tr->statelk); //ZZZ bad error message error("can't set open state"); } tr->state = SOpen; unlock(&tr->statelk); . 996c //ZZZ race on state . 990,992d 960a if(type == RChangeCipherSpec){ if(out->new == nil) error("change cipher without a new cipher"); free(out->sec); out->sec = out->new; out->new = nil; out->seq = 0; } . 952c nb->wp += maclen; . 909c if(tr->state != SHandshake && tr->state != SOpen && tr->state != SRemoteClosed) . 815c //ZZZ race on state . 804c //ZZZ race on state . 757a dechandq(tr); . 755a tr->hqref++; unlock(&tr->hqlock); . 754a lock(&tr->hqlock); . 742a // if(p[1] == ENoRenegotiation) // handclose(tr, "no renegotiation"); // if(p[1] == EUserCancelled) // handclose(tr, "user cancelled"); . 740,741c * propate messages to handshaker * EUserCancelled ENoRenegotiation ZZZ better comment, better thoughts about this . 737a // handclose(tr, "remote close"); error("remote close"); . 736d 706a b->wp -= in->sec->maclen; . 527c bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0); . 504c dstate[CONV(c->qid)] = nil; unlock(&dslock); tlshangup(tr); if(tr->c) cclose(tr->c); free(tr->in.sec); free(tr->in.new); free(tr->out.sec); free(tr->out.new); free(tr); . 496,502c if(t == Qhand) dechandq(tr); lock(&dslock); if(--tr->ref > 0) { unlock(&dslock); return; . 478a dechandq(TlsRec *tr) { lock(&tr->hqlock); if(--tr->hqref == 0 && tr->handq != nil){ qfree(tr->handq); tr->handq = nil; } unlock(&tr->hqlock); } static void . 473a //ZZZ propagate chown to tr->datafd? . 432,446d 427c if(t == Qhand){ if(waserror()){ unlock(&tr->hqlock); nexterror(); } lock(&tr->hqlock); if(tr->handq != nil) error(Einuse); //ZZZ what is the correct buffering here? tr->handq = qopen(2 * MaxRecLen, 0, nil, nil); if(tr->handq == nil) error("can't allocate handshake queue"); tr->hqref = 1; unlock(&tr->hqlock); poperror(); } . 424,425c && (strcmp(up->user, tr->user) != 0 || (perm & tr->perm) != perm)) . 354,374d 186,192d 114,115c char user[NAMELEN]; int perm; . 111c OneWay out; . 109a Lock hqlock; int hqref; Queue *handq; /* queue of handshake messages */ . 105,108c OneWay in; Block *processed; /* next bunch of application data */ Block *unprocessed; /* data read from c but not parsed into records */ . 17a MaxCipherRecLen = MaxRecLen + 2048, //ZZZ Maxdstate = 64, . ## diffname port/devtls.c 2001/0406 ## diff -e /n/emeliedump/2001/0405/sys/src/9/port/devtls.c /n/emeliedump/2001/0406/sys/src/9/port/devtls.c 1519,1530c tr->state = SClosed; tr->ref = 1; strncpy(tr->user, up->user, sizeof(tr->user)); tr->perm = 0660; return tr; . 1516,1517c tr = mallocz(sizeof(*tr), 1); if(tr == nil) . 1513,1514c TlsRec *tr; . 1510,1511c static TlsRec * mktlsrec(void) . 1504a *pp = mktlsrec(); if(pp - dstate >= dshiwat) dshiwat++; t = TYPE(ch->qid); if(t == Qclonus) t = Qctl; ch->qid.path = QID(pp - dstate, t); ch->qid.vers = 0; . 1503d 1496,1497d 1484,1485d 1480,1482c for(pp = dstate; pp < ep; pp++) if(*pp == nil) . 1472c int t, newmax; . 1469c newtls(Chan *ch) . 1465c tlsSetState(tr, SClosed, ~0); . 1442c if(tr->state & old) tr->state = new; . 1427,1440d 1425c tlsSetState(TlsRec *tr, int new, int old) . 1417,1421c unlock(&tr->statelk); if(s != SError) alertHand(tr, msg); . 1410,1415c lock(&tr->statelk); s = tr->state; tr->state = SError; if(s != SError){ strncpy(tr->err, msg, ERRLEN - 1); tr->err[ERRLEN - 1] = '\0'; . 1407,1408c int s; . 1405c tlsError(TlsRec *tr, char *msg) . 1401,1403d 1398c tlsError(tr, msg); . 1396d 1394a if(fatal) tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose); . 1382a msg = tlserrs[i].msg; . 1380a msg = "tls unknown alert"; . 1378a char *msg; . 1284a }else if(strcmp(cb->f[0], "changecipher") == 0){ if(cb->nf != 1) error("usage: changecipher"); if(tr->out.new == nil) error("can't change cipher spec without setting secret"); qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); poperror(); free(cb); poperror(); /* * the real work is done as the message is written * so the stream is encrypted in sync. */ b = allocb(1); *b->wp++ = 1; tlsrecwrite(tr, RChangeCipherSpec, b); return n; }else if(strcmp(cb->f[0], "opened") == 0){ if(cb->nf != 1) error("usage: opened"); if(tr->in.sec == nil || tr->out.sec == nil) error("cipher must be configure before enabling data messages"); lock(&tr->statelk); if(tr->state != SHandshake && tr->state != SOpen){ unlock(&tr->statelk); //ZZZ bad error message error("can't set open state"); } tr->state = SOpen; unlock(&tr->statelk); tr->opened = 1; }else if(strcmp(cb->f[0], "alert") == 0){ if(cb->nf != 2) error("usage: alert n"); if(tr->c == nil) error("must set fd before sending alerts"); m = strtol(cb->f[1], nil, 0); qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); poperror(); free(cb); poperror(); sendAlert(tr, m); if(m == ECloseNotify) tlsclosed(tr, SLClose); return n; . 1189,1235d 1175c error("usage: version vers"); . 1172c tlsSetState(tr, SHandshake, SClosed); . 1164c error("usage: fd open-fd version"); . 1013,1015c checkstate(tr, 0, SOpen); . 988a poperror(); . 987a if(waserror()){ if(strcmp(up->error, "interrupted") != 0) tlsError(tr, "channel error"); nexterror(); } . 920,922c checkstate(tr, type != RApplication, ok); . 918a ok = SHandshake|SOpen|SRClose; if(type == RAlert) ok |= SAlert; . 907c int n, maclen, ok; . 832a if(*b->rp++ == RAlert){ strncpy(up->error, (char*)b->rp, ERRLEN - 1); up->error[ERRLEN - 1] = '\0'; error(up->error); } . 829a . 827,828c checkstate(tr, 1, SOpen|SHandshake|SLClose); /* * it's ok to look at state without the lock * since it only protects reading records, * and we have that tr->in.io held. */ while(!tr->opened && !qcanread(tr->handq)) . 816,818c checkstate(tr, 0, SOpen); . 785a * got a fatal alert message */ static void rcvAlert(TlsRec *tr, int err) { char *s; int i; s = "unknown error"; for(i=0; i < nelem(tlserrs); i++){ if(tlserrs[i].err == err){ s = tlserrs[i].msg; break; } } tlsError(tr, s); if(!tr->opened) error(s); error("tls error"); } /* * found an error while decoding the input stream */ static void rcvError(TlsRec *tr, int err, char *fmt, ...) { char msg[ERRLEN]; va_list arg; va_start(arg, fmt); doprint(msg, msg+sizeof(msg), fmt, arg); va_end(arg); sendAlert(tr, err); if(!tr->opened) error(msg); error("tls error"); } /* * make sure the next hand operation returns with a 'msg' error */ static void alertHand(TlsRec *tr, char *msg) { Block *volatile b; int n; lock(&tr->hqlock); if(tr->handq == nil){ unlock(&tr->hqlock); return; } tr->hqref++; unlock(&tr->hqlock); n = strlen(msg); b = nil; if(waserror()){ if(b != nil) freeb(b); dechandq(tr); nexterror(); } b = allocb(n + 2); *b->wp++ = RAlert; memmove(b->wp, msg, n + 1); b->wp += n + 1; qbwrite(tr->handq, b); poperror(); dechandq(tr); } static void checkstate(TlsRec *tr, int ishand, int ok) { int state; lock(&tr->statelk); state = tr->state; unlock(&tr->statelk); if(state & ok) return; switch(state){ case SHandshake: case SOpen: break; case SError: case SAlert: if(ishand) error(tr->err); error("tls error"); case SRClose: case SLClose: case SClosed: error("tls hungup"); } error("tls improperly configured"); } /* . 782a poperror(); . 774,775c if(!tr->opened) . 768a poperror(); . 766a if(waserror()){ dechandq(tr); nexterror(); } b = padblock(b, 1); *b->rp = RHandshake; . 753a if(p[1] == ENoRenegotiation) alertHand(tr, "no renegotiation"); else if(p[1] == EUserCanceled) alertHand(tr, "handshake canceled by user"); else rcvError(tr, EIllegalParameter, "invalid alert code"); . 752a if(p[0] != 1) rcvError(tr, EIllegalParameter, "invalid alert fatal code"); /* * propate non-fatal alerts to handshaker */ if(p[1] == ECloseNotify) { tlsclosed(tr, SRClose); if(tr->opened) error("tls hungup"); error("close notify"); . 736,751c if(p[0] == 2) . 718c break; . 688,689d 686d 681a b = nil; if(waserror()){ if(b != nil) freeb(b); tlsError(tr, "channel error"); nexterror(); } . 668,670c if(len > MaxRecLen || len < 0) . 657a else tlsError(tr, "channel error"); . 640a static void tlsclosed(TlsRec *tr, int new) { lock(&tr->statelk); if(tr->state == SOpen || tr->state == SHandshake) tr->state = new; else if((new | tr->state) == (SRClose|SLClose)) tr->state = SClosed; unlock(&tr->statelk); alertHand(tr, "close notify"); } . 499c if(tr->c != nil) . 497a if(tr->c != nil && !waserror()){ checkstate(tr, 0, SOpen|SHandshake|SRClose); sendAlert(tr, ECloseNotify); poperror(); } . 423a tr->ref++; . 422c lock(&tr->hqlock); if(tr->handq != nil) error(Einuse); //ZZZ what is the correct buffering here? tr->handq = qopen(2 * MaxRecLen, 0, nil, nil); if(tr->handq == nil) error("can't allocate handshake queue"); tr->hqref = 1; unlock(&tr->hqlock); poperror(); . 420c nexterror(); . 400,418c error("must open connection using clone"); if((perm & (tr->perm>>6)) != perm && (strcmp(up->user, tr->user) != 0 || (perm & tr->perm) != perm)) error(Eperm); if(t == Qhand){ if(waserror()){ . 385c tr = newtls(c); . 224c static void tlsSetState(TlsRec *tr, int new, int old); . 213,214c static void tlsError(TlsRec*, char *); static void alertHand(TlsRec*, char *); static TlsRec *newtls(Chan *c); static TlsRec *mktlsrec(void); . 208a static void checkstate(TlsRec *, int, int); . 185a enum { Maxdstate = 64, }; . 182c 0, "no renegotiation"}, . 140c 1, "bad record mac"}, . 136c 0, "close notify"}, . 112a /* handshake queue */ . 108c /* input side -- protected by in.io */ . 103c int state; . 100c char verset; /* version has been set */ char opened; /* opened command every issued? */ char err[ERRLEN]; /* error message to return to handshake requests */ . 47,48c /* alerts */ . 45d 39,40c /* record types */ . 30,37c /* connection states */ SHandshake = 1 << 0, // doing handshake SOpen = 1 << 1, // application data can be sent SRClose = 1 << 2, // remote side has closed down SLClose = 1 << 3, // sent a close notify alert SAlert = 1 << 5, // sending or sent a fatal alert SError = 1 << 6, // some sort of error has occured SClosed = 1 << 7, // it is all over . 28d 21d ## diffname port/devtls.c 2001/0407 ## diff -e /n/emeliedump/2001/0406/sys/src/9/port/devtls.c /n/emeliedump/2001/0407/sys/src/9/port/devtls.c 1653c unlock(&tdlock); . 1651c ch->qid.path = QID(pp - tlsdevs, t); . 1646,1647c if(pp - tlsdevs >= tdhiwat) tdhiwat++; . 1639,1643c memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs); tlsdevs = np; pp = &tlsdevs[maxtlsdevs]; memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs)); maxtlsdevs = newmax; . 1635,1637c newmax = 2 * maxtlsdevs; if(newmax > MaxTlsDevs) newmax = MaxTlsDevs; . 1630,1631c if(maxtlsdevs >= MaxTlsDevs) { unlock(&tdlock); . 1624,1626c lock(&tdlock); ep = &tlsdevs[maxtlsdevs]; for(pp = tlsdevs; pp < ep; pp++) . 1621c unlock(&tdlock); . 1466c if((tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs)) == 0) . 1421,1422c error("can't enable data messages"); . 1308d 1264c tr = tlsdevs[CONV(c->qid)]; . 1159c tr = tlsdevs[CONV(c->qid)]; . 982a qlock(&tr->hqread); if(tr->hprocessed == nil){ b = qbread(tr->handq, MaxRecLen + 1); if(*b->rp++ == RAlert){ strncpy(up->error, (char*)b->rp, ERRLEN - 1); up->error[ERRLEN - 1] = '\0'; freeb(b); error(up->error); } tr->hprocessed = b; } b = qremove(&tr->hprocessed, n, 0); poperror(); qunlock(&tr->hqread); . 977,981c if(waserror()){ qunlock(&tr->hqread); nexterror(); . 972c while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq)) . 946c tr = tlsdevs[CONV(c->qid)]; . 499,500c tlsdevs[CONV(c->qid)] = nil; unlock(&tdlock); . 496c unlock(&tdlock); . 494c lock(&tdlock); . 487c tr = tlsdevs[CONV(c->qid)]; . 466,468c if(--tr->hqref == 0){ if(tr->handq != nil){ qfree(tr->handq); tr->handq = nil; } if(tr->hprocessed != nil){ freeb(tr->hprocessed); tr->hprocessed = nil; } . 451c tr = tlsdevs[CONV(c->qid)]; . 428c unlock(&tdlock); . 402,403c lock(&tdlock); pp = &tlsdevs[CONV(c->qid)]; . 399c unlock(&tdlock); . 330c ds = tlsdevs[CONV(c->qid)]; . 295c ds = tlsdevs[CONV(c->qid)]; . 284c if(s > tdhiwat) . 276c ds = tlsdevs[s]; . 273c if(s < tdhiwat) { . 209c #define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1)) . 189,192c static Lock tdlock; static int tdhiwat; static int maxtlsdevs = 128; static TlsRec **tlsdevs; . 186c /* max. open tls connections */ MaxTlsDevs = 1024 . 181d 113c Queue *handq; /* queue of handshake messages */ Block *hprocessed; /* remainder of last block read from handq */ QLock hqread; /* protects reads for hprocessed, handq */ . 111c Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */ . 107,108c Block *processed; /* next bunch of application data */ Block *unprocessed; /* data read from c but not parsed into records */ . 92,97c Chan *c; /* io channel */ int ref; /* serialized by tdlock for atomic destroy */ int version; /* version of the protocol we are speaking */ char verset; /* version has been set */ char opened; /* opened command every issued? */ char err[ERRLEN]; /* error message to return to handshake requests */ . 29,35c SHandshake = 1 << 0, /* doing handshake */ SOpen = 1 << 1, /* application data can be sent */ SRClose = 1 << 2, /* remote side has closed down */ SLClose = 1 << 3, /* sent a close notify alert */ SAlert = 1 << 5, /* sending or sent a fatal alert */ SError = 1 << 6, /* some sort of error has occured */ SClosed = 1 << 7, /* it is all over */ . 21a /* protocol versions we can accept */ . 19,20d 16a /* buffer limits */ . ## diffname port/devtls.c 2001/0410 ## diff -e /n/emeliedump/2001/0407/sys/src/9/port/devtls.c /n/emeliedump/2001/0410/sys/src/9/port/devtls.c 1690a } static void freeSec(Secret *s) { if(s != nil){ free(s->enckey); free(s); } } static int noenc(Secret *, uchar *, int n) { return n; } static int rc4enc(Secret *sec, uchar *buf, int n) { rc4(sec->enckey, buf, n); return n; } static int tlsunpad(uchar *buf, int n, int block) { int pad, nn; pad = buf[n - 1]; nn = n - 1 - pad; if(nn <= 0 || n % block) return -1; while(--n > nn) if(pad != buf[n - 1]) return -1; return nn; } static int sslunpad(uchar *buf, int n, int block) { int pad, nn; pad = buf[n - 1]; nn = n - 1 - pad; if(nn <= 0 || n % block) return -1; return nn; } static int blockpad(uchar *buf, int n, int block) { int pad, nn; nn = n + block; nn -= nn % block; pad = nn - (n + 1); while(n < nn) buf[n++] = pad; return nn; } static int des3enc(Secret *sec, uchar *buf, int n) { n = blockpad(buf, n, 8); if(n < 0 || (n & 7)) return -1; des3CBCencrypt(buf, n, sec->enckey); return n; } static int des3dec(Secret *sec, uchar *buf, int n) { if(n & 7) return -1; des3CBCdecrypt(buf, n, sec->enckey); return (*sec->unpad)(buf, n, 8); } static DigestState* nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *) { return nil; . 1410a if(tr->version == SSL3Version){ toc->unpad = sslunpad; tos->unpad = sslunpad; }else{ toc->unpad = tlsunpad; tos->unpad = tlsunpad; } . 1396,1403c if(!ha->initkey || !ea->initkey) error("misimplemented secret algorithm"); (*ha->initkey)(ha, tr->version, tos, &x[0]); (*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]); (*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); (*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); if(!tos->mac || !tos->enc || !tos->dec || !toc->mac || !toc->enc || !toc->dec) error("missing algorithm implementations"); . 1386a freeSec(tos); freeSec(toc); . 1385a tos = nil; toc = nil; . 1376c freeSec(tr->out.new); . 1372c freeSec(tr->in.new); . 1278c Secret *volatile tos, *volatile toc; . 1255,1256c { "clear", 0, 0, initclearenc }, { "rc4_128", 128/8, 0, initRC4key }, { "3des_ede_cbc", 3 * 8, 8, initDES3key }, . 1252a static void initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv) { s->enckey = smalloc(sizeof(DES3state)); s->enc = des3enc; s->dec = des3dec; //ZZZ type hack setupDES3state(s->enckey, (void*)p, iv); } static void initclearenc(Encalg *, Secret *s, uchar *, uchar *) { s->enc = noenc; s->dec = noenc; } . 1250c s->enckey = smalloc(sizeof(RC4state)); s->enc = rc4enc; s->dec = rc4enc; setupRC4state(s->enckey, p, ea->keylen); . 1222a { "sha1", SHA1dlen, initsha1key, }, . 1221c { "clear", 0, initclearmac, }, . 1218a static void initclearmac(Hashalg *, int, Secret *s, uchar *) { s->maclen = 0; s->mac = nomac; } static void initsha1key(Hashalg *ha, int version, Secret *s, uchar *p) { s->maclen = ha->maclen; if(version == SSL3Version) s->mac = sslmac_sha1; else s->mac = hmac_sha1; memmove(s->mackey, p, ha->maclen); } . 1149c freeSec(out->sec); . 1142,1144d 1139a /* encrypt */ n = (*out->sec->enc)(out->sec, p + RecHdrLen, n); nb->wp = nb->rp + n; . 1136,1137c (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n); . 1122c nb = padblock(bb, -(block - n)); . 1119a block = n + maclen + block; block -= n % block; . 1114d 1112c block = n + maclen + block; block -= n % block; nb = allocb(block + RecHdrLen); . 1108a block = out->sec->block; } . 1107c if(out->sec != nil){ . 761c freeSec(in->sec); . 742c b->wp = b->rp + in->sec->maclen; . 732d 729a len = (*in->sec->dec)(in->sec, p, len); if(len < 0) rcvError(tr, EDecodeError, "incorrectly encrypted message"); . 519,522c freeSec(tr->in.sec); freeSec(tr->in.new); freeSec(tr->out.sec); freeSec(tr->out.new); . 512c checkstate(tr, t != Qdata, SOpen|SHandshake|SRClose); . 460d 236a static int rc4enc(Secret *sec, uchar *buf, int n); static int des3enc(Secret *sec, uchar *buf, int n); static int des3dec(Secret *sec, uchar *buf, int n); static int noenc(Secret *sec, uchar *buf, int n); static int sslunpad(uchar *buf, int n, int block); static int tlsunpad(uchar *buf, int n, int block); static void freeSec(Secret *sec); . 224a static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s); . 77,78c void *enckey; uchar mackey[MaxMacLen]; . 75a int (*enc)(Secret*, uchar*, int); int (*dec)(Secret*, uchar*, int); int (*unpad)(uchar*, int, int); . 20a MaxMacLen = SHA1dlen, . ## diffname port/devtls.c 2001/0411 ## diff -e /n/emeliedump/2001/0410/sys/src/9/port/devtls.c /n/emeliedump/2001/0411/sys/src/9/port/devtls.c 1841,1842d 1832,1833d 1309a s->block = 0; . 1301,1302c s->block = 8; setupDES3state(s->enckey, (uchar(*)[8])p, iv); . 1291a s->block = 0; . 1161c nb->wp = p + RecHdrLen + n; . 1141,1142c if(pad) nb = padblock(bb, -pad); . 1138,1139d 1129,1131c nb = allocb(n + pad + RecHdrLen); . 1124c pad = maclen + out->sec->block; . 1121a pad = 0; . 1114c * with padding on the front for header and * back for mac and maximal block padding. . 1094c int n, maclen, pad, ok; . 1074,1084d 757c b->wp = b->rp + len; . 475a unlock(&tdlock); . 467a if(waserror()){ unlock(&tdlock); nexterror(); } lock(&tdlock); . 436d 347,348c lock(&tdlock); tr = tlsdevs[CONV(c->qid)]; devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, tr->user, tr->perm, dp); unlock(&tdlock); . 344c case Qencalgs: case Qhashalgs: perm = 0444; if(TYPE(c->qid) == Qclonus) perm = 0555; devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, eve, perm, dp); . 341c devdir(c, q, tlsnames[TYPE(q)], 0, nm, perm, dp); unlock(&tdlock); . 332,339d 330d 326d 322d 318a unlock(&tdlock); . 316a } . 312,315c lock(&tdlock); tr = tlsdevs[CONV(c->qid)]; if(tr != nil){ nm = tr->user; perm = tr->perm; }else{ perm = 0; . 303,304c sprint(name, "%d", s); q.path = QID(s, Qconvdir)|CHDIR; lock(&tdlock); tr = tlsdevs[s]; if(tr != nil) nm = tr->user; else nm = eve; devdir(c, q, name, 0, nm, CHDIR|0555, dp); unlock(&tdlock); . 301c s -= 3; if(s >= tdhiwat) . 290,298c if(s < 3){ switch(s) { default: return -1; case 0: q.path = QID(0, Qclonus); break; case 1: q.path = QID(0, Qencalgs); break; case 2: q.path = QID(0, Qhashalgs); break; } perm = 0444; if(TYPE(q) == Qclonus) perm = 0555; devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp); . 266,267c TlsRec *tr; char name[16], *nm; int perm; . 79a DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); int block; /* encryption block len, 0 if none */ . 76d ## diffname port/devtls.c 2001/0418 ## diff -e /n/emeliedump/2001/0411/sys/src/9/port/devtls.c /n/emeliedump/2001/0418/sys/src/9/port/devtls.c 1779a } static char* tlsstate(int s) { switch(s){ case SHandshake: return "Handshaking"; case SOpen: return "Established"; case SRClose: return "RemoteHangup"; case SLClose: return "LocalHangup"; case SAlert: return "Alerting"; case SError: return "Errored"; case SClosed: return "Closed"; } return "Unknown"; . 1499a toc->encalg = ea->name; toc->hashalg = ha->name; tos->encalg = ea->name; tos->hashalg = ha->name; . 1227a tr->dataout += n; . 1223a tr->handout += n; . 1069,1070c buf = smalloc(Statlen); snprint(buf, Statlen, "%lud", CONV(c->qid)); n = readstr(offset, a, n, buf); free(buf); return n; . 1067a case Qstatus: buf = smalloc(Statlen); qlock(&tr->in.seclock); qlock(&tr->out.seclock); e = buf + Statlen; s = seprint(buf, e, "%s version 0x%lux", tlsstate(tr->state), tr->version); if(tr->in.sec != nil) s = seprint(s, e, " EncIn %s HashIn %s", tr->in.sec->encalg, tr->in.sec->hashalg); if(tr->in.new != nil) s = seprint(s, e, " NewEncIn %s NewHashIn %s", tr->in.new->encalg, tr->in.new->hashalg); if(tr->out.sec != nil) s = seprint(s, e, " EncOut %s HashOut %s", tr->out.sec->encalg, tr->out.sec->hashalg); if(tr->out.new != nil) s = seprint(s, e, " NewEncOut %s NewHashOut %s", tr->out.new->encalg, tr->out.new->hashalg); seprint(s, e, "\n"); qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); n = readstr(offset, a, n, buf); free(buf); return n; case Qstats: buf = smalloc(Statlen); s = buf; e = buf + Statlen; s = seprint(s, e, "DataIn: %d\n", tr->datain); s = seprint(s, e, "DataOut: %d\n", tr->dataout); s = seprint(s, e, "HandIn: %d\n", tr->handin); seprint(s, e, "HandOut: %d\n", tr->handout); n = readstr(offset, a, n, buf); free(buf); return n; . 1064a tr = tlsdevs[CONV(c->qid)]; . 1060a TlsRec * tr; . 1059c char *buf, *s, *e; . 1046a tr->handin += BLEN(b); . 1014a tr->datain += BLEN(b); . 532a case Qstatus: case Qstats: . 436a case Qstatus: case Qstats: if((t == Qstatus || t == Qstats) && omode != OREAD) error(Eperm); . 369c if(tr != nil){ nm = tr->user; perm = tr->perm; }else{ perm = 0; nm = eve; } if(t == Qstatus || t == Qstats) perm &= 0444; devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp); . 364c devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp); . 362c if(t == Qclonus) . 341,355c t = convdir[s]; if(t == Qstatus || t == Qstats) perm &= 0444; q.path = QID(CONV(c->qid), t); devdir(c, q, tlsnames[t], 0, nm, perm, dp); . 331a if(s < 0 || s >= nelem(convdir)) return -1; . 274c t = TYPE(c->qid); switch(t) { . 269c int perm, t; . 262a static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats }; . 259,260c [Qstatus] "status", [Qstats] "stats", . 255a [Qencalgs] "encalgs", [Qhashalgs] "hashalgs", . 250a static char *tlsstate(int s); . 212,213c Qstatus, Qstats, . 207a Qencalgs, Qhashalgs, . 131d 103a vlong handin; /* bytes communicated by the record layer */ vlong handout; vlong datain; vlong dataout; . 95d 75a char *encalg; /* name of encryption alg */ char *hashalg; /* name of hash alg */ . 16a Statlen= 1024, /* max. length of status or stats message */ . 13,14c typedef struct OneWay OneWay; typedef struct Secret Secret; typedef struct TlsRec TlsRec; typedef struct TlsErrs TlsErrs; . ## diffname port/devtls.c 2001/0420 ## diff -e /n/emeliedump/2001/0418/sys/src/9/port/devtls.c /n/emeliedump/2001/0420/sys/src/9/port/devtls.c 1859c return "LocalClosed"; . 1857c return "RemoteClosed"; . 1107,1108c seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg); . 1105c s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg); . 1103c s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg); . 1101c s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg); . 1099c s = seprint(s, e, "State: %s\n", tlsstate(tr->state)); s = seprint(s, e, "Version: 0x%lux\n", tr->version); . 1097a s = buf; . ## diffname port/devtls.c 2001/0503 ## diff -e /n/emeliedump/2001/0420/sys/src/9/port/devtls.c /n/emeliedump/2001/0503/sys/src/9/port/devtls.c 1784,1785d 574c checkstate(tr, 0, SOpen|SHandshake|SRClose); . ## diffname port/devtls.c 2001/0504 ## diff -e /n/emeliedump/2001/0503/sys/src/9/port/devtls.c /n/emeliedump/2001/0504/sys/src/9/port/devtls.c 1732,1737c if(!waserror()){ b = allocb(2); *b->wp++ = fatal + 1; *b->wp++ = err; if(fatal) tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose); tlsrecwrite(tr, RAlert, b); poperror(); } . 1067c b = qgrab(&tr->hprocessed, n); . 1034c b = qgrab(&tr->processed, n); . 998,1004d 955,956d 953d 941c Block *b; . 878a poperror(); } . 877c }else if(tr->verset && tr->version != SSL3Version && !waserror()){ . 789d 781c b = qgrab(&tr->unprocessed, len); . 750c poperror(); }else . 748c if(strcmp(up->error, Eintr) == 0 && !waserror()){ . 734,735c * read and process one tls record layer message * must be called with tr->in.io held * We can't let Eintrs lose data, since doing so will get * us out of sync with the sender and break the reliablity * of the channel. Eintr only happens during the reads in * consume. Therefore we put back any bytes consumed before * the last call to ensure. . 717,718c i = 0; for(bb = b; bb != nil && i < n; bb = bb->next) i += BLEN(bb); if(i > n) i = n; bb = allocb(i); consume(l, bb->wp, i); bb->wp += i; return bb; . 683,715c b = *l; if(BLEN(b) == n){ *l = b->next; b->next = nil; return b; . 680c Block *bb, *b; . 678c qgrab(Block **l, int n) . 674c * remove at most n bytes from the queue in a single block, if discard is set . ## diffname port/devtls.c 2001/0505 ## diff -e /n/emeliedump/2001/0504/sys/src/9/port/devtls.c /n/emeliedump/2001/0505/sys/src/9/port/devtls.c 863,865c }else{ unlock(&tr->hqlock); if(tr->verset && tr->version != SSL3Version && !waserror()){ sendAlert(tr, ENoRenegotiation); poperror(); } . ## diffname port/devtls.c 2001/1007 ## diff -e /n/emeliedump/2001/0505/sys/src/9/port/devtls.c /n/emeliedump/2001/1007/sys/src/9/port/devtls.c 1826c kstrdup(&tr->user, up->user); . 1800a nmp = smalloc(sizeof *nmp * newmax); memmove(nmp, trnames, sizeof *nmp * maxtlsdevs); trnames = nmp; . 1775a char **nmp; . 1735,1736c strncpy(tr->err, msg, ERRMAX - 1); tr->err[ERRMAX - 1] = '\0'; . 1662d 1624a if((trnames = smalloc((sizeof *trnames) * maxtlsdevs)) == 0) panic("tlsinit"); . 1575c error("cipher must be configured before enabling data messages"); . 1410c ty = TYPE(c->qid); switch(ty){ . 1401c int m, ty; . 1261c ty = TYPE(c->qid); switch(ty) { . 1253a TlsRec *tr; . 1252c int ty; . 1238c if(strcmp(up->errstr, "interrupted") != 0) . 1109c snprint(buf, Statlen, "%llud", CONV(c->qid)); . 1072c ty = TYPE(c->qid); switch(ty) { . 1068c if(c->qid.type & QTDIR) . 1063c int i, ty; . 1044c nexterror(); . 1041,1042c strecpy(up->errstr, up->errstr+ERRMAX, (char*)b->rp); . 1009c if(ty == Qdata){ . 992c ty = TYPE(c->qid); switch(ty) { . 990a TlsRec *volatile tr; . 989c int ty; . 912c char msg[ERRMAX]; . 734c if(strcmp(up->errstr, Eintr) == 0 && !waserror()){ . 584a free(tr->user); . 522a return rv; . 520,521c d = smalloc(n + sizeof *d); rv = convM2D(dp, n, &d[0], (char*) &d[1]); if (rv > 0) { kstrdup(&tr->user, d->uid); tr->perm = d->mode; } free(d); poperror(); . 512a . 507,508d 505a int rv; . 504c Dir *d; . 501,502c static int tlswstat(Chan *c, uchar *dp, int n) . 412c return devstat(c, db, n, nil, 0, tlsgen); . 409,410c static int tlsstat(Chan *c, uchar *db, int n) . 406c return devwalk(c, nc, name, nname, nil, 0, tlsgen); . 403,404c static Walkqid* tlswalk(Chan *c, Chan *nc, char **name, int nname) . 398c c->qid.path = QID(0, Qtopdir); c->qid.type = QTDIR; . 343,344c q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "tls", 0, eve, 0555, dp); . 338c if ((name = trnames[s]) == nil) { name = trnames[s] = smalloc(16); sprint(name, "%d", s); } devdir(c, q, name, 0, nm, 0555, dp); . 330,331c q.path = QID(s, Qconvdir); q.type = QTDIR; . 303,304c q.path = QID(0, Qtopdir); q.type = QTDIR; devdir(c, q, ".", 0, eve, 0555, dp); . 298,299c q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "tls", 0, eve, 0555, dp); . 292,293c q.path = QID(0, Qtopdir); q.type = QTDIR; devdir(c, q, "#a", 0, eve, 0555, dp); . 287a q.type = QTFILE; . 285,286d 282c char *name, *nm; . 278c tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp) . 271,272c [Qstatus] "status", [Qstats] "stats", . 265c [Qclonus] "clone", . 207a static char **trnames; . 148,195c {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"}, {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"}, {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"}, {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"}, {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"}, {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"}, {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security paramters"}, {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"}, {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"}, {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"}, {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"}, {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"}, {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"}, {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"}, {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"}, {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"}, {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"}, {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"}, {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"}, {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"}, {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"}, {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"}, {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"}, . 135c char *user; . 125c Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */ . 102,111c Chan *c; /* io channel */ int ref; /* serialized by tdlock for atomic destroy */ int version; /* version of the protocol we are speaking */ char verset; /* version has been set */ char opened; /* opened command every issued? */ char err[ERRMAX]; /* error message to return to handshake requests */ vlong handin; /* bytes communicated by the record layer */ vlong handout; vlong datain; vlong dataout; . 88c uchar mackey[MaxMacLen]; . 49,72c ECloseNotify = 0, EUnexpectedMessage = 10, EBadRecordMac = 20, EDecryptionFailed = 21, ERecordOverflow = 22, EDecompressionFailure = 30, EHandshakeFailure = 40, ENoCertificate = 41, EBadCertificate = 42, EUnsupportedCertificate = 43, ECertificateRevoked = 44, ECertificateExpired = 45, ECertificateUnknown = 46, EIllegalParameter = 47, EUnknownCa = 48, EAccessDenied = 49, EDecodeError = 50, EDecryptError = 51, EExportRestriction = 60, EProtocolVersion = 70, EInsufficientSecurity = 71, EInternalError = 80, EUserCanceled = 90, ENoRenegotiation = 100, . 27,28c TLSVersion = 0x0301, SSL3Version = 0x0300, . 23,24c RecHdrLen = 5, MaxMacLen = SHA1dlen, . 21c MaxRecLen = 1<<14, /* max payload length of a record layer message */ . 14c typedef struct Secret Secret; . ## diffname port/devtls.c 2001/1106 ## diff -e /n/emeliedump/2001/1007/sys/src/9/port/devtls.c /n/emeliedump/2001/1106/sys/src/9/port/devtls.c 511c . 509a if(d->mode != ~0UL) . 508c if(rv == 0) error(Eshortstat); if(!emptystr(d->uid) . 494a free(d); . 493a d = nil; . ## diffname port/devtls.c 2001/1124 ## diff -e /n/emeliedump/2001/1106/sys/src/9/port/devtls.c /n/emeliedump/2001/1124/sys/src/9/port/devtls.c 512c if(!emptystr(d->uid)) . ## diffname port/devtls.c 2001/1207 ## diff -e /n/emeliedump/2001/1124/sys/src/9/port/devtls.c /n/emeliedump/2001/1207/sys/src/9/port/devtls.c 483a c->iounit = qiomaxatomic; . ## diffname port/devtls.c 2002/0109 ## diff -e /n/emeliedump/2001/1207/sys/src/9/port/devtls.c /n/emeliedump/2002/0109/sys/src/9/port/devtls.c 1668a devshutdown, . ## diffname port/devtls.c 2002/0201 ## diff -e /n/emeliedump/2002/0109/sys/src/9/port/devtls.c /n/emeliedump/2002/0201/sys/src/9/port/devtls.c 1629,1632c tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs); trnames = smalloc((sizeof *trnames) * maxtlsdevs); . ## diffname port/devtls.c 2002/0202 ## diff -e /n/emeliedump/2002/0201/sys/src/9/port/devtls.c /n/emeliedump/2002/0202/sys/src/9/port/devtls.c 167c {EProtocolVersion, EIllegalParameter, EProtocolVersion, 0, "protocol version not supported"}, . ## diffname port/devtls.c 2002/0203 ## diff -e /n/emeliedump/2002/0202/sys/src/9/port/devtls.c /n/emeliedump/2002/0203/sys/src/9/port/devtls.c 2041a } static void put24(uchar *p, int x) { p[0] = x>>16; p[1] = x>>8; p[2] = x; . 871a case SSL2ClientHello: lock(&tr->hqlock); if(tr->handq != nil){ tr->hqref++; unlock(&tr->hqlock); if(waserror()){ dechandq(tr); nexterror(); } /* Pass the SSL2 format data, so that the handshake code can compute the correct checksums. HSSL2ClientHello = HandshakeType 9 is unused in RFC2246. */ b = padblock(b, 8); b->rp[0] = RHandshake; b->rp[1] = HSSL2ClientHello; put24(&b->rp[2], len); b->rp[5] = SSL2ClientHello; put16(&b->rp[6], ver); qbwrite(tr->handq, b); b = nil; poperror(); dechandq(tr); }else{ unlock(&tr->hqlock); if(tr->verset && tr->version != SSL3Version && !waserror()){ sendAlert(tr, ENoRenegotiation); poperror(); } } break; . 745,747c if(tr->handin == 0 && header[0] & 0x80){ /* Cope with an SSL3 ClientHello expressed in SSL2 record format. This is sent by some clients that we must interoperate with, such as Java's JSSE and Microsoft's Internet Explorer. */ len = (get16(header) & ~0x8000) - 5; type = header[2]; ver = get16(header + 3); if(type != SSL2ClientHello || len < 22) rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message"); }else{ /* normal SSL3 record format */ type = header[0]; ver = get16(header+1); len = get16(header+3); } . 675,676c * remove at most n bytes from the queue . 167c {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, . 47a SSL2ClientHello = 1, HSSL2ClientHello = 9, /* local convention; see tlshand.c */ . ## diffname port/devtls.c 2002/0206 ## diff -e /n/emeliedump/2002/0203/sys/src/9/port/devtls.c /n/emeliedump/2002/0206/sys/src/9/port/devtls.c 901c put24(&b->rp[2], len+3); . 763c rcvError(tr, EProtocolVersion, "record layer saw ver %x, not %x/%d; %d %d", ver, tr->version, tr->verset, type, len); . 752c len = (get16(header) & ~0x8000) - 3; . 748c if((tr->handin == 0) && (header[0] & 0x80)){ . ## diffname port/devtls.c 2002/0214 ## diff -e /n/emeliedump/2002/0206/sys/src/9/port/devtls.c /n/emeliedump/2002/0214/sys/src/9/port/devtls.c 765,766c if(len > MaxCipherRecLen || len < 0) rcvError(tr, ERecordOverflow, "record message too long %d", len); . 467c tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil); . ## diffname port/devtls.c 2002/0217 ## diff -e /n/emeliedump/2002/0214/sys/src/9/port/devtls.c /n/emeliedump/2002/0217/sys/src/9/port/devtls.c 962c vseprint(msg, msg+sizeof(msg), fmt, arg); . ## diffname port/devtls.c 2002/0220 ## diff -e /n/emeliedump/2002/0217/sys/src/9/port/devtls.c /n/emeliedump/2002/0220/sys/src/9/port/devtls.c 1148,1151c s = seprint(s, e, "DataIn: %lld\n", tr->datain); s = seprint(s, e, "DataOut: %lld\n", tr->dataout); s = seprint(s, e, "HandIn: %lld\n", tr->handin); seprint(s, e, "HandOut: %lld\n", tr->handout); . 1130c s = seprint(s, e, "Version: 0x%x\n", tr->version); . ## diffname port/devtls.c 2002/0413 ## diff -e /n/emeliedump/2002/0220/sys/src/9/port/devtls.c /n/emeliedump/2002/0413/sys/src/9/port/devtls.c 763,764c rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'", tr->version, tr->verset?"/set":"", len, type, ver, (char*)header); . ## diffname port/devtls.c 2003/0223 ## diff -e /n/emeliedump/2002/0413/sys/src/9/port/devtls.c /n/emeliedump/2003/0223/sys/src/9/port/devtls.c 806c if(unpad_len <= in->sec->maclen || memcmp(hmac, p+len, in->sec->maclen) != 0) . 794,799c /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here should look alike, including timing of the response. */ unpad_len = (*in->sec->dec)(in->sec, p, len); if(unpad_len > in->sec->maclen) len = unpad_len - in->sec->maclen; . 733c int len, type, ver, unpad_len; . ## diffname port/devtls.c 2003/0406 ## diff -e /n/emeliedump/2003/0223/sys/src/9/port/devtls.c /n/emeliedump/2003/0406/sys/src/9/port/devtls.c 321c if((name = trnames[s]) == nil) { .