xref: /plan9-contrib/sys/src/9/port/devtls.c (revision e873706d149a4ba37516c5c133c6519e52a85b83)
19a747e4fSDavid du Colombier /*
22a47d117SDavid du Colombier  *  devtls - record layer for transport layer security 1.0, 1.1, 1.2 and secure sockets layer 3.0
39a747e4fSDavid du Colombier  */
49a747e4fSDavid du Colombier #include	"u.h"
59a747e4fSDavid du Colombier #include	"../port/lib.h"
69a747e4fSDavid du Colombier #include	"mem.h"
79a747e4fSDavid du Colombier #include	"dat.h"
89a747e4fSDavid du Colombier #include	"fns.h"
99a747e4fSDavid du Colombier #include	"../port/error.h"
109a747e4fSDavid du Colombier 
119a747e4fSDavid du Colombier #include	<libsec.h>
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier typedef struct OneWay	OneWay;
149a747e4fSDavid du Colombier typedef struct Secret		Secret;
159a747e4fSDavid du Colombier typedef struct TlsRec	TlsRec;
169a747e4fSDavid du Colombier typedef struct TlsErrs	TlsErrs;
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier enum {
199a747e4fSDavid du Colombier 	Statlen=	1024,		/* max. length of status or stats message */
209a747e4fSDavid du Colombier 	/* buffer limits */
219a747e4fSDavid du Colombier 	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */
229a747e4fSDavid du Colombier 	MaxCipherRecLen	= MaxRecLen + 2048,
239a747e4fSDavid du Colombier 	RecHdrLen		= 5,
242a47d117SDavid du Colombier 	MaxMacLen		= SHA2_256dlen,
259a747e4fSDavid du Colombier 
269a747e4fSDavid du Colombier 	/* protocol versions we can accept */
279a747e4fSDavid du Colombier 	SSL3Version		= 0x0300,
282a47d117SDavid du Colombier 	TLS10Version		= 0x0301,
292a47d117SDavid du Colombier 	TLS11Version		= 0x0302,
302a47d117SDavid du Colombier 	TLS12Version		= 0x0303,
319a747e4fSDavid du Colombier 	MinProtoVersion	= 0x0300,	/* limits on version we accept */
329a747e4fSDavid du Colombier 	MaxProtoVersion	= 0x03ff,
339a747e4fSDavid du Colombier 
349a747e4fSDavid du Colombier 	/* connection states */
359a747e4fSDavid du Colombier 	SHandshake	= 1 << 0,	/* doing handshake */
369a747e4fSDavid du Colombier 	SOpen		= 1 << 1,	/* application data can be sent */
379a747e4fSDavid du Colombier 	SRClose		= 1 << 2,	/* remote side has closed down */
389a747e4fSDavid du Colombier 	SLClose		= 1 << 3,	/* sent a close notify alert */
399a747e4fSDavid du Colombier 	SAlert		= 1 << 5,	/* sending or sent a fatal alert */
409a747e4fSDavid du Colombier 	SError		= 1 << 6,	/* some sort of error has occured */
419a747e4fSDavid du Colombier 	SClosed		= 1 << 7,	/* it is all over */
429a747e4fSDavid du Colombier 
439a747e4fSDavid du Colombier 	/* record types */
449a747e4fSDavid du Colombier 	RChangeCipherSpec = 20,
459a747e4fSDavid du Colombier 	RAlert,
469a747e4fSDavid du Colombier 	RHandshake,
479a747e4fSDavid du Colombier 	RApplication,
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier 	SSL2ClientHello = 1,
509a747e4fSDavid du Colombier 	HSSL2ClientHello = 9,  /* local convention;  see tlshand.c */
519a747e4fSDavid du Colombier 
529a747e4fSDavid du Colombier 	/* alerts */
539a747e4fSDavid du Colombier 	ECloseNotify 			= 0,
549a747e4fSDavid du Colombier 	EUnexpectedMessage 	= 10,
559a747e4fSDavid du Colombier 	EBadRecordMac 		= 20,
569a747e4fSDavid du Colombier 	EDecryptionFailed 		= 21,
579a747e4fSDavid du Colombier 	ERecordOverflow 		= 22,
589a747e4fSDavid du Colombier 	EDecompressionFailure 	= 30,
599a747e4fSDavid du Colombier 	EHandshakeFailure 		= 40,
609a747e4fSDavid du Colombier 	ENoCertificate 			= 41,
619a747e4fSDavid du Colombier 	EBadCertificate 		= 42,
629a747e4fSDavid du Colombier 	EUnsupportedCertificate 	= 43,
639a747e4fSDavid du Colombier 	ECertificateRevoked 		= 44,
649a747e4fSDavid du Colombier 	ECertificateExpired 		= 45,
659a747e4fSDavid du Colombier 	ECertificateUnknown 	= 46,
669a747e4fSDavid du Colombier 	EIllegalParameter 		= 47,
679a747e4fSDavid du Colombier 	EUnknownCa 			= 48,
689a747e4fSDavid du Colombier 	EAccessDenied 		= 49,
699a747e4fSDavid du Colombier 	EDecodeError 			= 50,
709a747e4fSDavid du Colombier 	EDecryptError 			= 51,
719a747e4fSDavid du Colombier 	EExportRestriction 		= 60,
729a747e4fSDavid du Colombier 	EProtocolVersion 		= 70,
739a747e4fSDavid du Colombier 	EInsufficientSecurity 	= 71,
749a747e4fSDavid du Colombier 	EInternalError 			= 80,
759a747e4fSDavid du Colombier 	EUserCanceled 			= 90,
769a747e4fSDavid du Colombier 	ENoRenegotiation 		= 100,
772a47d117SDavid du Colombier 	EUnrecognizedName		= 112,
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier 	EMAX = 256
809a747e4fSDavid du Colombier };
819a747e4fSDavid du Colombier 
829a747e4fSDavid du Colombier struct Secret
839a747e4fSDavid du Colombier {
849a747e4fSDavid du Colombier 	char		*encalg;	/* name of encryption alg */
859a747e4fSDavid du Colombier 	char		*hashalg;	/* name of hash alg */
869a747e4fSDavid du Colombier 	int		(*enc)(Secret*, uchar*, int);
879a747e4fSDavid du Colombier 	int		(*dec)(Secret*, uchar*, int);
889a747e4fSDavid du Colombier 	int		(*unpad)(uchar*, int, int);
899a747e4fSDavid du Colombier 	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
909a747e4fSDavid du Colombier 	int		block;		/* encryption block len, 0 if none */
919a747e4fSDavid du Colombier 	int		maclen;
929a747e4fSDavid du Colombier 	void		*enckey;
939a747e4fSDavid du Colombier 	uchar	mackey[MaxMacLen];
949a747e4fSDavid du Colombier };
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier struct OneWay
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier 	QLock		io;		/* locks io access */
999a747e4fSDavid du Colombier 	QLock		seclock;	/* locks secret paramaters */
1009a747e4fSDavid du Colombier 	ulong		seq;
1019a747e4fSDavid du Colombier 	Secret		*sec;		/* cipher in use */
1029a747e4fSDavid du Colombier 	Secret		*new;		/* cipher waiting for enable */
1039a747e4fSDavid du Colombier };
1049a747e4fSDavid du Colombier 
1059a747e4fSDavid du Colombier struct TlsRec
1069a747e4fSDavid du Colombier {
1079a747e4fSDavid du Colombier 	Chan	*c;				/* io channel */
1089a747e4fSDavid du Colombier 	int		ref;				/* serialized by tdlock for atomic destroy */
1099a747e4fSDavid du Colombier 	int		version;			/* version of the protocol we are speaking */
1109a747e4fSDavid du Colombier 	char		verset;			/* version has been set */
1119a747e4fSDavid du Colombier 	char		opened;			/* opened command every issued? */
1129a747e4fSDavid du Colombier 	char		err[ERRMAX];		/* error message to return to handshake requests */
1139a747e4fSDavid du Colombier 	vlong	handin;			/* bytes communicated by the record layer */
1149a747e4fSDavid du Colombier 	vlong	handout;
1159a747e4fSDavid du Colombier 	vlong	datain;
1169a747e4fSDavid du Colombier 	vlong	dataout;
1179a747e4fSDavid du Colombier 
1189a747e4fSDavid du Colombier 	Lock		statelk;
1199a747e4fSDavid du Colombier 	int		state;
1203751babcSDavid du Colombier 	int		debug;
1219a747e4fSDavid du Colombier 
1229a747e4fSDavid du Colombier 	/* record layer mac functions for different protocol versions */
1239a747e4fSDavid du Colombier 	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
1249a747e4fSDavid du Colombier 
1259a747e4fSDavid du Colombier 	/* input side -- protected by in.io */
1269a747e4fSDavid du Colombier 	OneWay		in;
1279a747e4fSDavid du Colombier 	Block		*processed;	/* next bunch of application data */
1289a747e4fSDavid du Colombier 	Block		*unprocessed;	/* data read from c but not parsed into records */
1299a747e4fSDavid du Colombier 
1309a747e4fSDavid du Colombier 	/* handshake queue */
1319a747e4fSDavid du Colombier 	Lock		hqlock;			/* protects hqref, alloc & free of handq, hprocessed */
1329a747e4fSDavid du Colombier 	int		hqref;
1339a747e4fSDavid du Colombier 	Queue		*handq;		/* queue of handshake messages */
1349a747e4fSDavid du Colombier 	Block		*hprocessed;	/* remainder of last block read from handq */
1359a747e4fSDavid du Colombier 	QLock		hqread;		/* protects reads for hprocessed, handq */
1369a747e4fSDavid du Colombier 
1379a747e4fSDavid du Colombier 	/* output side */
1389a747e4fSDavid du Colombier 	OneWay		out;
1399a747e4fSDavid du Colombier 
1409a747e4fSDavid du Colombier 	/* protections */
1419a747e4fSDavid du Colombier 	char		*user;
1429a747e4fSDavid du Colombier 	int		perm;
1439a747e4fSDavid du Colombier };
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier struct TlsErrs{
1469a747e4fSDavid du Colombier 	int	err;
1479a747e4fSDavid du Colombier 	int	sslerr;
1489a747e4fSDavid du Colombier 	int	tlserr;
1499a747e4fSDavid du Colombier 	int	fatal;
1509a747e4fSDavid du Colombier 	char	*msg;
1519a747e4fSDavid du Colombier };
1529a747e4fSDavid du Colombier 
1539a747e4fSDavid du Colombier static TlsErrs tlserrs[] = {
1549a747e4fSDavid du Colombier 	{ECloseNotify,			ECloseNotify,			ECloseNotify,			0, 	"close notify"},
1559a747e4fSDavid du Colombier 	{EUnexpectedMessage,	EUnexpectedMessage,	EUnexpectedMessage, 	1, "unexpected message"},
1569a747e4fSDavid du Colombier 	{EBadRecordMac,		EBadRecordMac,		EBadRecordMac, 		1, "bad record mac"},
1579a747e4fSDavid du Colombier 	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,		1, "decryption failed"},
1589a747e4fSDavid du Colombier 	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,		1, "record too long"},
1599a747e4fSDavid du Colombier 	{EDecompressionFailure,	EDecompressionFailure,	EDecompressionFailure,	1, "decompression failed"},
160f0ed0fb6SDavid du Colombier 	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,		1, "could not negotiate acceptable security parameters"},
1619a747e4fSDavid du Colombier 	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,	1, "no appropriate certificate available"},
1629a747e4fSDavid du Colombier 	{EBadCertificate,		EBadCertificate,		EBadCertificate,		1, "corrupted or invalid certificate"},
1639a747e4fSDavid du Colombier 	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,	1, "unsupported certificate type"},
1649a747e4fSDavid du Colombier 	{ECertificateRevoked,	ECertificateRevoked,		ECertificateRevoked,		1, "revoked certificate"},
1659a747e4fSDavid du Colombier 	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,		1, "expired certificate"},
1669a747e4fSDavid du Colombier 	{ECertificateUnknown,	ECertificateUnknown,	ECertificateUnknown,	1, "unacceptable certificate"},
1679a747e4fSDavid du Colombier 	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,		1, "illegal parameter"},
1689a747e4fSDavid du Colombier 	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,			1, "unknown certificate authority"},
1699a747e4fSDavid du Colombier 	{EAccessDenied,		EHandshakeFailure,		EAccessDenied,		1, "access denied"},
1709a747e4fSDavid du Colombier 	{EDecodeError,			EIllegalParameter,		EDecodeError,			1, "error decoding message"},
1719a747e4fSDavid du Colombier 	{EDecryptError,			EIllegalParameter,		EDecryptError,			1, "error decrypting message"},
1729a747e4fSDavid du Colombier 	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,		1, "export restriction violated"},
1739a747e4fSDavid du Colombier 	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,		1, "protocol version not supported"},
1749a747e4fSDavid du Colombier 	{EInsufficientSecurity,	EHandshakeFailure,		EInsufficientSecurity,	1, "stronger security routines required"},
1759a747e4fSDavid du Colombier 	{EInternalError,			EHandshakeFailure,		EInternalError,			1, "internal error"},
1769a747e4fSDavid du Colombier 	{EUserCanceled,		ECloseNotify,			EUserCanceled,			0, "handshake canceled by user"},
1779a747e4fSDavid du Colombier 	{ENoRenegotiation,		EUnexpectedMessage,	ENoRenegotiation,		0, "no renegotiation"},
1789a747e4fSDavid du Colombier };
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier enum
1819a747e4fSDavid du Colombier {
1829a747e4fSDavid du Colombier 	/* max. open tls connections */
183f20f6594SDavid du Colombier 	MaxTlsDevs	= 16*1024
1849a747e4fSDavid du Colombier };
1859a747e4fSDavid du Colombier 
1869a747e4fSDavid du Colombier static	Lock	tdlock;
1879a747e4fSDavid du Colombier static	int	tdhiwat;
1889a747e4fSDavid du Colombier static	int	maxtlsdevs = 128;
1899a747e4fSDavid du Colombier static	TlsRec	**tlsdevs;
1909a747e4fSDavid du Colombier static	char	**trnames;
1919a747e4fSDavid du Colombier static	char	*encalgs;
1929a747e4fSDavid du Colombier static	char	*hashalgs;
1939a747e4fSDavid du Colombier 
1949a747e4fSDavid du Colombier enum{
1959a747e4fSDavid du Colombier 	Qtopdir		= 1,	/* top level directory */
1969a747e4fSDavid du Colombier 	Qprotodir,
1979a747e4fSDavid du Colombier 	Qclonus,
1989a747e4fSDavid du Colombier 	Qencalgs,
1999a747e4fSDavid du Colombier 	Qhashalgs,
2009a747e4fSDavid du Colombier 	Qconvdir,		/* directory for a conversation */
2019a747e4fSDavid du Colombier 	Qdata,
2029a747e4fSDavid du Colombier 	Qctl,
2039a747e4fSDavid du Colombier 	Qhand,
2049a747e4fSDavid du Colombier 	Qstatus,
2059a747e4fSDavid du Colombier 	Qstats,
2069a747e4fSDavid du Colombier };
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier #define TYPE(x) 	((x).path & 0xf)
2099a747e4fSDavid du Colombier #define CONV(x) 	(((x).path >> 5)&(MaxTlsDevs-1))
2109a747e4fSDavid du Colombier #define QID(c, y) 	(((c)<<5) | (y))
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier static void	checkstate(TlsRec *, int, int);
2139a747e4fSDavid du Colombier static void	ensure(TlsRec*, Block**, int);
2149a747e4fSDavid du Colombier static void	consume(Block**, uchar*, int);
2159a747e4fSDavid du Colombier static Chan*	buftochan(char*);
2169a747e4fSDavid du Colombier static void	tlshangup(TlsRec*);
2179a747e4fSDavid du Colombier static void	tlsError(TlsRec*, char *);
2189a747e4fSDavid du Colombier static void	alertHand(TlsRec*, char *);
2199a747e4fSDavid du Colombier static TlsRec	*newtls(Chan *c);
2209a747e4fSDavid du Colombier static TlsRec	*mktlsrec(void);
2219a747e4fSDavid du Colombier static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2229a747e4fSDavid du Colombier static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2239a747e4fSDavid du Colombier static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2249a747e4fSDavid du Colombier static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2259a747e4fSDavid du Colombier static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2269a747e4fSDavid du Colombier static void	put64(uchar *p, vlong x);
2279a747e4fSDavid du Colombier static void	put32(uchar *p, u32int);
2289a747e4fSDavid du Colombier static void	put24(uchar *p, int);
2299a747e4fSDavid du Colombier static void	put16(uchar *p, int);
2309a747e4fSDavid du Colombier static u32int	get32(uchar *p);
2319a747e4fSDavid du Colombier static int	get16(uchar *p);
2329a747e4fSDavid du Colombier static void	tlsSetState(TlsRec *tr, int new, int old);
2339a747e4fSDavid du Colombier static void	rcvAlert(TlsRec *tr, int err);
2349a747e4fSDavid du Colombier static void	sendAlert(TlsRec *tr, int err);
2359a747e4fSDavid du Colombier static void	rcvError(TlsRec *tr, int err, char *msg, ...);
2369a747e4fSDavid du Colombier static int	rc4enc(Secret *sec, uchar *buf, int n);
2379a747e4fSDavid du Colombier static int	des3enc(Secret *sec, uchar *buf, int n);
2389a747e4fSDavid du Colombier static int	des3dec(Secret *sec, uchar *buf, int n);
239ad6ca847SDavid du Colombier static int	aesenc(Secret *sec, uchar *buf, int n);
240ad6ca847SDavid du Colombier static int	aesdec(Secret *sec, uchar *buf, int n);
2419a747e4fSDavid du Colombier static int	noenc(Secret *sec, uchar *buf, int n);
2429a747e4fSDavid du Colombier static int	sslunpad(uchar *buf, int n, int block);
2439a747e4fSDavid du Colombier static int	tlsunpad(uchar *buf, int n, int block);
2449a747e4fSDavid du Colombier static void	freeSec(Secret *sec);
2459a747e4fSDavid du Colombier static char	*tlsstate(int s);
2463751babcSDavid du Colombier static void	pdump(int, void*, char*);
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier #pragma	varargck	argpos	rcvError	3
2499a747e4fSDavid du Colombier 
2509a747e4fSDavid du Colombier static char *tlsnames[] = {
2519a747e4fSDavid du Colombier [Qclonus]		"clone",
2529a747e4fSDavid du Colombier [Qencalgs]	"encalgs",
2539a747e4fSDavid du Colombier [Qhashalgs]	"hashalgs",
2549a747e4fSDavid du Colombier [Qdata]		"data",
2559a747e4fSDavid du Colombier [Qctl]		"ctl",
2569a747e4fSDavid du Colombier [Qhand]		"hand",
2579a747e4fSDavid du Colombier [Qstatus]		"status",
2589a747e4fSDavid du Colombier [Qstats]		"stats",
2599a747e4fSDavid du Colombier };
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier static int
tlsgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)2649a747e4fSDavid du Colombier tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp)
2659a747e4fSDavid du Colombier {
2669a747e4fSDavid du Colombier 	Qid q;
2679a747e4fSDavid du Colombier 	TlsRec *tr;
2689a747e4fSDavid du Colombier 	char *name, *nm;
2699a747e4fSDavid du Colombier 	int perm, t;
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier 	q.vers = 0;
2729a747e4fSDavid du Colombier 	q.type = QTFILE;
2739a747e4fSDavid du Colombier 
2749a747e4fSDavid du Colombier 	t = TYPE(c->qid);
2759a747e4fSDavid du Colombier 	switch(t) {
2769a747e4fSDavid du Colombier 	case Qtopdir:
2779a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
2789a747e4fSDavid du Colombier 			q.path = QID(0, Qtopdir);
2799a747e4fSDavid du Colombier 			q.type = QTDIR;
2809a747e4fSDavid du Colombier 			devdir(c, q, "#a", 0, eve, 0555, dp);
2819a747e4fSDavid du Colombier 			return 1;
2829a747e4fSDavid du Colombier 		}
2839a747e4fSDavid du Colombier 		if(s > 0)
2849a747e4fSDavid du Colombier 			return -1;
2859a747e4fSDavid du Colombier 		q.path = QID(0, Qprotodir);
2869a747e4fSDavid du Colombier 		q.type = QTDIR;
2879a747e4fSDavid du Colombier 		devdir(c, q, "tls", 0, eve, 0555, dp);
2889a747e4fSDavid du Colombier 		return 1;
2899a747e4fSDavid du Colombier 	case Qprotodir:
2909a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
2919a747e4fSDavid du Colombier 			q.path = QID(0, Qtopdir);
2929a747e4fSDavid du Colombier 			q.type = QTDIR;
2939a747e4fSDavid du Colombier 			devdir(c, q, ".", 0, eve, 0555, dp);
2949a747e4fSDavid du Colombier 			return 1;
2959a747e4fSDavid du Colombier 		}
2969a747e4fSDavid du Colombier 		if(s < 3){
2979a747e4fSDavid du Colombier 			switch(s) {
2989a747e4fSDavid du Colombier 			default:
2999a747e4fSDavid du Colombier 				return -1;
3009a747e4fSDavid du Colombier 			case 0:
3019a747e4fSDavid du Colombier 				q.path = QID(0, Qclonus);
3029a747e4fSDavid du Colombier 				break;
3039a747e4fSDavid du Colombier 			case 1:
3049a747e4fSDavid du Colombier 				q.path = QID(0, Qencalgs);
3059a747e4fSDavid du Colombier 				break;
3069a747e4fSDavid du Colombier 			case 2:
3079a747e4fSDavid du Colombier 				q.path = QID(0, Qhashalgs);
3089a747e4fSDavid du Colombier 				break;
3099a747e4fSDavid du Colombier 			}
3109a747e4fSDavid du Colombier 			perm = 0444;
3119a747e4fSDavid du Colombier 			if(TYPE(q) == Qclonus)
3129a747e4fSDavid du Colombier 				perm = 0555;
3139a747e4fSDavid du Colombier 			devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
3149a747e4fSDavid du Colombier 			return 1;
3159a747e4fSDavid du Colombier 		}
3169a747e4fSDavid du Colombier 		s -= 3;
3179a747e4fSDavid du Colombier 		if(s >= tdhiwat)
3189a747e4fSDavid du Colombier 			return -1;
3199a747e4fSDavid du Colombier 		q.path = QID(s, Qconvdir);
3209a747e4fSDavid du Colombier 		q.type = QTDIR;
3219a747e4fSDavid du Colombier 		lock(&tdlock);
3229a747e4fSDavid du Colombier 		tr = tlsdevs[s];
3239a747e4fSDavid du Colombier 		if(tr != nil)
3249a747e4fSDavid du Colombier 			nm = tr->user;
3259a747e4fSDavid du Colombier 		else
3269a747e4fSDavid du Colombier 			nm = eve;
3279a747e4fSDavid du Colombier 		if((name = trnames[s]) == nil){
3289a747e4fSDavid du Colombier 			name = trnames[s] = smalloc(16);
3294e3613abSDavid du Colombier 			snprint(name, 16, "%d", s);
3309a747e4fSDavid du Colombier 		}
3319a747e4fSDavid du Colombier 		devdir(c, q, name, 0, nm, 0555, dp);
3329a747e4fSDavid du Colombier 		unlock(&tdlock);
3339a747e4fSDavid du Colombier 		return 1;
3349a747e4fSDavid du Colombier 	case Qconvdir:
3359a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
3369a747e4fSDavid du Colombier 			q.path = QID(0, Qprotodir);
3379a747e4fSDavid du Colombier 			q.type = QTDIR;
3389a747e4fSDavid du Colombier 			devdir(c, q, "tls", 0, eve, 0555, dp);
3399a747e4fSDavid du Colombier 			return 1;
3409a747e4fSDavid du Colombier 		}
3419a747e4fSDavid du Colombier 		if(s < 0 || s >= nelem(convdir))
3429a747e4fSDavid du Colombier 			return -1;
3439a747e4fSDavid du Colombier 		lock(&tdlock);
3449a747e4fSDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
3459a747e4fSDavid du Colombier 		if(tr != nil){
3469a747e4fSDavid du Colombier 			nm = tr->user;
3479a747e4fSDavid du Colombier 			perm = tr->perm;
3489a747e4fSDavid du Colombier 		}else{
3499a747e4fSDavid du Colombier 			perm = 0;
3509a747e4fSDavid du Colombier 			nm = eve;
3519a747e4fSDavid du Colombier 		}
3529a747e4fSDavid du Colombier 		t = convdir[s];
3539a747e4fSDavid du Colombier 		if(t == Qstatus || t == Qstats)
3549a747e4fSDavid du Colombier 			perm &= 0444;
3559a747e4fSDavid du Colombier 		q.path = QID(CONV(c->qid), t);
3569a747e4fSDavid du Colombier 		devdir(c, q, tlsnames[t], 0, nm, perm, dp);
3579a747e4fSDavid du Colombier 		unlock(&tdlock);
3589a747e4fSDavid du Colombier 		return 1;
3599a747e4fSDavid du Colombier 	case Qclonus:
3609a747e4fSDavid du Colombier 	case Qencalgs:
3619a747e4fSDavid du Colombier 	case Qhashalgs:
3629a747e4fSDavid du Colombier 		perm = 0444;
3639a747e4fSDavid du Colombier 		if(t == Qclonus)
3649a747e4fSDavid du Colombier 			perm = 0555;
3659a747e4fSDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
3669a747e4fSDavid du Colombier 		return 1;
3679a747e4fSDavid du Colombier 	default:
3689a747e4fSDavid du Colombier 		lock(&tdlock);
3699a747e4fSDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
3709a747e4fSDavid du Colombier 		if(tr != nil){
3719a747e4fSDavid du Colombier 			nm = tr->user;
3729a747e4fSDavid du Colombier 			perm = tr->perm;
3739a747e4fSDavid du Colombier 		}else{
3749a747e4fSDavid du Colombier 			perm = 0;
3759a747e4fSDavid du Colombier 			nm = eve;
3769a747e4fSDavid du Colombier 		}
3779a747e4fSDavid du Colombier 		if(t == Qstatus || t == Qstats)
3789a747e4fSDavid du Colombier 			perm &= 0444;
3799a747e4fSDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
3809a747e4fSDavid du Colombier 		unlock(&tdlock);
3819a747e4fSDavid du Colombier 		return 1;
3829a747e4fSDavid du Colombier 	}
3839a747e4fSDavid du Colombier }
3849a747e4fSDavid du Colombier 
3859a747e4fSDavid du Colombier static Chan*
tlsattach(char * spec)3869a747e4fSDavid du Colombier tlsattach(char *spec)
3879a747e4fSDavid du Colombier {
3889a747e4fSDavid du Colombier 	Chan *c;
3899a747e4fSDavid du Colombier 
3909a747e4fSDavid du Colombier 	c = devattach('a', spec);
3919a747e4fSDavid du Colombier 	c->qid.path = QID(0, Qtopdir);
3929a747e4fSDavid du Colombier 	c->qid.type = QTDIR;
3939a747e4fSDavid du Colombier 	c->qid.vers = 0;
3949a747e4fSDavid du Colombier 	return c;
3959a747e4fSDavid du Colombier }
3969a747e4fSDavid du Colombier 
3979a747e4fSDavid du Colombier static Walkqid*
tlswalk(Chan * c,Chan * nc,char ** name,int nname)3989a747e4fSDavid du Colombier tlswalk(Chan *c, Chan *nc, char **name, int nname)
3999a747e4fSDavid du Colombier {
4009a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, tlsgen);
4019a747e4fSDavid du Colombier }
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier static int
tlsstat(Chan * c,uchar * db,int n)4049a747e4fSDavid du Colombier tlsstat(Chan *c, uchar *db, int n)
4059a747e4fSDavid du Colombier {
4069a747e4fSDavid du Colombier 	return devstat(c, db, n, nil, 0, tlsgen);
4079a747e4fSDavid du Colombier }
4089a747e4fSDavid du Colombier 
4099a747e4fSDavid du Colombier static Chan*
tlsopen(Chan * c,int omode)4109a747e4fSDavid du Colombier tlsopen(Chan *c, int omode)
4119a747e4fSDavid du Colombier {
4129a747e4fSDavid du Colombier 	TlsRec *tr, **pp;
4139a747e4fSDavid du Colombier 	int t, perm;
4149a747e4fSDavid du Colombier 
4159a747e4fSDavid du Colombier 	perm = 0;
4169a747e4fSDavid du Colombier 	omode &= 3;
4179a747e4fSDavid du Colombier 	switch(omode) {
4189a747e4fSDavid du Colombier 	case OREAD:
4199a747e4fSDavid du Colombier 		perm = 4;
4209a747e4fSDavid du Colombier 		break;
4219a747e4fSDavid du Colombier 	case OWRITE:
4229a747e4fSDavid du Colombier 		perm = 2;
4239a747e4fSDavid du Colombier 		break;
4249a747e4fSDavid du Colombier 	case ORDWR:
4259a747e4fSDavid du Colombier 		perm = 6;
4269a747e4fSDavid du Colombier 		break;
4279a747e4fSDavid du Colombier 	}
4289a747e4fSDavid du Colombier 
4299a747e4fSDavid du Colombier 	t = TYPE(c->qid);
4309a747e4fSDavid du Colombier 	switch(t) {
4319a747e4fSDavid du Colombier 	default:
4329a747e4fSDavid du Colombier 		panic("tlsopen");
4339a747e4fSDavid du Colombier 	case Qtopdir:
4349a747e4fSDavid du Colombier 	case Qprotodir:
4359a747e4fSDavid du Colombier 	case Qconvdir:
4369a747e4fSDavid du Colombier 		if(omode != OREAD)
4379a747e4fSDavid du Colombier 			error(Eperm);
4389a747e4fSDavid du Colombier 		break;
4399a747e4fSDavid du Colombier 	case Qclonus:
4409a747e4fSDavid du Colombier 		tr = newtls(c);
4419a747e4fSDavid du Colombier 		if(tr == nil)
4429a747e4fSDavid du Colombier 			error(Enodev);
4439a747e4fSDavid du Colombier 		break;
4449a747e4fSDavid du Colombier 	case Qctl:
4459a747e4fSDavid du Colombier 	case Qdata:
4469a747e4fSDavid du Colombier 	case Qhand:
4479a747e4fSDavid du Colombier 	case Qstatus:
4489a747e4fSDavid du Colombier 	case Qstats:
4499a747e4fSDavid du Colombier 		if((t == Qstatus || t == Qstats) && omode != OREAD)
4509a747e4fSDavid du Colombier 			error(Eperm);
4519a747e4fSDavid du Colombier 		if(waserror()) {
4529a747e4fSDavid du Colombier 			unlock(&tdlock);
4539a747e4fSDavid du Colombier 			nexterror();
4549a747e4fSDavid du Colombier 		}
4559a747e4fSDavid du Colombier 		lock(&tdlock);
4569a747e4fSDavid du Colombier 		pp = &tlsdevs[CONV(c->qid)];
4579a747e4fSDavid du Colombier 		tr = *pp;
4589a747e4fSDavid du Colombier 		if(tr == nil)
4599a747e4fSDavid du Colombier 			error("must open connection using clone");
4609a747e4fSDavid du Colombier 		if((perm & (tr->perm>>6)) != perm
4619a747e4fSDavid du Colombier 		&& (strcmp(up->user, tr->user) != 0
4629a747e4fSDavid du Colombier 		    || (perm & tr->perm) != perm))
4639a747e4fSDavid du Colombier 			error(Eperm);
4649a747e4fSDavid du Colombier 		if(t == Qhand){
4659a747e4fSDavid du Colombier 			if(waserror()){
4669a747e4fSDavid du Colombier 				unlock(&tr->hqlock);
4679a747e4fSDavid du Colombier 				nexterror();
4689a747e4fSDavid du Colombier 			}
4699a747e4fSDavid du Colombier 			lock(&tr->hqlock);
4709a747e4fSDavid du Colombier 			if(tr->handq != nil)
4719a747e4fSDavid du Colombier 				error(Einuse);
4729a747e4fSDavid du Colombier 			tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
4739a747e4fSDavid du Colombier 			if(tr->handq == nil)
4748cd4f5a6SDavid du Colombier 				error("cannot allocate handshake queue");
4759a747e4fSDavid du Colombier 			tr->hqref = 1;
4769a747e4fSDavid du Colombier 			unlock(&tr->hqlock);
4779a747e4fSDavid du Colombier 			poperror();
4789a747e4fSDavid du Colombier 		}
4799a747e4fSDavid du Colombier 		tr->ref++;
4809a747e4fSDavid du Colombier 		unlock(&tdlock);
4819a747e4fSDavid du Colombier 		poperror();
4829a747e4fSDavid du Colombier 		break;
4839a747e4fSDavid du Colombier 	case Qencalgs:
4849a747e4fSDavid du Colombier 	case Qhashalgs:
4859a747e4fSDavid du Colombier 		if(omode != OREAD)
4869a747e4fSDavid du Colombier 			error(Eperm);
4879a747e4fSDavid du Colombier 		break;
4889a747e4fSDavid du Colombier 	}
4899a747e4fSDavid du Colombier 	c->mode = openmode(omode);
4909a747e4fSDavid du Colombier 	c->flag |= COPEN;
4919a747e4fSDavid du Colombier 	c->offset = 0;
4929a747e4fSDavid du Colombier 	c->iounit = qiomaxatomic;
4939a747e4fSDavid du Colombier 	return c;
4949a747e4fSDavid du Colombier }
4959a747e4fSDavid du Colombier 
4969a747e4fSDavid du Colombier static int
tlswstat(Chan * c,uchar * dp,int n)4979a747e4fSDavid du Colombier tlswstat(Chan *c, uchar *dp, int n)
4989a747e4fSDavid du Colombier {
4999a747e4fSDavid du Colombier 	Dir *d;
5009a747e4fSDavid du Colombier 	TlsRec *tr;
5019a747e4fSDavid du Colombier 	int rv;
5029a747e4fSDavid du Colombier 
5039a747e4fSDavid du Colombier 	d = nil;
5049a747e4fSDavid du Colombier 	if(waserror()){
5059a747e4fSDavid du Colombier 		free(d);
5069a747e4fSDavid du Colombier 		unlock(&tdlock);
5079a747e4fSDavid du Colombier 		nexterror();
5089a747e4fSDavid du Colombier 	}
5099a747e4fSDavid du Colombier 
5109a747e4fSDavid du Colombier 	lock(&tdlock);
5119a747e4fSDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
5129a747e4fSDavid du Colombier 	if(tr == nil)
5139a747e4fSDavid du Colombier 		error(Ebadusefd);
5149a747e4fSDavid du Colombier 	if(strcmp(tr->user, up->user) != 0)
5159a747e4fSDavid du Colombier 		error(Eperm);
5169a747e4fSDavid du Colombier 
5179a747e4fSDavid du Colombier 	d = smalloc(n + sizeof *d);
5189a747e4fSDavid du Colombier 	rv = convM2D(dp, n, &d[0], (char*) &d[1]);
5199a747e4fSDavid du Colombier 	if(rv == 0)
5209a747e4fSDavid du Colombier 		error(Eshortstat);
5219a747e4fSDavid du Colombier 	if(!emptystr(d->uid))
5229a747e4fSDavid du Colombier 		kstrdup(&tr->user, d->uid);
5239a747e4fSDavid du Colombier 	if(d->mode != ~0UL)
5249a747e4fSDavid du Colombier 		tr->perm = d->mode;
5259a747e4fSDavid du Colombier 
5269a747e4fSDavid du Colombier 	free(d);
5279a747e4fSDavid du Colombier 	poperror();
5289a747e4fSDavid du Colombier 	unlock(&tdlock);
5299a747e4fSDavid du Colombier 
5309a747e4fSDavid du Colombier 	return rv;
5319a747e4fSDavid du Colombier }
5329a747e4fSDavid du Colombier 
5339a747e4fSDavid du Colombier static void
dechandq(TlsRec * tr)5349a747e4fSDavid du Colombier dechandq(TlsRec *tr)
5359a747e4fSDavid du Colombier {
5369a747e4fSDavid du Colombier 	lock(&tr->hqlock);
5379a747e4fSDavid du Colombier 	if(--tr->hqref == 0){
5389a747e4fSDavid du Colombier 		if(tr->handq != nil){
5399a747e4fSDavid du Colombier 			qfree(tr->handq);
5409a747e4fSDavid du Colombier 			tr->handq = nil;
5419a747e4fSDavid du Colombier 		}
5429a747e4fSDavid du Colombier 		if(tr->hprocessed != nil){
5439a747e4fSDavid du Colombier 			freeb(tr->hprocessed);
5449a747e4fSDavid du Colombier 			tr->hprocessed = nil;
5459a747e4fSDavid du Colombier 		}
5469a747e4fSDavid du Colombier 	}
5479a747e4fSDavid du Colombier 	unlock(&tr->hqlock);
5489a747e4fSDavid du Colombier }
5499a747e4fSDavid du Colombier 
5509a747e4fSDavid du Colombier static void
tlsclose(Chan * c)5519a747e4fSDavid du Colombier tlsclose(Chan *c)
5529a747e4fSDavid du Colombier {
5539a747e4fSDavid du Colombier 	TlsRec *tr;
5549a747e4fSDavid du Colombier 	int t;
5559a747e4fSDavid du Colombier 
5569a747e4fSDavid du Colombier 	t = TYPE(c->qid);
5579a747e4fSDavid du Colombier 	switch(t) {
5589a747e4fSDavid du Colombier 	case Qctl:
5599a747e4fSDavid du Colombier 	case Qdata:
5609a747e4fSDavid du Colombier 	case Qhand:
5619a747e4fSDavid du Colombier 	case Qstatus:
5629a747e4fSDavid du Colombier 	case Qstats:
5639a747e4fSDavid du Colombier 		if((c->flag & COPEN) == 0)
5649a747e4fSDavid du Colombier 			break;
5659a747e4fSDavid du Colombier 
5669a747e4fSDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
5679a747e4fSDavid du Colombier 		if(tr == nil)
5689a747e4fSDavid du Colombier 			break;
5699a747e4fSDavid du Colombier 
5709a747e4fSDavid du Colombier 		if(t == Qhand)
5719a747e4fSDavid du Colombier 			dechandq(tr);
5729a747e4fSDavid du Colombier 
5739a747e4fSDavid du Colombier 		lock(&tdlock);
5749a747e4fSDavid du Colombier 		if(--tr->ref > 0) {
5759a747e4fSDavid du Colombier 			unlock(&tdlock);
5769a747e4fSDavid du Colombier 			return;
5779a747e4fSDavid du Colombier 		}
5789a747e4fSDavid du Colombier 		tlsdevs[CONV(c->qid)] = nil;
5799a747e4fSDavid du Colombier 		unlock(&tdlock);
5809a747e4fSDavid du Colombier 
5819a747e4fSDavid du Colombier 		if(tr->c != nil && !waserror()){
5829a747e4fSDavid du Colombier 			checkstate(tr, 0, SOpen|SHandshake|SRClose);
5839a747e4fSDavid du Colombier 			sendAlert(tr, ECloseNotify);
5849a747e4fSDavid du Colombier 			poperror();
5859a747e4fSDavid du Colombier 		}
5869a747e4fSDavid du Colombier 		tlshangup(tr);
5879a747e4fSDavid du Colombier 		if(tr->c != nil)
5889a747e4fSDavid du Colombier 			cclose(tr->c);
5899a747e4fSDavid du Colombier 		freeSec(tr->in.sec);
5909a747e4fSDavid du Colombier 		freeSec(tr->in.new);
5919a747e4fSDavid du Colombier 		freeSec(tr->out.sec);
5929a747e4fSDavid du Colombier 		freeSec(tr->out.new);
5939a747e4fSDavid du Colombier 		free(tr->user);
5949a747e4fSDavid du Colombier 		free(tr);
5959a747e4fSDavid du Colombier 		break;
5969a747e4fSDavid du Colombier 	}
5979a747e4fSDavid du Colombier }
5989a747e4fSDavid du Colombier 
5999a747e4fSDavid du Colombier /*
6009a747e4fSDavid du Colombier  *  make sure we have at least 'n' bytes in list 'l'
6019a747e4fSDavid du Colombier  */
6029a747e4fSDavid du Colombier static void
ensure(TlsRec * s,Block ** l,int n)6039a747e4fSDavid du Colombier ensure(TlsRec *s, Block **l, int n)
6049a747e4fSDavid du Colombier {
6059a747e4fSDavid du Colombier 	int sofar, i;
6069a747e4fSDavid du Colombier 	Block *b, *bl;
6079a747e4fSDavid du Colombier 
6089a747e4fSDavid du Colombier 	sofar = 0;
6099a747e4fSDavid du Colombier 	for(b = *l; b; b = b->next){
6109a747e4fSDavid du Colombier 		sofar += BLEN(b);
6119a747e4fSDavid du Colombier 		if(sofar >= n)
6129a747e4fSDavid du Colombier 			return;
6139a747e4fSDavid du Colombier 		l = &b->next;
6149a747e4fSDavid du Colombier 	}
6159a747e4fSDavid du Colombier 
6169a747e4fSDavid du Colombier 	while(sofar < n){
6179a747e4fSDavid du Colombier 		bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
6189a747e4fSDavid du Colombier 		if(bl == 0)
6199a747e4fSDavid du Colombier 			error(Ehungup);
6209a747e4fSDavid du Colombier 		*l = bl;
6219a747e4fSDavid du Colombier 		i = 0;
6229a747e4fSDavid du Colombier 		for(b = bl; b; b = b->next){
6239a747e4fSDavid du Colombier 			i += BLEN(b);
6249a747e4fSDavid du Colombier 			l = &b->next;
6259a747e4fSDavid du Colombier 		}
6269a747e4fSDavid du Colombier 		if(i == 0)
6279a747e4fSDavid du Colombier 			error(Ehungup);
6289a747e4fSDavid du Colombier 		sofar += i;
6299a747e4fSDavid du Colombier 	}
6303751babcSDavid du Colombier if(s->debug) pprint("ensure read %d\n", sofar);
6319a747e4fSDavid du Colombier }
6329a747e4fSDavid du Colombier 
6339a747e4fSDavid du Colombier /*
6349a747e4fSDavid du Colombier  *  copy 'n' bytes from 'l' into 'p' and free
6359a747e4fSDavid du Colombier  *  the bytes in 'l'
6369a747e4fSDavid du Colombier  */
6379a747e4fSDavid du Colombier static void
consume(Block ** l,uchar * p,int n)6389a747e4fSDavid du Colombier consume(Block **l, uchar *p, int n)
6399a747e4fSDavid du Colombier {
6409a747e4fSDavid du Colombier 	Block *b;
6419a747e4fSDavid du Colombier 	int i;
6429a747e4fSDavid du Colombier 
6439a747e4fSDavid du Colombier 	for(; *l && n > 0; n -= i){
6449a747e4fSDavid du Colombier 		b = *l;
6459a747e4fSDavid du Colombier 		i = BLEN(b);
6469a747e4fSDavid du Colombier 		if(i > n)
6479a747e4fSDavid du Colombier 			i = n;
6489a747e4fSDavid du Colombier 		memmove(p, b->rp, i);
6499a747e4fSDavid du Colombier 		b->rp += i;
6509a747e4fSDavid du Colombier 		p += i;
6519a747e4fSDavid du Colombier 		if(BLEN(b) < 0)
6529a747e4fSDavid du Colombier 			panic("consume");
6539a747e4fSDavid du Colombier 		if(BLEN(b))
6549a747e4fSDavid du Colombier 			break;
6559a747e4fSDavid du Colombier 		*l = b->next;
6569a747e4fSDavid du Colombier 		freeb(b);
6579a747e4fSDavid du Colombier 	}
6589a747e4fSDavid du Colombier }
6599a747e4fSDavid du Colombier 
6609a747e4fSDavid du Colombier /*
6619a747e4fSDavid du Colombier  *  give back n bytes
6629a747e4fSDavid du Colombier  */
6639a747e4fSDavid du Colombier static void
regurgitate(TlsRec * s,uchar * p,int n)6649a747e4fSDavid du Colombier regurgitate(TlsRec *s, uchar *p, int n)
6659a747e4fSDavid du Colombier {
6669a747e4fSDavid du Colombier 	Block *b;
6679a747e4fSDavid du Colombier 
6689a747e4fSDavid du Colombier 	if(n <= 0)
6699a747e4fSDavid du Colombier 		return;
6709a747e4fSDavid du Colombier 	b = s->unprocessed;
6719a747e4fSDavid du Colombier 	if(s->unprocessed == nil || b->rp - b->base < n) {
6729a747e4fSDavid du Colombier 		b = allocb(n);
6739a747e4fSDavid du Colombier 		memmove(b->wp, p, n);
6749a747e4fSDavid du Colombier 		b->wp += n;
6759a747e4fSDavid du Colombier 		b->next = s->unprocessed;
6769a747e4fSDavid du Colombier 		s->unprocessed = b;
6779a747e4fSDavid du Colombier 	} else {
6789a747e4fSDavid du Colombier 		b->rp -= n;
6799a747e4fSDavid du Colombier 		memmove(b->rp, p, n);
6809a747e4fSDavid du Colombier 	}
6819a747e4fSDavid du Colombier }
6829a747e4fSDavid du Colombier 
6839a747e4fSDavid du Colombier /*
6849a747e4fSDavid du Colombier  *  remove at most n bytes from the queue
6859a747e4fSDavid du Colombier  */
6869a747e4fSDavid du Colombier static Block*
qgrab(Block ** l,int n)6879a747e4fSDavid du Colombier qgrab(Block **l, int n)
6889a747e4fSDavid du Colombier {
6899a747e4fSDavid du Colombier 	Block *bb, *b;
6909a747e4fSDavid du Colombier 	int i;
6919a747e4fSDavid du Colombier 
6929a747e4fSDavid du Colombier 	b = *l;
6939a747e4fSDavid du Colombier 	if(BLEN(b) == n){
6949a747e4fSDavid du Colombier 		*l = b->next;
6959a747e4fSDavid du Colombier 		b->next = nil;
6969a747e4fSDavid du Colombier 		return b;
6979a747e4fSDavid du Colombier 	}
6989a747e4fSDavid du Colombier 
6999a747e4fSDavid du Colombier 	i = 0;
7009a747e4fSDavid du Colombier 	for(bb = b; bb != nil && i < n; bb = bb->next)
7019a747e4fSDavid du Colombier 		i += BLEN(bb);
7029a747e4fSDavid du Colombier 	if(i > n)
7039a747e4fSDavid du Colombier 		i = n;
7049a747e4fSDavid du Colombier 
7059a747e4fSDavid du Colombier 	bb = allocb(i);
7069a747e4fSDavid du Colombier 	consume(l, bb->wp, i);
7079a747e4fSDavid du Colombier 	bb->wp += i;
7089a747e4fSDavid du Colombier 	return bb;
7099a747e4fSDavid du Colombier }
7109a747e4fSDavid du Colombier 
7119a747e4fSDavid du Colombier static void
tlsclosed(TlsRec * tr,int new)7129a747e4fSDavid du Colombier tlsclosed(TlsRec *tr, int new)
7139a747e4fSDavid du Colombier {
7149a747e4fSDavid du Colombier 	lock(&tr->statelk);
7159a747e4fSDavid du Colombier 	if(tr->state == SOpen || tr->state == SHandshake)
7169a747e4fSDavid du Colombier 		tr->state = new;
7179a747e4fSDavid du Colombier 	else if((new | tr->state) == (SRClose|SLClose))
7189a747e4fSDavid du Colombier 		tr->state = SClosed;
7199a747e4fSDavid du Colombier 	unlock(&tr->statelk);
7209a747e4fSDavid du Colombier 	alertHand(tr, "close notify");
7219a747e4fSDavid du Colombier }
7229a747e4fSDavid du Colombier 
7239a747e4fSDavid du Colombier /*
7249a747e4fSDavid du Colombier  *  read and process one tls record layer message
7259a747e4fSDavid du Colombier  *  must be called with tr->in.io held
7269a747e4fSDavid du Colombier  *  We can't let Eintrs lose data, since doing so will get
7279a747e4fSDavid du Colombier  *  us out of sync with the sender and break the reliablity
7289a747e4fSDavid du Colombier  *  of the channel.  Eintr only happens during the reads in
7299a747e4fSDavid du Colombier  *  consume.  Therefore we put back any bytes consumed before
7309a747e4fSDavid du Colombier  *  the last call to ensure.
7319a747e4fSDavid du Colombier  */
7329a747e4fSDavid du Colombier static void
tlsrecread(TlsRec * tr)7339a747e4fSDavid du Colombier tlsrecread(TlsRec *tr)
7349a747e4fSDavid du Colombier {
7359a747e4fSDavid du Colombier 	OneWay *volatile in;
7369a747e4fSDavid du Colombier 	Block *volatile b;
7379b7bf7dfSDavid du Colombier 	uchar *p, seq[8], header[RecHdrLen], hmac[MaxMacLen];
7389a747e4fSDavid du Colombier 	int volatile nconsumed;
739a6a9e072SDavid du Colombier 	int len, type, ver, unpad_len;
7409a747e4fSDavid du Colombier 
7419a747e4fSDavid du Colombier 	nconsumed = 0;
7429a747e4fSDavid du Colombier 	if(waserror()){
7439a747e4fSDavid du Colombier 		if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
7449a747e4fSDavid du Colombier 			regurgitate(tr, header, nconsumed);
7459a747e4fSDavid du Colombier 			poperror();
7469a747e4fSDavid du Colombier 		}else
7479a747e4fSDavid du Colombier 			tlsError(tr, "channel error");
7489a747e4fSDavid du Colombier 		nexterror();
7499a747e4fSDavid du Colombier 	}
7509a747e4fSDavid du Colombier 	ensure(tr, &tr->unprocessed, RecHdrLen);
7519a747e4fSDavid du Colombier 	consume(&tr->unprocessed, header, RecHdrLen);
7523751babcSDavid du Colombier if(tr->debug)pprint("consumed %d header\n", RecHdrLen);
7539a747e4fSDavid du Colombier 	nconsumed = RecHdrLen;
7549a747e4fSDavid du Colombier 
7559a747e4fSDavid du Colombier 	if((tr->handin == 0) && (header[0] & 0x80)){
7569a747e4fSDavid du Colombier 		/* Cope with an SSL3 ClientHello expressed in SSL2 record format.
7579a747e4fSDavid du Colombier 			This is sent by some clients that we must interoperate
7589a747e4fSDavid du Colombier 			with, such as Java's JSSE and Microsoft's Internet Explorer. */
7599a747e4fSDavid du Colombier 		len = (get16(header) & ~0x8000) - 3;
7609a747e4fSDavid du Colombier 		type = header[2];
7619a747e4fSDavid du Colombier 		ver = get16(header + 3);
7629a747e4fSDavid du Colombier 		if(type != SSL2ClientHello || len < 22)
7639a747e4fSDavid du Colombier 			rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
7649a747e4fSDavid du Colombier 	}else{  /* normal SSL3 record format */
7659a747e4fSDavid du Colombier 		type = header[0];
7669a747e4fSDavid du Colombier 		ver = get16(header+1);
7679a747e4fSDavid du Colombier 		len = get16(header+3);
7689a747e4fSDavid du Colombier 	}
7699a747e4fSDavid du Colombier 	if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
7709a747e4fSDavid du Colombier 		rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
7719a747e4fSDavid du Colombier 			tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
772*e873706dSDavid du Colombier 	if(len > MaxCipherRecLen || len <= 0)
773*e873706dSDavid du Colombier 		rcvError(tr, ERecordOverflow, "bad record message length %d", len);
7749a747e4fSDavid du Colombier 	ensure(tr, &tr->unprocessed, len);
7759a747e4fSDavid du Colombier 	nconsumed = 0;
7769a747e4fSDavid du Colombier 	poperror();
7779a747e4fSDavid du Colombier 
7789a747e4fSDavid du Colombier 	/*
7799a747e4fSDavid du Colombier 	 * If an Eintr happens after this, we'll get out of sync.
7809a747e4fSDavid du Colombier 	 * Make sure nothing we call can sleep.
7819a747e4fSDavid du Colombier 	 * Errors are ok, as they kill the connection.
7829a747e4fSDavid du Colombier 	 * Luckily, allocb won't sleep, it'll just error out.
7839a747e4fSDavid du Colombier 	 */
7849a747e4fSDavid du Colombier 	b = nil;
7859a747e4fSDavid du Colombier 	if(waserror()){
7869a747e4fSDavid du Colombier 		if(b != nil)
7879a747e4fSDavid du Colombier 			freeb(b);
7889a747e4fSDavid du Colombier 		tlsError(tr, "channel error");
7899a747e4fSDavid du Colombier 		nexterror();
7909a747e4fSDavid du Colombier 	}
7919a747e4fSDavid du Colombier 	b = qgrab(&tr->unprocessed, len);
7923751babcSDavid du Colombier if(tr->debug) pprint("consumed unprocessed %d\n", len);
7939a747e4fSDavid du Colombier 
7949a747e4fSDavid du Colombier 	in = &tr->in;
7959a747e4fSDavid du Colombier 	if(waserror()){
7969a747e4fSDavid du Colombier 		qunlock(&in->seclock);
7979a747e4fSDavid du Colombier 		nexterror();
7989a747e4fSDavid du Colombier 	}
7999a747e4fSDavid du Colombier 	qlock(&in->seclock);
8009a747e4fSDavid du Colombier 	p = b->rp;
8019a747e4fSDavid du Colombier 	if(in->sec != nil) {
802a6a9e072SDavid du Colombier 		/* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
803a6a9e072SDavid du Colombier 		        should look alike, including timing of the response. */
804a6a9e072SDavid du Colombier 		unpad_len = (*in->sec->dec)(in->sec, p, len);
8052a47d117SDavid du Colombier 
8062a47d117SDavid du Colombier 		/* excplicit iv */
8072a47d117SDavid du Colombier 		if(tr->version >= TLS11Version){
8082a47d117SDavid du Colombier 			len -= in->sec->block;
8092a47d117SDavid du Colombier 			if(len < 0)
8102a47d117SDavid du Colombier 				rcvError(tr, EDecodeError, "runt record message");
8112a47d117SDavid du Colombier 
8122a47d117SDavid du Colombier 			unpad_len -= in->sec->block;
8132a47d117SDavid du Colombier 			p += in->sec->block;
8142a47d117SDavid du Colombier 		}
8152a47d117SDavid du Colombier 
8163751babcSDavid du Colombier 		if(unpad_len >= in->sec->maclen)
817a6a9e072SDavid du Colombier 			len = unpad_len - in->sec->maclen;
8183751babcSDavid du Colombier if(tr->debug) pprint("decrypted %d\n", unpad_len);
8193751babcSDavid du Colombier if(tr->debug) pdump(unpad_len, p, "decrypted:");
8209a747e4fSDavid du Colombier 
8219a747e4fSDavid du Colombier 		/* update length */
8229a747e4fSDavid du Colombier 		put16(header+3, len);
8239a747e4fSDavid du Colombier 		put64(seq, in->seq);
8249a747e4fSDavid du Colombier 		in->seq++;
8259a747e4fSDavid du Colombier 		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
8263751babcSDavid du Colombier 		if(unpad_len < in->sec->maclen)
827dc5a79c1SDavid du Colombier 			rcvError(tr, EBadRecordMac, "short record mac");
828dc5a79c1SDavid du Colombier 		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
8299a747e4fSDavid du Colombier 			rcvError(tr, EBadRecordMac, "record mac mismatch");
8302a47d117SDavid du Colombier 		b->rp = p;
8312a47d117SDavid du Colombier 		b->wp = p+len;
8329a747e4fSDavid du Colombier 	}
8339a747e4fSDavid du Colombier 	qunlock(&in->seclock);
8349a747e4fSDavid du Colombier 	poperror();
8353751babcSDavid du Colombier 	if(len < 0)
8369a747e4fSDavid du Colombier 		rcvError(tr, EDecodeError, "runt record message");
8379a747e4fSDavid du Colombier 
8389a747e4fSDavid du Colombier 	switch(type) {
8399a747e4fSDavid du Colombier 	default:
840567483c8SDavid du Colombier 		rcvError(tr, EIllegalParameter, "invalid record message %#x", type);
8419a747e4fSDavid du Colombier 		break;
8429a747e4fSDavid du Colombier 	case RChangeCipherSpec:
8439a747e4fSDavid du Colombier 		if(len != 1 || p[0] != 1)
8449a747e4fSDavid du Colombier 			rcvError(tr, EDecodeError, "invalid change cipher spec");
8459a747e4fSDavid du Colombier 		qlock(&in->seclock);
8469a747e4fSDavid du Colombier 		if(in->new == nil){
8479a747e4fSDavid du Colombier 			qunlock(&in->seclock);
8489a747e4fSDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
8499a747e4fSDavid du Colombier 		}
8509a747e4fSDavid du Colombier 		freeSec(in->sec);
8519a747e4fSDavid du Colombier 		in->sec = in->new;
8529a747e4fSDavid du Colombier 		in->new = nil;
8539a747e4fSDavid du Colombier 		in->seq = 0;
8549a747e4fSDavid du Colombier 		qunlock(&in->seclock);
8559a747e4fSDavid du Colombier 		break;
8569a747e4fSDavid du Colombier 	case RAlert:
8579a747e4fSDavid du Colombier 		if(len != 2)
8589a747e4fSDavid du Colombier 			rcvError(tr, EDecodeError, "invalid alert");
8599a747e4fSDavid du Colombier 		if(p[0] == 2)
8609a747e4fSDavid du Colombier 			rcvAlert(tr, p[1]);
8619a747e4fSDavid du Colombier 		if(p[0] != 1)
8629a747e4fSDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert fatal code");
8639a747e4fSDavid du Colombier 
8649a747e4fSDavid du Colombier 		/*
8659a747e4fSDavid du Colombier 		 * propate non-fatal alerts to handshaker
8669a747e4fSDavid du Colombier 		 */
8672a47d117SDavid du Colombier 		switch(p[1]){
8682a47d117SDavid du Colombier 		case ECloseNotify:
8699a747e4fSDavid du Colombier 			tlsclosed(tr, SRClose);
8709a747e4fSDavid du Colombier 			if(tr->opened)
8719a747e4fSDavid du Colombier 				error("tls hungup");
8729a747e4fSDavid du Colombier 			error("close notify");
8732a47d117SDavid du Colombier 			break;
8742a47d117SDavid du Colombier 		case ENoRenegotiation:
8759a747e4fSDavid du Colombier 			alertHand(tr, "no renegotiation");
8762a47d117SDavid du Colombier 			break;
8772a47d117SDavid du Colombier 		case EUserCanceled:
8789a747e4fSDavid du Colombier 			alertHand(tr, "handshake canceled by user");
8792a47d117SDavid du Colombier 			break;
8802a47d117SDavid du Colombier 		case EUnrecognizedName:
8812a47d117SDavid du Colombier 			/* happens in response to SNI, can be ignored. */
8822a47d117SDavid du Colombier 			break;
8832a47d117SDavid du Colombier 		default:
8849a747e4fSDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert code");
8852a47d117SDavid du Colombier 		}
8869a747e4fSDavid du Colombier 		break;
8879a747e4fSDavid du Colombier 	case RHandshake:
8889a747e4fSDavid du Colombier 		/*
8899a747e4fSDavid du Colombier 		 * don't worry about dropping the block
8909a747e4fSDavid du Colombier 		 * qbwrite always queues even if flow controlled and interrupted.
8919a747e4fSDavid du Colombier 		 *
8929a747e4fSDavid du Colombier 		 * if there isn't any handshaker, ignore the request,
8939a747e4fSDavid du Colombier 		 * but notify the other side we are doing so.
8949a747e4fSDavid du Colombier 		 */
8959a747e4fSDavid du Colombier 		lock(&tr->hqlock);
8969a747e4fSDavid du Colombier 		if(tr->handq != nil){
8979a747e4fSDavid du Colombier 			tr->hqref++;
8989a747e4fSDavid du Colombier 			unlock(&tr->hqlock);
8999a747e4fSDavid du Colombier 			if(waserror()){
9009a747e4fSDavid du Colombier 				dechandq(tr);
9019a747e4fSDavid du Colombier 				nexterror();
9029a747e4fSDavid du Colombier 			}
9039a747e4fSDavid du Colombier 			b = padblock(b, 1);
9049a747e4fSDavid du Colombier 			*b->rp = RHandshake;
9059a747e4fSDavid du Colombier 			qbwrite(tr->handq, b);
9069a747e4fSDavid du Colombier 			b = nil;
9079a747e4fSDavid du Colombier 			poperror();
9089a747e4fSDavid du Colombier 			dechandq(tr);
9099a747e4fSDavid du Colombier 		}else{
9109a747e4fSDavid du Colombier 			unlock(&tr->hqlock);
9119a747e4fSDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
9129a747e4fSDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
9139a747e4fSDavid du Colombier 				poperror();
9149a747e4fSDavid du Colombier 			}
9159a747e4fSDavid du Colombier 		}
9169a747e4fSDavid du Colombier 		break;
9179a747e4fSDavid du Colombier 	case SSL2ClientHello:
9189a747e4fSDavid du Colombier 		lock(&tr->hqlock);
9199a747e4fSDavid du Colombier 		if(tr->handq != nil){
9209a747e4fSDavid du Colombier 			tr->hqref++;
9219a747e4fSDavid du Colombier 			unlock(&tr->hqlock);
9229a747e4fSDavid du Colombier 			if(waserror()){
9239a747e4fSDavid du Colombier 				dechandq(tr);
9249a747e4fSDavid du Colombier 				nexterror();
9259a747e4fSDavid du Colombier 			}
9269a747e4fSDavid du Colombier 			/* Pass the SSL2 format data, so that the handshake code can compute
9279a747e4fSDavid du Colombier 				the correct checksums.  HSSL2ClientHello = HandshakeType 9 is
9289a747e4fSDavid du Colombier 				unused in RFC2246. */
9299a747e4fSDavid du Colombier 			b = padblock(b, 8);
9309a747e4fSDavid du Colombier 			b->rp[0] = RHandshake;
9319a747e4fSDavid du Colombier 			b->rp[1] = HSSL2ClientHello;
9329a747e4fSDavid du Colombier 			put24(&b->rp[2], len+3);
9339a747e4fSDavid du Colombier 			b->rp[5] = SSL2ClientHello;
9349a747e4fSDavid du Colombier 			put16(&b->rp[6], ver);
9359a747e4fSDavid du Colombier 			qbwrite(tr->handq, b);
9369a747e4fSDavid du Colombier 			b = nil;
9379a747e4fSDavid du Colombier 			poperror();
9389a747e4fSDavid du Colombier 			dechandq(tr);
9399a747e4fSDavid du Colombier 		}else{
9409a747e4fSDavid du Colombier 			unlock(&tr->hqlock);
9419a747e4fSDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
9429a747e4fSDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
9439a747e4fSDavid du Colombier 				poperror();
9449a747e4fSDavid du Colombier 			}
9459a747e4fSDavid du Colombier 		}
9469a747e4fSDavid du Colombier 		break;
9479a747e4fSDavid du Colombier 	case RApplication:
9489a747e4fSDavid du Colombier 		if(!tr->opened)
9499a747e4fSDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
9503751babcSDavid du Colombier 		if(BLEN(b) > 0){
9519a747e4fSDavid du Colombier 			tr->processed = b;
9529a747e4fSDavid du Colombier 			b = nil;
9533751babcSDavid du Colombier 		}
9549a747e4fSDavid du Colombier 		break;
9559a747e4fSDavid du Colombier 	}
9569a747e4fSDavid du Colombier 	if(b != nil)
9579a747e4fSDavid du Colombier 		freeb(b);
9589a747e4fSDavid du Colombier 	poperror();
9599a747e4fSDavid du Colombier }
9609a747e4fSDavid du Colombier 
9619a747e4fSDavid du Colombier /*
9629a747e4fSDavid du Colombier  * got a fatal alert message
9639a747e4fSDavid du Colombier  */
9649a747e4fSDavid du Colombier static void
rcvAlert(TlsRec * tr,int err)9659a747e4fSDavid du Colombier rcvAlert(TlsRec *tr, int err)
9669a747e4fSDavid du Colombier {
9679a747e4fSDavid du Colombier 	char *s;
9689a747e4fSDavid du Colombier 	int i;
9699a747e4fSDavid du Colombier 
9709a747e4fSDavid du Colombier 	s = "unknown error";
9719a747e4fSDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++){
9729a747e4fSDavid du Colombier 		if(tlserrs[i].err == err){
9739a747e4fSDavid du Colombier 			s = tlserrs[i].msg;
9749a747e4fSDavid du Colombier 			break;
9759a747e4fSDavid du Colombier 		}
9769a747e4fSDavid du Colombier 	}
9773751babcSDavid du Colombier if(tr->debug) pprint("rcvAlert: %s\n", s);
9789a747e4fSDavid du Colombier 
9799a747e4fSDavid du Colombier 	tlsError(tr, s);
9809a747e4fSDavid du Colombier 	if(!tr->opened)
9819a747e4fSDavid du Colombier 		error(s);
9829a747e4fSDavid du Colombier 	error("tls error");
9839a747e4fSDavid du Colombier }
9849a747e4fSDavid du Colombier 
9859a747e4fSDavid du Colombier /*
9869a747e4fSDavid du Colombier  * found an error while decoding the input stream
9879a747e4fSDavid du Colombier  */
9889a747e4fSDavid du Colombier static void
rcvError(TlsRec * tr,int err,char * fmt,...)9899a747e4fSDavid du Colombier rcvError(TlsRec *tr, int err, char *fmt, ...)
9909a747e4fSDavid du Colombier {
9919a747e4fSDavid du Colombier 	char msg[ERRMAX];
9929a747e4fSDavid du Colombier 	va_list arg;
9939a747e4fSDavid du Colombier 
9949a747e4fSDavid du Colombier 	va_start(arg, fmt);
9959a747e4fSDavid du Colombier 	vseprint(msg, msg+sizeof(msg), fmt, arg);
9969a747e4fSDavid du Colombier 	va_end(arg);
9973751babcSDavid du Colombier if(tr->debug) pprint("rcvError: %s\n", msg);
9989a747e4fSDavid du Colombier 
9999a747e4fSDavid du Colombier 	sendAlert(tr, err);
10009a747e4fSDavid du Colombier 
10019a747e4fSDavid du Colombier 	if(!tr->opened)
10029a747e4fSDavid du Colombier 		error(msg);
10039a747e4fSDavid du Colombier 	error("tls error");
10049a747e4fSDavid du Colombier }
10059a747e4fSDavid du Colombier 
10069a747e4fSDavid du Colombier /*
10079a747e4fSDavid du Colombier  * make sure the next hand operation returns with a 'msg' error
10089a747e4fSDavid du Colombier  */
10099a747e4fSDavid du Colombier static void
alertHand(TlsRec * tr,char * msg)10109a747e4fSDavid du Colombier alertHand(TlsRec *tr, char *msg)
10119a747e4fSDavid du Colombier {
10129a747e4fSDavid du Colombier 	Block *b;
10139a747e4fSDavid du Colombier 	int n;
10149a747e4fSDavid du Colombier 
10159a747e4fSDavid du Colombier 	lock(&tr->hqlock);
10169a747e4fSDavid du Colombier 	if(tr->handq == nil){
10179a747e4fSDavid du Colombier 		unlock(&tr->hqlock);
10189a747e4fSDavid du Colombier 		return;
10199a747e4fSDavid du Colombier 	}
10209a747e4fSDavid du Colombier 	tr->hqref++;
10219a747e4fSDavid du Colombier 	unlock(&tr->hqlock);
10229a747e4fSDavid du Colombier 
10239a747e4fSDavid du Colombier 	n = strlen(msg);
10249a747e4fSDavid du Colombier 	if(waserror()){
10259a747e4fSDavid du Colombier 		dechandq(tr);
10269a747e4fSDavid du Colombier 		nexterror();
10279a747e4fSDavid du Colombier 	}
10289a747e4fSDavid du Colombier 	b = allocb(n + 2);
10299a747e4fSDavid du Colombier 	*b->wp++ = RAlert;
10309a747e4fSDavid du Colombier 	memmove(b->wp, msg, n + 1);
10319a747e4fSDavid du Colombier 	b->wp += n + 1;
10329a747e4fSDavid du Colombier 
10339a747e4fSDavid du Colombier 	qbwrite(tr->handq, b);
10349a747e4fSDavid du Colombier 
10359a747e4fSDavid du Colombier 	poperror();
10369a747e4fSDavid du Colombier 	dechandq(tr);
10379a747e4fSDavid du Colombier }
10389a747e4fSDavid du Colombier 
10399a747e4fSDavid du Colombier static void
checkstate(TlsRec * tr,int ishand,int ok)10409a747e4fSDavid du Colombier checkstate(TlsRec *tr, int ishand, int ok)
10419a747e4fSDavid du Colombier {
10429a747e4fSDavid du Colombier 	int state;
10439a747e4fSDavid du Colombier 
10449a747e4fSDavid du Colombier 	lock(&tr->statelk);
10459a747e4fSDavid du Colombier 	state = tr->state;
10469a747e4fSDavid du Colombier 	unlock(&tr->statelk);
10479a747e4fSDavid du Colombier 	if(state & ok)
10489a747e4fSDavid du Colombier 		return;
10499a747e4fSDavid du Colombier 	switch(state){
10509a747e4fSDavid du Colombier 	case SHandshake:
10519a747e4fSDavid du Colombier 	case SOpen:
10529a747e4fSDavid du Colombier 		break;
10539a747e4fSDavid du Colombier 	case SError:
10549a747e4fSDavid du Colombier 	case SAlert:
10559a747e4fSDavid du Colombier 		if(ishand)
10569a747e4fSDavid du Colombier 			error(tr->err);
10579a747e4fSDavid du Colombier 		error("tls error");
10589a747e4fSDavid du Colombier 	case SRClose:
10599a747e4fSDavid du Colombier 	case SLClose:
10609a747e4fSDavid du Colombier 	case SClosed:
10619a747e4fSDavid du Colombier 		error("tls hungup");
10629a747e4fSDavid du Colombier 	}
10639a747e4fSDavid du Colombier 	error("tls improperly configured");
10649a747e4fSDavid du Colombier }
10659a747e4fSDavid du Colombier 
10669a747e4fSDavid du Colombier static Block*
tlsbread(Chan * c,long n,ulong offset)10679a747e4fSDavid du Colombier tlsbread(Chan *c, long n, ulong offset)
10689a747e4fSDavid du Colombier {
10699a747e4fSDavid du Colombier 	int ty;
10709a747e4fSDavid du Colombier 	Block *b;
10719a747e4fSDavid du Colombier 	TlsRec *volatile tr;
10729a747e4fSDavid du Colombier 
10739a747e4fSDavid du Colombier 	ty = TYPE(c->qid);
10749a747e4fSDavid du Colombier 	switch(ty) {
10759a747e4fSDavid du Colombier 	default:
10769a747e4fSDavid du Colombier 		return devbread(c, n, offset);
10779a747e4fSDavid du Colombier 	case Qhand:
10789a747e4fSDavid du Colombier 	case Qdata:
10799a747e4fSDavid du Colombier 		break;
10809a747e4fSDavid du Colombier 	}
10819a747e4fSDavid du Colombier 
10829a747e4fSDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
10839a747e4fSDavid du Colombier 	if(tr == nil)
10849a747e4fSDavid du Colombier 		panic("tlsbread");
10859a747e4fSDavid du Colombier 
10869a747e4fSDavid du Colombier 	if(waserror()){
10879a747e4fSDavid du Colombier 		qunlock(&tr->in.io);
10889a747e4fSDavid du Colombier 		nexterror();
10899a747e4fSDavid du Colombier 	}
10909a747e4fSDavid du Colombier 	qlock(&tr->in.io);
10919a747e4fSDavid du Colombier 	if(ty == Qdata){
10929a747e4fSDavid du Colombier 		checkstate(tr, 0, SOpen);
10939a747e4fSDavid du Colombier 		while(tr->processed == nil)
10949a747e4fSDavid du Colombier 			tlsrecread(tr);
10959a747e4fSDavid du Colombier 
10969a747e4fSDavid du Colombier 		/* return at most what was asked for */
10979a747e4fSDavid du Colombier 		b = qgrab(&tr->processed, n);
1098e464ed1aSDavid du Colombier if(tr->debug) pprint("consumed processed %ld\n", BLEN(b));
10993751babcSDavid du Colombier if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
11009a747e4fSDavid du Colombier 		qunlock(&tr->in.io);
11019a747e4fSDavid du Colombier 		poperror();
11029a747e4fSDavid du Colombier 		tr->datain += BLEN(b);
11039a747e4fSDavid du Colombier 	}else{
11049a747e4fSDavid du Colombier 		checkstate(tr, 1, SOpen|SHandshake|SLClose);
11059a747e4fSDavid du Colombier 
11069a747e4fSDavid du Colombier 		/*
11079a747e4fSDavid du Colombier 		 * it's ok to look at state without the lock
11089a747e4fSDavid du Colombier 		 * since it only protects reading records,
11099a747e4fSDavid du Colombier 		 * and we have that tr->in.io held.
11109a747e4fSDavid du Colombier 		 */
11119a747e4fSDavid du Colombier 		while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
11129a747e4fSDavid du Colombier 			tlsrecread(tr);
11139a747e4fSDavid du Colombier 
11149a747e4fSDavid du Colombier 		qunlock(&tr->in.io);
11159a747e4fSDavid du Colombier 		poperror();
11169a747e4fSDavid du Colombier 
11179a747e4fSDavid du Colombier 		if(waserror()){
11189a747e4fSDavid du Colombier 			qunlock(&tr->hqread);
11199a747e4fSDavid du Colombier 			nexterror();
11209a747e4fSDavid du Colombier 		}
11219a747e4fSDavid du Colombier 		qlock(&tr->hqread);
11229a747e4fSDavid du Colombier 		if(tr->hprocessed == nil){
11239a747e4fSDavid du Colombier 			b = qbread(tr->handq, MaxRecLen + 1);
11249a747e4fSDavid du Colombier 			if(*b->rp++ == RAlert){
1125dc5a79c1SDavid du Colombier 				kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
11269a747e4fSDavid du Colombier 				freeb(b);
11279a747e4fSDavid du Colombier 				nexterror();
11289a747e4fSDavid du Colombier 			}
11299a747e4fSDavid du Colombier 			tr->hprocessed = b;
11309a747e4fSDavid du Colombier 		}
11319a747e4fSDavid du Colombier 		b = qgrab(&tr->hprocessed, n);
11329a747e4fSDavid du Colombier 		poperror();
11339a747e4fSDavid du Colombier 		qunlock(&tr->hqread);
11349a747e4fSDavid du Colombier 		tr->handin += BLEN(b);
11359a747e4fSDavid du Colombier 	}
11369a747e4fSDavid du Colombier 
11379a747e4fSDavid du Colombier 	return b;
11389a747e4fSDavid du Colombier }
11399a747e4fSDavid du Colombier 
11409a747e4fSDavid du Colombier static long
tlsread(Chan * c,void * a,long n,vlong off)11419a747e4fSDavid du Colombier tlsread(Chan *c, void *a, long n, vlong off)
11429a747e4fSDavid du Colombier {
11439a747e4fSDavid du Colombier 	Block *volatile b;
11449a747e4fSDavid du Colombier 	Block *nb;
11459a747e4fSDavid du Colombier 	uchar *va;
11469a747e4fSDavid du Colombier 	int i, ty;
11479a747e4fSDavid du Colombier 	char *buf, *s, *e;
11489a747e4fSDavid du Colombier 	ulong offset = off;
11499a747e4fSDavid du Colombier 	TlsRec * tr;
11509a747e4fSDavid du Colombier 
11519a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR)
11529a747e4fSDavid du Colombier 		return devdirread(c, a, n, 0, 0, tlsgen);
11539a747e4fSDavid du Colombier 
11549a747e4fSDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
11559a747e4fSDavid du Colombier 	ty = TYPE(c->qid);
11569a747e4fSDavid du Colombier 	switch(ty) {
11579a747e4fSDavid du Colombier 	default:
11589a747e4fSDavid du Colombier 		error(Ebadusefd);
11599a747e4fSDavid du Colombier 	case Qstatus:
11609a747e4fSDavid du Colombier 		buf = smalloc(Statlen);
11619a747e4fSDavid du Colombier 		qlock(&tr->in.seclock);
11629a747e4fSDavid du Colombier 		qlock(&tr->out.seclock);
11639a747e4fSDavid du Colombier 		s = buf;
11649a747e4fSDavid du Colombier 		e = buf + Statlen;
11659a747e4fSDavid du Colombier 		s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
1166567483c8SDavid du Colombier 		s = seprint(s, e, "Version: %#x\n", tr->version);
11679a747e4fSDavid du Colombier 		if(tr->in.sec != nil)
11689a747e4fSDavid du Colombier 			s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
11699a747e4fSDavid du Colombier 		if(tr->in.new != nil)
11709a747e4fSDavid du Colombier 			s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
11719a747e4fSDavid du Colombier 		if(tr->out.sec != nil)
11729a747e4fSDavid du Colombier 			s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
11739a747e4fSDavid du Colombier 		if(tr->out.new != nil)
11749a747e4fSDavid du Colombier 			seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
11759a747e4fSDavid du Colombier 		qunlock(&tr->in.seclock);
11769a747e4fSDavid du Colombier 		qunlock(&tr->out.seclock);
11779a747e4fSDavid du Colombier 		n = readstr(offset, a, n, buf);
11789a747e4fSDavid du Colombier 		free(buf);
11799a747e4fSDavid du Colombier 		return n;
11809a747e4fSDavid du Colombier 	case Qstats:
11819a747e4fSDavid du Colombier 		buf = smalloc(Statlen);
11829a747e4fSDavid du Colombier 		s = buf;
11839a747e4fSDavid du Colombier 		e = buf + Statlen;
11849a747e4fSDavid du Colombier 		s = seprint(s, e, "DataIn: %lld\n", tr->datain);
11859a747e4fSDavid du Colombier 		s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
11869a747e4fSDavid du Colombier 		s = seprint(s, e, "HandIn: %lld\n", tr->handin);
11879a747e4fSDavid du Colombier 		seprint(s, e, "HandOut: %lld\n", tr->handout);
11889a747e4fSDavid du Colombier 		n = readstr(offset, a, n, buf);
11899a747e4fSDavid du Colombier 		free(buf);
11909a747e4fSDavid du Colombier 		return n;
11919a747e4fSDavid du Colombier 	case Qctl:
11929a747e4fSDavid du Colombier 		buf = smalloc(Statlen);
11939a747e4fSDavid du Colombier 		snprint(buf, Statlen, "%llud", CONV(c->qid));
11949a747e4fSDavid du Colombier 		n = readstr(offset, a, n, buf);
11959a747e4fSDavid du Colombier 		free(buf);
11969a747e4fSDavid du Colombier 		return n;
11979a747e4fSDavid du Colombier 	case Qdata:
11989a747e4fSDavid du Colombier 	case Qhand:
11999a747e4fSDavid du Colombier 		b = tlsbread(c, n, offset);
12009a747e4fSDavid du Colombier 		break;
12019a747e4fSDavid du Colombier 	case Qencalgs:
12029a747e4fSDavid du Colombier 		return readstr(offset, a, n, encalgs);
12039a747e4fSDavid du Colombier 	case Qhashalgs:
12049a747e4fSDavid du Colombier 		return readstr(offset, a, n, hashalgs);
12059a747e4fSDavid du Colombier 	}
12069a747e4fSDavid du Colombier 
12079a747e4fSDavid du Colombier 	if(waserror()){
12089a747e4fSDavid du Colombier 		freeblist(b);
12099a747e4fSDavid du Colombier 		nexterror();
12109a747e4fSDavid du Colombier 	}
12119a747e4fSDavid du Colombier 
12129a747e4fSDavid du Colombier 	n = 0;
12139a747e4fSDavid du Colombier 	va = a;
12149a747e4fSDavid du Colombier 	for(nb = b; nb; nb = nb->next){
12159a747e4fSDavid du Colombier 		i = BLEN(nb);
12169a747e4fSDavid du Colombier 		memmove(va+n, nb->rp, i);
12179a747e4fSDavid du Colombier 		n += i;
12189a747e4fSDavid du Colombier 	}
12199a747e4fSDavid du Colombier 
12209a747e4fSDavid du Colombier 	freeblist(b);
12219a747e4fSDavid du Colombier 	poperror();
12229a747e4fSDavid du Colombier 
12239a747e4fSDavid du Colombier 	return n;
12249a747e4fSDavid du Colombier }
12259a747e4fSDavid du Colombier 
12262a47d117SDavid du Colombier static void
randfill(uchar * buf,int len)12272a47d117SDavid du Colombier randfill(uchar *buf, int len)
12282a47d117SDavid du Colombier {
12292a47d117SDavid du Colombier 	while(len-- > 0)
12302a47d117SDavid du Colombier 		*buf++ = nrand(256);
12312a47d117SDavid du Colombier }
12322a47d117SDavid du Colombier 
12339a747e4fSDavid du Colombier /*
12349a747e4fSDavid du Colombier  *  write a block in tls records
12359a747e4fSDavid du Colombier  */
12369a747e4fSDavid du Colombier static void
tlsrecwrite(TlsRec * tr,int type,Block * b)12379a747e4fSDavid du Colombier tlsrecwrite(TlsRec *tr, int type, Block *b)
12389a747e4fSDavid du Colombier {
12399a747e4fSDavid du Colombier 	Block *volatile bb;
12409a747e4fSDavid du Colombier 	Block *nb;
12419a747e4fSDavid du Colombier 	uchar *p, seq[8];
12429a747e4fSDavid du Colombier 	OneWay *volatile out;
12432a47d117SDavid du Colombier 	int n, ivlen, maclen, pad, ok;
12449a747e4fSDavid du Colombier 
12459a747e4fSDavid du Colombier 	out = &tr->out;
12469a747e4fSDavid du Colombier 	bb = b;
12479a747e4fSDavid du Colombier 	if(waserror()){
12489a747e4fSDavid du Colombier 		qunlock(&out->io);
12499a747e4fSDavid du Colombier 		if(bb != nil)
12509a747e4fSDavid du Colombier 			freeb(bb);
12519a747e4fSDavid du Colombier 		nexterror();
12529a747e4fSDavid du Colombier 	}
12539a747e4fSDavid du Colombier 	qlock(&out->io);
1254e464ed1aSDavid du Colombier if(tr->debug)pprint("send %ld\n", BLEN(b));
12553751babcSDavid du Colombier if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
12563751babcSDavid du Colombier 
12578b74d3adSDavid du Colombier 	if(type == RApplication)
12588b74d3adSDavid du Colombier 		checkstate(tr, 0, SOpen);
12599a747e4fSDavid du Colombier 
12609a747e4fSDavid du Colombier 	ok = SHandshake|SOpen|SRClose;
12619a747e4fSDavid du Colombier 	if(type == RAlert)
12629a747e4fSDavid du Colombier 		ok |= SAlert;
12639a747e4fSDavid du Colombier 	while(bb != nil){
12649a747e4fSDavid du Colombier 		checkstate(tr, type != RApplication, ok);
12659a747e4fSDavid du Colombier 
12669a747e4fSDavid du Colombier 		/*
12679a747e4fSDavid du Colombier 		 * get at most one maximal record's input,
12689a747e4fSDavid du Colombier 		 * with padding on the front for header and
12699a747e4fSDavid du Colombier 		 * back for mac and maximal block padding.
12709a747e4fSDavid du Colombier 		 */
12719a747e4fSDavid du Colombier 		if(waserror()){
12729a747e4fSDavid du Colombier 			qunlock(&out->seclock);
12739a747e4fSDavid du Colombier 			nexterror();
12749a747e4fSDavid du Colombier 		}
12759a747e4fSDavid du Colombier 		qlock(&out->seclock);
12769a747e4fSDavid du Colombier 		maclen = 0;
12779a747e4fSDavid du Colombier 		pad = 0;
12782a47d117SDavid du Colombier 		ivlen = 0;
12799a747e4fSDavid du Colombier 		if(out->sec != nil){
12809a747e4fSDavid du Colombier 			maclen = out->sec->maclen;
12819a747e4fSDavid du Colombier 			pad = maclen + out->sec->block;
12822a47d117SDavid du Colombier 			if(tr->version >= TLS11Version)
12832a47d117SDavid du Colombier 				ivlen = out->sec->block;
12849a747e4fSDavid du Colombier 		}
12859a747e4fSDavid du Colombier 		n = BLEN(bb);
12869a747e4fSDavid du Colombier 		if(n > MaxRecLen){
12879a747e4fSDavid du Colombier 			n = MaxRecLen;
12882a47d117SDavid du Colombier 			nb = allocb(RecHdrLen + ivlen + n + pad);
12892a47d117SDavid du Colombier 			memmove(nb->wp + RecHdrLen + ivlen, bb->rp, n);
12909a747e4fSDavid du Colombier 			bb->rp += n;
12919a747e4fSDavid du Colombier 		}else{
12929a747e4fSDavid du Colombier 			/*
12939a747e4fSDavid du Colombier 			 * carefully reuse bb so it will get freed if we're out of memory
12949a747e4fSDavid du Colombier 			 */
12952a47d117SDavid du Colombier 			bb = padblock(bb, RecHdrLen + ivlen);
12969a747e4fSDavid du Colombier 			if(pad)
12979a747e4fSDavid du Colombier 				nb = padblock(bb, -pad);
12989a747e4fSDavid du Colombier 			else
12999a747e4fSDavid du Colombier 				nb = bb;
13009a747e4fSDavid du Colombier 			bb = nil;
13019a747e4fSDavid du Colombier 		}
13029a747e4fSDavid du Colombier 
13039a747e4fSDavid du Colombier 		p = nb->rp;
13049a747e4fSDavid du Colombier 		p[0] = type;
13059a747e4fSDavid du Colombier 		put16(p+1, tr->version);
13069a747e4fSDavid du Colombier 		put16(p+3, n);
13079a747e4fSDavid du Colombier 
13089a747e4fSDavid du Colombier 		if(out->sec != nil){
13099a747e4fSDavid du Colombier 			put64(seq, out->seq);
13109a747e4fSDavid du Colombier 			out->seq++;
13112a47d117SDavid du Colombier 			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen + ivlen, n, p + RecHdrLen + ivlen + n);
13129a747e4fSDavid du Colombier 			n += maclen;
13139a747e4fSDavid du Colombier 
13142a47d117SDavid du Colombier 			/* explicit iv */
13152a47d117SDavid du Colombier 			if(ivlen > 0){
13162a47d117SDavid du Colombier 				randfill(p + RecHdrLen, ivlen);
13172a47d117SDavid du Colombier 				n += ivlen;
13182a47d117SDavid du Colombier 			}
13192a47d117SDavid du Colombier 
13209a747e4fSDavid du Colombier 			/* encrypt */
13219a747e4fSDavid du Colombier 			n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
13229a747e4fSDavid du Colombier 			nb->wp = p + RecHdrLen + n;
13239a747e4fSDavid du Colombier 
13249a747e4fSDavid du Colombier 			/* update length */
13259a747e4fSDavid du Colombier 			put16(p+3, n);
13269a747e4fSDavid du Colombier 		}
13279a747e4fSDavid du Colombier 		if(type == RChangeCipherSpec){
13289a747e4fSDavid du Colombier 			if(out->new == nil)
13299a747e4fSDavid du Colombier 				error("change cipher without a new cipher");
13309a747e4fSDavid du Colombier 			freeSec(out->sec);
13319a747e4fSDavid du Colombier 			out->sec = out->new;
13329a747e4fSDavid du Colombier 			out->new = nil;
13339a747e4fSDavid du Colombier 			out->seq = 0;
13349a747e4fSDavid du Colombier 		}
13359a747e4fSDavid du Colombier 		qunlock(&out->seclock);
13369a747e4fSDavid du Colombier 		poperror();
13379a747e4fSDavid du Colombier 
13389a747e4fSDavid du Colombier 		/*
13399a747e4fSDavid du Colombier 		 * if bwrite error's, we assume the block is queued.
13409a747e4fSDavid du Colombier 		 * if not, we're out of sync with the receiver and will not recover.
13419a747e4fSDavid du Colombier 		 */
13429a747e4fSDavid du Colombier 		if(waserror()){
13439a747e4fSDavid du Colombier 			if(strcmp(up->errstr, "interrupted") != 0)
13449a747e4fSDavid du Colombier 				tlsError(tr, "channel error");
13459a747e4fSDavid du Colombier 			nexterror();
13469a747e4fSDavid du Colombier 		}
13479a747e4fSDavid du Colombier 		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
13489a747e4fSDavid du Colombier 		poperror();
13499a747e4fSDavid du Colombier 	}
13509a747e4fSDavid du Colombier 	qunlock(&out->io);
13519a747e4fSDavid du Colombier 	poperror();
13529a747e4fSDavid du Colombier }
13539a747e4fSDavid du Colombier 
13549a747e4fSDavid du Colombier static long
tlsbwrite(Chan * c,Block * b,ulong offset)13559a747e4fSDavid du Colombier tlsbwrite(Chan *c, Block *b, ulong offset)
13569a747e4fSDavid du Colombier {
13579a747e4fSDavid du Colombier 	int ty;
13589a747e4fSDavid du Colombier 	ulong n;
13599a747e4fSDavid du Colombier 	TlsRec *tr;
13609a747e4fSDavid du Colombier 
13619a747e4fSDavid du Colombier 	n = BLEN(b);
13629a747e4fSDavid du Colombier 
13639a747e4fSDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
13649a747e4fSDavid du Colombier 	if(tr == nil)
13659b7bf7dfSDavid du Colombier 		panic("tlsbwrite");
13669a747e4fSDavid du Colombier 
13679a747e4fSDavid du Colombier 	ty = TYPE(c->qid);
13689a747e4fSDavid du Colombier 	switch(ty) {
13699a747e4fSDavid du Colombier 	default:
13709a747e4fSDavid du Colombier 		return devbwrite(c, b, offset);
13719a747e4fSDavid du Colombier 	case Qhand:
13729a747e4fSDavid du Colombier 		tlsrecwrite(tr, RHandshake, b);
13739a747e4fSDavid du Colombier 		tr->handout += n;
13749a747e4fSDavid du Colombier 		break;
13759a747e4fSDavid du Colombier 	case Qdata:
13769a747e4fSDavid du Colombier 		tlsrecwrite(tr, RApplication, b);
13779a747e4fSDavid du Colombier 		tr->dataout += n;
13789a747e4fSDavid du Colombier 		break;
13799a747e4fSDavid du Colombier 	}
13809a747e4fSDavid du Colombier 
13819a747e4fSDavid du Colombier 	return n;
13829a747e4fSDavid du Colombier }
13839a747e4fSDavid du Colombier 
13849a747e4fSDavid du Colombier typedef struct Hashalg Hashalg;
13859a747e4fSDavid du Colombier struct Hashalg
13869a747e4fSDavid du Colombier {
13879a747e4fSDavid du Colombier 	char	*name;
13889a747e4fSDavid du Colombier 	int	maclen;
13899a747e4fSDavid du Colombier 	void	(*initkey)(Hashalg *, int, Secret *, uchar*);
13909a747e4fSDavid du Colombier };
13919a747e4fSDavid du Colombier 
13929a747e4fSDavid du Colombier static void
initmd5key(Hashalg * ha,int version,Secret * s,uchar * p)13939a747e4fSDavid du Colombier initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
13949a747e4fSDavid du Colombier {
13959a747e4fSDavid du Colombier 	s->maclen = ha->maclen;
13969a747e4fSDavid du Colombier 	if(version == SSL3Version)
13979a747e4fSDavid du Colombier 		s->mac = sslmac_md5;
13989a747e4fSDavid du Colombier 	else
13999a747e4fSDavid du Colombier 		s->mac = hmac_md5;
14009a747e4fSDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
14019a747e4fSDavid du Colombier }
14029a747e4fSDavid du Colombier 
14039a747e4fSDavid du Colombier static void
initclearmac(Hashalg *,int,Secret * s,uchar *)14049a747e4fSDavid du Colombier initclearmac(Hashalg *, int, Secret *s, uchar *)
14059a747e4fSDavid du Colombier {
14069a747e4fSDavid du Colombier 	s->maclen = 0;
14079a747e4fSDavid du Colombier 	s->mac = nomac;
14089a747e4fSDavid du Colombier }
14099a747e4fSDavid du Colombier 
14109a747e4fSDavid du Colombier static void
initsha1key(Hashalg * ha,int version,Secret * s,uchar * p)14119a747e4fSDavid du Colombier initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
14129a747e4fSDavid du Colombier {
14139a747e4fSDavid du Colombier 	s->maclen = ha->maclen;
14149a747e4fSDavid du Colombier 	if(version == SSL3Version)
14159a747e4fSDavid du Colombier 		s->mac = sslmac_sha1;
14169a747e4fSDavid du Colombier 	else
14179a747e4fSDavid du Colombier 		s->mac = hmac_sha1;
14189a747e4fSDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
14199a747e4fSDavid du Colombier }
14209a747e4fSDavid du Colombier 
14212a47d117SDavid du Colombier static void
initsha2_256key(Hashalg * ha,int version,Secret * s,uchar * p)14222a47d117SDavid du Colombier initsha2_256key(Hashalg *ha, int version, Secret *s, uchar *p)
14232a47d117SDavid du Colombier {
14242a47d117SDavid du Colombier 	if(version == SSL3Version)
14252a47d117SDavid du Colombier 		error("sha256 cannot be used with SSL");
14262a47d117SDavid du Colombier 	s->maclen = ha->maclen;
14272a47d117SDavid du Colombier 	s->mac = hmac_sha2_256;
14282a47d117SDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
14292a47d117SDavid du Colombier }
14302a47d117SDavid du Colombier 
14319a747e4fSDavid du Colombier static Hashalg hashtab[] =
14329a747e4fSDavid du Colombier {
14339a747e4fSDavid du Colombier 	{ "clear", 0, initclearmac, },
14349a747e4fSDavid du Colombier 	{ "md5", MD5dlen, initmd5key, },
14359a747e4fSDavid du Colombier 	{ "sha1", SHA1dlen, initsha1key, },
14362a47d117SDavid du Colombier 	{ "sha256", SHA2_256dlen, initsha2_256key, },
14379a747e4fSDavid du Colombier 	{ 0 }
14389a747e4fSDavid du Colombier };
14399a747e4fSDavid du Colombier 
14409a747e4fSDavid du Colombier static Hashalg*
parsehashalg(char * p)14419a747e4fSDavid du Colombier parsehashalg(char *p)
14429a747e4fSDavid du Colombier {
14439a747e4fSDavid du Colombier 	Hashalg *ha;
14449a747e4fSDavid du Colombier 
14459a747e4fSDavid du Colombier 	for(ha = hashtab; ha->name; ha++)
14469a747e4fSDavid du Colombier 		if(strcmp(p, ha->name) == 0)
14479a747e4fSDavid du Colombier 			return ha;
14489a747e4fSDavid du Colombier 	error("unsupported hash algorithm");
14499a747e4fSDavid du Colombier 	return nil;
14509a747e4fSDavid du Colombier }
14519a747e4fSDavid du Colombier 
14529a747e4fSDavid du Colombier typedef struct Encalg Encalg;
14539a747e4fSDavid du Colombier struct Encalg
14549a747e4fSDavid du Colombier {
14559a747e4fSDavid du Colombier 	char	*name;
14569a747e4fSDavid du Colombier 	int	keylen;
14579a747e4fSDavid du Colombier 	int	ivlen;
14589a747e4fSDavid du Colombier 	void	(*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
14599a747e4fSDavid du Colombier };
14609a747e4fSDavid du Colombier 
14619a747e4fSDavid du Colombier static void
initRC4key(Encalg * ea,Secret * s,uchar * p,uchar *)14629a747e4fSDavid du Colombier initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
14639a747e4fSDavid du Colombier {
14649a747e4fSDavid du Colombier 	s->enckey = smalloc(sizeof(RC4state));
14659a747e4fSDavid du Colombier 	s->enc = rc4enc;
14669a747e4fSDavid du Colombier 	s->dec = rc4enc;
14679a747e4fSDavid du Colombier 	s->block = 0;
14689a747e4fSDavid du Colombier 	setupRC4state(s->enckey, p, ea->keylen);
14699a747e4fSDavid du Colombier }
14709a747e4fSDavid du Colombier 
14719a747e4fSDavid du Colombier static void
initDES3key(Encalg *,Secret * s,uchar * p,uchar * iv)14729a747e4fSDavid du Colombier initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv)
14739a747e4fSDavid du Colombier {
14749a747e4fSDavid du Colombier 	s->enckey = smalloc(sizeof(DES3state));
14759a747e4fSDavid du Colombier 	s->enc = des3enc;
14769a747e4fSDavid du Colombier 	s->dec = des3dec;
14779a747e4fSDavid du Colombier 	s->block = 8;
14789a747e4fSDavid du Colombier 	setupDES3state(s->enckey, (uchar(*)[8])p, iv);
14799a747e4fSDavid du Colombier }
14809a747e4fSDavid du Colombier 
14819a747e4fSDavid du Colombier static void
initAESkey(Encalg * ea,Secret * s,uchar * p,uchar * iv)1482ad6ca847SDavid du Colombier initAESkey(Encalg *ea, Secret *s, uchar *p, uchar *iv)
1483ad6ca847SDavid du Colombier {
1484ad6ca847SDavid du Colombier 	s->enckey = smalloc(sizeof(AESstate));
1485ad6ca847SDavid du Colombier 	s->enc = aesenc;
1486ad6ca847SDavid du Colombier 	s->dec = aesdec;
1487ad6ca847SDavid du Colombier 	s->block = 16;
1488ad6ca847SDavid du Colombier 	setupAESstate(s->enckey, p, ea->keylen, iv);
1489ad6ca847SDavid du Colombier }
1490ad6ca847SDavid du Colombier 
1491ad6ca847SDavid du Colombier static void
initclearenc(Encalg *,Secret * s,uchar *,uchar *)14929a747e4fSDavid du Colombier initclearenc(Encalg *, Secret *s, uchar *, uchar *)
14939a747e4fSDavid du Colombier {
14949a747e4fSDavid du Colombier 	s->enc = noenc;
14959a747e4fSDavid du Colombier 	s->dec = noenc;
14969a747e4fSDavid du Colombier 	s->block = 0;
14979a747e4fSDavid du Colombier }
14989a747e4fSDavid du Colombier 
14999a747e4fSDavid du Colombier static Encalg encrypttab[] =
15009a747e4fSDavid du Colombier {
15019a747e4fSDavid du Colombier 	{ "clear", 0, 0, initclearenc },
15029a747e4fSDavid du Colombier 	{ "rc4_128", 128/8, 0, initRC4key },
15039a747e4fSDavid du Colombier 	{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
1504ad6ca847SDavid du Colombier 	{ "aes_128_cbc", 128/8, 16, initAESkey },
1505ad6ca847SDavid du Colombier 	{ "aes_256_cbc", 256/8, 16, initAESkey },
15069a747e4fSDavid du Colombier 	{ 0 }
15079a747e4fSDavid du Colombier };
15089a747e4fSDavid du Colombier 
15099a747e4fSDavid du Colombier static Encalg*
parseencalg(char * p)15109a747e4fSDavid du Colombier parseencalg(char *p)
15119a747e4fSDavid du Colombier {
15129a747e4fSDavid du Colombier 	Encalg *ea;
15139a747e4fSDavid du Colombier 
15149a747e4fSDavid du Colombier 	for(ea = encrypttab; ea->name; ea++)
15159a747e4fSDavid du Colombier 		if(strcmp(p, ea->name) == 0)
15169a747e4fSDavid du Colombier 			return ea;
15179a747e4fSDavid du Colombier 	error("unsupported encryption algorithm");
15189a747e4fSDavid du Colombier 	return nil;
15199a747e4fSDavid du Colombier }
15209a747e4fSDavid du Colombier 
15219a747e4fSDavid du Colombier static long
tlswrite(Chan * c,void * a,long n,vlong off)15229a747e4fSDavid du Colombier tlswrite(Chan *c, void *a, long n, vlong off)
15239a747e4fSDavid du Colombier {
15249a747e4fSDavid du Colombier 	Encalg *ea;
15259a747e4fSDavid du Colombier 	Hashalg *ha;
15269a747e4fSDavid du Colombier 	TlsRec *volatile tr;
15279a747e4fSDavid du Colombier 	Secret *volatile tos, *volatile toc;
15289a747e4fSDavid du Colombier 	Block *volatile b;
15299a747e4fSDavid du Colombier 	Cmdbuf *volatile cb;
15309a747e4fSDavid du Colombier 	int m, ty;
15319a747e4fSDavid du Colombier 	char *p, *e;
15329a747e4fSDavid du Colombier 	uchar *volatile x;
15339a747e4fSDavid du Colombier 	ulong offset = off;
15349a747e4fSDavid du Colombier 
15359a747e4fSDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
15369a747e4fSDavid du Colombier 	if(tr == nil)
15379a747e4fSDavid du Colombier 		panic("tlswrite");
15389a747e4fSDavid du Colombier 
15399a747e4fSDavid du Colombier 	ty = TYPE(c->qid);
15409a747e4fSDavid du Colombier 	switch(ty){
15419a747e4fSDavid du Colombier 	case Qdata:
15429a747e4fSDavid du Colombier 	case Qhand:
15439a747e4fSDavid du Colombier 		p = a;
15449a747e4fSDavid du Colombier 		e = p + n;
15459a747e4fSDavid du Colombier 		do{
15469a747e4fSDavid du Colombier 			m = e - p;
15479a747e4fSDavid du Colombier 			if(m > MaxRecLen)
15489a747e4fSDavid du Colombier 				m = MaxRecLen;
15499a747e4fSDavid du Colombier 
15509a747e4fSDavid du Colombier 			b = allocb(m);
15519a747e4fSDavid du Colombier 			if(waserror()){
15529a747e4fSDavid du Colombier 				freeb(b);
15539a747e4fSDavid du Colombier 				nexterror();
15549a747e4fSDavid du Colombier 			}
15559a747e4fSDavid du Colombier 			memmove(b->wp, p, m);
15569a747e4fSDavid du Colombier 			poperror();
15579a747e4fSDavid du Colombier 			b->wp += m;
15589a747e4fSDavid du Colombier 
15599a747e4fSDavid du Colombier 			tlsbwrite(c, b, offset);
15609a747e4fSDavid du Colombier 
15619a747e4fSDavid du Colombier 			p += m;
15629a747e4fSDavid du Colombier 		}while(p < e);
15639a747e4fSDavid du Colombier 		return n;
15649a747e4fSDavid du Colombier 	case Qctl:
15659a747e4fSDavid du Colombier 		break;
15669a747e4fSDavid du Colombier 	default:
15679a747e4fSDavid du Colombier 		error(Ebadusefd);
15689a747e4fSDavid du Colombier 		return -1;
15699a747e4fSDavid du Colombier 	}
15709a747e4fSDavid du Colombier 
15719a747e4fSDavid du Colombier 	cb = parsecmd(a, n);
15729a747e4fSDavid du Colombier 	if(waserror()){
15739a747e4fSDavid du Colombier 		free(cb);
15749a747e4fSDavid du Colombier 		nexterror();
15759a747e4fSDavid du Colombier 	}
15769a747e4fSDavid du Colombier 	if(cb->nf < 1)
15779a747e4fSDavid du Colombier 		error("short control request");
15789a747e4fSDavid du Colombier 
15799a747e4fSDavid du Colombier 	/* mutex with operations using what we're about to change */
15809a747e4fSDavid du Colombier 	if(waserror()){
15819a747e4fSDavid du Colombier 		qunlock(&tr->in.seclock);
15829a747e4fSDavid du Colombier 		qunlock(&tr->out.seclock);
15839a747e4fSDavid du Colombier 		nexterror();
15849a747e4fSDavid du Colombier 	}
15859a747e4fSDavid du Colombier 	qlock(&tr->in.seclock);
15869a747e4fSDavid du Colombier 	qlock(&tr->out.seclock);
15879a747e4fSDavid du Colombier 
15889a747e4fSDavid du Colombier 	if(strcmp(cb->f[0], "fd") == 0){
15899a747e4fSDavid du Colombier 		if(cb->nf != 3)
15909a747e4fSDavid du Colombier 			error("usage: fd open-fd version");
15919a747e4fSDavid du Colombier 		if(tr->c != nil)
15929a747e4fSDavid du Colombier 			error(Einuse);
15939a747e4fSDavid du Colombier 		m = strtol(cb->f[2], nil, 0);
15949a747e4fSDavid du Colombier 		if(m < MinProtoVersion || m > MaxProtoVersion)
15959a747e4fSDavid du Colombier 			error("unsupported version");
15969a747e4fSDavid du Colombier 		tr->c = buftochan(cb->f[1]);
15979a747e4fSDavid du Colombier 		tr->version = m;
15989a747e4fSDavid du Colombier 		tlsSetState(tr, SHandshake, SClosed);
15999a747e4fSDavid du Colombier 	}else if(strcmp(cb->f[0], "version") == 0){
16009a747e4fSDavid du Colombier 		if(cb->nf != 2)
16019a747e4fSDavid du Colombier 			error("usage: version vers");
16029a747e4fSDavid du Colombier 		if(tr->c == nil)
16039a747e4fSDavid du Colombier 			error("must set fd before version");
16049a747e4fSDavid du Colombier 		if(tr->verset)
16059a747e4fSDavid du Colombier 			error("version already set");
16069a747e4fSDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
16072a47d117SDavid du Colombier 		if(m < MinProtoVersion || m > MaxProtoVersion)
16082a47d117SDavid du Colombier 			error("unsupported version");
16099a747e4fSDavid du Colombier 		if(m == SSL3Version)
16109a747e4fSDavid du Colombier 			tr->packMac = sslPackMac;
16119a747e4fSDavid du Colombier 		else
16122a47d117SDavid du Colombier 			tr->packMac = tlsPackMac;
16139a747e4fSDavid du Colombier 		tr->verset = 1;
16149a747e4fSDavid du Colombier 		tr->version = m;
16159a747e4fSDavid du Colombier 	}else if(strcmp(cb->f[0], "secret") == 0){
16169a747e4fSDavid du Colombier 		if(cb->nf != 5)
16179a747e4fSDavid du Colombier 			error("usage: secret hashalg encalg isclient secretdata");
16189a747e4fSDavid du Colombier 		if(tr->c == nil || !tr->verset)
16199a747e4fSDavid du Colombier 			error("must set fd and version before secrets");
16209a747e4fSDavid du Colombier 
16219a747e4fSDavid du Colombier 		if(tr->in.new != nil){
16229a747e4fSDavid du Colombier 			freeSec(tr->in.new);
16239a747e4fSDavid du Colombier 			tr->in.new = nil;
16249a747e4fSDavid du Colombier 		}
16259a747e4fSDavid du Colombier 		if(tr->out.new != nil){
16269a747e4fSDavid du Colombier 			freeSec(tr->out.new);
16279a747e4fSDavid du Colombier 			tr->out.new = nil;
16289a747e4fSDavid du Colombier 		}
16299a747e4fSDavid du Colombier 
16309a747e4fSDavid du Colombier 		ha = parsehashalg(cb->f[1]);
16319a747e4fSDavid du Colombier 		ea = parseencalg(cb->f[2]);
16329a747e4fSDavid du Colombier 
16339a747e4fSDavid du Colombier 		p = cb->f[4];
16349a747e4fSDavid du Colombier 		m = (strlen(p)*3)/2;
16359a747e4fSDavid du Colombier 		x = smalloc(m);
16369a747e4fSDavid du Colombier 		tos = nil;
16379a747e4fSDavid du Colombier 		toc = nil;
16389a747e4fSDavid du Colombier 		if(waserror()){
16399a747e4fSDavid du Colombier 			freeSec(tos);
16409a747e4fSDavid du Colombier 			freeSec(toc);
16419a747e4fSDavid du Colombier 			free(x);
16429a747e4fSDavid du Colombier 			nexterror();
16439a747e4fSDavid du Colombier 		}
16449a747e4fSDavid du Colombier 		m = dec64(x, m, p, strlen(p));
16459a747e4fSDavid du Colombier 		if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
16469a747e4fSDavid du Colombier 			error("not enough secret data provided");
16479a747e4fSDavid du Colombier 
16489a747e4fSDavid du Colombier 		tos = smalloc(sizeof(Secret));
16499a747e4fSDavid du Colombier 		toc = smalloc(sizeof(Secret));
16509a747e4fSDavid du Colombier 		if(!ha->initkey || !ea->initkey)
16519a747e4fSDavid du Colombier 			error("misimplemented secret algorithm");
16529a747e4fSDavid du Colombier 		(*ha->initkey)(ha, tr->version, tos, &x[0]);
16539a747e4fSDavid du Colombier 		(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
16549a747e4fSDavid du Colombier 		(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
16559a747e4fSDavid du Colombier 		(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
16569a747e4fSDavid du Colombier 
16579a747e4fSDavid du Colombier 		if(!tos->mac || !tos->enc || !tos->dec
16589a747e4fSDavid du Colombier 		|| !toc->mac || !toc->enc || !toc->dec)
16599a747e4fSDavid du Colombier 			error("missing algorithm implementations");
16609a747e4fSDavid du Colombier 		if(strtol(cb->f[3], nil, 0) == 0){
16619a747e4fSDavid du Colombier 			tr->in.new = tos;
16629a747e4fSDavid du Colombier 			tr->out.new = toc;
16639a747e4fSDavid du Colombier 		}else{
16649a747e4fSDavid du Colombier 			tr->in.new = toc;
16659a747e4fSDavid du Colombier 			tr->out.new = tos;
16669a747e4fSDavid du Colombier 		}
16679a747e4fSDavid du Colombier 		if(tr->version == SSL3Version){
16689a747e4fSDavid du Colombier 			toc->unpad = sslunpad;
16699a747e4fSDavid du Colombier 			tos->unpad = sslunpad;
16709a747e4fSDavid du Colombier 		}else{
16719a747e4fSDavid du Colombier 			toc->unpad = tlsunpad;
16729a747e4fSDavid du Colombier 			tos->unpad = tlsunpad;
16739a747e4fSDavid du Colombier 		}
16749a747e4fSDavid du Colombier 		toc->encalg = ea->name;
16759a747e4fSDavid du Colombier 		toc->hashalg = ha->name;
16769a747e4fSDavid du Colombier 		tos->encalg = ea->name;
16779a747e4fSDavid du Colombier 		tos->hashalg = ha->name;
16789a747e4fSDavid du Colombier 
16799a747e4fSDavid du Colombier 		free(x);
16809a747e4fSDavid du Colombier 		poperror();
16819a747e4fSDavid du Colombier 	}else if(strcmp(cb->f[0], "changecipher") == 0){
16829a747e4fSDavid du Colombier 		if(cb->nf != 1)
16839a747e4fSDavid du Colombier 			error("usage: changecipher");
16849a747e4fSDavid du Colombier 		if(tr->out.new == nil)
16858cd4f5a6SDavid du Colombier 			error("cannot change cipher spec without setting secret");
16869a747e4fSDavid du Colombier 
16879a747e4fSDavid du Colombier 		qunlock(&tr->in.seclock);
16889a747e4fSDavid du Colombier 		qunlock(&tr->out.seclock);
16899a747e4fSDavid du Colombier 		poperror();
16909a747e4fSDavid du Colombier 		free(cb);
16919a747e4fSDavid du Colombier 		poperror();
16929a747e4fSDavid du Colombier 
16939a747e4fSDavid du Colombier 		/*
16949a747e4fSDavid du Colombier 		 * the real work is done as the message is written
16959a747e4fSDavid du Colombier 		 * so the stream is encrypted in sync.
16969a747e4fSDavid du Colombier 		 */
16979a747e4fSDavid du Colombier 		b = allocb(1);
16989a747e4fSDavid du Colombier 		*b->wp++ = 1;
16999a747e4fSDavid du Colombier 		tlsrecwrite(tr, RChangeCipherSpec, b);
17009a747e4fSDavid du Colombier 		return n;
17019a747e4fSDavid du Colombier 	}else if(strcmp(cb->f[0], "opened") == 0){
17029a747e4fSDavid du Colombier 		if(cb->nf != 1)
17039a747e4fSDavid du Colombier 			error("usage: opened");
17049a747e4fSDavid du Colombier 		if(tr->in.sec == nil || tr->out.sec == nil)
17059a747e4fSDavid du Colombier 			error("cipher must be configured before enabling data messages");
17069a747e4fSDavid du Colombier 		lock(&tr->statelk);
17079a747e4fSDavid du Colombier 		if(tr->state != SHandshake && tr->state != SOpen){
17089a747e4fSDavid du Colombier 			unlock(&tr->statelk);
17098cd4f5a6SDavid du Colombier 			error("cannot enable data messages");
17109a747e4fSDavid du Colombier 		}
17119a747e4fSDavid du Colombier 		tr->state = SOpen;
17129a747e4fSDavid du Colombier 		unlock(&tr->statelk);
17139a747e4fSDavid du Colombier 		tr->opened = 1;
17149a747e4fSDavid du Colombier 	}else if(strcmp(cb->f[0], "alert") == 0){
17159a747e4fSDavid du Colombier 		if(cb->nf != 2)
17169a747e4fSDavid du Colombier 			error("usage: alert n");
17179a747e4fSDavid du Colombier 		if(tr->c == nil)
17189a747e4fSDavid du Colombier 			error("must set fd before sending alerts");
17199a747e4fSDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
17209a747e4fSDavid du Colombier 
17219a747e4fSDavid du Colombier 		qunlock(&tr->in.seclock);
17229a747e4fSDavid du Colombier 		qunlock(&tr->out.seclock);
17239a747e4fSDavid du Colombier 		poperror();
17249a747e4fSDavid du Colombier 		free(cb);
17259a747e4fSDavid du Colombier 		poperror();
17269a747e4fSDavid du Colombier 
17279a747e4fSDavid du Colombier 		sendAlert(tr, m);
17289a747e4fSDavid du Colombier 
17299a747e4fSDavid du Colombier 		if(m == ECloseNotify)
17309a747e4fSDavid du Colombier 			tlsclosed(tr, SLClose);
17319a747e4fSDavid du Colombier 
17329a747e4fSDavid du Colombier 		return n;
17333751babcSDavid du Colombier 	} else if(strcmp(cb->f[0], "debug") == 0){
17343751babcSDavid du Colombier 		if(cb->nf == 2){
17353751babcSDavid du Colombier 			if(strcmp(cb->f[1], "on") == 0)
17363751babcSDavid du Colombier 				tr->debug = 1;
17373751babcSDavid du Colombier 			else
17383751babcSDavid du Colombier 				tr->debug = 0;
17393751babcSDavid du Colombier 		} else
17403751babcSDavid du Colombier 			tr->debug = 1;
17419a747e4fSDavid du Colombier 	} else
17429a747e4fSDavid du Colombier 		error(Ebadarg);
17439a747e4fSDavid du Colombier 
17449a747e4fSDavid du Colombier 	qunlock(&tr->in.seclock);
17459a747e4fSDavid du Colombier 	qunlock(&tr->out.seclock);
17469a747e4fSDavid du Colombier 	poperror();
17479a747e4fSDavid du Colombier 	free(cb);
17489a747e4fSDavid du Colombier 	poperror();
17499a747e4fSDavid du Colombier 
17509a747e4fSDavid du Colombier 	return n;
17519a747e4fSDavid du Colombier }
17529a747e4fSDavid du Colombier 
17539a747e4fSDavid du Colombier static void
tlsinit(void)17549a747e4fSDavid du Colombier tlsinit(void)
17559a747e4fSDavid du Colombier {
17569a747e4fSDavid du Colombier 	struct Encalg *e;
17579a747e4fSDavid du Colombier 	struct Hashalg *h;
17589a747e4fSDavid du Colombier 	int n;
17599a747e4fSDavid du Colombier 	char *cp;
17603751babcSDavid du Colombier 	static int already;
17613751babcSDavid du Colombier 
17623751babcSDavid du Colombier 	if(!already){
17633751babcSDavid du Colombier 		fmtinstall('H', encodefmt);
17643751babcSDavid du Colombier 		already = 1;
17653751babcSDavid du Colombier 	}
17669a747e4fSDavid du Colombier 
17679a747e4fSDavid du Colombier 	tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
17689a747e4fSDavid du Colombier 	trnames = smalloc((sizeof *trnames) * maxtlsdevs);
17699a747e4fSDavid du Colombier 
17709a747e4fSDavid du Colombier 	n = 1;
17719a747e4fSDavid du Colombier 	for(e = encrypttab; e->name != nil; e++)
17729a747e4fSDavid du Colombier 		n += strlen(e->name) + 1;
17739a747e4fSDavid du Colombier 	cp = encalgs = smalloc(n);
17749a747e4fSDavid du Colombier 	for(e = encrypttab;;){
17759a747e4fSDavid du Colombier 		strcpy(cp, e->name);
17769a747e4fSDavid du Colombier 		cp += strlen(e->name);
17779a747e4fSDavid du Colombier 		e++;
17789a747e4fSDavid du Colombier 		if(e->name == nil)
17799a747e4fSDavid du Colombier 			break;
17809a747e4fSDavid du Colombier 		*cp++ = ' ';
17819a747e4fSDavid du Colombier 	}
17829a747e4fSDavid du Colombier 	*cp = 0;
17839a747e4fSDavid du Colombier 
17849a747e4fSDavid du Colombier 	n = 1;
17859a747e4fSDavid du Colombier 	for(h = hashtab; h->name != nil; h++)
17869a747e4fSDavid du Colombier 		n += strlen(h->name) + 1;
17879a747e4fSDavid du Colombier 	cp = hashalgs = smalloc(n);
17889a747e4fSDavid du Colombier 	for(h = hashtab;;){
17899a747e4fSDavid du Colombier 		strcpy(cp, h->name);
17909a747e4fSDavid du Colombier 		cp += strlen(h->name);
17919a747e4fSDavid du Colombier 		h++;
17929a747e4fSDavid du Colombier 		if(h->name == nil)
17939a747e4fSDavid du Colombier 			break;
17949a747e4fSDavid du Colombier 		*cp++ = ' ';
17959a747e4fSDavid du Colombier 	}
17969a747e4fSDavid du Colombier 	*cp = 0;
17979a747e4fSDavid du Colombier }
17989a747e4fSDavid du Colombier 
17999a747e4fSDavid du Colombier Dev tlsdevtab = {
18009a747e4fSDavid du Colombier 	'a',
18019a747e4fSDavid du Colombier 	"tls",
18029a747e4fSDavid du Colombier 
18039a747e4fSDavid du Colombier 	devreset,
18049a747e4fSDavid du Colombier 	tlsinit,
18059a747e4fSDavid du Colombier 	devshutdown,
18069a747e4fSDavid du Colombier 	tlsattach,
18079a747e4fSDavid du Colombier 	tlswalk,
18089a747e4fSDavid du Colombier 	tlsstat,
18099a747e4fSDavid du Colombier 	tlsopen,
18109a747e4fSDavid du Colombier 	devcreate,
18119a747e4fSDavid du Colombier 	tlsclose,
18129a747e4fSDavid du Colombier 	tlsread,
18139a747e4fSDavid du Colombier 	tlsbread,
18149a747e4fSDavid du Colombier 	tlswrite,
18159a747e4fSDavid du Colombier 	tlsbwrite,
18169a747e4fSDavid du Colombier 	devremove,
18179a747e4fSDavid du Colombier 	tlswstat,
18189a747e4fSDavid du Colombier };
18199a747e4fSDavid du Colombier 
18209a747e4fSDavid du Colombier /* get channel associated with an fd */
18219a747e4fSDavid du Colombier static Chan*
buftochan(char * p)18229a747e4fSDavid du Colombier buftochan(char *p)
18239a747e4fSDavid du Colombier {
18249a747e4fSDavid du Colombier 	Chan *c;
18259a747e4fSDavid du Colombier 	int fd;
18269a747e4fSDavid du Colombier 
18279a747e4fSDavid du Colombier 	if(p == 0)
18289a747e4fSDavid du Colombier 		error(Ebadarg);
18299a747e4fSDavid du Colombier 	fd = strtoul(p, 0, 0);
18309a747e4fSDavid du Colombier 	if(fd < 0)
18319a747e4fSDavid du Colombier 		error(Ebadarg);
18329a747e4fSDavid du Colombier 	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
18339a747e4fSDavid du Colombier 	return c;
18349a747e4fSDavid du Colombier }
18359a747e4fSDavid du Colombier 
18369a747e4fSDavid du Colombier static void
sendAlert(TlsRec * tr,int err)18379a747e4fSDavid du Colombier sendAlert(TlsRec *tr, int err)
18389a747e4fSDavid du Colombier {
18399a747e4fSDavid du Colombier 	Block *b;
18409a747e4fSDavid du Colombier 	int i, fatal;
18419a747e4fSDavid du Colombier 	char *msg;
18429a747e4fSDavid du Colombier 
18433751babcSDavid du Colombier if(tr->debug)pprint("sendAlert %d\n", err);
18449a747e4fSDavid du Colombier 	fatal = 1;
18459a747e4fSDavid du Colombier 	msg = "tls unknown alert";
18469a747e4fSDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++) {
18479a747e4fSDavid du Colombier 		if(tlserrs[i].err == err) {
18489a747e4fSDavid du Colombier 			msg = tlserrs[i].msg;
18499a747e4fSDavid du Colombier 			if(tr->version == SSL3Version)
18509a747e4fSDavid du Colombier 				err = tlserrs[i].sslerr;
18519a747e4fSDavid du Colombier 			else
18529a747e4fSDavid du Colombier 				err = tlserrs[i].tlserr;
18539a747e4fSDavid du Colombier 			fatal = tlserrs[i].fatal;
18549a747e4fSDavid du Colombier 			break;
18559a747e4fSDavid du Colombier 		}
18569a747e4fSDavid du Colombier 	}
18579a747e4fSDavid du Colombier 
18589a747e4fSDavid du Colombier 	if(!waserror()){
18599a747e4fSDavid du Colombier 		b = allocb(2);
18609a747e4fSDavid du Colombier 		*b->wp++ = fatal + 1;
18619a747e4fSDavid du Colombier 		*b->wp++ = err;
18629a747e4fSDavid du Colombier 		if(fatal)
18639a747e4fSDavid du Colombier 			tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
18649a747e4fSDavid du Colombier 		tlsrecwrite(tr, RAlert, b);
18659a747e4fSDavid du Colombier 		poperror();
18669a747e4fSDavid du Colombier 	}
18679a747e4fSDavid du Colombier 	if(fatal)
18689a747e4fSDavid du Colombier 		tlsError(tr, msg);
18699a747e4fSDavid du Colombier }
18709a747e4fSDavid du Colombier 
18719a747e4fSDavid du Colombier static void
tlsError(TlsRec * tr,char * msg)18729a747e4fSDavid du Colombier tlsError(TlsRec *tr, char *msg)
18739a747e4fSDavid du Colombier {
18749a747e4fSDavid du Colombier 	int s;
18759a747e4fSDavid du Colombier 
18763751babcSDavid du Colombier if(tr->debug)pprint("tleError %s\n", msg);
18779a747e4fSDavid du Colombier 	lock(&tr->statelk);
18789a747e4fSDavid du Colombier 	s = tr->state;
18799a747e4fSDavid du Colombier 	tr->state = SError;
18809a747e4fSDavid du Colombier 	if(s != SError){
18819a747e4fSDavid du Colombier 		strncpy(tr->err, msg, ERRMAX - 1);
18829a747e4fSDavid du Colombier 		tr->err[ERRMAX - 1] = '\0';
18839a747e4fSDavid du Colombier 	}
18849a747e4fSDavid du Colombier 	unlock(&tr->statelk);
18859a747e4fSDavid du Colombier 	if(s != SError)
18869a747e4fSDavid du Colombier 		alertHand(tr, msg);
18879a747e4fSDavid du Colombier }
18889a747e4fSDavid du Colombier 
18899a747e4fSDavid du Colombier static void
tlsSetState(TlsRec * tr,int new,int old)18909a747e4fSDavid du Colombier tlsSetState(TlsRec *tr, int new, int old)
18919a747e4fSDavid du Colombier {
18929a747e4fSDavid du Colombier 	lock(&tr->statelk);
18939a747e4fSDavid du Colombier 	if(tr->state & old)
18949a747e4fSDavid du Colombier 		tr->state = new;
18959a747e4fSDavid du Colombier 	unlock(&tr->statelk);
18969a747e4fSDavid du Colombier }
18979a747e4fSDavid du Colombier 
18989a747e4fSDavid du Colombier /* hand up a digest connection */
18999a747e4fSDavid du Colombier static void
tlshangup(TlsRec * tr)19009a747e4fSDavid du Colombier tlshangup(TlsRec *tr)
19019a747e4fSDavid du Colombier {
19029a747e4fSDavid du Colombier 	Block *b;
19039a747e4fSDavid du Colombier 
19049a747e4fSDavid du Colombier 	qlock(&tr->in.io);
19059a747e4fSDavid du Colombier 	for(b = tr->processed; b; b = tr->processed){
19069a747e4fSDavid du Colombier 		tr->processed = b->next;
19079a747e4fSDavid du Colombier 		freeb(b);
19089a747e4fSDavid du Colombier 	}
19099a747e4fSDavid du Colombier 	if(tr->unprocessed != nil){
19109a747e4fSDavid du Colombier 		freeb(tr->unprocessed);
19119a747e4fSDavid du Colombier 		tr->unprocessed = nil;
19129a747e4fSDavid du Colombier 	}
19139a747e4fSDavid du Colombier 	qunlock(&tr->in.io);
19149a747e4fSDavid du Colombier 
19159a747e4fSDavid du Colombier 	tlsSetState(tr, SClosed, ~0);
19169a747e4fSDavid du Colombier }
19179a747e4fSDavid du Colombier 
19189a747e4fSDavid du Colombier static TlsRec*
newtls(Chan * ch)19199a747e4fSDavid du Colombier newtls(Chan *ch)
19209a747e4fSDavid du Colombier {
19219a747e4fSDavid du Colombier 	TlsRec **pp, **ep, **np;
19229a747e4fSDavid du Colombier 	char **nmp;
19239a747e4fSDavid du Colombier 	int t, newmax;
19249a747e4fSDavid du Colombier 
19259a747e4fSDavid du Colombier 	if(waserror()) {
19269a747e4fSDavid du Colombier 		unlock(&tdlock);
19279a747e4fSDavid du Colombier 		nexterror();
19289a747e4fSDavid du Colombier 	}
19299a747e4fSDavid du Colombier 	lock(&tdlock);
19309a747e4fSDavid du Colombier 	ep = &tlsdevs[maxtlsdevs];
19319a747e4fSDavid du Colombier 	for(pp = tlsdevs; pp < ep; pp++)
19329a747e4fSDavid du Colombier 		if(*pp == nil)
19339a747e4fSDavid du Colombier 			break;
19349a747e4fSDavid du Colombier 	if(pp >= ep) {
19359a747e4fSDavid du Colombier 		if(maxtlsdevs >= MaxTlsDevs) {
19369a747e4fSDavid du Colombier 			unlock(&tdlock);
19379a747e4fSDavid du Colombier 			poperror();
19389a747e4fSDavid du Colombier 			return nil;
19399a747e4fSDavid du Colombier 		}
19409a747e4fSDavid du Colombier 		newmax = 2 * maxtlsdevs;
19419a747e4fSDavid du Colombier 		if(newmax > MaxTlsDevs)
19429a747e4fSDavid du Colombier 			newmax = MaxTlsDevs;
19439a747e4fSDavid du Colombier 		np = smalloc(sizeof(TlsRec*) * newmax);
19449a747e4fSDavid du Colombier 		memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
19459a747e4fSDavid du Colombier 		tlsdevs = np;
19469a747e4fSDavid du Colombier 		pp = &tlsdevs[maxtlsdevs];
19479a747e4fSDavid du Colombier 		memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
19489a747e4fSDavid du Colombier 
19499a747e4fSDavid du Colombier 		nmp = smalloc(sizeof *nmp * newmax);
19509a747e4fSDavid du Colombier 		memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
19519a747e4fSDavid du Colombier 		trnames = nmp;
19529a747e4fSDavid du Colombier 
19539a747e4fSDavid du Colombier 		maxtlsdevs = newmax;
19549a747e4fSDavid du Colombier 	}
19559a747e4fSDavid du Colombier 	*pp = mktlsrec();
19569a747e4fSDavid du Colombier 	if(pp - tlsdevs >= tdhiwat)
19579a747e4fSDavid du Colombier 		tdhiwat++;
19589a747e4fSDavid du Colombier 	t = TYPE(ch->qid);
19599a747e4fSDavid du Colombier 	if(t == Qclonus)
19609a747e4fSDavid du Colombier 		t = Qctl;
19619a747e4fSDavid du Colombier 	ch->qid.path = QID(pp - tlsdevs, t);
19629a747e4fSDavid du Colombier 	ch->qid.vers = 0;
19639a747e4fSDavid du Colombier 	unlock(&tdlock);
19649a747e4fSDavid du Colombier 	poperror();
19659a747e4fSDavid du Colombier 	return *pp;
19669a747e4fSDavid du Colombier }
19679a747e4fSDavid du Colombier 
19689a747e4fSDavid du Colombier static TlsRec *
mktlsrec(void)19699a747e4fSDavid du Colombier mktlsrec(void)
19709a747e4fSDavid du Colombier {
19719a747e4fSDavid du Colombier 	TlsRec *tr;
19729a747e4fSDavid du Colombier 
19739a747e4fSDavid du Colombier 	tr = mallocz(sizeof(*tr), 1);
19749a747e4fSDavid du Colombier 	if(tr == nil)
19759a747e4fSDavid du Colombier 		error(Enomem);
19769a747e4fSDavid du Colombier 	tr->state = SClosed;
19779a747e4fSDavid du Colombier 	tr->ref = 1;
19789a747e4fSDavid du Colombier 	kstrdup(&tr->user, up->user);
19799a747e4fSDavid du Colombier 	tr->perm = 0660;
19809a747e4fSDavid du Colombier 	return tr;
19819a747e4fSDavid du Colombier }
19829a747e4fSDavid du Colombier 
19839a747e4fSDavid du Colombier static char*
tlsstate(int s)19849a747e4fSDavid du Colombier tlsstate(int s)
19859a747e4fSDavid du Colombier {
19869a747e4fSDavid du Colombier 	switch(s){
19879a747e4fSDavid du Colombier 	case SHandshake:
19889a747e4fSDavid du Colombier 		return "Handshaking";
19899a747e4fSDavid du Colombier 	case SOpen:
19909a747e4fSDavid du Colombier 		return "Established";
19919a747e4fSDavid du Colombier 	case SRClose:
19929a747e4fSDavid du Colombier 		return "RemoteClosed";
19939a747e4fSDavid du Colombier 	case SLClose:
19949a747e4fSDavid du Colombier 		return "LocalClosed";
19959a747e4fSDavid du Colombier 	case SAlert:
19969a747e4fSDavid du Colombier 		return "Alerting";
19979a747e4fSDavid du Colombier 	case SError:
19989a747e4fSDavid du Colombier 		return "Errored";
19999a747e4fSDavid du Colombier 	case SClosed:
20009a747e4fSDavid du Colombier 		return "Closed";
20019a747e4fSDavid du Colombier 	}
20029a747e4fSDavid du Colombier 	return "Unknown";
20039a747e4fSDavid du Colombier }
20049a747e4fSDavid du Colombier 
20059a747e4fSDavid du Colombier static void
freeSec(Secret * s)20069a747e4fSDavid du Colombier freeSec(Secret *s)
20079a747e4fSDavid du Colombier {
20089a747e4fSDavid du Colombier 	if(s != nil){
20099a747e4fSDavid du Colombier 		free(s->enckey);
20109a747e4fSDavid du Colombier 		free(s);
20119a747e4fSDavid du Colombier 	}
20129a747e4fSDavid du Colombier }
20139a747e4fSDavid du Colombier 
20149a747e4fSDavid du Colombier static int
noenc(Secret *,uchar *,int n)20159a747e4fSDavid du Colombier noenc(Secret *, uchar *, int n)
20169a747e4fSDavid du Colombier {
20179a747e4fSDavid du Colombier 	return n;
20189a747e4fSDavid du Colombier }
20199a747e4fSDavid du Colombier 
20209a747e4fSDavid du Colombier static int
rc4enc(Secret * sec,uchar * buf,int n)20219a747e4fSDavid du Colombier rc4enc(Secret *sec, uchar *buf, int n)
20229a747e4fSDavid du Colombier {
20239a747e4fSDavid du Colombier 	rc4(sec->enckey, buf, n);
20249a747e4fSDavid du Colombier 	return n;
20259a747e4fSDavid du Colombier }
20269a747e4fSDavid du Colombier 
20279a747e4fSDavid du Colombier static int
tlsunpad(uchar * buf,int n,int block)20289a747e4fSDavid du Colombier tlsunpad(uchar *buf, int n, int block)
20299a747e4fSDavid du Colombier {
20309a747e4fSDavid du Colombier 	int pad, nn;
20319a747e4fSDavid du Colombier 
20329a747e4fSDavid du Colombier 	pad = buf[n - 1];
20339a747e4fSDavid du Colombier 	nn = n - 1 - pad;
20349a747e4fSDavid du Colombier 	if(nn <= 0 || n % block)
20359a747e4fSDavid du Colombier 		return -1;
20369a747e4fSDavid du Colombier 	while(--n > nn)
20379a747e4fSDavid du Colombier 		if(pad != buf[n - 1])
20389a747e4fSDavid du Colombier 			return -1;
20399a747e4fSDavid du Colombier 	return nn;
20409a747e4fSDavid du Colombier }
20419a747e4fSDavid du Colombier 
20429a747e4fSDavid du Colombier static int
sslunpad(uchar * buf,int n,int block)20439a747e4fSDavid du Colombier sslunpad(uchar *buf, int n, int block)
20449a747e4fSDavid du Colombier {
20459a747e4fSDavid du Colombier 	int pad, nn;
20469a747e4fSDavid du Colombier 
20479a747e4fSDavid du Colombier 	pad = buf[n - 1];
20489a747e4fSDavid du Colombier 	nn = n - 1 - pad;
20499a747e4fSDavid du Colombier 	if(nn <= 0 || n % block)
20509a747e4fSDavid du Colombier 		return -1;
20519a747e4fSDavid du Colombier 	return nn;
20529a747e4fSDavid du Colombier }
20539a747e4fSDavid du Colombier 
20549a747e4fSDavid du Colombier static int
blockpad(uchar * buf,int n,int block)20559a747e4fSDavid du Colombier blockpad(uchar *buf, int n, int block)
20569a747e4fSDavid du Colombier {
20579a747e4fSDavid du Colombier 	int pad, nn;
20589a747e4fSDavid du Colombier 
20599a747e4fSDavid du Colombier 	nn = n + block;
20609a747e4fSDavid du Colombier 	nn -= nn % block;
20619a747e4fSDavid du Colombier 	pad = nn - (n + 1);
20629a747e4fSDavid du Colombier 	while(n < nn)
20639a747e4fSDavid du Colombier 		buf[n++] = pad;
20649a747e4fSDavid du Colombier 	return nn;
20659a747e4fSDavid du Colombier }
20669a747e4fSDavid du Colombier 
20679a747e4fSDavid du Colombier static int
des3enc(Secret * sec,uchar * buf,int n)20689a747e4fSDavid du Colombier des3enc(Secret *sec, uchar *buf, int n)
20699a747e4fSDavid du Colombier {
20709a747e4fSDavid du Colombier 	n = blockpad(buf, n, 8);
20719a747e4fSDavid du Colombier 	des3CBCencrypt(buf, n, sec->enckey);
20729a747e4fSDavid du Colombier 	return n;
20739a747e4fSDavid du Colombier }
20749a747e4fSDavid du Colombier 
20759a747e4fSDavid du Colombier static int
des3dec(Secret * sec,uchar * buf,int n)20769a747e4fSDavid du Colombier des3dec(Secret *sec, uchar *buf, int n)
20779a747e4fSDavid du Colombier {
20789a747e4fSDavid du Colombier 	des3CBCdecrypt(buf, n, sec->enckey);
20799a747e4fSDavid du Colombier 	return (*sec->unpad)(buf, n, 8);
20809a747e4fSDavid du Colombier }
2081ad6ca847SDavid du Colombier 
2082ad6ca847SDavid du Colombier static int
aesenc(Secret * sec,uchar * buf,int n)2083ad6ca847SDavid du Colombier aesenc(Secret *sec, uchar *buf, int n)
2084ad6ca847SDavid du Colombier {
2085ad6ca847SDavid du Colombier 	n = blockpad(buf, n, 16);
2086ad6ca847SDavid du Colombier 	aesCBCencrypt(buf, n, sec->enckey);
2087ad6ca847SDavid du Colombier 	return n;
2088ad6ca847SDavid du Colombier }
2089ad6ca847SDavid du Colombier 
2090ad6ca847SDavid du Colombier static int
aesdec(Secret * sec,uchar * buf,int n)2091ad6ca847SDavid du Colombier aesdec(Secret *sec, uchar *buf, int n)
2092ad6ca847SDavid du Colombier {
2093ad6ca847SDavid du Colombier 	aesCBCdecrypt(buf, n, sec->enckey);
2094ad6ca847SDavid du Colombier 	return (*sec->unpad)(buf, n, 16);
2095ad6ca847SDavid du Colombier }
2096ad6ca847SDavid du Colombier 
20979a747e4fSDavid du Colombier static DigestState*
nomac(uchar *,ulong,uchar *,ulong,uchar *,DigestState *)20989a747e4fSDavid du Colombier nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *)
20999a747e4fSDavid du Colombier {
21009a747e4fSDavid du Colombier 	return nil;
21019a747e4fSDavid du Colombier }
21029a747e4fSDavid du Colombier 
21039a747e4fSDavid du Colombier /*
21049a747e4fSDavid du Colombier  * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
21059a747e4fSDavid du Colombier  */
21069a747e4fSDavid du Colombier static DigestState*
sslmac_x(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s,DigestState * (* x)(uchar *,ulong,uchar *,DigestState *),int xlen,int padlen)21079a747e4fSDavid du Colombier sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
21089a747e4fSDavid du Colombier 	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
21099a747e4fSDavid du Colombier {
21109a747e4fSDavid du Colombier 	int i;
21119a747e4fSDavid du Colombier 	uchar pad[48], innerdigest[20];
21129a747e4fSDavid du Colombier 
21139a747e4fSDavid du Colombier 	if(xlen > sizeof(innerdigest)
21149a747e4fSDavid du Colombier 	|| padlen > sizeof(pad))
21159a747e4fSDavid du Colombier 		return nil;
21169a747e4fSDavid du Colombier 
21179a747e4fSDavid du Colombier 	if(klen>64)
21189a747e4fSDavid du Colombier 		return nil;
21199a747e4fSDavid du Colombier 
21209a747e4fSDavid du Colombier 	/* first time through */
21219a747e4fSDavid du Colombier 	if(s == nil){
21229a747e4fSDavid du Colombier 		for(i=0; i<padlen; i++)
21239a747e4fSDavid du Colombier 			pad[i] = 0x36;
21249a747e4fSDavid du Colombier 		s = (*x)(key, klen, nil, nil);
21259a747e4fSDavid du Colombier 		s = (*x)(pad, padlen, nil, s);
21269a747e4fSDavid du Colombier 		if(s == nil)
21279a747e4fSDavid du Colombier 			return nil;
21289a747e4fSDavid du Colombier 	}
21299a747e4fSDavid du Colombier 
21309a747e4fSDavid du Colombier 	s = (*x)(p, len, nil, s);
21319a747e4fSDavid du Colombier 	if(digest == nil)
21329a747e4fSDavid du Colombier 		return s;
21339a747e4fSDavid du Colombier 
21349a747e4fSDavid du Colombier 	/* last time through */
21359a747e4fSDavid du Colombier 	for(i=0; i<padlen; i++)
21369a747e4fSDavid du Colombier 		pad[i] = 0x5c;
21379a747e4fSDavid du Colombier 	(*x)(nil, 0, innerdigest, s);
21389a747e4fSDavid du Colombier 	s = (*x)(key, klen, nil, nil);
21399a747e4fSDavid du Colombier 	s = (*x)(pad, padlen, nil, s);
21409a747e4fSDavid du Colombier 	(*x)(innerdigest, xlen, digest, s);
21419a747e4fSDavid du Colombier 	return nil;
21429a747e4fSDavid du Colombier }
21439a747e4fSDavid du Colombier 
21449a747e4fSDavid du Colombier static DigestState*
sslmac_sha1(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)21459a747e4fSDavid du Colombier sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
21469a747e4fSDavid du Colombier {
21479a747e4fSDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
21489a747e4fSDavid du Colombier }
21499a747e4fSDavid du Colombier 
21509a747e4fSDavid du Colombier static DigestState*
sslmac_md5(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)21519a747e4fSDavid du Colombier sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
21529a747e4fSDavid du Colombier {
21539a747e4fSDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
21549a747e4fSDavid du Colombier }
21559a747e4fSDavid du Colombier 
21569a747e4fSDavid du Colombier static void
sslPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)21579a747e4fSDavid du Colombier sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
21589a747e4fSDavid du Colombier {
21599a747e4fSDavid du Colombier 	DigestState *s;
21609a747e4fSDavid du Colombier 	uchar buf[11];
21619a747e4fSDavid du Colombier 
21629a747e4fSDavid du Colombier 	memmove(buf, seq, 8);
21639a747e4fSDavid du Colombier 	buf[8] = header[0];
21649a747e4fSDavid du Colombier 	buf[9] = header[3];
21659a747e4fSDavid du Colombier 	buf[10] = header[4];
21669a747e4fSDavid du Colombier 
21679a747e4fSDavid du Colombier 	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
21689a747e4fSDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21699a747e4fSDavid du Colombier }
21709a747e4fSDavid du Colombier 
21719a747e4fSDavid du Colombier static void
tlsPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)21729a747e4fSDavid du Colombier tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
21739a747e4fSDavid du Colombier {
21749a747e4fSDavid du Colombier 	DigestState *s;
21759a747e4fSDavid du Colombier 	uchar buf[13];
21769a747e4fSDavid du Colombier 
21779a747e4fSDavid du Colombier 	memmove(buf, seq, 8);
21789a747e4fSDavid du Colombier 	memmove(&buf[8], header, 5);
21799a747e4fSDavid du Colombier 
21809a747e4fSDavid du Colombier 	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
21819a747e4fSDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21829a747e4fSDavid du Colombier }
21839a747e4fSDavid du Colombier 
21849a747e4fSDavid du Colombier static void
put32(uchar * p,u32int x)21859a747e4fSDavid du Colombier put32(uchar *p, u32int x)
21869a747e4fSDavid du Colombier {
21879a747e4fSDavid du Colombier 	p[0] = x>>24;
21889a747e4fSDavid du Colombier 	p[1] = x>>16;
21899a747e4fSDavid du Colombier 	p[2] = x>>8;
21909a747e4fSDavid du Colombier 	p[3] = x;
21919a747e4fSDavid du Colombier }
21929a747e4fSDavid du Colombier 
21939a747e4fSDavid du Colombier static void
put64(uchar * p,vlong x)21949a747e4fSDavid du Colombier put64(uchar *p, vlong x)
21959a747e4fSDavid du Colombier {
21969a747e4fSDavid du Colombier 	put32(p, (u32int)(x >> 32));
21979a747e4fSDavid du Colombier 	put32(p+4, (u32int)x);
21989a747e4fSDavid du Colombier }
21999a747e4fSDavid du Colombier 
22009a747e4fSDavid du Colombier static void
put24(uchar * p,int x)22019a747e4fSDavid du Colombier put24(uchar *p, int x)
22029a747e4fSDavid du Colombier {
22039a747e4fSDavid du Colombier 	p[0] = x>>16;
22049a747e4fSDavid du Colombier 	p[1] = x>>8;
22059a747e4fSDavid du Colombier 	p[2] = x;
22069a747e4fSDavid du Colombier }
22079a747e4fSDavid du Colombier 
22089a747e4fSDavid du Colombier static void
put16(uchar * p,int x)22099a747e4fSDavid du Colombier put16(uchar *p, int x)
22109a747e4fSDavid du Colombier {
22119a747e4fSDavid du Colombier 	p[0] = x>>8;
22129a747e4fSDavid du Colombier 	p[1] = x;
22139a747e4fSDavid du Colombier }
22149a747e4fSDavid du Colombier 
22159a747e4fSDavid du Colombier static u32int
get32(uchar * p)22169a747e4fSDavid du Colombier get32(uchar *p)
22179a747e4fSDavid du Colombier {
22189a747e4fSDavid du Colombier 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
22199a747e4fSDavid du Colombier }
22209a747e4fSDavid du Colombier 
22219a747e4fSDavid du Colombier static int
get16(uchar * p)22229a747e4fSDavid du Colombier get16(uchar *p)
22239a747e4fSDavid du Colombier {
22249a747e4fSDavid du Colombier 	return (p[0]<<8)|p[1];
22259a747e4fSDavid du Colombier }
22263751babcSDavid du Colombier 
22273751babcSDavid du Colombier static char *charmap = "0123456789abcdef";
22283751babcSDavid du Colombier 
22293751babcSDavid du Colombier static void
pdump(int len,void * a,char * tag)22303751babcSDavid du Colombier pdump(int len, void *a, char *tag)
22313751babcSDavid du Colombier {
22323751babcSDavid du Colombier 	uchar *p;
22333751babcSDavid du Colombier 	int i;
22343751babcSDavid du Colombier 	char buf[65+32];
22353751babcSDavid du Colombier 	char *q;
22363751babcSDavid du Colombier 
22373751babcSDavid du Colombier 	p = a;
22383751babcSDavid du Colombier 	strcpy(buf, tag);
22393751babcSDavid du Colombier 	while(len > 0){
22403751babcSDavid du Colombier 		q = buf + strlen(tag);
22413751babcSDavid du Colombier 		for(i = 0; len > 0 && i < 32; i++){
22423751babcSDavid du Colombier 			if(*p >= ' ' && *p < 0x7f){
22433751babcSDavid du Colombier 				*q++ = ' ';
22443751babcSDavid du Colombier 				*q++ = *p;
22453751babcSDavid du Colombier 			} else {
22463751babcSDavid du Colombier 				*q++ = charmap[*p>>4];
22473751babcSDavid du Colombier 				*q++ = charmap[*p & 0xf];
22483751babcSDavid du Colombier 			}
22493751babcSDavid du Colombier 			len--;
22503751babcSDavid du Colombier 			p++;
22513751babcSDavid du Colombier 		}
22523751babcSDavid du Colombier 		*q = 0;
22533751babcSDavid du Colombier 
22543751babcSDavid du Colombier 		if(len > 0)
22553751babcSDavid du Colombier 			pprint("%s...\n", buf);
22563751babcSDavid du Colombier 		else
22573751babcSDavid du Colombier 			pprint("%s\n", buf);
22583751babcSDavid du Colombier 	}
22593751babcSDavid du Colombier }
2260