| plan 9 kernel history: overview | file list | diff list |
2002/0109/port/devssl.c (diff list | history)
| port/devssl.c on 1995/1213 | ||
| 1995/1213 | /* | |
| 1996/0531 | * devssl - secure sockets layer emulation | |
| 1995/1213 | */ | |
| 1997/0618 | #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" | |
| 1995/1213 | ||
| 2000/0112 | #include <libsec.h> | |
| 1995/1213 | ||
| 1998/0806 | #define NOSPOOKS 1 | |
| 1995/1213 | typedef struct OneWay OneWay; struct OneWay { | |
| 1996/0531 | QLock q; | |
| 1996/1029 | QLock ctlq; | |
| 1995/1213 | ||
| 1996/0531 | void *state; /* encryption state */ int slen; /* hash data length */ uchar *secret; /* secret */ ulong mid; /* message id */ | |
| 1995/1213 | }; enum { /* connection states */ | |
| 1996/1029 | Sincomplete= 0, | |
| 1997/0618 | Sclear= 1, Sencrypting= 2, Sdigesting= 4, Sdigenc= Sencrypting|Sdigesting, | |
| 1995/1213 | /* encryption algorithms */ | |
| 1995/1218 | Noencryption= 0, | |
| 1995/1213 | DESCBC= 1, | |
| 1996/1029 | DESECB= 2, RC4= 3 | |
| 1995/1213 | }; typedef struct Dstate Dstate; struct Dstate { Chan *c; /* io channel */ uchar state; /* state of connection */ | |
| 1996/1029 | int ref; /* serialized by dslock for atomic destroy */ | |
| 1995/1213 | uchar encryptalg; /* encryption algorithm */ | |
| 1995/1218 | ushort blocklen; /* blocking length */ | |
| 1995/1213 | ushort diglen; /* length of digest */ DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ | |
| 1995/1218 | /* for SSL format */ | |
| 1995/1213 | int max; /* maximum unpadded data per msg */ | |
| 1995/1217 | int maxpad; /* maximum padded data per msg */ | |
| 1995/1213 | /* input side */ OneWay in; Block *processed; Block *unprocessed; /* output side */ OneWay out; | |
| 1996/1029 | /* protections */ | |
| 2001/0527 | char *user; | |
| 1996/1029 | int perm; | |
| 1995/1213 | }; enum { | |
| 1996/1029 | Maxdmsg= 1<<16, | |
| 2001/0823 | Maxdstate= 128, /* must be a power of 2 */ | |
| 1995/1213 | }; | |
| 2001/0823 | Lock dslock; int dshiwat; char *dsname[Maxdstate]; Dstate *dstate[Maxdstate]; char *encalgs; char *hashalgs; | |
| 1995/1213 | enum{ | |
| 1996/1029 | Qtopdir = 1, /* top level directory */ | |
| 1998/0417 | Qprotodir, | |
| 1996/1029 | Qclonus, Qconvdir, /* directory for a conversation */ Qdata, Qctl, Qsecretin, | |
| 1998/0417 | Qsecretout, Qencalgs, Qhashalgs, | |
| 1995/1213 | }; | |
| 1996/1029 | #define TYPE(x) ((x).path & 0xf) | |
| 2000/0325 | #define CONV(x) (((x).path >> 5)&(Maxdstate-1)) #define QID(c, y) (((c)<<5) | (y)) | |
| 1995/1213 | ||
| 1995/1217 | static void ensure(Dstate*, Block**, int); static void consume(Block**, uchar*, int); | |
| 1996/1029 | static void setsecret(OneWay*, uchar*, int); | |
| 1995/1217 | static Block* encryptb(Dstate*, Block*, int); static Block* decryptb(Dstate*, Block*); static Block* digestb(Dstate*, Block*, int); static void checkdigestb(Dstate*, Block*); | |
| 1996/1029 | static Chan* buftochan(char*); static void sslhangup(Dstate*); static Dstate* dsclone(Chan *c); static void dsnew(Chan *c, Dstate **); | |
| 1995/1217 | ||
| 2000/0325 | char *sslnames[] = { [Qclonus] "clone", [Qdata] "data", [Qctl] "ctl", [Qsecretin] "secretin", [Qsecretout] "secretout", [Qencalgs] "encalgs", [Qhashalgs] "hashalgs", }; | |
| 1997/0327 | static int | |
| 2001/0527 | sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp) | |
| 1996/1029 | { Qid q; Dstate *ds; char name[16], *p, *nm; | |
| 2001/0527 | int ft; | |
| 1996/1029 | USED(nd); USED(d); | |
| 2001/0527 | q.type = QTFILE; | |
| 1996/1029 | q.vers = 0; | |
| 2001/0527 | ft = TYPE(c->qid); switch(ft) { | |
| 1996/1029 | case Qtopdir: | |
| 1999/1230 | if(s == DEVDOTDOT){ | |
| 2001/0527 | q.path = QID(0, Qtopdir); q.type = QTDIR; devdir(c, q, "#D", 0, eve, 0555, dp); | |
| 1999/1230 | return 1; } | |
| 1998/0417 | if(s > 0) return -1; | |
| 2001/0527 | q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "ssl", 0, eve, 0555, dp); | |
| 1998/0417 | return 1; case Qprotodir: | |
| 1999/1230 | if(s == DEVDOTDOT){ | |
| 2001/0527 | q.path = QID(0, Qtopdir); q.type = QTDIR; devdir(c, q, ".", 0, eve, 0555, dp); | |
| 1999/1230 | return 1; } | |
| 1996/1029 | if(s < dshiwat) { | |
| 2001/0527 | q.path = QID(s, Qconvdir); q.type = QTDIR; | |
| 1996/1029 | ds = dstate[s]; if(ds != 0) nm = ds->user; else nm = eve; | |
| 2001/0527 | if(dsname[s] == nil){ sprint(name, "%d", s); kstrdup(&dsname[s], name); } devdir(c, q, dsname[s], 0, nm, 0555, dp); | |
| 1996/1029 | return 1; } if(s > dshiwat) return -1; q.path = QID(0, Qclonus); devdir(c, q, "clone", 0, eve, 0555, dp); return 1; case Qconvdir: | |
| 1999/1230 | if(s == DEVDOTDOT){ | |
| 2001/0527 | q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "ssl", 0, eve, 0555, dp); | |
| 1999/1230 | return 1; } | |
| 1996/1029 | 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; | |
| 1998/0417 | case 4: q.path = QID(CONV(c->qid), Qencalgs); p = "encalgs"; break; case 5: q.path = QID(CONV(c->qid), Qhashalgs); p = "hashalgs"; break; | |
| 1996/1029 | } devdir(c, q, p, 0, nm, 0660, dp); | |
| 2000/0325 | return 1; case Qclonus: devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, eve, 0555, dp); return 1; default: ds = dstate[CONV(c->qid)]; | |
| 2001/0527 | devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, ds->user, 0660, dp); | |
| 1996/1029 | return 1; } return -1; } | |
| 1997/0327 | static Chan* | |
| 1999/0320 | sslattach(char *spec) | |
| 1995/1213 | { | |
| 1996/1029 | Chan *c; c = devattach('D', spec); | |
| 2001/0527 | c->qid.path = QID(0, Qtopdir); | |
| 1996/1029 | c->qid.vers = 0; | |
| 2001/0527 | c->qid.type = QTDIR; | |
| 1996/1029 | return c; | |
| 1995/1213 | } | |
| 2001/0527 | static Walkqid* sslwalk(Chan *c, Chan *nc, char **name, int nname) | |
| 1995/1213 | { | |
| 2001/0527 | return devwalk(c, nc, name, nname, nil, 0, sslgen); | |
| 1995/1213 | } | |
| 2001/0527 | static int sslstat(Chan *c, uchar *db, int n) | |
| 1995/1213 | { | |
| 2001/0527 | return devstat(c, db, n, nil, 0, sslgen); | |
| 1995/1213 | } | |
| 1997/0327 | static Chan* | |
| 1995/1213 | sslopen(Chan *c, int omode) { | |
| 1996/1029 | Dstate *s, **pp; int perm; | |
| 2001/0527 | int ft; | |
| 1995/1213 | ||
| 1996/1029 | perm = 0; omode &= 3; switch(omode) { case OREAD: perm = 4; | |
| 1995/1213 | break; | |
| 1996/1029 | case OWRITE: perm = 2; break; case ORDWR: perm = 6; break; | |
| 1995/1213 | } | |
| 1996/1029 | ||
| 2001/0527 | ft = TYPE(c->qid); switch(ft) { | |
| 1996/1029 | default: panic("sslopen"); case Qtopdir: | |
| 1998/0417 | case Qprotodir: | |
| 1996/1029 | 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; | |
| 1998/0417 | case Qencalgs: case Qhashalgs: if(omode != OREAD) error(Eperm); break; | |
| 1996/1029 | } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; | |
| 1995/1213 | } | |
| 2001/0527 | static int sslwstat(Chan *c, uchar *db, int n) | |
| 1995/1213 | { | |
| 2001/0527 | Dir *dir; | |
| 1996/1029 | Dstate *s; | |
| 2001/0527 | int m; | |
| 1996/1029 | s = dstate[CONV(c->qid)]; if(s == 0) error(Ebadusefd); if(strcmp(s->user, up->user) != 0) error(Eperm); | |
| 2001/0527 | dir = smalloc(sizeof(Dir)+n); m = convM2D(db, n, &dir[0], (char*)&dir[1]); | |
| 2001/1106 | if(m == 0){ free(dir); error(Eshortstat); } if(!emptystr(dir->uid)) | |
| 2001/0527 | kstrdup(&s->user, dir->uid); | |
| 2001/1106 | if(dir->mode != ~0UL) | |
| 2001/0527 | s->perm = dir->mode; | |
| 2001/1106 | ||
| 2001/0527 | free(dir); return m; | |
| 1995/1213 | } | |
| 1997/0327 | static void | |
| 1995/1213 | sslclose(Chan *c) { Dstate *s; | |
| 2001/0527 | int ft; | |
| 1995/1213 | ||
| 2001/0527 | ft = TYPE(c->qid); switch(ft) { | |
| 1996/1029 | case Qctl: case Qdata: case Qsecretin: case Qsecretout: if((c->flag & COPEN) == 0) break; s = dstate[CONV(c->qid)]; if(s == 0) break; lock(&dslock); if(--s->ref > 0) { unlock(&dslock); break; } dstate[CONV(c->qid)] = 0; unlock(&dslock); | |
| 2001/0527 | if(s->user != nil) free(s->user); | |
| 1996/1029 | sslhangup(s); | |
| 1995/1213 | if(s->c) | |
| 1997/0327 | cclose(s->c); | |
| 1995/1213 | if(s->in.secret) free(s->in.secret); if(s->out.secret) free(s->out.secret); | |
| 1996/1029 | if(s->in.state) free(s->in.state); if(s->out.state) free(s->out.state); | |
| 1995/1213 | free(s); | |
| 1996/1029 | ||
| 1995/1213 | } } | |
| 1996/1029 | /* * 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){ | |
| 1997/0327 | bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); | |
| 1996/1029 | if(bl == 0) | |
| 2001/0825 | nexterror(); | |
| 1996/1029 | *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); } } /* | |
| 2000/0913 | * 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); | |
| 2000/0914 | memmove(b->wp, p, n); | |
| 2000/0913 | b->wp += n; b->next = s->unprocessed; s->unprocessed = b; } else { b->rp -= n; | |
| 2000/0914 | memmove(b->rp, p, n); | |
| 2000/0913 | } } | |
| 2001/0825 | */ | |
| 2000/0913 | /* | |
| 1996/1029 | * remove at most n bytes from the queue, if discard is set * dump the remainder */ static Block* | |
| 2001/0602 | qtake(Block **l, int n, int discard) | |
| 1996/1029 | { 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; | |
| 1999/0302 | return first; | |
| 1996/1029 | } 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) | |
| 2001/0602 | panic("qtake"); | |
| 1996/1029 | return first; } else n -= i; if(BLEN(b) < 0) | |
| 2001/0602 | panic("qtake"); | |
| 1996/1029 | } *l = 0; return first; } | |
| 2000/0913 | /* | |
| 2001/0527 | * 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. | |
| 2000/0913 | */ | |
| 1997/0327 | static Block* | |
| 1998/0327 | sslbread(Chan *c, long n, ulong) | |
| 1995/1213 | { | |
| 1996/0531 | volatile struct { Dstate *s; } s; | |
| 1995/1218 | Block *b; | |
| 2001/0825 | uchar consumed[3], *p; int toconsume; | |
| 1996/1029 | int len, pad; | |
| 1995/1217 | ||
| 1996/1029 | s.s = dstate[CONV(c->qid)]; if(s.s == 0) panic("sslbread"); if(s.s->state == Sincomplete) | |
| 1995/1217 | error(Ebadusefd); | |
| 2001/0825 | qlock(&s.s->in.q); | |
| 1995/1217 | if(waserror()){ | |
| 1996/0531 | qunlock(&s.s->in.q); | |
| 1995/1217 | nexterror(); | |
| 1995/1213 | } | |
| 1995/1217 | ||
| 1996/1029 | if(s.s->processed == 0){ | |
| 2001/0825 | /* * Read in the whole message. Until we've got it all, * it stays on s.s->unprocessed, so that if we get Eintr, * we'll pick up where we left off. */ ensure(s.s, &s.s->unprocessed, 3); s.s->unprocessed = pullupblock(s.s->unprocessed, 2); p = s.s->unprocessed->rp; if(p[0] & 0x80){ len = ((p[0] & 0x7f)<<8) | p[1]; | |
| 1996/1029 | ensure(s.s, &s.s->unprocessed, len); | |
| 1995/1217 | pad = 0; | |
| 2001/0825 | toconsume = 2; | |
| 1995/1217 | } else { | |
| 2001/0825 | len = ((p[0] & 0x3f)<<8) | p[1]; pad = p[2]; | |
| 1996/1029 | if(pad > len){ print("pad %d buf len %d\n", pad, len); error("bad pad in ssl message"); } | |
| 2001/0825 | toconsume = 3; | |
| 1995/1217 | } | |
| 2001/0825 | ensure(s.s, &s.s->unprocessed, toconsume+len+pad); | |
| 1995/1217 | ||
| 2001/0825 | /* * Now we have a full SSL packet in the unprocessed list. * Start processing. We can't get Eintr's here. * The only cause for errors from here until the end of the * loop is allocation failures in the block manipulation. * We'll worry about that when we come across it. | |
| 2000/0913 | */ | |
| 1995/1217 | ||
| 2001/0825 | if(waserror()){ print("devssl: unhandled allocation failure\n"); nexterror(); } /* skip header */ consume(&s.s->unprocessed, consumed, toconsume); | |
| 2000/0913 | /* grab the next message and decode/decrypt it */ | |
| 2001/0825 | b = qtake(&s.s->unprocessed, len+pad, 0); | |
| 2000/0913 | ||
| 2001/0825 | if(blocklen(b) != len+pad) print("devssl: sslbread got wrong count %d != %d", blocklen(b), len); | |
| 1996/1029 | if(waserror()){ qunlock(&s.s->in.ctlq); | |
| 2000/0913 | if(b != nil) freeb(b); | |
| 1996/1029 | nexterror(); | |
| 1995/1217 | } | |
| 1996/1029 | qlock(&s.s->in.ctlq); switch(s.s->state){ case Sencrypting: | |
| 2001/0825 | if(b == nil) error("ssl message too short (encrypting)"); | |
| 2000/0913 | b = decryptb(s.s, b); | |
| 1996/1029 | break; case Sdigesting: | |
| 2000/0913 | b = pullupblock(b, s.s->diglen); if(b == nil) | |
| 2001/0825 | error("ssl message too short (digesting)"); | |
| 2000/0913 | checkdigestb(s.s, b); b->rp += s.s->diglen; | |
| 1996/1029 | break; | |
| 1997/0618 | case Sdigenc: | |
| 2000/0913 | b = decryptb(s.s, b); b = pullupblock(b, s.s->diglen); if(b == nil) | |
| 2001/0825 | error("ssl message too short (dig+enc)"); | |
| 2000/0913 | checkdigestb(s.s, b); b->rp += s.s->diglen; | |
| 1998/0501 | len -= s.s->diglen; | |
| 1997/0618 | break; | |
| 1995/1218 | } | |
| 1995/1217 | /* remove pad */ | |
| 1996/1029 | if(pad) | |
| 2001/0602 | s.s->processed = qtake(&b, len - pad, 1); | |
| 2000/0913 | else s.s->processed = b; b = nil; s.s->in.mid++; qunlock(&s.s->in.ctlq); poperror(); | |
| 2001/0825 | poperror(); | |
| 1995/1217 | } | |
| 1996/1029 | /* return at most what was asked for */ | |
| 2001/0602 | b = qtake(&s.s->processed, n, 0); | |
| 1995/1217 | ||
| 1996/0531 | qunlock(&s.s->in.q); | |
| 1995/1217 | poperror(); return b; | |
| 1995/1213 | } | |
| 1997/0327 | static long | |
| 1998/0319 | sslread(Chan *c, void *a, long n, vlong off) | |
| 1995/1213 | { | |
| 1996/0531 | volatile struct { Block *b; } b; | |
| 1996/1029 | Block *nb; uchar *va; int i; char buf[128]; | |
| 1998/0319 | ulong offset = off; | |
| 2001/0527 | int ft; | |
| 1995/1213 | ||
| 2001/0527 | if(c->qid.type & QTDIR) | |
| 1996/1029 | return devdirread(c, a, n, 0, 0, sslgen); | |
| 2001/0527 | ft = TYPE(c->qid); switch(ft) { | |
| 1996/1029 | default: error(Ebadusefd); case Qctl: | |
| 2001/0527 | ft = CONV(c->qid); sprint(buf, "%d", ft); | |
| 1996/1029 | return readstr(offset, a, n, buf); case Qdata: b.b = sslbread(c, n, offset); break; | |
| 1998/0417 | case Qencalgs: return readstr(offset, a, n, encalgs); | |
| 2001/0527 | break; | |
| 1998/0417 | case Qhashalgs: return readstr(offset, a, n, hashalgs); | |
| 2001/0527 | break; | |
| 1995/1213 | } | |
| 1996/0223 | if(waserror()){ | |
| 1996/1029 | freeblist(b.b); | |
| 1996/0223 | nexterror(); | |
| 1995/1213 | } | |
| 1996/1029 | n = 0; va = a; for(nb = b.b; nb; nb = nb->next){ i = BLEN(nb); memmove(va+n, nb->rp, i); n += i; } | |
| 1996/0223 | ||
| 1996/1029 | freeblist(b.b); | |
| 1996/0223 | poperror(); | |
| 1995/1213 | return n; } | |
| 1995/1215 | /* | |
| 1996/1029 | * 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) { | |
| 1997/0618 | while(len-- > 0) *buf++ = nrand(256); | |
| 1996/1029 | } /* | |
| 2000/0913 | * 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. | |
| 1995/1215 | */ | |
| 1997/0327 | static long | |
| 1995/1215 | sslbwrite(Chan *c, Block *b, ulong offset) | |
| 1995/1213 | { | |
| 1996/0531 | volatile struct { Dstate *s; } s; volatile struct { Block *b; } bb; | |
| 1995/1213 | Block *nb; | |
| 1995/1215 | int h, n, m, pad, rv; | |
| 1995/1217 | uchar *p; | |
| 1995/1215 | ||
| 1996/0531 | bb.b = b; | |
| 1996/1029 | s.s = dstate[CONV(c->qid)]; if(s.s == 0) panic("sslbwrite"); if(s.s->state == Sincomplete){ freeb(b); | |
| 1995/1215 | error(Ebadusefd); | |
| 1996/1029 | } | |
| 1995/1215 | if(waserror()){ | |
| 1996/0531 | qunlock(&s.s->out.q); | |
| 2000/0913 | if(bb.b != nil) | |
| 1996/0531 | freeb(bb.b); | |
| 1995/1215 | nexterror(); } | |
| 1996/0531 | qlock(&s.s->out.q); | |
| 1995/1215 | rv = 0; | |
| 1996/0531 | while(bb.b){ m = n = BLEN(bb.b); h = s.s->diglen + 2; | |
| 1995/1215 | ||
| 1995/1218 | /* trim to maximum block size */ | |
| 1995/1215 | pad = 0; | |
| 1996/0531 | if(m > s.s->max){ m = s.s->max; } else if(s.s->blocklen != 1){ | |
| 1998/0501 | pad = (m + s.s->diglen)%s.s->blocklen; | |
| 1995/1215 | if(pad){ | |
| 1996/0531 | if(m > s.s->maxpad){ | |
| 1995/1215 | pad = 0; | |
| 1996/0531 | m = s.s->maxpad; | |
| 1995/1218 | } else { | |
| 1996/0531 | pad = s.s->blocklen - pad; | |
| 1995/1218 | h++; | |
| 1995/1215 | } } } rv += m; if(m != n){ nb = allocb(m + h + pad); | |
| 1996/0531 | memmove(nb->wp + h, bb.b->rp, m); | |
| 1995/1215 | nb->wp += m + h; | |
| 1996/0531 | bb.b->rp += m; | |
| 1995/1215 | } else { | |
| 1995/1218 | /* add header space */ | |
| 1996/0531 | nb = padblock(bb.b, h); bb.b = 0; | |
| 1995/1215 | } | |
| 1996/0531 | m += s.s->diglen; | |
| 1995/1215 | /* SSL style count */ if(pad){ | |
| 1995/1218 | nb = padblock(nb, -pad); | |
| 1996/1029 | randfill(nb->wp, pad); | |
| 1995/1227 | nb->wp += pad; | |
| 1995/1215 | m += pad; | |
| 1995/1218 | p = nb->rp; | |
| 1995/1217 | p[0] = (m>>8); p[1] = m; p[2] = pad; offset = 3; } else { | |
| 1995/1218 | p = nb->rp; p[0] = (m>>8) | 0x80; | |
| 1995/1217 | p[1] = m; offset = 2; } | |
| 1995/1213 | ||
| 1996/1029 | switch(s.s->state){ case Sencrypting: | |
| 1996/0531 | nb = encryptb(s.s, nb, offset); | |
| 1996/1029 | break; case Sdigesting: | |
| 1996/0531 | nb = digestb(s.s, nb, offset); | |
| 1996/1029 | break; | |
| 1997/0618 | case Sdigenc: nb = digestb(s.s, nb, offset); nb = encryptb(s.s, nb, offset); break; | |
| 1996/1029 | } | |
| 1995/1213 | ||
| 1997/0618 | s.s->out.mid++; | |
| 2001/0527 | m = BLEN(nb); | |
| 1997/0327 | devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset); | |
| 2001/0527 | s.s->c->offset += m; | |
| 1995/1215 | } | |
| 1996/0531 | qunlock(&s.s->out.q); | |
| 1995/1215 | poperror(); return rv; | |
| 1996/0223 | } | |
| 1996/1029 | static void setsecret(OneWay *w, uchar *secret, int n) { if(w->secret) free(w->secret); | |
| 1997/0618 | w->secret = smalloc(n); | |
| 1996/1029 | memmove(w->secret, secret, n); w->slen = n; } static void initDESkey(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } | |
| 1997/0618 | w->state = smalloc(sizeof(DESstate)); | |
| 1996/1029 | 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"); } | |
| 1998/0806 | /* * 40 bit DES is the same as 56 bit DES. However, * 16 bits of the key are masked to zero. */ | |
| 1996/1029 | static void | |
| 1998/0806 | initDESkey_40(OneWay *w) { | |
| 1999/0415 | uchar key[8]; | |
| 1998/0806 | if(w->state){ free(w->state); w->state = 0; } | |
| 1999/0415 | if(w->slen >= 8){ memmove(key, w->secret, 8); key[0] &= 0x0f; key[2] &= 0x0f; key[4] &= 0x0f; key[6] &= 0x0f; | |
| 1998/0806 | } w->state = malloc(sizeof(DESstate)); if(w->slen >= 16) | |
| 1999/0415 | setupDESstate(w->state, key, w->secret+8); | |
| 1998/0806 | else if(w->slen >= 8) | |
| 1999/0415 | setupDESstate(w->state, key, 0); | |
| 1998/0806 | else error("secret too short"); } static void | |
| 1996/1029 | initRC4key(OneWay *w) { if(w->state){ free(w->state); w->state = 0; } | |
| 1997/0618 | w->state = smalloc(sizeof(RC4state)); | |
| 1996/1029 | setupRC4state(w->state, w->secret, w->slen); } | |
| 1998/0806 | /* * 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); } | |
| 1998/0910 | /* * 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); } | |
| 1998/0806 | typedef struct Hashalg Hashalg; | |
| 1997/0618 | struct Hashalg { char *name; int diglen; DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); | |
| 1998/0806 | }; Hashalg hashtab[] = | |
| 1997/0618 | { { "md4", MD4dlen, md4, }, { "md5", MD5dlen, md5, }, | |
| 2000/0112 | { "sha1", SHA1dlen, sha1, }, | |
| 2000/0204 | { "sha", SHA1dlen, sha1, }, | |
| 1997/0618 | { 0 } }; static int parsehashalg(char *p, Dstate *s) { | |
| 1998/0806 | Hashalg *ha; | |
| 1997/0618 | 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; } | |
| 1998/0806 | typedef struct Encalg Encalg; | |
| 1997/0618 | struct Encalg { char *name; int blocklen; int alg; void (*keyinit)(OneWay*); | |
| 1998/0806 | }; #ifdef NOSPOOKS Encalg encrypttab[] = | |
| 1997/0618 | { | |
| 1998/0910 | { "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, }, | |
| 1998/0806 | { "rc4_40", 1, RC4, initRC4key_40, }, | |
| 1997/0618 | { 0 } }; | |
| 1998/0806 | #else Encalg encrypttab[] = { | |
| 1998/0910 | { "des_40_cbc", 8, DESCBC, initDESkey_40, }, { "des_40_ecb", 8, DESECB, initDESkey_40, }, { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ | |
| 1998/0806 | { "rc4_40", 1, RC4, initRC4key_40, }, { 0 } }; #endif NOSPOOKS | |
| 1997/0618 | static int parseencryptalg(char *p, Dstate *s) { | |
| 1998/0806 | Encalg *ea; | |
| 1997/0618 | 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; } | |
| 1997/0327 | static long | |
| 1998/0319 | sslwrite(Chan *c, void *a, long n, vlong off) | |
| 1996/0223 | { | |
| 1996/1029 | volatile struct { Dstate *s; } s; | |
| 1996/0531 | volatile struct { Block *b; } b; | |
| 1996/1029 | int m, t; | |
| 1999/0804 | char *p, *np, *e, buf[128]; uchar *x; | |
| 1998/0319 | ulong offset = off; | |
| 1996/0223 | ||
| 1996/1029 | s.s = dstate[CONV(c->qid)]; if(s.s == 0) panic("sslwrite"); | |
| 1996/0223 | ||
| 1996/1029 | t = TYPE(c->qid); if(t == Qdata){ if(s.s->state == Sincomplete) error(Ebadusefd); | |
| 1996/0223 | p = a; | |
| 1996/1029 | e = p + n; do { | |
| 1996/0223 | m = e - p; | |
| 1996/1029 | if(m > s.s->max) m = s.s->max; | |
| 1998/0512 | ||
| 1996/0531 | b.b = allocb(m); | |
| 1996/0223 | if(waserror()){ | |
| 1996/0531 | freeb(b.b); | |
| 1996/0223 | nexterror(); } | |
| 1996/0531 | memmove(b.b->wp, p, m); | |
| 1996/0223 | poperror(); | |
| 1996/0531 | b.b->wp += m; | |
| 1998/0512 | ||
| 1996/0531 | sslbwrite(c, b.b, offset); | |
| 1996/1029 | p += m; } while(p < e); return n; } /* mutex with operations using what we're about to change */ if(waserror()){ qunlock(&s.s->in.ctlq); qunlock(&s.s->out.q); nexterror(); } qlock(&s.s->in.ctlq); qlock(&s.s->out.q); switch(t){ | |
| 1996/0223 | default: | |
| 1996/1029 | panic("sslwrite"); case Qsecretin: setsecret(&s.s->in, a, n); goto out; case Qsecretout: setsecret(&s.s->out, a, n); goto out; case Qctl: break; | |
| 1996/0223 | } | |
| 1996/1029 | if(n >= sizeof(buf)) | |
| 1999/0804 | error("arg too long"); | |
| 1996/1029 | strncpy(buf, a, n); buf[n] = 0; p = strchr(buf, '\n'); if(p) *p = 0; p = strchr(buf, ' '); if(p) *p++ = 0; | |
| 1995/1213 | ||
| 1996/1029 | if(strcmp(buf, "fd") == 0){ s.s->c = buftochan(p); | |
| 1995/1213 | ||
| 1996/1029 | /* default is clear (msg delimiters only) */ s.s->state = Sclear; s.s->blocklen = 1; s.s->diglen = 0; s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; | |
| 1997/0618 | s.s->in.mid = 0; s.s->out.mid = 0; | |
| 1996/1029 | } else if(strcmp(buf, "alg") == 0 && p != 0){ s.s->blocklen = 1; s.s->diglen = 0; | |
| 1995/1213 | ||
| 1996/1029 | if(s.s->c == 0) error("must set fd before algorithm"); | |
| 1999/0527 | s.s->state = Sclear; s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; | |
| 1996/1029 | if(strcmp(p, "clear") == 0){ goto out; | |
| 1995/1213 | } | |
| 1996/1029 | if(s.s->in.secret && s.s->out.secret == 0) setsecret(&s.s->out, s.s->in.secret, s.s->in.slen); if(s.s->out.secret && s.s->in.secret == 0) setsecret(&s.s->in, s.s->out.secret, s.s->out.slen); | |
| 1997/0618 | if(s.s->in.secret == 0 || s.s->out.secret == 0) error("algorithm but no secret"); s.s->hf = 0; s.s->encryptalg = Noencryption; s.s->blocklen = 1; for(;;){ np = strchr(p, ' '); if(np) *np++ = 0; if(parsehashalg(p, s.s) < 0) if(parseencryptalg(p, s.s) < 0) | |
| 1999/0804 | error("bad algorithm"); | |
| 1997/0618 | if(np == 0) break; p = np; } if(s.s->hf == 0 && s.s->encryptalg == Noencryption) | |
| 1999/0804 | error("bad algorithm"); | |
| 1995/1213 | ||
| 1996/1029 | if(s.s->blocklen != 1){ s.s->max = (1<<15) - s.s->diglen - 1; s.s->max -= s.s->max % s.s->blocklen; s.s->maxpad = (1<<14) - s.s->diglen - 1; s.s->maxpad -= s.s->maxpad % s.s->blocklen; } else s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; | |
| 1999/0804 | } 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.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.s->out, x, n); free(x); | |
| 1996/1029 | } else error(Ebadarg); | |
| 1995/1213 | ||
| 1996/1029 | out: qunlock(&s.s->in.ctlq); qunlock(&s.s->out.q); poperror(); return n; | |
| 1998/0417 | } static void sslinit(void) { struct Encalg *e; struct Hashalg *h; int n; char *cp; 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; | |
| 1995/1213 | } | |
| 1997/0327 | Dev ssldevtab = { | |
| 1997/0408 | 'D', "ssl", | |
| 1997/0327 | devreset, sslinit, | |
| 2002/0109 | devshutdown, | |
| 1997/0327 | sslattach, sslwalk, sslstat, sslopen, devcreate, sslclose, sslread, sslbread, sslwrite, sslbwrite, devremove, sslwstat, }; | |
| 1995/1213 | ||
| 1995/1217 | static Block* encryptb(Dstate *s, Block *b, int offset) { | |
| 1995/1218 | uchar *p, *ep, *p2, *ip, *eip; | |
| 1995/1217 | DESstate *ds; | |
| 1995/1213 | ||
| 1995/1217 | switch(s->encryptalg){ | |
| 1995/1218 | case DESECB: | |
| 1995/1217 | 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); | |
| 1995/1218 | 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); } | |
| 1995/1217 | break; | |
| 1996/1029 | case RC4: rc4(s->out.state, b->rp + offset, BLEN(b) - offset); break; | |
| 1995/1213 | } | |
| 1995/1217 | return b; } | |
| 1995/1213 | ||
| 1995/1217 | static Block* | |
| 1999/0414 | decryptb(Dstate *s, Block *bin) | |
| 1995/1217 | { | |
| 1996/1029 | Block *b, **l; | |
| 1995/1218 | uchar *p, *ep, *tp, *ip, *eip; | |
| 1995/1217 | DESstate *ds; | |
| 1995/1218 | uchar tmp[8]; | |
| 1996/1029 | int i; | |
| 1995/1213 | ||
| 1999/0414 | l = &bin; for(b = bin; b; b = b->next){ | |
| 1996/1029 | /* 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("ssl encrypted message too short"); | |
| 1995/1218 | } } | |
| 1996/1029 | l = &b->next; /* decrypt */ switch(s->encryptalg){ case DESECB: ds = s->in.state; ep = b->rp + BLEN(b); | |
| 1998/0501 | for(p = b->rp; p < ep; p += 8) | |
| 1996/1029 | block_cipher(ds->expanded, p, 1); break; case DESCBC: ds = s->in.state; ep = b->rp + BLEN(b); | |
| 1998/0501 | for(p = b->rp; p < ep;){ | |
| 1996/1029 | 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; } | |
| 1995/1217 | } | |
| 1999/0414 | return bin; | |
| 1995/1213 | } | |
| 1995/1217 | static Block* digestb(Dstate *s, Block *b, int offset) | |
| 1995/1213 | { | |
| 1995/1215 | uchar *p; DigestState ss; uchar msgid[4]; ulong n, h; OneWay *w; | |
| 1995/1213 | ||
| 1995/1215 | w = &s->out; | |
| 1995/1213 | ||
| 1995/1215 | memset(&ss, 0, sizeof(ss)); | |
| 1995/1217 | h = s->diglen + offset; | |
| 1995/1215 | n = BLEN(b) - h; | |
| 1995/1213 | ||
| 1995/1215 | /* hash secret + message */ | |
| 1997/0618 | (*s->hf)(w->secret, w->slen, 0, &ss); (*s->hf)(b->rp + h, n, 0, &ss); | |
| 1995/1213 | ||
| 1995/1215 | /* hash message id */ p = msgid; | |
| 1997/0618 | n = w->mid; | |
| 1995/1215 | *p++ = n>>24; *p++ = n>>16; *p++ = n>>8; *p = n; | |
| 1997/0618 | (*s->hf)(msgid, 4, b->rp + offset, &ss); | |
| 1995/1217 | ||
| 1995/1218 | return b; | |
| 1995/1215 | } | |
| 1995/1213 | ||
| 1995/1217 | static void | |
| 1999/0414 | checkdigestb(Dstate *s, Block *bin) | |
| 1995/1215 | { | |
| 1995/1217 | uchar *p; DigestState ss; uchar msgid[4]; | |
| 1995/1218 | int n, h; | |
| 1995/1217 | OneWay *w; uchar digest[128]; | |
| 1996/1029 | Block *b; | |
| 1995/1213 | ||
| 1995/1217 | w = &s->in; | |
| 1995/1213 | ||
| 1995/1217 | memset(&ss, 0, sizeof(ss)); | |
| 1996/1029 | /* hash secret */ | |
| 1997/0618 | (*s->hf)(w->secret, w->slen, 0, &ss); | |
| 1995/1217 | ||
| 1996/1029 | /* hash message */ h = s->diglen; | |
| 1999/0414 | for(b = bin; b; b = b->next){ | |
| 1996/1029 | n = BLEN(b) - h; if(n < 0) panic("checkdigestb"); | |
| 1997/0618 | (*s->hf)(b->rp + h, n, 0, &ss); | |
| 1996/1029 | h = 0; } | |
| 1995/1217 | /* hash message id */ p = msgid; | |
| 1997/0618 | n = w->mid; | |
| 1995/1217 | *p++ = n>>24; *p++ = n>>16; *p++ = n>>8; *p = n; | |
| 1997/0618 | (*s->hf)(msgid, 4, digest, &ss); | |
| 1995/1217 | ||
| 1999/0414 | if(memcmp(digest, bin->rp, s->diglen) != 0) | |
| 1995/1217 | error("bad digest"); | |
| 1995/1213 | } | |
| 1995/1215 | /* get channel associated with an fd */ | |
| 1995/1213 | static Chan* | |
| 1996/1029 | buftochan(char *p) | |
| 1995/1213 | { Chan *c; int fd; | |
| 1996/1029 | if(p == 0) error(Ebadarg); fd = strtoul(p, 0, 0); if(fd < 0) error(Ebadarg); | |
| 1995/1213 | c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ | |
| 2001/0823 | if(devtab[c->type] == &ssldevtab){ cclose(c); error("cannot ssl encrypt devssl files"); } | |
| 1995/1213 | return c; } | |
| 1995/1215 | /* hand up a digest connection */ static void | |
| 1996/1029 | sslhangup(Dstate *s) | |
| 1995/1213 | { | |
| 1995/1215 | Block *b; | |
| 1995/1213 | ||
| 1996/0531 | qlock(&s->in.q); | |
| 1995/1215 | for(b = s->processed; b; b = s->processed){ s->processed = b->next; freeb(b); | |
| 1995/1213 | } | |
| 1995/1215 | if(s->unprocessed){ freeb(s->unprocessed); s->unprocessed = 0; | |
| 1995/1213 | } | |
| 1996/1029 | s->state = Sincomplete; | |
| 1996/0531 | qunlock(&s->in.q); | |
| 1995/1213 | } | |
| 1996/1029 | static Dstate* dsclone(Chan *ch) { | |
| 2001/0823 | int i; Dstate *ret; | |
| 1996/1029 | if(waserror()) { unlock(&dslock); nexterror(); } lock(&dslock); | |
| 2001/0823 | ret = nil; for(i=0; i<Maxdstate; i++){ if(dstate[i] == nil){ dsnew(ch, &dstate[i]); ret = dstate[i]; | |
| 1996/1029 | break; } } unlock(&dslock); poperror(); | |
| 2001/0823 | return ret; | |
| 1996/1029 | } 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; | |
| 2001/0527 | kstrdup(&s->user, up->user); | |
| 1996/1029 | s->perm = 0660; t = TYPE(ch->qid); if(t == Qclonus) t = Qctl; ch->qid.path = QID(pp - dstate, t); ch->qid.vers = 0; } | |