123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * 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 */
- uint8_t *secret; /* secret */
- uint32_t 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 */
- uint8_t state; /* state of connection */
- int ref; /* serialized by dslock for atomic destroy */
- uint8_t encryptalg; /* encryption algorithm */
- uint16_t blocklen; /* blocking length */
- uint16_t diglen; /* length of digest */
- DigestState *(*hf)(uint8_t*, uint32_t, uint8_t*, 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= 128, /* must be a power of 2 */
- };
- Lock dslock;
- int dshiwat;
- char *dsname[Maxdstate];
- Dstate *dstate[Maxdstate];
- char *encalgs;
- 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**, uint8_t*, int);
- static void setsecret(OneWay*, uint8_t*, 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 int32_t 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* j, 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){
- sprint(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 int32_t
- sslstat(Chan *c, uint8_t *db, int32_t 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 int32_t
- sslwstat(Chan *c, uint8_t *db, int32_t n)
- {
- Dir *dir;
- Dstate *s;
- int l;
- s = dstate[CONV(c->qid)];
- if(s == 0)
- error(Ebadusefd);
- if(strcmp(s->user, up->user) != 0)
- error(Eperm);
- dir = smalloc(sizeof(Dir)+n);
- l = convM2D(db, n, &dir[0], (char*)&dir[1]);
- if(l == 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 l;
- }
- 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 = s->c->dev->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, uint8_t *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, int32_t n, int64_t m)
- {
- Dstate * volatile s;
- Block *b;
- uint8_t 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 int32_t
- sslread(Chan *c, void *a, int32_t n, int64_t off)
- {
- Block * volatile b;
- Block *nb;
- uint8_t *va;
- int i;
- char buf[128];
- int32_t offset;
- int ft;
- if(c->qid.type & QTDIR)
- return devdirread(c, a, n, 0, 0, sslgen);
- ft = TYPE(c->qid);
- offset = off;
- switch(ft) {
- default:
- error(Ebadusefd);
- case Qctl:
- ft = CONV(c->qid);
- sprint(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(uint8_t *buf, int len)
- {
- while(len-- > 0)
- *buf++ = nrand(256);
- }
- static int32_t
- sslbwrite(Chan *c, Block *b, int64_t m)
- {
- Dstate * volatile s;
- int32_t 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 int32_t
- sslput(Dstate *s, Block * volatile b)
- {
- Block *nb;
- int h, n, l, pad, rv;
- uint8_t *p;
- int offset;
- if(waserror()){
- if(b != nil)
- freeb(b);
- nexterror();
- }
- rv = 0;
- while(b != nil){
- l = n = BLEN(b);
- h = s->diglen + 2;
- /* trim to maximum block size */
- pad = 0;
- if(l > s->max){
- l = s->max;
- } else if(s->blocklen != 1){
- pad = (l + s->diglen)%s->blocklen;
- if(pad){
- if(l > s->maxpad){
- pad = 0;
- l = s->maxpad;
- } else {
- pad = s->blocklen - pad;
- h++;
- }
- }
- }
- rv += l;
- if(l != n){
- nb = allocb(l + h + pad);
- memmove(nb->wp + h, b->rp, l);
- nb->wp += l + h;
- b->rp += l;
- } else {
- /* add header space */
- nb = padblock(b, h);
- b = 0;
- }
- l += s->diglen;
- /* SSL style count */
- if(pad){
- nb = padblock(nb, -pad);
- randfill(nb->wp, pad);
- nb->wp += pad;
- l += pad;
- p = nb->rp;
- p[0] = (l>>8);
- p[1] = l;
- p[2] = pad;
- offset = 3;
- } else {
- p = nb->rp;
- p[0] = (l>>8) | 0x80;
- p[1] = l;
- 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++;
- l = BLEN(nb);
- s->c->dev->bwrite(s->c, nb, s->c->offset);
- s->c->offset += l;
- }
- poperror();
- return rv;
- }
- static void
- setsecret(OneWay *w, uint8_t *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)
- {
- uint8_t 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 = smalloc(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 = smalloc(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 = smalloc(sizeof(RC4state));
- setupRC4state(w->state, w->secret, w->slen);
- }
- typedef struct Hashalg Hashalg;
- struct Hashalg
- {
- char *name;
- int diglen;
- DigestState *(*hf)(uint8_t*, uint32_t, uint8_t*, 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 int32_t
- sslwrite(Chan *c, void *a, int32_t n, int64_t m)
- {
- Dstate * volatile s;
- Block * volatile b;
- int l, t;
- char *p, *np, *e, buf[128];
- uint8_t *x;
- 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 {
- l = e - p;
- if(l > s->max)
- l = s->max;
- b = allocb(l);
- if(waserror()){
- freeb(b);
- nexterror();
- }
- memmove(b->wp, p, l);
- poperror();
- b->wp += l;
- sslput(s, b);
- p += l;
- } 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(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) {
- l = (strlen(p)*3)/2;
- x = smalloc(l);
- t = dec64(x, l, p, strlen(p));
- setsecret(&s->in, x, t);
- free(x);
- } else if(strcmp(buf, "secretout") == 0 && p != 0) {
- l = (strlen(p)*3)/2 + 1;
- x = smalloc(l);
- t = dec64(x, l, p, strlen(p));
- setsecret(&s->out, x, t);
- free(x);
- } else
- error(Ebadarg);
- 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 = {
- .dc = 'D',
- .name = "ssl",
- .reset = devreset,
- .init = sslinit,
- .shutdown = devshutdown,
- .attach = sslattach,
- .walk = sslwalk,
- .stat = sslstat,
- .open = sslopen,
- .create = devcreate,
- .close = sslclose,
- .read = sslread,
- .bread = sslbread,
- .write = sslwrite,
- .bwrite = sslbwrite,
- .remove = devremove,
- .wstat = sslwstat,
- };
- static Block*
- encryptb(Dstate *s, Block *b, int offset)
- {
- uint8_t *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;
- uint8_t *p, *ep, *tp, *ip, *eip;
- DESstate *ds;
- uint8_t 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)
- {
- uint8_t *p;
- DigestState ss;
- uint8_t msgid[4];
- uint32_t 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)
- {
- uint8_t *p;
- DigestState ss;
- uint8_t msgid[4];
- int n, h;
- OneWay *w;
- uint8_t 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(c->dev == &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;
- }
|