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