12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505 |
- /*
- * 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= 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**, 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){
- 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;
- }
- 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);
- b->rp += 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);
- b->rp += 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);
- 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(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)
- free(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->slen >= 16)
- setupDESstate(w->state, key, w->secret+8);
- else if(w->slen >= 8)
- setupDESstate(w->state, key, 0);
- else
- error("secret too short");
- }
- static void
- initRC4key(OneWay *w)
- {
- if(w->state){
- free(w->state);
- w->state = 0;
- }
- w->state = smalloc(sizeof(RC4state));
- setupRC4state(w->state, w->secret, w->slen);
- }
- /*
- * 40 bit RC4 is the same as n-bit RC4. However,
- * we ignore all but the first 40 bits of the key.
- */
- static void
- initRC4key_40(OneWay *w)
- {
- if(w->state){
- free(w->state);
- w->state = 0;
- }
- if(w->slen > 5)
- w->slen = 5;
- w->state = malloc(sizeof(RC4state));
- setupRC4state(w->state, w->secret, w->slen);
- }
- /*
- * 128 bit RC4 is the same as n-bit RC4. However,
- * we ignore all but the first 128 bits of the key.
- */
- static void
- initRC4key_128(OneWay *w)
- {
- if(w->state){
- free(w->state);
- w->state = 0;
- }
- if(w->slen > 16)
- w->slen = 16;
- w->state = malloc(sizeof(RC4state));
- setupRC4state(w->state, w->secret, w->slen);
- }
- typedef struct Hashalg Hashalg;
- struct Hashalg
- {
- char *name;
- int diglen;
- DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
- };
- Hashalg hashtab[] =
- {
- { "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;
- 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(strcmp(buf, "fd") == 0){
- s->c = buftochan(p);
- /* default is clear (msg delimiters only) */
- s->state = Sclear;
- s->blocklen = 1;
- s->diglen = 0;
- s->maxpad = s->max = (1<<15) - s->diglen - 1;
- s->in.mid = 0;
- s->out.mid = 0;
- } else if(strcmp(buf, "alg") == 0 && p != 0){
- s->blocklen = 1;
- s->diglen = 0;
- if(s->c == 0)
- error("must set fd before algorithm");
- s->state = Sclear;
- s->maxpad = s->max = (1<<15) - s->diglen - 1;
- if(strcmp(p, "clear") == 0){
- goto out;
- }
- if(s->in.secret && s->out.secret == 0)
- setsecret(&s->out, s->in.secret, s->in.slen);
- if(s->out.secret && s->in.secret == 0)
- setsecret(&s->in, s->out.secret, s->out.slen);
- if(s->in.secret == 0 || s->out.secret == 0)
- error("algorithm but no secret");
- s->hf = 0;
- s->encryptalg = Noencryption;
- s->blocklen = 1;
- for(;;){
- np = strchr(p, ' ');
- if(np)
- *np++ = 0;
- if(parsehashalg(p, s) < 0)
- if(parseencryptalg(p, s) < 0)
- error("bad algorithm");
- if(np == 0)
- break;
- p = np;
- }
- if(s->hf == 0 && s->encryptalg == Noencryption)
- error("bad algorithm");
- if(s->blocklen != 1){
- s->max = (1<<15) - s->diglen - 1;
- s->max -= s->max % s->blocklen;
- s->maxpad = (1<<14) - s->diglen - 1;
- s->maxpad -= s->maxpad % s->blocklen;
- } else
- s->maxpad = s->max = (1<<15) - s->diglen - 1;
- } else if(strcmp(buf, "secretin") == 0 && p != 0) {
- m = (strlen(p)*3)/2;
- x = smalloc(m);
- t = dec64(x, m, p, strlen(p));
- setsecret(&s->in, x, t);
- free(x);
- } else if(strcmp(buf, "secretout") == 0 && p != 0) {
- m = (strlen(p)*3)/2 + 1;
- x = smalloc(m);
- t = dec64(x, m, 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 = {
- '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;
- }
|