Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/devtls.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## 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) {
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.