📄 devtls.c
字号:
/* * devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0 */#include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "error.h"#include "libsec.h"typedef struct OneWay OneWay;typedef struct Secret Secret;typedef struct TlsRec TlsRec;typedef struct TlsErrs TlsErrs;enum { Statlen= 1024, /* max. length of status or stats message */ /* buffer limits */ MaxRecLen = 1<<14, /* max payload length of a record layer message */ MaxCipherRecLen = MaxRecLen + 2048, RecHdrLen = 5, MaxMacLen = SHA1dlen, /* protocol versions we can accept */ TLSVersion = 0x0301, SSL3Version = 0x0300, ProtocolVersion = 0x0301, /* maximum version we speak */ MinProtoVersion = 0x0300, /* limits on version we accept */ MaxProtoVersion = 0x03ff, /* connection states */ SHandshake = 1 << 0, /* doing handshake */ SOpen = 1 << 1, /* application data can be sent */ SRClose = 1 << 2, /* remote side has closed down */ SLClose = 1 << 3, /* sent a close notify alert */ SAlert = 1 << 5, /* sending or sent a fatal alert */ SError = 1 << 6, /* some sort of error has occured */ SClosed = 1 << 7, /* it is all over */ /* record types */ RChangeCipherSpec = 20, RAlert, RHandshake, RApplication, SSL2ClientHello = 1, HSSL2ClientHello = 9, /* local convention; see tlshand.c */ /* alerts */ ECloseNotify = 0, EUnexpectedMessage = 10, EBadRecordMac = 20, EDecryptionFailed = 21, ERecordOverflow = 22, EDecompressionFailure = 30, EHandshakeFailure = 40, ENoCertificate = 41, EBadCertificate = 42, EUnsupportedCertificate = 43, ECertificateRevoked = 44, ECertificateExpired = 45, ECertificateUnknown = 46, EIllegalParameter = 47, EUnknownCa = 48, EAccessDenied = 49, EDecodeError = 50, EDecryptError = 51, EExportRestriction = 60, EProtocolVersion = 70, EInsufficientSecurity = 71, EInternalError = 80, EUserCanceled = 90, ENoRenegotiation = 100, EMAX = 256};struct Secret{ char *encalg; /* name of encryption alg */ char *hashalg; /* name of hash alg */ int (*enc)(Secret*, uchar*, int); int (*dec)(Secret*, uchar*, int); int (*unpad)(uchar*, int, int); DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); int block; /* encryption block len, 0 if none */ int maclen; void *enckey; uchar mackey[MaxMacLen];};struct OneWay{ QLock io; /* locks io access */ QLock seclock; /* locks secret paramaters */ ulong seq; Secret *sec; /* cipher in use */ Secret *new; /* cipher waiting for enable */};struct TlsRec{ Chan *c; /* io channel */ int ref; /* serialized by tdlock for atomic destroy */ int version; /* version of the protocol we are speaking */ char verset; /* version has been set */ char opened; /* opened command every issued? */ char err[ERRMAX]; /* error message to return to handshake requests */ vlong handin; /* bytes communicated by the record layer */ vlong handout; vlong datain; vlong dataout; Lock statelk; int state; int debug; /* record layer mac functions for different protocol versions */ void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); /* input side -- protected by in.io */ OneWay in; Block *processed; /* next bunch of application data */ Block *unprocessed; /* data read from c but not parsed into records */ /* handshake queue */ Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */ int hqref; Queue *handq; /* queue of handshake messages */ Block *hprocessed; /* remainder of last block read from handq */ QLock hqread; /* protects reads for hprocessed, handq */ /* output side */ OneWay out; /* protections */ char *user; int perm;};struct TlsErrs{ int err; int sslerr; int tlserr; int fatal; char *msg;};static TlsErrs tlserrs[] = { {ECloseNotify, ECloseNotify, ECloseNotify, 0, "close notify"}, {EUnexpectedMessage, EUnexpectedMessage, EUnexpectedMessage, 1, "unexpected message"}, {EBadRecordMac, EBadRecordMac, EBadRecordMac, 1, "bad record mac"}, {EDecryptionFailed, EIllegalParameter, EDecryptionFailed, 1, "decryption failed"}, {ERecordOverflow, EIllegalParameter, ERecordOverflow, 1, "record too long"}, {EDecompressionFailure, EDecompressionFailure, EDecompressionFailure, 1, "decompression failed"}, {EHandshakeFailure, EHandshakeFailure, EHandshakeFailure, 1, "could not negotiate acceptable security parameters"}, {ENoCertificate, ENoCertificate, ECertificateUnknown, 1, "no appropriate certificate available"}, {EBadCertificate, EBadCertificate, EBadCertificate, 1, "corrupted or invalid certificate"}, {EUnsupportedCertificate, EUnsupportedCertificate, EUnsupportedCertificate, 1, "unsupported certificate type"}, {ECertificateRevoked, ECertificateRevoked, ECertificateRevoked, 1, "revoked certificate"}, {ECertificateExpired, ECertificateExpired, ECertificateExpired, 1, "expired certificate"}, {ECertificateUnknown, ECertificateUnknown, ECertificateUnknown, 1, "unacceptable certificate"}, {EIllegalParameter, EIllegalParameter, EIllegalParameter, 1, "illegal parameter"}, {EUnknownCa, EHandshakeFailure, EUnknownCa, 1, "unknown certificate authority"}, {EAccessDenied, EHandshakeFailure, EAccessDenied, 1, "access denied"}, {EDecodeError, EIllegalParameter, EDecodeError, 1, "error decoding message"}, {EDecryptError, EIllegalParameter, EDecryptError, 1, "error decrypting message"}, {EExportRestriction, EHandshakeFailure, EExportRestriction, 1, "export restriction violated"}, {EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"}, {EInsufficientSecurity, EHandshakeFailure, EInsufficientSecurity, 1, "stronger security routines required"}, {EInternalError, EHandshakeFailure, EInternalError, 1, "internal error"}, {EUserCanceled, ECloseNotify, EUserCanceled, 0, "handshake canceled by user"}, {ENoRenegotiation, EUnexpectedMessage, ENoRenegotiation, 0, "no renegotiation"},};enum{ /* max. open tls connections */ MaxTlsDevs = 1024};static Lock tdlock;static int tdhiwat;static int maxtlsdevs = 128;static TlsRec **tlsdevs;static char **trnames;static char *encalgs;static char *hashalgs;enum{ Qtopdir = 1, /* top level directory */ Qprotodir, Qclonus, Qencalgs, Qhashalgs, Qconvdir, /* directory for a conversation */ Qdata, Qctl, Qhand, Qstatus, Qstats,};#define TYPE(x) ((x).path & 0xf)#define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1))#define QID(c, y) (((c)<<5) | (y))static void checkstate(TlsRec *, int, int);static void ensure(TlsRec*, Block**, int);static void consume(Block**, uchar*, int);static Chan* buftochan(char*);static void tlshangup(TlsRec*);static void tlsError(TlsRec*, char *);static void alertHand(TlsRec*, char *);static TlsRec *newtls(Chan *c);static TlsRec *mktlsrec(void);static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static void sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);static void tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);static void put64(uchar *p, vlong x);static void put32(uchar *p, u32int);static void put24(uchar *p, int);static void put16(uchar *p, int);static int get16(uchar *p);static void tlsSetState(TlsRec *tr, int new, int old);static void rcvAlert(TlsRec *tr, int err);static void sendAlert(TlsRec *tr, int err);static void rcvError(TlsRec *tr, int err, char *msg, ...);static int rc4enc(Secret *sec, uchar *buf, int n);static int des3enc(Secret *sec, uchar *buf, int n);static int des3dec(Secret *sec, uchar *buf, int n);static int noenc(Secret *sec, uchar *buf, int n);static int sslunpad(uchar *buf, int n, int block);static int tlsunpad(uchar *buf, int n, int block);static void freeSec(Secret *sec);static char *tlsstate(int s);static void pdump(int, void*, char*);static char *tlsnames[] = {[Qclonus] "clone",[Qencalgs] "encalgs",[Qhashalgs] "hashalgs",[Qdata] "data",[Qctl] "ctl",[Qhand] "hand",[Qstatus] "status",[Qstats] "stats",};static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };static inttlsgen(Chan *c, char *_, Dirtab *__, int ___, int s, Dir *dp){ Qid q; TlsRec *tr; char *name, *nm; int perm, t; q.vers = 0; q.type = QTFILE; t = TYPE(c->qid); switch(t) { case Qtopdir: if(s == DEVDOTDOT){ q.path = QID(0, Qtopdir); q.type = QTDIR; devdir(c, q, "#a", 0, eve, 0555, dp); return 1; } if(s > 0) return -1; q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "tls", 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 < 3){ switch(s) { default: return -1; case 0: q.path = QID(0, Qclonus); break; case 1: q.path = QID(0, Qencalgs); break; case 2: q.path = QID(0, Qhashalgs); break; } perm = 0444; if(TYPE(q) == Qclonus) perm = 0555; devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp); return 1; } s -= 3; if(s >= tdhiwat) return -1; q.path = QID(s, Qconvdir); q.type = QTDIR; lock(&tdlock); tr = tlsdevs[s]; if(tr != nil) nm = tr->user; else nm = eve; if((name = trnames[s]) == nil){ name = trnames[s] = smalloc(16); sprint(name, "%d", s); } devdir(c, q, name, 0, nm, 0555, dp); unlock(&tdlock); return 1; case Qconvdir: if(s == DEVDOTDOT){ q.path = QID(0, Qprotodir); q.type = QTDIR; devdir(c, q, "tls", 0, eve, 0555, dp); return 1; } if(s < 0 || s >= nelem(convdir)) return -1; lock(&tdlock); tr = tlsdevs[CONV(c->qid)]; if(tr != nil){ nm = tr->user; perm = tr->perm; }else{ perm = 0; nm = eve; } t = convdir[s]; if(t == Qstatus || t == Qstats) perm &= 0444; q.path = QID(CONV(c->qid), t); devdir(c, q, tlsnames[t], 0, nm, perm, dp); unlock(&tdlock); return 1; case Qclonus: case Qencalgs: case Qhashalgs: perm = 0444; if(t == Qclonus) perm = 0555; devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp); return 1; default: lock(&tdlock); tr = tlsdevs[CONV(c->qid)]; if(tr != nil){ nm = tr->user; perm = tr->perm; }else{ perm = 0; nm = eve; } if(t == Qstatus || t == Qstats) perm &= 0444; devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp); unlock(&tdlock); return 1; }}static Chan*tlsattach(char *spec){ Chan *c; c = devattach('a', spec); c->qid.path = QID(0, Qtopdir); c->qid.type = QTDIR; c->qid.vers = 0; return c;}static Walkqid*tlswalk(Chan *c, Chan *nc, char **name, int nname){ return devwalk(c, nc, name, nname, nil, 0, tlsgen);}static inttlsstat(Chan *c, uchar *db, int n){ return devstat(c, db, n, nil, 0, tlsgen);}static Chan*tlsopen(Chan *c, int omode){ TlsRec *tr, **pp; int t, perm; perm = 0; omode &= 3; switch(omode) { case OREAD: perm = 4; break; case OWRITE: perm = 2; break; case ORDWR: perm = 6; break; } t = TYPE(c->qid); switch(t) { default: panic("tlsopen"); case Qtopdir: case Qprotodir: case Qconvdir: if(omode != OREAD) error(Eperm); break; case Qclonus: tr = newtls(c); if(tr == nil) error(Enodev); break; case Qctl: case Qdata: case Qhand: case Qstatus: case Qstats: if((t == Qstatus || t == Qstats) && omode != OREAD) error(Eperm); if(waserror()) { unlock(&tdlock); nexterror(); } lock(&tdlock); pp = &tlsdevs[CONV(c->qid)]; tr = *pp; if(tr == nil) error("must open connection using clone"); if((perm & (tr->perm>>6)) != perm && (strcmp(up->user, tr->user) != 0 || (perm & tr->perm) != perm)) error(Eperm); if(t == Qhand){ if(waserror()){ unlock(&tr->hqlock); nexterror(); } lock(&tr->hqlock); if(tr->handq != nil) error(Einuse); tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil); if(tr->handq == nil) error("cannot allocate handshake queue"); tr->hqref = 1; unlock(&tr->hqlock); poperror(); } tr->ref++; unlock(&tdlock); poperror(); break; case Qencalgs: case Qhashalgs: if(omode != OREAD) error(Eperm); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; c->iounit = qiomaxatomic; return c;}static inttlswstat(Chan *c, uchar *dp, int n){ Dir *d; TlsRec *tr; int rv; d = nil; if(waserror()){ free(d); unlock(&tdlock); nexterror(); } lock(&tdlock); tr = tlsdevs[CONV(c->qid)]; if(tr == nil) error(Ebadusefd); if(strcmp(tr->user, up->user) != 0) error(Eperm); d = smalloc(n + sizeof *d); rv = convM2D(dp, n, &d[0], (char*) &d[1]); if(rv == 0) error(Eshortstat); if(!emptystr(d->uid)) kstrdup(&tr->user, d->uid); if(d->mode != ~0UL) tr->perm = d->mode; free(d); poperror(); unlock(&tdlock); return rv;}static voiddechandq(TlsRec *tr){ lock(&tr->hqlock); if(--tr->hqref == 0){ if(tr->handq != nil){ qfree(tr->handq); tr->handq = nil; } if(tr->hprocessed != nil){ freeb(tr->hprocessed); tr->hprocessed = nil; } } unlock(&tr->hqlock);}static voidtlsclose(Chan *c){ TlsRec *tr; int t; t = TYPE(c->qid); switch(t) { case Qctl: case Qdata: case Qhand: case Qstatus: case Qstats: if((c->flag & COPEN) == 0) break; tr = tlsdevs[CONV(c->qid)]; if(tr == nil) break; if(t == Qhand) dechandq(tr); lock(&tdlock); if(--tr->ref > 0) { unlock(&tdlock); return; } tlsdevs[CONV(c->qid)] = nil; unlock(&tdlock); if(tr->c != nil && !waserror()){ checkstate(tr, 0, SOpen|SHandshake|SRClose); sendAlert(tr, ECloseNotify); poperror(); } tlshangup(tr); if(tr->c != nil) cclose(tr->c); freeSec(tr->in.sec); freeSec(tr->in.new); freeSec(tr->out.sec); freeSec(tr->out.new); free(tr->user); free(tr); break; }}/* * make sure we have at least 'n' bytes in list 'l' */static voidensure(TlsRec *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, MaxCipherRecLen + RecHdrLen, 0); if(bl == 0) error(Ehungup); *l = bl; i = 0; for(b = bl; b; b = b->next){ i += BLEN(b); l = &b->next; } if(i == 0) error(Ehungup); sofar += i; }if(s->debug) pprint("ensure read %d\n", sofar);}/* * 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 bytes */static voidregurgitate(TlsRec *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 */static Block*qgrab(Block **l, int n){ Block *bb, *b; int i; b = *l; if(BLEN(b) == n){ *l = b->next; b->next = nil; return b; } i = 0; for(bb = b; bb != nil && i < n; bb = bb->next) i += BLEN(bb); if(i > n) i = n; bb = allocb(i); consume(l, bb->wp, i); bb->wp += i; return bb;}static voidtlsclosed(TlsRec *tr, int new){ lock(&tr->statelk); if(tr->state == SOpen || tr->state == SHandshake) tr->state = new; else if((new | tr->state) == (SRClose|SLClose)) tr->state = SClosed; unlock(&tr->statelk); alertHand(tr, "close notify");}/* * read and process one tls record layer message * must be called with tr->in.io held * We can't let Eintrs lose data, since doing so will get * us out of sync with the sender and break the reliablity * of the channel. Eintr only happens during the reads in * consume. Therefore we put back any bytes consumed before * the last call to ensure. */static voidtlsrecread(TlsRec *tr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -