📄 devssl.c
字号:
/* * devssl - secure sockets layer */#include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "error.h"#include "libsec.h"#define NOSPOOKS 1typedef 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 intsslgen(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; }}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 intsslstat(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 intsslwstat(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 voidsslclose(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 voidensure(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 voidconsume(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 bytesstatic voidregurgitate(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 longsslread(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 voidrandfill(uchar *buf, int len){ while(len-- > 0) *buf++ = nrand(256);}static longsslbwrite(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -