~kris/9p

9hist

03aa1d4aba79d611d4faa06e6a711c545aca3bed — David du Colombier 29 years ago a687bf0
Plan 9 from Bell Labs 1996-10-29
2 files changed, 764 insertions(+), 297 deletions(-)

M port/devcons.c
M port/devssl.c
M port/devcons.c => port/devcons.c +9 -10
@@ 906,15 906,6 @@ conswstat(Chan*, char*)
	error(Eperm);
}

int
nrand(int n)
{
	static ulong randn;

	randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
	return (randn>>16) % n;
}

void
setterm(char *f)
{


@@ 935,10 926,18 @@ static struct
	uchar	*rp;
	uchar	*wp;
	uchar	next;
	uchar	bits;
	uchar	wakeme;
	ushort	bits;
	ulong	randn;
} rb;

int
nrand(int n)
{
	rb.randn ^= rb.bits;
	rb.randn = rb.randn*1103515245 + 12345 + MACHP(0)->ticks;
	return (rb.randn>>16) % n;
}

static int
rbnotfull(void*)

M port/devssl.c => port/devssl.c +755 -287
@@ 10,11 10,13 @@

#include <libcrypt.h>

#define NOSPOOKS 1

typedef struct OneWay OneWay;
struct OneWay
{
	QLock	q;
	QLock	ctlq;

	void	*state;		/* encryption state */
	int	slen;		/* hash data length */


@@ 25,17 27,18 @@ struct OneWay
enum
{
	/* connection states */
	Algwait=	0,	/* waiting for user to write algorithm */
	Fdwait=		1,	/* waiting for user to write fd */
	Secretinwait=	2,	/* waiting for user to write input secret */
	Secretoutwait=	3,	/* waiting for user to write output secret */
	Established=	4,
	Closed=		5,
	Sincomplete=	0,
	Sclear,
	Sencrypting,
	Sdigesting,

	/* encryption algorithms */
	Noencryption=	0,
#ifdef NOSPOOKS
	DESCBC=		1,
	DESECB=		2
	DESECB=		2,
#endif NOSPOOKS
	RC4=		3
};

typedef struct Dstate Dstate;


@@ 43,6 46,8 @@ 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 */



@@ 60,60 65,130 @@ struct Dstate

	/* output side */
	OneWay	out;

	/* protections */
	char	user[NAMELEN];
	int	perm;
};

Lock	dslock;
int	dshiwat;
int	maxdstate = 20;
Dstate** dstate;

enum
{
	Maxdmsg=	1<<16
	Maxdmsg=	1<<16,
	Maxdstate=	64
};

enum{
	Qdir,
	Qclone
	Qtopdir		= 1,	/* top level directory */
	Qclonus,
	Qconvdir,		/* directory for a conversation */
	Qdata,
	Qctl,
	Qsecretin,
	Qsecretout
};
Dirtab digesttab[]={
	"ssl",		{Qclone, 0},	0,	0666,
};
#define Ndigesttab (sizeof(digesttab)/sizeof(Dirtab))

/* a circular list of random numbers */
typedef struct
{
	uchar	*rp;
	uchar	*wp;
	uchar	buf[1024];
	uchar	*ep;
} Randq;
Randq randq;

void producerand(void);
#define TYPE(x) 	((x).path & 0xf)
#define CONV(x) 	(((x).path >> 4)&(Maxdstate-1))
#define QID(c, y) 	(((c)<<4) | (y))

static void	ensure(Dstate*, Block**, int);
static void	consume(Block**, uchar*, int);
static void	setsecret(Dstate*, OneWay*, 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*, long);
static void	dighangup(Dstate*);
static Chan*	buftochan(char*);
static void	sslhangup(Dstate*);
static Dstate*	dsclone(Chan *c);
static void	dsnew(Chan *c, Dstate **);

void
sslreset(void)
{
}

int
sslgen(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 < 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:
		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;
		}
		devdir(c, q, p, 0, nm, 0660, dp);
		return 1;
	}
	return -1;
}

void
sslinit(void)
{
	randq.ep = randq.buf + sizeof(randq.buf);
	randq.rp = randq.wp = randq.buf;
	if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0)
		panic("sslinit");
}

Chan *
sslattach(void *spec)
{
	return devattach('D', spec);
	Chan *c;

	c = devattach('D', spec);
	c->qid.path = QID(0, Qtopdir)|CHDIR;
	c->qid.vers = 0;
	return c;
}

Chan *


@@ 125,29 200,77 @@ sslclone(Chan *c, Chan *nc)
int
sslwalk(Chan *c, char *name)
{
	return devwalk(c, name, digesttab, Ndigesttab, devgen);
	return devwalk(c, name, 0, 0, sslgen);
}

void
sslstat(Chan *c, char *db)
{
	devstat(c, db, digesttab, Ndigesttab, devgen);
	devstat(c, db, 0, 0, sslgen);
}

Chan *
sslopen(Chan *c, int omode)
{
	Dstate *s;
	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("sslopen");
	case Qtopdir:
	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);

	switch(c->qid.path & ~CHDIR){
	case Qclone:
		s = malloc(sizeof(Dstate));
		memset(s, 0, sizeof(*s));
		s->state = Algwait;
		c->aux = s;
			s->ref++;
		}
		unlock(&dslock);
		poperror();
		break;
	}
	return devopen(c, omode, digesttab, Ndigesttab, devgen);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void


@@ 170,9 293,19 @@ sslremove(Chan *c)
void
sslwstat(Chan *c, char *dp)
{
	USED(c);
	USED(dp);
	error(Eperm);
	Dir d;
	Dstate *s;

	convM2D(dp, &d);

	s = dstate[CONV(c->qid)];
	if(s == 0)
		error(Ebadusefd);
	if(strcmp(s->user, up->user) != 0)
		error(Eperm);

	memmove(s->user, d.uid, NAMELEN);
	s->perm = d.mode;
}

void


@@ 180,17 313,172 @@ sslclose(Chan *c)
{
	Dstate *s;

	if(c->aux){
		s = c->aux;
		dighangup(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 == 0)
			break;

		lock(&dslock);
		if(--s->ref > 0) {
			unlock(&dslock);
			break;
		}
		dstate[CONV(c->qid)] = 0;
		unlock(&dslock);

		sslhangup(s);
		if(s->c)
			close(s->c);
		if(s->in.secret)
			free(s->in.secret);
		if(s->out.secret)
			free(s->out.secret);
		if(s->in.state)
			free(s->in.state);
		if(s->out.state)
			free(s->out.state);
		free(s);

	}
}

static int
blen(Block *bp)
{
	int i = 0;

	for(; bp; bp = bp->next)
		i += BLEN(bp);
	return i;
}

/*
 *  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);
	}
}

/* 
 *  free a list of blocks
 */
void
freeblist(Block *b)
{
	Block *next;

	for(; b != 0; b = next){
		next = b->next;
		freeb(b);
	}
}

/*
 *  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;
			break;
		} 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;
}

Block*


@@ 199,76 487,72 @@ sslbread(Chan *c, long n, ulong offset)
	volatile struct { Dstate *s; } s;
	Block *b;
	uchar count[2];
	int i, len, pad;
	int len, pad;

	USED(offset);

	s.s = c->aux;
	if(s.s == 0 || s.s->state != Established)
	s.s = dstate[CONV(c->qid)];
	if(s.s == 0)
		panic("sslbread");
	if(s.s->state == Sincomplete)
		error(Ebadusefd);

	if(waserror()){
		qunlock(&s.s->in.q);
		dighangup(s.s);
		sslhangup(s.s);
		nexterror();
	}
	qlock(&s.s->in.q);

	b = s.s->processed;
	if(b == 0){
	
	if(s.s->processed == 0){
		/* read in the whole message */
		s.s->processed = s.s->unprocessed;
		s.s->unprocessed = 0;
		ensure(s.s, &s.s->processed, 2);
		consume(&s.s->processed, count, 2);
		ensure(s.s, &s.s->unprocessed, 2);
		consume(&s.s->unprocessed, count, 2);
		if(count[0] & 0x80){
			len = ((count[0] & 0x7f)<<8) | count[1];
			ensure(s.s, &s.s->processed, len);
			ensure(s.s, &s.s->unprocessed, len);
			pad = 0;
		} else {
			len = ((count[0] & 0x3f)<<8) | count[1];
			ensure(s.s, &s.s->processed, len+1);
			consume(&s.s->processed, count, 1);
			ensure(s.s, &s.s->unprocessed, len+1);
			consume(&s.s->unprocessed, count, 1);
			pad = count[0];
			if(pad > len){
				print("pad %d buf len %d\n", pad, len);
				error("bad pad in ssl message");
			}
		}

		/* trade memory bandwidth for less processing complexity */
		b = s.s->processed = pullupblock(s.s->processed, len);
		/* put extra on unprocessed queue */
		s.s->processed = qremove(&s.s->unprocessed, len, 0);

		/* put remainder on unprocessed queue */
		i = BLEN(b);
		if(i > len){
			i -= len;
			s.s->unprocessed = allocb(i);
			memmove(s.s->unprocessed->wp, b->rp+len, i);
			s.s->unprocessed->wp += i;
			b->wp -= i;
		if(waserror()){
			qunlock(&s.s->in.ctlq);
			nexterror();
		}

		if(s.s->encryptalg)
			b = decryptb(s.s, b);
		else {
			if(BLEN(b) < s.s->diglen)
				error("baddigest");
			checkdigestb(s.s, b);
			b->rp += s.s->diglen;
		qlock(&s.s->in.ctlq);
		switch(s.s->state){
		case Sencrypting:
			s.s->processed = decryptb(s.s, s.s->processed);
			break;
		case Sdigesting:
			s.s->processed = pullupblock(s.s->processed, s.s->diglen);
			if(s.s->processed == 0)
				error("ssl message too short");
			checkdigestb(s.s, s.s->processed);
			s.s->processed->rp += s.s->diglen;
			break;
		}
		qunlock(&s.s->in.ctlq);
		poperror();

		/* remove pad */
		if(b->wp - b->rp < pad)
			panic("sslbread");
		b->wp -= pad;
		s.s->processed = b;
		if(pad)
			s.s->processed = qremove(&s.s->processed, len - pad, 1);
	}

	if(BLEN(b) > n){
		b = allocb(n);
		memmove(b->wp, s.s->processed->rp, n);
		b->wp += n;
		s.s->processed->rp += n;
	} else 
		s.s->processed = b->next;
	/* return at most what was asked for */
	b = qremove(&s.s->processed, n, 0);

	qunlock(&s.s->in.q);
	poperror();


@@ 280,29 564,67 @@ long
sslread(Chan *c, void *a, long n, ulong offset)
{
	volatile struct { Block *b; } b;
	Block *nb;
	uchar *va;
	int i;
	char buf[128];

	switch(c->qid.path & ~CHDIR){
	case Qdir:
		return devdirread(c, a, n, digesttab, Ndigesttab, devgen);
	}
	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, sslgen);

	b.b = sslbread(c, n, offset);
	switch(TYPE(c->qid)) {
	default:
		error(Ebadusefd);
	case Qctl:
		sprint(buf, "%d", CONV(c->qid));
		return readstr(offset, a, n, buf);
	case Qdata:
		b.b = sslbread(c, n, offset);
		break;
	}

	if(waserror()){
		freeb(b.b);
		freeblist(b.b);
		nexterror();
	}

	n = BLEN(b.b);
	memmove(a, b.b->rp, n);
	freeb(b.b);
	n = 0;
	va = a;
	for(nb = b.b; nb; nb = nb->next){
		i = BLEN(nb);
		memmove(va+n, nb->rp, i);
		n += i;
	}

	freeblist(b.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)
{
	int j;
	ushort rn;

	j = 0;
	rn = 0;
	while(len-- > 0){
		if(j == 0){
			rn = nrand(1<<16);
			*buf++ = rn;
		} else
			*buf++ = rn>>8;
		j ^= 1;
	}
}

/*
 *  use SSL record format, add in count and digest or encrypt
 */
long


@@ 315,15 637,19 @@ sslbwrite(Chan *c, Block *b, ulong offset)
	uchar *p;

	bb.b = b;
	s.s = c->aux;
	if(s.s == 0 || s.s->state != Established)
	s.s = dstate[CONV(c->qid)];
	if(s.s == 0)
		panic("sslbwrite");
	if(s.s->state == Sincomplete){
		freeb(b);
		error(Ebadusefd);
	}

	if(waserror()){
		qunlock(&s.s->out.q);
		if(bb.b)
			freeb(bb.b);
		dighangup(s.s);
		sslhangup(s.s);
		nexterror();
	}
	qlock(&s.s->out.q);


@@ 366,7 692,7 @@ sslbwrite(Chan *c, Block *b, ulong offset)
		/* SSL style count */
		if(pad){
			nb = padblock(nb, -pad);
			memset(nb->wp, 0, pad);
			randfill(nb->wp, pad);
			nb->wp += pad;
			m += pad;



@@ 382,13 708,18 @@ sslbwrite(Chan *c, Block *b, ulong offset)
			offset = 2;
		}

		if(s.s->encryptalg)
		switch(s.s->state){
		case Sencrypting:
			nb = encryptb(s.s, nb, offset);
		else
			break;
		case Sdigesting:
			nb = digestb(s.s, nb, offset);
			break;
		}

		(*devtab[s.s->c->type].bwrite)(s.s->c, nb, offset);

		m = BLEN(nb);
		(*devtab[s.s->c->type].bwrite)(s.s->c, nb, s.s->c->offset);
		s.s->c->offset += m;
	}
	qunlock(&s.s->out.q);
	poperror();


@@ 396,78 727,78 @@ sslbwrite(Chan *c, Block *b, ulong offset)
	return rv;
}

static void
setsecret(OneWay *w, uchar *secret, int n)
{
	if(w->secret)
		free(w->secret);

	w->secret = malloc(n);
	memmove(w->secret, secret, n);
	w->slen = n;
	w->mid = 0;

}

#ifdef NOSPOOKS
static void
initDESkey(OneWay *w)
{
	if(w->state){
		free(w->state);
		w->state = 0;
	}

	w->state = malloc(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");
}
#endif NOSPOOKS

static void
initRC4key(OneWay *w)
{
	if(w->state){
		free(w->state);
		w->state = 0;
	}

#ifndef NOSPOOKS
	if(w->slen > 5)
		w->slen = 5;
#endif NOSPOOKS

	w->state = malloc(sizeof(RC4state));
	setupRC4state(w->state, w->secret, w->slen);
}

long
sslwrite(Chan *c, void *a, long n, ulong offset)
{
	Dstate *s;
	volatile struct { Dstate *s; } s;
	volatile struct { Block *b; } b;
	int m;
	int m, t;
	char *p, *e, buf[32];

	switch(c->qid.path & ~CHDIR){
	case Qclone:
		break;
	default:
		error(Ebadusefd);
	}
	s.s = dstate[CONV(c->qid)];
	if(s.s == 0)
		panic("sslwrite");

	s = c->aux;
	if(s == 0)
		error(Ebadusefd);
	t = TYPE(c->qid);
	if(t == Qdata){
		if(s.s->state == Sincomplete)
			error(Ebadusefd);

	switch(s->state){
	case Algwait:
		/* get algorithm */
		if(n >= sizeof(buf))
			error(Ebadarg);
		strncpy(buf, a, n);
		buf[n] = 0;
		s->blocklen = 1;
		s->diglen = 0;
		if(strcmp(buf, "md5") == 0){
			s->hf = md5;
			s->diglen = MD5dlen;
		} else if(strcmp(buf, "sha") == 0){
			s->hf = sha;
			s->diglen = SHAdlen;
		} else if(strcmp(buf, "descbc") == 0){
			s->encryptalg = DESCBC;
			s->blocklen = 8;
		} else if(strcmp(buf, "desecb") == 0){
			s->encryptalg = DESECB;
			s->blocklen = 8;
		} else
			error(Ebadarg);
		s->state = Fdwait;
		break;
	case Fdwait:
		/* get communications channel */
		s->c = buftochan(a, n);
		s->state = Secretinwait;
		break;
	case Secretinwait:
		/* get secret for incoming messages */
		setsecret(s, &s->in, a, n);
		s->state = Secretoutwait;
		break;
	case Secretoutwait:
		/* get secret for outgoing messages */
		setsecret(s, &s->out, a, n);
		if(s->blocklen != 1){
			s->max = (1<<15) - s->diglen;
			s->max -= s->max % s->blocklen;
			s->maxpad = (1<<14) - s->diglen;
			s->maxpad -= s->maxpad % s->blocklen;
		} else
			s->maxpad = s->max = (1<<15) - s->diglen;
		s->state = Established;
		break;
	case Established:
		p = a;
		for(e = p + n; p < e; p += m){
		e = p + n;
		do {
			m = e - p;
			if(m > s->max)
				m = s->max;
			if(m > s.s->max)
				m = s.s->max;
	
			b.b = allocb(m);
			if(waserror()){


@@ 479,103 810,137 @@ sslwrite(Chan *c, void *a, long n, ulong offset)
			b.b->wp += m;
	
			sslbwrite(c, b.b, offset);
		}
		break;
	default:
		error(Ebadusefd);
	}

	return n;
}
			p += m;
		} while(p < e);
		return n;
	}

/*
 *  make sure we have at least 'n' bytes in list 'l'
 */
static void
ensure(Dstate *s, Block **l, int n)
{
	int i, sofar;
	Block *b;
	/* mutex with operations using what we're about to change */
	if(waserror()){
		qunlock(&s.s->in.ctlq);
		qunlock(&s.s->out.q);
		nexterror();
	}
	qlock(&s.s->in.ctlq);
	qlock(&s.s->out.q);

	b = *l;
	if(b){
		sofar = BLEN(b);
		l = &b->next;
	} else
		sofar = 0;
	switch(t){
	default:
		panic("sslwrite");
	case Qsecretin:
		setsecret(&s.s->in, a, n);
		goto out;
		return n;
	case Qsecretout:
		setsecret(&s.s->out, a, n);
		goto out;
		return n;
	case Qctl:
		break;
	}

	while(sofar < n){
		b = (*devtab[s->c->type].bread)(s->c, Maxdmsg, 0);
		if(b == 0)
			error(Ehungup);
		i = BLEN(b);
		if(i <= 0){
			freeb(b);
			continue;
	if(n >= sizeof(buf))
		error(Ebadarg);
	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.s->c = buftochan(p);

		/* default is clear (msg delimiters only) */
		s.s->state = Sclear;
		s.s->blocklen = 1;
		s.s->diglen = 0;
		s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
	} else if(strcmp(buf, "alg") == 0 && p != 0){
		s.s->blocklen = 1;
		s.s->diglen = 0;

		if(s.s->c == 0)
			error("must set fd before algorithm");

		if(strcmp(p, "clear") == 0){
			s.s->state = Sclear;
			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
			goto out;
		}

		*l = b;
		l = &b->next;
		sofar += i;
	}
}
		if(s.s->in.secret && s.s->out.secret == 0)
			setsecret(&s.s->out, s.s->in.secret, s.s->in.slen);
		if(s.s->out.secret && s.s->in.secret == 0)
			setsecret(&s.s->in, s.s->out.secret, s.s->out.slen);
		if(strcmp(p, "md5") == 0){
			s.s->hf = md5;
			s.s->diglen = MD5dlen;
			s.s->state = Sdigesting;
		} else if(strcmp(p, "sha") == 0){
			s.s->hf = sha;
			s.s->diglen = SHAdlen;
			s.s->state = Sdigesting;
#ifdef NOSPOOKS
		} else if(strcmp(p, "descbc") == 0){
			if(s.s->in.secret == 0 || s.s->out.secret == 0)
				error(Ebadarg);
			s.s->encryptalg = DESCBC;
			s.s->blocklen = 8;
			initDESkey(&s.s->in);
			initDESkey(&s.s->out);
			s.s->state = Sencrypting;
		} else if(strcmp(p, "desecb") == 0){
			if(s.s->in.secret == 0 || s.s->out.secret == 0)
				error(Ebadarg);
			s.s->encryptalg = DESECB;
			s.s->blocklen = 8;
			initDESkey(&s.s->in);
			initDESkey(&s.s->out);
			s.s->state = Sencrypting;
#endif NOSPOOKS
		} else if(strcmp(p, "rc4") == 0){
			if(s.s->in.secret == 0 || s.s->out.secret == 0)
				error(Ebadarg);
			s.s->encryptalg = RC4;
			s.s->blocklen = 1;
			initRC4key(&s.s->in);
			initRC4key(&s.s->out);
			s.s->state = Sencrypting;
		} else
			error(Ebadarg);

/*
 *  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;
		if(s.s->blocklen != 1){
			s.s->max = (1<<15) - s.s->diglen - 1;
			s.s->max -= s.s->max % s.s->blocklen;
			s.s->maxpad = (1<<14) - s.s->diglen - 1;
			s.s->maxpad -= s.s->maxpad % s.s->blocklen;
		} else
			s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1;
	} else
		error(Ebadarg);

	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))
			break;
		*l = b->next;
		freeb(b);
	}
out:
	qunlock(&s.s->in.ctlq);
	qunlock(&s.s->out.q);
	poperror();
	return n;
}

static void
setsecret(Dstate *s, OneWay *w, uchar *secret, int n)
{
	w->secret = malloc(n);
	memmove(w->secret, secret, n);
	w->slen = n;
	w->mid = 0;

	switch(s->encryptalg){
	case DESECB:
		if(n < 8)
			error("secret too small");
		w->state = malloc(sizeof(DESstate));
		setupDESstate(w->state, secret, 0);
		break;
	case DESCBC:
		if(n < 16)
			error("secret too small");
		w->state = malloc(sizeof(DESstate));
		setupDESstate(w->state, secret, secret+8);
		break;
	}
}

static Block*
encryptb(Dstate *s, Block *b, int offset)
{
#ifdef NOSPOOKS
	uchar *p, *ep, *p2, *ip, *eip;
	DESstate *ds;
#endif NOSPOOKS

	switch(s->encryptalg){
#ifdef NOSPOOKS
	case DESECB:
		ds = s->out.state;
		ep = b->rp + BLEN(b);


@@ 594,40 959,68 @@ encryptb(Dstate *s, Block *b, int offset)
			memmove(ds->ivec, p, 8);
		}
		break;
#endif NOSPOOKS
	case RC4:
		rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
		break;
	}
	return b;
}

static Block*
decryptb(Dstate *s, Block *b)
decryptb(Dstate *s, Block *inb)
{
	Block *b, **l;
#ifdef NOSPOOKS
	uchar *p, *ep, *tp, *ip, *eip;
	DESstate *ds;
	uchar tmp[8];
#endif NOSPOOKS
	int i;

	switch(s->encryptalg){
	case DESECB:
		ds = s->out.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + s->diglen; p < ep; p += 8)
			block_cipher(ds->expanded, p, 1);
		break;
	case DESCBC:
		ds = s->out.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + s->diglen; 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++;
	l = &inb;
	for(b = inb; 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("ssl encrypted message too short");
			}
		}
		break;
		l = &b->next;

		/* decrypt */
		switch(s->encryptalg){
#ifdef NOSPOOKS
		case DESECB:
			ds = s->in.state;
			ep = b->rp + BLEN(b);
			for(p = b->rp + s->diglen; 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 + s->diglen; 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;
#endif NOSPOOKS
		case RC4:
			rc4(s->in.state, b->rp, BLEN(b));
			break;
		}
	}
	return b;
	return inb;
}

static Block*


@@ 662,7 1055,7 @@ digestb(Dstate *s, Block *b, int offset)
}

static void
checkdigestb(Dstate *s, Block *b)
checkdigestb(Dstate *s, Block *inb)
{
	uchar *p;
	DigestState ss;


@@ 670,16 1063,24 @@ checkdigestb(Dstate *s, Block *b)
	int n, h;
	OneWay *w;
	uchar digest[128];
	Block *b;

	w = &s->in;

	memset(&ss, 0, sizeof(ss));
	h = s->diglen;
	n = BLEN(b) - h;

	/* hash secret + message */
	/* hash secret */
	(*s->hf)(w->secret, w->slen, 0, &ss);
	(*s->hf)(b->rp + h, n, 0, &ss);

	/* hash message */
	h = s->diglen;
	for(b = inb; 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;


@@ 690,31 1091,29 @@ checkdigestb(Dstate *s, Block *b)
	*p = n;
	(*s->hf)(msgid, 4, digest, &ss);

	if(memcmp(digest, b->rp, s->diglen) != 0)
	if(memcmp(digest, inb->rp, s->diglen) != 0)
		error("bad digest");
}

/* get channel associated with an fd */
static Chan*
buftochan(char *a, long n)
buftochan(char *p)
{
	Chan *c;
	int fd;
	char buf[32];

	if(n >= sizeof buf)
		error(Egreg);
	memmove(buf, a, n);		/* so we can NUL-terminate */
	buf[n] = 0;
	fd = strtoul(buf, 0, 0);

	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
dighangup(Dstate *s)
sslhangup(Dstate *s)
{
	Block *b;



@@ 727,7 1126,7 @@ dighangup(Dstate *s)
		freeb(s->unprocessed);
		s->unprocessed = 0;
	}
	s->state = Closed;
	s->state = Sincomplete;
	qunlock(&s->in.q);
}



@@ 739,11 1138,13 @@ void
handle_exception(int type, char *exception)
{
	if(type == CRITICAL)
		panic("crypt library (devssl): %s", exception);
		panic("crypt library: %s: %r", exception);
	else
		print("crypt library (devssl): %s\n", exception);
		print("crypt library: %s: %r\n", exception);
}

extern void rbcheck(char*);

void*
crypt_malloc(int size)
{


@@ 762,3 1163,70 @@ crypt_free(void *x)
		handle_exception(CRITICAL, "freeing null pointer");
	free(x);
}


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;
}