1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519 |
- /*
- * devssl - secure sockets layer
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- #include <libsec.h>
- #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;
- int perm;
- };
- enum
- {
- Maxdmsg= 1<<16,
- Maxdstate= 512, /* max. open ssl conn's; must be a power of 2 */
- };
- static Lock dslock;
- static int dshiwat;
- static char *dsname[Maxdstate];
- static Dstate *dstate[Maxdstate];
- static char *encalgs;
- static char *hashalgs;
- 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 sslhangup(Dstate*);
- static Dstate* dsclone(Chan *c);
- static void dsnew(Chan *c, Dstate **);
- static long sslput(Dstate *s, Block * volatile b);
- char *sslnames[] = {
- [Qclonus] "clone",
- [Qdata] "data",
- [Qctl] "ctl",
- [Qsecretin] "secretin",
- [Qsecretout] "secretout",
- [Qencalgs] "encalgs",
- [Qhashalgs] "hashalgs",
- };
- static int
- sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp)
- {
- Qid q;
- Dstate *ds;
- char name[16], *p, *nm;
- int ft;
- USED(nd);
- USED(d);
- q.type = QTFILE;
- q.vers = 0;
- ft = TYPE(c->qid);
- switch(ft) {
- case Qtopdir:
- if(s == DEVDOTDOT){
- q.path = QID(0, Qtopdir);
- q.type = QTDIR;
- devdir(c, q, "#D", 0, eve, 0555, dp);
- return 1;
- }
- if(s > 0)
- return -1;
- q.path = QID(0, Qprotodir);
- q.type = QTDIR;
- devdir(c, q, "ssl", 0, eve, 0555, dp);
- return 1;
- case Qprotodir:
- if(s == DEVDOTDOT){
- q.path = QID(0, Qtopdir);
- q.type = QTDIR;
- devdir(c, q, ".", 0, eve, 0555, dp);
- return 1;
- }
- if(s < dshiwat) {
- q.path = QID(s, Qconvdir);
- q.type = QTDIR;
- ds = dstate[s];
- if(ds != 0)
- nm = ds->user;
- else
- nm = eve;
- if(dsname[s] == nil){
- snprint(name, sizeof name, "%d", s);
- kstrdup(&dsname[s], name);
- }
- devdir(c, q, dsname[s], 0, nm, 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);
- q.type = QTDIR;
- devdir(c, q, "ssl", 0, eve, 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, sslnames[TYPE(c->qid)], 0, eve, 0555, dp);
- return 1;
- default:
- ds = dstate[CONV(c->qid)];
- if(ds != 0)
- nm = ds->user;
- else
- nm = eve;
- devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, nm, 0660, dp);
- return 1;
- }
- }
- static Chan*
- sslattach(char *spec)
- {
- Chan *c;
- c = devattach('D', spec);
- c->qid.path = QID(0, Qtopdir);
- c->qid.vers = 0;
- c->qid.type = QTDIR;
- return c;
- }
- static Walkqid*
- sslwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, nil, 0, sslgen);
- }
- static int
- sslstat(Chan *c, uchar *db, int n)
- {
- return devstat(c, db, n, nil, 0, sslgen);
- }
- static Chan*
- sslopen(Chan *c, int omode)
- {
- Dstate *s, **pp;
- int perm;
- int ft;
- perm = 0;
- omode &= 3;
- switch(omode) {
- case OREAD:
- perm = 4;
- break;
- case OWRITE:
- perm = 2;
- break;
- case ORDWR:
- perm = 6;
- break;
- }
- ft = TYPE(c->qid);
- switch(ft) {
- default:
- panic("sslopen");
- 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 int
- sslwstat(Chan *c, uchar *db, int n)
- {
- Dir *dir;
- Dstate *s;
- int m;
- s = dstate[CONV(c->qid)];
- if(s == 0)
- error(Ebadusefd);
- if(strcmp(s->user, up->user) != 0)
- error(Eperm);
- dir = smalloc(sizeof(Dir)+n);
- m = convM2D(db, n, &dir[0], (char*)&dir[1]);
- if(m == 0){
- free(dir);
- error(Eshortstat);
- }
- if(!emptystr(dir->uid))
- kstrdup(&s->user, dir->uid);
- if(dir->mode != ~0UL)
- s->perm = dir->mode;
- free(dir);
- return m;
- }
- static void
- sslclose(Chan *c)
- {
- Dstate *s;
- int ft;
- ft = TYPE(c->qid);
- switch(ft) {
- 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);
- if(s->user != nil)
- free(s->user);
- sslhangup(s);
- if(s->c)
- cclose(s->c);
- if(s->in.secret)
- free(s->in.secret);
- if(s->out.secret)
- free(s->out.secret);
- if(s->in.state)
- free(s->in.state);
- if(s->out.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)
- nexterror();
- *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*
- qtake(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("qtake");
- return first;
- } else
- n -= i;
- if(BLEN(b) < 0)
- panic("qtake");
- }
- *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*
- sslbread(Chan *c, long n, ulong)
- {
- Dstate * volatile s;
- Block *b;
- uchar consumed[3], *p;
- int toconsume;
- int len, pad;
- s = dstate[CONV(c->qid)];
- if(s == 0)
- panic("sslbread");
- if(s->state == Sincomplete)
- error(Ebadusefd);
- qlock(&s->in.q);
- if(waserror()){
- qunlock(&s->in.q);
- nexterror();
- }
- if(s->processed == 0){
- /*
- * Read in the whole message. Until we've got it all,
- * it stays on s->unprocessed, so that if we get Eintr,
- * we'll pick up where we left off.
- */
- ensure(s, &s->unprocessed, 3);
- s->unprocessed = pullupblock(s->unprocessed, 2);
- p = s->unprocessed->rp;
- if(p[0] & 0x80){
- len = ((p[0] & 0x7f)<<8) | p[1];
- ensure(s, &s->unprocessed, len);
- pad = 0;
- toconsume = 2;
- } else {
- s->unprocessed = pullupblock(s->unprocessed, 3);
- len = ((p[0] & 0x3f)<<8) | p[1];
- pad = p[2];
- if(pad > len){
- print("pad %d buf len %d\n", pad, len);
- error("bad pad in ssl message");
- }
- toconsume = 3;
- }
- ensure(s, &s->unprocessed, toconsume+len);
- /* skip header */
- consume(&s->unprocessed, consumed, toconsume);
- /* grab the next message and decode/decrypt it */
- b = qtake(&s->unprocessed, len, 0);
- if(blocklen(b) != len)
- print("devssl: sslbread got wrong count %d != %d", blocklen(b), len);
- if(waserror()){
- qunlock(&s->in.ctlq);
- if(b != nil)
- freeb(b);
- nexterror();
- }
- qlock(&s->in.ctlq);
- switch(s->state){
- case Sencrypting:
- if(b == nil)
- error("ssl message too short (encrypting)");
- b = decryptb(s, b);
- break;
- case Sdigesting:
- b = pullupblock(b, s->diglen);
- if(b == nil)
- error("ssl message too short (digesting)");
- checkdigestb(s, b);
- pullblock(&b, s->diglen);
- len -= s->diglen;
- break;
- case Sdigenc:
- b = decryptb(s, b);
- b = pullupblock(b, s->diglen);
- if(b == nil)
- error("ssl message too short (dig+enc)");
- checkdigestb(s, b);
- pullblock(&b, s->diglen);
- len -= s->diglen;
- break;
- }
- /* remove pad */
- if(pad)
- s->processed = qtake(&b, len - pad, 1);
- else
- s->processed = b;
- b = nil;
- s->in.mid++;
- qunlock(&s->in.ctlq);
- poperror();
- }
- /* return at most what was asked for */
- b = qtake(&s->processed, n, 0);
- qunlock(&s->in.q);
- poperror();
- return b;
- }
- static long
- sslread(Chan *c, void *a, long n, vlong off)
- {
- Block * volatile b;
- Block *nb;
- uchar *va;
- int i;
- char buf[128];
- ulong offset = off;
- int ft;
- if(c->qid.type & QTDIR)
- return devdirread(c, a, n, 0, 0, sslgen);
- ft = TYPE(c->qid);
- switch(ft) {
- default:
- error(Ebadusefd);
- case Qctl:
- ft = CONV(c->qid);
- snprint(buf, sizeof buf, "%d", ft);
- return readstr(offset, a, n, buf);
- case Qdata:
- b = sslbread(c, n, offset);
- break;
- case Qencalgs:
- return readstr(offset, a, n, encalgs);
- break;
- case Qhashalgs:
- return readstr(offset, a, n, hashalgs);
- break;
- }
- 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);
- }
- static long
- sslbwrite(Chan *c, Block *b, ulong)
- {
- Dstate * volatile s;
- long rv;
- s = dstate[CONV(c->qid)];
- if(s == nil)
- panic("sslbwrite");
- if(s->state == Sincomplete){
- freeb(b);
- error(Ebadusefd);
- }
- /* lock so split writes won't interleave */
- if(waserror()){
- qunlock(&s->out.q);
- nexterror();
- }
- qlock(&s->out.q);
- rv = sslput(s, b);
- poperror();
- qunlock(&s->out.q);
- return rv;
- }
- /*
- * 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
- sslput(Dstate *s, Block * volatile b)
- {
- Block *nb;
- int h, n, m, pad, rv;
- uchar *p;
- int offset;
- if(waserror()){
- if(b != nil)
- freeb(b);
- nexterror();
- }
- rv = 0;
- while(b != nil){
- m = n = BLEN(b);
- 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, b->rp, m);
- nb->wp += m + h;
- b->rp += m;
- } else {
- /* add header space */
- nb = padblock(b, h);
- b = 0;
- }
- 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;
- }
- 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->state == nil)
- error(Enomem);
- 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));
- if(w->state == nil)
- error(Enomem);
- 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));
- if(w->state == nil)
- error(Enomem);
- 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[] =
- {
- { "md4", MD4dlen, md4, },
- { "md5", MD5dlen, md5, },
- { "sha1", SHA1dlen, sha1, },
- { "sha", 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
- sslwrite(Chan *c, void *a, long n, vlong)
- {
- Dstate * volatile s;
- Block * volatile b;
- int m, t;
- char *p, *np, *e, buf[128];
- uchar *x;
- x = nil;
- s = dstate[CONV(c->qid)];
- if(s == 0)
- panic("sslwrite");
- t = TYPE(c->qid);
- if(t == Qdata){
- if(s->state == Sincomplete)
- error(Ebadusefd);
- /* lock should a write gets split over multiple records */
- if(waserror()){
- qunlock(&s->out.q);
- nexterror();
- }
- qlock(&s->out.q);
- 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;
- sslput(s, b);
- p += m;
- } while(p < e);
- poperror();
- qunlock(&s->out.q);
- 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("sslwrite");
- 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(waserror()){
- free(x);
- nexterror();
- }
- 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 outx;
- 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);
- t = dec64(x, m, p, strlen(p));
- if(t <= 0)
- error(Ebadarg);
- setsecret(&s->in, x, t);
- } else if(strcmp(buf, "secretout") == 0 && p != 0) {
- m = (strlen(p)*3)/2 + 1;
- x = smalloc(m);
- t = dec64(x, m, p, strlen(p));
- if(t <= 0)
- error(Ebadarg);
- setsecret(&s->out, x, t);
- } else
- error(Ebadarg);
- outx:
- free(x);
- poperror();
- out:
- qunlock(&s->in.ctlq);
- qunlock(&s->out.q);
- poperror();
- return n;
- }
- 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;
- }
- Dev ssldevtab = {
- 'D',
- "ssl",
- devreset,
- sslinit,
- devshutdown,
- sslattach,
- sslwalk,
- sslstat,
- sslopen,
- devcreate,
- sslclose,
- sslread,
- sslbread,
- sslwrite,
- sslbwrite,
- devremove,
- sslwstat,
- };
- 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("ssl 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 */
- if(devtab[c->type] == &ssldevtab){
- cclose(c);
- error("cannot ssl encrypt devssl files");
- }
- return c;
- }
- /* hand up a digest connection */
- static void
- sslhangup(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)
- {
- int i;
- Dstate *ret;
- if(waserror()) {
- unlock(&dslock);
- nexterror();
- }
- lock(&dslock);
- ret = nil;
- for(i=0; i<Maxdstate; i++){
- if(dstate[i] == nil){
- dsnew(ch, &dstate[i]);
- ret = dstate[i];
- break;
- }
- }
- unlock(&dslock);
- poperror();
- return ret;
- }
- 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;
- kstrdup(&s->user, up->user);
- s->perm = 0660;
- t = TYPE(ch->qid);
- if(t == Qclonus)
- t = Qctl;
- ch->qid.path = QID(pp - dstate, t);
- ch->qid.vers = 0;
- }
|