From 5e14454e3445b374009a0ce4928095fda8cbc7a7 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 3 Apr 2001 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2001-04-03 --- alphapc/ether2114x.c | 154 +++++++-- ip/devip.c | 43 ++- port/devtls.c | 786 ++++++++++++++++++++----------------------- 3 files changed, 540 insertions(+), 443 deletions(-) diff --git a/alphapc/ether2114x.c b/alphapc/ether2114x.c index 40f931193657d37b1d4d15beac9cf50381cbd240..a70500a0461ef05978e412255b13c65bd4a05e41 100644 --- a/alphapc/ether2114x.c +++ b/alphapc/ether2114x.c @@ -19,27 +19,30 @@ #include "etherif.h" -#define DEBUG 0 +#define DEBUG (1) #define debug if(DEBUG)print enum { Nrde = 64, Ntde = 64, - - Rxbufsz = ROUNDUP(sizeof(Etherpkt)+4, 4), }; +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + enum { /* CRS0 - Bus Mode */ Swr = 0x00000001, /* Software Reset */ Bar = 0x00000002, /* Bus Arbitration */ - Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + DslMASK = 0x0000007C, /* Descriptor Skip Length */ + DslSHIFT = 2, Ble = 0x00000080, /* Big/Little Endian */ - Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + PblMASK = 0x00003F00, /* Programmable Burst Length (field) */ + PblSHIFT = 8, Cal = 0x0000C000, /* Cache Alignment (field) */ Cal8 = 0x00004000, /* 8 longword boundary alignment */ Cal16 = 0x00008000, /* 16 longword boundary alignment */ Cal32 = 0x0000C000, /* 32 longword boundary alignment */ - Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + TapMASK = 0x000E0000, /* Transmit Automatic Polling */ + TapSHIFT = 17, Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ Rml = 0x00200000, /* Read Multiple */ }; @@ -183,12 +186,20 @@ enum { /* PHY registers */ Aner = 6, /* Auto-Negotiation Expansion */ }; +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, +}; + typedef struct Ctlr Ctlr; typedef struct Ctlr { int port; Pcidev* pcidev; Ctlr* next; int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ uchar srom[128]; uchar* sromea; /* MAC address */ @@ -457,7 +468,7 @@ interrupt(Ureg*, void* arg) if(des->status & De) ctlr->de++; } - else if(bp = iallocb(Rxbufsz)){ + else if(bp = iallocb(Rbsz)){ len = ((des->status & Fl)>>16)-4; des->bp->wp = des->bp->rp+len; etheriq(ether, des->bp, 1); @@ -466,7 +477,7 @@ interrupt(Ureg*, void* arg) } des->control &= Er; - des->control |= Rxbufsz; + des->control |= Rbsz; coherence(); des->status = Own; @@ -566,11 +577,11 @@ ctlrinit(Ether* ether) * create and post a setup packet to initialise * the physical ethernet address. */ - ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0); + ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ - des->bp = allocb(Rxbufsz); + des->bp = allocb(Rbsz); des->status = Own; - des->control = Rxbufsz; + des->control = Rbsz; des->addr = PCIWADDR(des->bp->rp); } ctlr->rdr[ctlr->nrdr-1].control |= Er; @@ -583,7 +594,12 @@ ctlrinit(Ether* ether) ctlr->tdri = 0; csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); csr32w(ctlr, 7, ctlr->mask); ctlr->csr6 |= St; csr32w(ctlr, 6, ctlr->csr6); @@ -654,7 +670,20 @@ miimdo(Ctlr* ctlr, int bits, int n) static int miir(Ctlr* ctlr, int phyad, int regad) { - int data; + int data, i; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } /* * Preamble; @@ -692,6 +721,17 @@ sromr(Ctlr* ctlr, int r) { int i, op, data; + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + return csr32r(ctlr, 9) & 0xFFFF; + } + /* * This sequence for reading a 16-bit register 'r' * in the EEPROM is taken straight from Section @@ -739,7 +779,7 @@ softreset(Ctlr* ctlr) */ csr32w(ctlr, 0, Swr); microdelay(10); - csr32w(ctlr, 0, Rml|Cal16); + csr32w(ctlr, 0, Rml|Cal16|(32<pcidev->did == 0x0009){ + if(ctlr->id != Tulip3){ for(i = 0; i < len; i++){ csr32w(ctlr, 12, *block); block++; @@ -817,8 +857,17 @@ typephylink(Ctlr* ctlr, uchar*) } else if((bmcr & 0x2100) == 0x2100) x = 0x4000; - else if(bmcr & 0x2000) - x = 0x2000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } else if(bmcr & 0x0100) x = 0x1000; else @@ -949,7 +998,8 @@ type0mode(Ctlr* ctlr, uchar* block, int wait) { int csr6, m, timeo; - csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; +// csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; + csr6 = Mbo|Hbd|Sb|TrMODE; debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); switch(block[0]){ @@ -1132,6 +1182,30 @@ static uchar* leaf21140[] = { nil, }; +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + static int srom(Ctlr* ctlr) { @@ -1172,6 +1246,19 @@ srom(Ctlr* ctlr) } } + /* + * Fake up the SROM for the PNIC. + * It looks like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Eaddrlen; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + /* * Next, try to find the info leaf in the SROM for media detection. * If it's a non-conforming card try to match the vendor ethernet code @@ -1205,7 +1292,7 @@ srom(Ctlr* ctlr) ctlr->leaf = p; ctlr->sct = *p++; ctlr->sct |= *p++<<8; - if(ctlr->pcidev->did == 0x0009){ + if(ctlr->id != Tulip3){ csr32w(ctlr, 12, Gpc|*p++); delay(200); } @@ -1220,7 +1307,7 @@ srom(Ctlr* ctlr) * The RAMIX PMC665 has a badly-coded SROM, * hence the test for 21143 and type 3. */ - if((*p & 0x80) || (ctlr->pcidev->did == 0x0019 && *(p+1) == 3)){ + if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ *p |= 0x80; if(*(p+1) == 1 || *(p+1) == 3) phy = 1; @@ -1271,12 +1358,14 @@ dec2114xpci(void) int x; p = nil; - while(p = pcimatch(p, 0x1011, 0)){ - switch(p->did){ + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ default: continue; - case 0x0019: /* 21143 */ + case Tulip3: /* 21143 */ /* * Exit sleep mode. */ @@ -1285,7 +1374,9 @@ dec2114xpci(void) pcicfgw32(p, 0x40, x); /*FALLTHROUGH*/ - case 0x0009: /* 21140 */ + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case Tulip0: /* 21140 */ break; } @@ -1296,9 +1387,10 @@ dec2114xpci(void) ctlr = malloc(sizeof(Ctlr)); ctlr->port = p->mem[0].bar & ~0x01; ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ - print("dec2114x: port %d in use\n", ctlr->port); + print("dec2114x: port 0x%uX in use\n", ctlr->port); free(ctlr); continue; } @@ -1312,7 +1404,20 @@ dec2114xpci(void) softreset(ctlr); if(srom(ctlr)){ + iofree(ctlr->port); free(ctlr); + continue; + } + + switch(ctlr->id){ + default: +// break; +// +// case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); break; } @@ -1424,5 +1529,6 @@ reset(Ether* ether) void ether2114xlink(void) { + addethercard("21140", reset); addethercard("2114x", reset); } diff --git a/ip/devip.c b/ip/devip.c index 38bfb2120909dc9c140c79fc35c6d5e33ec0dbef..c664792297c12d5dc29dbd7cb17936691076385c 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -82,9 +82,9 @@ ip3gen(Chan *c, int i, Dir *dp) devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp); return 1; case Qlisten: - p = "listen"; q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qlisten), 0}; - break; + devdir(c, q, "listen", qlen(cv->eq), cv->owner, cv->perm, dp); + return 1; case Qlocal: p = "local"; q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qlocal), 0}; @@ -98,7 +98,7 @@ ip3gen(Chan *c, int i, Dir *dp) q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qstatus), 0}; break; } - devdir(c, q, p, 0, cv->owner, 0666, dp); + devdir(c, q, p, 0, cv->owner, 0444, dp); return 1; } @@ -419,6 +419,14 @@ ipopen(Chan* c, int omode) break; case Qlisten: cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)]; + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(commonuser(), cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + + } + if(cv->state != Announced) error("not announced"); @@ -470,9 +478,34 @@ ipremove(Chan*) } static void -ipwstat(Chan*, char*) +ipwstat(Chan *c, char *dp) { - error(Eperm); + Dir d; + Conv *cv; + Fs *f; + Proto *p; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + default: + error(Eperm); + break; + case Qctl: + case Qdata: + break; + } + + convM2D(dp, &d); + + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(!iseve() && strcmp(commonuser(), cv->owner) != 0) + error(Eperm); + if(d.uid[0]){ + strncpy(cv->owner, d.uid, sizeof(cv->owner)); + cv->owner[sizeof(cv->owner)-1] = 0; + } + cv->perm = d.mode & 0777; } void diff --git a/port/devtls.c b/port/devtls.c index 0839093978255278f8db028b055ad67f8011be8a..4145c501f4f71fbf9ec5223a586283cf5133594b 100644 --- a/port/devtls.c +++ b/port/devtls.c @@ -10,18 +10,8 @@ #include -#define NOSPOOKS 1 - typedef struct OneWay OneWay; -enum -{ - - /* encryption algorithms */ - Noencryption= 0, - DESCBC= 1, - DESECB= 2, - RC4= 3 -}; +typedef struct Secret Secret; enum { MaxRecLen = 1<<14, /* max payload length of a record layer message */ @@ -79,19 +69,23 @@ enum { EMAX = 256 }; -struct OneWay -{ - QLock q; /* locks io access */ - QLock ctlq; /* locks one-way paramaters */ - - ulong seq; - int keyed; /* have key, waiting for cipher enable */ - int protected; /* cipher is enabled */ +struct Secret +{ + DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); + int maclen; RC4state rc4; uchar mackey[64]; }; +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 */ +}; typedef struct TlsRec TlsRec; struct TlsRec @@ -105,9 +99,7 @@ struct TlsRec int state; /* must be set using setstate */ /* record layer mac functions for different protocol versions */ - void (*packMac)(TlsRec*, uchar*, uchar*, uchar*, uchar*, int, uchar*); - DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); - int maclen; + void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); /* input side -- protected by in.q */ OneWay in; @@ -154,15 +146,13 @@ enum{ static void ensure(TlsRec*, Block**, int); static void consume(Block**, uchar*, int); -static void setsecret(OneWay*, uchar*, int); -static Block* encryptb(TlsRec*, Block*, int); -static Block* decryptb(TlsRec*, Block*); -static Block* digestb(TlsRec*, Block*, int); -static void checkdigestb(TlsRec*, Block*); static Chan* buftochan(char*); static void tlshangup(TlsRec*); static TlsRec* dsclone(Chan *c); static void dsnew(Chan *c, TlsRec **); +static DigestState*sslmac_md5(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); @@ -297,11 +287,32 @@ tlsstat(Chan *c, char *db) devstat(c, db, 0, 0, tlsgen); } +static void +closedown(Chan *c, TlsRec *tr) +{ + lock(&dslock); + if(--tr->ref > 0) { + unlock(&dslock); + return; + } + dstate[CONV(c->qid)] = nil; + unlock(&dslock); + + tlshangup(tr); + if(tr->c) + cclose(tr->c); + free(tr->in.sec); + free(tr->in.new); + free(tr->out.sec); + free(tr->out.new); + free(tr); +} + static Chan* tlsopen(Chan *c, int omode) { - TlsRec *s, **pp; - int perm; + TlsRec *tr, **pp; + int t, perm; perm = 0; omode &= 3; @@ -317,7 +328,8 @@ tlsopen(Chan *c, int omode) break; } - switch(TYPE(c->qid)) { + t = TYPE(c->qid); + switch(t) { default: panic("tlsopen"); case Qtopdir: @@ -327,8 +339,8 @@ tlsopen(Chan *c, int omode) error(Eperm); break; case Qclonus: - s = dsclone(c); - if(s == 0) + tr = dsclone(c); + if(tr == nil) error(Enodev); break; case Qctl: @@ -340,19 +352,33 @@ tlsopen(Chan *c, int omode) } lock(&dslock); pp = &dstate[CONV(c->qid)]; - s = *pp; - if(s == 0) + tr = *pp; + if(tr == nil) dsnew(c, pp); else { - if((perm & (s->perm>>6)) != perm - && (strcmp(up->user, s->user) != 0 - || (perm & s->perm) != perm)) + if((perm & (tr->perm>>6)) != perm + && (strcmp(up->user, tr->user) != 0 + || (perm & tr->perm) != perm)) error(Eperm); - s->ref++; + tr->ref++; } unlock(&dslock); poperror(); + if(t == Qhand){ + if(waserror()){ + qunlock(&tr->in.io); + closedown(c, tr); + } + qlock(&tr->in.io); + if(tr->handq != nil) + error(Einuse); + tr->handq = qopen(MaxRecLen, 0, nil, nil); + if(tr->handq == nil) + error("can't allocate handshake queue"); + qunlock(&tr->in.io); + poperror(); + } break; case Qencalgs: case Qhashalgs: @@ -370,48 +396,48 @@ static void tlswstat(Chan *c, char *dp) { Dir d; - TlsRec *s; + TlsRec *tr; convM2D(dp, &d); - s = dstate[CONV(c->qid)]; - if(s == nil) + tr = dstate[CONV(c->qid)]; + if(tr == nil) error(Ebadusefd); - if(strcmp(s->user, up->user) != 0) + if(strcmp(tr->user, up->user) != 0) error(Eperm); - memmove(s->user, d.uid, NAMELEN); - s->perm = d.mode; + memmove(tr->user, d.uid, NAMELEN); + tr->perm = d.mode; } static void tlsclose(Chan *c) { - TlsRec *s; + TlsRec *tr; + int t; - switch(TYPE(c->qid)) { + t = TYPE(c->qid); + switch(t) { case Qctl: case Qdata: case Qhand: if((c->flag & COPEN) == 0) break; - s = dstate[CONV(c->qid)]; - if(s == nil) + tr = dstate[CONV(c->qid)]; + if(tr == nil) break; - lock(&dslock); - if(--s->ref > 0) { - unlock(&dslock); - break; + if(t == Qhand){ + qlock(&tr->in.io); + if(tr->handq != nil){ + qfree(tr->handq); + tr->handq = nil; + } + qunlock(&tr->in.io); } - dstate[CONV(c->qid)] = nil; - unlock(&dslock); - - tlshangup(s); - if(s->c) - cclose(s->c); - free(s); + closedown(c, tr); + break; } } @@ -548,7 +574,7 @@ qremove(Block **l, int n, int discard) /* * read and process one tls record layer message - * must be called with tr->in.q held + * must be called with tr->in.io held */ static void tlsrecread(TlsRec *tr) @@ -581,38 +607,39 @@ tlsrecread(TlsRec *tr) nconsumed = 0; /* - * if an Eintr happens after this, we're screwed. Make - * sure nothing we call can sleep. Luckily, allocb - * won't sleep, it'll just error out. - * grab the next message and decode/decrypt it + * 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 = qremove(&tr->unprocessed, len, 0); in = &tr->in; if(waserror()){ - qunlock(&in->ctlq); +//ZZZ kill the connection + qunlock(&in->seclock); if(b != nil) freeb(b); nexterror(); } - qlock(&in->ctlq); + qlock(&in->seclock); b = pullupblock(b, len); p = b->rp; - if(in->protected) { - if(len <= tr->maclen) + if(in->sec != nil) { + if(len <= in->sec->maclen) tlsError(tr, EDecodeError, "record message too short for mac"); - rc4(&in->rc4, p, len); - len -= tr->maclen; + rc4(&in->sec->rc4, p, len); + len -= in->sec->maclen; /* update length */ put16(header+3, len); put64(seq, in->seq); in->seq++; - (*tr->packMac)(tr, in->mackey, seq, header, p, len, hmac); - if(memcmp(hmac, p+len, tr->maclen) != 0) + (*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac); + if(memcmp(hmac, p+len, in->sec->maclen) != 0) tlsError(tr, EBadRecordMac, "record mac mismatch"); } - qunlock(&tr->in.ctlq); + qunlock(&tr->in.seclock); poperror(); if(len <= 0) tlsError(tr, EDecodeError, "runt record message"); @@ -624,15 +651,16 @@ tlsrecread(TlsRec *tr) case RChangeCipherSpec: if(len != 1 || p[0] != 1) tlsError(tr, EHandshakeFailure, "invalid change cipher spec"); - qlock(&in->ctlq); - if(!tr->in.keyed){ - qunlock(&in->ctlq); + qlock(&in->seclock); + if(in->new == nil){ + qunlock(&in->seclock); tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec"); } - tr->in.keyed = 0; - tr->in.protected = 1; + free(tr->in.sec); + tr->in.sec = tr->in.new; + tr->in.new = nil; tr->in.seq = 0; - qunlock(&in->ctlq); + qunlock(&in->seclock); break; case RAlert: if(len != 2) @@ -695,10 +723,10 @@ tlsbread(Chan *c, long n, ulong offset) panic("tlsbread"); if(waserror()){ - qunlock(&tr->in.q); + qunlock(&tr->in.io); nexterror(); } - qlock(&tr->in.q); + qlock(&tr->in.io); if(TYPE(c->qid) == Qdata){ //ZZZ race setting state if(tr->state != SOpen) @@ -708,13 +736,13 @@ tlsbread(Chan *c, long n, ulong offset) /* return at most what was asked for */ b = qremove(&tr->processed, n, 0); - qunlock(&tr->in.q); + qunlock(&tr->in.io); poperror(); }else{ //ZZZ race setting state while(tr->state == SHandshake && !qcanread(tr->handq)) tlsrecread(tr); - qunlock(&tr->in.q); + qunlock(&tr->in.io); poperror(); b = qbread(tr->handq, n); } @@ -729,7 +757,7 @@ tlsread(Chan *c, void *a, long n, vlong off) Block *nb; uchar *va; int i; - char buf[128]; + char buf[16]; ulong offset = off; if(c->qid.path & CHDIR) @@ -739,7 +767,7 @@ tlsread(Chan *c, void *a, long n, vlong off) default: error(Ebadusefd); case Qctl: - sprint(buf, "%lud", CONV(c->qid)); + snprint(buf, sizeof(buf), "%lud", CONV(c->qid)); return readstr(offset, a, n, buf); case Qdata: b = tlsbread(c, n, offset); @@ -795,12 +823,12 @@ tlsrecwrite(TlsRec *tr, int type, Block *b) out = &tr->out; bb = b; if(waserror()){ - qunlock(&out->q); + qunlock(&out->io); if(bb != nil) freeb(bb); nexterror(); } - qlock(&out->q); + qlock(&out->io); while(bb != nil){ //ZZZ race on state @@ -812,13 +840,13 @@ tlsrecwrite(TlsRec *tr, int type, Block *b) * with padding on the front for header and back for mac */ if(waserror()){ - qunlock(&out->ctlq); + qunlock(&out->seclock); nexterror(); } - qlock(&out->ctlq); + qlock(&out->seclock); maclen = 0; - if(out->protected) - maclen = tr->maclen; + if(out->sec != nil) + maclen = out->sec->maclen; n = BLEN(bb); if(n > MaxRecLen){ n = MaxRecLen; @@ -843,10 +871,10 @@ tlsrecwrite(TlsRec *tr, int type, Block *b) put16(p+1, tr->version); put16(p+3, n); - if(out->protected){ + if(out->sec != nil){ put64(seq, out->seq); out->seq++; - (*tr->packMac)(tr, out->mackey, seq, p, p + RecHdrLen, n, nb->wp); + (*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp); b->wp += maclen; n += maclen; @@ -854,9 +882,9 @@ tlsrecwrite(TlsRec *tr, int type, Block *b) put16(p+3, n); /* encrypt */ - rc4(&out->rc4, p+RecHdrLen, n); + rc4(&out->sec->rc4, p+RecHdrLen, n); } - qunlock(&out->ctlq); + qunlock(&out->seclock); poperror(); /* @@ -865,7 +893,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b) */ devtab[tr->c->type]->bwrite(tr->c, nb, 0); } - qunlock(&out->q); + qunlock(&out->io); poperror(); } @@ -901,109 +929,88 @@ tlsbwrite(Chan *c, Block *b, ulong offset) return n; } -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; -} - -/* - * 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*); + int maclen; + void (*initkey)(Hashalg *, int, Secret *, uchar*); }; +static void +initmd5key(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); +} + Hashalg hashtab[] = { - { "md5", MD5dlen, md5, }, + { "clear" }, + { "md5", MD5dlen, initmd5key, }, { 0 } }; -static int -parsehashalg(char *p, TlsRec *s) +static Hashalg* +parsehashalg(char *p) { 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; + 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 blocklen; - int alg; - void (*keyinit)(OneWay*); + int keylen; + int ivlen; + void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*); }; +static void +initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *) +{ + setupRC4state(&s->rc4, p, ea->keylen); +} + Encalg encrypttab[] = { - { "rc4_128", 1, RC4, initRC4key_128, }, + { "clear" }, + { "rc4_128", 128/8, 0, initRC4key, }, { 0 } }; -static int -parseencryptalg(char *p, TlsRec *s) +static Encalg* +parseencalg(char *p) { 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; + for(ea = encrypttab; ea->name; ea++) + if(strcmp(p, ea->name) == 0) + return ea; + error("unsupported encryption algorithm"); + return nil; } static long tlswrite(Chan *c, void *a, long n, vlong off) { + Encalg *ea; + Hashalg *ha; TlsRec *volatile tr; Block *volatile b; - int m, t; - char *p, *np, *e, buf[128]; + Cmdbuf *volatile cb; + int m; + char *p, *e, buf[128]; uchar *x; ulong offset = off; @@ -1011,11 +1018,12 @@ tlswrite(Chan *c, void *a, long n, vlong off) if(tr == nil) panic("tlswrite"); - t = TYPE(c->qid); - if(t == Qdata || t == Qhand){ + switch(TYPE(c->qid)){ + case Qdata: + case Qhand: p = a; e = p + n; - do { + do{ m = e - p; if(m > MaxRecLen) m = MaxRecLen; @@ -1032,115 +1040,140 @@ tlswrite(Chan *c, void *a, long n, vlong off) tlsbwrite(c, b, offset); p += m; - } while(p < e); + }while(p < e); return n; + case Qctl: + break; + default: + error(Ebadusefd); + return -1; } - /* mutex with operations using what we're about to change */ + cb = parsecmd(buf, n); if(waserror()){ - qunlock(&s->in.ctlq); - qunlock(&s->out.q); + free(cb); nexterror(); } - qlock(&s->in.ctlq); - qlock(&s->out.q); + if(cb->nf < 1) + error("short control request"); - switch(t){ - default: - error(Ebadusefd); - case Qctl: - break; + /* mutex with operations using what we're about to change */ +//ZZZ check this locking + if(waserror()){ + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + nexterror(); } + qlock(&tr->in.seclock); + qlock(&tr->out.seclock); - 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){ - if(s->c != nil) + if(strcmp(cb->f[0], "fd") == 0){ + if(cb->nf != 3) + error("usage: fd n version"); + if(tr->c != nil) error(Einuse); - s->c = buftochan(p); - - /* default is clear (msg delimiters only) */ - s->state = Sclear; - s->blocklen = 1; - s->diglen = 0; - 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; - } + n = strtol(cb->f[2], nil, 0); + if(n < MinProtoVersion || n > MinProtoVersion) + error("unsupported version"); + tr->c = buftochan(cb->f[1]); + tr->version = n; + }else if(strcmp(cb->f[0], "version") == 0){ + if(cb->nf != 2) + error("usage: version n"); + if(tr->c == nil) + error("must set fd before version"); + if(tr->verset) + error("version already set"); + n = strtol(cb->f[1], nil, 0); + if(n == SSL3Version) + tr->packMac = sslPackMac; + else if(n == TLSVersion) + tr->packMac = tlsPackMac; + else + error("unsupported version"); + tr->verset = 1; + tr->version = n; + }else if(strcmp(cb->f[0], "alert") == 0){ + if(cb->nf != 2) + error("usage: alert n"); + if(tr->c == nil) + error("must set fd before sending alerts"); + + n = strtol(cb->f[1], nil, 0); + + tlsError(tr, n); + b = allocb(2); +//ZZZ need to check for fatal error + *b->wp++ = 1; + *b->wp++ = n; + tlsrecwrite(tr, RAlert, b); + freeb(b); +//ZZZ race on state + tlsSetState(tr, SError); + }else if(strcmp(cb->f[0], "changecipher") == 0){ + if(cb->nf != 1) + error("usage: changecipher"); + if(tr->out.new == nil) + error("can't change cipher spec without setting secret"); + + b = allocb(1); + *b->wp++ = 1; + tlsrecwrite(tr, RChangeCipherSpec, b); + freeb(b); - 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; + free(tr->out.sec); + tr->out.sec = tr->out.new; + tr->out.new = nil; + }else if(strcmp(cb->f[0], "secret") == 0){ + if(cb->nf != 4) + error("usage: secret hashalg encalg secretdata"); + if(tr->c == nil || !tr->verset) + error("must set fd and version before secrets"); + + if(tr->in.new != nil){ + free(tr->in.new); + tr->in.new = nil; + } + if(tr->out.new != nil){ + free(tr->out.new); + tr->out.new = nil; } - if(s->hf == 0 && s->encryptalg == Noencryption) - error("bad algorithm"); + ha = parsehashalg(cb->f[1]); + ea = parseencalg(cb->f[2]); - 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); - n = dec64(x, m, p, strlen(p)); - setsecret(&s->in, x, n); - free(x); - } else if(strcmp(buf, "secretout") == 0 && p != 0) { - m = (strlen(p)*3)/2 + 1; + m = (strlen(cb->f[3])*3)/2; x = smalloc(m); - n = dec64(x, m, p, strlen(p)); - setsecret(&s->out, x, n); + if(waserror()){ + free(x); + poperror(); + } + m = dec64(x, m, cb->f[3], strlen(cb->f[3])); + if(m != 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen) + error("bad secret data length"); + + tr->out.new = smalloc(sizeof(Secret)); + tr->in.new = smalloc(sizeof(Secret)); + if(ha->initkey){ + (*ha->initkey)(ha, tr->version, tr->in.new, &x[0]); + (*ha->initkey)(ha, tr->version, tr->out.new, &x[ha->maclen]); + } + if(ea->initkey){ + (*ea->initkey)(ea, tr->in.new, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]); + (*ea->initkey)(ea, tr->out.new, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]); + } + free(x); + poperror(); } else error(Ebadarg); -out: - qunlock(&s->in.ctlq); - qunlock(&s->out.q); + qunlock(&tr->in.seclock); + qunlock(&tr->out.seclock); + poperror(); + free(cb); poperror(); + return n; } @@ -1205,162 +1238,6 @@ Dev tlsdevtab = { tlswstat, }; -static Block* -encryptb(TlsRec *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(TlsRec *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("tls 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(TlsRec *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(TlsRec *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) @@ -1383,7 +1260,7 @@ tlshangup(TlsRec *s) { Block *b; - qlock(&s->in.q); + qlock(&s->in.io); for(b = s->processed; b; b = s->processed){ s->processed = b->next; freeb(b); @@ -1392,8 +1269,8 @@ tlshangup(TlsRec *s) freeb(s->unprocessed); s->unprocessed = nil; } - s->state = Sincomplete; - qunlock(&s->in.q); + s->state = SClosed; + qunlock(&s->in.io); } static TlsRec* @@ -1450,7 +1327,7 @@ dsnew(Chan *ch, TlsRec **pp) if(pp - dstate >= dshiwat) dshiwat++; memset(s, 0, sizeof(*s)); - s->state = Sincomplete; + s->state = SClosed; s->ref = 1; strncpy(s->user, up->user, sizeof(s->user)); s->perm = 0660; @@ -1461,6 +1338,87 @@ dsnew(Chan *ch, TlsRec **pp) ch->qid.vers = 0; } +/* + * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac. + */ +static DigestState* +sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s, + DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen) +{ + int i; + uchar pad[48], innerdigest[20]; + + if(xlen > sizeof(innerdigest) + || padlen > sizeof(pad)) + return nil; + + if(klen>64) + return nil; + + /* first time through */ + if(s == nil){ + for(i=0; imac)(buf, 11, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + +static void +tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac) +{ + DigestState *s; + uchar buf[13]; + + memmove(buf, seq, 8); + memmove(&buf[8], header, 5); + + s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0); + (*sec->mac)(body, len, mackey, sec->maclen, mac, s); +} + static void put32(uchar *p, u32int x) {