xref: /plan9/sys/src/9/port/devssl.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
17dd7cddfSDavid du Colombier /*
29a747e4fSDavid du Colombier  *  devssl - secure sockets layer
37dd7cddfSDavid du Colombier  */
47dd7cddfSDavid du Colombier #include	"u.h"
57dd7cddfSDavid du Colombier #include	"../port/lib.h"
67dd7cddfSDavid du Colombier #include	"mem.h"
77dd7cddfSDavid du Colombier #include	"dat.h"
87dd7cddfSDavid du Colombier #include	"fns.h"
97dd7cddfSDavid du Colombier #include	"../port/error.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier #include	<libsec.h>
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier #define NOSPOOKS 1
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier typedef struct OneWay OneWay;
167dd7cddfSDavid du Colombier struct OneWay
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	QLock	q;
197dd7cddfSDavid du Colombier 	QLock	ctlq;
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier 	void	*state;		/* encryption state */
227dd7cddfSDavid du Colombier 	int	slen;		/* hash data length */
237dd7cddfSDavid du Colombier 	uchar	*secret;	/* secret */
247dd7cddfSDavid du Colombier 	ulong	mid;		/* message id */
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier enum
287dd7cddfSDavid du Colombier {
297dd7cddfSDavid du Colombier 	/* connection states */
307dd7cddfSDavid du Colombier 	Sincomplete=	0,
317dd7cddfSDavid du Colombier 	Sclear=		1,
327dd7cddfSDavid du Colombier 	Sencrypting=	2,
337dd7cddfSDavid du Colombier 	Sdigesting=	4,
347dd7cddfSDavid du Colombier 	Sdigenc=	Sencrypting|Sdigesting,
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier 	/* encryption algorithms */
377dd7cddfSDavid du Colombier 	Noencryption=	0,
387dd7cddfSDavid du Colombier 	DESCBC=		1,
397dd7cddfSDavid du Colombier 	DESECB=		2,
407dd7cddfSDavid du Colombier 	RC4=		3
417dd7cddfSDavid du Colombier };
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier typedef struct Dstate Dstate;
447dd7cddfSDavid du Colombier struct Dstate
457dd7cddfSDavid du Colombier {
467dd7cddfSDavid du Colombier 	Chan	*c;		/* io channel */
477dd7cddfSDavid du Colombier 	uchar	state;		/* state of connection */
487dd7cddfSDavid du Colombier 	int	ref;		/* serialized by dslock for atomic destroy */
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier 	uchar	encryptalg;	/* encryption algorithm */
517dd7cddfSDavid du Colombier 	ushort	blocklen;	/* blocking length */
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier 	ushort	diglen;		/* length of digest */
547dd7cddfSDavid du Colombier 	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);	/* hash func */
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	/* for SSL format */
577dd7cddfSDavid du Colombier 	int	max;		/* maximum unpadded data per msg */
587dd7cddfSDavid du Colombier 	int	maxpad;		/* maximum padded data per msg */
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier 	/* input side */
617dd7cddfSDavid du Colombier 	OneWay	in;
627dd7cddfSDavid du Colombier 	Block	*processed;
637dd7cddfSDavid du Colombier 	Block	*unprocessed;
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier 	/* output side */
667dd7cddfSDavid du Colombier 	OneWay	out;
677dd7cddfSDavid du Colombier 
687dd7cddfSDavid du Colombier 	/* protections */
699a747e4fSDavid du Colombier 	char	*user;
707dd7cddfSDavid du Colombier 	int	perm;
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier enum
747dd7cddfSDavid du Colombier {
757dd7cddfSDavid du Colombier 	Maxdmsg=	1<<16,
76e9b54818SDavid du Colombier 	Maxdstate=	512,	/* max. open ssl conn's; must be a power of 2 */
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier 
79e9b54818SDavid du Colombier static	Lock	dslock;
80e9b54818SDavid du Colombier static	int	dshiwat;
81e9b54818SDavid du Colombier static	char	*dsname[Maxdstate];
82e9b54818SDavid du Colombier static	Dstate	*dstate[Maxdstate];
83e9b54818SDavid du Colombier static	char	*encalgs;
84e9b54818SDavid du Colombier static	char	*hashalgs;
859a747e4fSDavid du Colombier 
867dd7cddfSDavid du Colombier enum{
877dd7cddfSDavid du Colombier 	Qtopdir		= 1,	/* top level directory */
887dd7cddfSDavid du Colombier 	Qprotodir,
897dd7cddfSDavid du Colombier 	Qclonus,
907dd7cddfSDavid du Colombier 	Qconvdir,		/* directory for a conversation */
917dd7cddfSDavid du Colombier 	Qdata,
927dd7cddfSDavid du Colombier 	Qctl,
937dd7cddfSDavid du Colombier 	Qsecretin,
947dd7cddfSDavid du Colombier 	Qsecretout,
957dd7cddfSDavid du Colombier 	Qencalgs,
967dd7cddfSDavid du Colombier 	Qhashalgs,
977dd7cddfSDavid du Colombier };
987dd7cddfSDavid du Colombier 
997dd7cddfSDavid du Colombier #define TYPE(x) 	((x).path & 0xf)
1007dd7cddfSDavid du Colombier #define CONV(x) 	(((x).path >> 5)&(Maxdstate-1))
1017dd7cddfSDavid du Colombier #define QID(c, y) 	(((c)<<5) | (y))
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier static void	ensure(Dstate*, Block**, int);
1047dd7cddfSDavid du Colombier static void	consume(Block**, uchar*, int);
1057dd7cddfSDavid du Colombier static void	setsecret(OneWay*, uchar*, int);
1067dd7cddfSDavid du Colombier static Block*	encryptb(Dstate*, Block*, int);
1077dd7cddfSDavid du Colombier static Block*	decryptb(Dstate*, Block*);
1087dd7cddfSDavid du Colombier static Block*	digestb(Dstate*, Block*, int);
1097dd7cddfSDavid du Colombier static void	checkdigestb(Dstate*, Block*);
1107dd7cddfSDavid du Colombier static Chan*	buftochan(char*);
1117dd7cddfSDavid du Colombier static void	sslhangup(Dstate*);
1127dd7cddfSDavid du Colombier static Dstate*	dsclone(Chan *c);
1137dd7cddfSDavid du Colombier static void	dsnew(Chan *c, Dstate **);
1149a747e4fSDavid du Colombier static long	sslput(Dstate *s, Block * volatile b);
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier char *sslnames[] = {
1177dd7cddfSDavid du Colombier [Qclonus]	"clone",
1187dd7cddfSDavid du Colombier [Qdata]		"data",
1197dd7cddfSDavid du Colombier [Qctl]		"ctl",
1207dd7cddfSDavid du Colombier [Qsecretin]	"secretin",
1217dd7cddfSDavid du Colombier [Qsecretout]	"secretout",
1227dd7cddfSDavid du Colombier [Qencalgs]	"encalgs",
1237dd7cddfSDavid du Colombier [Qhashalgs]	"hashalgs",
1247dd7cddfSDavid du Colombier };
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier static int
sslgen(Chan * c,char *,Dirtab * d,int nd,int s,Dir * dp)1279a747e4fSDavid du Colombier sslgen(Chan *c, char*, Dirtab *d, int nd, int s, Dir *dp)
1287dd7cddfSDavid du Colombier {
1297dd7cddfSDavid du Colombier 	Qid q;
1307dd7cddfSDavid du Colombier 	Dstate *ds;
1317dd7cddfSDavid du Colombier 	char name[16], *p, *nm;
1329a747e4fSDavid du Colombier 	int ft;
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	USED(nd);
1357dd7cddfSDavid du Colombier 	USED(d);
1369a747e4fSDavid du Colombier 
1379a747e4fSDavid du Colombier 	q.type = QTFILE;
1387dd7cddfSDavid du Colombier 	q.vers = 0;
1399a747e4fSDavid du Colombier 
1409a747e4fSDavid du Colombier 	ft = TYPE(c->qid);
1419a747e4fSDavid du Colombier 	switch(ft) {
1427dd7cddfSDavid du Colombier 	case Qtopdir:
1437dd7cddfSDavid du Colombier 		if(s == DEVDOTDOT){
1449a747e4fSDavid du Colombier 			q.path = QID(0, Qtopdir);
1459a747e4fSDavid du Colombier 			q.type = QTDIR;
1469a747e4fSDavid du Colombier 			devdir(c, q, "#D", 0, eve, 0555, dp);
1477dd7cddfSDavid du Colombier 			return 1;
1487dd7cddfSDavid du Colombier 		}
1497dd7cddfSDavid du Colombier 		if(s > 0)
1507dd7cddfSDavid du Colombier 			return -1;
1519a747e4fSDavid du Colombier 		q.path = QID(0, Qprotodir);
1529a747e4fSDavid du Colombier 		q.type = QTDIR;
1539a747e4fSDavid du Colombier 		devdir(c, q, "ssl", 0, eve, 0555, dp);
1547dd7cddfSDavid du Colombier 		return 1;
1557dd7cddfSDavid du Colombier 	case Qprotodir:
1567dd7cddfSDavid du Colombier 		if(s == DEVDOTDOT){
1579a747e4fSDavid du Colombier 			q.path = QID(0, Qtopdir);
1589a747e4fSDavid du Colombier 			q.type = QTDIR;
1599a747e4fSDavid du Colombier 			devdir(c, q, ".", 0, eve, 0555, dp);
1607dd7cddfSDavid du Colombier 			return 1;
1617dd7cddfSDavid du Colombier 		}
1627dd7cddfSDavid du Colombier 		if(s < dshiwat) {
1639a747e4fSDavid du Colombier 			q.path = QID(s, Qconvdir);
1649a747e4fSDavid du Colombier 			q.type = QTDIR;
1657dd7cddfSDavid du Colombier 			ds = dstate[s];
1667dd7cddfSDavid du Colombier 			if(ds != 0)
1677dd7cddfSDavid du Colombier 				nm = ds->user;
1687dd7cddfSDavid du Colombier 			else
1697dd7cddfSDavid du Colombier 				nm = eve;
1709a747e4fSDavid du Colombier 			if(dsname[s] == nil){
171*4e3613abSDavid du Colombier 				snprint(name, sizeof name, "%d", s);
1729a747e4fSDavid du Colombier 				kstrdup(&dsname[s], name);
1739a747e4fSDavid du Colombier 			}
1749a747e4fSDavid du Colombier 			devdir(c, q, dsname[s], 0, nm, 0555, dp);
1757dd7cddfSDavid du Colombier 			return 1;
1767dd7cddfSDavid du Colombier 		}
1777dd7cddfSDavid du Colombier 		if(s > dshiwat)
1787dd7cddfSDavid du Colombier 			return -1;
1797dd7cddfSDavid du Colombier 		q.path = QID(0, Qclonus);
1807dd7cddfSDavid du Colombier 		devdir(c, q, "clone", 0, eve, 0555, dp);
1817dd7cddfSDavid du Colombier 		return 1;
1827dd7cddfSDavid du Colombier 	case Qconvdir:
1837dd7cddfSDavid du Colombier 		if(s == DEVDOTDOT){
1849a747e4fSDavid du Colombier 			q.path = QID(0, Qprotodir);
1859a747e4fSDavid du Colombier 			q.type = QTDIR;
1869a747e4fSDavid du Colombier 			devdir(c, q, "ssl", 0, eve, 0555, dp);
1877dd7cddfSDavid du Colombier 			return 1;
1887dd7cddfSDavid du Colombier 		}
1897dd7cddfSDavid du Colombier 		ds = dstate[CONV(c->qid)];
1907dd7cddfSDavid du Colombier 		if(ds != 0)
1917dd7cddfSDavid du Colombier 			nm = ds->user;
1927dd7cddfSDavid du Colombier 		else
1937dd7cddfSDavid du Colombier 			nm = eve;
1947dd7cddfSDavid du Colombier 		switch(s) {
1957dd7cddfSDavid du Colombier 		default:
1967dd7cddfSDavid du Colombier 			return -1;
1977dd7cddfSDavid du Colombier 		case 0:
1987dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qctl);
1997dd7cddfSDavid du Colombier 			p = "ctl";
2007dd7cddfSDavid du Colombier 			break;
2017dd7cddfSDavid du Colombier 		case 1:
2027dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qdata);
2037dd7cddfSDavid du Colombier 			p = "data";
2047dd7cddfSDavid du Colombier 			break;
2057dd7cddfSDavid du Colombier 		case 2:
2067dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qsecretin);
2077dd7cddfSDavid du Colombier 			p = "secretin";
2087dd7cddfSDavid du Colombier 			break;
2097dd7cddfSDavid du Colombier 		case 3:
2107dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qsecretout);
2117dd7cddfSDavid du Colombier 			p = "secretout";
2127dd7cddfSDavid du Colombier 			break;
2137dd7cddfSDavid du Colombier 		case 4:
2147dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qencalgs);
2157dd7cddfSDavid du Colombier 			p = "encalgs";
2167dd7cddfSDavid du Colombier 			break;
2177dd7cddfSDavid du Colombier 		case 5:
2187dd7cddfSDavid du Colombier 			q.path = QID(CONV(c->qid), Qhashalgs);
2197dd7cddfSDavid du Colombier 			p = "hashalgs";
2207dd7cddfSDavid du Colombier 			break;
2217dd7cddfSDavid du Colombier 		}
2227dd7cddfSDavid du Colombier 		devdir(c, q, p, 0, nm, 0660, dp);
2237dd7cddfSDavid du Colombier 		return 1;
2247dd7cddfSDavid du Colombier 	case Qclonus:
2257dd7cddfSDavid du Colombier 		devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, eve, 0555, dp);
2267dd7cddfSDavid du Colombier 		return 1;
2277dd7cddfSDavid du Colombier 	default:
2287dd7cddfSDavid du Colombier 		ds = dstate[CONV(c->qid)];
2293ff48bf5SDavid du Colombier 		if(ds != 0)
2303ff48bf5SDavid du Colombier 			nm = ds->user;
2313ff48bf5SDavid du Colombier 		else
2323ff48bf5SDavid du Colombier 			nm = eve;
2333ff48bf5SDavid du Colombier 		devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, nm, 0660, dp);
2347dd7cddfSDavid du Colombier 		return 1;
2357dd7cddfSDavid du Colombier 	}
2367dd7cddfSDavid du Colombier }
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier static Chan*
sslattach(char * spec)2397dd7cddfSDavid du Colombier sslattach(char *spec)
2407dd7cddfSDavid du Colombier {
2417dd7cddfSDavid du Colombier 	Chan *c;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	c = devattach('D', spec);
2449a747e4fSDavid du Colombier 	c->qid.path = QID(0, Qtopdir);
2457dd7cddfSDavid du Colombier 	c->qid.vers = 0;
2469a747e4fSDavid du Colombier 	c->qid.type = QTDIR;
2477dd7cddfSDavid du Colombier 	return c;
2487dd7cddfSDavid du Colombier }
2497dd7cddfSDavid du Colombier 
2509a747e4fSDavid du Colombier static Walkqid*
sslwalk(Chan * c,Chan * nc,char ** name,int nname)2519a747e4fSDavid du Colombier sslwalk(Chan *c, Chan *nc, char **name, int nname)
2527dd7cddfSDavid du Colombier {
2539a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, sslgen);
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier 
2569a747e4fSDavid du Colombier static int
sslstat(Chan * c,uchar * db,int n)2579a747e4fSDavid du Colombier sslstat(Chan *c, uchar *db, int n)
2587dd7cddfSDavid du Colombier {
2599a747e4fSDavid du Colombier 	return devstat(c, db, n, nil, 0, sslgen);
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier static Chan*
sslopen(Chan * c,int omode)2637dd7cddfSDavid du Colombier sslopen(Chan *c, int omode)
2647dd7cddfSDavid du Colombier {
2657dd7cddfSDavid du Colombier 	Dstate *s, **pp;
2667dd7cddfSDavid du Colombier 	int perm;
2679a747e4fSDavid du Colombier 	int ft;
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier 	perm = 0;
2707dd7cddfSDavid du Colombier 	omode &= 3;
2717dd7cddfSDavid du Colombier 	switch(omode) {
2727dd7cddfSDavid du Colombier 	case OREAD:
2737dd7cddfSDavid du Colombier 		perm = 4;
2747dd7cddfSDavid du Colombier 		break;
2757dd7cddfSDavid du Colombier 	case OWRITE:
2767dd7cddfSDavid du Colombier 		perm = 2;
2777dd7cddfSDavid du Colombier 		break;
2787dd7cddfSDavid du Colombier 	case ORDWR:
2797dd7cddfSDavid du Colombier 		perm = 6;
2807dd7cddfSDavid du Colombier 		break;
2817dd7cddfSDavid du Colombier 	}
2827dd7cddfSDavid du Colombier 
2839a747e4fSDavid du Colombier 	ft = TYPE(c->qid);
2849a747e4fSDavid du Colombier 	switch(ft) {
2857dd7cddfSDavid du Colombier 	default:
2867dd7cddfSDavid du Colombier 		panic("sslopen");
2877dd7cddfSDavid du Colombier 	case Qtopdir:
2887dd7cddfSDavid du Colombier 	case Qprotodir:
2897dd7cddfSDavid du Colombier 	case Qconvdir:
2907dd7cddfSDavid du Colombier 		if(omode != OREAD)
2917dd7cddfSDavid du Colombier 			error(Eperm);
2927dd7cddfSDavid du Colombier 		break;
2937dd7cddfSDavid du Colombier 	case Qclonus:
2947dd7cddfSDavid du Colombier 		s = dsclone(c);
2957dd7cddfSDavid du Colombier 		if(s == 0)
2967dd7cddfSDavid du Colombier 			error(Enodev);
2977dd7cddfSDavid du Colombier 		break;
2987dd7cddfSDavid du Colombier 	case Qctl:
2997dd7cddfSDavid du Colombier 	case Qdata:
3007dd7cddfSDavid du Colombier 	case Qsecretin:
3017dd7cddfSDavid du Colombier 	case Qsecretout:
3027dd7cddfSDavid du Colombier 		if(waserror()) {
3037dd7cddfSDavid du Colombier 			unlock(&dslock);
3047dd7cddfSDavid du Colombier 			nexterror();
3057dd7cddfSDavid du Colombier 		}
3067dd7cddfSDavid du Colombier 		lock(&dslock);
3077dd7cddfSDavid du Colombier 		pp = &dstate[CONV(c->qid)];
3087dd7cddfSDavid du Colombier 		s = *pp;
3097dd7cddfSDavid du Colombier 		if(s == 0)
3107dd7cddfSDavid du Colombier 			dsnew(c, pp);
3117dd7cddfSDavid du Colombier 		else {
3127dd7cddfSDavid du Colombier 			if((perm & (s->perm>>6)) != perm
3137dd7cddfSDavid du Colombier 			   && (strcmp(up->user, s->user) != 0
3147dd7cddfSDavid du Colombier 			     || (perm & s->perm) != perm))
3157dd7cddfSDavid du Colombier 				error(Eperm);
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier 			s->ref++;
3187dd7cddfSDavid du Colombier 		}
3197dd7cddfSDavid du Colombier 		unlock(&dslock);
3207dd7cddfSDavid du Colombier 		poperror();
3217dd7cddfSDavid du Colombier 		break;
3227dd7cddfSDavid du Colombier 	case Qencalgs:
3237dd7cddfSDavid du Colombier 	case Qhashalgs:
3247dd7cddfSDavid du Colombier 		if(omode != OREAD)
3257dd7cddfSDavid du Colombier 			error(Eperm);
3267dd7cddfSDavid du Colombier 		break;
3277dd7cddfSDavid du Colombier 	}
3287dd7cddfSDavid du Colombier 	c->mode = openmode(omode);
3297dd7cddfSDavid du Colombier 	c->flag |= COPEN;
3307dd7cddfSDavid du Colombier 	c->offset = 0;
3317dd7cddfSDavid du Colombier 	return c;
3327dd7cddfSDavid du Colombier }
3337dd7cddfSDavid du Colombier 
3349a747e4fSDavid du Colombier static int
sslwstat(Chan * c,uchar * db,int n)3359a747e4fSDavid du Colombier sslwstat(Chan *c, uchar *db, int n)
3367dd7cddfSDavid du Colombier {
3379a747e4fSDavid du Colombier 	Dir *dir;
3387dd7cddfSDavid du Colombier 	Dstate *s;
3399a747e4fSDavid du Colombier 	int m;
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 	s = dstate[CONV(c->qid)];
3427dd7cddfSDavid du Colombier 	if(s == 0)
3437dd7cddfSDavid du Colombier 		error(Ebadusefd);
3447dd7cddfSDavid du Colombier 	if(strcmp(s->user, up->user) != 0)
3457dd7cddfSDavid du Colombier 		error(Eperm);
3467dd7cddfSDavid du Colombier 
3479a747e4fSDavid du Colombier 	dir = smalloc(sizeof(Dir)+n);
3489a747e4fSDavid du Colombier 	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
3499a747e4fSDavid du Colombier 	if(m == 0){
3509a747e4fSDavid du Colombier 		free(dir);
3519a747e4fSDavid du Colombier 		error(Eshortstat);
3529a747e4fSDavid du Colombier 	}
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier 	if(!emptystr(dir->uid))
3559a747e4fSDavid du Colombier 		kstrdup(&s->user, dir->uid);
3569a747e4fSDavid du Colombier 	if(dir->mode != ~0UL)
3579a747e4fSDavid du Colombier 		s->perm = dir->mode;
3589a747e4fSDavid du Colombier 
3599a747e4fSDavid du Colombier 	free(dir);
3609a747e4fSDavid du Colombier 	return m;
3617dd7cddfSDavid du Colombier }
3627dd7cddfSDavid du Colombier 
3637dd7cddfSDavid du Colombier static void
sslclose(Chan * c)3647dd7cddfSDavid du Colombier sslclose(Chan *c)
3657dd7cddfSDavid du Colombier {
3667dd7cddfSDavid du Colombier 	Dstate *s;
3679a747e4fSDavid du Colombier 	int ft;
3687dd7cddfSDavid du Colombier 
3699a747e4fSDavid du Colombier 	ft = TYPE(c->qid);
3709a747e4fSDavid du Colombier 	switch(ft) {
3717dd7cddfSDavid du Colombier 	case Qctl:
3727dd7cddfSDavid du Colombier 	case Qdata:
3737dd7cddfSDavid du Colombier 	case Qsecretin:
3747dd7cddfSDavid du Colombier 	case Qsecretout:
3757dd7cddfSDavid du Colombier 		if((c->flag & COPEN) == 0)
3767dd7cddfSDavid du Colombier 			break;
3777dd7cddfSDavid du Colombier 
3787dd7cddfSDavid du Colombier 		s = dstate[CONV(c->qid)];
3797dd7cddfSDavid du Colombier 		if(s == 0)
3807dd7cddfSDavid du Colombier 			break;
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 		lock(&dslock);
3837dd7cddfSDavid du Colombier 		if(--s->ref > 0) {
3847dd7cddfSDavid du Colombier 			unlock(&dslock);
3857dd7cddfSDavid du Colombier 			break;
3867dd7cddfSDavid du Colombier 		}
3877dd7cddfSDavid du Colombier 		dstate[CONV(c->qid)] = 0;
3887dd7cddfSDavid du Colombier 		unlock(&dslock);
3897dd7cddfSDavid du Colombier 
3909a747e4fSDavid du Colombier 		if(s->user != nil)
3919a747e4fSDavid du Colombier 			free(s->user);
3927dd7cddfSDavid du Colombier 		sslhangup(s);
3937dd7cddfSDavid du Colombier 		if(s->c)
3947dd7cddfSDavid du Colombier 			cclose(s->c);
3957dd7cddfSDavid du Colombier 		if(s->in.secret)
3967dd7cddfSDavid du Colombier 			free(s->in.secret);
3977dd7cddfSDavid du Colombier 		if(s->out.secret)
3987dd7cddfSDavid du Colombier 			free(s->out.secret);
3997dd7cddfSDavid du Colombier 		if(s->in.state)
4007dd7cddfSDavid du Colombier 			free(s->in.state);
4017dd7cddfSDavid du Colombier 		if(s->out.state)
4027dd7cddfSDavid du Colombier 			free(s->out.state);
4037dd7cddfSDavid du Colombier 		free(s);
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier 	}
4067dd7cddfSDavid du Colombier }
4077dd7cddfSDavid du Colombier 
4087dd7cddfSDavid du Colombier /*
4097dd7cddfSDavid du Colombier  *  make sure we have at least 'n' bytes in list 'l'
4107dd7cddfSDavid du Colombier  */
4117dd7cddfSDavid du Colombier static void
ensure(Dstate * s,Block ** l,int n)4127dd7cddfSDavid du Colombier ensure(Dstate *s, Block **l, int n)
4137dd7cddfSDavid du Colombier {
4147dd7cddfSDavid du Colombier 	int sofar, i;
4157dd7cddfSDavid du Colombier 	Block *b, *bl;
4167dd7cddfSDavid du Colombier 
4177dd7cddfSDavid du Colombier 	sofar = 0;
4187dd7cddfSDavid du Colombier 	for(b = *l; b; b = b->next){
4197dd7cddfSDavid du Colombier 		sofar += BLEN(b);
4207dd7cddfSDavid du Colombier 		if(sofar >= n)
4217dd7cddfSDavid du Colombier 			return;
4227dd7cddfSDavid du Colombier 		l = &b->next;
4237dd7cddfSDavid du Colombier 	}
4247dd7cddfSDavid du Colombier 
4257dd7cddfSDavid du Colombier 	while(sofar < n){
4267dd7cddfSDavid du Colombier 		bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
4277dd7cddfSDavid du Colombier 		if(bl == 0)
4289a747e4fSDavid du Colombier 			nexterror();
4297dd7cddfSDavid du Colombier 		*l = bl;
4307dd7cddfSDavid du Colombier 		i = 0;
4317dd7cddfSDavid du Colombier 		for(b = bl; b; b = b->next){
4327dd7cddfSDavid du Colombier 			i += BLEN(b);
4337dd7cddfSDavid du Colombier 			l = &b->next;
4347dd7cddfSDavid du Colombier 		}
4357dd7cddfSDavid du Colombier 		if(i == 0)
4367dd7cddfSDavid du Colombier 			error(Ehungup);
4377dd7cddfSDavid du Colombier 		sofar += i;
4387dd7cddfSDavid du Colombier 	}
4397dd7cddfSDavid du Colombier }
4407dd7cddfSDavid du Colombier 
4417dd7cddfSDavid du Colombier /*
4427dd7cddfSDavid du Colombier  *  copy 'n' bytes from 'l' into 'p' and free
4437dd7cddfSDavid du Colombier  *  the bytes in 'l'
4447dd7cddfSDavid du Colombier  */
4457dd7cddfSDavid du Colombier static void
consume(Block ** l,uchar * p,int n)4467dd7cddfSDavid du Colombier consume(Block **l, uchar *p, int n)
4477dd7cddfSDavid du Colombier {
4487dd7cddfSDavid du Colombier 	Block *b;
4497dd7cddfSDavid du Colombier 	int i;
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	for(; *l && n > 0; n -= i){
4527dd7cddfSDavid du Colombier 		b = *l;
4537dd7cddfSDavid du Colombier 		i = BLEN(b);
4547dd7cddfSDavid du Colombier 		if(i > n)
4557dd7cddfSDavid du Colombier 			i = n;
4567dd7cddfSDavid du Colombier 		memmove(p, b->rp, i);
4577dd7cddfSDavid du Colombier 		b->rp += i;
4587dd7cddfSDavid du Colombier 		p += i;
4597dd7cddfSDavid du Colombier 		if(BLEN(b) < 0)
4607dd7cddfSDavid du Colombier 			panic("consume");
4617dd7cddfSDavid du Colombier 		if(BLEN(b))
4627dd7cddfSDavid du Colombier 			break;
4637dd7cddfSDavid du Colombier 		*l = b->next;
4647dd7cddfSDavid du Colombier 		freeb(b);
4657dd7cddfSDavid du Colombier 	}
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier /*
46959cc4ca5SDavid du Colombier  *  give back n bytes
47059cc4ca5SDavid du Colombier static void
47159cc4ca5SDavid du Colombier regurgitate(Dstate *s, uchar *p, int n)
47259cc4ca5SDavid du Colombier {
47359cc4ca5SDavid du Colombier 	Block *b;
47459cc4ca5SDavid du Colombier 
47559cc4ca5SDavid du Colombier 	if(n <= 0)
47659cc4ca5SDavid du Colombier 		return;
47759cc4ca5SDavid du Colombier 	b = s->unprocessed;
47859cc4ca5SDavid du Colombier 	if(s->unprocessed == nil || b->rp - b->base < n) {
47959cc4ca5SDavid du Colombier 		b = allocb(n);
48059cc4ca5SDavid du Colombier 		memmove(b->wp, p, n);
48159cc4ca5SDavid du Colombier 		b->wp += n;
48259cc4ca5SDavid du Colombier 		b->next = s->unprocessed;
48359cc4ca5SDavid du Colombier 		s->unprocessed = b;
48459cc4ca5SDavid du Colombier 	} else {
48559cc4ca5SDavid du Colombier 		b->rp -= n;
48659cc4ca5SDavid du Colombier 		memmove(b->rp, p, n);
48759cc4ca5SDavid du Colombier 	}
48859cc4ca5SDavid du Colombier }
4899a747e4fSDavid du Colombier  */
49059cc4ca5SDavid du Colombier 
49159cc4ca5SDavid du Colombier /*
4927dd7cddfSDavid du Colombier  *  remove at most n bytes from the queue, if discard is set
4937dd7cddfSDavid du Colombier  *  dump the remainder
4947dd7cddfSDavid du Colombier  */
4957dd7cddfSDavid du Colombier static Block*
qtake(Block ** l,int n,int discard)4969a747e4fSDavid du Colombier qtake(Block **l, int n, int discard)
4977dd7cddfSDavid du Colombier {
4987dd7cddfSDavid du Colombier 	Block *nb, *b, *first;
4997dd7cddfSDavid du Colombier 	int i;
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	first = *l;
5027dd7cddfSDavid du Colombier 	for(b = first; b; b = b->next){
5037dd7cddfSDavid du Colombier 		i = BLEN(b);
5047dd7cddfSDavid du Colombier 		if(i == n){
5057dd7cddfSDavid du Colombier 			if(discard){
5067dd7cddfSDavid du Colombier 				freeblist(b->next);
5077dd7cddfSDavid du Colombier 				*l = 0;
5087dd7cddfSDavid du Colombier 			} else
5097dd7cddfSDavid du Colombier 				*l = b->next;
5107dd7cddfSDavid du Colombier 			b->next = 0;
5117dd7cddfSDavid du Colombier 			return first;
5127dd7cddfSDavid du Colombier 		} else if(i > n){
5137dd7cddfSDavid du Colombier 			i -= n;
5147dd7cddfSDavid du Colombier 			if(discard){
5157dd7cddfSDavid du Colombier 				freeblist(b->next);
5167dd7cddfSDavid du Colombier 				b->wp -= i;
5177dd7cddfSDavid du Colombier 				*l = 0;
5187dd7cddfSDavid du Colombier 			} else {
5197dd7cddfSDavid du Colombier 				nb = allocb(i);
5207dd7cddfSDavid du Colombier 				memmove(nb->wp, b->rp+n, i);
5217dd7cddfSDavid du Colombier 				nb->wp += i;
5227dd7cddfSDavid du Colombier 				b->wp -= i;
5237dd7cddfSDavid du Colombier 				nb->next = b->next;
5247dd7cddfSDavid du Colombier 				*l = nb;
5257dd7cddfSDavid du Colombier 			}
5267dd7cddfSDavid du Colombier 			b->next = 0;
5277dd7cddfSDavid du Colombier 			if(BLEN(b) < 0)
5289a747e4fSDavid du Colombier 				panic("qtake");
5297dd7cddfSDavid du Colombier 			return first;
5307dd7cddfSDavid du Colombier 		} else
5317dd7cddfSDavid du Colombier 			n -= i;
5327dd7cddfSDavid du Colombier 		if(BLEN(b) < 0)
5339a747e4fSDavid du Colombier 			panic("qtake");
5347dd7cddfSDavid du Colombier 	}
5357dd7cddfSDavid du Colombier 	*l = 0;
5367dd7cddfSDavid du Colombier 	return first;
5377dd7cddfSDavid du Colombier }
5387dd7cddfSDavid du Colombier 
53959cc4ca5SDavid du Colombier /*
54059cc4ca5SDavid du Colombier  *  We can't let Eintr's lose data since the program
54159cc4ca5SDavid du Colombier  *  doing the read may be able to handle it.  The only
54259cc4ca5SDavid du Colombier  *  places Eintr is possible is during the read's in consume.
54359cc4ca5SDavid du Colombier  *  Therefore, we make sure we can always put back the bytes
54459cc4ca5SDavid du Colombier  *  consumed before the last ensure.
54559cc4ca5SDavid du Colombier  */
5467dd7cddfSDavid du Colombier static Block*
sslbread(Chan * c,long n,ulong)5477dd7cddfSDavid du Colombier sslbread(Chan *c, long n, ulong)
5487dd7cddfSDavid du Colombier {
5499a747e4fSDavid du Colombier 	Dstate * volatile s;
5507dd7cddfSDavid du Colombier 	Block *b;
5519a747e4fSDavid du Colombier 	uchar consumed[3], *p;
5529a747e4fSDavid du Colombier 	int toconsume;
5537dd7cddfSDavid du Colombier 	int len, pad;
5547dd7cddfSDavid du Colombier 
5559a747e4fSDavid du Colombier 	s = dstate[CONV(c->qid)];
5569a747e4fSDavid du Colombier 	if(s == 0)
5577dd7cddfSDavid du Colombier 		panic("sslbread");
5589a747e4fSDavid du Colombier 	if(s->state == Sincomplete)
5597dd7cddfSDavid du Colombier 		error(Ebadusefd);
5607dd7cddfSDavid du Colombier 
5619a747e4fSDavid du Colombier 	qlock(&s->in.q);
5627dd7cddfSDavid du Colombier 	if(waserror()){
5639a747e4fSDavid du Colombier 		qunlock(&s->in.q);
5647dd7cddfSDavid du Colombier 		nexterror();
5657dd7cddfSDavid du Colombier 	}
5667dd7cddfSDavid du Colombier 
5679a747e4fSDavid du Colombier 	if(s->processed == 0){
5689a747e4fSDavid du Colombier 		/*
5699a747e4fSDavid du Colombier 		 * Read in the whole message.  Until we've got it all,
5709a747e4fSDavid du Colombier 		 * it stays on s->unprocessed, so that if we get Eintr,
5719a747e4fSDavid du Colombier 		 * we'll pick up where we left off.
5729a747e4fSDavid du Colombier 		 */
5739a747e4fSDavid du Colombier 		ensure(s, &s->unprocessed, 3);
5749a747e4fSDavid du Colombier 		s->unprocessed = pullupblock(s->unprocessed, 2);
5759a747e4fSDavid du Colombier 		p = s->unprocessed->rp;
5769a747e4fSDavid du Colombier 		if(p[0] & 0x80){
5779a747e4fSDavid du Colombier 			len = ((p[0] & 0x7f)<<8) | p[1];
5789a747e4fSDavid du Colombier 			ensure(s, &s->unprocessed, len);
5797dd7cddfSDavid du Colombier 			pad = 0;
5809a747e4fSDavid du Colombier 			toconsume = 2;
5817dd7cddfSDavid du Colombier 		} else {
5829a747e4fSDavid du Colombier 			s->unprocessed = pullupblock(s->unprocessed, 3);
5839a747e4fSDavid du Colombier 			len = ((p[0] & 0x3f)<<8) | p[1];
5849a747e4fSDavid du Colombier 			pad = p[2];
5857dd7cddfSDavid du Colombier 			if(pad > len){
5867dd7cddfSDavid du Colombier 				print("pad %d buf len %d\n", pad, len);
5877dd7cddfSDavid du Colombier 				error("bad pad in ssl message");
5887dd7cddfSDavid du Colombier 			}
5899a747e4fSDavid du Colombier 			toconsume = 3;
5907dd7cddfSDavid du Colombier 		}
5919a747e4fSDavid du Colombier 		ensure(s, &s->unprocessed, toconsume+len);
5927dd7cddfSDavid du Colombier 
5939a747e4fSDavid du Colombier 		/* skip header */
5949a747e4fSDavid du Colombier 		consume(&s->unprocessed, consumed, toconsume);
5959a747e4fSDavid du Colombier 
59659cc4ca5SDavid du Colombier 		/* grab the next message and decode/decrypt it */
5979a747e4fSDavid du Colombier 		b = qtake(&s->unprocessed, len, 0);
5989a747e4fSDavid du Colombier 
5999a747e4fSDavid du Colombier 		if(blocklen(b) != len)
6009a747e4fSDavid du Colombier 			print("devssl: sslbread got wrong count %d != %d", blocklen(b), len);
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier 		if(waserror()){
6039a747e4fSDavid du Colombier 			qunlock(&s->in.ctlq);
60459cc4ca5SDavid du Colombier 			if(b != nil)
60559cc4ca5SDavid du Colombier 				freeb(b);
6067dd7cddfSDavid du Colombier 			nexterror();
6077dd7cddfSDavid du Colombier 		}
6089a747e4fSDavid du Colombier 		qlock(&s->in.ctlq);
6099a747e4fSDavid du Colombier 		switch(s->state){
6107dd7cddfSDavid du Colombier 		case Sencrypting:
6119a747e4fSDavid du Colombier 			if(b == nil)
6129a747e4fSDavid du Colombier 				error("ssl message too short (encrypting)");
6139a747e4fSDavid du Colombier 			b = decryptb(s, b);
6147dd7cddfSDavid du Colombier 			break;
6157dd7cddfSDavid du Colombier 		case Sdigesting:
6169a747e4fSDavid du Colombier 			b = pullupblock(b, s->diglen);
61759cc4ca5SDavid du Colombier 			if(b == nil)
6189a747e4fSDavid du Colombier 				error("ssl message too short (digesting)");
6199a747e4fSDavid du Colombier 			checkdigestb(s, b);
62018027f8cSDavid du Colombier 			pullblock(&b, s->diglen);
62118027f8cSDavid du Colombier 			len -= s->diglen;
6227dd7cddfSDavid du Colombier 			break;
6237dd7cddfSDavid du Colombier 		case Sdigenc:
6249a747e4fSDavid du Colombier 			b = decryptb(s, b);
6259a747e4fSDavid du Colombier 			b = pullupblock(b, s->diglen);
62659cc4ca5SDavid du Colombier 			if(b == nil)
6279a747e4fSDavid du Colombier 				error("ssl message too short (dig+enc)");
6289a747e4fSDavid du Colombier 			checkdigestb(s, b);
62918027f8cSDavid du Colombier 			pullblock(&b, s->diglen);
6309a747e4fSDavid du Colombier 			len -= s->diglen;
6317dd7cddfSDavid du Colombier 			break;
6327dd7cddfSDavid du Colombier 		}
6337dd7cddfSDavid du Colombier 
6347dd7cddfSDavid du Colombier 		/* remove pad */
6357dd7cddfSDavid du Colombier 		if(pad)
6369a747e4fSDavid du Colombier 			s->processed = qtake(&b, len - pad, 1);
63759cc4ca5SDavid du Colombier 		else
6389a747e4fSDavid du Colombier 			s->processed = b;
63959cc4ca5SDavid du Colombier 		b = nil;
6409a747e4fSDavid du Colombier 		s->in.mid++;
6419a747e4fSDavid du Colombier 		qunlock(&s->in.ctlq);
64259cc4ca5SDavid du Colombier 		poperror();
6437dd7cddfSDavid du Colombier 	}
6447dd7cddfSDavid du Colombier 
6457dd7cddfSDavid du Colombier 	/* return at most what was asked for */
6469a747e4fSDavid du Colombier 	b = qtake(&s->processed, n, 0);
6477dd7cddfSDavid du Colombier 
6489a747e4fSDavid du Colombier 	qunlock(&s->in.q);
6497dd7cddfSDavid du Colombier 	poperror();
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier 	return b;
6527dd7cddfSDavid du Colombier }
6537dd7cddfSDavid du Colombier 
6547dd7cddfSDavid du Colombier static long
sslread(Chan * c,void * a,long n,vlong off)6557dd7cddfSDavid du Colombier sslread(Chan *c, void *a, long n, vlong off)
6567dd7cddfSDavid du Colombier {
6579a747e4fSDavid du Colombier 	Block * volatile b;
6587dd7cddfSDavid du Colombier 	Block *nb;
6597dd7cddfSDavid du Colombier 	uchar *va;
6607dd7cddfSDavid du Colombier 	int i;
6617dd7cddfSDavid du Colombier 	char buf[128];
6627dd7cddfSDavid du Colombier 	ulong offset = off;
6639a747e4fSDavid du Colombier 	int ft;
6647dd7cddfSDavid du Colombier 
6659a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR)
6667dd7cddfSDavid du Colombier 		return devdirread(c, a, n, 0, 0, sslgen);
6677dd7cddfSDavid du Colombier 
6689a747e4fSDavid du Colombier 	ft = TYPE(c->qid);
6699a747e4fSDavid du Colombier 	switch(ft) {
6707dd7cddfSDavid du Colombier 	default:
6717dd7cddfSDavid du Colombier 		error(Ebadusefd);
6727dd7cddfSDavid du Colombier 	case Qctl:
6739a747e4fSDavid du Colombier 		ft = CONV(c->qid);
674*4e3613abSDavid du Colombier 		snprint(buf, sizeof buf, "%d", ft);
6757dd7cddfSDavid du Colombier 		return readstr(offset, a, n, buf);
6767dd7cddfSDavid du Colombier 	case Qdata:
6779a747e4fSDavid du Colombier 		b = sslbread(c, n, offset);
6787dd7cddfSDavid du Colombier 		break;
6797dd7cddfSDavid du Colombier 	case Qencalgs:
6807dd7cddfSDavid du Colombier 		return readstr(offset, a, n, encalgs);
6817dd7cddfSDavid du Colombier 		break;
6827dd7cddfSDavid du Colombier 	case Qhashalgs:
6837dd7cddfSDavid du Colombier 		return readstr(offset, a, n, hashalgs);
6847dd7cddfSDavid du Colombier 		break;
6857dd7cddfSDavid du Colombier 	}
6867dd7cddfSDavid du Colombier 
6877dd7cddfSDavid du Colombier 	if(waserror()){
6889a747e4fSDavid du Colombier 		freeblist(b);
6897dd7cddfSDavid du Colombier 		nexterror();
6907dd7cddfSDavid du Colombier 	}
6917dd7cddfSDavid du Colombier 
6927dd7cddfSDavid du Colombier 	n = 0;
6937dd7cddfSDavid du Colombier 	va = a;
6949a747e4fSDavid du Colombier 	for(nb = b; nb; nb = nb->next){
6957dd7cddfSDavid du Colombier 		i = BLEN(nb);
6967dd7cddfSDavid du Colombier 		memmove(va+n, nb->rp, i);
6977dd7cddfSDavid du Colombier 		n += i;
6987dd7cddfSDavid du Colombier 	}
6997dd7cddfSDavid du Colombier 
7009a747e4fSDavid du Colombier 	freeblist(b);
7017dd7cddfSDavid du Colombier 	poperror();
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier 	return n;
7047dd7cddfSDavid du Colombier }
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier /*
7077dd7cddfSDavid du Colombier  *  this algorithm doesn't have to be great since we're just
7087dd7cddfSDavid du Colombier  *  trying to obscure the block fill
7097dd7cddfSDavid du Colombier  */
7107dd7cddfSDavid du Colombier static void
randfill(uchar * buf,int len)7117dd7cddfSDavid du Colombier randfill(uchar *buf, int len)
7127dd7cddfSDavid du Colombier {
7137dd7cddfSDavid du Colombier 	while(len-- > 0)
7147dd7cddfSDavid du Colombier 		*buf++ = nrand(256);
7157dd7cddfSDavid du Colombier }
7167dd7cddfSDavid du Colombier 
7179a747e4fSDavid du Colombier static long
sslbwrite(Chan * c,Block * b,ulong)7189a747e4fSDavid du Colombier sslbwrite(Chan *c, Block *b, ulong)
7199a747e4fSDavid du Colombier {
7209a747e4fSDavid du Colombier 	Dstate * volatile s;
7219a747e4fSDavid du Colombier 	long rv;
7229a747e4fSDavid du Colombier 
7239a747e4fSDavid du Colombier 	s = dstate[CONV(c->qid)];
7249a747e4fSDavid du Colombier 	if(s == nil)
7259a747e4fSDavid du Colombier 		panic("sslbwrite");
7269a747e4fSDavid du Colombier 
7279a747e4fSDavid du Colombier 	if(s->state == Sincomplete){
7289a747e4fSDavid du Colombier 		freeb(b);
7299a747e4fSDavid du Colombier 		error(Ebadusefd);
7309a747e4fSDavid du Colombier 	}
7319a747e4fSDavid du Colombier 
7329a747e4fSDavid du Colombier 	/* lock so split writes won't interleave */
7339a747e4fSDavid du Colombier 	if(waserror()){
7349a747e4fSDavid du Colombier 		qunlock(&s->out.q);
7359a747e4fSDavid du Colombier 		nexterror();
7369a747e4fSDavid du Colombier 	}
7379a747e4fSDavid du Colombier 	qlock(&s->out.q);
7389a747e4fSDavid du Colombier 
7399a747e4fSDavid du Colombier 	rv = sslput(s, b);
7409a747e4fSDavid du Colombier 
7419a747e4fSDavid du Colombier 	poperror();
7429a747e4fSDavid du Colombier 	qunlock(&s->out.q);
7439a747e4fSDavid du Colombier 
7449a747e4fSDavid du Colombier 	return rv;
7459a747e4fSDavid du Colombier }
7469a747e4fSDavid du Colombier 
7477dd7cddfSDavid du Colombier /*
74859cc4ca5SDavid du Colombier  *  use SSL record format, add in count, digest and/or encrypt.
74959cc4ca5SDavid du Colombier  *  the write is interruptable.  if it is interrupted, we'll
75059cc4ca5SDavid du Colombier  *  get out of sync with the far side.  not much we can do about
75159cc4ca5SDavid du Colombier  *  it since we don't know if any bytes have been written.
7527dd7cddfSDavid du Colombier  */
7537dd7cddfSDavid du Colombier static long
sslput(Dstate * s,Block * volatile b)7549a747e4fSDavid du Colombier sslput(Dstate *s, Block * volatile b)
7557dd7cddfSDavid du Colombier {
7567dd7cddfSDavid du Colombier 	Block *nb;
7577dd7cddfSDavid du Colombier 	int h, n, m, pad, rv;
7587dd7cddfSDavid du Colombier 	uchar *p;
7599a747e4fSDavid du Colombier 	int offset;
7607dd7cddfSDavid du Colombier 
7617dd7cddfSDavid du Colombier 	if(waserror()){
7629a747e4fSDavid du Colombier 		if(b != nil)
763e360d427SDavid du Colombier 			freeb(b);
7647dd7cddfSDavid du Colombier 		nexterror();
7657dd7cddfSDavid du Colombier 	}
7667dd7cddfSDavid du Colombier 
7677dd7cddfSDavid du Colombier 	rv = 0;
7689a747e4fSDavid du Colombier 	while(b != nil){
7699a747e4fSDavid du Colombier 		m = n = BLEN(b);
7709a747e4fSDavid du Colombier 		h = s->diglen + 2;
7717dd7cddfSDavid du Colombier 
7727dd7cddfSDavid du Colombier 		/* trim to maximum block size */
7737dd7cddfSDavid du Colombier 		pad = 0;
7749a747e4fSDavid du Colombier 		if(m > s->max){
7759a747e4fSDavid du Colombier 			m = s->max;
7769a747e4fSDavid du Colombier 		} else if(s->blocklen != 1){
7779a747e4fSDavid du Colombier 			pad = (m + s->diglen)%s->blocklen;
7787dd7cddfSDavid du Colombier 			if(pad){
7799a747e4fSDavid du Colombier 				if(m > s->maxpad){
7807dd7cddfSDavid du Colombier 					pad = 0;
7819a747e4fSDavid du Colombier 					m = s->maxpad;
7827dd7cddfSDavid du Colombier 				} else {
7839a747e4fSDavid du Colombier 					pad = s->blocklen - pad;
7847dd7cddfSDavid du Colombier 					h++;
7857dd7cddfSDavid du Colombier 				}
7867dd7cddfSDavid du Colombier 			}
7877dd7cddfSDavid du Colombier 		}
7887dd7cddfSDavid du Colombier 
7897dd7cddfSDavid du Colombier 		rv += m;
7907dd7cddfSDavid du Colombier 		if(m != n){
7917dd7cddfSDavid du Colombier 			nb = allocb(m + h + pad);
7929a747e4fSDavid du Colombier 			memmove(nb->wp + h, b->rp, m);
7937dd7cddfSDavid du Colombier 			nb->wp += m + h;
7949a747e4fSDavid du Colombier 			b->rp += m;
7957dd7cddfSDavid du Colombier 		} else {
7967dd7cddfSDavid du Colombier 			/* add header space */
7979a747e4fSDavid du Colombier 			nb = padblock(b, h);
7989a747e4fSDavid du Colombier 			b = 0;
7997dd7cddfSDavid du Colombier 		}
8009a747e4fSDavid du Colombier 		m += s->diglen;
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 		/* SSL style count */
8037dd7cddfSDavid du Colombier 		if(pad){
8047dd7cddfSDavid du Colombier 			nb = padblock(nb, -pad);
8057dd7cddfSDavid du Colombier 			randfill(nb->wp, pad);
8067dd7cddfSDavid du Colombier 			nb->wp += pad;
8077dd7cddfSDavid du Colombier 			m += pad;
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier 			p = nb->rp;
8107dd7cddfSDavid du Colombier 			p[0] = (m>>8);
8117dd7cddfSDavid du Colombier 			p[1] = m;
8127dd7cddfSDavid du Colombier 			p[2] = pad;
8137dd7cddfSDavid du Colombier 			offset = 3;
8147dd7cddfSDavid du Colombier 		} else {
8157dd7cddfSDavid du Colombier 			p = nb->rp;
8167dd7cddfSDavid du Colombier 			p[0] = (m>>8) | 0x80;
8177dd7cddfSDavid du Colombier 			p[1] = m;
8187dd7cddfSDavid du Colombier 			offset = 2;
8197dd7cddfSDavid du Colombier 		}
8207dd7cddfSDavid du Colombier 
8219a747e4fSDavid du Colombier 		switch(s->state){
8227dd7cddfSDavid du Colombier 		case Sencrypting:
8239a747e4fSDavid du Colombier 			nb = encryptb(s, nb, offset);
8247dd7cddfSDavid du Colombier 			break;
8257dd7cddfSDavid du Colombier 		case Sdigesting:
8269a747e4fSDavid du Colombier 			nb = digestb(s, nb, offset);
8277dd7cddfSDavid du Colombier 			break;
8287dd7cddfSDavid du Colombier 		case Sdigenc:
8299a747e4fSDavid du Colombier 			nb = digestb(s, nb, offset);
8309a747e4fSDavid du Colombier 			nb = encryptb(s, nb, offset);
8317dd7cddfSDavid du Colombier 			break;
8327dd7cddfSDavid du Colombier 		}
8337dd7cddfSDavid du Colombier 
8349a747e4fSDavid du Colombier 		s->out.mid++;
8357dd7cddfSDavid du Colombier 
8367dd7cddfSDavid du Colombier 		m = BLEN(nb);
8379a747e4fSDavid du Colombier 		devtab[s->c->type]->bwrite(s->c, nb, s->c->offset);
8389a747e4fSDavid du Colombier 		s->c->offset += m;
8397dd7cddfSDavid du Colombier 	}
8407dd7cddfSDavid du Colombier 
8419a747e4fSDavid du Colombier 	poperror();
8427dd7cddfSDavid du Colombier 	return rv;
8437dd7cddfSDavid du Colombier }
8447dd7cddfSDavid du Colombier 
8457dd7cddfSDavid du Colombier static void
setsecret(OneWay * w,uchar * secret,int n)8467dd7cddfSDavid du Colombier setsecret(OneWay *w, uchar *secret, int n)
8477dd7cddfSDavid du Colombier {
8487dd7cddfSDavid du Colombier 	if(w->secret)
8497dd7cddfSDavid du Colombier 		free(w->secret);
8507dd7cddfSDavid du Colombier 
8517dd7cddfSDavid du Colombier 	w->secret = smalloc(n);
8527dd7cddfSDavid du Colombier 	memmove(w->secret, secret, n);
8537dd7cddfSDavid du Colombier 	w->slen = n;
8547dd7cddfSDavid du Colombier }
8557dd7cddfSDavid du Colombier 
8567dd7cddfSDavid du Colombier static void
initDESkey(OneWay * w)8577dd7cddfSDavid du Colombier initDESkey(OneWay *w)
8587dd7cddfSDavid du Colombier {
8597dd7cddfSDavid du Colombier 	if(w->state){
8607dd7cddfSDavid du Colombier 		free(w->state);
8617dd7cddfSDavid du Colombier 		w->state = 0;
8627dd7cddfSDavid du Colombier 	}
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier 	w->state = smalloc(sizeof(DESstate));
8657dd7cddfSDavid du Colombier 	if(w->slen >= 16)
8667dd7cddfSDavid du Colombier 		setupDESstate(w->state, w->secret, w->secret+8);
8677dd7cddfSDavid du Colombier 	else if(w->slen >= 8)
8687dd7cddfSDavid du Colombier 		setupDESstate(w->state, w->secret, 0);
8697dd7cddfSDavid du Colombier 	else
8707dd7cddfSDavid du Colombier 		error("secret too short");
8717dd7cddfSDavid du Colombier }
8727dd7cddfSDavid du Colombier 
8737dd7cddfSDavid du Colombier /*
8747dd7cddfSDavid du Colombier  *  40 bit DES is the same as 56 bit DES.  However,
8757dd7cddfSDavid du Colombier  *  16 bits of the key are masked to zero.
8767dd7cddfSDavid du Colombier  */
8777dd7cddfSDavid du Colombier static void
initDESkey_40(OneWay * w)8787dd7cddfSDavid du Colombier initDESkey_40(OneWay *w)
8797dd7cddfSDavid du Colombier {
8807dd7cddfSDavid du Colombier 	uchar key[8];
8817dd7cddfSDavid du Colombier 
8827dd7cddfSDavid du Colombier 	if(w->state){
8837dd7cddfSDavid du Colombier 		free(w->state);
8847dd7cddfSDavid du Colombier 		w->state = 0;
8857dd7cddfSDavid du Colombier 	}
8867dd7cddfSDavid du Colombier 
8877dd7cddfSDavid du Colombier 	if(w->slen >= 8){
8887dd7cddfSDavid du Colombier 		memmove(key, w->secret, 8);
8897dd7cddfSDavid du Colombier 		key[0] &= 0x0f;
8907dd7cddfSDavid du Colombier 		key[2] &= 0x0f;
8917dd7cddfSDavid du Colombier 		key[4] &= 0x0f;
8927dd7cddfSDavid du Colombier 		key[6] &= 0x0f;
8937dd7cddfSDavid du Colombier 	}
8947dd7cddfSDavid du Colombier 
8957dd7cddfSDavid du Colombier 	w->state = malloc(sizeof(DESstate));
896aa72973aSDavid du Colombier 	if(w->state == nil)
897aa72973aSDavid du Colombier 		error(Enomem);
8987dd7cddfSDavid du Colombier 	if(w->slen >= 16)
8997dd7cddfSDavid du Colombier 		setupDESstate(w->state, key, w->secret+8);
9007dd7cddfSDavid du Colombier 	else if(w->slen >= 8)
9017dd7cddfSDavid du Colombier 		setupDESstate(w->state, key, 0);
9027dd7cddfSDavid du Colombier 	else
9037dd7cddfSDavid du Colombier 		error("secret too short");
9047dd7cddfSDavid du Colombier }
9057dd7cddfSDavid du Colombier 
9067dd7cddfSDavid du Colombier static void
initRC4key(OneWay * w)9077dd7cddfSDavid du Colombier initRC4key(OneWay *w)
9087dd7cddfSDavid du Colombier {
9097dd7cddfSDavid du Colombier 	if(w->state){
9107dd7cddfSDavid du Colombier 		free(w->state);
9117dd7cddfSDavid du Colombier 		w->state = 0;
9127dd7cddfSDavid du Colombier 	}
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier 	w->state = smalloc(sizeof(RC4state));
9157dd7cddfSDavid du Colombier 	setupRC4state(w->state, w->secret, w->slen);
9167dd7cddfSDavid du Colombier }
9177dd7cddfSDavid du Colombier 
9187dd7cddfSDavid du Colombier /*
9197dd7cddfSDavid du Colombier  *  40 bit RC4 is the same as n-bit RC4.  However,
9207dd7cddfSDavid du Colombier  *  we ignore all but the first 40 bits of the key.
9217dd7cddfSDavid du Colombier  */
9227dd7cddfSDavid du Colombier static void
initRC4key_40(OneWay * w)9237dd7cddfSDavid du Colombier initRC4key_40(OneWay *w)
9247dd7cddfSDavid du Colombier {
9257dd7cddfSDavid du Colombier 	if(w->state){
9267dd7cddfSDavid du Colombier 		free(w->state);
9277dd7cddfSDavid du Colombier 		w->state = 0;
9287dd7cddfSDavid du Colombier 	}
9297dd7cddfSDavid du Colombier 
9307dd7cddfSDavid du Colombier 	if(w->slen > 5)
9317dd7cddfSDavid du Colombier 		w->slen = 5;
9327dd7cddfSDavid du Colombier 
9337dd7cddfSDavid du Colombier 	w->state = malloc(sizeof(RC4state));
934aa72973aSDavid du Colombier 	if(w->state == nil)
935aa72973aSDavid du Colombier 		error(Enomem);
9367dd7cddfSDavid du Colombier 	setupRC4state(w->state, w->secret, w->slen);
9377dd7cddfSDavid du Colombier }
9387dd7cddfSDavid du Colombier 
9397dd7cddfSDavid du Colombier /*
9407dd7cddfSDavid du Colombier  *  128 bit RC4 is the same as n-bit RC4.  However,
9417dd7cddfSDavid du Colombier  *  we ignore all but the first 128 bits of the key.
9427dd7cddfSDavid du Colombier  */
9437dd7cddfSDavid du Colombier static void
initRC4key_128(OneWay * w)9447dd7cddfSDavid du Colombier initRC4key_128(OneWay *w)
9457dd7cddfSDavid du Colombier {
9467dd7cddfSDavid du Colombier 	if(w->state){
9477dd7cddfSDavid du Colombier 		free(w->state);
9487dd7cddfSDavid du Colombier 		w->state = 0;
9497dd7cddfSDavid du Colombier 	}
9507dd7cddfSDavid du Colombier 
9517dd7cddfSDavid du Colombier 	if(w->slen > 16)
9527dd7cddfSDavid du Colombier 		w->slen = 16;
9537dd7cddfSDavid du Colombier 
9547dd7cddfSDavid du Colombier 	w->state = malloc(sizeof(RC4state));
955aa72973aSDavid du Colombier 	if(w->state == nil)
956aa72973aSDavid du Colombier 		error(Enomem);
9577dd7cddfSDavid du Colombier 	setupRC4state(w->state, w->secret, w->slen);
9587dd7cddfSDavid du Colombier }
9597dd7cddfSDavid du Colombier 
9607dd7cddfSDavid du Colombier 
9617dd7cddfSDavid du Colombier typedef struct Hashalg Hashalg;
9627dd7cddfSDavid du Colombier struct Hashalg
9637dd7cddfSDavid du Colombier {
9647dd7cddfSDavid du Colombier 	char	*name;
9657dd7cddfSDavid du Colombier 	int	diglen;
9667dd7cddfSDavid du Colombier 	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
9677dd7cddfSDavid du Colombier };
9687dd7cddfSDavid du Colombier 
9697dd7cddfSDavid du Colombier Hashalg hashtab[] =
9707dd7cddfSDavid du Colombier {
9717dd7cddfSDavid du Colombier 	{ "md4", MD4dlen, md4, },
9727dd7cddfSDavid du Colombier 	{ "md5", MD5dlen, md5, },
9737dd7cddfSDavid du Colombier 	{ "sha1", SHA1dlen, sha1, },
9747dd7cddfSDavid du Colombier 	{ "sha", SHA1dlen, sha1, },
9757dd7cddfSDavid du Colombier 	{ 0 }
9767dd7cddfSDavid du Colombier };
9777dd7cddfSDavid du Colombier 
9787dd7cddfSDavid du Colombier static int
parsehashalg(char * p,Dstate * s)9797dd7cddfSDavid du Colombier parsehashalg(char *p, Dstate *s)
9807dd7cddfSDavid du Colombier {
9817dd7cddfSDavid du Colombier 	Hashalg *ha;
9827dd7cddfSDavid du Colombier 
9837dd7cddfSDavid du Colombier 	for(ha = hashtab; ha->name; ha++){
9847dd7cddfSDavid du Colombier 		if(strcmp(p, ha->name) == 0){
9857dd7cddfSDavid du Colombier 			s->hf = ha->hf;
9867dd7cddfSDavid du Colombier 			s->diglen = ha->diglen;
9877dd7cddfSDavid du Colombier 			s->state &= ~Sclear;
9887dd7cddfSDavid du Colombier 			s->state |= Sdigesting;
9897dd7cddfSDavid du Colombier 			return 0;
9907dd7cddfSDavid du Colombier 		}
9917dd7cddfSDavid du Colombier 	}
9927dd7cddfSDavid du Colombier 	return -1;
9937dd7cddfSDavid du Colombier }
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier typedef struct Encalg Encalg;
9967dd7cddfSDavid du Colombier struct Encalg
9977dd7cddfSDavid du Colombier {
9987dd7cddfSDavid du Colombier 	char	*name;
9997dd7cddfSDavid du Colombier 	int	blocklen;
10007dd7cddfSDavid du Colombier 	int	alg;
10017dd7cddfSDavid du Colombier 	void	(*keyinit)(OneWay*);
10027dd7cddfSDavid du Colombier };
10037dd7cddfSDavid du Colombier 
10047dd7cddfSDavid du Colombier #ifdef NOSPOOKS
10057dd7cddfSDavid du Colombier Encalg encrypttab[] =
10067dd7cddfSDavid du Colombier {
10077dd7cddfSDavid du Colombier 	{ "descbc", 8, DESCBC, initDESkey, },           /* DEPRECATED -- use des_56_cbc */
10087dd7cddfSDavid du Colombier 	{ "desecb", 8, DESECB, initDESkey, },           /* DEPRECATED -- use des_56_ecb */
10097dd7cddfSDavid du Colombier 	{ "des_56_cbc", 8, DESCBC, initDESkey, },
10107dd7cddfSDavid du Colombier 	{ "des_56_ecb", 8, DESECB, initDESkey, },
10117dd7cddfSDavid du Colombier 	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
10127dd7cddfSDavid du Colombier 	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
10137dd7cddfSDavid du Colombier 	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
10147dd7cddfSDavid du Colombier 	{ "rc4_256", 1, RC4, initRC4key, },
10157dd7cddfSDavid du Colombier 	{ "rc4_128", 1, RC4, initRC4key_128, },
10167dd7cddfSDavid du Colombier 	{ "rc4_40", 1, RC4, initRC4key_40, },
10177dd7cddfSDavid du Colombier 	{ 0 }
10187dd7cddfSDavid du Colombier };
10197dd7cddfSDavid du Colombier #else
10207dd7cddfSDavid du Colombier Encalg encrypttab[] =
10217dd7cddfSDavid du Colombier {
10227dd7cddfSDavid du Colombier 	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
10237dd7cddfSDavid du Colombier 	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
10247dd7cddfSDavid du Colombier 	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
10257dd7cddfSDavid du Colombier 	{ "rc4_40", 1, RC4, initRC4key_40, },
10267dd7cddfSDavid du Colombier 	{ 0 }
10277dd7cddfSDavid du Colombier };
10287dd7cddfSDavid du Colombier #endif NOSPOOKS
10297dd7cddfSDavid du Colombier 
10307dd7cddfSDavid du Colombier static int
parseencryptalg(char * p,Dstate * s)10317dd7cddfSDavid du Colombier parseencryptalg(char *p, Dstate *s)
10327dd7cddfSDavid du Colombier {
10337dd7cddfSDavid du Colombier 	Encalg *ea;
10347dd7cddfSDavid du Colombier 
10357dd7cddfSDavid du Colombier 	for(ea = encrypttab; ea->name; ea++){
10367dd7cddfSDavid du Colombier 		if(strcmp(p, ea->name) == 0){
10377dd7cddfSDavid du Colombier 			s->encryptalg = ea->alg;
10387dd7cddfSDavid du Colombier 			s->blocklen = ea->blocklen;
10397dd7cddfSDavid du Colombier 			(*ea->keyinit)(&s->in);
10407dd7cddfSDavid du Colombier 			(*ea->keyinit)(&s->out);
10417dd7cddfSDavid du Colombier 			s->state &= ~Sclear;
10427dd7cddfSDavid du Colombier 			s->state |= Sencrypting;
10437dd7cddfSDavid du Colombier 			return 0;
10447dd7cddfSDavid du Colombier 		}
10457dd7cddfSDavid du Colombier 	}
10467dd7cddfSDavid du Colombier 	return -1;
10477dd7cddfSDavid du Colombier }
10487dd7cddfSDavid du Colombier 
10497dd7cddfSDavid du Colombier static long
sslwrite(Chan * c,void * a,long n,vlong)10509a747e4fSDavid du Colombier sslwrite(Chan *c, void *a, long n, vlong)
10517dd7cddfSDavid du Colombier {
10529a747e4fSDavid du Colombier 	Dstate * volatile s;
10539a747e4fSDavid du Colombier 	Block * volatile b;
10547dd7cddfSDavid du Colombier 	int m, t;
10557dd7cddfSDavid du Colombier 	char *p, *np, *e, buf[128];
10567dd7cddfSDavid du Colombier 	uchar *x;
10577dd7cddfSDavid du Colombier 
1058f7db6155SDavid du Colombier 	x = nil;
10599a747e4fSDavid du Colombier 	s = dstate[CONV(c->qid)];
10609a747e4fSDavid du Colombier 	if(s == 0)
10617dd7cddfSDavid du Colombier 		panic("sslwrite");
10627dd7cddfSDavid du Colombier 
10637dd7cddfSDavid du Colombier 	t = TYPE(c->qid);
10647dd7cddfSDavid du Colombier 	if(t == Qdata){
10659a747e4fSDavid du Colombier 		if(s->state == Sincomplete)
10667dd7cddfSDavid du Colombier 			error(Ebadusefd);
10677dd7cddfSDavid du Colombier 
10689a747e4fSDavid du Colombier 		/* lock should a write gets split over multiple records */
10699a747e4fSDavid du Colombier 		if(waserror()){
10709a747e4fSDavid du Colombier 			qunlock(&s->out.q);
10719a747e4fSDavid du Colombier 			nexterror();
10729a747e4fSDavid du Colombier 		}
10739a747e4fSDavid du Colombier 		qlock(&s->out.q);
10749a747e4fSDavid du Colombier 
10757dd7cddfSDavid du Colombier 		p = a;
10767dd7cddfSDavid du Colombier 		e = p + n;
10777dd7cddfSDavid du Colombier 		do {
10787dd7cddfSDavid du Colombier 			m = e - p;
10799a747e4fSDavid du Colombier 			if(m > s->max)
10809a747e4fSDavid du Colombier 				m = s->max;
10817dd7cddfSDavid du Colombier 
10829a747e4fSDavid du Colombier 			b = allocb(m);
10837dd7cddfSDavid du Colombier 			if(waserror()){
10849a747e4fSDavid du Colombier 				freeb(b);
10857dd7cddfSDavid du Colombier 				nexterror();
10867dd7cddfSDavid du Colombier 			}
10879a747e4fSDavid du Colombier 			memmove(b->wp, p, m);
10887dd7cddfSDavid du Colombier 			poperror();
10899a747e4fSDavid du Colombier 			b->wp += m;
10907dd7cddfSDavid du Colombier 
10919a747e4fSDavid du Colombier 			sslput(s, b);
10927dd7cddfSDavid du Colombier 
10937dd7cddfSDavid du Colombier 			p += m;
10947dd7cddfSDavid du Colombier 		} while(p < e);
10959a747e4fSDavid du Colombier 
10969a747e4fSDavid du Colombier 		poperror();
10979a747e4fSDavid du Colombier 		qunlock(&s->out.q);
10987dd7cddfSDavid du Colombier 		return n;
10997dd7cddfSDavid du Colombier 	}
11007dd7cddfSDavid du Colombier 
11017dd7cddfSDavid du Colombier 	/* mutex with operations using what we're about to change */
11027dd7cddfSDavid du Colombier 	if(waserror()){
11039a747e4fSDavid du Colombier 		qunlock(&s->in.ctlq);
11049a747e4fSDavid du Colombier 		qunlock(&s->out.q);
11057dd7cddfSDavid du Colombier 		nexterror();
11067dd7cddfSDavid du Colombier 	}
11079a747e4fSDavid du Colombier 	qlock(&s->in.ctlq);
11089a747e4fSDavid du Colombier 	qlock(&s->out.q);
11097dd7cddfSDavid du Colombier 
11107dd7cddfSDavid du Colombier 	switch(t){
11117dd7cddfSDavid du Colombier 	default:
11127dd7cddfSDavid du Colombier 		panic("sslwrite");
11137dd7cddfSDavid du Colombier 	case Qsecretin:
11149a747e4fSDavid du Colombier 		setsecret(&s->in, a, n);
11157dd7cddfSDavid du Colombier 		goto out;
11167dd7cddfSDavid du Colombier 	case Qsecretout:
11179a747e4fSDavid du Colombier 		setsecret(&s->out, a, n);
11187dd7cddfSDavid du Colombier 		goto out;
11197dd7cddfSDavid du Colombier 	case Qctl:
11207dd7cddfSDavid du Colombier 		break;
11217dd7cddfSDavid du Colombier 	}
11227dd7cddfSDavid du Colombier 
11237dd7cddfSDavid du Colombier 	if(n >= sizeof(buf))
11247dd7cddfSDavid du Colombier 		error("arg too long");
11257dd7cddfSDavid du Colombier 	strncpy(buf, a, n);
11267dd7cddfSDavid du Colombier 	buf[n] = 0;
11277dd7cddfSDavid du Colombier 	p = strchr(buf, '\n');
11287dd7cddfSDavid du Colombier 	if(p)
11297dd7cddfSDavid du Colombier 		*p = 0;
11307dd7cddfSDavid du Colombier 	p = strchr(buf, ' ');
11317dd7cddfSDavid du Colombier 	if(p)
11327dd7cddfSDavid du Colombier 		*p++ = 0;
11337dd7cddfSDavid du Colombier 
1134f7db6155SDavid du Colombier 	if(waserror()){
1135f7db6155SDavid du Colombier 		free(x);
1136f7db6155SDavid du Colombier 		nexterror();
1137f7db6155SDavid du Colombier 	}
11387dd7cddfSDavid du Colombier 	if(strcmp(buf, "fd") == 0){
11399a747e4fSDavid du Colombier 		s->c = buftochan(p);
11407dd7cddfSDavid du Colombier 
11417dd7cddfSDavid du Colombier 		/* default is clear (msg delimiters only) */
11429a747e4fSDavid du Colombier 		s->state = Sclear;
11439a747e4fSDavid du Colombier 		s->blocklen = 1;
11449a747e4fSDavid du Colombier 		s->diglen = 0;
11459a747e4fSDavid du Colombier 		s->maxpad = s->max = (1<<15) - s->diglen - 1;
11469a747e4fSDavid du Colombier 		s->in.mid = 0;
11479a747e4fSDavid du Colombier 		s->out.mid = 0;
11487dd7cddfSDavid du Colombier 	} else if(strcmp(buf, "alg") == 0 && p != 0){
11499a747e4fSDavid du Colombier 		s->blocklen = 1;
11509a747e4fSDavid du Colombier 		s->diglen = 0;
11517dd7cddfSDavid du Colombier 
11529a747e4fSDavid du Colombier 		if(s->c == 0)
11537dd7cddfSDavid du Colombier 			error("must set fd before algorithm");
11547dd7cddfSDavid du Colombier 
11559a747e4fSDavid du Colombier 		s->state = Sclear;
11569a747e4fSDavid du Colombier 		s->maxpad = s->max = (1<<15) - s->diglen - 1;
1157f7db6155SDavid du Colombier 		if(strcmp(p, "clear") == 0)
1158f7db6155SDavid du Colombier 			goto outx;
11597dd7cddfSDavid du Colombier 
11609a747e4fSDavid du Colombier 		if(s->in.secret && s->out.secret == 0)
11619a747e4fSDavid du Colombier 			setsecret(&s->out, s->in.secret, s->in.slen);
11629a747e4fSDavid du Colombier 		if(s->out.secret && s->in.secret == 0)
11639a747e4fSDavid du Colombier 			setsecret(&s->in, s->out.secret, s->out.slen);
11649a747e4fSDavid du Colombier 		if(s->in.secret == 0 || s->out.secret == 0)
11657dd7cddfSDavid du Colombier 			error("algorithm but no secret");
11667dd7cddfSDavid du Colombier 
11679a747e4fSDavid du Colombier 		s->hf = 0;
11689a747e4fSDavid du Colombier 		s->encryptalg = Noencryption;
11699a747e4fSDavid du Colombier 		s->blocklen = 1;
11707dd7cddfSDavid du Colombier 
11717dd7cddfSDavid du Colombier 		for(;;){
11727dd7cddfSDavid du Colombier 			np = strchr(p, ' ');
11737dd7cddfSDavid du Colombier 			if(np)
11747dd7cddfSDavid du Colombier 				*np++ = 0;
11757dd7cddfSDavid du Colombier 
11769a747e4fSDavid du Colombier 			if(parsehashalg(p, s) < 0)
11779a747e4fSDavid du Colombier 			if(parseencryptalg(p, s) < 0)
11787dd7cddfSDavid du Colombier 				error("bad algorithm");
11797dd7cddfSDavid du Colombier 
11807dd7cddfSDavid du Colombier 			if(np == 0)
11817dd7cddfSDavid du Colombier 				break;
11827dd7cddfSDavid du Colombier 			p = np;
11837dd7cddfSDavid du Colombier 		}
11847dd7cddfSDavid du Colombier 
11859a747e4fSDavid du Colombier 		if(s->hf == 0 && s->encryptalg == Noencryption)
11867dd7cddfSDavid du Colombier 			error("bad algorithm");
11877dd7cddfSDavid du Colombier 
11889a747e4fSDavid du Colombier 		if(s->blocklen != 1){
11899a747e4fSDavid du Colombier 			s->max = (1<<15) - s->diglen - 1;
11909a747e4fSDavid du Colombier 			s->max -= s->max % s->blocklen;
11919a747e4fSDavid du Colombier 			s->maxpad = (1<<14) - s->diglen - 1;
11929a747e4fSDavid du Colombier 			s->maxpad -= s->maxpad % s->blocklen;
11937dd7cddfSDavid du Colombier 		} else
11949a747e4fSDavid du Colombier 			s->maxpad = s->max = (1<<15) - s->diglen - 1;
11957dd7cddfSDavid du Colombier 	} else if(strcmp(buf, "secretin") == 0 && p != 0) {
11967dd7cddfSDavid du Colombier 		m = (strlen(p)*3)/2;
11977dd7cddfSDavid du Colombier 		x = smalloc(m);
11989a747e4fSDavid du Colombier 		t = dec64(x, m, p, strlen(p));
1199f7db6155SDavid du Colombier 		if(t <= 0)
1200f7db6155SDavid du Colombier 			error(Ebadarg);
12019a747e4fSDavid du Colombier 		setsecret(&s->in, x, t);
12027dd7cddfSDavid du Colombier 	} else if(strcmp(buf, "secretout") == 0 && p != 0) {
12037dd7cddfSDavid du Colombier 		m = (strlen(p)*3)/2 + 1;
12047dd7cddfSDavid du Colombier 		x = smalloc(m);
12059a747e4fSDavid du Colombier 		t = dec64(x, m, p, strlen(p));
1206f7db6155SDavid du Colombier 		if(t <= 0)
1207f7db6155SDavid du Colombier 			error(Ebadarg);
12089a747e4fSDavid du Colombier 		setsecret(&s->out, x, t);
12097dd7cddfSDavid du Colombier 	} else
12107dd7cddfSDavid du Colombier 		error(Ebadarg);
1211f7db6155SDavid du Colombier outx:
1212f7db6155SDavid du Colombier 	free(x);
1213f7db6155SDavid du Colombier 	poperror();
12147dd7cddfSDavid du Colombier out:
12159a747e4fSDavid du Colombier 	qunlock(&s->in.ctlq);
12169a747e4fSDavid du Colombier 	qunlock(&s->out.q);
12177dd7cddfSDavid du Colombier 	poperror();
12187dd7cddfSDavid du Colombier 	return n;
12197dd7cddfSDavid du Colombier }
12207dd7cddfSDavid du Colombier 
12217dd7cddfSDavid du Colombier static void
sslinit(void)12227dd7cddfSDavid du Colombier sslinit(void)
12237dd7cddfSDavid du Colombier {
12247dd7cddfSDavid du Colombier 	struct Encalg *e;
12257dd7cddfSDavid du Colombier 	struct Hashalg *h;
12267dd7cddfSDavid du Colombier 	int n;
12277dd7cddfSDavid du Colombier 	char *cp;
12287dd7cddfSDavid du Colombier 
12297dd7cddfSDavid du Colombier 	n = 1;
12307dd7cddfSDavid du Colombier 	for(e = encrypttab; e->name != nil; e++)
12317dd7cddfSDavid du Colombier 		n += strlen(e->name) + 1;
12327dd7cddfSDavid du Colombier 	cp = encalgs = smalloc(n);
12337dd7cddfSDavid du Colombier 	for(e = encrypttab;;){
12347dd7cddfSDavid du Colombier 		strcpy(cp, e->name);
12357dd7cddfSDavid du Colombier 		cp += strlen(e->name);
12367dd7cddfSDavid du Colombier 		e++;
12377dd7cddfSDavid du Colombier 		if(e->name == nil)
12387dd7cddfSDavid du Colombier 			break;
12397dd7cddfSDavid du Colombier 		*cp++ = ' ';
12407dd7cddfSDavid du Colombier 	}
12417dd7cddfSDavid du Colombier 	*cp = 0;
12427dd7cddfSDavid du Colombier 
12437dd7cddfSDavid du Colombier 	n = 1;
12447dd7cddfSDavid du Colombier 	for(h = hashtab; h->name != nil; h++)
12457dd7cddfSDavid du Colombier 		n += strlen(h->name) + 1;
12467dd7cddfSDavid du Colombier 	cp = hashalgs = smalloc(n);
12477dd7cddfSDavid du Colombier 	for(h = hashtab;;){
12487dd7cddfSDavid du Colombier 		strcpy(cp, h->name);
12497dd7cddfSDavid du Colombier 		cp += strlen(h->name);
12507dd7cddfSDavid du Colombier 		h++;
12517dd7cddfSDavid du Colombier 		if(h->name == nil)
12527dd7cddfSDavid du Colombier 			break;
12537dd7cddfSDavid du Colombier 		*cp++ = ' ';
12547dd7cddfSDavid du Colombier 	}
12557dd7cddfSDavid du Colombier 	*cp = 0;
12567dd7cddfSDavid du Colombier }
12577dd7cddfSDavid du Colombier 
12587dd7cddfSDavid du Colombier Dev ssldevtab = {
12597dd7cddfSDavid du Colombier 	'D',
12607dd7cddfSDavid du Colombier 	"ssl",
12617dd7cddfSDavid du Colombier 
12627dd7cddfSDavid du Colombier 	devreset,
12637dd7cddfSDavid du Colombier 	sslinit,
12649a747e4fSDavid du Colombier 	devshutdown,
12657dd7cddfSDavid du Colombier 	sslattach,
12667dd7cddfSDavid du Colombier 	sslwalk,
12677dd7cddfSDavid du Colombier 	sslstat,
12687dd7cddfSDavid du Colombier 	sslopen,
12697dd7cddfSDavid du Colombier 	devcreate,
12707dd7cddfSDavid du Colombier 	sslclose,
12717dd7cddfSDavid du Colombier 	sslread,
12727dd7cddfSDavid du Colombier 	sslbread,
12737dd7cddfSDavid du Colombier 	sslwrite,
12747dd7cddfSDavid du Colombier 	sslbwrite,
12757dd7cddfSDavid du Colombier 	devremove,
12767dd7cddfSDavid du Colombier 	sslwstat,
12777dd7cddfSDavid du Colombier };
12787dd7cddfSDavid du Colombier 
12797dd7cddfSDavid du Colombier static Block*
encryptb(Dstate * s,Block * b,int offset)12807dd7cddfSDavid du Colombier encryptb(Dstate *s, Block *b, int offset)
12817dd7cddfSDavid du Colombier {
12827dd7cddfSDavid du Colombier 	uchar *p, *ep, *p2, *ip, *eip;
12837dd7cddfSDavid du Colombier 	DESstate *ds;
12847dd7cddfSDavid du Colombier 
12857dd7cddfSDavid du Colombier 	switch(s->encryptalg){
12867dd7cddfSDavid du Colombier 	case DESECB:
12877dd7cddfSDavid du Colombier 		ds = s->out.state;
12887dd7cddfSDavid du Colombier 		ep = b->rp + BLEN(b);
12897dd7cddfSDavid du Colombier 		for(p = b->rp + offset; p < ep; p += 8)
12907dd7cddfSDavid du Colombier 			block_cipher(ds->expanded, p, 0);
12917dd7cddfSDavid du Colombier 		break;
12927dd7cddfSDavid du Colombier 	case DESCBC:
12937dd7cddfSDavid du Colombier 		ds = s->out.state;
12947dd7cddfSDavid du Colombier 		ep = b->rp + BLEN(b);
12957dd7cddfSDavid du Colombier 		for(p = b->rp + offset; p < ep; p += 8){
12967dd7cddfSDavid du Colombier 			p2 = p;
12977dd7cddfSDavid du Colombier 			ip = ds->ivec;
12987dd7cddfSDavid du Colombier 			for(eip = ip+8; ip < eip; )
12997dd7cddfSDavid du Colombier 				*p2++ ^= *ip++;
13007dd7cddfSDavid du Colombier 			block_cipher(ds->expanded, p, 0);
13017dd7cddfSDavid du Colombier 			memmove(ds->ivec, p, 8);
13027dd7cddfSDavid du Colombier 		}
13037dd7cddfSDavid du Colombier 		break;
13047dd7cddfSDavid du Colombier 	case RC4:
13057dd7cddfSDavid du Colombier 		rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
13067dd7cddfSDavid du Colombier 		break;
13077dd7cddfSDavid du Colombier 	}
13087dd7cddfSDavid du Colombier 	return b;
13097dd7cddfSDavid du Colombier }
13107dd7cddfSDavid du Colombier 
13117dd7cddfSDavid du Colombier static Block*
decryptb(Dstate * s,Block * bin)13127dd7cddfSDavid du Colombier decryptb(Dstate *s, Block *bin)
13137dd7cddfSDavid du Colombier {
13147dd7cddfSDavid du Colombier 	Block *b, **l;
13157dd7cddfSDavid du Colombier 	uchar *p, *ep, *tp, *ip, *eip;
13167dd7cddfSDavid du Colombier 	DESstate *ds;
13177dd7cddfSDavid du Colombier 	uchar tmp[8];
13187dd7cddfSDavid du Colombier 	int i;
13197dd7cddfSDavid du Colombier 
13207dd7cddfSDavid du Colombier 	l = &bin;
13217dd7cddfSDavid du Colombier 	for(b = bin; b; b = b->next){
13227dd7cddfSDavid du Colombier 		/* make sure we have a multiple of s->blocklen */
13237dd7cddfSDavid du Colombier 		if(s->blocklen > 1){
13247dd7cddfSDavid du Colombier 			i = BLEN(b);
13257dd7cddfSDavid du Colombier 			if(i % s->blocklen){
13267dd7cddfSDavid du Colombier 				*l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
13277dd7cddfSDavid du Colombier 				if(b == 0)
13287dd7cddfSDavid du Colombier 					error("ssl encrypted message too short");
13297dd7cddfSDavid du Colombier 			}
13307dd7cddfSDavid du Colombier 		}
13317dd7cddfSDavid du Colombier 		l = &b->next;
13327dd7cddfSDavid du Colombier 
13337dd7cddfSDavid du Colombier 		/* decrypt */
13347dd7cddfSDavid du Colombier 		switch(s->encryptalg){
13357dd7cddfSDavid du Colombier 		case DESECB:
13367dd7cddfSDavid du Colombier 			ds = s->in.state;
13377dd7cddfSDavid du Colombier 			ep = b->rp + BLEN(b);
13387dd7cddfSDavid du Colombier 			for(p = b->rp; p < ep; p += 8)
13397dd7cddfSDavid du Colombier 				block_cipher(ds->expanded, p, 1);
13407dd7cddfSDavid du Colombier 			break;
13417dd7cddfSDavid du Colombier 		case DESCBC:
13427dd7cddfSDavid du Colombier 			ds = s->in.state;
13437dd7cddfSDavid du Colombier 			ep = b->rp + BLEN(b);
13447dd7cddfSDavid du Colombier 			for(p = b->rp; p < ep;){
13457dd7cddfSDavid du Colombier 				memmove(tmp, p, 8);
13467dd7cddfSDavid du Colombier 				block_cipher(ds->expanded, p, 1);
13477dd7cddfSDavid du Colombier 				tp = tmp;
13487dd7cddfSDavid du Colombier 				ip = ds->ivec;
13497dd7cddfSDavid du Colombier 				for(eip = ip+8; ip < eip; ){
13507dd7cddfSDavid du Colombier 					*p++ ^= *ip;
13517dd7cddfSDavid du Colombier 					*ip++ = *tp++;
13527dd7cddfSDavid du Colombier 				}
13537dd7cddfSDavid du Colombier 			}
13547dd7cddfSDavid du Colombier 			break;
13557dd7cddfSDavid du Colombier 		case RC4:
13567dd7cddfSDavid du Colombier 			rc4(s->in.state, b->rp, BLEN(b));
13577dd7cddfSDavid du Colombier 			break;
13587dd7cddfSDavid du Colombier 		}
13597dd7cddfSDavid du Colombier 	}
13607dd7cddfSDavid du Colombier 	return bin;
13617dd7cddfSDavid du Colombier }
13627dd7cddfSDavid du Colombier 
13637dd7cddfSDavid du Colombier static Block*
digestb(Dstate * s,Block * b,int offset)13647dd7cddfSDavid du Colombier digestb(Dstate *s, Block *b, int offset)
13657dd7cddfSDavid du Colombier {
13667dd7cddfSDavid du Colombier 	uchar *p;
13677dd7cddfSDavid du Colombier 	DigestState ss;
13687dd7cddfSDavid du Colombier 	uchar msgid[4];
13697dd7cddfSDavid du Colombier 	ulong n, h;
13707dd7cddfSDavid du Colombier 	OneWay *w;
13717dd7cddfSDavid du Colombier 
13727dd7cddfSDavid du Colombier 	w = &s->out;
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier 	memset(&ss, 0, sizeof(ss));
13757dd7cddfSDavid du Colombier 	h = s->diglen + offset;
13767dd7cddfSDavid du Colombier 	n = BLEN(b) - h;
13777dd7cddfSDavid du Colombier 
13787dd7cddfSDavid du Colombier 	/* hash secret + message */
13797dd7cddfSDavid du Colombier 	(*s->hf)(w->secret, w->slen, 0, &ss);
13807dd7cddfSDavid du Colombier 	(*s->hf)(b->rp + h, n, 0, &ss);
13817dd7cddfSDavid du Colombier 
13827dd7cddfSDavid du Colombier 	/* hash message id */
13837dd7cddfSDavid du Colombier 	p = msgid;
13847dd7cddfSDavid du Colombier 	n = w->mid;
13857dd7cddfSDavid du Colombier 	*p++ = n>>24;
13867dd7cddfSDavid du Colombier 	*p++ = n>>16;
13877dd7cddfSDavid du Colombier 	*p++ = n>>8;
13887dd7cddfSDavid du Colombier 	*p = n;
13897dd7cddfSDavid du Colombier 	(*s->hf)(msgid, 4, b->rp + offset, &ss);
13907dd7cddfSDavid du Colombier 
13917dd7cddfSDavid du Colombier 	return b;
13927dd7cddfSDavid du Colombier }
13937dd7cddfSDavid du Colombier 
13947dd7cddfSDavid du Colombier static void
checkdigestb(Dstate * s,Block * bin)13957dd7cddfSDavid du Colombier checkdigestb(Dstate *s, Block *bin)
13967dd7cddfSDavid du Colombier {
13977dd7cddfSDavid du Colombier 	uchar *p;
13987dd7cddfSDavid du Colombier 	DigestState ss;
13997dd7cddfSDavid du Colombier 	uchar msgid[4];
14007dd7cddfSDavid du Colombier 	int n, h;
14017dd7cddfSDavid du Colombier 	OneWay *w;
14027dd7cddfSDavid du Colombier 	uchar digest[128];
14037dd7cddfSDavid du Colombier 	Block *b;
14047dd7cddfSDavid du Colombier 
14057dd7cddfSDavid du Colombier 	w = &s->in;
14067dd7cddfSDavid du Colombier 
14077dd7cddfSDavid du Colombier 	memset(&ss, 0, sizeof(ss));
14087dd7cddfSDavid du Colombier 
14097dd7cddfSDavid du Colombier 	/* hash secret */
14107dd7cddfSDavid du Colombier 	(*s->hf)(w->secret, w->slen, 0, &ss);
14117dd7cddfSDavid du Colombier 
14127dd7cddfSDavid du Colombier 	/* hash message */
14137dd7cddfSDavid du Colombier 	h = s->diglen;
14147dd7cddfSDavid du Colombier 	for(b = bin; b; b = b->next){
14157dd7cddfSDavid du Colombier 		n = BLEN(b) - h;
14167dd7cddfSDavid du Colombier 		if(n < 0)
14177dd7cddfSDavid du Colombier 			panic("checkdigestb");
14187dd7cddfSDavid du Colombier 		(*s->hf)(b->rp + h, n, 0, &ss);
14197dd7cddfSDavid du Colombier 		h = 0;
14207dd7cddfSDavid du Colombier 	}
14217dd7cddfSDavid du Colombier 
14227dd7cddfSDavid du Colombier 	/* hash message id */
14237dd7cddfSDavid du Colombier 	p = msgid;
14247dd7cddfSDavid du Colombier 	n = w->mid;
14257dd7cddfSDavid du Colombier 	*p++ = n>>24;
14267dd7cddfSDavid du Colombier 	*p++ = n>>16;
14277dd7cddfSDavid du Colombier 	*p++ = n>>8;
14287dd7cddfSDavid du Colombier 	*p = n;
14297dd7cddfSDavid du Colombier 	(*s->hf)(msgid, 4, digest, &ss);
14307dd7cddfSDavid du Colombier 
14317dd7cddfSDavid du Colombier 	if(memcmp(digest, bin->rp, s->diglen) != 0)
14327dd7cddfSDavid du Colombier 		error("bad digest");
14337dd7cddfSDavid du Colombier }
14347dd7cddfSDavid du Colombier 
14357dd7cddfSDavid du Colombier /* get channel associated with an fd */
14367dd7cddfSDavid du Colombier static Chan*
buftochan(char * p)14377dd7cddfSDavid du Colombier buftochan(char *p)
14387dd7cddfSDavid du Colombier {
14397dd7cddfSDavid du Colombier 	Chan *c;
14407dd7cddfSDavid du Colombier 	int fd;
14417dd7cddfSDavid du Colombier 
14427dd7cddfSDavid du Colombier 	if(p == 0)
14437dd7cddfSDavid du Colombier 		error(Ebadarg);
14447dd7cddfSDavid du Colombier 	fd = strtoul(p, 0, 0);
14457dd7cddfSDavid du Colombier 	if(fd < 0)
14467dd7cddfSDavid du Colombier 		error(Ebadarg);
14477dd7cddfSDavid du Colombier 	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
14489a747e4fSDavid du Colombier 	if(devtab[c->type] == &ssldevtab){
14499a747e4fSDavid du Colombier 		cclose(c);
14509a747e4fSDavid du Colombier 		error("cannot ssl encrypt devssl files");
14519a747e4fSDavid du Colombier 	}
14527dd7cddfSDavid du Colombier 	return c;
14537dd7cddfSDavid du Colombier }
14547dd7cddfSDavid du Colombier 
14557dd7cddfSDavid du Colombier /* hand up a digest connection */
14567dd7cddfSDavid du Colombier static void
sslhangup(Dstate * s)14577dd7cddfSDavid du Colombier sslhangup(Dstate *s)
14587dd7cddfSDavid du Colombier {
14597dd7cddfSDavid du Colombier 	Block *b;
14607dd7cddfSDavid du Colombier 
14617dd7cddfSDavid du Colombier 	qlock(&s->in.q);
14627dd7cddfSDavid du Colombier 	for(b = s->processed; b; b = s->processed){
14637dd7cddfSDavid du Colombier 		s->processed = b->next;
14647dd7cddfSDavid du Colombier 		freeb(b);
14657dd7cddfSDavid du Colombier 	}
14667dd7cddfSDavid du Colombier 	if(s->unprocessed){
14677dd7cddfSDavid du Colombier 		freeb(s->unprocessed);
14687dd7cddfSDavid du Colombier 		s->unprocessed = 0;
14697dd7cddfSDavid du Colombier 	}
14707dd7cddfSDavid du Colombier 	s->state = Sincomplete;
14717dd7cddfSDavid du Colombier 	qunlock(&s->in.q);
14727dd7cddfSDavid du Colombier }
14737dd7cddfSDavid du Colombier 
14747dd7cddfSDavid du Colombier static Dstate*
dsclone(Chan * ch)14757dd7cddfSDavid du Colombier dsclone(Chan *ch)
14767dd7cddfSDavid du Colombier {
14779a747e4fSDavid du Colombier 	int i;
14789a747e4fSDavid du Colombier 	Dstate *ret;
14797dd7cddfSDavid du Colombier 
14807dd7cddfSDavid du Colombier 	if(waserror()) {
14817dd7cddfSDavid du Colombier 		unlock(&dslock);
14827dd7cddfSDavid du Colombier 		nexterror();
14837dd7cddfSDavid du Colombier 	}
14847dd7cddfSDavid du Colombier 	lock(&dslock);
14859a747e4fSDavid du Colombier 	ret = nil;
14869a747e4fSDavid du Colombier 	for(i=0; i<Maxdstate; i++){
14879a747e4fSDavid du Colombier 		if(dstate[i] == nil){
14889a747e4fSDavid du Colombier 			dsnew(ch, &dstate[i]);
14899a747e4fSDavid du Colombier 			ret = dstate[i];
14907dd7cddfSDavid du Colombier 			break;
14917dd7cddfSDavid du Colombier 		}
14927dd7cddfSDavid du Colombier 	}
14937dd7cddfSDavid du Colombier 	unlock(&dslock);
14947dd7cddfSDavid du Colombier 	poperror();
14959a747e4fSDavid du Colombier 	return ret;
14967dd7cddfSDavid du Colombier }
14977dd7cddfSDavid du Colombier 
14987dd7cddfSDavid du Colombier static void
dsnew(Chan * ch,Dstate ** pp)14997dd7cddfSDavid du Colombier dsnew(Chan *ch, Dstate **pp)
15007dd7cddfSDavid du Colombier {
15017dd7cddfSDavid du Colombier 	Dstate *s;
15027dd7cddfSDavid du Colombier 	int t;
15037dd7cddfSDavid du Colombier 
15047dd7cddfSDavid du Colombier 	*pp = s = malloc(sizeof(*s));
15057dd7cddfSDavid du Colombier 	if(!s)
15067dd7cddfSDavid du Colombier 		error(Enomem);
15077dd7cddfSDavid du Colombier 	if(pp - dstate >= dshiwat)
15087dd7cddfSDavid du Colombier 		dshiwat++;
15097dd7cddfSDavid du Colombier 	memset(s, 0, sizeof(*s));
15107dd7cddfSDavid du Colombier 	s->state = Sincomplete;
15117dd7cddfSDavid du Colombier 	s->ref = 1;
15129a747e4fSDavid du Colombier 	kstrdup(&s->user, up->user);
15137dd7cddfSDavid du Colombier 	s->perm = 0660;
15147dd7cddfSDavid du Colombier 	t = TYPE(ch->qid);
15157dd7cddfSDavid du Colombier 	if(t == Qclonus)
15167dd7cddfSDavid du Colombier 		t = Qctl;
15177dd7cddfSDavid du Colombier 	ch->qid.path = QID(pp - dstate, t);
15187dd7cddfSDavid du Colombier 	ch->qid.vers = 0;
15197dd7cddfSDavid du Colombier }
1520