📄 devtls.c
字号:
{ OneWay *volatile in; Block *volatile b; uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen]; int volatile nconsumed; int len, type, ver, unpad_len; nconsumed = 0; if(waserror()){ if(strcmp(up->errstr, Eintr) == 0 && !waserror()){ regurgitate(tr, header, nconsumed); poperror(); }else tlsError(tr, "channel error"); nexterror(); } ensure(tr, &tr->unprocessed, RecHdrLen); consume(&tr->unprocessed, header, RecHdrLen);if(tr->debug)pprint("consumed %d header\n", RecHdrLen); nconsumed = RecHdrLen; if((tr->handin == 0) && (header[0] & 0x80)){ /* Cope with an SSL3 ClientHello expressed in SSL2 record format. This is sent by some clients that we must interoperate with, such as Java's JSSE and Microsoft's Internet Explorer. */ len = (get16(header) & ~0x8000) - 3; type = header[2]; ver = get16(header + 3); if(type != SSL2ClientHello || len < 22) rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message"); }else{ /* normal SSL3 record format */ type = header[0]; ver = get16(header+1); len = get16(header+3); } if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion)) rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'", tr->version, tr->verset?"/set":"", len, type, ver, (char*)header); if(len > MaxCipherRecLen || len < 0) rcvError(tr, ERecordOverflow, "record message too long %d", len); ensure(tr, &tr->unprocessed, len); nconsumed = 0; poperror(); /* * If an Eintr happens after this, we'll get out of sync. * Make sure nothing we call can sleep. * Errors are ok, as they kill the connection. * Luckily, allocb won't sleep, it'll just error out. */ b = nil; if(waserror()){ if(b != nil) freeb(b); tlsError(tr, "channel error"); nexterror(); } b = qgrab(&tr->unprocessed, len);if(tr->debug) pprint("consumed unprocessed %d\n", len); in = &tr->in; if(waserror()){ qunlock(&in->seclock); nexterror(); } qlock(&in->seclock); p = b->rp; if(in->sec != nil) { /* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here should look alike, including timing of the response. */ unpad_len = (*in->sec->dec)(in->sec, p, len); if(unpad_len >= in->sec->maclen) len = unpad_len - in->sec->maclen;if(tr->debug) pprint("decrypted %d\n", unpad_len);if(tr->debug) pdump(unpad_len, p, "decrypted:"); /* update length */ put16(header+3, len); put64(seq, in->seq); in->seq++; (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac); if(unpad_len < in->sec->maclen) rcvError(tr, EBadRecordMac, "short record mac"); if(memcmp(hmac, p+len, in->sec->maclen) != 0) rcvError(tr, EBadRecordMac, "record mac mismatch"); b->wp = b->rp + len; } qunlock(&in->seclock); poperror(); if(len < 0) rcvError(tr, EDecodeError, "runt record message"); switch(type) { default: rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type); break; case RChangeCipherSpec: if(len != 1 || p[0] != 1) rcvError(tr, EDecodeError, "invalid change cipher spec"); qlock(&in->seclock); if(in->new == nil){ qunlock(&in->seclock); rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec"); } freeSec(in->sec); in->sec = in->new; in->new = nil; in->seq = 0; qunlock(&in->seclock); break; case RAlert: if(len != 2) rcvError(tr, EDecodeError, "invalid alert"); if(p[0] == 2) rcvAlert(tr, p[1]); if(p[0] != 1) rcvError(tr, EIllegalParameter, "invalid alert fatal code"); /* * propate non-fatal alerts to handshaker */ if(p[1] == ECloseNotify) { tlsclosed(tr, SRClose); if(tr->opened) error("tls hungup"); error("close notify"); } if(p[1] == ENoRenegotiation) alertHand(tr, "no renegotiation"); else if(p[1] == EUserCanceled) alertHand(tr, "handshake canceled by user"); else rcvError(tr, EIllegalParameter, "invalid alert code"); break; case RHandshake: /* * don't worry about dropping the block * qbwrite always queues even if flow controlled and interrupted. * * if there isn't any handshaker, ignore the request, * but notify the other side we are doing so. */ lock(&tr->hqlock); if(tr->handq != nil){ tr->hqref++; unlock(&tr->hqlock); if(waserror()){ dechandq(tr); nexterror(); } b = padblock(b, 1); *b->rp = RHandshake; qbwrite(tr->handq, b); b = nil; poperror(); dechandq(tr); }else{ unlock(&tr->hqlock); if(tr->verset && tr->version != SSL3Version && !waserror()){ sendAlert(tr, ENoRenegotiation); poperror(); } } break; case SSL2ClientHello: lock(&tr->hqlock); if(tr->handq != nil){ tr->hqref++; unlock(&tr->hqlock); if(waserror()){ dechandq(tr); nexterror(); } /* Pass the SSL2 format data, so that the handshake code can compute the correct checksums. HSSL2ClientHello = HandshakeType 9 is unused in RFC2246. */ b = padblock(b, 8); b->rp[0] = RHandshake; b->rp[1] = HSSL2ClientHello; put24(&b->rp[2], len+3); b->rp[5] = SSL2ClientHello; put16(&b->rp[6], ver); qbwrite(tr->handq, b); b = nil; poperror(); dechandq(tr); }else{ unlock(&tr->hqlock); if(tr->verset && tr->version != SSL3Version && !waserror()){ sendAlert(tr, ENoRenegotiation); poperror(); } } break; case RApplication: if(!tr->opened) rcvError(tr, EUnexpectedMessage, "application message received before handshake completed"); if(BLEN(b) > 0){ tr->processed = b; b = nil; } break; } if(b != nil) freeb(b); poperror();}/* * got a fatal alert message */static voidrcvAlert(TlsRec *tr, int err){ char *s; int i; s = "unknown error"; for(i=0; i < nelem(tlserrs); i++){ if(tlserrs[i].err == err){ s = tlserrs[i].msg; break; } }if(tr->debug) pprint("rcvAlert: %s\n", s); tlsError(tr, s); if(!tr->opened) error(s); error("tls error");}/* * found an error while decoding the input stream */static voidrcvError(TlsRec *tr, int err, char *fmt, ...){ char msg[ERRMAX]; va_list arg; va_start(arg, fmt); vseprint(msg, msg+sizeof(msg), fmt, arg); va_end(arg);if(tr->debug) pprint("rcvError: %s\n", msg); sendAlert(tr, err); if(!tr->opened) error(msg); error("tls error");}/* * make sure the next hand operation returns with a 'msg' error */static voidalertHand(TlsRec *tr, char *msg){ Block *b; int n; lock(&tr->hqlock); if(tr->handq == nil){ unlock(&tr->hqlock); return; } tr->hqref++; unlock(&tr->hqlock); n = strlen(msg); if(waserror()){ dechandq(tr); nexterror(); } b = allocb(n + 2); *b->wp++ = RAlert; memmove(b->wp, msg, n + 1); b->wp += n + 1; qbwrite(tr->handq, b); poperror(); dechandq(tr);}static voidcheckstate(TlsRec *tr, int ishand, int ok){ int state; lock(&tr->statelk); state = tr->state; unlock(&tr->statelk); if(state & ok) return; switch(state){ case SHandshake: case SOpen: break; case SError: case SAlert: if(ishand) error(tr->err); error("tls error"); case SRClose: case SLClose: case SClosed: error("tls hungup"); } error("tls improperly configured");}static Block*tlsbread(Chan *c, long n, ulong offset){ int ty; Block *b; TlsRec *volatile tr; ty = TYPE(c->qid); switch(ty) { default: return devbread(c, n, offset); case Qhand: case Qdata: break; } tr = tlsdevs[CONV(c->qid)]; if(tr == nil) panic("tlsbread"); if(waserror()){ qunlock(&tr->in.io); nexterror(); } qlock(&tr->in.io); if(ty == Qdata){ checkstate(tr, 0, SOpen); while(tr->processed == nil) tlsrecread(tr); /* return at most what was asked for */ b = qgrab(&tr->processed, n);if(tr->debug) pprint("consumed processed %d\n", BLEN(b));if(tr->debug) pdump(BLEN(b), b->rp, "consumed:"); qunlock(&tr->in.io); poperror(); tr->datain += BLEN(b); }else{ checkstate(tr, 1, SOpen|SHandshake|SLClose); /* * it's ok to look at state without the lock * since it only protects reading records, * and we have that tr->in.io held. */ while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq)) tlsrecread(tr); qunlock(&tr->in.io); poperror(); if(waserror()){ qunlock(&tr->hqread); nexterror(); } qlock(&tr->hqread); if(tr->hprocessed == nil){ b = qbread(tr->handq, MaxRecLen + 1); if(*b->rp++ == RAlert){ kstrcpy(up->errstr, (char*)b->rp, ERRMAX); freeb(b); nexterror(); } tr->hprocessed = b; } b = qgrab(&tr->hprocessed, n); poperror(); qunlock(&tr->hqread); tr->handin += BLEN(b); } return b;}static longtlsread(Chan *c, void *a, long n, vlong off){ Block *volatile b; Block *nb; uchar *va; int i, ty; char *buf, *s, *e; ulong offset = off; TlsRec * tr; if(c->qid.type & QTDIR) return devdirread(c, a, n, 0, 0, tlsgen); tr = tlsdevs[CONV(c->qid)]; ty = TYPE(c->qid); switch(ty) { default: error(Ebadusefd); case Qstatus: buf = smalloc(Statlen); qlock(&tr->in.seclock); qlock(&tr->out.seclock); s = buf; e = buf + Statlen; s = seprint(s, e, "State: %s\n", tlsstate(tr->state)); s = seprint(s, e, "Version: 0x%x\n", tr->version); if(tr->in.sec != nil) s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg); if(tr->in.new != nil) s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg); if(tr->out.sec != nil) s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg); if(tr->out.new != nil) seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg); qunlock(&tr->in.seclock); qunlock(&tr->out.seclock); n = readstr(offset, a, n, buf); free(buf); return n; case Qstats: buf = smalloc(Statlen); s = buf; e = buf + Statlen; s = seprint(s, e, "DataIn: %lld\n", tr->datain); s = seprint(s, e, "DataOut: %lld\n", tr->dataout); s = seprint(s, e, "HandIn: %lld\n", tr->handin); seprint(s, e, "HandOut: %lld\n", tr->handout); n = readstr(offset, a, n, buf); free(buf); return n; case Qctl: buf = smalloc(Statlen); snprint(buf, Statlen, "%llud", CONV(c->qid)); n = readstr(offset, a, n, buf); free(buf); return n; case Qdata: case Qhand: b = tlsbread(c, n, offset); break; case Qencalgs: return readstr(offset, a, n, encalgs); case Qhashalgs: return readstr(offset, a, n, hashalgs); } 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;}/* * write a block in tls records */static voidtlsrecwrite(TlsRec *tr, int type, Block *b){ Block *volatile bb; Block *nb; uchar *p, seq[8]; OneWay *volatile out; int n, maclen, pad, ok; out = &tr->out; bb = b; if(waserror()){ qunlock(&out->io); if(bb != nil) freeb(bb); nexterror(); } qlock(&out->io);if(tr->debug)pprint("send %d\n", BLEN(b));if(tr->debug)pdump(BLEN(b), b->rp, "sent:"); ok = SHandshake|SOpen|SRClose; if(type == RAlert) ok |= SAlert; while(bb != nil){ checkstate(tr, type != RApplication, ok); /* * get at most one maximal record's input, * with padding on the front for header and * back for mac and maximal block padding. */ if(waserror()){ qunlock(&out->seclock); nexterror(); } qlock(&out->seclock); maclen = 0; pad = 0; if(out->sec != nil){ maclen = out->sec->maclen; pad = maclen + out->sec->block; } n = BLEN(bb); if(n > MaxRecLen){ n = MaxRecLen; nb = allocb(n + pad + RecHdrLen); memmove(nb->wp + RecHdrLen, bb->rp, n); bb->rp += n; }else{ /* * carefully reuse bb so it will get freed if we're out of memory */ bb = padblock(bb, RecHdrLen); if(pad) nb = padblock(bb, -pad); else nb = bb; bb = nil; } p = nb->rp; p[0] = type; put16(p+1, tr->version); put16(p+3, n); if(out->sec != nil){ put64(seq, out->seq); out->seq++; (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n); n += maclen; /* encrypt */ n = (*out->sec->enc)(out->sec, p + RecHdrLen, n); nb->wp = p + RecHdrLen + n; /* update length */ put16(p+3, n); } if(type == RChangeCipherSpec){ if(out->new == nil) error("change cipher without a new cipher"); freeSec(out->sec); out->sec = out->new; out->new = nil; out->seq = 0; } qunlock(&out->seclock); poperror(); /* * if bwrite error's, we assume the block is queued. * if not, we're out of sync with the receiver and will not recover. */ if(waserror()){ if(strcmp(up->errstr, "interrupted") != 0) tlsError(tr, "channel error"); nexterror(); } devtab[tr->c->type]->bwrite(tr->c, nb, 0); poperror(); } qunlock(&out->io); poperror();}static longtlsbwrite(Chan *c, Block *b, ulong offset){ int ty; ulong n; TlsRec *tr; n = BLEN(b); tr = tlsdevs[CONV(c->qid)]; if(tr == nil) panic("tlsbread"); ty = TYPE(c->qid); switch(ty) { default: return devbwrite(c, b, offset); case Qhand: tlsrecwrite(tr, RHandshake, b); tr->handout += n; break; case Qdata: checkstate(tr, 0, SOpen); tlsrecwrite(tr, RApplication, b); tr->dataout += n; break; } return n;}typedef struct Hashalg Hashalg;struct Hashalg{ char *name; int maclen; void (*initkey)(Hashalg *, int, Secret *, uchar*);};static voidinitmd5key(Hashalg *ha, int version, Secret *s, uchar *p){ s->maclen = ha->maclen; if(version == SSL3Version) s->mac = sslmac_md5; else s->mac = hmac_md5; memmove(s->mackey, p, ha->maclen);}static voidinitclearmac(Hashalg *h, int i, Secret *s, uchar *u){ s->maclen = 0; s->mac = nomac;}static voidinitsha1key(Hashalg *ha, int version, Secret *s, uchar *p){ s->maclen = ha->maclen; if(version == SSL3Version) s->mac = sslmac_sha1; else s->mac = hmac_sha1; memmove(s->mackey, p, ha->maclen);}static Hashalg hashtab[] ={ { "clear", 0, initclearmac, }, { "md5", MD5dlen, initmd5key, }, { "sha1", SHA1dlen, initsha1key, }, { 0 }};static Hashalg*parsehashalg(char *p){ Hashalg *ha; for(ha = hashtab; ha->name; ha++) if(strcmp(p, ha->name) == 0) return ha; error("unsupported hash algorithm"); return nil;}typedef struct Encalg Encalg;struct Encalg{ char *name; int keylen; int ivlen; void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*);};static voidinitRC4key(Encalg *ea, Secret *s, uchar *p, uchar *u){ s->enckey = smalloc(sizeof(RC4state)); s->enc = rc4enc; s->dec = rc4enc; s->block = 0; setupRC4state(s->enckey, p, ea->keylen);}static voidinitDES3key(Encalg *ea, Secret *s, uchar *p, uchar *iv){ s->enckey = smalloc(sizeof(DES3state)); s->enc = des3enc; s->dec = des3dec; s->block = 8; setupDES3state(s->enckey, (uchar(*)[8])p, iv);}static voidinitclearenc(Encalg *ea, Secret *s, uchar *u, uchar *uu){ s->enc = noenc; s->dec = noenc; s->block = 0;}static Encalg encrypttab[] ={ { "clear", 0, 0, initclearenc }, { "rc4_128", 128/8, 0, initRC4key }, { "3des_ede_cbc", 3 * 8, 8, initDES3key }, { 0 }};static Encalg*parseencalg(char *p){ Encalg *ea; for(ea = encrypttab; ea->name; ea++) if(strcmp(p, ea->name) == 0) return ea; error("unsupported encryption algorithm"); return nil;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -