## diffname port/devtls.c 2001/0208
## diff -e /dev/null /n/emeliedump/2001/0208/sys/src/9/port/devtls.c
0a
/*
* devtls - transport layer security 1.0 and secure sockets layer 3.0 record 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[NAMELEN];
int perm;
};
static Lock dslock;
static int dshiwat;
static int maxdstate = 128;
static Dstate** dstate;
static char *encalgs;
static char *hashalgs;
enum
{
Maxdmsg= 1<<16,
Maxdstate= 64
};
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 tlshangup(Dstate*);
static Dstate* dsclone(Chan *c);
static void dsnew(Chan *c, Dstate **);
static char *tlsnames[] = {
[Qclonus] "clone",
[Qdata] "data",
[Qctl] "ctl",
[Qsecretin] "secretin",
[Qsecretout] "secretout",
[Qencalgs] "encalgs",
[Qhashalgs] "hashalgs",
};
static int
tlsgen(Chan *c, Dirtab *d, int nd, int s, Dir *dp)
{
Qid q;
Dstate *ds;
char name[16], *p, *nm;
USED(nd);
USED(d);
q.vers = 0;
switch(TYPE(c->qid)) {
case Qtopdir:
if(s == DEVDOTDOT){
q.path = QID(0, Qtopdir)|CHDIR;
devdir(c, q, "#D", 0, eve, CHDIR|0555, dp);
return 1;
}
if(s > 0)
return -1;
q.path = QID(0, Qprotodir)|CHDIR;
devdir(c, q, "tls", 0, eve, CHDIR|0555, dp);
return 1;
case Qprotodir:
if(s == DEVDOTDOT){
q.path = QID(0, Qtopdir)|CHDIR;
devdir(c, q, ".", 0, eve, CHDIR|0555, dp);
return 1;
}
if(s < dshiwat) {
sprint(name, "%d", s);
q.path = QID(s, Qconvdir)|CHDIR;
ds = dstate[s];
if(ds != 0)
nm = ds->user;
else
nm = eve;
devdir(c, q, name, 0, nm, CHDIR|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)|CHDIR;
devdir(c, q, "tls", 0, eve, CHDIR|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, tlsnames[TYPE(c->qid)], 0, eve, 0555, dp);
return 1;
default:
ds = dstate[CONV(c->qid)];
devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, ds->user, 0660, dp);
return 1;
}
return -1;
}
static Chan*
tlsattach(char *spec)
{
Chan *c;
c = devattach('D', spec);
c->qid.path = QID(0, Qtopdir)|CHDIR;
c->qid.vers = 0;
return c;
}
static int
tlswalk(Chan *c, char *name)
{
return devwalk(c, name, 0, 0, tlsgen);
}
static void
tlsstat(Chan *c, char *db)
{
devstat(c, db, 0, 0, tlsgen);
}
static Chan*
tlsopen(Chan *c, int omode)
{
Dstate *s, **pp;
int perm;
perm = 0;
omode &= 3;
switch(omode) {
case OREAD:
perm = 4;
break;
case OWRITE:
perm = 2;
break;
case ORDWR:
perm = 6;
break;
}
switch(TYPE(c->qid)) {
default:
panic("tlsopen");
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 void
tlswstat(Chan *c, char *dp)
{
Dir d;
Dstate *s;
convM2D(dp, &d);
s = dstate[CONV(c->qid)];
if(s == nil)
error(Ebadusefd);
if(strcmp(s->user, up->user) != 0)
error(Eperm);
memmove(s->user, d.uid, NAMELEN);
s->perm = d.mode;
}
static void
tlsclose(Chan *c)
{
Dstate *s;
switch(TYPE(c->qid)) {
case Qctl:
case Qdata:
case Qsecretin:
case Qsecretout:
if((c->flag & COPEN) == 0)
break;
s = dstate[CONV(c->qid)];
if(s == nil)
break;
lock(&dslock);
if(--s->ref > 0) {
unlock(&dslock);
break;
}
dstate[CONV(c->qid)] = nil;
unlock(&dslock);
tlshangup(s);
if(s->c)
cclose(s->c);
free(s->in.secret);
free(s->out.secret);
free(s->in.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)
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;
}
}
/*
* 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*
qremove(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("qremove");
return first;
} else
n -= i;
if(BLEN(b) < 0)
panic("qremove");
}
*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*
tlsbread(Chan *c, long n, ulong)
{
Dstate *volatile s;
Block *b;
uchar consumed[3];
int nconsumed;
int len, pad;
s = dstate[CONV(c->qid)];
if(s == nil)
panic("tlsbread");
if(s->state == Sincomplete)
error(Ebadusefd);
nconsumed = 0;
if(waserror()){
if(strcmp(up->error, Eintr) != 0)
regurgitate(s, consumed, nconsumed);
qunlock(&s->in.q);
nexterror();
}
qlock(&s->in.q);
if(s->processed == 0){
/* read in the whole message */
ensure(s, &s->unprocessed, 2);
consume(&s->unprocessed, consumed, 2);
nconsumed = 2;
if(consumed[0] & 0x80){
len = ((consumed[0] & 0x7f)<<8) | consumed[1];
ensure(s, &s->unprocessed, len);
pad = 0;
} else {
len = ((consumed[0] & 0x3f)<<8) | consumed[1];
ensure(s, &s->unprocessed, len+1);
consume(&s->unprocessed, &consumed[2], 1);
pad = consumed[2];
if(pad > len){
print("pad %d buf len %d\n", pad, len);
error("bad pad in tls message");
}
}
USED(nconsumed);
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 */
b = qremove(&s->unprocessed, len, 0);
if(waserror()){
qunlock(&s->in.ctlq);
if(b != nil)
freeb(b);
nexterror();
}
qlock(&s->in.ctlq);
switch(s->state){
case Sencrypting:
b = decryptb(s, b);
break;
case Sdigesting:
b = pullupblock(b, s->diglen);
if(b == nil)
error("tls message too short");
checkdigestb(s, b);
b->rp += s->diglen;
break;
case Sdigenc:
b = decryptb(s, b);
b = pullupblock(b, s->diglen);
if(b == nil)
error("tls message too short");
checkdigestb(s, b);
b->rp += s->diglen;
len -= s->diglen;
break;
}
/* remove pad */
if(pad)
s->processed = qremove(&b, len - pad, 1);
else
s->processed = b;
b = nil;
s->in.mid++;
qunlock(&s->in.ctlq);
poperror();
USED(nconsumed);
}
/* return at most what was asked for */
b = qremove(&s->processed, n, 0);
qunlock(&s->in.q);
poperror();
return b;
}
static long
tlsread(Chan *c, void *a, long n, vlong off)
{
Block *volatile b;
Block *nb;
uchar *va;
int i;
char buf[128];
ulong offset = off;
if(c->qid.path & CHDIR)
return devdirread(c, a, n, 0, 0, tlsgen);
switch(TYPE(c->qid)) {
default:
error(Ebadusefd);
case Qctl:
sprint(buf, "%lud", CONV(c->qid));
return readstr(offset, a, n, buf);
case Qdata:
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;
}
/*
* 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);
}
/*
* 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
tlsbwrite(Chan *c, Block *b, ulong offset)
{
Dstate *volatile s;
Block *volatile bb;
Block *nb;
int h, n, m, pad, rv;
uchar *p;
bb = b;
s = dstate[CONV(c->qid)];
if(s == nil)
panic("tlsbwrite");
if(s->state == Sincomplete){
freeb(b);
error(Ebadusefd);
}
if(waserror()){
qunlock(&s->out.q);
if(bb != nil)
freeb(bb);
nexterror();
}
qlock(&s->out.q);
rv = 0;
while(bb){
m = n = BLEN(bb);
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, bb->rp, m);
nb->wp += m + h;
bb->rp += m;
} else {
/* add header space */
nb = padblock(bb, h);
bb = nil;
}
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;
}
qunlock(&s->out.q);
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[] =
{
{ "md5", MD5dlen, md5, },
{ "sha1", 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
tlswrite(Chan *c, void *a, long n, vlong off)
{
Dstate *volatile s;
Block *volatile b;
int m, t;
char *p, *np, *e, buf[128];
uchar *x;
ulong offset = off;
s = dstate[CONV(c->qid)];
if(s == nil)
panic("tlswrite");
t = TYPE(c->qid);
if(t == Qdata){
if(s->state == Sincomplete)
error(Ebadusefd);
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;
tlsbwrite(c, b, offset);
p += m;
} while(p < e);
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("tlswrite");
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);
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;
x = smalloc(m);
n = dec64(x, m, p, strlen(p));
setsecret(&s->out, x, n);
free(x);
} else
error(Ebadarg);
out:
qunlock(&s->in.ctlq);
qunlock(&s->out.q);
poperror();
return n;
}
static void
tlsinit(void)
{
struct Encalg *e;
struct Hashalg *h;
int n;
char *cp;
if((dstate = smalloc(sizeof(Dstate*) * maxdstate)) == 0)
panic("tlsinit");
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 tlsdevtab = {
'D',
"tls",
devreset,
tlsinit,
tlsattach,
devclone,
tlswalk,
tlsstat,
tlsopen,
devcreate,
tlsclose,
tlsread,
tlsbread,
tlswrite,
tlsbwrite,
devremove,
tlswstat,
};
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("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(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 */
return c;
}
/* hand up a digest connection */
static void
tlshangup(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)
{
Dstate **pp, **ep, **np;
int newmax;
if(waserror()) {
unlock(&dslock);
nexterror();
}
lock(&dslock);
ep = &dstate[maxdstate];
for(pp = dstate; pp < ep; pp++) {
if(*pp == 0) {
dsnew(ch, pp);
break;
}
}
if(pp >= ep) {
if(maxdstate >= Maxdstate) {
unlock(&dslock);
poperror();
return 0;
}
newmax = 2 * maxdstate;
if(newmax > Maxdstate)
newmax = Maxdstate;
np = smalloc(sizeof(Dstate*) * newmax);
if(np == 0)
error(Enomem);
memmove(np, dstate, sizeof(Dstate*) * maxdstate);
dstate = np;
pp = &dstate[maxdstate];
memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
maxdstate = newmax;
dsnew(ch, pp);
}
unlock(&dslock);
poperror();
return *pp;
}
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;
strncpy(s->user, up->user, sizeof(s->user));
s->perm = 0660;
t = TYPE(ch->qid);
if(t == Qclonus)
t = Qctl;
ch->qid.path = QID(pp - dstate, t);
ch->qid.vers = 0;
}
.
## diffname port/devtls.c 2001/0331
## diff -e /n/emeliedump/2001/0208/sys/src/9/port/devtls.c /n/emeliedump/2001/0331/sys/src/9/port/devtls.c
1444a
}
static void
put32(uchar *p, u32int x)
{
p[0] = x>>24;
p[1] = x>>16;
p[2] = x>>8;
p[3] = x;
}
static void
put64(uchar *p, vlong x)
{
put32(p, (u32int)(x >> 32));
put32(p+4, (u32int)x);
}
static void
put16(uchar *p, int x)
{
p[0] = x>>8;
p[1] = x;
}
static u32int
get32(uchar *p)
{
return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
}
static int
get16(uchar *p)
{
return (p[0]<<8)|p[1];
.
1427c
TlsRec *s;
.
1425c
dsnew(Chan *ch, TlsRec **pp)
.
1415c
memset(pp, 0, sizeof(TlsRec*)*(newmax - maxdstate));
.
1412c
memmove(np, dstate, sizeof(TlsRec*) * maxdstate);
.
1409,1410c
np = smalloc(sizeof(TlsRec*) * newmax);
if(np == nil)
.
1404c
return nil;
.
1395c
if(*pp == nil) {
.
1385c
TlsRec **pp, **ep, **np;
.
1382c
static TlsRec*
.
1376c
s->unprocessed = nil;
.
1374c
if(s->unprocessed != nil){
.
1365c
tlshangup(TlsRec *s)
.
1307c
checkdigestb(TlsRec *s, Block *bin)
.
1276c
digestb(TlsRec *s, Block *b, int offset)
.
1224c
decryptb(TlsRec *s, Block *bin)
.
1192c
encryptb(TlsRec *s, Block *b, int offset)
.
1171c
'a',
.
1138c
if((dstate = smalloc(sizeof(TlsRec*) * maxdstate)) == 0)
.
1057d
1050a
if(s->c != nil)
error(Einuse);
.
1028,1034c
error(Ebadusefd);
.
998,999c
if(m > MaxRecLen)
m = MaxRecLen;
.
990,993c
if(t == Qdata || t == Qhand){
.
985,986c
tr = dstate[CONV(c->qid)];
if(tr == nil)
.
978c
TlsRec *volatile tr;
.
957c
parseencryptalg(char *p, TlsRec *s)
.
945,954d
942d
933,940d
930d
905c
parsehashalg(char *p, TlsRec *s)
.
900d
851,869d
846,847c
w->secret = smalloc(n);
memmove(w->secret, secret, n);
w->slen = n;
.
841,844c
if(w->secret)
free(w->secret);
.
839c
setsecret(OneWay *w, uchar *secret, int n)
.
821,835c
return n;
.
798,818c
switch(TYPE(c->qid)) {
default:
return devbwrite(c, b, offset);
case Qhand:
//ZZZ race setting state
if(tr->state != SHandshake && tr->state != SOpen)
error(Ebadusefd);
tlsrecwrite(tr, RHandshake, b);
break;
case Qdata:
//ZZZ race setting state
if(tr->state != SOpen)
error(Ebadusefd);
tlsrecwrite(tr, RApplication, b);
break;
.
790,796c
tr = dstate[CONV(c->qid)];
if(tr == nil)
panic("tlsbread");
.
785,788c
n = BLEN(b);
.
782,783c
TlsRec *tr;
ulong n;
.
779,780c
static long
tlsbwrite(Chan *c, Block *b, ulong offset)
.
775,776d
773c
qunlock(&out->q);
.
767,771c
/*
* if bwrite error's, we assume the block is queued.
* if not, we're out of sync with the receiver and will not recover.
*/
devtab[tr->c->type]->bwrite(tr->c, nb, 0);
.
765a
qunlock(&out->ctlq);
poperror();
.
754,764c
/* update length */
put16(p+3, n);
/* encrypt */
rc4(&out->rc4, p+RecHdrLen, n);
.
742,752c
if(out->protected){
put64(seq, out->seq);
out->seq++;
(*tr->packMac)(tr, out->mackey, seq, p, p + RecHdrLen, n, nb->wp);
b->wp += maclen;
n += maclen;
.
735,740c
p = nb->rp;
p[0] = type;
put16(p+1, tr->version);
put16(p+3, n);
.
733d
721,730c
qlock(&out->ctlq);
maclen = 0;
if(out->protected)
maclen = tr->maclen;
n = BLEN(bb);
if(n > MaxRecLen){
n = MaxRecLen;
nb = allocb(n + RecHdrLen + maclen);
memmove(nb->wp + RecHdrLen, bb->rp, n);
nb->wp += n + RecHdrLen;
bb->rp += n;
}else{
/*
* carefully reuse bb so it will get freed if we're out of memory
*/
bb = padblock(bb, RecHdrLen);
if(maclen)
nb = padblock(bb, -maclen);
else
nb = bb;
.
705,719c
/*
* get at most one maximal record's input,
* with padding on the front for header and back for mac
*/
if(waserror()){
qunlock(&out->ctlq);
nexterror();
.
700,703c
while(bb != nil){
//ZZZ race on state
if(tr->state != SHandshake && tr->state != SOpen)
error(Ebadusefd);
.
698c
qlock(&out->q);
.
693c
qunlock(&out->q);
.
684,691d
682a
out = &tr->out;
.
680,681c
uchar *p, seq[8];
OneWay *volatile out;
int n, maclen;
.
677d
674,675c
static void
tlsrecwrite(TlsRec *tr, int type, Block *b)
.
669,672c
* write a block in tls records
.
601,606d
598c
}else{
//ZZZ race setting state
while(tr->state == SHandshake && !qcanread(tr->handq))
tlsrecread(tr);
qunlock(&tr->in.q);
poperror();
b = qbread(tr->handq, n);
.
530,596c
/* return at most what was asked for */
b = qremove(&tr->processed, n, 0);
qunlock(&tr->in.q);
.
528c
qlock(&tr->in.q);
if(TYPE(c->qid) == Qdata){
//ZZZ race setting state
if(tr->state != SOpen)
error(Ebadusefd);
while(tr->processed == nil)
tlsrecread(tr);
.
523,525c
qunlock(&tr->in.q);
.
521d
518,519d
515,516c
switch(TYPE(c->qid)) {
default:
return devbread(c, n, offset);
case Qhand:
case Qdata:
break;
}
tr = dstate[CONV(c->qid)];
if(tr == nil)
.
511,513d
509c
TlsRec *volatile tr;
.
507c
tlsbread(Chan *c, long n, ulong offset)
.
499a
* read and process one tls record layer message
* must be called with tr->in.q held
*/
static void
tlsrecread(TlsRec *tr)
{
OneWay *volatile in;
Block *volatile b;
uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
int volatile nconsumed;
int len, type, ver;
nconsumed = 0;
if(waserror()){
if(strcmp(up->error, Eintr) == 0)
regurgitate(tr, header, nconsumed);
nexterror();
}
ensure(tr, &tr->unprocessed, RecHdrLen);
consume(&tr->unprocessed, header, RecHdrLen);
nconsumed = RecHdrLen;
type = header[0];
ver = get16(header+1);
len = get16(header+3);
if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
tlsError(tr, EProtocolVersion, "invalid version in record layer");
if(len <= 0)
tlsError(tr, EIllegalParameter, "invalid length in record layer");
if(len > MaxRecLen)
tlsError(tr, ERecordOverflow, "record message too long");
ensure(tr, &tr->unprocessed, len);
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
*/
b = qremove(&tr->unprocessed, len, 0);
in = &tr->in;
if(waserror()){
qunlock(&in->ctlq);
if(b != nil)
freeb(b);
nexterror();
}
qlock(&in->ctlq);
b = pullupblock(b, len);
p = b->rp;
if(in->protected) {
if(len <= tr->maclen)
tlsError(tr, EDecodeError, "record message too short for mac");
rc4(&in->rc4, p, len);
len -= tr->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)
tlsError(tr, EBadRecordMac, "record mac mismatch");
}
qunlock(&tr->in.ctlq);
poperror();
if(len <= 0)
tlsError(tr, EDecodeError, "runt record message");
switch(type) {
default:
tlsError(tr, EIllegalParameter, "invalid record message 0x%x", type);
return;
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);
tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec");
}
tr->in.keyed = 0;
tr->in.protected = 1;
tr->in.seq = 0;
qunlock(&in->ctlq);
break;
case RAlert:
if(len != 2)
tlsError(tr, EDecodeError, "invalid alert");
if(p[0] == 1) {
if(p[1] == ECloseNotify) {
tlsError(tr, ECloseNotify, "remote close");
tlsSetState(tr, SRemoteClosed);
}
} else {
tlsSetState(tr, SError);
tlsAlert(tr, p[1]);
}
break;
case RHandshake:
/*
* don't worry about dropping the block
* qbwrite always queue it even if flow controlled and interrupted.
*/
if(tr->handq != nil){
qbwrite(tr->handq, b);
b = nil;
}
break;
case RApplication:
//ZZZ race on state
if(tr->state != SOpen)
tlsError(tr, EUnexpectedMessage, "application message received before handshake completed");
tr->processed = b;
b = nil;
break;
}
if(b != nil)
freeb(b);
}
/*
.
432c
regurgitate(TlsRec *s, uchar *p, int n)
.
372c
ensure(TlsRec *s, Block **l, int n)
.
360,363d
340,341c
case Qhand:
.
335c
TlsRec *s;
.
318c
TlsRec *s;
.
280,281c
case Qhand:
.
247c
TlsRec *s, **pp;
.
226c
c = devattach('a', spec);
.
203c
case 4:
.
196,199d
192,193c
q.path = QID(CONV(c->qid), Qhand);
p = "hand";
.
129c
TlsRec *ds;
.
119,120c
[Qhand] "hand",
.
111,113c
static void tlshangup(TlsRec*);
static TlsRec* dsclone(Chan *c);
static void dsnew(Chan *c, TlsRec **);
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 u32int get32(uchar *p);
static int get16(uchar *p);
static void tlsAlert(TlsRec *tr, int err);
static void tlsError(TlsRec *tr, int err, char *msg, ...);
#pragma varargck argpos tlsError 3
.
106,109c
static Block* encryptb(TlsRec*, Block*, int);
static Block* decryptb(TlsRec*, Block*);
static Block* digestb(TlsRec*, Block*, int);
static void checkdigestb(TlsRec*, Block*);
.
103c
static void ensure(TlsRec*, Block**, int);
.
93,94c
Qhand,
.
76c
static TlsRec** dstate;
.
62,63c
Queue *handq; /* queue of handshake messages */
Block *processed; /* next bunch of application data */
Block *unprocessed; /* data read from c but not parsed into records */
.
60c
typedef struct TlsRec TlsRec;
struct TlsRec
{
Chan *c; /* io channel */
int ref; /* serialized by dslock for atomic destroy */
int version; /* version of the protocol we are speaking */
int verset; /* version has been set */
Lock statelk;
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;
/* input side -- protected by in.q */
.
56,58d
53,54c
int protected; /* cipher is enabled */
RC4state rc4;
uchar mackey[64];
};
.
50,51c
ulong seq;
int keyed; /* have key, waiting for cipher enable */
.
46,48c
QLock q; /* locks io access */
QLock ctlq; /* locks one-way paramaters */
.
43,44c
enum {
MaxRecLen = 1<<14, /* max payload length of a record layer message */
RecHdrLen = 5,
TLSVersion = 0x0301,
SSL3Version = 0x0300,
ProtocolVersion = 0x0301, /* maximum version we speak */
MinProtoVersion = 0x0300, /* limits on version we accept */
MaxProtoVersion = 0x03ff,
};
/* connection states */
enum {
SHandshake, // doing handshake
SOpen, // application data can be sent
SRemoteClosed, // remote side has closed down
SError, // some sort of error has occured
SClosed, // it is all over
};
/* record types */
enum {
RChangeCipherSpec = 20,
RAlert,
RHandshake,
RApplication,
};
/* alerts */
enum {
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 OneWay
.
29,34d
16,26d
2c
* devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
.
## diffname port/devtls.c 2001/0403
## diff -e /n/emeliedump/2001/0331/sys/src/9/port/devtls.c /n/emeliedump/2001/0403/sys/src/9/port/devtls.c
1461a
}
/*
* 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; i<padlen; i++)
pad[i] = 0x36;
s = (*x)(key, klen, nil, nil);
s = (*x)(pad, padlen, nil, s);
if(s == nil)
return nil;
}
s = (*x)(p, len, nil, s);
if(digest == nil)
return s;
/* last time through */
for(i=0; i<padlen; i++)
pad[i] = 0x5c;
(*x)(nil, 0, innerdigest, s);
s = (*x)(key, klen, nil, nil);
s = (*x)(pad, padlen, nil, s);
(*x)(innerdigest, xlen, digest, s);
return nil;
}
static DigestState*
sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
{
return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
}
static DigestState*
sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
{
return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
}
static void
sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
{
DigestState *s;
uchar buf[11];
memmove(buf, seq, 8);
buf[8] = header[0];
buf[9] = header[3];
buf[10] = header[4];
s = (*sec->mac)(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);
.
1453c
s->state = SClosed;
.
1395,1396c
s->state = SClosed;
qunlock(&s->in.io);
.
1386c
qlock(&s->in.io);
.
1208,1363d
1143a
free(cb);
poperror();
.
1140,1142c
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
.
1131,1136c
poperror();
.
1115,1129d
1113a
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]);
}
.
1110,1112c
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]);
.
1106,1108c
m = (strlen(cb->f[3])*3)/2;
x = smalloc(m);
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");
.
1101,1104c
ha = parsehashalg(cb->f[1]);
ea = parseencalg(cb->f[2]);
.
1097,1099c
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;
}
.
1090,1095c
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");
.
1084,1088c
b = allocb(1);
*b->wp++ = 1;
tlsrecwrite(tr, RChangeCipherSpec, b);
freeb(b);
.
1081,1082c
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");
.
1071,1079c
n = strtol(cb->f[1], nil, 0);
.
1069c
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");
.
1055,1067c
if(strcmp(cb->f[0], "fd") == 0){
if(cb->nf != 3)
error("usage: fd n version");
if(tr->c != nil)
.
1053a
qlock(&tr->in.seclock);
qlock(&tr->out.seclock);
.
1048,1052c
/* 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();
.
1045,1046c
if(cb->nf < 1)
error("short control request");
.
1041,1042c
free(cb);
.
1039c
cb = parsecmd(buf, n);
.
1036a
case Qctl:
break;
default:
error(Ebadusefd);
return -1;
.
1035c
}while(p < e);
.
1018c
do{
.
1014,1015c
switch(TYPE(c->qid)){
case Qdata:
case Qhand:
.
1005,1006c
Cmdbuf *volatile cb;
int m;
char *p, *e, buf[128];
.
1002a
Encalg *ea;
Hashalg *ha;
.
986,997c
for(ea = encrypttab; ea->name; ea++)
if(strcmp(p, ea->name) == 0)
return ea;
error("unsupported encryption algorithm");
return nil;
.
981,982c
static Encalg*
parseencalg(char *p)
.
977c
{ "clear" },
{ "rc4_128", 128/8, 0, initRC4key, },
.
974a
static void
initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
{
setupRC4state(&s->rc4, p, ea->keylen);
}
.
970,972c
int keylen;
int ivlen;
void (*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
.
954,963c
for(ha = hashtab; ha->name; ha++)
if(strcmp(p, ha->name) == 0)
return ha;
error("unsupported hash algorithm");
return nil;
.
949,950c
static Hashalg*
parsehashalg(char *p)
.
945c
{ "clear" },
{ "md5", MD5dlen, initmd5key, },
.
942a
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);
}
.
939,940c
int maclen;
void (*initkey)(Hashalg *, int, Secret *, uchar*);
.
904,934d
868c
qunlock(&out->io);
.
859c
qunlock(&out->seclock);
.
857c
rc4(&out->sec->rc4, p+RecHdrLen, n);
.
849c
(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp);
.
846c
if(out->sec != nil){
.
820,821c
if(out->sec != nil)
maclen = out->sec->maclen;
.
818c
qlock(&out->seclock);
.
815c
qunlock(&out->seclock);
.
803c
qlock(&out->io);
.
798c
qunlock(&out->io);
.
742c
snprint(buf, sizeof(buf), "%lud", CONV(c->qid));
.
732c
char buf[16];
.
717c
qunlock(&tr->in.io);
.
711c
qunlock(&tr->in.io);
.
701c
qlock(&tr->in.io);
.
698c
qunlock(&tr->in.io);
.
635c
qunlock(&in->seclock);
.
632,633c
free(tr->in.sec);
tr->in.sec = tr->in.new;
tr->in.new = nil;
.
627,629c
qlock(&in->seclock);
if(in->new == nil){
qunlock(&in->seclock);
.
615c
qunlock(&tr->in.seclock);
.
611,612c
(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
if(memcmp(hmac, p+len, in->sec->maclen) != 0)
.
604,605c
rc4(&in->sec->rc4, p, len);
len -= in->sec->maclen;
.
601,602c
if(in->sec != nil) {
if(len <= in->sec->maclen)
.
598c
qlock(&in->seclock);
.
593c
//ZZZ kill the connection
qunlock(&in->seclock);
.
584,587c
* 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.
.
551c
* must be called with tr->in.io held
.
408,414c
closedown(c, tr);
break;
.
403,406c
if(t == Qhand){
qlock(&tr->in.io);
if(tr->handq != nil){
qfree(tr->handq);
tr->handq = nil;
}
qunlock(&tr->in.io);
.
399,400c
tr = dstate[CONV(c->qid)];
if(tr == nil)
.
392c
t = TYPE(c->qid);
switch(t) {
.
390c
TlsRec *tr;
int t;
.
383,384c
memmove(tr->user, d.uid, NAMELEN);
tr->perm = d.mode;
.
380c
if(strcmp(tr->user, up->user) != 0)
.
377,378c
tr = dstate[CONV(c->qid)];
if(tr == nil)
.
373c
TlsRec *tr;
.
355a
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();
}
.
352c
tr->ref++;
.
347,349c
if((perm & (tr->perm>>6)) != perm
&& (strcmp(up->user, tr->user) != 0
|| (perm & tr->perm) != perm))
.
343,344c
tr = *pp;
if(tr == nil)
.
330,331c
tr = dsclone(c);
if(tr == nil)
.
320c
t = TYPE(c->qid);
switch(t) {
.
303,304c
TlsRec *tr, **pp;
int t, perm;
.
299a
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);
}
.
165a
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);
.
157,161d
108,110c
void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
.
94a
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 */
};
.
87,90c
struct Secret
{
DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
int maclen;
.
82,85d
19,25d
16,17c
typedef struct Secret Secret;
.
13,14d
## diffname port/devtls.c 2001/0404
## diff -e /n/emeliedump/2001/0403/sys/src/9/port/devtls.c /n/emeliedump/2001/0404/sys/src/9/port/devtls.c
1254a
}
static void
sendAlert(TlsRec *tr, int err)
{
Block *b;
int i, fatal;
fatal = 1;
for(i=0; i < nelem(tlserrs); i++) {
if(tlserrs[i].err == err) {
if(tr->version == SSL3Version)
err = tlserrs[i].sslerr;
else
err = tlserrs[i].tlserr;
fatal = tlserrs[i].fatal;
break;
}
}
b = allocb(2);
*b->wp++ = fatal + 1;
*b->wp++ = err;
tlsrecwrite(tr, RAlert, b);
//ZZZ race on state
if(fatal)
tlsSetState(tr, SError);
}
static void
rcvAlert(TlsRec *tr, int err)
{
char *s;
int i, fatal;
s = "unknown error";
fatal = 1;
for(i=0; i < nelem(tlserrs); i++){
if(tlserrs[i].err == err){
s = tlserrs[i].msg;
fatal = tlserrs[i].fatal;
break;
}
}
//ZZZ need to kill session if fatal error
if(fatal)
tlsSetState(tr, SError);
error(s);
}
static void
rcvError(TlsRec *tr, int err, char *fmt, ...)
{
char msg[ERRLEN];
va_list arg;
sendAlert(tr, err);
va_start(arg, fmt);
strcpy(msg, "tls local %s");
doprint(strchr(msg, '\0'), msg+sizeof(msg), fmt, arg);
va_end(arg);
error(msg);
}
static void
tlsSetState(TlsRec *tr, int newstate)
{
lock(&tr->statelk);
tr->state = newstate;
unlock(&tr->statelk);
.
1164a
if(strtol(cb->f[3], nil, 0) == 0){
tr->in.new = tos;
tr->out.new = toc;
}else{
tr->in.new = toc;
tr->out.new = tos;
}
.
1162,1163c
(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
.
1158,1159c
(*ha->initkey)(ha, tr->version, tos, &x[0]);
(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
.
1155,1156c
tos = smalloc(sizeof(Secret));
toc = smalloc(sizeof(Secret));
.
1151,1153c
m = dec64(x, m, p, strlen(p));
if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
error("not enough secret data provided");
.
1149c
nexterror();
.
1145c
p = cb->f[4];
m = (strlen(p)*3)/2;
.
1128,1129c
if(cb->nf != 5)
error("usage: secret hashalg encalg isclient secretdata");
.
1125,1126c
tr->out.sec = toc;
qunlock(&tr->out.seclock);
return n;
.
1123a
qlock(&tr->out.seclock);
.
1122d
1118a
toc = tr->out.new;
tr->out.new = nil;
//ZZZ minor race; worth fixing?
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
poperror();
free(cb);
poperror();
.
1104,1112c
sendAlert(tr, m);
return n;
.
1102c
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
poperror();
free(cb);
poperror();
.
1100a
m = strtol(cb->f[1], nil, 0);
.
1095c
tr->version = m;
}else if(strcmp(cb->f[0], "opened") == 0){
if(cb->nf != 1)
error("usage: opened");
tlsSetState(tr, SOpen);
.
1090c
else if(m == TLSVersion)
.
1087,1088c
m = strtol(cb->f[1], nil, 0);
if(m == SSL3Version)
.
1079c
tr->version = m;
tlsSetState(tr, SHandshake);
.
1075,1076c
m = strtol(cb->f[2], nil, 0);
if(m < MinProtoVersion || m > MaxProtoVersion)
.
1052c
cb = parsecmd(a, n);
.
1013,1014c
char *p, *e;
uchar *volatile x;
.
1009a
Secret *tos, *toc;
.
985c
static Encalg encrypttab[] =
.
951c
static Hashalg hashtab[] =
.
917,918c
// if(tr->state != SHandshake && tr->state != SOpen)
// error(Ebadusefd);
.
772a
case Qhand:
.
691c
rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
.
686c
}else if(tr->verset && tr->version != SSL3Version)
sendAlert(tr, ENoRenegotiation);
.
681c
* 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.
.
674,675c
rcvAlert(tr, p[1]);
.
672a
/*
* ignore EUserCancelled, it's meaningless
* need to handle ENoRenegotiation
*/
.
670c
rcvError(tr, ECloseNotify, "remote close");
.
667c
rcvError(tr, EDecodeError, "invalid alert");
.
659,662c
free(in->sec);
in->sec = in->new;
in->new = nil;
in->seq = 0;
.
657c
rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
.
653c
rcvError(tr, EDecodeError, "invalid change cipher spec");
.
649c
rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
.
645c
rcvError(tr, EDecodeError, "runt record message");
.
642c
qunlock(&in->seclock);
.
640c
rcvError(tr, EBadRecordMac, "record mac mismatch");
.
630c
rcvError(tr, EDecodeError, "record message too short for mac");
.
619c
//ZZZ kill the connection?
.
607a
poperror();
.
605c
rcvError(tr, ERecordOverflow, "record message too long");
.
603c
rcvError(tr, EIllegalParameter, "invalid length in record layer");
.
601c
rcvError(tr, EProtocolVersion, "invalid version in record layer");
.
376c
//ZZZ what is the correct buffering here?
tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
.
162,164c
static void tlsSetState(TlsRec *tr, int newstate);
static void rcvAlert(TlsRec *tr, int err);
static void sendAlert(TlsRec *tr, int err);
static void rcvError(TlsRec *tr, int err, char *msg, ...);
#pragma varargck argpos rcvError 3
.
126a
//ZZZ
.
117a
typedef struct TlsErrs TlsErrs;
struct TlsErrs{
int err;
int sslerr;
int tlserr;
int fatal;
char *msg;
};
static TlsErrs tlserrs[] = {
{ECloseNotify, ECloseNotify, ECloseNotify,
0, "remote close"},
{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 paramters"},
{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, "renegotiation not supported"},
{-1},
};
.
## diffname port/devtls.c 2001/0405
## diff -e /n/emeliedump/2001/0404/sys/src/9/port/devtls.c /n/emeliedump/2001/0405/sys/src/9/port/devtls.c
1440,1441c
qunlock(&tr->in.io);
tlsrecwrite(tr, RAlert, ECloseNotify);
tlsSetState(tr, SClosed);
.
1436,1438c
if(tr->unprocessed != nil){
freeb(tr->unprocessed);
tr->unprocessed = nil;
.
1431,1433c
qlock(&tr->in.io);
for(b = tr->processed; b; b = tr->processed){
tr->processed = b->next;
.
1427c
tlshangup(TlsRec *tr)
.
1398,1399c
// handclose(tr, err);
tlshangup(tr);
tlsSetState(tr, SError);
.
1393d
1389d
1386c
int i;
.
1381a
/*
* got a fatal alert message
*/
.
1211,1215d
1207c
/*
* the real work is done as the message is written
* so the stream is encrypted in sync.
*/
.
1198,1201d
1175c
lock(&tr->statelk);
if(tr->state != SHandshake && tr->state != SOpen){
unlock(&tr->statelk);
//ZZZ bad error message
error("can't set open state");
}
tr->state = SOpen;
unlock(&tr->statelk);
.
996c
//ZZZ race on state
.
990,992d
960a
if(type == RChangeCipherSpec){
if(out->new == nil)
error("change cipher without a new cipher");
free(out->sec);
out->sec = out->new;
out->new = nil;
out->seq = 0;
}
.
952c
nb->wp += maclen;
.
909c
if(tr->state != SHandshake && tr->state != SOpen && tr->state != SRemoteClosed)
.
815c
//ZZZ race on state
.
804c
//ZZZ race on state
.
757a
dechandq(tr);
.
755a
tr->hqref++;
unlock(&tr->hqlock);
.
754a
lock(&tr->hqlock);
.
742a
// if(p[1] == ENoRenegotiation)
// handclose(tr, "no renegotiation");
// if(p[1] == EUserCancelled)
// handclose(tr, "user cancelled");
.
740,741c
* propate messages to handshaker
* EUserCancelled ENoRenegotiation
ZZZ better comment, better thoughts about this
.
737a
// handclose(tr, "remote close");
error("remote close");
.
736d
706a
b->wp -= in->sec->maclen;
.
527c
bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
.
504c
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);
.
496,502c
if(t == Qhand)
dechandq(tr);
lock(&dslock);
if(--tr->ref > 0) {
unlock(&dslock);
return;
.
478a
dechandq(TlsRec *tr)
{
lock(&tr->hqlock);
if(--tr->hqref == 0 && tr->handq != nil){
qfree(tr->handq);
tr->handq = nil;
}
unlock(&tr->hqlock);
}
static void
.
473a
//ZZZ propagate chown to tr->datafd?
.
432,446d
427c
if(t == Qhand){
if(waserror()){
unlock(&tr->hqlock);
nexterror();
}
lock(&tr->hqlock);
if(tr->handq != nil)
error(Einuse);
//ZZZ what is the correct buffering here?
tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
if(tr->handq == nil)
error("can't allocate handshake queue");
tr->hqref = 1;
unlock(&tr->hqlock);
poperror();
}
.
424,425c
&& (strcmp(up->user, tr->user) != 0
|| (perm & tr->perm) != perm))
.
354,374d
186,192d
114,115c
char user[NAMELEN];
int perm;
.
111c
OneWay out;
.
109a
Lock hqlock;
int hqref;
Queue *handq; /* queue of handshake messages */
.
105,108c
OneWay in;
Block *processed; /* next bunch of application data */
Block *unprocessed; /* data read from c but not parsed into records */
.
17a
MaxCipherRecLen = MaxRecLen + 2048,
//ZZZ
Maxdstate = 64,
.
## diffname port/devtls.c 2001/0406
## diff -e /n/emeliedump/2001/0405/sys/src/9/port/devtls.c /n/emeliedump/2001/0406/sys/src/9/port/devtls.c
1519,1530c
tr->state = SClosed;
tr->ref = 1;
strncpy(tr->user, up->user, sizeof(tr->user));
tr->perm = 0660;
return tr;
.
1516,1517c
tr = mallocz(sizeof(*tr), 1);
if(tr == nil)
.
1513,1514c
TlsRec *tr;
.
1510,1511c
static TlsRec *
mktlsrec(void)
.
1504a
*pp = mktlsrec();
if(pp - dstate >= dshiwat)
dshiwat++;
t = TYPE(ch->qid);
if(t == Qclonus)
t = Qctl;
ch->qid.path = QID(pp - dstate, t);
ch->qid.vers = 0;
.
1503d
1496,1497d
1484,1485d
1480,1482c
for(pp = dstate; pp < ep; pp++)
if(*pp == nil)
.
1472c
int t, newmax;
.
1469c
newtls(Chan *ch)
.
1465c
tlsSetState(tr, SClosed, ~0);
.
1442c
if(tr->state & old)
tr->state = new;
.
1427,1440d
1425c
tlsSetState(TlsRec *tr, int new, int old)
.
1417,1421c
unlock(&tr->statelk);
if(s != SError)
alertHand(tr, msg);
.
1410,1415c
lock(&tr->statelk);
s = tr->state;
tr->state = SError;
if(s != SError){
strncpy(tr->err, msg, ERRLEN - 1);
tr->err[ERRLEN - 1] = '\0';
.
1407,1408c
int s;
.
1405c
tlsError(TlsRec *tr, char *msg)
.
1401,1403d
1398c
tlsError(tr, msg);
.
1396d
1394a
if(fatal)
tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
.
1382a
msg = tlserrs[i].msg;
.
1380a
msg = "tls unknown alert";
.
1378a
char *msg;
.
1284a
}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");
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
poperror();
free(cb);
poperror();
/*
* the real work is done as the message is written
* so the stream is encrypted in sync.
*/
b = allocb(1);
*b->wp++ = 1;
tlsrecwrite(tr, RChangeCipherSpec, b);
return n;
}else if(strcmp(cb->f[0], "opened") == 0){
if(cb->nf != 1)
error("usage: opened");
if(tr->in.sec == nil || tr->out.sec == nil)
error("cipher must be configure before enabling data messages");
lock(&tr->statelk);
if(tr->state != SHandshake && tr->state != SOpen){
unlock(&tr->statelk);
//ZZZ bad error message
error("can't set open state");
}
tr->state = SOpen;
unlock(&tr->statelk);
tr->opened = 1;
}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");
m = strtol(cb->f[1], nil, 0);
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
poperror();
free(cb);
poperror();
sendAlert(tr, m);
if(m == ECloseNotify)
tlsclosed(tr, SLClose);
return n;
.
1189,1235d
1175c
error("usage: version vers");
.
1172c
tlsSetState(tr, SHandshake, SClosed);
.
1164c
error("usage: fd open-fd version");
.
1013,1015c
checkstate(tr, 0, SOpen);
.
988a
poperror();
.
987a
if(waserror()){
if(strcmp(up->error, "interrupted") != 0)
tlsError(tr, "channel error");
nexterror();
}
.
920,922c
checkstate(tr, type != RApplication, ok);
.
918a
ok = SHandshake|SOpen|SRClose;
if(type == RAlert)
ok |= SAlert;
.
907c
int n, maclen, ok;
.
832a
if(*b->rp++ == RAlert){
strncpy(up->error, (char*)b->rp, ERRLEN - 1);
up->error[ERRLEN - 1] = '\0';
error(up->error);
}
.
829a
.
827,828c
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 && !qcanread(tr->handq))
.
816,818c
checkstate(tr, 0, SOpen);
.
785a
* got a fatal alert message
*/
static void
rcvAlert(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;
}
}
tlsError(tr, s);
if(!tr->opened)
error(s);
error("tls error");
}
/*
* found an error while decoding the input stream
*/
static void
rcvError(TlsRec *tr, int err, char *fmt, ...)
{
char msg[ERRLEN];
va_list arg;
va_start(arg, fmt);
doprint(msg, msg+sizeof(msg), fmt, arg);
va_end(arg);
sendAlert(tr, err);
if(!tr->opened)
error(msg);
error("tls error");
}
/*
* make sure the next hand operation returns with a 'msg' error
*/
static void
alertHand(TlsRec *tr, char *msg)
{
Block *volatile b;
int n;
lock(&tr->hqlock);
if(tr->handq == nil){
unlock(&tr->hqlock);
return;
}
tr->hqref++;
unlock(&tr->hqlock);
n = strlen(msg);
b = nil;
if(waserror()){
if(b != nil)
freeb(b);
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 void
checkstate(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");
}
/*
.
782a
poperror();
.
774,775c
if(!tr->opened)
.
768a
poperror();
.
766a
if(waserror()){
dechandq(tr);
nexterror();
}
b = padblock(b, 1);
*b->rp = RHandshake;
.
753a
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");
.
752a
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");
.
736,751c
if(p[0] == 2)
.
718c
break;
.
688,689d
686d
681a
b = nil;
if(waserror()){
if(b != nil)
freeb(b);
tlsError(tr, "channel error");
nexterror();
}
.
668,670c
if(len > MaxRecLen || len < 0)
.
657a
else
tlsError(tr, "channel error");
.
640a
static void
tlsclosed(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");
}
.
499c
if(tr->c != nil)
.
497a
if(tr->c != nil && !waserror()){
checkstate(tr, 0, SOpen|SHandshake|SRClose);
sendAlert(tr, ECloseNotify);
poperror();
}
.
423a
tr->ref++;
.
422c
lock(&tr->hqlock);
if(tr->handq != nil)
error(Einuse);
//ZZZ what is the correct buffering here?
tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
if(tr->handq == nil)
error("can't allocate handshake queue");
tr->hqref = 1;
unlock(&tr->hqlock);
poperror();
.
420c
nexterror();
.
400,418c
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()){
.
385c
tr = newtls(c);
.
224c
static void tlsSetState(TlsRec *tr, int new, int old);
.
213,214c
static void tlsError(TlsRec*, char *);
static void alertHand(TlsRec*, char *);
static TlsRec *newtls(Chan *c);
static TlsRec *mktlsrec(void);
.
208a
static void checkstate(TlsRec *, int, int);
.
185a
enum
{
Maxdstate = 64,
};
.
182c
0, "no renegotiation"},
.
140c
1, "bad record mac"},
.
136c
0, "close notify"},
.
112a
/* handshake queue */
.
108c
/* input side -- protected by in.io */
.
103c
int state;
.
100c
char verset; /* version has been set */
char opened; /* opened command every issued? */
char err[ERRLEN]; /* error message to return to handshake requests */
.
47,48c
/* alerts */
.
45d
39,40c
/* record types */
.
30,37c
/* 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
.
28d
21d
## diffname port/devtls.c 2001/0407
## diff -e /n/emeliedump/2001/0406/sys/src/9/port/devtls.c /n/emeliedump/2001/0407/sys/src/9/port/devtls.c
1653c
unlock(&tdlock);
.
1651c
ch->qid.path = QID(pp - tlsdevs, t);
.
1646,1647c
if(pp - tlsdevs >= tdhiwat)
tdhiwat++;
.
1639,1643c
memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
tlsdevs = np;
pp = &tlsdevs[maxtlsdevs];
memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
maxtlsdevs = newmax;
.
1635,1637c
newmax = 2 * maxtlsdevs;
if(newmax > MaxTlsDevs)
newmax = MaxTlsDevs;
.
1630,1631c
if(maxtlsdevs >= MaxTlsDevs) {
unlock(&tdlock);
.
1624,1626c
lock(&tdlock);
ep = &tlsdevs[maxtlsdevs];
for(pp = tlsdevs; pp < ep; pp++)
.
1621c
unlock(&tdlock);
.
1466c
if((tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs)) == 0)
.
1421,1422c
error("can't enable data messages");
.
1308d
1264c
tr = tlsdevs[CONV(c->qid)];
.
1159c
tr = tlsdevs[CONV(c->qid)];
.
982a
qlock(&tr->hqread);
if(tr->hprocessed == nil){
b = qbread(tr->handq, MaxRecLen + 1);
if(*b->rp++ == RAlert){
strncpy(up->error, (char*)b->rp, ERRLEN - 1);
up->error[ERRLEN - 1] = '\0';
freeb(b);
error(up->error);
}
tr->hprocessed = b;
}
b = qremove(&tr->hprocessed, n, 0);
poperror();
qunlock(&tr->hqread);
.
977,981c
if(waserror()){
qunlock(&tr->hqread);
nexterror();
.
972c
while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
.
946c
tr = tlsdevs[CONV(c->qid)];
.
499,500c
tlsdevs[CONV(c->qid)] = nil;
unlock(&tdlock);
.
496c
unlock(&tdlock);
.
494c
lock(&tdlock);
.
487c
tr = tlsdevs[CONV(c->qid)];
.
466,468c
if(--tr->hqref == 0){
if(tr->handq != nil){
qfree(tr->handq);
tr->handq = nil;
}
if(tr->hprocessed != nil){
freeb(tr->hprocessed);
tr->hprocessed = nil;
}
.
451c
tr = tlsdevs[CONV(c->qid)];
.
428c
unlock(&tdlock);
.
402,403c
lock(&tdlock);
pp = &tlsdevs[CONV(c->qid)];
.
399c
unlock(&tdlock);
.
330c
ds = tlsdevs[CONV(c->qid)];
.
295c
ds = tlsdevs[CONV(c->qid)];
.
284c
if(s > tdhiwat)
.
276c
ds = tlsdevs[s];
.
273c
if(s < tdhiwat) {
.
209c
#define CONV(x) (((x).path >> 5)&(MaxTlsDevs-1))
.
189,192c
static Lock tdlock;
static int tdhiwat;
static int maxtlsdevs = 128;
static TlsRec **tlsdevs;
.
186c
/* max. open tls connections */
MaxTlsDevs = 1024
.
181d
113c
Queue *handq; /* queue of handshake messages */
Block *hprocessed; /* remainder of last block read from handq */
QLock hqread; /* protects reads for hprocessed, handq */
.
111c
Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */
.
107,108c
Block *processed; /* next bunch of application data */
Block *unprocessed; /* data read from c but not parsed into records */
.
92,97c
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[ERRLEN]; /* error message to return to handshake requests */
.
29,35c
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 */
.
21a
/* protocol versions we can accept */
.
19,20d
16a
/* buffer limits */
.
## diffname port/devtls.c 2001/0410
## diff -e /n/emeliedump/2001/0407/sys/src/9/port/devtls.c /n/emeliedump/2001/0410/sys/src/9/port/devtls.c
1690a
}
static void
freeSec(Secret *s)
{
if(s != nil){
free(s->enckey);
free(s);
}
}
static int
noenc(Secret *, uchar *, int n)
{
return n;
}
static int
rc4enc(Secret *sec, uchar *buf, int n)
{
rc4(sec->enckey, buf, n);
return n;
}
static int
tlsunpad(uchar *buf, int n, int block)
{
int pad, nn;
pad = buf[n - 1];
nn = n - 1 - pad;
if(nn <= 0 || n % block)
return -1;
while(--n > nn)
if(pad != buf[n - 1])
return -1;
return nn;
}
static int
sslunpad(uchar *buf, int n, int block)
{
int pad, nn;
pad = buf[n - 1];
nn = n - 1 - pad;
if(nn <= 0 || n % block)
return -1;
return nn;
}
static int
blockpad(uchar *buf, int n, int block)
{
int pad, nn;
nn = n + block;
nn -= nn % block;
pad = nn - (n + 1);
while(n < nn)
buf[n++] = pad;
return nn;
}
static int
des3enc(Secret *sec, uchar *buf, int n)
{
n = blockpad(buf, n, 8);
if(n < 0 || (n & 7))
return -1;
des3CBCencrypt(buf, n, sec->enckey);
return n;
}
static int
des3dec(Secret *sec, uchar *buf, int n)
{
if(n & 7)
return -1;
des3CBCdecrypt(buf, n, sec->enckey);
return (*sec->unpad)(buf, n, 8);
}
static DigestState*
nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *)
{
return nil;
.
1410a
if(tr->version == SSL3Version){
toc->unpad = sslunpad;
tos->unpad = sslunpad;
}else{
toc->unpad = tlsunpad;
tos->unpad = tlsunpad;
}
.
1396,1403c
if(!ha->initkey || !ea->initkey)
error("misimplemented secret algorithm");
(*ha->initkey)(ha, tr->version, tos, &x[0]);
(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
if(!tos->mac || !tos->enc || !tos->dec
|| !toc->mac || !toc->enc || !toc->dec)
error("missing algorithm implementations");
.
1386a
freeSec(tos);
freeSec(toc);
.
1385a
tos = nil;
toc = nil;
.
1376c
freeSec(tr->out.new);
.
1372c
freeSec(tr->in.new);
.
1278c
Secret *volatile tos, *volatile toc;
.
1255,1256c
{ "clear", 0, 0, initclearenc },
{ "rc4_128", 128/8, 0, initRC4key },
{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
.
1252a
static void
initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv)
{
s->enckey = smalloc(sizeof(DES3state));
s->enc = des3enc;
s->dec = des3dec;
//ZZZ type hack
setupDES3state(s->enckey, (void*)p, iv);
}
static void
initclearenc(Encalg *, Secret *s, uchar *, uchar *)
{
s->enc = noenc;
s->dec = noenc;
}
.
1250c
s->enckey = smalloc(sizeof(RC4state));
s->enc = rc4enc;
s->dec = rc4enc;
setupRC4state(s->enckey, p, ea->keylen);
.
1222a
{ "sha1", SHA1dlen, initsha1key, },
.
1221c
{ "clear", 0, initclearmac, },
.
1218a
static void
initclearmac(Hashalg *, int, Secret *s, uchar *)
{
s->maclen = 0;
s->mac = nomac;
}
static void
initsha1key(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);
}
.
1149c
freeSec(out->sec);
.
1142,1144d
1139a
/* encrypt */
n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
nb->wp = nb->rp + n;
.
1136,1137c
(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
.
1122c
nb = padblock(bb, -(block - n));
.
1119a
block = n + maclen + block;
block -= n % block;
.
1114d
1112c
block = n + maclen + block;
block -= n % block;
nb = allocb(block + RecHdrLen);
.
1108a
block = out->sec->block;
}
.
1107c
if(out->sec != nil){
.
761c
freeSec(in->sec);
.
742c
b->wp = b->rp + in->sec->maclen;
.
732d
729a
len = (*in->sec->dec)(in->sec, p, len);
if(len < 0)
rcvError(tr, EDecodeError, "incorrectly encrypted message");
.
519,522c
freeSec(tr->in.sec);
freeSec(tr->in.new);
freeSec(tr->out.sec);
freeSec(tr->out.new);
.
512c
checkstate(tr, t != Qdata, SOpen|SHandshake|SRClose);
.
460d
236a
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);
.
224a
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);
.
77,78c
void *enckey;
uchar mackey[MaxMacLen];
.
75a
int (*enc)(Secret*, uchar*, int);
int (*dec)(Secret*, uchar*, int);
int (*unpad)(uchar*, int, int);
.
20a
MaxMacLen = SHA1dlen,
.
## diffname port/devtls.c 2001/0411
## diff -e /n/emeliedump/2001/0410/sys/src/9/port/devtls.c /n/emeliedump/2001/0411/sys/src/9/port/devtls.c
1841,1842d
1832,1833d
1309a
s->block = 0;
.
1301,1302c
s->block = 8;
setupDES3state(s->enckey, (uchar(*)[8])p, iv);
.
1291a
s->block = 0;
.
1161c
nb->wp = p + RecHdrLen + n;
.
1141,1142c
if(pad)
nb = padblock(bb, -pad);
.
1138,1139d
1129,1131c
nb = allocb(n + pad + RecHdrLen);
.
1124c
pad = maclen + out->sec->block;
.
1121a
pad = 0;
.
1114c
* with padding on the front for header and
* back for mac and maximal block padding.
.
1094c
int n, maclen, pad, ok;
.
1074,1084d
757c
b->wp = b->rp + len;
.
475a
unlock(&tdlock);
.
467a
if(waserror()){
unlock(&tdlock);
nexterror();
}
lock(&tdlock);
.
436d
347,348c
lock(&tdlock);
tr = tlsdevs[CONV(c->qid)];
devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, tr->user, tr->perm, dp);
unlock(&tdlock);
.
344c
case Qencalgs:
case Qhashalgs:
perm = 0444;
if(TYPE(c->qid) == Qclonus)
perm = 0555;
devdir(c, c->qid, tlsnames[TYPE(c->qid)], 0, eve, perm, dp);
.
341c
devdir(c, q, tlsnames[TYPE(q)], 0, nm, perm, dp);
unlock(&tdlock);
.
332,339d
330d
326d
322d
318a
unlock(&tdlock);
.
316a
}
.
312,315c
lock(&tdlock);
tr = tlsdevs[CONV(c->qid)];
if(tr != nil){
nm = tr->user;
perm = tr->perm;
}else{
perm = 0;
.
303,304c
sprint(name, "%d", s);
q.path = QID(s, Qconvdir)|CHDIR;
lock(&tdlock);
tr = tlsdevs[s];
if(tr != nil)
nm = tr->user;
else
nm = eve;
devdir(c, q, name, 0, nm, CHDIR|0555, dp);
unlock(&tdlock);
.
301c
s -= 3;
if(s >= tdhiwat)
.
290,298c
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);
.
266,267c
TlsRec *tr;
char name[16], *nm;
int perm;
.
79a
DigestState *(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
int block; /* encryption block len, 0 if none */
.
76d
## diffname port/devtls.c 2001/0418
## diff -e /n/emeliedump/2001/0411/sys/src/9/port/devtls.c /n/emeliedump/2001/0418/sys/src/9/port/devtls.c
1779a
}
static char*
tlsstate(int s)
{
switch(s){
case SHandshake:
return "Handshaking";
case SOpen:
return "Established";
case SRClose:
return "RemoteHangup";
case SLClose:
return "LocalHangup";
case SAlert:
return "Alerting";
case SError:
return "Errored";
case SClosed:
return "Closed";
}
return "Unknown";
.
1499a
toc->encalg = ea->name;
toc->hashalg = ha->name;
tos->encalg = ea->name;
tos->hashalg = ha->name;
.
1227a
tr->dataout += n;
.
1223a
tr->handout += n;
.
1069,1070c
buf = smalloc(Statlen);
snprint(buf, Statlen, "%lud", CONV(c->qid));
n = readstr(offset, a, n, buf);
free(buf);
return n;
.
1067a
case Qstatus:
buf = smalloc(Statlen);
qlock(&tr->in.seclock);
qlock(&tr->out.seclock);
e = buf + Statlen;
s = seprint(buf, e, "%s version 0x%lux", tlsstate(tr->state), tr->version);
if(tr->in.sec != nil)
s = seprint(s, e, " EncIn %s HashIn %s", tr->in.sec->encalg, tr->in.sec->hashalg);
if(tr->in.new != nil)
s = seprint(s, e, " NewEncIn %s NewHashIn %s", tr->in.new->encalg, tr->in.new->hashalg);
if(tr->out.sec != nil)
s = seprint(s, e, " EncOut %s HashOut %s", tr->out.sec->encalg, tr->out.sec->hashalg);
if(tr->out.new != nil)
s = seprint(s, e, " NewEncOut %s NewHashOut %s", tr->out.new->encalg, tr->out.new->hashalg);
seprint(s, e, "\n");
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: %d\n", tr->datain);
s = seprint(s, e, "DataOut: %d\n", tr->dataout);
s = seprint(s, e, "HandIn: %d\n", tr->handin);
seprint(s, e, "HandOut: %d\n", tr->handout);
n = readstr(offset, a, n, buf);
free(buf);
return n;
.
1064a
tr = tlsdevs[CONV(c->qid)];
.
1060a
TlsRec * tr;
.
1059c
char *buf, *s, *e;
.
1046a
tr->handin += BLEN(b);
.
1014a
tr->datain += BLEN(b);
.
532a
case Qstatus:
case Qstats:
.
436a
case Qstatus:
case Qstats:
if((t == Qstatus || t == Qstats) && omode != OREAD)
error(Eperm);
.
369c
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);
.
364c
devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
.
362c
if(t == Qclonus)
.
341,355c
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);
.
331a
if(s < 0 || s >= nelem(convdir))
return -1;
.
274c
t = TYPE(c->qid);
switch(t) {
.
269c
int perm, t;
.
262a
static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
.
259,260c
[Qstatus] "status",
[Qstats] "stats",
.
255a
[Qencalgs] "encalgs",
[Qhashalgs] "hashalgs",
.
250a
static char *tlsstate(int s);
.
212,213c
Qstatus,
Qstats,
.
207a
Qencalgs,
Qhashalgs,
.
131d
103a
vlong handin; /* bytes communicated by the record layer */
vlong handout;
vlong datain;
vlong dataout;
.
95d
75a
char *encalg; /* name of encryption alg */
char *hashalg; /* name of hash alg */
.
16a
Statlen= 1024, /* max. length of status or stats message */
.
13,14c
typedef struct OneWay OneWay;
typedef struct Secret Secret;
typedef struct TlsRec TlsRec;
typedef struct TlsErrs TlsErrs;
.
## diffname port/devtls.c 2001/0420
## diff -e /n/emeliedump/2001/0418/sys/src/9/port/devtls.c /n/emeliedump/2001/0420/sys/src/9/port/devtls.c
1859c
return "LocalClosed";
.
1857c
return "RemoteClosed";
.
1107,1108c
seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
.
1105c
s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
.
1103c
s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
.
1101c
s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
.
1099c
s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
s = seprint(s, e, "Version: 0x%lux\n", tr->version);
.
1097a
s = buf;
.
## diffname port/devtls.c 2001/0503
## diff -e /n/emeliedump/2001/0420/sys/src/9/port/devtls.c /n/emeliedump/2001/0503/sys/src/9/port/devtls.c
1784,1785d
574c
checkstate(tr, 0, SOpen|SHandshake|SRClose);
.
## diffname port/devtls.c 2001/0504
## diff -e /n/emeliedump/2001/0503/sys/src/9/port/devtls.c /n/emeliedump/2001/0504/sys/src/9/port/devtls.c
1732,1737c
if(!waserror()){
b = allocb(2);
*b->wp++ = fatal + 1;
*b->wp++ = err;
if(fatal)
tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
tlsrecwrite(tr, RAlert, b);
poperror();
}
.
1067c
b = qgrab(&tr->hprocessed, n);
.
1034c
b = qgrab(&tr->processed, n);
.
998,1004d
955,956d
953d
941c
Block *b;
.
878a
poperror();
}
.
877c
}else if(tr->verset && tr->version != SSL3Version && !waserror()){
.
789d
781c
b = qgrab(&tr->unprocessed, len);
.
750c
poperror();
}else
.
748c
if(strcmp(up->error, Eintr) == 0 && !waserror()){
.
734,735c
* 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.
.
717,718c
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;
.
683,715c
b = *l;
if(BLEN(b) == n){
*l = b->next;
b->next = nil;
return b;
.
680c
Block *bb, *b;
.
678c
qgrab(Block **l, int n)
.
674c
* remove at most n bytes from the queue in a single block, if discard is set
.
## diffname port/devtls.c 2001/0505
## diff -e /n/emeliedump/2001/0504/sys/src/9/port/devtls.c /n/emeliedump/2001/0505/sys/src/9/port/devtls.c
863,865c
}else{
unlock(&tr->hqlock);
if(tr->verset && tr->version != SSL3Version && !waserror()){
sendAlert(tr, ENoRenegotiation);
poperror();
}
.
## diffname port/devtls.c 2001/1007
## diff -e /n/emeliedump/2001/0505/sys/src/9/port/devtls.c /n/emeliedump/2001/1007/sys/src/9/port/devtls.c
1826c
kstrdup(&tr->user, up->user);
.
1800a
nmp = smalloc(sizeof *nmp * newmax);
memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
trnames = nmp;
.
1775a
char **nmp;
.
1735,1736c
strncpy(tr->err, msg, ERRMAX - 1);
tr->err[ERRMAX - 1] = '\0';
.
1662d
1624a
if((trnames = smalloc((sizeof *trnames) * maxtlsdevs)) == 0)
panic("tlsinit");
.
1575c
error("cipher must be configured before enabling data messages");
.
1410c
ty = TYPE(c->qid);
switch(ty){
.
1401c
int m, ty;
.
1261c
ty = TYPE(c->qid);
switch(ty) {
.
1253a
TlsRec *tr;
.
1252c
int ty;
.
1238c
if(strcmp(up->errstr, "interrupted") != 0)
.
1109c
snprint(buf, Statlen, "%llud", CONV(c->qid));
.
1072c
ty = TYPE(c->qid);
switch(ty) {
.
1068c
if(c->qid.type & QTDIR)
.
1063c
int i, ty;
.
1044c
nexterror();
.
1041,1042c
strecpy(up->errstr, up->errstr+ERRMAX, (char*)b->rp);
.
1009c
if(ty == Qdata){
.
992c
ty = TYPE(c->qid);
switch(ty) {
.
990a
TlsRec *volatile tr;
.
989c
int ty;
.
912c
char msg[ERRMAX];
.
734c
if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
.
584a
free(tr->user);
.
522a
return rv;
.
520,521c
d = smalloc(n + sizeof *d);
rv = convM2D(dp, n, &d[0], (char*) &d[1]);
if (rv > 0) {
kstrdup(&tr->user, d->uid);
tr->perm = d->mode;
}
free(d);
poperror();
.
512a
.
507,508d
505a
int rv;
.
504c
Dir *d;
.
501,502c
static int
tlswstat(Chan *c, uchar *dp, int n)
.
412c
return devstat(c, db, n, nil, 0, tlsgen);
.
409,410c
static int
tlsstat(Chan *c, uchar *db, int n)
.
406c
return devwalk(c, nc, name, nname, nil, 0, tlsgen);
.
403,404c
static Walkqid*
tlswalk(Chan *c, Chan *nc, char **name, int nname)
.
398c
c->qid.path = QID(0, Qtopdir);
c->qid.type = QTDIR;
.
343,344c
q.path = QID(0, Qprotodir);
q.type = QTDIR;
devdir(c, q, "tls", 0, eve, 0555, dp);
.
338c
if ((name = trnames[s]) == nil) {
name = trnames[s] = smalloc(16);
sprint(name, "%d", s);
}
devdir(c, q, name, 0, nm, 0555, dp);
.
330,331c
q.path = QID(s, Qconvdir);
q.type = QTDIR;
.
303,304c
q.path = QID(0, Qtopdir);
q.type = QTDIR;
devdir(c, q, ".", 0, eve, 0555, dp);
.
298,299c
q.path = QID(0, Qprotodir);
q.type = QTDIR;
devdir(c, q, "tls", 0, eve, 0555, dp);
.
292,293c
q.path = QID(0, Qtopdir);
q.type = QTDIR;
devdir(c, q, "#a", 0, eve, 0555, dp);
.
287a
q.type = QTFILE;
.
285,286d
282c
char *name, *nm;
.
278c
tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp)
.
271,272c
[Qstatus] "status",
[Qstats] "stats",
.
265c
[Qclonus] "clone",
.
207a
static char **trnames;
.
148,195c
{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 paramters"},
{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"},
.
135c
char *user;
.
125c
Lock hqlock; /* protects hqref, alloc & free of handq, hprocessed */
.
102,111c
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;
.
88c
uchar mackey[MaxMacLen];
.
49,72c
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,
.
27,28c
TLSVersion = 0x0301,
SSL3Version = 0x0300,
.
23,24c
RecHdrLen = 5,
MaxMacLen = SHA1dlen,
.
21c
MaxRecLen = 1<<14, /* max payload length of a record layer message */
.
14c
typedef struct Secret Secret;
.
## diffname port/devtls.c 2001/1106
## diff -e /n/emeliedump/2001/1007/sys/src/9/port/devtls.c /n/emeliedump/2001/1106/sys/src/9/port/devtls.c
511c
.
509a
if(d->mode != ~0UL)
.
508c
if(rv == 0)
error(Eshortstat);
if(!emptystr(d->uid)
.
494a
free(d);
.
493a
d = nil;
.
## diffname port/devtls.c 2001/1124
## diff -e /n/emeliedump/2001/1106/sys/src/9/port/devtls.c /n/emeliedump/2001/1124/sys/src/9/port/devtls.c
512c
if(!emptystr(d->uid))
.
## diffname port/devtls.c 2001/1207
## diff -e /n/emeliedump/2001/1124/sys/src/9/port/devtls.c /n/emeliedump/2001/1207/sys/src/9/port/devtls.c
483a
c->iounit = qiomaxatomic;
.
## diffname port/devtls.c 2002/0109
## diff -e /n/emeliedump/2001/1207/sys/src/9/port/devtls.c /n/emeliedump/2002/0109/sys/src/9/port/devtls.c
1668a
devshutdown,
.
## diffname port/devtls.c 2002/0201
## diff -e /n/emeliedump/2002/0109/sys/src/9/port/devtls.c /n/emeliedump/2002/0201/sys/src/9/port/devtls.c
1629,1632c
tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
trnames = smalloc((sizeof *trnames) * maxtlsdevs);
.
## diffname port/devtls.c 2002/0202
## diff -e /n/emeliedump/2002/0201/sys/src/9/port/devtls.c /n/emeliedump/2002/0202/sys/src/9/port/devtls.c
167c
{EProtocolVersion, EIllegalParameter, EProtocolVersion, 0, "protocol version not supported"},
.
## diffname port/devtls.c 2002/0203
## diff -e /n/emeliedump/2002/0202/sys/src/9/port/devtls.c /n/emeliedump/2002/0203/sys/src/9/port/devtls.c
2041a
}
static void
put24(uchar *p, int x)
{
p[0] = x>>16;
p[1] = x>>8;
p[2] = x;
.
871a
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);
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;
.
745,747c
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) - 5;
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);
}
.
675,676c
* remove at most n bytes from the queue
.
167c
{EProtocolVersion, EIllegalParameter, EProtocolVersion, 1, "protocol version not supported"},
.
47a
SSL2ClientHello = 1,
HSSL2ClientHello = 9, /* local convention; see tlshand.c */
.
## diffname port/devtls.c 2002/0206
## diff -e /n/emeliedump/2002/0203/sys/src/9/port/devtls.c /n/emeliedump/2002/0206/sys/src/9/port/devtls.c
901c
put24(&b->rp[2], len+3);
.
763c
rcvError(tr, EProtocolVersion, "record layer saw ver %x, not %x/%d; %d %d",
ver, tr->version, tr->verset, type, len);
.
752c
len = (get16(header) & ~0x8000) - 3;
.
748c
if((tr->handin == 0) && (header[0] & 0x80)){
.
## diffname port/devtls.c 2002/0214
## diff -e /n/emeliedump/2002/0206/sys/src/9/port/devtls.c /n/emeliedump/2002/0214/sys/src/9/port/devtls.c
765,766c
if(len > MaxCipherRecLen || len < 0)
rcvError(tr, ERecordOverflow, "record message too long %d", len);
.
467c
tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
.
## diffname port/devtls.c 2002/0217
## diff -e /n/emeliedump/2002/0214/sys/src/9/port/devtls.c /n/emeliedump/2002/0217/sys/src/9/port/devtls.c
962c
vseprint(msg, msg+sizeof(msg), fmt, arg);
.
## diffname port/devtls.c 2002/0220
## diff -e /n/emeliedump/2002/0217/sys/src/9/port/devtls.c /n/emeliedump/2002/0220/sys/src/9/port/devtls.c
1148,1151c
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);
.
1130c
s = seprint(s, e, "Version: 0x%x\n", tr->version);
.
## diffname port/devtls.c 2002/0413
## diff -e /n/emeliedump/2002/0220/sys/src/9/port/devtls.c /n/emeliedump/2002/0413/sys/src/9/port/devtls.c
763,764c
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);
.
## diffname port/devtls.c 2003/0223
## diff -e /n/emeliedump/2002/0413/sys/src/9/port/devtls.c /n/emeliedump/2003/0223/sys/src/9/port/devtls.c
806c
if(unpad_len <= in->sec->maclen || memcmp(hmac, p+len, in->sec->maclen) != 0)
.
794,799c
/* 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;
.
733c
int len, type, ver, unpad_len;
.
## diffname port/devtls.c 2003/0406
## diff -e /n/emeliedump/2003/0223/sys/src/9/port/devtls.c /n/emeliedump/2003/0406/sys/src/9/port/devtls.c
321c
if((name = trnames[s]) == nil) {
.
|