~kris/9p

9hist

f539ce2d08335740e1027430e8cb424db8b36555 — David du Colombier 30 years ago f1d0d06
Plan 9 from Bell Labs 1995-12-13
1 files changed, 733 insertions(+), 0 deletions(-)

A port/devssl.c
A port/devssl.c => port/devssl.c +733 -0
@@ 0,0 1,733 @@
/*
 *  template for making a new device
 */

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	<libcrypt.h>

#include	"devtab.h"

typedef struct OneWay OneWay;
struct OneWay
{
	QLock;

	void		*state;		/* encryption state */
	int		slen;		/* hash data length */
	uchar		*secret;	/* secret */
	ulong		mid;		/* message id */
};

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,

	/* encryption algorithms */
	DESCBC=		1,
};

typedef struct Dstate Dstate;
struct Dstate
{
	Chan	*c;		/* io channel */
	uchar	state;		/* state of connection */
	uchar	encryptalg;	/* encryption algorithm */
	ushort	blocking;	/* blocking length */

	ushort	diglen;		/* length of digest */
	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);	/* hash func */

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

enum
{
	Maxdmsg=	1<<16,
};

enum{
	Qdir,
	Qclone,
};
Dirtab digesttab[]={
	"digestclone",		{Qclone, 0},	0,	0600,
};
#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);

void
sslreset(void)
{
	randq.ep = randq.buf + sizeof(randq.buf);
	randq.rp = randq.wp = randq.buf;
}

void
sslinit(void)
{
}

Chan *
sslattach(char *spec)
{
	return devattach('D', spec);
}

Chan *
sslclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
sslwalk(Chan *c, char *name)
{
	return devwalk(c, name, digesttab, Ndigesttab, devgen);
}

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

Chan *
sslopen(Chan *c, int omode)
{
	Dstate *s;

	switch(c->qid.path & ~CHDIR){
	case Qclone:
		s = smalloc(sizeof(Dstate));
		memset(s, 0, sizeof(*s));
		s->state = Algwait;
		c->aux = s;
		break;
	}
	return devopen(c, omode, digesttab, Ndigesttab, devgen);
}

void
sslcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
sslremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void
sslwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

static void
dighangup(Dstate *s)
{
	Block *b;

	qlock(&s->in);
	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 = Closed;
	qunlock(&s->in);
}

void
sslclose(Chan *c)
{
	Dstate *s;

	if(c->aux){
		s = c->aux;
		dighangup(s);
		if(s->c)
			close(s->c);
		if(s->in.secret)
			free(s->in.secret);
		if(s->out.secret)
			free(s->out.secret);
		free(s);
	}
}

long
sslread(Chan *c, void *a, long n, ulong offset)
{
	Block *b;

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

	b = sslbread(c, n, offset);

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

	n = BLEN(b);
	memmove(a, b->rp, n);
	freeb(b);

	poperror();

	return n;
}

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

	switch(s->encryptalg){
	case DESCBC:
		w->state = smalloc(sizeof(DESstate));
		setupDESstate(w->state, secret, 0);
		break;
	case DESCBC:
		w->state = smalloc(sizeof(DESstate));
		setupDESstate(w->state, secret, secret+8);
		break;
	}
}

long
sslwrite(Chan *c, char *a, long n, ulong offset)
{
	Dstate *s;
	Block *b;
	int m, sofar;
	char buf[32];

	switch(c->qid.path & ~CHDIR){
	case Qclone:
		break;
	default:
		error(Ebadusefd);
	}

	s = c->aux;
	if(s == 0)
		error(Ebadusefd);

	switch(s->state){
	case Algwait:
		/* get algorithm */
		if(n >= sizeof(buf))
			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, "desebc") == 0){
			s->encryptalg = DESEBC;
			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->in, a, n);
		s->state = Secretoutwait;
		break;
	case Secretoutwait:
		/* get secret for outgoing messages */
		setsecret(&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:
		sofar = 0;
		do {
			m = n - sofar;
			if(m > s->max)
				m = s->max;
	
			b = allocb(m);
			if(waserror()){
				freeb(b);
				nexterror();
			}
			memmove(b->wp, a+sofar, m);
			poperror();
			b->wp += m;
	
			sslbwrite(c, b, offset);
	
			sofar += m;
		} while(sofar < n);
		break;
	default:
		error(Ebadusefd);
	}

	return n;
}

void
digestb(Dstate *s, Block *b, OneWay *w)
{
	Block *nb;
	uchar *p;
	DigestState ss;
	uchar msgid[4];
	ulong n, h;

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

	/* hash secret + message */
	(*s->hf)(w->secret, w->slen, 0, &ss);
	(*s->hf)(nb->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->func)(msgid, 4, nb->rp + 2, &ss);
}

long
encryptb(Dstate *s, Block *b)
{
	ulong n, h;
	int j;
	uchar *p, *ep, *ip;
	DESstate *ds;

	h = s->diglen + 2;

	switch(s->encryptalg){
	case DESEBC:
		ds = s->out.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + h; 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 + h; p < ep; p += 8)
			bCBCEncrypt(p, ds->ivec, ds->expanded, 8);
		break;
	}
	
}

long
decryptb(Dstate *s, Block *b)
{
	ulong n, h;
	uchar *p, *ep;
	DESstate *ds;

	h = s->diglen + 2;

	switch(s->encryptalg){
	case DESEBC:
		ds = s->in.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + h; 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 + h; p < ep; p += 8)
			bCBCDecrypt(p, ds->ivec, ds->expanded, 8);
		break;
	}
}

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

	b = *l;
	if(b){
		sofar = BLEN(b);
		l = &b->next;
	} else
		sofar = 0;

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

		*l = b;
		l = &b->next;
		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))
			break;
		*l = b->next;
		freeb(b);
	}
}

static Block*
digestbread(Dstate *s, long n)
{
	Block *b;
	int i, m, len;
	uchar *p;
	uchar *dp;
	uchar digestin[32];
	uchar digest[32];
	DigestState ss;

	memset(&ss, 0, sizeof(ss));

	ensure(s, &s->unprocessed, s->diglen);
		len = 0;
		for(i = 0; i < 4; i++){
			consume(&s->unprocessed, digestin+i, 1);
			m = digestin[i];
			if((m & 0x80) == 0)
				break;
			len = (len<<7) | (m & 0x7f);
		}
	
		/* digest count */
		p = &digestin[s->diglen];
		(*s->func)(p, i, 0, &ss);
		ensure(s, &s->unprocessed, s->diglen);

		/* get message */
		s->processed = s->unprocessed;
		s->unprocessed = 0;
		ensure(s, &s->processed, len);

		/* digest message */
		i = 0;
		for(b = s->processed; b; b = b->next){
			i = BLEN(b);
			if(i >= len)
				break;
			(*s->func)(b->rp, i, 0, &ss);
			len -= i;
		}
		if(b == 0)
			panic("digestbread");
		if(i > len){
			i -= len;
			s->unprocessed = allocb(i);
			memmove(s->unprocessed->wp, b->rp+len, i);
			s->unprocessed->wp += i;
			b->wp -= i;
		}
		(*s->func)(b->rp, len, 0, &ss);

		/* digest secret & message id */
		p = s->in.secret;
		m = s->in.mid++;
		*p++ = m>>24;
		*p++ = m>>16;
		*p++ = m>>8;
		*p = m;
		(*s->func)(s->in.secret, s->in.slen, digest, &ss);

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

	b = s->processed;
	if(BLEN(b) > n){
		b = allocb(n);
		memmove(b->wp, s->processed->rp, n);
		b->wp += n;
		s->processed->rp += n;
	} else 
		s->processed = b->next;

	return b;
}

static Block*
sslbread(Chan *c, long n, ulong offset)
{
	Block *bp;
	uchar count[2];
	int len;
	int pad;

	USED(offset);

	s = c->aux;
	if(s == 0 || s->state != Established)
		error(Ebadusefd);

	if(waserror()){
		qunlock(&s->in);
		dighangup(s);
		nexterror();
	}

	qlock(&s->in);

	/* get the whole message */
	ensure(s, &s->unprocessed, 2);
	consume(&s->unprocessed, count, 2);
	if(count[0] & 0x80){
		len = ((count[0] & 0x7f)<<8) | count[1];
		pad = 0;
	} else {
		len = ((count[0] & 0x3f)<<8) | count[1];
		ensure(s, &s->unprocessed, 1);
		consume(&s->unprocessed, count, 1);
		pad = count[0];
	}
	ensure(s, &s->unprocessed, len);
		
	if(s->encrypalg)
		b = decryptb(s, len);
	else
		b = digestb(s, len);

	if(pad)

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

	return b;
}

static Chan*
buftochan(char *a, long n)
{
	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);

	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
	return c;
}

/*
 *  use SSL record format, add in count and digest or encrypt
 */
long
sslbwrite(Chan *c, Block *b, ulong offset)
{
	Dstate *s;
	Block *nb;
	int h, n, m, pad, rv;

	s = c->aux;
	if(s == 0 || s->state != Established)
		error(Ebadusefd);

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

	rv = 0;
	while(b){
		m = n = BLEN(b);
		h = s->diglen + 2;

		/* padded blocks are shorter than unpadded ones (strange) */
		pad = 0;
		if(m > s->max){
			m = s->max;
		} else if(s->blocklen != 1){
			pad = m%s->blocklen;
			if(pad){
				pad = s->blocklen - pad;
				if(m > s->maxpad){
					pad = 0;
					m = s->maxpad;
				}
			}
		}

		rv += m;
		if(m != n){
			nb = allocb(m + h + pad);
			memmove(nb->wp + h, m, nb->rptr);
			nb->wp += m + h;
			b->rp += m;
		} else {
			/* add header */
			nb = padblock(b, h);
			nb->rp -= h;

			/* add pad */
			if(pad)
				nb = padblock(nb, -pad);
			b = 0;
		}

		/* SSL style count */
		if(pad){
			memset(nb->wp, 0, pad);
			m += pad;
			nb->wp += pad;
		} else
			m |= 0x8000;
		np->rp[0] = (m>>8);
		np->rp[1] = m;

		if(encryptalg)
			encryptb(s, nb);
		else
			digestb(s, nb);

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

	}
	qunlock(&s->out);
	poperror();

	return rv;
}

/*
 *  crypt's interface to system, included here to override the
 *  library version
 */
void
handle_exception(int type, char *exception)
{
	if(type == CRITICAL)
		panic("kernel ssl: %s", exception);
	else
		print("kernel ssl: %s\n", exception);
}

void*
crypt_malloc(int size)
{
	void *x;

	x = smalloc(size);
	if(x == 0)
		handle_exception(CRITICAL, "out of memory");
	return x;
}

void
crypt_free(void *x)
{
	if(x == 0)
		handle_exception(CRITICAL, "freeing null pointer");
	free(x);
}