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