xref: /plan9/sys/src/cmd/unix/drawterm/kern/devtls.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
11a4050f5SDavid du Colombier /*
21a4050f5SDavid du Colombier  *  devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
31a4050f5SDavid du Colombier  */
41a4050f5SDavid du Colombier #include	"u.h"
51a4050f5SDavid du Colombier #include	"lib.h"
61a4050f5SDavid du Colombier #include	"dat.h"
71a4050f5SDavid du Colombier #include	"fns.h"
81a4050f5SDavid du Colombier #include	"error.h"
91a4050f5SDavid du Colombier 
101a4050f5SDavid du Colombier #include	"libsec.h"
111a4050f5SDavid du Colombier 
121a4050f5SDavid du Colombier typedef struct OneWay	OneWay;
131a4050f5SDavid du Colombier typedef struct Secret		Secret;
141a4050f5SDavid du Colombier typedef struct TlsRec	TlsRec;
151a4050f5SDavid du Colombier typedef struct TlsErrs	TlsErrs;
161a4050f5SDavid du Colombier 
171a4050f5SDavid du Colombier enum {
181a4050f5SDavid du Colombier 	Statlen=	1024,		/* max. length of status or stats message */
191a4050f5SDavid du Colombier 	/* buffer limits */
201a4050f5SDavid du Colombier 	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */
211a4050f5SDavid du Colombier 	MaxCipherRecLen	= MaxRecLen + 2048,
221a4050f5SDavid du Colombier 	RecHdrLen		= 5,
231a4050f5SDavid du Colombier 	MaxMacLen		= SHA1dlen,
241a4050f5SDavid du Colombier 
251a4050f5SDavid du Colombier 	/* protocol versions we can accept */
261a4050f5SDavid du Colombier 	TLSVersion		= 0x0301,
271a4050f5SDavid du Colombier 	SSL3Version		= 0x0300,
281a4050f5SDavid du Colombier 	ProtocolVersion	= 0x0301,	/* maximum version we speak */
291a4050f5SDavid du Colombier 	MinProtoVersion	= 0x0300,	/* limits on version we accept */
301a4050f5SDavid du Colombier 	MaxProtoVersion	= 0x03ff,
311a4050f5SDavid du Colombier 
321a4050f5SDavid du Colombier 	/* connection states */
331a4050f5SDavid du Colombier 	SHandshake	= 1 << 0,	/* doing handshake */
341a4050f5SDavid du Colombier 	SOpen		= 1 << 1,	/* application data can be sent */
351a4050f5SDavid du Colombier 	SRClose		= 1 << 2,	/* remote side has closed down */
361a4050f5SDavid du Colombier 	SLClose		= 1 << 3,	/* sent a close notify alert */
371a4050f5SDavid du Colombier 	SAlert		= 1 << 5,	/* sending or sent a fatal alert */
381a4050f5SDavid du Colombier 	SError		= 1 << 6,	/* some sort of error has occured */
391a4050f5SDavid du Colombier 	SClosed		= 1 << 7,	/* it is all over */
401a4050f5SDavid du Colombier 
411a4050f5SDavid du Colombier 	/* record types */
421a4050f5SDavid du Colombier 	RChangeCipherSpec = 20,
431a4050f5SDavid du Colombier 	RAlert,
441a4050f5SDavid du Colombier 	RHandshake,
451a4050f5SDavid du Colombier 	RApplication,
461a4050f5SDavid du Colombier 
471a4050f5SDavid du Colombier 	SSL2ClientHello = 1,
481a4050f5SDavid du Colombier 	HSSL2ClientHello = 9,  /* local convention;  see tlshand.c */
491a4050f5SDavid du Colombier 
501a4050f5SDavid du Colombier 	/* alerts */
511a4050f5SDavid du Colombier 	ECloseNotify 			= 0,
521a4050f5SDavid du Colombier 	EUnexpectedMessage 	= 10,
531a4050f5SDavid du Colombier 	EBadRecordMac 		= 20,
541a4050f5SDavid du Colombier 	EDecryptionFailed 		= 21,
551a4050f5SDavid du Colombier 	ERecordOverflow 		= 22,
561a4050f5SDavid du Colombier 	EDecompressionFailure 	= 30,
571a4050f5SDavid du Colombier 	EHandshakeFailure 		= 40,
581a4050f5SDavid du Colombier 	ENoCertificate 			= 41,
591a4050f5SDavid du Colombier 	EBadCertificate 		= 42,
601a4050f5SDavid du Colombier 	EUnsupportedCertificate 	= 43,
611a4050f5SDavid du Colombier 	ECertificateRevoked 		= 44,
621a4050f5SDavid du Colombier 	ECertificateExpired 		= 45,
631a4050f5SDavid du Colombier 	ECertificateUnknown 	= 46,
641a4050f5SDavid du Colombier 	EIllegalParameter 		= 47,
651a4050f5SDavid du Colombier 	EUnknownCa 			= 48,
661a4050f5SDavid du Colombier 	EAccessDenied 		= 49,
671a4050f5SDavid du Colombier 	EDecodeError 			= 50,
681a4050f5SDavid du Colombier 	EDecryptError 			= 51,
691a4050f5SDavid du Colombier 	EExportRestriction 		= 60,
701a4050f5SDavid du Colombier 	EProtocolVersion 		= 70,
711a4050f5SDavid du Colombier 	EInsufficientSecurity 	= 71,
721a4050f5SDavid du Colombier 	EInternalError 			= 80,
731a4050f5SDavid du Colombier 	EUserCanceled 			= 90,
741a4050f5SDavid du Colombier 	ENoRenegotiation 		= 100,
751a4050f5SDavid du Colombier 
761a4050f5SDavid du Colombier 	EMAX = 256
771a4050f5SDavid du Colombier };
781a4050f5SDavid du Colombier 
791a4050f5SDavid du Colombier struct Secret
801a4050f5SDavid du Colombier {
811a4050f5SDavid du Colombier 	char		*encalg;	/* name of encryption alg */
821a4050f5SDavid du Colombier 	char		*hashalg;	/* name of hash alg */
831a4050f5SDavid du Colombier 	int		(*enc)(Secret*, uchar*, int);
841a4050f5SDavid du Colombier 	int		(*dec)(Secret*, uchar*, int);
851a4050f5SDavid du Colombier 	int		(*unpad)(uchar*, int, int);
861a4050f5SDavid du Colombier 	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
871a4050f5SDavid du Colombier 	int		block;		/* encryption block len, 0 if none */
881a4050f5SDavid du Colombier 	int		maclen;
891a4050f5SDavid du Colombier 	void		*enckey;
901a4050f5SDavid du Colombier 	uchar	mackey[MaxMacLen];
911a4050f5SDavid du Colombier };
921a4050f5SDavid du Colombier 
931a4050f5SDavid du Colombier struct OneWay
941a4050f5SDavid du Colombier {
951a4050f5SDavid du Colombier 	QLock		io;		/* locks io access */
961a4050f5SDavid du Colombier 	QLock		seclock;	/* locks secret paramaters */
971a4050f5SDavid du Colombier 	ulong		seq;
981a4050f5SDavid du Colombier 	Secret		*sec;		/* cipher in use */
991a4050f5SDavid du Colombier 	Secret		*new;		/* cipher waiting for enable */
1001a4050f5SDavid du Colombier };
1011a4050f5SDavid du Colombier 
1021a4050f5SDavid du Colombier struct TlsRec
1031a4050f5SDavid du Colombier {
1041a4050f5SDavid du Colombier 	Chan	*c;				/* io channel */
1051a4050f5SDavid du Colombier 	int		ref;				/* serialized by tdlock for atomic destroy */
1061a4050f5SDavid du Colombier 	int		version;			/* version of the protocol we are speaking */
1071a4050f5SDavid du Colombier 	char		verset;			/* version has been set */
1081a4050f5SDavid du Colombier 	char		opened;			/* opened command every issued? */
1091a4050f5SDavid du Colombier 	char		err[ERRMAX];		/* error message to return to handshake requests */
1101a4050f5SDavid du Colombier 	vlong	handin;			/* bytes communicated by the record layer */
1111a4050f5SDavid du Colombier 	vlong	handout;
1121a4050f5SDavid du Colombier 	vlong	datain;
1131a4050f5SDavid du Colombier 	vlong	dataout;
1141a4050f5SDavid du Colombier 
1151a4050f5SDavid du Colombier 	Lock		statelk;
1161a4050f5SDavid du Colombier 	int		state;
1171a4050f5SDavid du Colombier 	int		debug;
1181a4050f5SDavid du Colombier 
1191a4050f5SDavid du Colombier 	/* record layer mac functions for different protocol versions */
1201a4050f5SDavid du Colombier 	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
1211a4050f5SDavid du Colombier 
1221a4050f5SDavid du Colombier 	/* input side -- protected by in.io */
1231a4050f5SDavid du Colombier 	OneWay		in;
1241a4050f5SDavid du Colombier 	Block		*processed;	/* next bunch of application data */
1251a4050f5SDavid du Colombier 	Block		*unprocessed;	/* data read from c but not parsed into records */
1261a4050f5SDavid du Colombier 
1271a4050f5SDavid du Colombier 	/* handshake queue */
1281a4050f5SDavid du Colombier 	Lock		hqlock;			/* protects hqref, alloc & free of handq, hprocessed */
1291a4050f5SDavid du Colombier 	int		hqref;
1301a4050f5SDavid du Colombier 	Queue		*handq;		/* queue of handshake messages */
1311a4050f5SDavid du Colombier 	Block		*hprocessed;	/* remainder of last block read from handq */
1321a4050f5SDavid du Colombier 	QLock		hqread;		/* protects reads for hprocessed, handq */
1331a4050f5SDavid du Colombier 
1341a4050f5SDavid du Colombier 	/* output side */
1351a4050f5SDavid du Colombier 	OneWay		out;
1361a4050f5SDavid du Colombier 
1371a4050f5SDavid du Colombier 	/* protections */
1381a4050f5SDavid du Colombier 	char		*user;
1391a4050f5SDavid du Colombier 	int		perm;
1401a4050f5SDavid du Colombier };
1411a4050f5SDavid du Colombier 
1421a4050f5SDavid du Colombier struct TlsErrs{
1431a4050f5SDavid du Colombier 	int	err;
1441a4050f5SDavid du Colombier 	int	sslerr;
1451a4050f5SDavid du Colombier 	int	tlserr;
1461a4050f5SDavid du Colombier 	int	fatal;
1471a4050f5SDavid du Colombier 	char	*msg;
1481a4050f5SDavid du Colombier };
1491a4050f5SDavid du Colombier 
1501a4050f5SDavid du Colombier static TlsErrs tlserrs[] = {
1511a4050f5SDavid du Colombier 	{ECloseNotify,			ECloseNotify,			ECloseNotify,			0, 	"close notify"},
1521a4050f5SDavid du Colombier 	{EUnexpectedMessage,	EUnexpectedMessage,	EUnexpectedMessage, 	1, "unexpected message"},
1531a4050f5SDavid du Colombier 	{EBadRecordMac,		EBadRecordMac,		EBadRecordMac, 		1, "bad record mac"},
1541a4050f5SDavid du Colombier 	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,		1, "decryption failed"},
1551a4050f5SDavid du Colombier 	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,		1, "record too long"},
1561a4050f5SDavid du Colombier 	{EDecompressionFailure,	EDecompressionFailure,	EDecompressionFailure,	1, "decompression failed"},
1571a4050f5SDavid du Colombier 	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,		1, "could not negotiate acceptable security parameters"},
1581a4050f5SDavid du Colombier 	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,	1, "no appropriate certificate available"},
1591a4050f5SDavid du Colombier 	{EBadCertificate,		EBadCertificate,		EBadCertificate,		1, "corrupted or invalid certificate"},
1601a4050f5SDavid du Colombier 	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,	1, "unsupported certificate type"},
1611a4050f5SDavid du Colombier 	{ECertificateRevoked,	ECertificateRevoked,		ECertificateRevoked,		1, "revoked certificate"},
1621a4050f5SDavid du Colombier 	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,		1, "expired certificate"},
1631a4050f5SDavid du Colombier 	{ECertificateUnknown,	ECertificateUnknown,	ECertificateUnknown,	1, "unacceptable certificate"},
1641a4050f5SDavid du Colombier 	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,		1, "illegal parameter"},
1651a4050f5SDavid du Colombier 	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,			1, "unknown certificate authority"},
1661a4050f5SDavid du Colombier 	{EAccessDenied,		EHandshakeFailure,		EAccessDenied,		1, "access denied"},
1671a4050f5SDavid du Colombier 	{EDecodeError,			EIllegalParameter,		EDecodeError,			1, "error decoding message"},
1681a4050f5SDavid du Colombier 	{EDecryptError,			EIllegalParameter,		EDecryptError,			1, "error decrypting message"},
1691a4050f5SDavid du Colombier 	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,		1, "export restriction violated"},
1701a4050f5SDavid du Colombier 	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,		1, "protocol version not supported"},
1711a4050f5SDavid du Colombier 	{EInsufficientSecurity,	EHandshakeFailure,		EInsufficientSecurity,	1, "stronger security routines required"},
1721a4050f5SDavid du Colombier 	{EInternalError,			EHandshakeFailure,		EInternalError,			1, "internal error"},
1731a4050f5SDavid du Colombier 	{EUserCanceled,		ECloseNotify,			EUserCanceled,			0, "handshake canceled by user"},
1741a4050f5SDavid du Colombier 	{ENoRenegotiation,		EUnexpectedMessage,	ENoRenegotiation,		0, "no renegotiation"},
1751a4050f5SDavid du Colombier };
1761a4050f5SDavid du Colombier 
1771a4050f5SDavid du Colombier enum
1781a4050f5SDavid du Colombier {
1791a4050f5SDavid du Colombier 	/* max. open tls connections */
1801a4050f5SDavid du Colombier 	MaxTlsDevs	= 1024
1811a4050f5SDavid du Colombier };
1821a4050f5SDavid du Colombier 
1831a4050f5SDavid du Colombier static	Lock	tdlock;
1841a4050f5SDavid du Colombier static	int	tdhiwat;
1851a4050f5SDavid du Colombier static	int	maxtlsdevs = 128;
1861a4050f5SDavid du Colombier static	TlsRec	**tlsdevs;
1871a4050f5SDavid du Colombier static	char	**trnames;
1881a4050f5SDavid du Colombier static	char	*encalgs;
1891a4050f5SDavid du Colombier static	char	*hashalgs;
1901a4050f5SDavid du Colombier 
1911a4050f5SDavid du Colombier enum{
1921a4050f5SDavid du Colombier 	Qtopdir		= 1,	/* top level directory */
1931a4050f5SDavid du Colombier 	Qprotodir,
1941a4050f5SDavid du Colombier 	Qclonus,
1951a4050f5SDavid du Colombier 	Qencalgs,
1961a4050f5SDavid du Colombier 	Qhashalgs,
1971a4050f5SDavid du Colombier 	Qconvdir,		/* directory for a conversation */
1981a4050f5SDavid du Colombier 	Qdata,
1991a4050f5SDavid du Colombier 	Qctl,
2001a4050f5SDavid du Colombier 	Qhand,
2011a4050f5SDavid du Colombier 	Qstatus,
2021a4050f5SDavid du Colombier 	Qstats,
2031a4050f5SDavid du Colombier };
2041a4050f5SDavid du Colombier 
2051a4050f5SDavid du Colombier #define TYPE(x) 	((x).path & 0xf)
2061a4050f5SDavid du Colombier #define CONV(x) 	(((x).path >> 5)&(MaxTlsDevs-1))
2071a4050f5SDavid du Colombier #define QID(c, y) 	(((c)<<5) | (y))
2081a4050f5SDavid du Colombier 
2091a4050f5SDavid du Colombier static void	checkstate(TlsRec *, int, int);
2101a4050f5SDavid du Colombier static void	ensure(TlsRec*, Block**, int);
2111a4050f5SDavid du Colombier static void	consume(Block**, uchar*, int);
2121a4050f5SDavid du Colombier static Chan*	buftochan(char*);
2131a4050f5SDavid du Colombier static void	tlshangup(TlsRec*);
2141a4050f5SDavid du Colombier static void	tlsError(TlsRec*, char *);
2151a4050f5SDavid du Colombier static void	alertHand(TlsRec*, char *);
2161a4050f5SDavid du Colombier static TlsRec	*newtls(Chan *c);
2171a4050f5SDavid du Colombier static TlsRec	*mktlsrec(void);
2181a4050f5SDavid du Colombier static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2191a4050f5SDavid du Colombier static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2201a4050f5SDavid du Colombier static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
2211a4050f5SDavid du Colombier static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2221a4050f5SDavid du Colombier static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
2231a4050f5SDavid du Colombier static void	put64(uchar *p, vlong x);
2241a4050f5SDavid du Colombier static void	put32(uchar *p, u32int);
2251a4050f5SDavid du Colombier static void	put24(uchar *p, int);
2261a4050f5SDavid du Colombier static void	put16(uchar *p, int);
227*ec59a3ddSDavid du Colombier /* static u32int	get32(uchar *p); */
2281a4050f5SDavid du Colombier static int	get16(uchar *p);
2291a4050f5SDavid du Colombier static void	tlsSetState(TlsRec *tr, int new, int old);
2301a4050f5SDavid du Colombier static void	rcvAlert(TlsRec *tr, int err);
2311a4050f5SDavid du Colombier static void	sendAlert(TlsRec *tr, int err);
2321a4050f5SDavid du Colombier static void	rcvError(TlsRec *tr, int err, char *msg, ...);
2331a4050f5SDavid du Colombier static int	rc4enc(Secret *sec, uchar *buf, int n);
2341a4050f5SDavid du Colombier static int	des3enc(Secret *sec, uchar *buf, int n);
2351a4050f5SDavid du Colombier static int	des3dec(Secret *sec, uchar *buf, int n);
2361a4050f5SDavid du Colombier static int	noenc(Secret *sec, uchar *buf, int n);
2371a4050f5SDavid du Colombier static int	sslunpad(uchar *buf, int n, int block);
2381a4050f5SDavid du Colombier static int	tlsunpad(uchar *buf, int n, int block);
2391a4050f5SDavid du Colombier static void	freeSec(Secret *sec);
2401a4050f5SDavid du Colombier static char	*tlsstate(int s);
2411a4050f5SDavid du Colombier static void	pdump(int, void*, char*);
2421a4050f5SDavid du Colombier 
2431a4050f5SDavid du Colombier static char *tlsnames[] = {
244d04cc87cSDavid du Colombier 	/* unused */ 0,
245d04cc87cSDavid du Colombier 	/* topdir */ 0,
246d04cc87cSDavid du Colombier 	/* protodir */ 0,
247*ec59a3ddSDavid du Colombier 	"clone",
248*ec59a3ddSDavid du Colombier 	"encalgs",
249*ec59a3ddSDavid du Colombier 	"hashalgs",
250d04cc87cSDavid du Colombier 	/* convdir */ 0,
251*ec59a3ddSDavid du Colombier 	"data",
252*ec59a3ddSDavid du Colombier 	"ctl",
253*ec59a3ddSDavid du Colombier 	"hand",
254*ec59a3ddSDavid du Colombier 	"status",
255*ec59a3ddSDavid du Colombier 	"stats",
2561a4050f5SDavid du Colombier };
2571a4050f5SDavid du Colombier 
2581a4050f5SDavid du Colombier static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
2591a4050f5SDavid du Colombier 
2601a4050f5SDavid du Colombier static int
tlsgen(Chan * c,char * unused1,Dirtab * unused2,int unused3,int s,Dir * dp)2611a4050f5SDavid du Colombier tlsgen(Chan *c, char*unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
2621a4050f5SDavid du Colombier {
2631a4050f5SDavid du Colombier 	Qid q;
2641a4050f5SDavid du Colombier 	TlsRec *tr;
2651a4050f5SDavid du Colombier 	char *name, *nm;
2661a4050f5SDavid du Colombier 	int perm, t;
2671a4050f5SDavid du Colombier 
2681a4050f5SDavid du Colombier 	q.vers = 0;
2691a4050f5SDavid du Colombier 	q.type = QTFILE;
2701a4050f5SDavid du Colombier 
2711a4050f5SDavid du Colombier 	t = TYPE(c->qid);
2721a4050f5SDavid du Colombier 	switch(t) {
2731a4050f5SDavid du Colombier 	case Qtopdir:
2741a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
2751a4050f5SDavid du Colombier 			q.path = QID(0, Qtopdir);
2761a4050f5SDavid du Colombier 			q.type = QTDIR;
2771a4050f5SDavid du Colombier 			devdir(c, q, "#a", 0, eve, 0555, dp);
2781a4050f5SDavid du Colombier 			return 1;
2791a4050f5SDavid du Colombier 		}
2801a4050f5SDavid du Colombier 		if(s > 0)
2811a4050f5SDavid du Colombier 			return -1;
2821a4050f5SDavid du Colombier 		q.path = QID(0, Qprotodir);
2831a4050f5SDavid du Colombier 		q.type = QTDIR;
2841a4050f5SDavid du Colombier 		devdir(c, q, "tls", 0, eve, 0555, dp);
2851a4050f5SDavid du Colombier 		return 1;
2861a4050f5SDavid du Colombier 	case Qprotodir:
2871a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
2881a4050f5SDavid du Colombier 			q.path = QID(0, Qtopdir);
2891a4050f5SDavid du Colombier 			q.type = QTDIR;
2901a4050f5SDavid du Colombier 			devdir(c, q, ".", 0, eve, 0555, dp);
2911a4050f5SDavid du Colombier 			return 1;
2921a4050f5SDavid du Colombier 		}
2931a4050f5SDavid du Colombier 		if(s < 3){
2941a4050f5SDavid du Colombier 			switch(s) {
2951a4050f5SDavid du Colombier 			default:
2961a4050f5SDavid du Colombier 				return -1;
2971a4050f5SDavid du Colombier 			case 0:
2981a4050f5SDavid du Colombier 				q.path = QID(0, Qclonus);
2991a4050f5SDavid du Colombier 				break;
3001a4050f5SDavid du Colombier 			case 1:
3011a4050f5SDavid du Colombier 				q.path = QID(0, Qencalgs);
3021a4050f5SDavid du Colombier 				break;
3031a4050f5SDavid du Colombier 			case 2:
3041a4050f5SDavid du Colombier 				q.path = QID(0, Qhashalgs);
3051a4050f5SDavid du Colombier 				break;
3061a4050f5SDavid du Colombier 			}
3071a4050f5SDavid du Colombier 			perm = 0444;
3081a4050f5SDavid du Colombier 			if(TYPE(q) == Qclonus)
3091a4050f5SDavid du Colombier 				perm = 0555;
3101a4050f5SDavid du Colombier 			devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
3111a4050f5SDavid du Colombier 			return 1;
3121a4050f5SDavid du Colombier 		}
3131a4050f5SDavid du Colombier 		s -= 3;
3141a4050f5SDavid du Colombier 		if(s >= tdhiwat)
3151a4050f5SDavid du Colombier 			return -1;
3161a4050f5SDavid du Colombier 		q.path = QID(s, Qconvdir);
3171a4050f5SDavid du Colombier 		q.type = QTDIR;
3181a4050f5SDavid du Colombier 		lock(&tdlock);
3191a4050f5SDavid du Colombier 		tr = tlsdevs[s];
3201a4050f5SDavid du Colombier 		if(tr != nil)
3211a4050f5SDavid du Colombier 			nm = tr->user;
3221a4050f5SDavid du Colombier 		else
3231a4050f5SDavid du Colombier 			nm = eve;
3241a4050f5SDavid du Colombier 		if((name = trnames[s]) == nil){
3251a4050f5SDavid du Colombier 			name = trnames[s] = smalloc(16);
3261a4050f5SDavid du Colombier 			sprint(name, "%d", s);
3271a4050f5SDavid du Colombier 		}
3281a4050f5SDavid du Colombier 		devdir(c, q, name, 0, nm, 0555, dp);
3291a4050f5SDavid du Colombier 		unlock(&tdlock);
3301a4050f5SDavid du Colombier 		return 1;
3311a4050f5SDavid du Colombier 	case Qconvdir:
3321a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
3331a4050f5SDavid du Colombier 			q.path = QID(0, Qprotodir);
3341a4050f5SDavid du Colombier 			q.type = QTDIR;
3351a4050f5SDavid du Colombier 			devdir(c, q, "tls", 0, eve, 0555, dp);
3361a4050f5SDavid du Colombier 			return 1;
3371a4050f5SDavid du Colombier 		}
3381a4050f5SDavid du Colombier 		if(s < 0 || s >= nelem(convdir))
3391a4050f5SDavid du Colombier 			return -1;
3401a4050f5SDavid du Colombier 		lock(&tdlock);
3411a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
3421a4050f5SDavid du Colombier 		if(tr != nil){
3431a4050f5SDavid du Colombier 			nm = tr->user;
3441a4050f5SDavid du Colombier 			perm = tr->perm;
3451a4050f5SDavid du Colombier 		}else{
3461a4050f5SDavid du Colombier 			perm = 0;
3471a4050f5SDavid du Colombier 			nm = eve;
3481a4050f5SDavid du Colombier 		}
3491a4050f5SDavid du Colombier 		t = convdir[s];
3501a4050f5SDavid du Colombier 		if(t == Qstatus || t == Qstats)
3511a4050f5SDavid du Colombier 			perm &= 0444;
3521a4050f5SDavid du Colombier 		q.path = QID(CONV(c->qid), t);
3531a4050f5SDavid du Colombier 		devdir(c, q, tlsnames[t], 0, nm, perm, dp);
3541a4050f5SDavid du Colombier 		unlock(&tdlock);
3551a4050f5SDavid du Colombier 		return 1;
3561a4050f5SDavid du Colombier 	case Qclonus:
3571a4050f5SDavid du Colombier 	case Qencalgs:
3581a4050f5SDavid du Colombier 	case Qhashalgs:
3591a4050f5SDavid du Colombier 		perm = 0444;
3601a4050f5SDavid du Colombier 		if(t == Qclonus)
3611a4050f5SDavid du Colombier 			perm = 0555;
3621a4050f5SDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
3631a4050f5SDavid du Colombier 		return 1;
3641a4050f5SDavid du Colombier 	default:
3651a4050f5SDavid du Colombier 		lock(&tdlock);
3661a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
3671a4050f5SDavid du Colombier 		if(tr != nil){
3681a4050f5SDavid du Colombier 			nm = tr->user;
3691a4050f5SDavid du Colombier 			perm = tr->perm;
3701a4050f5SDavid du Colombier 		}else{
3711a4050f5SDavid du Colombier 			perm = 0;
3721a4050f5SDavid du Colombier 			nm = eve;
3731a4050f5SDavid du Colombier 		}
3741a4050f5SDavid du Colombier 		if(t == Qstatus || t == Qstats)
3751a4050f5SDavid du Colombier 			perm &= 0444;
3761a4050f5SDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
3771a4050f5SDavid du Colombier 		unlock(&tdlock);
3781a4050f5SDavid du Colombier 		return 1;
3791a4050f5SDavid du Colombier 	}
3801a4050f5SDavid du Colombier 	return -1;
3811a4050f5SDavid du Colombier }
3821a4050f5SDavid du Colombier 
3831a4050f5SDavid du Colombier static Chan*
tlsattach(char * spec)3841a4050f5SDavid du Colombier tlsattach(char *spec)
3851a4050f5SDavid du Colombier {
3861a4050f5SDavid du Colombier 	Chan *c;
3871a4050f5SDavid du Colombier 
3881a4050f5SDavid du Colombier 	c = devattach('a', spec);
3891a4050f5SDavid du Colombier 	c->qid.path = QID(0, Qtopdir);
3901a4050f5SDavid du Colombier 	c->qid.type = QTDIR;
3911a4050f5SDavid du Colombier 	c->qid.vers = 0;
3921a4050f5SDavid du Colombier 	return c;
3931a4050f5SDavid du Colombier }
3941a4050f5SDavid du Colombier 
3951a4050f5SDavid du Colombier static Walkqid*
tlswalk(Chan * c,Chan * nc,char ** name,int nname)3961a4050f5SDavid du Colombier tlswalk(Chan *c, Chan *nc, char **name, int nname)
3971a4050f5SDavid du Colombier {
3981a4050f5SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, tlsgen);
3991a4050f5SDavid du Colombier }
4001a4050f5SDavid du Colombier 
4011a4050f5SDavid du Colombier static int
tlsstat(Chan * c,uchar * db,int n)4021a4050f5SDavid du Colombier tlsstat(Chan *c, uchar *db, int n)
4031a4050f5SDavid du Colombier {
4041a4050f5SDavid du Colombier 	return devstat(c, db, n, nil, 0, tlsgen);
4051a4050f5SDavid du Colombier }
4061a4050f5SDavid du Colombier 
4071a4050f5SDavid du Colombier static Chan*
tlsopen(Chan * c,int omode)4081a4050f5SDavid du Colombier tlsopen(Chan *c, int omode)
4091a4050f5SDavid du Colombier {
4101a4050f5SDavid du Colombier 	TlsRec *tr, **pp;
4111a4050f5SDavid du Colombier 	int t, perm;
4121a4050f5SDavid du Colombier 
4131a4050f5SDavid du Colombier 	perm = 0;
4141a4050f5SDavid du Colombier 	omode &= 3;
4151a4050f5SDavid du Colombier 	switch(omode) {
4161a4050f5SDavid du Colombier 	case OREAD:
4171a4050f5SDavid du Colombier 		perm = 4;
4181a4050f5SDavid du Colombier 		break;
4191a4050f5SDavid du Colombier 	case OWRITE:
4201a4050f5SDavid du Colombier 		perm = 2;
4211a4050f5SDavid du Colombier 		break;
4221a4050f5SDavid du Colombier 	case ORDWR:
4231a4050f5SDavid du Colombier 		perm = 6;
4241a4050f5SDavid du Colombier 		break;
4251a4050f5SDavid du Colombier 	}
4261a4050f5SDavid du Colombier 
4271a4050f5SDavid du Colombier 	t = TYPE(c->qid);
4281a4050f5SDavid du Colombier 	switch(t) {
4291a4050f5SDavid du Colombier 	default:
4301a4050f5SDavid du Colombier 		panic("tlsopen");
4311a4050f5SDavid du Colombier 	case Qtopdir:
4321a4050f5SDavid du Colombier 	case Qprotodir:
4331a4050f5SDavid du Colombier 	case Qconvdir:
4341a4050f5SDavid du Colombier 		if(omode != OREAD)
4351a4050f5SDavid du Colombier 			error(Eperm);
4361a4050f5SDavid du Colombier 		break;
4371a4050f5SDavid du Colombier 	case Qclonus:
4381a4050f5SDavid du Colombier 		tr = newtls(c);
4391a4050f5SDavid du Colombier 		if(tr == nil)
4401a4050f5SDavid du Colombier 			error(Enodev);
4411a4050f5SDavid du Colombier 		break;
4421a4050f5SDavid du Colombier 	case Qctl:
4431a4050f5SDavid du Colombier 	case Qdata:
4441a4050f5SDavid du Colombier 	case Qhand:
4451a4050f5SDavid du Colombier 	case Qstatus:
4461a4050f5SDavid du Colombier 	case Qstats:
4471a4050f5SDavid du Colombier 		if((t == Qstatus || t == Qstats) && omode != OREAD)
4481a4050f5SDavid du Colombier 			error(Eperm);
4491a4050f5SDavid du Colombier 		if(waserror()) {
4501a4050f5SDavid du Colombier 			unlock(&tdlock);
4511a4050f5SDavid du Colombier 			nexterror();
4521a4050f5SDavid du Colombier 		}
4531a4050f5SDavid du Colombier 		lock(&tdlock);
4541a4050f5SDavid du Colombier 		pp = &tlsdevs[CONV(c->qid)];
4551a4050f5SDavid du Colombier 		tr = *pp;
4561a4050f5SDavid du Colombier 		if(tr == nil)
4571a4050f5SDavid du Colombier 			error("must open connection using clone");
4581a4050f5SDavid du Colombier 		if((perm & (tr->perm>>6)) != perm
4591a4050f5SDavid du Colombier 		&& (strcmp(up->user, tr->user) != 0
4601a4050f5SDavid du Colombier 		    || (perm & tr->perm) != perm))
4611a4050f5SDavid du Colombier 			error(Eperm);
4621a4050f5SDavid du Colombier 		if(t == Qhand){
4631a4050f5SDavid du Colombier 			if(waserror()){
4641a4050f5SDavid du Colombier 				unlock(&tr->hqlock);
4651a4050f5SDavid du Colombier 				nexterror();
4661a4050f5SDavid du Colombier 			}
4671a4050f5SDavid du Colombier 			lock(&tr->hqlock);
4681a4050f5SDavid du Colombier 			if(tr->handq != nil)
4691a4050f5SDavid du Colombier 				error(Einuse);
470*ec59a3ddSDavid du Colombier 			tr->handq = qopen(2 * MaxCipherRecLen, 0, 0, nil);
4711a4050f5SDavid du Colombier 			if(tr->handq == nil)
4721a4050f5SDavid du Colombier 				error("cannot allocate handshake queue");
4731a4050f5SDavid du Colombier 			tr->hqref = 1;
4741a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
4751a4050f5SDavid du Colombier 			poperror();
4761a4050f5SDavid du Colombier 		}
4771a4050f5SDavid du Colombier 		tr->ref++;
4781a4050f5SDavid du Colombier 		unlock(&tdlock);
4791a4050f5SDavid du Colombier 		poperror();
4801a4050f5SDavid du Colombier 		break;
4811a4050f5SDavid du Colombier 	case Qencalgs:
4821a4050f5SDavid du Colombier 	case Qhashalgs:
4831a4050f5SDavid du Colombier 		if(omode != OREAD)
4841a4050f5SDavid du Colombier 			error(Eperm);
4851a4050f5SDavid du Colombier 		break;
4861a4050f5SDavid du Colombier 	}
4871a4050f5SDavid du Colombier 	c->mode = openmode(omode);
4881a4050f5SDavid du Colombier 	c->flag |= COPEN;
4891a4050f5SDavid du Colombier 	c->offset = 0;
4901a4050f5SDavid du Colombier 	c->iounit = qiomaxatomic;
4911a4050f5SDavid du Colombier 	return c;
4921a4050f5SDavid du Colombier }
4931a4050f5SDavid du Colombier 
4941a4050f5SDavid du Colombier static int
tlswstat(Chan * c,uchar * dp,int n)4951a4050f5SDavid du Colombier tlswstat(Chan *c, uchar *dp, int n)
4961a4050f5SDavid du Colombier {
4971a4050f5SDavid du Colombier 	Dir *d;
4981a4050f5SDavid du Colombier 	TlsRec *tr;
4991a4050f5SDavid du Colombier 	int rv;
5001a4050f5SDavid du Colombier 
5011a4050f5SDavid du Colombier 	d = nil;
5021a4050f5SDavid du Colombier 	if(waserror()){
5031a4050f5SDavid du Colombier 		free(d);
5041a4050f5SDavid du Colombier 		unlock(&tdlock);
5051a4050f5SDavid du Colombier 		nexterror();
5061a4050f5SDavid du Colombier 	}
5071a4050f5SDavid du Colombier 
5081a4050f5SDavid du Colombier 	lock(&tdlock);
5091a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
5101a4050f5SDavid du Colombier 	if(tr == nil)
5111a4050f5SDavid du Colombier 		error(Ebadusefd);
5121a4050f5SDavid du Colombier 	if(strcmp(tr->user, up->user) != 0)
5131a4050f5SDavid du Colombier 		error(Eperm);
5141a4050f5SDavid du Colombier 
5151a4050f5SDavid du Colombier 	d = smalloc(n + sizeof *d);
5161a4050f5SDavid du Colombier 	rv = convM2D(dp, n, &d[0], (char*) &d[1]);
5171a4050f5SDavid du Colombier 	if(rv == 0)
5181a4050f5SDavid du Colombier 		error(Eshortstat);
5191a4050f5SDavid du Colombier 	if(!emptystr(d->uid))
5201a4050f5SDavid du Colombier 		kstrdup(&tr->user, d->uid);
5211a4050f5SDavid du Colombier 	if(d->mode != ~0UL)
5221a4050f5SDavid du Colombier 		tr->perm = d->mode;
5231a4050f5SDavid du Colombier 
5241a4050f5SDavid du Colombier 	free(d);
5251a4050f5SDavid du Colombier 	poperror();
5261a4050f5SDavid du Colombier 	unlock(&tdlock);
5271a4050f5SDavid du Colombier 
5281a4050f5SDavid du Colombier 	return rv;
5291a4050f5SDavid du Colombier }
5301a4050f5SDavid du Colombier 
5311a4050f5SDavid du Colombier static void
dechandq(TlsRec * tr)5321a4050f5SDavid du Colombier dechandq(TlsRec *tr)
5331a4050f5SDavid du Colombier {
5341a4050f5SDavid du Colombier 	lock(&tr->hqlock);
5351a4050f5SDavid du Colombier 	if(--tr->hqref == 0){
5361a4050f5SDavid du Colombier 		if(tr->handq != nil){
5371a4050f5SDavid du Colombier 			qfree(tr->handq);
5381a4050f5SDavid du Colombier 			tr->handq = nil;
5391a4050f5SDavid du Colombier 		}
5401a4050f5SDavid du Colombier 		if(tr->hprocessed != nil){
5411a4050f5SDavid du Colombier 			freeb(tr->hprocessed);
5421a4050f5SDavid du Colombier 			tr->hprocessed = nil;
5431a4050f5SDavid du Colombier 		}
5441a4050f5SDavid du Colombier 	}
5451a4050f5SDavid du Colombier 	unlock(&tr->hqlock);
5461a4050f5SDavid du Colombier }
5471a4050f5SDavid du Colombier 
5481a4050f5SDavid du Colombier static void
tlsclose(Chan * c)5491a4050f5SDavid du Colombier tlsclose(Chan *c)
5501a4050f5SDavid du Colombier {
5511a4050f5SDavid du Colombier 	TlsRec *tr;
5521a4050f5SDavid du Colombier 	int t;
5531a4050f5SDavid du Colombier 
5541a4050f5SDavid du Colombier 	t = TYPE(c->qid);
5551a4050f5SDavid du Colombier 	switch(t) {
5561a4050f5SDavid du Colombier 	case Qctl:
5571a4050f5SDavid du Colombier 	case Qdata:
5581a4050f5SDavid du Colombier 	case Qhand:
5591a4050f5SDavid du Colombier 	case Qstatus:
5601a4050f5SDavid du Colombier 	case Qstats:
5611a4050f5SDavid du Colombier 		if((c->flag & COPEN) == 0)
5621a4050f5SDavid du Colombier 			break;
5631a4050f5SDavid du Colombier 
5641a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
5651a4050f5SDavid du Colombier 		if(tr == nil)
5661a4050f5SDavid du Colombier 			break;
5671a4050f5SDavid du Colombier 
5681a4050f5SDavid du Colombier 		if(t == Qhand)
5691a4050f5SDavid du Colombier 			dechandq(tr);
5701a4050f5SDavid du Colombier 
5711a4050f5SDavid du Colombier 		lock(&tdlock);
5721a4050f5SDavid du Colombier 		if(--tr->ref > 0) {
5731a4050f5SDavid du Colombier 			unlock(&tdlock);
5741a4050f5SDavid du Colombier 			return;
5751a4050f5SDavid du Colombier 		}
5761a4050f5SDavid du Colombier 		tlsdevs[CONV(c->qid)] = nil;
5771a4050f5SDavid du Colombier 		unlock(&tdlock);
5781a4050f5SDavid du Colombier 
5791a4050f5SDavid du Colombier 		if(tr->c != nil && !waserror()){
5801a4050f5SDavid du Colombier 			checkstate(tr, 0, SOpen|SHandshake|SRClose);
5811a4050f5SDavid du Colombier 			sendAlert(tr, ECloseNotify);
5821a4050f5SDavid du Colombier 			poperror();
5831a4050f5SDavid du Colombier 		}
5841a4050f5SDavid du Colombier 		tlshangup(tr);
5851a4050f5SDavid du Colombier 		if(tr->c != nil)
5861a4050f5SDavid du Colombier 			cclose(tr->c);
5871a4050f5SDavid du Colombier 		freeSec(tr->in.sec);
5881a4050f5SDavid du Colombier 		freeSec(tr->in.new);
5891a4050f5SDavid du Colombier 		freeSec(tr->out.sec);
5901a4050f5SDavid du Colombier 		freeSec(tr->out.new);
5911a4050f5SDavid du Colombier 		free(tr->user);
5921a4050f5SDavid du Colombier 		free(tr);
5931a4050f5SDavid du Colombier 		break;
5941a4050f5SDavid du Colombier 	}
5951a4050f5SDavid du Colombier }
5961a4050f5SDavid du Colombier 
5971a4050f5SDavid du Colombier /*
5981a4050f5SDavid du Colombier  *  make sure we have at least 'n' bytes in list 'l'
5991a4050f5SDavid du Colombier  */
6001a4050f5SDavid du Colombier static void
ensure(TlsRec * s,Block ** l,int n)6011a4050f5SDavid du Colombier ensure(TlsRec *s, Block **l, int n)
6021a4050f5SDavid du Colombier {
6031a4050f5SDavid du Colombier 	int sofar, i;
6041a4050f5SDavid du Colombier 	Block *b, *bl;
6051a4050f5SDavid du Colombier 
6061a4050f5SDavid du Colombier 	sofar = 0;
6071a4050f5SDavid du Colombier 	for(b = *l; b; b = b->next){
6081a4050f5SDavid du Colombier 		sofar += BLEN(b);
6091a4050f5SDavid du Colombier 		if(sofar >= n)
6101a4050f5SDavid du Colombier 			return;
6111a4050f5SDavid du Colombier 		l = &b->next;
6121a4050f5SDavid du Colombier 	}
6131a4050f5SDavid du Colombier 
6141a4050f5SDavid du Colombier 	while(sofar < n){
6151a4050f5SDavid du Colombier 		bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
6161a4050f5SDavid du Colombier 		if(bl == 0)
6171a4050f5SDavid du Colombier 			error(Ehungup);
6181a4050f5SDavid du Colombier 		*l = bl;
6191a4050f5SDavid du Colombier 		i = 0;
6201a4050f5SDavid du Colombier 		for(b = bl; b; b = b->next){
6211a4050f5SDavid du Colombier 			i += BLEN(b);
6221a4050f5SDavid du Colombier 			l = &b->next;
6231a4050f5SDavid du Colombier 		}
6241a4050f5SDavid du Colombier 		if(i == 0)
6251a4050f5SDavid du Colombier 			error(Ehungup);
6261a4050f5SDavid du Colombier 		sofar += i;
6271a4050f5SDavid du Colombier 	}
6281a4050f5SDavid du Colombier if(s->debug) pprint("ensure read %d\n", sofar);
6291a4050f5SDavid du Colombier }
6301a4050f5SDavid du Colombier 
6311a4050f5SDavid du Colombier /*
6321a4050f5SDavid du Colombier  *  copy 'n' bytes from 'l' into 'p' and free
6331a4050f5SDavid du Colombier  *  the bytes in 'l'
6341a4050f5SDavid du Colombier  */
6351a4050f5SDavid du Colombier static void
consume(Block ** l,uchar * p,int n)6361a4050f5SDavid du Colombier consume(Block **l, uchar *p, int n)
6371a4050f5SDavid du Colombier {
6381a4050f5SDavid du Colombier 	Block *b;
6391a4050f5SDavid du Colombier 	int i;
6401a4050f5SDavid du Colombier 
6411a4050f5SDavid du Colombier 	for(; *l && n > 0; n -= i){
6421a4050f5SDavid du Colombier 		b = *l;
6431a4050f5SDavid du Colombier 		i = BLEN(b);
6441a4050f5SDavid du Colombier 		if(i > n)
6451a4050f5SDavid du Colombier 			i = n;
6461a4050f5SDavid du Colombier 		memmove(p, b->rp, i);
6471a4050f5SDavid du Colombier 		b->rp += i;
6481a4050f5SDavid du Colombier 		p += i;
6491a4050f5SDavid du Colombier 		if(BLEN(b) < 0)
6501a4050f5SDavid du Colombier 			panic("consume");
6511a4050f5SDavid du Colombier 		if(BLEN(b))
6521a4050f5SDavid du Colombier 			break;
6531a4050f5SDavid du Colombier 		*l = b->next;
6541a4050f5SDavid du Colombier 		freeb(b);
6551a4050f5SDavid du Colombier 	}
6561a4050f5SDavid du Colombier }
6571a4050f5SDavid du Colombier 
6581a4050f5SDavid du Colombier /*
6591a4050f5SDavid du Colombier  *  give back n bytes
6601a4050f5SDavid du Colombier  */
6611a4050f5SDavid du Colombier static void
regurgitate(TlsRec * s,uchar * p,int n)6621a4050f5SDavid du Colombier regurgitate(TlsRec *s, uchar *p, int n)
6631a4050f5SDavid du Colombier {
6641a4050f5SDavid du Colombier 	Block *b;
6651a4050f5SDavid du Colombier 
6661a4050f5SDavid du Colombier 	if(n <= 0)
6671a4050f5SDavid du Colombier 		return;
6681a4050f5SDavid du Colombier 	b = s->unprocessed;
6691a4050f5SDavid du Colombier 	if(s->unprocessed == nil || b->rp - b->base < n) {
6701a4050f5SDavid du Colombier 		b = allocb(n);
6711a4050f5SDavid du Colombier 		memmove(b->wp, p, n);
6721a4050f5SDavid du Colombier 		b->wp += n;
6731a4050f5SDavid du Colombier 		b->next = s->unprocessed;
6741a4050f5SDavid du Colombier 		s->unprocessed = b;
6751a4050f5SDavid du Colombier 	} else {
6761a4050f5SDavid du Colombier 		b->rp -= n;
6771a4050f5SDavid du Colombier 		memmove(b->rp, p, n);
6781a4050f5SDavid du Colombier 	}
6791a4050f5SDavid du Colombier }
6801a4050f5SDavid du Colombier 
6811a4050f5SDavid du Colombier /*
6821a4050f5SDavid du Colombier  *  remove at most n bytes from the queue
6831a4050f5SDavid du Colombier  */
6841a4050f5SDavid du Colombier static Block*
qgrab(Block ** l,int n)6851a4050f5SDavid du Colombier qgrab(Block **l, int n)
6861a4050f5SDavid du Colombier {
6871a4050f5SDavid du Colombier 	Block *bb, *b;
6881a4050f5SDavid du Colombier 	int i;
6891a4050f5SDavid du Colombier 
6901a4050f5SDavid du Colombier 	b = *l;
6911a4050f5SDavid du Colombier 	if(BLEN(b) == n){
6921a4050f5SDavid du Colombier 		*l = b->next;
6931a4050f5SDavid du Colombier 		b->next = nil;
6941a4050f5SDavid du Colombier 		return b;
6951a4050f5SDavid du Colombier 	}
6961a4050f5SDavid du Colombier 
6971a4050f5SDavid du Colombier 	i = 0;
6981a4050f5SDavid du Colombier 	for(bb = b; bb != nil && i < n; bb = bb->next)
6991a4050f5SDavid du Colombier 		i += BLEN(bb);
7001a4050f5SDavid du Colombier 	if(i > n)
7011a4050f5SDavid du Colombier 		i = n;
7021a4050f5SDavid du Colombier 
7031a4050f5SDavid du Colombier 	bb = allocb(i);
7041a4050f5SDavid du Colombier 	consume(l, bb->wp, i);
7051a4050f5SDavid du Colombier 	bb->wp += i;
7061a4050f5SDavid du Colombier 	return bb;
7071a4050f5SDavid du Colombier }
7081a4050f5SDavid du Colombier 
7091a4050f5SDavid du Colombier static void
tlsclosed(TlsRec * tr,int new)7101a4050f5SDavid du Colombier tlsclosed(TlsRec *tr, int new)
7111a4050f5SDavid du Colombier {
7121a4050f5SDavid du Colombier 	lock(&tr->statelk);
7131a4050f5SDavid du Colombier 	if(tr->state == SOpen || tr->state == SHandshake)
7141a4050f5SDavid du Colombier 		tr->state = new;
7151a4050f5SDavid du Colombier 	else if((new | tr->state) == (SRClose|SLClose))
7161a4050f5SDavid du Colombier 		tr->state = SClosed;
7171a4050f5SDavid du Colombier 	unlock(&tr->statelk);
7181a4050f5SDavid du Colombier 	alertHand(tr, "close notify");
7191a4050f5SDavid du Colombier }
7201a4050f5SDavid du Colombier 
7211a4050f5SDavid du Colombier /*
7221a4050f5SDavid du Colombier  *  read and process one tls record layer message
7231a4050f5SDavid du Colombier  *  must be called with tr->in.io held
7241a4050f5SDavid du Colombier  *  We can't let Eintrs lose data, since doing so will get
7251a4050f5SDavid du Colombier  *  us out of sync with the sender and break the reliablity
7261a4050f5SDavid du Colombier  *  of the channel.  Eintr only happens during the reads in
7271a4050f5SDavid du Colombier  *  consume.  Therefore we put back any bytes consumed before
7281a4050f5SDavid du Colombier  *  the last call to ensure.
7291a4050f5SDavid du Colombier  */
7301a4050f5SDavid du Colombier static void
tlsrecread(TlsRec * tr)7311a4050f5SDavid du Colombier tlsrecread(TlsRec *tr)
7321a4050f5SDavid du Colombier {
7331a4050f5SDavid du Colombier 	OneWay *volatile in;
7341a4050f5SDavid du Colombier 	Block *volatile b;
7351a4050f5SDavid du Colombier 	uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
7361a4050f5SDavid du Colombier 	int volatile nconsumed;
7371a4050f5SDavid du Colombier 	int len, type, ver, unpad_len;
7381a4050f5SDavid du Colombier 
7391a4050f5SDavid du Colombier 	nconsumed = 0;
7401a4050f5SDavid du Colombier 	if(waserror()){
7411a4050f5SDavid du Colombier 		if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
7421a4050f5SDavid du Colombier 			regurgitate(tr, header, nconsumed);
7431a4050f5SDavid du Colombier 			poperror();
7441a4050f5SDavid du Colombier 		}else
7451a4050f5SDavid du Colombier 			tlsError(tr, "channel error");
7461a4050f5SDavid du Colombier 		nexterror();
7471a4050f5SDavid du Colombier 	}
7481a4050f5SDavid du Colombier 	ensure(tr, &tr->unprocessed, RecHdrLen);
7491a4050f5SDavid du Colombier 	consume(&tr->unprocessed, header, RecHdrLen);
7501a4050f5SDavid du Colombier if(tr->debug)pprint("consumed %d header\n", RecHdrLen);
7511a4050f5SDavid du Colombier 	nconsumed = RecHdrLen;
7521a4050f5SDavid du Colombier 
7531a4050f5SDavid du Colombier 	if((tr->handin == 0) && (header[0] & 0x80)){
7541a4050f5SDavid du Colombier 		/* Cope with an SSL3 ClientHello expressed in SSL2 record format.
7551a4050f5SDavid du Colombier 			This is sent by some clients that we must interoperate
7561a4050f5SDavid du Colombier 			with, such as Java's JSSE and Microsoft's Internet Explorer. */
7571a4050f5SDavid du Colombier 		len = (get16(header) & ~0x8000) - 3;
7581a4050f5SDavid du Colombier 		type = header[2];
7591a4050f5SDavid du Colombier 		ver = get16(header + 3);
7601a4050f5SDavid du Colombier 		if(type != SSL2ClientHello || len < 22)
7611a4050f5SDavid du Colombier 			rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
7621a4050f5SDavid du Colombier 	}else{  /* normal SSL3 record format */
7631a4050f5SDavid du Colombier 		type = header[0];
7641a4050f5SDavid du Colombier 		ver = get16(header+1);
7651a4050f5SDavid du Colombier 		len = get16(header+3);
7661a4050f5SDavid du Colombier 	}
7671a4050f5SDavid du Colombier 	if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
7681a4050f5SDavid du Colombier 		rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
7691a4050f5SDavid du Colombier 			tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
7701a4050f5SDavid du Colombier 	if(len > MaxCipherRecLen || len < 0)
7711a4050f5SDavid du Colombier 		rcvError(tr, ERecordOverflow, "record message too long %d", len);
7721a4050f5SDavid du Colombier 	ensure(tr, &tr->unprocessed, len);
7731a4050f5SDavid du Colombier 	nconsumed = 0;
7741a4050f5SDavid du Colombier 	poperror();
7751a4050f5SDavid du Colombier 
7761a4050f5SDavid du Colombier 	/*
7771a4050f5SDavid du Colombier 	 * If an Eintr happens after this, we'll get out of sync.
7781a4050f5SDavid du Colombier 	 * Make sure nothing we call can sleep.
7791a4050f5SDavid du Colombier 	 * Errors are ok, as they kill the connection.
7801a4050f5SDavid du Colombier 	 * Luckily, allocb won't sleep, it'll just error out.
7811a4050f5SDavid du Colombier 	 */
7821a4050f5SDavid du Colombier 	b = nil;
7831a4050f5SDavid du Colombier 	if(waserror()){
7841a4050f5SDavid du Colombier 		if(b != nil)
7851a4050f5SDavid du Colombier 			freeb(b);
7861a4050f5SDavid du Colombier 		tlsError(tr, "channel error");
7871a4050f5SDavid du Colombier 		nexterror();
7881a4050f5SDavid du Colombier 	}
7891a4050f5SDavid du Colombier 	b = qgrab(&tr->unprocessed, len);
7901a4050f5SDavid du Colombier if(tr->debug) pprint("consumed unprocessed %d\n", len);
7911a4050f5SDavid du Colombier 
7921a4050f5SDavid du Colombier 	in = &tr->in;
7931a4050f5SDavid du Colombier 	if(waserror()){
7941a4050f5SDavid du Colombier 		qunlock(&in->seclock);
7951a4050f5SDavid du Colombier 		nexterror();
7961a4050f5SDavid du Colombier 	}
7971a4050f5SDavid du Colombier 	qlock(&in->seclock);
7981a4050f5SDavid du Colombier 	p = b->rp;
7991a4050f5SDavid du Colombier 	if(in->sec != nil) {
8001a4050f5SDavid du Colombier 		/* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
8011a4050f5SDavid du Colombier 		        should look alike, including timing of the response. */
8021a4050f5SDavid du Colombier 		unpad_len = (*in->sec->dec)(in->sec, p, len);
8031a4050f5SDavid du Colombier 		if(unpad_len >= in->sec->maclen)
8041a4050f5SDavid du Colombier 			len = unpad_len - in->sec->maclen;
8051a4050f5SDavid du Colombier if(tr->debug) pprint("decrypted %d\n", unpad_len);
8061a4050f5SDavid du Colombier if(tr->debug) pdump(unpad_len, p, "decrypted:");
8071a4050f5SDavid du Colombier 
8081a4050f5SDavid du Colombier 		/* update length */
8091a4050f5SDavid du Colombier 		put16(header+3, len);
8101a4050f5SDavid du Colombier 		put64(seq, in->seq);
8111a4050f5SDavid du Colombier 		in->seq++;
8121a4050f5SDavid du Colombier 		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
8131a4050f5SDavid du Colombier 		if(unpad_len < in->sec->maclen)
8141a4050f5SDavid du Colombier 			rcvError(tr, EBadRecordMac, "short record mac");
8151a4050f5SDavid du Colombier 		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
8161a4050f5SDavid du Colombier 			rcvError(tr, EBadRecordMac, "record mac mismatch");
8171a4050f5SDavid du Colombier 		b->wp = b->rp + len;
8181a4050f5SDavid du Colombier 	}
8191a4050f5SDavid du Colombier 	qunlock(&in->seclock);
8201a4050f5SDavid du Colombier 	poperror();
8211a4050f5SDavid du Colombier 	if(len < 0)
8221a4050f5SDavid du Colombier 		rcvError(tr, EDecodeError, "runt record message");
8231a4050f5SDavid du Colombier 
8241a4050f5SDavid du Colombier 	switch(type) {
8251a4050f5SDavid du Colombier 	default:
8261a4050f5SDavid du Colombier 		rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
8271a4050f5SDavid du Colombier 		break;
8281a4050f5SDavid du Colombier 	case RChangeCipherSpec:
8291a4050f5SDavid du Colombier 		if(len != 1 || p[0] != 1)
8301a4050f5SDavid du Colombier 			rcvError(tr, EDecodeError, "invalid change cipher spec");
8311a4050f5SDavid du Colombier 		qlock(&in->seclock);
8321a4050f5SDavid du Colombier 		if(in->new == nil){
8331a4050f5SDavid du Colombier 			qunlock(&in->seclock);
8341a4050f5SDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
8351a4050f5SDavid du Colombier 		}
8361a4050f5SDavid du Colombier 		freeSec(in->sec);
8371a4050f5SDavid du Colombier 		in->sec = in->new;
8381a4050f5SDavid du Colombier 		in->new = nil;
8391a4050f5SDavid du Colombier 		in->seq = 0;
8401a4050f5SDavid du Colombier 		qunlock(&in->seclock);
8411a4050f5SDavid du Colombier 		break;
8421a4050f5SDavid du Colombier 	case RAlert:
8431a4050f5SDavid du Colombier 		if(len != 2)
8441a4050f5SDavid du Colombier 			rcvError(tr, EDecodeError, "invalid alert");
8451a4050f5SDavid du Colombier 		if(p[0] == 2)
8461a4050f5SDavid du Colombier 			rcvAlert(tr, p[1]);
8471a4050f5SDavid du Colombier 		if(p[0] != 1)
8481a4050f5SDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert fatal code");
8491a4050f5SDavid du Colombier 
8501a4050f5SDavid du Colombier 		/*
8511a4050f5SDavid du Colombier 		 * propate non-fatal alerts to handshaker
8521a4050f5SDavid du Colombier 		 */
8531a4050f5SDavid du Colombier 		if(p[1] == ECloseNotify) {
8541a4050f5SDavid du Colombier 			tlsclosed(tr, SRClose);
8551a4050f5SDavid du Colombier 			if(tr->opened)
8561a4050f5SDavid du Colombier 				error("tls hungup");
8571a4050f5SDavid du Colombier 			error("close notify");
8581a4050f5SDavid du Colombier 		}
8591a4050f5SDavid du Colombier 		if(p[1] == ENoRenegotiation)
8601a4050f5SDavid du Colombier 			alertHand(tr, "no renegotiation");
8611a4050f5SDavid du Colombier 		else if(p[1] == EUserCanceled)
8621a4050f5SDavid du Colombier 			alertHand(tr, "handshake canceled by user");
8631a4050f5SDavid du Colombier 		else
8641a4050f5SDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert code");
8651a4050f5SDavid du Colombier 		break;
8661a4050f5SDavid du Colombier 	case RHandshake:
8671a4050f5SDavid du Colombier 		/*
8681a4050f5SDavid du Colombier 		 * don't worry about dropping the block
8691a4050f5SDavid du Colombier 		 * qbwrite always queues even if flow controlled and interrupted.
8701a4050f5SDavid du Colombier 		 *
8711a4050f5SDavid du Colombier 		 * if there isn't any handshaker, ignore the request,
8721a4050f5SDavid du Colombier 		 * but notify the other side we are doing so.
8731a4050f5SDavid du Colombier 		 */
8741a4050f5SDavid du Colombier 		lock(&tr->hqlock);
8751a4050f5SDavid du Colombier 		if(tr->handq != nil){
8761a4050f5SDavid du Colombier 			tr->hqref++;
8771a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
8781a4050f5SDavid du Colombier 			if(waserror()){
8791a4050f5SDavid du Colombier 				dechandq(tr);
8801a4050f5SDavid du Colombier 				nexterror();
8811a4050f5SDavid du Colombier 			}
8821a4050f5SDavid du Colombier 			b = padblock(b, 1);
8831a4050f5SDavid du Colombier 			*b->rp = RHandshake;
8841a4050f5SDavid du Colombier 			qbwrite(tr->handq, b);
8851a4050f5SDavid du Colombier 			b = nil;
8861a4050f5SDavid du Colombier 			poperror();
8871a4050f5SDavid du Colombier 			dechandq(tr);
8881a4050f5SDavid du Colombier 		}else{
8891a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
8901a4050f5SDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
8911a4050f5SDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
8921a4050f5SDavid du Colombier 				poperror();
8931a4050f5SDavid du Colombier 			}
8941a4050f5SDavid du Colombier 		}
8951a4050f5SDavid du Colombier 		break;
8961a4050f5SDavid du Colombier 	case SSL2ClientHello:
8971a4050f5SDavid du Colombier 		lock(&tr->hqlock);
8981a4050f5SDavid du Colombier 		if(tr->handq != nil){
8991a4050f5SDavid du Colombier 			tr->hqref++;
9001a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
9011a4050f5SDavid du Colombier 			if(waserror()){
9021a4050f5SDavid du Colombier 				dechandq(tr);
9031a4050f5SDavid du Colombier 				nexterror();
9041a4050f5SDavid du Colombier 			}
9051a4050f5SDavid du Colombier 			/* Pass the SSL2 format data, so that the handshake code can compute
9061a4050f5SDavid du Colombier 				the correct checksums.  HSSL2ClientHello = HandshakeType 9 is
9071a4050f5SDavid du Colombier 				unused in RFC2246. */
9081a4050f5SDavid du Colombier 			b = padblock(b, 8);
9091a4050f5SDavid du Colombier 			b->rp[0] = RHandshake;
9101a4050f5SDavid du Colombier 			b->rp[1] = HSSL2ClientHello;
9111a4050f5SDavid du Colombier 			put24(&b->rp[2], len+3);
9121a4050f5SDavid du Colombier 			b->rp[5] = SSL2ClientHello;
9131a4050f5SDavid du Colombier 			put16(&b->rp[6], ver);
9141a4050f5SDavid du Colombier 			qbwrite(tr->handq, b);
9151a4050f5SDavid du Colombier 			b = nil;
9161a4050f5SDavid du Colombier 			poperror();
9171a4050f5SDavid du Colombier 			dechandq(tr);
9181a4050f5SDavid du Colombier 		}else{
9191a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
9201a4050f5SDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
9211a4050f5SDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
9221a4050f5SDavid du Colombier 				poperror();
9231a4050f5SDavid du Colombier 			}
9241a4050f5SDavid du Colombier 		}
9251a4050f5SDavid du Colombier 		break;
9261a4050f5SDavid du Colombier 	case RApplication:
9271a4050f5SDavid du Colombier 		if(!tr->opened)
9281a4050f5SDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
9291a4050f5SDavid du Colombier 		if(BLEN(b) > 0){
9301a4050f5SDavid du Colombier 			tr->processed = b;
9311a4050f5SDavid du Colombier 			b = nil;
9321a4050f5SDavid du Colombier 		}
9331a4050f5SDavid du Colombier 		break;
9341a4050f5SDavid du Colombier 	}
9351a4050f5SDavid du Colombier 	if(b != nil)
9361a4050f5SDavid du Colombier 		freeb(b);
9371a4050f5SDavid du Colombier 	poperror();
9381a4050f5SDavid du Colombier }
9391a4050f5SDavid du Colombier 
9401a4050f5SDavid du Colombier /*
9411a4050f5SDavid du Colombier  * got a fatal alert message
9421a4050f5SDavid du Colombier  */
9431a4050f5SDavid du Colombier static void
rcvAlert(TlsRec * tr,int err)9441a4050f5SDavid du Colombier rcvAlert(TlsRec *tr, int err)
9451a4050f5SDavid du Colombier {
9461a4050f5SDavid du Colombier 	char *s;
9471a4050f5SDavid du Colombier 	int i;
9481a4050f5SDavid du Colombier 
9491a4050f5SDavid du Colombier 	s = "unknown error";
9501a4050f5SDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++){
9511a4050f5SDavid du Colombier 		if(tlserrs[i].err == err){
9521a4050f5SDavid du Colombier 			s = tlserrs[i].msg;
9531a4050f5SDavid du Colombier 			break;
9541a4050f5SDavid du Colombier 		}
9551a4050f5SDavid du Colombier 	}
9561a4050f5SDavid du Colombier if(tr->debug) pprint("rcvAlert: %s\n", s);
9571a4050f5SDavid du Colombier 
9581a4050f5SDavid du Colombier 	tlsError(tr, s);
9591a4050f5SDavid du Colombier 	if(!tr->opened)
9601a4050f5SDavid du Colombier 		error(s);
9611a4050f5SDavid du Colombier 	error("tls error");
9621a4050f5SDavid du Colombier }
9631a4050f5SDavid du Colombier 
9641a4050f5SDavid du Colombier /*
9651a4050f5SDavid du Colombier  * found an error while decoding the input stream
9661a4050f5SDavid du Colombier  */
9671a4050f5SDavid du Colombier static void
rcvError(TlsRec * tr,int err,char * fmt,...)9681a4050f5SDavid du Colombier rcvError(TlsRec *tr, int err, char *fmt, ...)
9691a4050f5SDavid du Colombier {
9701a4050f5SDavid du Colombier 	char msg[ERRMAX];
9711a4050f5SDavid du Colombier 	va_list arg;
9721a4050f5SDavid du Colombier 
9731a4050f5SDavid du Colombier 	va_start(arg, fmt);
9741a4050f5SDavid du Colombier 	vseprint(msg, msg+sizeof(msg), fmt, arg);
9751a4050f5SDavid du Colombier 	va_end(arg);
9761a4050f5SDavid du Colombier if(tr->debug) pprint("rcvError: %s\n", msg);
9771a4050f5SDavid du Colombier 
9781a4050f5SDavid du Colombier 	sendAlert(tr, err);
9791a4050f5SDavid du Colombier 
9801a4050f5SDavid du Colombier 	if(!tr->opened)
9811a4050f5SDavid du Colombier 		error(msg);
9821a4050f5SDavid du Colombier 	error("tls error");
9831a4050f5SDavid du Colombier }
9841a4050f5SDavid du Colombier 
9851a4050f5SDavid du Colombier /*
9861a4050f5SDavid du Colombier  * make sure the next hand operation returns with a 'msg' error
9871a4050f5SDavid du Colombier  */
9881a4050f5SDavid du Colombier static void
alertHand(TlsRec * tr,char * msg)9891a4050f5SDavid du Colombier alertHand(TlsRec *tr, char *msg)
9901a4050f5SDavid du Colombier {
9911a4050f5SDavid du Colombier 	Block *b;
9921a4050f5SDavid du Colombier 	int n;
9931a4050f5SDavid du Colombier 
9941a4050f5SDavid du Colombier 	lock(&tr->hqlock);
9951a4050f5SDavid du Colombier 	if(tr->handq == nil){
9961a4050f5SDavid du Colombier 		unlock(&tr->hqlock);
9971a4050f5SDavid du Colombier 		return;
9981a4050f5SDavid du Colombier 	}
9991a4050f5SDavid du Colombier 	tr->hqref++;
10001a4050f5SDavid du Colombier 	unlock(&tr->hqlock);
10011a4050f5SDavid du Colombier 
10021a4050f5SDavid du Colombier 	n = strlen(msg);
10031a4050f5SDavid du Colombier 	if(waserror()){
10041a4050f5SDavid du Colombier 		dechandq(tr);
10051a4050f5SDavid du Colombier 		nexterror();
10061a4050f5SDavid du Colombier 	}
10071a4050f5SDavid du Colombier 	b = allocb(n + 2);
10081a4050f5SDavid du Colombier 	*b->wp++ = RAlert;
10091a4050f5SDavid du Colombier 	memmove(b->wp, msg, n + 1);
10101a4050f5SDavid du Colombier 	b->wp += n + 1;
10111a4050f5SDavid du Colombier 
10121a4050f5SDavid du Colombier 	qbwrite(tr->handq, b);
10131a4050f5SDavid du Colombier 
10141a4050f5SDavid du Colombier 	poperror();
10151a4050f5SDavid du Colombier 	dechandq(tr);
10161a4050f5SDavid du Colombier }
10171a4050f5SDavid du Colombier 
10181a4050f5SDavid du Colombier static void
checkstate(TlsRec * tr,int ishand,int ok)10191a4050f5SDavid du Colombier checkstate(TlsRec *tr, int ishand, int ok)
10201a4050f5SDavid du Colombier {
10211a4050f5SDavid du Colombier 	int state;
10221a4050f5SDavid du Colombier 
10231a4050f5SDavid du Colombier 	lock(&tr->statelk);
10241a4050f5SDavid du Colombier 	state = tr->state;
10251a4050f5SDavid du Colombier 	unlock(&tr->statelk);
10261a4050f5SDavid du Colombier 	if(state & ok)
10271a4050f5SDavid du Colombier 		return;
10281a4050f5SDavid du Colombier 	switch(state){
10291a4050f5SDavid du Colombier 	case SHandshake:
10301a4050f5SDavid du Colombier 	case SOpen:
10311a4050f5SDavid du Colombier 		break;
10321a4050f5SDavid du Colombier 	case SError:
10331a4050f5SDavid du Colombier 	case SAlert:
10341a4050f5SDavid du Colombier 		if(ishand)
10351a4050f5SDavid du Colombier 			error(tr->err);
10361a4050f5SDavid du Colombier 		error("tls error");
10371a4050f5SDavid du Colombier 	case SRClose:
10381a4050f5SDavid du Colombier 	case SLClose:
10391a4050f5SDavid du Colombier 	case SClosed:
10401a4050f5SDavid du Colombier 		error("tls hungup");
10411a4050f5SDavid du Colombier 	}
10421a4050f5SDavid du Colombier 	error("tls improperly configured");
10431a4050f5SDavid du Colombier }
10441a4050f5SDavid du Colombier 
10451a4050f5SDavid du Colombier static Block*
tlsbread(Chan * c,long n,ulong offset)10461a4050f5SDavid du Colombier tlsbread(Chan *c, long n, ulong offset)
10471a4050f5SDavid du Colombier {
10481a4050f5SDavid du Colombier 	int ty;
10491a4050f5SDavid du Colombier 	Block *b;
10501a4050f5SDavid du Colombier 	TlsRec *volatile tr;
10511a4050f5SDavid du Colombier 
10521a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
10531a4050f5SDavid du Colombier 	switch(ty) {
10541a4050f5SDavid du Colombier 	default:
10551a4050f5SDavid du Colombier 		return devbread(c, n, offset);
10561a4050f5SDavid du Colombier 	case Qhand:
10571a4050f5SDavid du Colombier 	case Qdata:
10581a4050f5SDavid du Colombier 		break;
10591a4050f5SDavid du Colombier 	}
10601a4050f5SDavid du Colombier 
10611a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
10621a4050f5SDavid du Colombier 	if(tr == nil)
10631a4050f5SDavid du Colombier 		panic("tlsbread");
10641a4050f5SDavid du Colombier 
10651a4050f5SDavid du Colombier 	if(waserror()){
10661a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
10671a4050f5SDavid du Colombier 		nexterror();
10681a4050f5SDavid du Colombier 	}
10691a4050f5SDavid du Colombier 	qlock(&tr->in.io);
10701a4050f5SDavid du Colombier 	if(ty == Qdata){
10711a4050f5SDavid du Colombier 		checkstate(tr, 0, SOpen);
10721a4050f5SDavid du Colombier 		while(tr->processed == nil)
10731a4050f5SDavid du Colombier 			tlsrecread(tr);
10741a4050f5SDavid du Colombier 
10751a4050f5SDavid du Colombier 		/* return at most what was asked for */
10761a4050f5SDavid du Colombier 		b = qgrab(&tr->processed, n);
10771a4050f5SDavid du Colombier if(tr->debug) pprint("consumed processed %d\n", BLEN(b));
10781a4050f5SDavid du Colombier if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
10791a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
10801a4050f5SDavid du Colombier 		poperror();
10811a4050f5SDavid du Colombier 		tr->datain += BLEN(b);
10821a4050f5SDavid du Colombier 	}else{
10831a4050f5SDavid du Colombier 		checkstate(tr, 1, SOpen|SHandshake|SLClose);
10841a4050f5SDavid du Colombier 
10851a4050f5SDavid du Colombier 		/*
10861a4050f5SDavid du Colombier 		 * it's ok to look at state without the lock
10871a4050f5SDavid du Colombier 		 * since it only protects reading records,
10881a4050f5SDavid du Colombier 		 * and we have that tr->in.io held.
10891a4050f5SDavid du Colombier 		 */
10901a4050f5SDavid du Colombier 		while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
10911a4050f5SDavid du Colombier 			tlsrecread(tr);
10921a4050f5SDavid du Colombier 
10931a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
10941a4050f5SDavid du Colombier 		poperror();
10951a4050f5SDavid du Colombier 
10961a4050f5SDavid du Colombier 		if(waserror()){
10971a4050f5SDavid du Colombier 			qunlock(&tr->hqread);
10981a4050f5SDavid du Colombier 			nexterror();
10991a4050f5SDavid du Colombier 		}
11001a4050f5SDavid du Colombier 		qlock(&tr->hqread);
11011a4050f5SDavid du Colombier 		if(tr->hprocessed == nil){
11021a4050f5SDavid du Colombier 			b = qbread(tr->handq, MaxRecLen + 1);
11031a4050f5SDavid du Colombier 			if(*b->rp++ == RAlert){
11041a4050f5SDavid du Colombier 				kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
11051a4050f5SDavid du Colombier 				freeb(b);
11061a4050f5SDavid du Colombier 				nexterror();
11071a4050f5SDavid du Colombier 			}
11081a4050f5SDavid du Colombier 			tr->hprocessed = b;
11091a4050f5SDavid du Colombier 		}
11101a4050f5SDavid du Colombier 		b = qgrab(&tr->hprocessed, n);
11111a4050f5SDavid du Colombier 		poperror();
11121a4050f5SDavid du Colombier 		qunlock(&tr->hqread);
11131a4050f5SDavid du Colombier 		tr->handin += BLEN(b);
11141a4050f5SDavid du Colombier 	}
11151a4050f5SDavid du Colombier 
11161a4050f5SDavid du Colombier 	return b;
11171a4050f5SDavid du Colombier }
11181a4050f5SDavid du Colombier 
11191a4050f5SDavid du Colombier static long
tlsread(Chan * c,void * a,long n,vlong off)11201a4050f5SDavid du Colombier tlsread(Chan *c, void *a, long n, vlong off)
11211a4050f5SDavid du Colombier {
11221a4050f5SDavid du Colombier 	Block *volatile b;
11231a4050f5SDavid du Colombier 	Block *nb;
11241a4050f5SDavid du Colombier 	uchar *va;
11251a4050f5SDavid du Colombier 	int i, ty;
11261a4050f5SDavid du Colombier 	char *buf, *s, *e;
11271a4050f5SDavid du Colombier 	ulong offset = off;
11281a4050f5SDavid du Colombier 	TlsRec * tr;
11291a4050f5SDavid du Colombier 
11301a4050f5SDavid du Colombier 	if(c->qid.type & QTDIR)
11311a4050f5SDavid du Colombier 		return devdirread(c, a, n, 0, 0, tlsgen);
11321a4050f5SDavid du Colombier 
11331a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
11341a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
11351a4050f5SDavid du Colombier 	switch(ty) {
11361a4050f5SDavid du Colombier 	default:
11371a4050f5SDavid du Colombier 		error(Ebadusefd);
11381a4050f5SDavid du Colombier 	case Qstatus:
11391a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
11401a4050f5SDavid du Colombier 		qlock(&tr->in.seclock);
11411a4050f5SDavid du Colombier 		qlock(&tr->out.seclock);
11421a4050f5SDavid du Colombier 		s = buf;
11431a4050f5SDavid du Colombier 		e = buf + Statlen;
11441a4050f5SDavid du Colombier 		s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
11451a4050f5SDavid du Colombier 		s = seprint(s, e, "Version: 0x%x\n", tr->version);
11461a4050f5SDavid du Colombier 		if(tr->in.sec != nil)
11471a4050f5SDavid du Colombier 			s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
11481a4050f5SDavid du Colombier 		if(tr->in.new != nil)
11491a4050f5SDavid du Colombier 			s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
11501a4050f5SDavid du Colombier 		if(tr->out.sec != nil)
11511a4050f5SDavid du Colombier 			s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
11521a4050f5SDavid du Colombier 		if(tr->out.new != nil)
11531a4050f5SDavid du Colombier 			seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
11541a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
11551a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
11561a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
11571a4050f5SDavid du Colombier 		free(buf);
11581a4050f5SDavid du Colombier 		return n;
11591a4050f5SDavid du Colombier 	case Qstats:
11601a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
11611a4050f5SDavid du Colombier 		s = buf;
11621a4050f5SDavid du Colombier 		e = buf + Statlen;
11631a4050f5SDavid du Colombier 		s = seprint(s, e, "DataIn: %lld\n", tr->datain);
11641a4050f5SDavid du Colombier 		s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
11651a4050f5SDavid du Colombier 		s = seprint(s, e, "HandIn: %lld\n", tr->handin);
11661a4050f5SDavid du Colombier 		seprint(s, e, "HandOut: %lld\n", tr->handout);
11671a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
11681a4050f5SDavid du Colombier 		free(buf);
11691a4050f5SDavid du Colombier 		return n;
11701a4050f5SDavid du Colombier 	case Qctl:
11711a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
11721a4050f5SDavid du Colombier 		snprint(buf, Statlen, "%llud", CONV(c->qid));
11731a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
11741a4050f5SDavid du Colombier 		free(buf);
11751a4050f5SDavid du Colombier 		return n;
11761a4050f5SDavid du Colombier 	case Qdata:
11771a4050f5SDavid du Colombier 	case Qhand:
11781a4050f5SDavid du Colombier 		b = tlsbread(c, n, offset);
11791a4050f5SDavid du Colombier 		break;
11801a4050f5SDavid du Colombier 	case Qencalgs:
11811a4050f5SDavid du Colombier 		return readstr(offset, a, n, encalgs);
11821a4050f5SDavid du Colombier 	case Qhashalgs:
11831a4050f5SDavid du Colombier 		return readstr(offset, a, n, hashalgs);
11841a4050f5SDavid du Colombier 	}
11851a4050f5SDavid du Colombier 
11861a4050f5SDavid du Colombier 	if(waserror()){
11871a4050f5SDavid du Colombier 		freeblist(b);
11881a4050f5SDavid du Colombier 		nexterror();
11891a4050f5SDavid du Colombier 	}
11901a4050f5SDavid du Colombier 
11911a4050f5SDavid du Colombier 	n = 0;
11921a4050f5SDavid du Colombier 	va = a;
11931a4050f5SDavid du Colombier 	for(nb = b; nb; nb = nb->next){
11941a4050f5SDavid du Colombier 		i = BLEN(nb);
11951a4050f5SDavid du Colombier 		memmove(va+n, nb->rp, i);
11961a4050f5SDavid du Colombier 		n += i;
11971a4050f5SDavid du Colombier 	}
11981a4050f5SDavid du Colombier 
11991a4050f5SDavid du Colombier 	freeblist(b);
12001a4050f5SDavid du Colombier 	poperror();
12011a4050f5SDavid du Colombier 
12021a4050f5SDavid du Colombier 	return n;
12031a4050f5SDavid du Colombier }
12041a4050f5SDavid du Colombier 
12051a4050f5SDavid du Colombier /*
12061a4050f5SDavid du Colombier  *  write a block in tls records
12071a4050f5SDavid du Colombier  */
12081a4050f5SDavid du Colombier static void
tlsrecwrite(TlsRec * tr,int type,Block * b)12091a4050f5SDavid du Colombier tlsrecwrite(TlsRec *tr, int type, Block *b)
12101a4050f5SDavid du Colombier {
12111a4050f5SDavid du Colombier 	Block *volatile bb;
12121a4050f5SDavid du Colombier 	Block *nb;
12131a4050f5SDavid du Colombier 	uchar *p, seq[8];
12141a4050f5SDavid du Colombier 	OneWay *volatile out;
12151a4050f5SDavid du Colombier 	int n, maclen, pad, ok;
12161a4050f5SDavid du Colombier 
12171a4050f5SDavid du Colombier 	out = &tr->out;
12181a4050f5SDavid du Colombier 	bb = b;
12191a4050f5SDavid du Colombier 	if(waserror()){
12201a4050f5SDavid du Colombier 		qunlock(&out->io);
12211a4050f5SDavid du Colombier 		if(bb != nil)
12221a4050f5SDavid du Colombier 			freeb(bb);
12231a4050f5SDavid du Colombier 		nexterror();
12241a4050f5SDavid du Colombier 	}
12251a4050f5SDavid du Colombier 	qlock(&out->io);
12261a4050f5SDavid du Colombier if(tr->debug)pprint("send %d\n", BLEN(b));
12271a4050f5SDavid du Colombier if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
12281a4050f5SDavid du Colombier 
12291a4050f5SDavid du Colombier 
12301a4050f5SDavid du Colombier 	ok = SHandshake|SOpen|SRClose;
12311a4050f5SDavid du Colombier 	if(type == RAlert)
12321a4050f5SDavid du Colombier 		ok |= SAlert;
12331a4050f5SDavid du Colombier 	while(bb != nil){
12341a4050f5SDavid du Colombier 		checkstate(tr, type != RApplication, ok);
12351a4050f5SDavid du Colombier 
12361a4050f5SDavid du Colombier 		/*
12371a4050f5SDavid du Colombier 		 * get at most one maximal record's input,
12381a4050f5SDavid du Colombier 		 * with padding on the front for header and
12391a4050f5SDavid du Colombier 		 * back for mac and maximal block padding.
12401a4050f5SDavid du Colombier 		 */
12411a4050f5SDavid du Colombier 		if(waserror()){
12421a4050f5SDavid du Colombier 			qunlock(&out->seclock);
12431a4050f5SDavid du Colombier 			nexterror();
12441a4050f5SDavid du Colombier 		}
12451a4050f5SDavid du Colombier 		qlock(&out->seclock);
12461a4050f5SDavid du Colombier 		maclen = 0;
12471a4050f5SDavid du Colombier 		pad = 0;
12481a4050f5SDavid du Colombier 		if(out->sec != nil){
12491a4050f5SDavid du Colombier 			maclen = out->sec->maclen;
12501a4050f5SDavid du Colombier 			pad = maclen + out->sec->block;
12511a4050f5SDavid du Colombier 		}
12521a4050f5SDavid du Colombier 		n = BLEN(bb);
12531a4050f5SDavid du Colombier 		if(n > MaxRecLen){
12541a4050f5SDavid du Colombier 			n = MaxRecLen;
12551a4050f5SDavid du Colombier 			nb = allocb(n + pad + RecHdrLen);
12561a4050f5SDavid du Colombier 			memmove(nb->wp + RecHdrLen, bb->rp, n);
12571a4050f5SDavid du Colombier 			bb->rp += n;
12581a4050f5SDavid du Colombier 		}else{
12591a4050f5SDavid du Colombier 			/*
12601a4050f5SDavid du Colombier 			 * carefully reuse bb so it will get freed if we're out of memory
12611a4050f5SDavid du Colombier 			 */
12621a4050f5SDavid du Colombier 			bb = padblock(bb, RecHdrLen);
12631a4050f5SDavid du Colombier 			if(pad)
12641a4050f5SDavid du Colombier 				nb = padblock(bb, -pad);
12651a4050f5SDavid du Colombier 			else
12661a4050f5SDavid du Colombier 				nb = bb;
12671a4050f5SDavid du Colombier 			bb = nil;
12681a4050f5SDavid du Colombier 		}
12691a4050f5SDavid du Colombier 
12701a4050f5SDavid du Colombier 		p = nb->rp;
12711a4050f5SDavid du Colombier 		p[0] = type;
12721a4050f5SDavid du Colombier 		put16(p+1, tr->version);
12731a4050f5SDavid du Colombier 		put16(p+3, n);
12741a4050f5SDavid du Colombier 
12751a4050f5SDavid du Colombier 		if(out->sec != nil){
12761a4050f5SDavid du Colombier 			put64(seq, out->seq);
12771a4050f5SDavid du Colombier 			out->seq++;
12781a4050f5SDavid du Colombier 			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
12791a4050f5SDavid du Colombier 			n += maclen;
12801a4050f5SDavid du Colombier 
12811a4050f5SDavid du Colombier 			/* encrypt */
12821a4050f5SDavid du Colombier 			n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
12831a4050f5SDavid du Colombier 			nb->wp = p + RecHdrLen + n;
12841a4050f5SDavid du Colombier 
12851a4050f5SDavid du Colombier 			/* update length */
12861a4050f5SDavid du Colombier 			put16(p+3, n);
12871a4050f5SDavid du Colombier 		}
12881a4050f5SDavid du Colombier 		if(type == RChangeCipherSpec){
12891a4050f5SDavid du Colombier 			if(out->new == nil)
12901a4050f5SDavid du Colombier 				error("change cipher without a new cipher");
12911a4050f5SDavid du Colombier 			freeSec(out->sec);
12921a4050f5SDavid du Colombier 			out->sec = out->new;
12931a4050f5SDavid du Colombier 			out->new = nil;
12941a4050f5SDavid du Colombier 			out->seq = 0;
12951a4050f5SDavid du Colombier 		}
12961a4050f5SDavid du Colombier 		qunlock(&out->seclock);
12971a4050f5SDavid du Colombier 		poperror();
12981a4050f5SDavid du Colombier 
12991a4050f5SDavid du Colombier 		/*
13001a4050f5SDavid du Colombier 		 * if bwrite error's, we assume the block is queued.
13011a4050f5SDavid du Colombier 		 * if not, we're out of sync with the receiver and will not recover.
13021a4050f5SDavid du Colombier 		 */
13031a4050f5SDavid du Colombier 		if(waserror()){
13041a4050f5SDavid du Colombier 			if(strcmp(up->errstr, "interrupted") != 0)
13051a4050f5SDavid du Colombier 				tlsError(tr, "channel error");
13061a4050f5SDavid du Colombier 			nexterror();
13071a4050f5SDavid du Colombier 		}
13081a4050f5SDavid du Colombier 		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
13091a4050f5SDavid du Colombier 		poperror();
13101a4050f5SDavid du Colombier 	}
13111a4050f5SDavid du Colombier 	qunlock(&out->io);
13121a4050f5SDavid du Colombier 	poperror();
13131a4050f5SDavid du Colombier }
13141a4050f5SDavid du Colombier 
13151a4050f5SDavid du Colombier static long
tlsbwrite(Chan * c,Block * b,ulong offset)13161a4050f5SDavid du Colombier tlsbwrite(Chan *c, Block *b, ulong offset)
13171a4050f5SDavid du Colombier {
13181a4050f5SDavid du Colombier 	int ty;
13191a4050f5SDavid du Colombier 	ulong n;
13201a4050f5SDavid du Colombier 	TlsRec *tr;
13211a4050f5SDavid du Colombier 
13221a4050f5SDavid du Colombier 	n = BLEN(b);
13231a4050f5SDavid du Colombier 
13241a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
13251a4050f5SDavid du Colombier 	if(tr == nil)
13261a4050f5SDavid du Colombier 		panic("tlsbread");
13271a4050f5SDavid du Colombier 
13281a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
13291a4050f5SDavid du Colombier 	switch(ty) {
13301a4050f5SDavid du Colombier 	default:
13311a4050f5SDavid du Colombier 		return devbwrite(c, b, offset);
13321a4050f5SDavid du Colombier 	case Qhand:
13331a4050f5SDavid du Colombier 		tlsrecwrite(tr, RHandshake, b);
13341a4050f5SDavid du Colombier 		tr->handout += n;
13351a4050f5SDavid du Colombier 		break;
13361a4050f5SDavid du Colombier 	case Qdata:
13371a4050f5SDavid du Colombier 		checkstate(tr, 0, SOpen);
13381a4050f5SDavid du Colombier 		tlsrecwrite(tr, RApplication, b);
13391a4050f5SDavid du Colombier 		tr->dataout += n;
13401a4050f5SDavid du Colombier 		break;
13411a4050f5SDavid du Colombier 	}
13421a4050f5SDavid du Colombier 
13431a4050f5SDavid du Colombier 	return n;
13441a4050f5SDavid du Colombier }
13451a4050f5SDavid du Colombier 
13461a4050f5SDavid du Colombier typedef struct Hashalg Hashalg;
13471a4050f5SDavid du Colombier struct Hashalg
13481a4050f5SDavid du Colombier {
13491a4050f5SDavid du Colombier 	char	*name;
13501a4050f5SDavid du Colombier 	int	maclen;
13511a4050f5SDavid du Colombier 	void	(*initkey)(Hashalg *, int, Secret *, uchar*);
13521a4050f5SDavid du Colombier };
13531a4050f5SDavid du Colombier 
13541a4050f5SDavid du Colombier static void
initmd5key(Hashalg * ha,int version,Secret * s,uchar * p)13551a4050f5SDavid du Colombier initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
13561a4050f5SDavid du Colombier {
13571a4050f5SDavid du Colombier 	s->maclen = ha->maclen;
13581a4050f5SDavid du Colombier 	if(version == SSL3Version)
13591a4050f5SDavid du Colombier 		s->mac = sslmac_md5;
13601a4050f5SDavid du Colombier 	else
13611a4050f5SDavid du Colombier 		s->mac = hmac_md5;
13621a4050f5SDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
13631a4050f5SDavid du Colombier }
13641a4050f5SDavid du Colombier 
13651a4050f5SDavid du Colombier static void
initclearmac(Hashalg * unused1,int unused2,Secret * s,uchar * unused3)13661a4050f5SDavid du Colombier initclearmac(Hashalg *unused1, int unused2, Secret *s, uchar *unused3)
13671a4050f5SDavid du Colombier {
13681a4050f5SDavid du Colombier 	s->maclen = 0;
13691a4050f5SDavid du Colombier 	s->mac = nomac;
13701a4050f5SDavid du Colombier }
13711a4050f5SDavid du Colombier 
13721a4050f5SDavid du Colombier static void
initsha1key(Hashalg * ha,int version,Secret * s,uchar * p)13731a4050f5SDavid du Colombier initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
13741a4050f5SDavid du Colombier {
13751a4050f5SDavid du Colombier 	s->maclen = ha->maclen;
13761a4050f5SDavid du Colombier 	if(version == SSL3Version)
13771a4050f5SDavid du Colombier 		s->mac = sslmac_sha1;
13781a4050f5SDavid du Colombier 	else
13791a4050f5SDavid du Colombier 		s->mac = hmac_sha1;
13801a4050f5SDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
13811a4050f5SDavid du Colombier }
13821a4050f5SDavid du Colombier 
13831a4050f5SDavid du Colombier static Hashalg hashtab[] =
13841a4050f5SDavid du Colombier {
13851a4050f5SDavid du Colombier 	{ "clear", 0, initclearmac, },
13861a4050f5SDavid du Colombier 	{ "md5", MD5dlen, initmd5key, },
13871a4050f5SDavid du Colombier 	{ "sha1", SHA1dlen, initsha1key, },
13881a4050f5SDavid du Colombier 	{ 0 }
13891a4050f5SDavid du Colombier };
13901a4050f5SDavid du Colombier 
13911a4050f5SDavid du Colombier static Hashalg*
parsehashalg(char * p)13921a4050f5SDavid du Colombier parsehashalg(char *p)
13931a4050f5SDavid du Colombier {
13941a4050f5SDavid du Colombier 	Hashalg *ha;
13951a4050f5SDavid du Colombier 
13961a4050f5SDavid du Colombier 	for(ha = hashtab; ha->name; ha++)
13971a4050f5SDavid du Colombier 		if(strcmp(p, ha->name) == 0)
13981a4050f5SDavid du Colombier 			return ha;
13991a4050f5SDavid du Colombier 	error("unsupported hash algorithm");
14001a4050f5SDavid du Colombier 	return nil;
14011a4050f5SDavid du Colombier }
14021a4050f5SDavid du Colombier 
14031a4050f5SDavid du Colombier typedef struct Encalg Encalg;
14041a4050f5SDavid du Colombier struct Encalg
14051a4050f5SDavid du Colombier {
14061a4050f5SDavid du Colombier 	char	*name;
14071a4050f5SDavid du Colombier 	int	keylen;
14081a4050f5SDavid du Colombier 	int	ivlen;
14091a4050f5SDavid du Colombier 	void	(*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
14101a4050f5SDavid du Colombier };
14111a4050f5SDavid du Colombier 
14121a4050f5SDavid du Colombier static void
initRC4key(Encalg * ea,Secret * s,uchar * p,uchar * unused1)14131a4050f5SDavid du Colombier initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *unused1)
14141a4050f5SDavid du Colombier {
14151a4050f5SDavid du Colombier 	s->enckey = smalloc(sizeof(RC4state));
14161a4050f5SDavid du Colombier 	s->enc = rc4enc;
14171a4050f5SDavid du Colombier 	s->dec = rc4enc;
14181a4050f5SDavid du Colombier 	s->block = 0;
14191a4050f5SDavid du Colombier 	setupRC4state(s->enckey, p, ea->keylen);
14201a4050f5SDavid du Colombier }
14211a4050f5SDavid du Colombier 
14221a4050f5SDavid du Colombier static void
initDES3key(Encalg * unused1,Secret * s,uchar * p,uchar * iv)14231a4050f5SDavid du Colombier initDES3key(Encalg *unused1, Secret *s, uchar *p, uchar *iv)
14241a4050f5SDavid du Colombier {
14251a4050f5SDavid du Colombier 	s->enckey = smalloc(sizeof(DES3state));
14261a4050f5SDavid du Colombier 	s->enc = des3enc;
14271a4050f5SDavid du Colombier 	s->dec = des3dec;
14281a4050f5SDavid du Colombier 	s->block = 8;
14291a4050f5SDavid du Colombier 	setupDES3state(s->enckey, (uchar(*)[8])p, iv);
14301a4050f5SDavid du Colombier }
14311a4050f5SDavid du Colombier 
14321a4050f5SDavid du Colombier static void
initclearenc(Encalg * unused1,Secret * s,uchar * unused2,uchar * unused3)14331a4050f5SDavid du Colombier initclearenc(Encalg *unused1, Secret *s, uchar *unused2, uchar *unused3)
14341a4050f5SDavid du Colombier {
14351a4050f5SDavid du Colombier 	s->enc = noenc;
14361a4050f5SDavid du Colombier 	s->dec = noenc;
14371a4050f5SDavid du Colombier 	s->block = 0;
14381a4050f5SDavid du Colombier }
14391a4050f5SDavid du Colombier 
14401a4050f5SDavid du Colombier static Encalg encrypttab[] =
14411a4050f5SDavid du Colombier {
14421a4050f5SDavid du Colombier 	{ "clear", 0, 0, initclearenc },
14431a4050f5SDavid du Colombier 	{ "rc4_128", 128/8, 0, initRC4key },
14441a4050f5SDavid du Colombier 	{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
14451a4050f5SDavid du Colombier 	{ 0 }
14461a4050f5SDavid du Colombier };
14471a4050f5SDavid du Colombier 
14481a4050f5SDavid du Colombier static Encalg*
parseencalg(char * p)14491a4050f5SDavid du Colombier parseencalg(char *p)
14501a4050f5SDavid du Colombier {
14511a4050f5SDavid du Colombier 	Encalg *ea;
14521a4050f5SDavid du Colombier 
14531a4050f5SDavid du Colombier 	for(ea = encrypttab; ea->name; ea++)
14541a4050f5SDavid du Colombier 		if(strcmp(p, ea->name) == 0)
14551a4050f5SDavid du Colombier 			return ea;
14561a4050f5SDavid du Colombier 	error("unsupported encryption algorithm");
14571a4050f5SDavid du Colombier 	return nil;
14581a4050f5SDavid du Colombier }
14591a4050f5SDavid du Colombier 
14601a4050f5SDavid du Colombier static long
tlswrite(Chan * c,void * a,long n,vlong off)14611a4050f5SDavid du Colombier tlswrite(Chan *c, void *a, long n, vlong off)
14621a4050f5SDavid du Colombier {
14631a4050f5SDavid du Colombier 	Encalg *ea;
14641a4050f5SDavid du Colombier 	Hashalg *ha;
14651a4050f5SDavid du Colombier 	TlsRec *volatile tr;
14661a4050f5SDavid du Colombier 	Secret *volatile tos, *volatile toc;
14671a4050f5SDavid du Colombier 	Block *volatile b;
14681a4050f5SDavid du Colombier 	Cmdbuf *volatile cb;
14691a4050f5SDavid du Colombier 	int m, ty;
14701a4050f5SDavid du Colombier 	char *p, *e;
14711a4050f5SDavid du Colombier 	uchar *volatile x;
14721a4050f5SDavid du Colombier 	ulong offset = off;
14731a4050f5SDavid du Colombier 
14741a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
14751a4050f5SDavid du Colombier 	if(tr == nil)
14761a4050f5SDavid du Colombier 		panic("tlswrite");
14771a4050f5SDavid du Colombier 
14781a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
14791a4050f5SDavid du Colombier 	switch(ty){
14801a4050f5SDavid du Colombier 	case Qdata:
14811a4050f5SDavid du Colombier 	case Qhand:
14821a4050f5SDavid du Colombier 		p = a;
14831a4050f5SDavid du Colombier 		e = p + n;
14841a4050f5SDavid du Colombier 		do{
14851a4050f5SDavid du Colombier 			m = e - p;
14861a4050f5SDavid du Colombier 			if(m > MaxRecLen)
14871a4050f5SDavid du Colombier 				m = MaxRecLen;
14881a4050f5SDavid du Colombier 
14891a4050f5SDavid du Colombier 			b = allocb(m);
14901a4050f5SDavid du Colombier 			if(waserror()){
14911a4050f5SDavid du Colombier 				freeb(b);
14921a4050f5SDavid du Colombier 				nexterror();
14931a4050f5SDavid du Colombier 			}
14941a4050f5SDavid du Colombier 			memmove(b->wp, p, m);
14951a4050f5SDavid du Colombier 			poperror();
14961a4050f5SDavid du Colombier 			b->wp += m;
14971a4050f5SDavid du Colombier 
14981a4050f5SDavid du Colombier 			tlsbwrite(c, b, offset);
14991a4050f5SDavid du Colombier 
15001a4050f5SDavid du Colombier 			p += m;
15011a4050f5SDavid du Colombier 		}while(p < e);
15021a4050f5SDavid du Colombier 		return n;
15031a4050f5SDavid du Colombier 	case Qctl:
15041a4050f5SDavid du Colombier 		break;
15051a4050f5SDavid du Colombier 	default:
15061a4050f5SDavid du Colombier 		error(Ebadusefd);
15071a4050f5SDavid du Colombier 		return -1;
15081a4050f5SDavid du Colombier 	}
15091a4050f5SDavid du Colombier 
15101a4050f5SDavid du Colombier 	cb = parsecmd(a, n);
15111a4050f5SDavid du Colombier 	if(waserror()){
15121a4050f5SDavid du Colombier 		free(cb);
15131a4050f5SDavid du Colombier 		nexterror();
15141a4050f5SDavid du Colombier 	}
15151a4050f5SDavid du Colombier 	if(cb->nf < 1)
15161a4050f5SDavid du Colombier 		error("short control request");
15171a4050f5SDavid du Colombier 
15181a4050f5SDavid du Colombier 	/* mutex with operations using what we're about to change */
15191a4050f5SDavid du Colombier 	if(waserror()){
15201a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
15211a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
15221a4050f5SDavid du Colombier 		nexterror();
15231a4050f5SDavid du Colombier 	}
15241a4050f5SDavid du Colombier 	qlock(&tr->in.seclock);
15251a4050f5SDavid du Colombier 	qlock(&tr->out.seclock);
15261a4050f5SDavid du Colombier 
15271a4050f5SDavid du Colombier 	if(strcmp(cb->f[0], "fd") == 0){
15281a4050f5SDavid du Colombier 		if(cb->nf != 3)
15291a4050f5SDavid du Colombier 			error("usage: fd open-fd version");
15301a4050f5SDavid du Colombier 		if(tr->c != nil)
15311a4050f5SDavid du Colombier 			error(Einuse);
15321a4050f5SDavid du Colombier 		m = strtol(cb->f[2], nil, 0);
15331a4050f5SDavid du Colombier 		if(m < MinProtoVersion || m > MaxProtoVersion)
15341a4050f5SDavid du Colombier 			error("unsupported version");
15351a4050f5SDavid du Colombier 		tr->c = buftochan(cb->f[1]);
15361a4050f5SDavid du Colombier 		tr->version = m;
15371a4050f5SDavid du Colombier 		tlsSetState(tr, SHandshake, SClosed);
15381a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "version") == 0){
15391a4050f5SDavid du Colombier 		if(cb->nf != 2)
15401a4050f5SDavid du Colombier 			error("usage: version vers");
15411a4050f5SDavid du Colombier 		if(tr->c == nil)
15421a4050f5SDavid du Colombier 			error("must set fd before version");
15431a4050f5SDavid du Colombier 		if(tr->verset)
15441a4050f5SDavid du Colombier 			error("version already set");
15451a4050f5SDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
15461a4050f5SDavid du Colombier 		if(m == SSL3Version)
15471a4050f5SDavid du Colombier 			tr->packMac = sslPackMac;
15481a4050f5SDavid du Colombier 		else if(m == TLSVersion)
15491a4050f5SDavid du Colombier 			tr->packMac = tlsPackMac;
15501a4050f5SDavid du Colombier 		else
15511a4050f5SDavid du Colombier 			error("unsupported version");
15521a4050f5SDavid du Colombier 		tr->verset = 1;
15531a4050f5SDavid du Colombier 		tr->version = m;
15541a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "secret") == 0){
15551a4050f5SDavid du Colombier 		if(cb->nf != 5)
15561a4050f5SDavid du Colombier 			error("usage: secret hashalg encalg isclient secretdata");
15571a4050f5SDavid du Colombier 		if(tr->c == nil || !tr->verset)
15581a4050f5SDavid du Colombier 			error("must set fd and version before secrets");
15591a4050f5SDavid du Colombier 
15601a4050f5SDavid du Colombier 		if(tr->in.new != nil){
15611a4050f5SDavid du Colombier 			freeSec(tr->in.new);
15621a4050f5SDavid du Colombier 			tr->in.new = nil;
15631a4050f5SDavid du Colombier 		}
15641a4050f5SDavid du Colombier 		if(tr->out.new != nil){
15651a4050f5SDavid du Colombier 			freeSec(tr->out.new);
15661a4050f5SDavid du Colombier 			tr->out.new = nil;
15671a4050f5SDavid du Colombier 		}
15681a4050f5SDavid du Colombier 
15691a4050f5SDavid du Colombier 		ha = parsehashalg(cb->f[1]);
15701a4050f5SDavid du Colombier 		ea = parseencalg(cb->f[2]);
15711a4050f5SDavid du Colombier 
15721a4050f5SDavid du Colombier 		p = cb->f[4];
15731a4050f5SDavid du Colombier 		m = (strlen(p)*3)/2;
15741a4050f5SDavid du Colombier 		x = smalloc(m);
15751a4050f5SDavid du Colombier 		tos = nil;
15761a4050f5SDavid du Colombier 		toc = nil;
15771a4050f5SDavid du Colombier 		if(waserror()){
15781a4050f5SDavid du Colombier 			freeSec(tos);
15791a4050f5SDavid du Colombier 			freeSec(toc);
15801a4050f5SDavid du Colombier 			free(x);
15811a4050f5SDavid du Colombier 			nexterror();
15821a4050f5SDavid du Colombier 		}
15831a4050f5SDavid du Colombier 		m = dec64(x, m, p, strlen(p));
15841a4050f5SDavid du Colombier 		if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
15851a4050f5SDavid du Colombier 			error("not enough secret data provided");
15861a4050f5SDavid du Colombier 
15871a4050f5SDavid du Colombier 		tos = smalloc(sizeof(Secret));
15881a4050f5SDavid du Colombier 		toc = smalloc(sizeof(Secret));
15891a4050f5SDavid du Colombier 		if(!ha->initkey || !ea->initkey)
15901a4050f5SDavid du Colombier 			error("misimplemented secret algorithm");
15911a4050f5SDavid du Colombier 		(*ha->initkey)(ha, tr->version, tos, &x[0]);
15921a4050f5SDavid du Colombier 		(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
15931a4050f5SDavid du Colombier 		(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
15941a4050f5SDavid du Colombier 		(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
15951a4050f5SDavid du Colombier 
15961a4050f5SDavid du Colombier 		if(!tos->mac || !tos->enc || !tos->dec
15971a4050f5SDavid du Colombier 		|| !toc->mac || !toc->enc || !toc->dec)
15981a4050f5SDavid du Colombier 			error("missing algorithm implementations");
15991a4050f5SDavid du Colombier 		if(strtol(cb->f[3], nil, 0) == 0){
16001a4050f5SDavid du Colombier 			tr->in.new = tos;
16011a4050f5SDavid du Colombier 			tr->out.new = toc;
16021a4050f5SDavid du Colombier 		}else{
16031a4050f5SDavid du Colombier 			tr->in.new = toc;
16041a4050f5SDavid du Colombier 			tr->out.new = tos;
16051a4050f5SDavid du Colombier 		}
16061a4050f5SDavid du Colombier 		if(tr->version == SSL3Version){
16071a4050f5SDavid du Colombier 			toc->unpad = sslunpad;
16081a4050f5SDavid du Colombier 			tos->unpad = sslunpad;
16091a4050f5SDavid du Colombier 		}else{
16101a4050f5SDavid du Colombier 			toc->unpad = tlsunpad;
16111a4050f5SDavid du Colombier 			tos->unpad = tlsunpad;
16121a4050f5SDavid du Colombier 		}
16131a4050f5SDavid du Colombier 		toc->encalg = ea->name;
16141a4050f5SDavid du Colombier 		toc->hashalg = ha->name;
16151a4050f5SDavid du Colombier 		tos->encalg = ea->name;
16161a4050f5SDavid du Colombier 		tos->hashalg = ha->name;
16171a4050f5SDavid du Colombier 
16181a4050f5SDavid du Colombier 		free(x);
16191a4050f5SDavid du Colombier 		poperror();
16201a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "changecipher") == 0){
16211a4050f5SDavid du Colombier 		if(cb->nf != 1)
16221a4050f5SDavid du Colombier 			error("usage: changecipher");
16231a4050f5SDavid du Colombier 		if(tr->out.new == nil)
16241a4050f5SDavid du Colombier 			error("cannot change cipher spec without setting secret");
16251a4050f5SDavid du Colombier 
16261a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
16271a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
16281a4050f5SDavid du Colombier 		poperror();
16291a4050f5SDavid du Colombier 		free(cb);
16301a4050f5SDavid du Colombier 		poperror();
16311a4050f5SDavid du Colombier 
16321a4050f5SDavid du Colombier 		/*
16331a4050f5SDavid du Colombier 		 * the real work is done as the message is written
16341a4050f5SDavid du Colombier 		 * so the stream is encrypted in sync.
16351a4050f5SDavid du Colombier 		 */
16361a4050f5SDavid du Colombier 		b = allocb(1);
16371a4050f5SDavid du Colombier 		*b->wp++ = 1;
16381a4050f5SDavid du Colombier 		tlsrecwrite(tr, RChangeCipherSpec, b);
16391a4050f5SDavid du Colombier 		return n;
16401a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "opened") == 0){
16411a4050f5SDavid du Colombier 		if(cb->nf != 1)
16421a4050f5SDavid du Colombier 			error("usage: opened");
16431a4050f5SDavid du Colombier 		if(tr->in.sec == nil || tr->out.sec == nil)
16441a4050f5SDavid du Colombier 			error("cipher must be configured before enabling data messages");
16451a4050f5SDavid du Colombier 		lock(&tr->statelk);
16461a4050f5SDavid du Colombier 		if(tr->state != SHandshake && tr->state != SOpen){
16471a4050f5SDavid du Colombier 			unlock(&tr->statelk);
16481a4050f5SDavid du Colombier 			error("cannot enable data messages");
16491a4050f5SDavid du Colombier 		}
16501a4050f5SDavid du Colombier 		tr->state = SOpen;
16511a4050f5SDavid du Colombier 		unlock(&tr->statelk);
16521a4050f5SDavid du Colombier 		tr->opened = 1;
16531a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "alert") == 0){
16541a4050f5SDavid du Colombier 		if(cb->nf != 2)
16551a4050f5SDavid du Colombier 			error("usage: alert n");
16561a4050f5SDavid du Colombier 		if(tr->c == nil)
16571a4050f5SDavid du Colombier 			error("must set fd before sending alerts");
16581a4050f5SDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
16591a4050f5SDavid du Colombier 
16601a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
16611a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
16621a4050f5SDavid du Colombier 		poperror();
16631a4050f5SDavid du Colombier 		free(cb);
16641a4050f5SDavid du Colombier 		poperror();
16651a4050f5SDavid du Colombier 
16661a4050f5SDavid du Colombier 		sendAlert(tr, m);
16671a4050f5SDavid du Colombier 
16681a4050f5SDavid du Colombier 		if(m == ECloseNotify)
16691a4050f5SDavid du Colombier 			tlsclosed(tr, SLClose);
16701a4050f5SDavid du Colombier 
16711a4050f5SDavid du Colombier 		return n;
16721a4050f5SDavid du Colombier 	} else if(strcmp(cb->f[0], "debug") == 0){
16731a4050f5SDavid du Colombier 		if(cb->nf == 2){
16741a4050f5SDavid du Colombier 			if(strcmp(cb->f[1], "on") == 0)
16751a4050f5SDavid du Colombier 				tr->debug = 1;
16761a4050f5SDavid du Colombier 			else
16771a4050f5SDavid du Colombier 				tr->debug = 0;
16781a4050f5SDavid du Colombier 		} else
16791a4050f5SDavid du Colombier 			tr->debug = 1;
16801a4050f5SDavid du Colombier 	} else
16811a4050f5SDavid du Colombier 		error(Ebadarg);
16821a4050f5SDavid du Colombier 
16831a4050f5SDavid du Colombier 	qunlock(&tr->in.seclock);
16841a4050f5SDavid du Colombier 	qunlock(&tr->out.seclock);
16851a4050f5SDavid du Colombier 	poperror();
16861a4050f5SDavid du Colombier 	free(cb);
16871a4050f5SDavid du Colombier 	poperror();
16881a4050f5SDavid du Colombier 
16891a4050f5SDavid du Colombier 	return n;
16901a4050f5SDavid du Colombier }
16911a4050f5SDavid du Colombier 
16921a4050f5SDavid du Colombier static void
tlsinit(void)16931a4050f5SDavid du Colombier tlsinit(void)
16941a4050f5SDavid du Colombier {
16951a4050f5SDavid du Colombier 	struct Encalg *e;
16961a4050f5SDavid du Colombier 	struct Hashalg *h;
16971a4050f5SDavid du Colombier 	int n;
16981a4050f5SDavid du Colombier 	char *cp;
16991a4050f5SDavid du Colombier 	static int already;
17001a4050f5SDavid du Colombier 
17011a4050f5SDavid du Colombier 	if(!already){
17021a4050f5SDavid du Colombier 		fmtinstall('H', encodefmt);
17031a4050f5SDavid du Colombier 		already = 1;
17041a4050f5SDavid du Colombier 	}
17051a4050f5SDavid du Colombier 
17061a4050f5SDavid du Colombier 	tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
17071a4050f5SDavid du Colombier 	trnames = smalloc((sizeof *trnames) * maxtlsdevs);
17081a4050f5SDavid du Colombier 
17091a4050f5SDavid du Colombier 	n = 1;
17101a4050f5SDavid du Colombier 	for(e = encrypttab; e->name != nil; e++)
17111a4050f5SDavid du Colombier 		n += strlen(e->name) + 1;
17121a4050f5SDavid du Colombier 	cp = encalgs = smalloc(n);
17131a4050f5SDavid du Colombier 	for(e = encrypttab;;){
17141a4050f5SDavid du Colombier 		strcpy(cp, e->name);
17151a4050f5SDavid du Colombier 		cp += strlen(e->name);
17161a4050f5SDavid du Colombier 		e++;
17171a4050f5SDavid du Colombier 		if(e->name == nil)
17181a4050f5SDavid du Colombier 			break;
17191a4050f5SDavid du Colombier 		*cp++ = ' ';
17201a4050f5SDavid du Colombier 	}
17211a4050f5SDavid du Colombier 	*cp = 0;
17221a4050f5SDavid du Colombier 
17231a4050f5SDavid du Colombier 	n = 1;
17241a4050f5SDavid du Colombier 	for(h = hashtab; h->name != nil; h++)
17251a4050f5SDavid du Colombier 		n += strlen(h->name) + 1;
17261a4050f5SDavid du Colombier 	cp = hashalgs = smalloc(n);
17271a4050f5SDavid du Colombier 	for(h = hashtab;;){
17281a4050f5SDavid du Colombier 		strcpy(cp, h->name);
17291a4050f5SDavid du Colombier 		cp += strlen(h->name);
17301a4050f5SDavid du Colombier 		h++;
17311a4050f5SDavid du Colombier 		if(h->name == nil)
17321a4050f5SDavid du Colombier 			break;
17331a4050f5SDavid du Colombier 		*cp++ = ' ';
17341a4050f5SDavid du Colombier 	}
17351a4050f5SDavid du Colombier 	*cp = 0;
17361a4050f5SDavid du Colombier }
17371a4050f5SDavid du Colombier 
17381a4050f5SDavid du Colombier Dev tlsdevtab = {
17391a4050f5SDavid du Colombier 	'a',
17401a4050f5SDavid du Colombier 	"tls",
17411a4050f5SDavid du Colombier 
17421a4050f5SDavid du Colombier 	devreset,
17431a4050f5SDavid du Colombier 	tlsinit,
17441a4050f5SDavid du Colombier 	devshutdown,
17451a4050f5SDavid du Colombier 	tlsattach,
17461a4050f5SDavid du Colombier 	tlswalk,
17471a4050f5SDavid du Colombier 	tlsstat,
17481a4050f5SDavid du Colombier 	tlsopen,
17491a4050f5SDavid du Colombier 	devcreate,
17501a4050f5SDavid du Colombier 	tlsclose,
17511a4050f5SDavid du Colombier 	tlsread,
17521a4050f5SDavid du Colombier 	tlsbread,
17531a4050f5SDavid du Colombier 	tlswrite,
17541a4050f5SDavid du Colombier 	tlsbwrite,
17551a4050f5SDavid du Colombier 	devremove,
17561a4050f5SDavid du Colombier 	tlswstat,
17571a4050f5SDavid du Colombier };
17581a4050f5SDavid du Colombier 
17591a4050f5SDavid du Colombier /* get channel associated with an fd */
17601a4050f5SDavid du Colombier static Chan*
buftochan(char * p)17611a4050f5SDavid du Colombier buftochan(char *p)
17621a4050f5SDavid du Colombier {
17631a4050f5SDavid du Colombier 	Chan *c;
17641a4050f5SDavid du Colombier 	int fd;
17651a4050f5SDavid du Colombier 
17661a4050f5SDavid du Colombier 	if(p == 0)
17671a4050f5SDavid du Colombier 		error(Ebadarg);
17681a4050f5SDavid du Colombier 	fd = strtoul(p, 0, 0);
17691a4050f5SDavid du Colombier 	if(fd < 0)
17701a4050f5SDavid du Colombier 		error(Ebadarg);
17711a4050f5SDavid du Colombier 	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
17721a4050f5SDavid du Colombier 	return c;
17731a4050f5SDavid du Colombier }
17741a4050f5SDavid du Colombier 
17751a4050f5SDavid du Colombier static void
sendAlert(TlsRec * tr,int err)17761a4050f5SDavid du Colombier sendAlert(TlsRec *tr, int err)
17771a4050f5SDavid du Colombier {
17781a4050f5SDavid du Colombier 	Block *b;
17791a4050f5SDavid du Colombier 	int i, fatal;
17801a4050f5SDavid du Colombier 	char *msg;
17811a4050f5SDavid du Colombier 
17821a4050f5SDavid du Colombier if(tr->debug)pprint("sendAlert %d\n", err);
17831a4050f5SDavid du Colombier 	fatal = 1;
17841a4050f5SDavid du Colombier 	msg = "tls unknown alert";
17851a4050f5SDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++) {
17861a4050f5SDavid du Colombier 		if(tlserrs[i].err == err) {
17871a4050f5SDavid du Colombier 			msg = tlserrs[i].msg;
17881a4050f5SDavid du Colombier 			if(tr->version == SSL3Version)
17891a4050f5SDavid du Colombier 				err = tlserrs[i].sslerr;
17901a4050f5SDavid du Colombier 			else
17911a4050f5SDavid du Colombier 				err = tlserrs[i].tlserr;
17921a4050f5SDavid du Colombier 			fatal = tlserrs[i].fatal;
17931a4050f5SDavid du Colombier 			break;
17941a4050f5SDavid du Colombier 		}
17951a4050f5SDavid du Colombier 	}
17961a4050f5SDavid du Colombier 
17971a4050f5SDavid du Colombier 	if(!waserror()){
17981a4050f5SDavid du Colombier 		b = allocb(2);
17991a4050f5SDavid du Colombier 		*b->wp++ = fatal + 1;
18001a4050f5SDavid du Colombier 		*b->wp++ = err;
18011a4050f5SDavid du Colombier 		if(fatal)
18021a4050f5SDavid du Colombier 			tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
18031a4050f5SDavid du Colombier 		tlsrecwrite(tr, RAlert, b);
18041a4050f5SDavid du Colombier 		poperror();
18051a4050f5SDavid du Colombier 	}
18061a4050f5SDavid du Colombier 	if(fatal)
18071a4050f5SDavid du Colombier 		tlsError(tr, msg);
18081a4050f5SDavid du Colombier }
18091a4050f5SDavid du Colombier 
18101a4050f5SDavid du Colombier static void
tlsError(TlsRec * tr,char * msg)18111a4050f5SDavid du Colombier tlsError(TlsRec *tr, char *msg)
18121a4050f5SDavid du Colombier {
18131a4050f5SDavid du Colombier 	int s;
18141a4050f5SDavid du Colombier 
18151a4050f5SDavid du Colombier if(tr->debug)pprint("tleError %s\n", msg);
18161a4050f5SDavid du Colombier 	lock(&tr->statelk);
18171a4050f5SDavid du Colombier 	s = tr->state;
18181a4050f5SDavid du Colombier 	tr->state = SError;
18191a4050f5SDavid du Colombier 	if(s != SError){
18201a4050f5SDavid du Colombier 		strncpy(tr->err, msg, ERRMAX - 1);
18211a4050f5SDavid du Colombier 		tr->err[ERRMAX - 1] = '\0';
18221a4050f5SDavid du Colombier 	}
18231a4050f5SDavid du Colombier 	unlock(&tr->statelk);
18241a4050f5SDavid du Colombier 	if(s != SError)
18251a4050f5SDavid du Colombier 		alertHand(tr, msg);
18261a4050f5SDavid du Colombier }
18271a4050f5SDavid du Colombier 
18281a4050f5SDavid du Colombier static void
tlsSetState(TlsRec * tr,int new,int old)18291a4050f5SDavid du Colombier tlsSetState(TlsRec *tr, int new, int old)
18301a4050f5SDavid du Colombier {
18311a4050f5SDavid du Colombier 	lock(&tr->statelk);
18321a4050f5SDavid du Colombier 	if(tr->state & old)
18331a4050f5SDavid du Colombier 		tr->state = new;
18341a4050f5SDavid du Colombier 	unlock(&tr->statelk);
18351a4050f5SDavid du Colombier }
18361a4050f5SDavid du Colombier 
18371a4050f5SDavid du Colombier /* hand up a digest connection */
18381a4050f5SDavid du Colombier static void
tlshangup(TlsRec * tr)18391a4050f5SDavid du Colombier tlshangup(TlsRec *tr)
18401a4050f5SDavid du Colombier {
18411a4050f5SDavid du Colombier 	Block *b;
18421a4050f5SDavid du Colombier 
18431a4050f5SDavid du Colombier 	qlock(&tr->in.io);
18441a4050f5SDavid du Colombier 	for(b = tr->processed; b; b = tr->processed){
18451a4050f5SDavid du Colombier 		tr->processed = b->next;
18461a4050f5SDavid du Colombier 		freeb(b);
18471a4050f5SDavid du Colombier 	}
18481a4050f5SDavid du Colombier 	if(tr->unprocessed != nil){
18491a4050f5SDavid du Colombier 		freeb(tr->unprocessed);
18501a4050f5SDavid du Colombier 		tr->unprocessed = nil;
18511a4050f5SDavid du Colombier 	}
18521a4050f5SDavid du Colombier 	qunlock(&tr->in.io);
18531a4050f5SDavid du Colombier 
18541a4050f5SDavid du Colombier 	tlsSetState(tr, SClosed, ~0);
18551a4050f5SDavid du Colombier }
18561a4050f5SDavid du Colombier 
18571a4050f5SDavid du Colombier static TlsRec*
newtls(Chan * ch)18581a4050f5SDavid du Colombier newtls(Chan *ch)
18591a4050f5SDavid du Colombier {
18601a4050f5SDavid du Colombier 	TlsRec **pp, **ep, **np;
18611a4050f5SDavid du Colombier 	char **nmp;
18621a4050f5SDavid du Colombier 	int t, newmax;
18631a4050f5SDavid du Colombier 
18641a4050f5SDavid du Colombier 	if(waserror()) {
18651a4050f5SDavid du Colombier 		unlock(&tdlock);
18661a4050f5SDavid du Colombier 		nexterror();
18671a4050f5SDavid du Colombier 	}
18681a4050f5SDavid du Colombier 	lock(&tdlock);
18691a4050f5SDavid du Colombier 	ep = &tlsdevs[maxtlsdevs];
18701a4050f5SDavid du Colombier 	for(pp = tlsdevs; pp < ep; pp++)
18711a4050f5SDavid du Colombier 		if(*pp == nil)
18721a4050f5SDavid du Colombier 			break;
18731a4050f5SDavid du Colombier 	if(pp >= ep) {
18741a4050f5SDavid du Colombier 		if(maxtlsdevs >= MaxTlsDevs) {
18751a4050f5SDavid du Colombier 			unlock(&tdlock);
18761a4050f5SDavid du Colombier 			poperror();
18771a4050f5SDavid du Colombier 			return nil;
18781a4050f5SDavid du Colombier 		}
18791a4050f5SDavid du Colombier 		newmax = 2 * maxtlsdevs;
18801a4050f5SDavid du Colombier 		if(newmax > MaxTlsDevs)
18811a4050f5SDavid du Colombier 			newmax = MaxTlsDevs;
18821a4050f5SDavid du Colombier 		np = smalloc(sizeof(TlsRec*) * newmax);
18831a4050f5SDavid du Colombier 		memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
18841a4050f5SDavid du Colombier 		tlsdevs = np;
18851a4050f5SDavid du Colombier 		pp = &tlsdevs[maxtlsdevs];
18861a4050f5SDavid du Colombier 		memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
18871a4050f5SDavid du Colombier 
18881a4050f5SDavid du Colombier 		nmp = smalloc(sizeof *nmp * newmax);
18891a4050f5SDavid du Colombier 		memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
18901a4050f5SDavid du Colombier 		trnames = nmp;
18911a4050f5SDavid du Colombier 
18921a4050f5SDavid du Colombier 		maxtlsdevs = newmax;
18931a4050f5SDavid du Colombier 	}
18941a4050f5SDavid du Colombier 	*pp = mktlsrec();
18951a4050f5SDavid du Colombier 	if(pp - tlsdevs >= tdhiwat)
18961a4050f5SDavid du Colombier 		tdhiwat++;
18971a4050f5SDavid du Colombier 	t = TYPE(ch->qid);
18981a4050f5SDavid du Colombier 	if(t == Qclonus)
18991a4050f5SDavid du Colombier 		t = Qctl;
19001a4050f5SDavid du Colombier 	ch->qid.path = QID(pp - tlsdevs, t);
19011a4050f5SDavid du Colombier 	ch->qid.vers = 0;
19021a4050f5SDavid du Colombier 	unlock(&tdlock);
19031a4050f5SDavid du Colombier 	poperror();
19041a4050f5SDavid du Colombier 	return *pp;
19051a4050f5SDavid du Colombier }
19061a4050f5SDavid du Colombier 
19071a4050f5SDavid du Colombier static TlsRec *
mktlsrec(void)19081a4050f5SDavid du Colombier mktlsrec(void)
19091a4050f5SDavid du Colombier {
19101a4050f5SDavid du Colombier 	TlsRec *tr;
19111a4050f5SDavid du Colombier 
19121a4050f5SDavid du Colombier 	tr = mallocz(sizeof(*tr), 1);
19131a4050f5SDavid du Colombier 	if(tr == nil)
19141a4050f5SDavid du Colombier 		error(Enomem);
19151a4050f5SDavid du Colombier 	tr->state = SClosed;
19161a4050f5SDavid du Colombier 	tr->ref = 1;
19171a4050f5SDavid du Colombier 	kstrdup(&tr->user, up->user);
19181a4050f5SDavid du Colombier 	tr->perm = 0660;
19191a4050f5SDavid du Colombier 	return tr;
19201a4050f5SDavid du Colombier }
19211a4050f5SDavid du Colombier 
19221a4050f5SDavid du Colombier static char*
tlsstate(int s)19231a4050f5SDavid du Colombier tlsstate(int s)
19241a4050f5SDavid du Colombier {
19251a4050f5SDavid du Colombier 	switch(s){
19261a4050f5SDavid du Colombier 	case SHandshake:
19271a4050f5SDavid du Colombier 		return "Handshaking";
19281a4050f5SDavid du Colombier 	case SOpen:
19291a4050f5SDavid du Colombier 		return "Established";
19301a4050f5SDavid du Colombier 	case SRClose:
19311a4050f5SDavid du Colombier 		return "RemoteClosed";
19321a4050f5SDavid du Colombier 	case SLClose:
19331a4050f5SDavid du Colombier 		return "LocalClosed";
19341a4050f5SDavid du Colombier 	case SAlert:
19351a4050f5SDavid du Colombier 		return "Alerting";
19361a4050f5SDavid du Colombier 	case SError:
19371a4050f5SDavid du Colombier 		return "Errored";
19381a4050f5SDavid du Colombier 	case SClosed:
19391a4050f5SDavid du Colombier 		return "Closed";
19401a4050f5SDavid du Colombier 	}
19411a4050f5SDavid du Colombier 	return "Unknown";
19421a4050f5SDavid du Colombier }
19431a4050f5SDavid du Colombier 
19441a4050f5SDavid du Colombier static void
freeSec(Secret * s)19451a4050f5SDavid du Colombier freeSec(Secret *s)
19461a4050f5SDavid du Colombier {
19471a4050f5SDavid du Colombier 	if(s != nil){
19481a4050f5SDavid du Colombier 		free(s->enckey);
19491a4050f5SDavid du Colombier 		free(s);
19501a4050f5SDavid du Colombier 	}
19511a4050f5SDavid du Colombier }
19521a4050f5SDavid du Colombier 
19531a4050f5SDavid du Colombier static int
noenc(Secret * unused1,uchar * unused2,int n)19541a4050f5SDavid du Colombier noenc(Secret *unused1, uchar *unused2, int n)
19551a4050f5SDavid du Colombier {
19561a4050f5SDavid du Colombier 	return n;
19571a4050f5SDavid du Colombier }
19581a4050f5SDavid du Colombier 
19591a4050f5SDavid du Colombier static int
rc4enc(Secret * sec,uchar * buf,int n)19601a4050f5SDavid du Colombier rc4enc(Secret *sec, uchar *buf, int n)
19611a4050f5SDavid du Colombier {
19621a4050f5SDavid du Colombier 	rc4(sec->enckey, buf, n);
19631a4050f5SDavid du Colombier 	return n;
19641a4050f5SDavid du Colombier }
19651a4050f5SDavid du Colombier 
19661a4050f5SDavid du Colombier static int
tlsunpad(uchar * buf,int n,int block)19671a4050f5SDavid du Colombier tlsunpad(uchar *buf, int n, int block)
19681a4050f5SDavid du Colombier {
19691a4050f5SDavid du Colombier 	int pad, nn;
19701a4050f5SDavid du Colombier 
19711a4050f5SDavid du Colombier 	pad = buf[n - 1];
19721a4050f5SDavid du Colombier 	nn = n - 1 - pad;
19731a4050f5SDavid du Colombier 	if(nn <= 0 || n % block)
19741a4050f5SDavid du Colombier 		return -1;
19751a4050f5SDavid du Colombier 	while(--n > nn)
19761a4050f5SDavid du Colombier 		if(pad != buf[n - 1])
19771a4050f5SDavid du Colombier 			return -1;
19781a4050f5SDavid du Colombier 	return nn;
19791a4050f5SDavid du Colombier }
19801a4050f5SDavid du Colombier 
19811a4050f5SDavid du Colombier static int
sslunpad(uchar * buf,int n,int block)19821a4050f5SDavid du Colombier sslunpad(uchar *buf, int n, int block)
19831a4050f5SDavid du Colombier {
19841a4050f5SDavid du Colombier 	int pad, nn;
19851a4050f5SDavid du Colombier 
19861a4050f5SDavid du Colombier 	pad = buf[n - 1];
19871a4050f5SDavid du Colombier 	nn = n - 1 - pad;
19881a4050f5SDavid du Colombier 	if(nn <= 0 || n % block)
19891a4050f5SDavid du Colombier 		return -1;
19901a4050f5SDavid du Colombier 	return nn;
19911a4050f5SDavid du Colombier }
19921a4050f5SDavid du Colombier 
19931a4050f5SDavid du Colombier static int
blockpad(uchar * buf,int n,int block)19941a4050f5SDavid du Colombier blockpad(uchar *buf, int n, int block)
19951a4050f5SDavid du Colombier {
19961a4050f5SDavid du Colombier 	int pad, nn;
19971a4050f5SDavid du Colombier 
19981a4050f5SDavid du Colombier 	nn = n + block;
19991a4050f5SDavid du Colombier 	nn -= nn % block;
20001a4050f5SDavid du Colombier 	pad = nn - (n + 1);
20011a4050f5SDavid du Colombier 	while(n < nn)
20021a4050f5SDavid du Colombier 		buf[n++] = pad;
20031a4050f5SDavid du Colombier 	return nn;
20041a4050f5SDavid du Colombier }
20051a4050f5SDavid du Colombier 
20061a4050f5SDavid du Colombier static int
des3enc(Secret * sec,uchar * buf,int n)20071a4050f5SDavid du Colombier des3enc(Secret *sec, uchar *buf, int n)
20081a4050f5SDavid du Colombier {
20091a4050f5SDavid du Colombier 	n = blockpad(buf, n, 8);
20101a4050f5SDavid du Colombier 	des3CBCencrypt(buf, n, sec->enckey);
20111a4050f5SDavid du Colombier 	return n;
20121a4050f5SDavid du Colombier }
20131a4050f5SDavid du Colombier 
20141a4050f5SDavid du Colombier static int
des3dec(Secret * sec,uchar * buf,int n)20151a4050f5SDavid du Colombier des3dec(Secret *sec, uchar *buf, int n)
20161a4050f5SDavid du Colombier {
20171a4050f5SDavid du Colombier 	des3CBCdecrypt(buf, n, sec->enckey);
20181a4050f5SDavid du Colombier 	return (*sec->unpad)(buf, n, 8);
20191a4050f5SDavid du Colombier }
20201a4050f5SDavid du Colombier static DigestState*
nomac(uchar * unused1,ulong unused2,uchar * unused3,ulong unused4,uchar * unused5,DigestState * unused6)20211a4050f5SDavid du Colombier nomac(uchar *unused1, ulong unused2, uchar *unused3, ulong unused4,
20221a4050f5SDavid du Colombier 	uchar *unused5, DigestState *unused6)
20231a4050f5SDavid du Colombier {
20241a4050f5SDavid du Colombier 	return nil;
20251a4050f5SDavid du Colombier }
20261a4050f5SDavid du Colombier 
20271a4050f5SDavid du Colombier /*
20281a4050f5SDavid du Colombier  * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
20291a4050f5SDavid du Colombier  */
20301a4050f5SDavid 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)20311a4050f5SDavid du Colombier sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
20321a4050f5SDavid du Colombier 	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
20331a4050f5SDavid du Colombier {
20341a4050f5SDavid du Colombier 	int i;
20351a4050f5SDavid du Colombier 	uchar pad[48], innerdigest[20];
20361a4050f5SDavid du Colombier 
20371a4050f5SDavid du Colombier 	if(xlen > sizeof(innerdigest)
20381a4050f5SDavid du Colombier 	|| padlen > sizeof(pad))
20391a4050f5SDavid du Colombier 		return nil;
20401a4050f5SDavid du Colombier 
20411a4050f5SDavid du Colombier 	if(klen>64)
20421a4050f5SDavid du Colombier 		return nil;
20431a4050f5SDavid du Colombier 
20441a4050f5SDavid du Colombier 	/* first time through */
20451a4050f5SDavid du Colombier 	if(s == nil){
20461a4050f5SDavid du Colombier 		for(i=0; i<padlen; i++)
20471a4050f5SDavid du Colombier 			pad[i] = 0x36;
20481a4050f5SDavid du Colombier 		s = (*x)(key, klen, nil, nil);
20491a4050f5SDavid du Colombier 		s = (*x)(pad, padlen, nil, s);
20501a4050f5SDavid du Colombier 		if(s == nil)
20511a4050f5SDavid du Colombier 			return nil;
20521a4050f5SDavid du Colombier 	}
20531a4050f5SDavid du Colombier 
20541a4050f5SDavid du Colombier 	s = (*x)(p, len, nil, s);
20551a4050f5SDavid du Colombier 	if(digest == nil)
20561a4050f5SDavid du Colombier 		return s;
20571a4050f5SDavid du Colombier 
20581a4050f5SDavid du Colombier 	/* last time through */
20591a4050f5SDavid du Colombier 	for(i=0; i<padlen; i++)
20601a4050f5SDavid du Colombier 		pad[i] = 0x5c;
20611a4050f5SDavid du Colombier 	(*x)(nil, 0, innerdigest, s);
20621a4050f5SDavid du Colombier 	s = (*x)(key, klen, nil, nil);
20631a4050f5SDavid du Colombier 	s = (*x)(pad, padlen, nil, s);
20641a4050f5SDavid du Colombier 	(*x)(innerdigest, xlen, digest, s);
20651a4050f5SDavid du Colombier 	return nil;
20661a4050f5SDavid du Colombier }
20671a4050f5SDavid du Colombier 
20681a4050f5SDavid du Colombier static DigestState*
sslmac_sha1(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)20691a4050f5SDavid du Colombier sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
20701a4050f5SDavid du Colombier {
20711a4050f5SDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
20721a4050f5SDavid du Colombier }
20731a4050f5SDavid du Colombier 
20741a4050f5SDavid du Colombier static DigestState*
sslmac_md5(uchar * p,ulong len,uchar * key,ulong klen,uchar * digest,DigestState * s)20751a4050f5SDavid du Colombier sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
20761a4050f5SDavid du Colombier {
20771a4050f5SDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
20781a4050f5SDavid du Colombier }
20791a4050f5SDavid du Colombier 
20801a4050f5SDavid du Colombier static void
sslPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)20811a4050f5SDavid du Colombier sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
20821a4050f5SDavid du Colombier {
20831a4050f5SDavid du Colombier 	DigestState *s;
20841a4050f5SDavid du Colombier 	uchar buf[11];
20851a4050f5SDavid du Colombier 
20861a4050f5SDavid du Colombier 	memmove(buf, seq, 8);
20871a4050f5SDavid du Colombier 	buf[8] = header[0];
20881a4050f5SDavid du Colombier 	buf[9] = header[3];
20891a4050f5SDavid du Colombier 	buf[10] = header[4];
20901a4050f5SDavid du Colombier 
20911a4050f5SDavid du Colombier 	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
20921a4050f5SDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
20931a4050f5SDavid du Colombier }
20941a4050f5SDavid du Colombier 
20951a4050f5SDavid du Colombier static void
tlsPackMac(Secret * sec,uchar * mackey,uchar * seq,uchar * header,uchar * body,int len,uchar * mac)20961a4050f5SDavid du Colombier tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
20971a4050f5SDavid du Colombier {
20981a4050f5SDavid du Colombier 	DigestState *s;
20991a4050f5SDavid du Colombier 	uchar buf[13];
21001a4050f5SDavid du Colombier 
21011a4050f5SDavid du Colombier 	memmove(buf, seq, 8);
21021a4050f5SDavid du Colombier 	memmove(&buf[8], header, 5);
21031a4050f5SDavid du Colombier 
21041a4050f5SDavid du Colombier 	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
21051a4050f5SDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
21061a4050f5SDavid du Colombier }
21071a4050f5SDavid du Colombier 
21081a4050f5SDavid du Colombier static void
put32(uchar * p,u32int x)21091a4050f5SDavid du Colombier put32(uchar *p, u32int x)
21101a4050f5SDavid du Colombier {
21111a4050f5SDavid du Colombier 	p[0] = x>>24;
21121a4050f5SDavid du Colombier 	p[1] = x>>16;
21131a4050f5SDavid du Colombier 	p[2] = x>>8;
21141a4050f5SDavid du Colombier 	p[3] = x;
21151a4050f5SDavid du Colombier }
21161a4050f5SDavid du Colombier 
21171a4050f5SDavid du Colombier static void
put64(uchar * p,vlong x)21181a4050f5SDavid du Colombier put64(uchar *p, vlong x)
21191a4050f5SDavid du Colombier {
21201a4050f5SDavid du Colombier 	put32(p, (u32int)(x >> 32));
21211a4050f5SDavid du Colombier 	put32(p+4, (u32int)x);
21221a4050f5SDavid du Colombier }
21231a4050f5SDavid du Colombier 
21241a4050f5SDavid du Colombier static void
put24(uchar * p,int x)21251a4050f5SDavid du Colombier put24(uchar *p, int x)
21261a4050f5SDavid du Colombier {
21271a4050f5SDavid du Colombier 	p[0] = x>>16;
21281a4050f5SDavid du Colombier 	p[1] = x>>8;
21291a4050f5SDavid du Colombier 	p[2] = x;
21301a4050f5SDavid du Colombier }
21311a4050f5SDavid du Colombier 
21321a4050f5SDavid du Colombier static void
put16(uchar * p,int x)21331a4050f5SDavid du Colombier put16(uchar *p, int x)
21341a4050f5SDavid du Colombier {
21351a4050f5SDavid du Colombier 	p[0] = x>>8;
21361a4050f5SDavid du Colombier 	p[1] = x;
21371a4050f5SDavid du Colombier }
21381a4050f5SDavid du Colombier 
2139*ec59a3ddSDavid du Colombier /*
21401a4050f5SDavid du Colombier static u32int
21411a4050f5SDavid du Colombier get32(uchar *p)
21421a4050f5SDavid du Colombier {
21431a4050f5SDavid du Colombier 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
21441a4050f5SDavid du Colombier }
2145*ec59a3ddSDavid du Colombier */
21461a4050f5SDavid du Colombier 
21471a4050f5SDavid du Colombier static int
get16(uchar * p)21481a4050f5SDavid du Colombier get16(uchar *p)
21491a4050f5SDavid du Colombier {
21501a4050f5SDavid du Colombier 	return (p[0]<<8)|p[1];
21511a4050f5SDavid du Colombier }
21521a4050f5SDavid du Colombier 
21531a4050f5SDavid du Colombier static char *charmap = "0123456789abcdef";
21541a4050f5SDavid du Colombier 
21551a4050f5SDavid du Colombier static void
pdump(int len,void * a,char * tag)21561a4050f5SDavid du Colombier pdump(int len, void *a, char *tag)
21571a4050f5SDavid du Colombier {
21581a4050f5SDavid du Colombier 	uchar *p;
21591a4050f5SDavid du Colombier 	int i;
21601a4050f5SDavid du Colombier 	char buf[65+32];
21611a4050f5SDavid du Colombier 	char *q;
21621a4050f5SDavid du Colombier 
21631a4050f5SDavid du Colombier 	p = a;
21641a4050f5SDavid du Colombier 	strcpy(buf, tag);
21651a4050f5SDavid du Colombier 	while(len > 0){
21661a4050f5SDavid du Colombier 		q = buf + strlen(tag);
21671a4050f5SDavid du Colombier 		for(i = 0; len > 0 && i < 32; i++){
21681a4050f5SDavid du Colombier 			if(*p >= ' ' && *p < 0x7f){
21691a4050f5SDavid du Colombier 				*q++ = ' ';
21701a4050f5SDavid du Colombier 				*q++ = *p;
21711a4050f5SDavid du Colombier 			} else {
21721a4050f5SDavid du Colombier 				*q++ = charmap[*p>>4];
21731a4050f5SDavid du Colombier 				*q++ = charmap[*p & 0xf];
21741a4050f5SDavid du Colombier 			}
21751a4050f5SDavid du Colombier 			len--;
21761a4050f5SDavid du Colombier 			p++;
21771a4050f5SDavid du Colombier 		}
21781a4050f5SDavid du Colombier 		*q = 0;
21791a4050f5SDavid du Colombier 
21801a4050f5SDavid du Colombier 		if(len > 0)
21811a4050f5SDavid du Colombier 			pprint("%s...\n", buf);
21821a4050f5SDavid du Colombier 		else
21831a4050f5SDavid du Colombier 			pprint("%s\n", buf);
21841a4050f5SDavid du Colombier 	}
21851a4050f5SDavid du Colombier }
2186