xref: /plan9-contrib/sys/src/cmd/unix/drawterm/kern/devtls.c (revision 1a4050f5b2ddf426a278e3233ccd7b6bcb0639b8)
1*1a4050f5SDavid du Colombier /*
2*1a4050f5SDavid du Colombier  *  devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
3*1a4050f5SDavid du Colombier  */
4*1a4050f5SDavid du Colombier #include	"u.h"
5*1a4050f5SDavid du Colombier #include	"lib.h"
6*1a4050f5SDavid du Colombier #include	"dat.h"
7*1a4050f5SDavid du Colombier #include	"fns.h"
8*1a4050f5SDavid du Colombier #include	"error.h"
9*1a4050f5SDavid du Colombier 
10*1a4050f5SDavid du Colombier #include	"libsec.h"
11*1a4050f5SDavid du Colombier 
12*1a4050f5SDavid du Colombier typedef struct OneWay	OneWay;
13*1a4050f5SDavid du Colombier typedef struct Secret		Secret;
14*1a4050f5SDavid du Colombier typedef struct TlsRec	TlsRec;
15*1a4050f5SDavid du Colombier typedef struct TlsErrs	TlsErrs;
16*1a4050f5SDavid du Colombier 
17*1a4050f5SDavid du Colombier enum {
18*1a4050f5SDavid du Colombier 	Statlen=	1024,		/* max. length of status or stats message */
19*1a4050f5SDavid du Colombier 	/* buffer limits */
20*1a4050f5SDavid du Colombier 	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */
21*1a4050f5SDavid du Colombier 	MaxCipherRecLen	= MaxRecLen + 2048,
22*1a4050f5SDavid du Colombier 	RecHdrLen		= 5,
23*1a4050f5SDavid du Colombier 	MaxMacLen		= SHA1dlen,
24*1a4050f5SDavid du Colombier 
25*1a4050f5SDavid du Colombier 	/* protocol versions we can accept */
26*1a4050f5SDavid du Colombier 	TLSVersion		= 0x0301,
27*1a4050f5SDavid du Colombier 	SSL3Version		= 0x0300,
28*1a4050f5SDavid du Colombier 	ProtocolVersion	= 0x0301,	/* maximum version we speak */
29*1a4050f5SDavid du Colombier 	MinProtoVersion	= 0x0300,	/* limits on version we accept */
30*1a4050f5SDavid du Colombier 	MaxProtoVersion	= 0x03ff,
31*1a4050f5SDavid du Colombier 
32*1a4050f5SDavid du Colombier 	/* connection states */
33*1a4050f5SDavid du Colombier 	SHandshake	= 1 << 0,	/* doing handshake */
34*1a4050f5SDavid du Colombier 	SOpen		= 1 << 1,	/* application data can be sent */
35*1a4050f5SDavid du Colombier 	SRClose		= 1 << 2,	/* remote side has closed down */
36*1a4050f5SDavid du Colombier 	SLClose		= 1 << 3,	/* sent a close notify alert */
37*1a4050f5SDavid du Colombier 	SAlert		= 1 << 5,	/* sending or sent a fatal alert */
38*1a4050f5SDavid du Colombier 	SError		= 1 << 6,	/* some sort of error has occured */
39*1a4050f5SDavid du Colombier 	SClosed		= 1 << 7,	/* it is all over */
40*1a4050f5SDavid du Colombier 
41*1a4050f5SDavid du Colombier 	/* record types */
42*1a4050f5SDavid du Colombier 	RChangeCipherSpec = 20,
43*1a4050f5SDavid du Colombier 	RAlert,
44*1a4050f5SDavid du Colombier 	RHandshake,
45*1a4050f5SDavid du Colombier 	RApplication,
46*1a4050f5SDavid du Colombier 
47*1a4050f5SDavid du Colombier 	SSL2ClientHello = 1,
48*1a4050f5SDavid du Colombier 	HSSL2ClientHello = 9,  /* local convention;  see tlshand.c */
49*1a4050f5SDavid du Colombier 
50*1a4050f5SDavid du Colombier 	/* alerts */
51*1a4050f5SDavid du Colombier 	ECloseNotify 			= 0,
52*1a4050f5SDavid du Colombier 	EUnexpectedMessage 	= 10,
53*1a4050f5SDavid du Colombier 	EBadRecordMac 		= 20,
54*1a4050f5SDavid du Colombier 	EDecryptionFailed 		= 21,
55*1a4050f5SDavid du Colombier 	ERecordOverflow 		= 22,
56*1a4050f5SDavid du Colombier 	EDecompressionFailure 	= 30,
57*1a4050f5SDavid du Colombier 	EHandshakeFailure 		= 40,
58*1a4050f5SDavid du Colombier 	ENoCertificate 			= 41,
59*1a4050f5SDavid du Colombier 	EBadCertificate 		= 42,
60*1a4050f5SDavid du Colombier 	EUnsupportedCertificate 	= 43,
61*1a4050f5SDavid du Colombier 	ECertificateRevoked 		= 44,
62*1a4050f5SDavid du Colombier 	ECertificateExpired 		= 45,
63*1a4050f5SDavid du Colombier 	ECertificateUnknown 	= 46,
64*1a4050f5SDavid du Colombier 	EIllegalParameter 		= 47,
65*1a4050f5SDavid du Colombier 	EUnknownCa 			= 48,
66*1a4050f5SDavid du Colombier 	EAccessDenied 		= 49,
67*1a4050f5SDavid du Colombier 	EDecodeError 			= 50,
68*1a4050f5SDavid du Colombier 	EDecryptError 			= 51,
69*1a4050f5SDavid du Colombier 	EExportRestriction 		= 60,
70*1a4050f5SDavid du Colombier 	EProtocolVersion 		= 70,
71*1a4050f5SDavid du Colombier 	EInsufficientSecurity 	= 71,
72*1a4050f5SDavid du Colombier 	EInternalError 			= 80,
73*1a4050f5SDavid du Colombier 	EUserCanceled 			= 90,
74*1a4050f5SDavid du Colombier 	ENoRenegotiation 		= 100,
75*1a4050f5SDavid du Colombier 
76*1a4050f5SDavid du Colombier 	EMAX = 256
77*1a4050f5SDavid du Colombier };
78*1a4050f5SDavid du Colombier 
79*1a4050f5SDavid du Colombier struct Secret
80*1a4050f5SDavid du Colombier {
81*1a4050f5SDavid du Colombier 	char		*encalg;	/* name of encryption alg */
82*1a4050f5SDavid du Colombier 	char		*hashalg;	/* name of hash alg */
83*1a4050f5SDavid du Colombier 	int		(*enc)(Secret*, uchar*, int);
84*1a4050f5SDavid du Colombier 	int		(*dec)(Secret*, uchar*, int);
85*1a4050f5SDavid du Colombier 	int		(*unpad)(uchar*, int, int);
86*1a4050f5SDavid du Colombier 	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
87*1a4050f5SDavid du Colombier 	int		block;		/* encryption block len, 0 if none */
88*1a4050f5SDavid du Colombier 	int		maclen;
89*1a4050f5SDavid du Colombier 	void		*enckey;
90*1a4050f5SDavid du Colombier 	uchar	mackey[MaxMacLen];
91*1a4050f5SDavid du Colombier };
92*1a4050f5SDavid du Colombier 
93*1a4050f5SDavid du Colombier struct OneWay
94*1a4050f5SDavid du Colombier {
95*1a4050f5SDavid du Colombier 	QLock		io;		/* locks io access */
96*1a4050f5SDavid du Colombier 	QLock		seclock;	/* locks secret paramaters */
97*1a4050f5SDavid du Colombier 	ulong		seq;
98*1a4050f5SDavid du Colombier 	Secret		*sec;		/* cipher in use */
99*1a4050f5SDavid du Colombier 	Secret		*new;		/* cipher waiting for enable */
100*1a4050f5SDavid du Colombier };
101*1a4050f5SDavid du Colombier 
102*1a4050f5SDavid du Colombier struct TlsRec
103*1a4050f5SDavid du Colombier {
104*1a4050f5SDavid du Colombier 	Chan	*c;				/* io channel */
105*1a4050f5SDavid du Colombier 	int		ref;				/* serialized by tdlock for atomic destroy */
106*1a4050f5SDavid du Colombier 	int		version;			/* version of the protocol we are speaking */
107*1a4050f5SDavid du Colombier 	char		verset;			/* version has been set */
108*1a4050f5SDavid du Colombier 	char		opened;			/* opened command every issued? */
109*1a4050f5SDavid du Colombier 	char		err[ERRMAX];		/* error message to return to handshake requests */
110*1a4050f5SDavid du Colombier 	vlong	handin;			/* bytes communicated by the record layer */
111*1a4050f5SDavid du Colombier 	vlong	handout;
112*1a4050f5SDavid du Colombier 	vlong	datain;
113*1a4050f5SDavid du Colombier 	vlong	dataout;
114*1a4050f5SDavid du Colombier 
115*1a4050f5SDavid du Colombier 	Lock		statelk;
116*1a4050f5SDavid du Colombier 	int		state;
117*1a4050f5SDavid du Colombier 	int		debug;
118*1a4050f5SDavid du Colombier 
119*1a4050f5SDavid du Colombier 	/* record layer mac functions for different protocol versions */
120*1a4050f5SDavid du Colombier 	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
121*1a4050f5SDavid du Colombier 
122*1a4050f5SDavid du Colombier 	/* input side -- protected by in.io */
123*1a4050f5SDavid du Colombier 	OneWay		in;
124*1a4050f5SDavid du Colombier 	Block		*processed;	/* next bunch of application data */
125*1a4050f5SDavid du Colombier 	Block		*unprocessed;	/* data read from c but not parsed into records */
126*1a4050f5SDavid du Colombier 
127*1a4050f5SDavid du Colombier 	/* handshake queue */
128*1a4050f5SDavid du Colombier 	Lock		hqlock;			/* protects hqref, alloc & free of handq, hprocessed */
129*1a4050f5SDavid du Colombier 	int		hqref;
130*1a4050f5SDavid du Colombier 	Queue		*handq;		/* queue of handshake messages */
131*1a4050f5SDavid du Colombier 	Block		*hprocessed;	/* remainder of last block read from handq */
132*1a4050f5SDavid du Colombier 	QLock		hqread;		/* protects reads for hprocessed, handq */
133*1a4050f5SDavid du Colombier 
134*1a4050f5SDavid du Colombier 	/* output side */
135*1a4050f5SDavid du Colombier 	OneWay		out;
136*1a4050f5SDavid du Colombier 
137*1a4050f5SDavid du Colombier 	/* protections */
138*1a4050f5SDavid du Colombier 	char		*user;
139*1a4050f5SDavid du Colombier 	int		perm;
140*1a4050f5SDavid du Colombier };
141*1a4050f5SDavid du Colombier 
142*1a4050f5SDavid du Colombier struct TlsErrs{
143*1a4050f5SDavid du Colombier 	int	err;
144*1a4050f5SDavid du Colombier 	int	sslerr;
145*1a4050f5SDavid du Colombier 	int	tlserr;
146*1a4050f5SDavid du Colombier 	int	fatal;
147*1a4050f5SDavid du Colombier 	char	*msg;
148*1a4050f5SDavid du Colombier };
149*1a4050f5SDavid du Colombier 
150*1a4050f5SDavid du Colombier static TlsErrs tlserrs[] = {
151*1a4050f5SDavid du Colombier 	{ECloseNotify,			ECloseNotify,			ECloseNotify,			0, 	"close notify"},
152*1a4050f5SDavid du Colombier 	{EUnexpectedMessage,	EUnexpectedMessage,	EUnexpectedMessage, 	1, "unexpected message"},
153*1a4050f5SDavid du Colombier 	{EBadRecordMac,		EBadRecordMac,		EBadRecordMac, 		1, "bad record mac"},
154*1a4050f5SDavid du Colombier 	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,		1, "decryption failed"},
155*1a4050f5SDavid du Colombier 	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,		1, "record too long"},
156*1a4050f5SDavid du Colombier 	{EDecompressionFailure,	EDecompressionFailure,	EDecompressionFailure,	1, "decompression failed"},
157*1a4050f5SDavid du Colombier 	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,		1, "could not negotiate acceptable security parameters"},
158*1a4050f5SDavid du Colombier 	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,	1, "no appropriate certificate available"},
159*1a4050f5SDavid du Colombier 	{EBadCertificate,		EBadCertificate,		EBadCertificate,		1, "corrupted or invalid certificate"},
160*1a4050f5SDavid du Colombier 	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,	1, "unsupported certificate type"},
161*1a4050f5SDavid du Colombier 	{ECertificateRevoked,	ECertificateRevoked,		ECertificateRevoked,		1, "revoked certificate"},
162*1a4050f5SDavid du Colombier 	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,		1, "expired certificate"},
163*1a4050f5SDavid du Colombier 	{ECertificateUnknown,	ECertificateUnknown,	ECertificateUnknown,	1, "unacceptable certificate"},
164*1a4050f5SDavid du Colombier 	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,		1, "illegal parameter"},
165*1a4050f5SDavid du Colombier 	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,			1, "unknown certificate authority"},
166*1a4050f5SDavid du Colombier 	{EAccessDenied,		EHandshakeFailure,		EAccessDenied,		1, "access denied"},
167*1a4050f5SDavid du Colombier 	{EDecodeError,			EIllegalParameter,		EDecodeError,			1, "error decoding message"},
168*1a4050f5SDavid du Colombier 	{EDecryptError,			EIllegalParameter,		EDecryptError,			1, "error decrypting message"},
169*1a4050f5SDavid du Colombier 	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,		1, "export restriction violated"},
170*1a4050f5SDavid du Colombier 	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,		1, "protocol version not supported"},
171*1a4050f5SDavid du Colombier 	{EInsufficientSecurity,	EHandshakeFailure,		EInsufficientSecurity,	1, "stronger security routines required"},
172*1a4050f5SDavid du Colombier 	{EInternalError,			EHandshakeFailure,		EInternalError,			1, "internal error"},
173*1a4050f5SDavid du Colombier 	{EUserCanceled,		ECloseNotify,			EUserCanceled,			0, "handshake canceled by user"},
174*1a4050f5SDavid du Colombier 	{ENoRenegotiation,		EUnexpectedMessage,	ENoRenegotiation,		0, "no renegotiation"},
175*1a4050f5SDavid du Colombier };
176*1a4050f5SDavid du Colombier 
177*1a4050f5SDavid du Colombier enum
178*1a4050f5SDavid du Colombier {
179*1a4050f5SDavid du Colombier 	/* max. open tls connections */
180*1a4050f5SDavid du Colombier 	MaxTlsDevs	= 1024
181*1a4050f5SDavid du Colombier };
182*1a4050f5SDavid du Colombier 
183*1a4050f5SDavid du Colombier static	Lock	tdlock;
184*1a4050f5SDavid du Colombier static	int	tdhiwat;
185*1a4050f5SDavid du Colombier static	int	maxtlsdevs = 128;
186*1a4050f5SDavid du Colombier static	TlsRec	**tlsdevs;
187*1a4050f5SDavid du Colombier static	char	**trnames;
188*1a4050f5SDavid du Colombier static	char	*encalgs;
189*1a4050f5SDavid du Colombier static	char	*hashalgs;
190*1a4050f5SDavid du Colombier 
191*1a4050f5SDavid du Colombier enum{
192*1a4050f5SDavid du Colombier 	Qtopdir		= 1,	/* top level directory */
193*1a4050f5SDavid du Colombier 	Qprotodir,
194*1a4050f5SDavid du Colombier 	Qclonus,
195*1a4050f5SDavid du Colombier 	Qencalgs,
196*1a4050f5SDavid du Colombier 	Qhashalgs,
197*1a4050f5SDavid du Colombier 	Qconvdir,		/* directory for a conversation */
198*1a4050f5SDavid du Colombier 	Qdata,
199*1a4050f5SDavid du Colombier 	Qctl,
200*1a4050f5SDavid du Colombier 	Qhand,
201*1a4050f5SDavid du Colombier 	Qstatus,
202*1a4050f5SDavid du Colombier 	Qstats,
203*1a4050f5SDavid du Colombier };
204*1a4050f5SDavid du Colombier 
205*1a4050f5SDavid du Colombier #define TYPE(x) 	((x).path & 0xf)
206*1a4050f5SDavid du Colombier #define CONV(x) 	(((x).path >> 5)&(MaxTlsDevs-1))
207*1a4050f5SDavid du Colombier #define QID(c, y) 	(((c)<<5) | (y))
208*1a4050f5SDavid du Colombier 
209*1a4050f5SDavid du Colombier static void	checkstate(TlsRec *, int, int);
210*1a4050f5SDavid du Colombier static void	ensure(TlsRec*, Block**, int);
211*1a4050f5SDavid du Colombier static void	consume(Block**, uchar*, int);
212*1a4050f5SDavid du Colombier static Chan*	buftochan(char*);
213*1a4050f5SDavid du Colombier static void	tlshangup(TlsRec*);
214*1a4050f5SDavid du Colombier static void	tlsError(TlsRec*, char *);
215*1a4050f5SDavid du Colombier static void	alertHand(TlsRec*, char *);
216*1a4050f5SDavid du Colombier static TlsRec	*newtls(Chan *c);
217*1a4050f5SDavid du Colombier static TlsRec	*mktlsrec(void);
218*1a4050f5SDavid du Colombier static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
219*1a4050f5SDavid du Colombier static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
220*1a4050f5SDavid du Colombier static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
221*1a4050f5SDavid du Colombier static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
222*1a4050f5SDavid du Colombier static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
223*1a4050f5SDavid du Colombier static void	put64(uchar *p, vlong x);
224*1a4050f5SDavid du Colombier static void	put32(uchar *p, u32int);
225*1a4050f5SDavid du Colombier static void	put24(uchar *p, int);
226*1a4050f5SDavid du Colombier static void	put16(uchar *p, int);
227*1a4050f5SDavid du Colombier static u32int	get32(uchar *p);
228*1a4050f5SDavid du Colombier static int	get16(uchar *p);
229*1a4050f5SDavid du Colombier static void	tlsSetState(TlsRec *tr, int new, int old);
230*1a4050f5SDavid du Colombier static void	rcvAlert(TlsRec *tr, int err);
231*1a4050f5SDavid du Colombier static void	sendAlert(TlsRec *tr, int err);
232*1a4050f5SDavid du Colombier static void	rcvError(TlsRec *tr, int err, char *msg, ...);
233*1a4050f5SDavid du Colombier static int	rc4enc(Secret *sec, uchar *buf, int n);
234*1a4050f5SDavid du Colombier static int	des3enc(Secret *sec, uchar *buf, int n);
235*1a4050f5SDavid du Colombier static int	des3dec(Secret *sec, uchar *buf, int n);
236*1a4050f5SDavid du Colombier static int	noenc(Secret *sec, uchar *buf, int n);
237*1a4050f5SDavid du Colombier static int	sslunpad(uchar *buf, int n, int block);
238*1a4050f5SDavid du Colombier static int	tlsunpad(uchar *buf, int n, int block);
239*1a4050f5SDavid du Colombier static void	freeSec(Secret *sec);
240*1a4050f5SDavid du Colombier static char	*tlsstate(int s);
241*1a4050f5SDavid du Colombier static void	pdump(int, void*, char*);
242*1a4050f5SDavid du Colombier 
243*1a4050f5SDavid du Colombier static char *tlsnames[] = {
244*1a4050f5SDavid du Colombier [Qclonus]		"clone",
245*1a4050f5SDavid du Colombier [Qencalgs]	"encalgs",
246*1a4050f5SDavid du Colombier [Qhashalgs]	"hashalgs",
247*1a4050f5SDavid du Colombier [Qdata]		"data",
248*1a4050f5SDavid du Colombier [Qctl]		"ctl",
249*1a4050f5SDavid du Colombier [Qhand]		"hand",
250*1a4050f5SDavid du Colombier [Qstatus]		"status",
251*1a4050f5SDavid du Colombier [Qstats]		"stats",
252*1a4050f5SDavid du Colombier };
253*1a4050f5SDavid du Colombier 
254*1a4050f5SDavid du Colombier static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
255*1a4050f5SDavid du Colombier 
256*1a4050f5SDavid du Colombier static int
257*1a4050f5SDavid du Colombier tlsgen(Chan *c, char*unused1, Dirtab *unused2, int unused3, int s, Dir *dp)
258*1a4050f5SDavid du Colombier {
259*1a4050f5SDavid du Colombier 	Qid q;
260*1a4050f5SDavid du Colombier 	TlsRec *tr;
261*1a4050f5SDavid du Colombier 	char *name, *nm;
262*1a4050f5SDavid du Colombier 	int perm, t;
263*1a4050f5SDavid du Colombier 
264*1a4050f5SDavid du Colombier 	q.vers = 0;
265*1a4050f5SDavid du Colombier 	q.type = QTFILE;
266*1a4050f5SDavid du Colombier 
267*1a4050f5SDavid du Colombier 	t = TYPE(c->qid);
268*1a4050f5SDavid du Colombier 	switch(t) {
269*1a4050f5SDavid du Colombier 	case Qtopdir:
270*1a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
271*1a4050f5SDavid du Colombier 			q.path = QID(0, Qtopdir);
272*1a4050f5SDavid du Colombier 			q.type = QTDIR;
273*1a4050f5SDavid du Colombier 			devdir(c, q, "#a", 0, eve, 0555, dp);
274*1a4050f5SDavid du Colombier 			return 1;
275*1a4050f5SDavid du Colombier 		}
276*1a4050f5SDavid du Colombier 		if(s > 0)
277*1a4050f5SDavid du Colombier 			return -1;
278*1a4050f5SDavid du Colombier 		q.path = QID(0, Qprotodir);
279*1a4050f5SDavid du Colombier 		q.type = QTDIR;
280*1a4050f5SDavid du Colombier 		devdir(c, q, "tls", 0, eve, 0555, dp);
281*1a4050f5SDavid du Colombier 		return 1;
282*1a4050f5SDavid du Colombier 	case Qprotodir:
283*1a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
284*1a4050f5SDavid du Colombier 			q.path = QID(0, Qtopdir);
285*1a4050f5SDavid du Colombier 			q.type = QTDIR;
286*1a4050f5SDavid du Colombier 			devdir(c, q, ".", 0, eve, 0555, dp);
287*1a4050f5SDavid du Colombier 			return 1;
288*1a4050f5SDavid du Colombier 		}
289*1a4050f5SDavid du Colombier 		if(s < 3){
290*1a4050f5SDavid du Colombier 			switch(s) {
291*1a4050f5SDavid du Colombier 			default:
292*1a4050f5SDavid du Colombier 				return -1;
293*1a4050f5SDavid du Colombier 			case 0:
294*1a4050f5SDavid du Colombier 				q.path = QID(0, Qclonus);
295*1a4050f5SDavid du Colombier 				break;
296*1a4050f5SDavid du Colombier 			case 1:
297*1a4050f5SDavid du Colombier 				q.path = QID(0, Qencalgs);
298*1a4050f5SDavid du Colombier 				break;
299*1a4050f5SDavid du Colombier 			case 2:
300*1a4050f5SDavid du Colombier 				q.path = QID(0, Qhashalgs);
301*1a4050f5SDavid du Colombier 				break;
302*1a4050f5SDavid du Colombier 			}
303*1a4050f5SDavid du Colombier 			perm = 0444;
304*1a4050f5SDavid du Colombier 			if(TYPE(q) == Qclonus)
305*1a4050f5SDavid du Colombier 				perm = 0555;
306*1a4050f5SDavid du Colombier 			devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
307*1a4050f5SDavid du Colombier 			return 1;
308*1a4050f5SDavid du Colombier 		}
309*1a4050f5SDavid du Colombier 		s -= 3;
310*1a4050f5SDavid du Colombier 		if(s >= tdhiwat)
311*1a4050f5SDavid du Colombier 			return -1;
312*1a4050f5SDavid du Colombier 		q.path = QID(s, Qconvdir);
313*1a4050f5SDavid du Colombier 		q.type = QTDIR;
314*1a4050f5SDavid du Colombier 		lock(&tdlock);
315*1a4050f5SDavid du Colombier 		tr = tlsdevs[s];
316*1a4050f5SDavid du Colombier 		if(tr != nil)
317*1a4050f5SDavid du Colombier 			nm = tr->user;
318*1a4050f5SDavid du Colombier 		else
319*1a4050f5SDavid du Colombier 			nm = eve;
320*1a4050f5SDavid du Colombier 		if((name = trnames[s]) == nil){
321*1a4050f5SDavid du Colombier 			name = trnames[s] = smalloc(16);
322*1a4050f5SDavid du Colombier 			sprint(name, "%d", s);
323*1a4050f5SDavid du Colombier 		}
324*1a4050f5SDavid du Colombier 		devdir(c, q, name, 0, nm, 0555, dp);
325*1a4050f5SDavid du Colombier 		unlock(&tdlock);
326*1a4050f5SDavid du Colombier 		return 1;
327*1a4050f5SDavid du Colombier 	case Qconvdir:
328*1a4050f5SDavid du Colombier 		if(s == DEVDOTDOT){
329*1a4050f5SDavid du Colombier 			q.path = QID(0, Qprotodir);
330*1a4050f5SDavid du Colombier 			q.type = QTDIR;
331*1a4050f5SDavid du Colombier 			devdir(c, q, "tls", 0, eve, 0555, dp);
332*1a4050f5SDavid du Colombier 			return 1;
333*1a4050f5SDavid du Colombier 		}
334*1a4050f5SDavid du Colombier 		if(s < 0 || s >= nelem(convdir))
335*1a4050f5SDavid du Colombier 			return -1;
336*1a4050f5SDavid du Colombier 		lock(&tdlock);
337*1a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
338*1a4050f5SDavid du Colombier 		if(tr != nil){
339*1a4050f5SDavid du Colombier 			nm = tr->user;
340*1a4050f5SDavid du Colombier 			perm = tr->perm;
341*1a4050f5SDavid du Colombier 		}else{
342*1a4050f5SDavid du Colombier 			perm = 0;
343*1a4050f5SDavid du Colombier 			nm = eve;
344*1a4050f5SDavid du Colombier 		}
345*1a4050f5SDavid du Colombier 		t = convdir[s];
346*1a4050f5SDavid du Colombier 		if(t == Qstatus || t == Qstats)
347*1a4050f5SDavid du Colombier 			perm &= 0444;
348*1a4050f5SDavid du Colombier 		q.path = QID(CONV(c->qid), t);
349*1a4050f5SDavid du Colombier 		devdir(c, q, tlsnames[t], 0, nm, perm, dp);
350*1a4050f5SDavid du Colombier 		unlock(&tdlock);
351*1a4050f5SDavid du Colombier 		return 1;
352*1a4050f5SDavid du Colombier 	case Qclonus:
353*1a4050f5SDavid du Colombier 	case Qencalgs:
354*1a4050f5SDavid du Colombier 	case Qhashalgs:
355*1a4050f5SDavid du Colombier 		perm = 0444;
356*1a4050f5SDavid du Colombier 		if(t == Qclonus)
357*1a4050f5SDavid du Colombier 			perm = 0555;
358*1a4050f5SDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
359*1a4050f5SDavid du Colombier 		return 1;
360*1a4050f5SDavid du Colombier 	default:
361*1a4050f5SDavid du Colombier 		lock(&tdlock);
362*1a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
363*1a4050f5SDavid du Colombier 		if(tr != nil){
364*1a4050f5SDavid du Colombier 			nm = tr->user;
365*1a4050f5SDavid du Colombier 			perm = tr->perm;
366*1a4050f5SDavid du Colombier 		}else{
367*1a4050f5SDavid du Colombier 			perm = 0;
368*1a4050f5SDavid du Colombier 			nm = eve;
369*1a4050f5SDavid du Colombier 		}
370*1a4050f5SDavid du Colombier 		if(t == Qstatus || t == Qstats)
371*1a4050f5SDavid du Colombier 			perm &= 0444;
372*1a4050f5SDavid du Colombier 		devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
373*1a4050f5SDavid du Colombier 		unlock(&tdlock);
374*1a4050f5SDavid du Colombier 		return 1;
375*1a4050f5SDavid du Colombier 	}
376*1a4050f5SDavid du Colombier 	return -1;
377*1a4050f5SDavid du Colombier }
378*1a4050f5SDavid du Colombier 
379*1a4050f5SDavid du Colombier static Chan*
380*1a4050f5SDavid du Colombier tlsattach(char *spec)
381*1a4050f5SDavid du Colombier {
382*1a4050f5SDavid du Colombier 	Chan *c;
383*1a4050f5SDavid du Colombier 
384*1a4050f5SDavid du Colombier 	c = devattach('a', spec);
385*1a4050f5SDavid du Colombier 	c->qid.path = QID(0, Qtopdir);
386*1a4050f5SDavid du Colombier 	c->qid.type = QTDIR;
387*1a4050f5SDavid du Colombier 	c->qid.vers = 0;
388*1a4050f5SDavid du Colombier 	return c;
389*1a4050f5SDavid du Colombier }
390*1a4050f5SDavid du Colombier 
391*1a4050f5SDavid du Colombier static Walkqid*
392*1a4050f5SDavid du Colombier tlswalk(Chan *c, Chan *nc, char **name, int nname)
393*1a4050f5SDavid du Colombier {
394*1a4050f5SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, tlsgen);
395*1a4050f5SDavid du Colombier }
396*1a4050f5SDavid du Colombier 
397*1a4050f5SDavid du Colombier static int
398*1a4050f5SDavid du Colombier tlsstat(Chan *c, uchar *db, int n)
399*1a4050f5SDavid du Colombier {
400*1a4050f5SDavid du Colombier 	return devstat(c, db, n, nil, 0, tlsgen);
401*1a4050f5SDavid du Colombier }
402*1a4050f5SDavid du Colombier 
403*1a4050f5SDavid du Colombier static Chan*
404*1a4050f5SDavid du Colombier tlsopen(Chan *c, int omode)
405*1a4050f5SDavid du Colombier {
406*1a4050f5SDavid du Colombier 	TlsRec *tr, **pp;
407*1a4050f5SDavid du Colombier 	int t, perm;
408*1a4050f5SDavid du Colombier 
409*1a4050f5SDavid du Colombier 	perm = 0;
410*1a4050f5SDavid du Colombier 	omode &= 3;
411*1a4050f5SDavid du Colombier 	switch(omode) {
412*1a4050f5SDavid du Colombier 	case OREAD:
413*1a4050f5SDavid du Colombier 		perm = 4;
414*1a4050f5SDavid du Colombier 		break;
415*1a4050f5SDavid du Colombier 	case OWRITE:
416*1a4050f5SDavid du Colombier 		perm = 2;
417*1a4050f5SDavid du Colombier 		break;
418*1a4050f5SDavid du Colombier 	case ORDWR:
419*1a4050f5SDavid du Colombier 		perm = 6;
420*1a4050f5SDavid du Colombier 		break;
421*1a4050f5SDavid du Colombier 	}
422*1a4050f5SDavid du Colombier 
423*1a4050f5SDavid du Colombier 	t = TYPE(c->qid);
424*1a4050f5SDavid du Colombier 	switch(t) {
425*1a4050f5SDavid du Colombier 	default:
426*1a4050f5SDavid du Colombier 		panic("tlsopen");
427*1a4050f5SDavid du Colombier 	case Qtopdir:
428*1a4050f5SDavid du Colombier 	case Qprotodir:
429*1a4050f5SDavid du Colombier 	case Qconvdir:
430*1a4050f5SDavid du Colombier 		if(omode != OREAD)
431*1a4050f5SDavid du Colombier 			error(Eperm);
432*1a4050f5SDavid du Colombier 		break;
433*1a4050f5SDavid du Colombier 	case Qclonus:
434*1a4050f5SDavid du Colombier 		tr = newtls(c);
435*1a4050f5SDavid du Colombier 		if(tr == nil)
436*1a4050f5SDavid du Colombier 			error(Enodev);
437*1a4050f5SDavid du Colombier 		break;
438*1a4050f5SDavid du Colombier 	case Qctl:
439*1a4050f5SDavid du Colombier 	case Qdata:
440*1a4050f5SDavid du Colombier 	case Qhand:
441*1a4050f5SDavid du Colombier 	case Qstatus:
442*1a4050f5SDavid du Colombier 	case Qstats:
443*1a4050f5SDavid du Colombier 		if((t == Qstatus || t == Qstats) && omode != OREAD)
444*1a4050f5SDavid du Colombier 			error(Eperm);
445*1a4050f5SDavid du Colombier 		if(waserror()) {
446*1a4050f5SDavid du Colombier 			unlock(&tdlock);
447*1a4050f5SDavid du Colombier 			nexterror();
448*1a4050f5SDavid du Colombier 		}
449*1a4050f5SDavid du Colombier 		lock(&tdlock);
450*1a4050f5SDavid du Colombier 		pp = &tlsdevs[CONV(c->qid)];
451*1a4050f5SDavid du Colombier 		tr = *pp;
452*1a4050f5SDavid du Colombier 		if(tr == nil)
453*1a4050f5SDavid du Colombier 			error("must open connection using clone");
454*1a4050f5SDavid du Colombier 		if((perm & (tr->perm>>6)) != perm
455*1a4050f5SDavid du Colombier 		&& (strcmp(up->user, tr->user) != 0
456*1a4050f5SDavid du Colombier 		    || (perm & tr->perm) != perm))
457*1a4050f5SDavid du Colombier 			error(Eperm);
458*1a4050f5SDavid du Colombier 		if(t == Qhand){
459*1a4050f5SDavid du Colombier 			if(waserror()){
460*1a4050f5SDavid du Colombier 				unlock(&tr->hqlock);
461*1a4050f5SDavid du Colombier 				nexterror();
462*1a4050f5SDavid du Colombier 			}
463*1a4050f5SDavid du Colombier 			lock(&tr->hqlock);
464*1a4050f5SDavid du Colombier 			if(tr->handq != nil)
465*1a4050f5SDavid du Colombier 				error(Einuse);
466*1a4050f5SDavid du Colombier 			tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
467*1a4050f5SDavid du Colombier 			if(tr->handq == nil)
468*1a4050f5SDavid du Colombier 				error("cannot allocate handshake queue");
469*1a4050f5SDavid du Colombier 			tr->hqref = 1;
470*1a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
471*1a4050f5SDavid du Colombier 			poperror();
472*1a4050f5SDavid du Colombier 		}
473*1a4050f5SDavid du Colombier 		tr->ref++;
474*1a4050f5SDavid du Colombier 		unlock(&tdlock);
475*1a4050f5SDavid du Colombier 		poperror();
476*1a4050f5SDavid du Colombier 		break;
477*1a4050f5SDavid du Colombier 	case Qencalgs:
478*1a4050f5SDavid du Colombier 	case Qhashalgs:
479*1a4050f5SDavid du Colombier 		if(omode != OREAD)
480*1a4050f5SDavid du Colombier 			error(Eperm);
481*1a4050f5SDavid du Colombier 		break;
482*1a4050f5SDavid du Colombier 	}
483*1a4050f5SDavid du Colombier 	c->mode = openmode(omode);
484*1a4050f5SDavid du Colombier 	c->flag |= COPEN;
485*1a4050f5SDavid du Colombier 	c->offset = 0;
486*1a4050f5SDavid du Colombier 	c->iounit = qiomaxatomic;
487*1a4050f5SDavid du Colombier 	return c;
488*1a4050f5SDavid du Colombier }
489*1a4050f5SDavid du Colombier 
490*1a4050f5SDavid du Colombier static int
491*1a4050f5SDavid du Colombier tlswstat(Chan *c, uchar *dp, int n)
492*1a4050f5SDavid du Colombier {
493*1a4050f5SDavid du Colombier 	Dir *d;
494*1a4050f5SDavid du Colombier 	TlsRec *tr;
495*1a4050f5SDavid du Colombier 	int rv;
496*1a4050f5SDavid du Colombier 
497*1a4050f5SDavid du Colombier 	d = nil;
498*1a4050f5SDavid du Colombier 	if(waserror()){
499*1a4050f5SDavid du Colombier 		free(d);
500*1a4050f5SDavid du Colombier 		unlock(&tdlock);
501*1a4050f5SDavid du Colombier 		nexterror();
502*1a4050f5SDavid du Colombier 	}
503*1a4050f5SDavid du Colombier 
504*1a4050f5SDavid du Colombier 	lock(&tdlock);
505*1a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
506*1a4050f5SDavid du Colombier 	if(tr == nil)
507*1a4050f5SDavid du Colombier 		error(Ebadusefd);
508*1a4050f5SDavid du Colombier 	if(strcmp(tr->user, up->user) != 0)
509*1a4050f5SDavid du Colombier 		error(Eperm);
510*1a4050f5SDavid du Colombier 
511*1a4050f5SDavid du Colombier 	d = smalloc(n + sizeof *d);
512*1a4050f5SDavid du Colombier 	rv = convM2D(dp, n, &d[0], (char*) &d[1]);
513*1a4050f5SDavid du Colombier 	if(rv == 0)
514*1a4050f5SDavid du Colombier 		error(Eshortstat);
515*1a4050f5SDavid du Colombier 	if(!emptystr(d->uid))
516*1a4050f5SDavid du Colombier 		kstrdup(&tr->user, d->uid);
517*1a4050f5SDavid du Colombier 	if(d->mode != ~0UL)
518*1a4050f5SDavid du Colombier 		tr->perm = d->mode;
519*1a4050f5SDavid du Colombier 
520*1a4050f5SDavid du Colombier 	free(d);
521*1a4050f5SDavid du Colombier 	poperror();
522*1a4050f5SDavid du Colombier 	unlock(&tdlock);
523*1a4050f5SDavid du Colombier 
524*1a4050f5SDavid du Colombier 	return rv;
525*1a4050f5SDavid du Colombier }
526*1a4050f5SDavid du Colombier 
527*1a4050f5SDavid du Colombier static void
528*1a4050f5SDavid du Colombier dechandq(TlsRec *tr)
529*1a4050f5SDavid du Colombier {
530*1a4050f5SDavid du Colombier 	lock(&tr->hqlock);
531*1a4050f5SDavid du Colombier 	if(--tr->hqref == 0){
532*1a4050f5SDavid du Colombier 		if(tr->handq != nil){
533*1a4050f5SDavid du Colombier 			qfree(tr->handq);
534*1a4050f5SDavid du Colombier 			tr->handq = nil;
535*1a4050f5SDavid du Colombier 		}
536*1a4050f5SDavid du Colombier 		if(tr->hprocessed != nil){
537*1a4050f5SDavid du Colombier 			freeb(tr->hprocessed);
538*1a4050f5SDavid du Colombier 			tr->hprocessed = nil;
539*1a4050f5SDavid du Colombier 		}
540*1a4050f5SDavid du Colombier 	}
541*1a4050f5SDavid du Colombier 	unlock(&tr->hqlock);
542*1a4050f5SDavid du Colombier }
543*1a4050f5SDavid du Colombier 
544*1a4050f5SDavid du Colombier static void
545*1a4050f5SDavid du Colombier tlsclose(Chan *c)
546*1a4050f5SDavid du Colombier {
547*1a4050f5SDavid du Colombier 	TlsRec *tr;
548*1a4050f5SDavid du Colombier 	int t;
549*1a4050f5SDavid du Colombier 
550*1a4050f5SDavid du Colombier 	t = TYPE(c->qid);
551*1a4050f5SDavid du Colombier 	switch(t) {
552*1a4050f5SDavid du Colombier 	case Qctl:
553*1a4050f5SDavid du Colombier 	case Qdata:
554*1a4050f5SDavid du Colombier 	case Qhand:
555*1a4050f5SDavid du Colombier 	case Qstatus:
556*1a4050f5SDavid du Colombier 	case Qstats:
557*1a4050f5SDavid du Colombier 		if((c->flag & COPEN) == 0)
558*1a4050f5SDavid du Colombier 			break;
559*1a4050f5SDavid du Colombier 
560*1a4050f5SDavid du Colombier 		tr = tlsdevs[CONV(c->qid)];
561*1a4050f5SDavid du Colombier 		if(tr == nil)
562*1a4050f5SDavid du Colombier 			break;
563*1a4050f5SDavid du Colombier 
564*1a4050f5SDavid du Colombier 		if(t == Qhand)
565*1a4050f5SDavid du Colombier 			dechandq(tr);
566*1a4050f5SDavid du Colombier 
567*1a4050f5SDavid du Colombier 		lock(&tdlock);
568*1a4050f5SDavid du Colombier 		if(--tr->ref > 0) {
569*1a4050f5SDavid du Colombier 			unlock(&tdlock);
570*1a4050f5SDavid du Colombier 			return;
571*1a4050f5SDavid du Colombier 		}
572*1a4050f5SDavid du Colombier 		tlsdevs[CONV(c->qid)] = nil;
573*1a4050f5SDavid du Colombier 		unlock(&tdlock);
574*1a4050f5SDavid du Colombier 
575*1a4050f5SDavid du Colombier 		if(tr->c != nil && !waserror()){
576*1a4050f5SDavid du Colombier 			checkstate(tr, 0, SOpen|SHandshake|SRClose);
577*1a4050f5SDavid du Colombier 			sendAlert(tr, ECloseNotify);
578*1a4050f5SDavid du Colombier 			poperror();
579*1a4050f5SDavid du Colombier 		}
580*1a4050f5SDavid du Colombier 		tlshangup(tr);
581*1a4050f5SDavid du Colombier 		if(tr->c != nil)
582*1a4050f5SDavid du Colombier 			cclose(tr->c);
583*1a4050f5SDavid du Colombier 		freeSec(tr->in.sec);
584*1a4050f5SDavid du Colombier 		freeSec(tr->in.new);
585*1a4050f5SDavid du Colombier 		freeSec(tr->out.sec);
586*1a4050f5SDavid du Colombier 		freeSec(tr->out.new);
587*1a4050f5SDavid du Colombier 		free(tr->user);
588*1a4050f5SDavid du Colombier 		free(tr);
589*1a4050f5SDavid du Colombier 		break;
590*1a4050f5SDavid du Colombier 	}
591*1a4050f5SDavid du Colombier }
592*1a4050f5SDavid du Colombier 
593*1a4050f5SDavid du Colombier /*
594*1a4050f5SDavid du Colombier  *  make sure we have at least 'n' bytes in list 'l'
595*1a4050f5SDavid du Colombier  */
596*1a4050f5SDavid du Colombier static void
597*1a4050f5SDavid du Colombier ensure(TlsRec *s, Block **l, int n)
598*1a4050f5SDavid du Colombier {
599*1a4050f5SDavid du Colombier 	int sofar, i;
600*1a4050f5SDavid du Colombier 	Block *b, *bl;
601*1a4050f5SDavid du Colombier 
602*1a4050f5SDavid du Colombier 	sofar = 0;
603*1a4050f5SDavid du Colombier 	for(b = *l; b; b = b->next){
604*1a4050f5SDavid du Colombier 		sofar += BLEN(b);
605*1a4050f5SDavid du Colombier 		if(sofar >= n)
606*1a4050f5SDavid du Colombier 			return;
607*1a4050f5SDavid du Colombier 		l = &b->next;
608*1a4050f5SDavid du Colombier 	}
609*1a4050f5SDavid du Colombier 
610*1a4050f5SDavid du Colombier 	while(sofar < n){
611*1a4050f5SDavid du Colombier 		bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
612*1a4050f5SDavid du Colombier 		if(bl == 0)
613*1a4050f5SDavid du Colombier 			error(Ehungup);
614*1a4050f5SDavid du Colombier 		*l = bl;
615*1a4050f5SDavid du Colombier 		i = 0;
616*1a4050f5SDavid du Colombier 		for(b = bl; b; b = b->next){
617*1a4050f5SDavid du Colombier 			i += BLEN(b);
618*1a4050f5SDavid du Colombier 			l = &b->next;
619*1a4050f5SDavid du Colombier 		}
620*1a4050f5SDavid du Colombier 		if(i == 0)
621*1a4050f5SDavid du Colombier 			error(Ehungup);
622*1a4050f5SDavid du Colombier 		sofar += i;
623*1a4050f5SDavid du Colombier 	}
624*1a4050f5SDavid du Colombier if(s->debug) pprint("ensure read %d\n", sofar);
625*1a4050f5SDavid du Colombier }
626*1a4050f5SDavid du Colombier 
627*1a4050f5SDavid du Colombier /*
628*1a4050f5SDavid du Colombier  *  copy 'n' bytes from 'l' into 'p' and free
629*1a4050f5SDavid du Colombier  *  the bytes in 'l'
630*1a4050f5SDavid du Colombier  */
631*1a4050f5SDavid du Colombier static void
632*1a4050f5SDavid du Colombier consume(Block **l, uchar *p, int n)
633*1a4050f5SDavid du Colombier {
634*1a4050f5SDavid du Colombier 	Block *b;
635*1a4050f5SDavid du Colombier 	int i;
636*1a4050f5SDavid du Colombier 
637*1a4050f5SDavid du Colombier 	for(; *l && n > 0; n -= i){
638*1a4050f5SDavid du Colombier 		b = *l;
639*1a4050f5SDavid du Colombier 		i = BLEN(b);
640*1a4050f5SDavid du Colombier 		if(i > n)
641*1a4050f5SDavid du Colombier 			i = n;
642*1a4050f5SDavid du Colombier 		memmove(p, b->rp, i);
643*1a4050f5SDavid du Colombier 		b->rp += i;
644*1a4050f5SDavid du Colombier 		p += i;
645*1a4050f5SDavid du Colombier 		if(BLEN(b) < 0)
646*1a4050f5SDavid du Colombier 			panic("consume");
647*1a4050f5SDavid du Colombier 		if(BLEN(b))
648*1a4050f5SDavid du Colombier 			break;
649*1a4050f5SDavid du Colombier 		*l = b->next;
650*1a4050f5SDavid du Colombier 		freeb(b);
651*1a4050f5SDavid du Colombier 	}
652*1a4050f5SDavid du Colombier }
653*1a4050f5SDavid du Colombier 
654*1a4050f5SDavid du Colombier /*
655*1a4050f5SDavid du Colombier  *  give back n bytes
656*1a4050f5SDavid du Colombier  */
657*1a4050f5SDavid du Colombier static void
658*1a4050f5SDavid du Colombier regurgitate(TlsRec *s, uchar *p, int n)
659*1a4050f5SDavid du Colombier {
660*1a4050f5SDavid du Colombier 	Block *b;
661*1a4050f5SDavid du Colombier 
662*1a4050f5SDavid du Colombier 	if(n <= 0)
663*1a4050f5SDavid du Colombier 		return;
664*1a4050f5SDavid du Colombier 	b = s->unprocessed;
665*1a4050f5SDavid du Colombier 	if(s->unprocessed == nil || b->rp - b->base < n) {
666*1a4050f5SDavid du Colombier 		b = allocb(n);
667*1a4050f5SDavid du Colombier 		memmove(b->wp, p, n);
668*1a4050f5SDavid du Colombier 		b->wp += n;
669*1a4050f5SDavid du Colombier 		b->next = s->unprocessed;
670*1a4050f5SDavid du Colombier 		s->unprocessed = b;
671*1a4050f5SDavid du Colombier 	} else {
672*1a4050f5SDavid du Colombier 		b->rp -= n;
673*1a4050f5SDavid du Colombier 		memmove(b->rp, p, n);
674*1a4050f5SDavid du Colombier 	}
675*1a4050f5SDavid du Colombier }
676*1a4050f5SDavid du Colombier 
677*1a4050f5SDavid du Colombier /*
678*1a4050f5SDavid du Colombier  *  remove at most n bytes from the queue
679*1a4050f5SDavid du Colombier  */
680*1a4050f5SDavid du Colombier static Block*
681*1a4050f5SDavid du Colombier qgrab(Block **l, int n)
682*1a4050f5SDavid du Colombier {
683*1a4050f5SDavid du Colombier 	Block *bb, *b;
684*1a4050f5SDavid du Colombier 	int i;
685*1a4050f5SDavid du Colombier 
686*1a4050f5SDavid du Colombier 	b = *l;
687*1a4050f5SDavid du Colombier 	if(BLEN(b) == n){
688*1a4050f5SDavid du Colombier 		*l = b->next;
689*1a4050f5SDavid du Colombier 		b->next = nil;
690*1a4050f5SDavid du Colombier 		return b;
691*1a4050f5SDavid du Colombier 	}
692*1a4050f5SDavid du Colombier 
693*1a4050f5SDavid du Colombier 	i = 0;
694*1a4050f5SDavid du Colombier 	for(bb = b; bb != nil && i < n; bb = bb->next)
695*1a4050f5SDavid du Colombier 		i += BLEN(bb);
696*1a4050f5SDavid du Colombier 	if(i > n)
697*1a4050f5SDavid du Colombier 		i = n;
698*1a4050f5SDavid du Colombier 
699*1a4050f5SDavid du Colombier 	bb = allocb(i);
700*1a4050f5SDavid du Colombier 	consume(l, bb->wp, i);
701*1a4050f5SDavid du Colombier 	bb->wp += i;
702*1a4050f5SDavid du Colombier 	return bb;
703*1a4050f5SDavid du Colombier }
704*1a4050f5SDavid du Colombier 
705*1a4050f5SDavid du Colombier static void
706*1a4050f5SDavid du Colombier tlsclosed(TlsRec *tr, int new)
707*1a4050f5SDavid du Colombier {
708*1a4050f5SDavid du Colombier 	lock(&tr->statelk);
709*1a4050f5SDavid du Colombier 	if(tr->state == SOpen || tr->state == SHandshake)
710*1a4050f5SDavid du Colombier 		tr->state = new;
711*1a4050f5SDavid du Colombier 	else if((new | tr->state) == (SRClose|SLClose))
712*1a4050f5SDavid du Colombier 		tr->state = SClosed;
713*1a4050f5SDavid du Colombier 	unlock(&tr->statelk);
714*1a4050f5SDavid du Colombier 	alertHand(tr, "close notify");
715*1a4050f5SDavid du Colombier }
716*1a4050f5SDavid du Colombier 
717*1a4050f5SDavid du Colombier /*
718*1a4050f5SDavid du Colombier  *  read and process one tls record layer message
719*1a4050f5SDavid du Colombier  *  must be called with tr->in.io held
720*1a4050f5SDavid du Colombier  *  We can't let Eintrs lose data, since doing so will get
721*1a4050f5SDavid du Colombier  *  us out of sync with the sender and break the reliablity
722*1a4050f5SDavid du Colombier  *  of the channel.  Eintr only happens during the reads in
723*1a4050f5SDavid du Colombier  *  consume.  Therefore we put back any bytes consumed before
724*1a4050f5SDavid du Colombier  *  the last call to ensure.
725*1a4050f5SDavid du Colombier  */
726*1a4050f5SDavid du Colombier static void
727*1a4050f5SDavid du Colombier tlsrecread(TlsRec *tr)
728*1a4050f5SDavid du Colombier {
729*1a4050f5SDavid du Colombier 	OneWay *volatile in;
730*1a4050f5SDavid du Colombier 	Block *volatile b;
731*1a4050f5SDavid du Colombier 	uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
732*1a4050f5SDavid du Colombier 	int volatile nconsumed;
733*1a4050f5SDavid du Colombier 	int len, type, ver, unpad_len;
734*1a4050f5SDavid du Colombier 
735*1a4050f5SDavid du Colombier 	nconsumed = 0;
736*1a4050f5SDavid du Colombier 	if(waserror()){
737*1a4050f5SDavid du Colombier 		if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
738*1a4050f5SDavid du Colombier 			regurgitate(tr, header, nconsumed);
739*1a4050f5SDavid du Colombier 			poperror();
740*1a4050f5SDavid du Colombier 		}else
741*1a4050f5SDavid du Colombier 			tlsError(tr, "channel error");
742*1a4050f5SDavid du Colombier 		nexterror();
743*1a4050f5SDavid du Colombier 	}
744*1a4050f5SDavid du Colombier 	ensure(tr, &tr->unprocessed, RecHdrLen);
745*1a4050f5SDavid du Colombier 	consume(&tr->unprocessed, header, RecHdrLen);
746*1a4050f5SDavid du Colombier if(tr->debug)pprint("consumed %d header\n", RecHdrLen);
747*1a4050f5SDavid du Colombier 	nconsumed = RecHdrLen;
748*1a4050f5SDavid du Colombier 
749*1a4050f5SDavid du Colombier 	if((tr->handin == 0) && (header[0] & 0x80)){
750*1a4050f5SDavid du Colombier 		/* Cope with an SSL3 ClientHello expressed in SSL2 record format.
751*1a4050f5SDavid du Colombier 			This is sent by some clients that we must interoperate
752*1a4050f5SDavid du Colombier 			with, such as Java's JSSE and Microsoft's Internet Explorer. */
753*1a4050f5SDavid du Colombier 		len = (get16(header) & ~0x8000) - 3;
754*1a4050f5SDavid du Colombier 		type = header[2];
755*1a4050f5SDavid du Colombier 		ver = get16(header + 3);
756*1a4050f5SDavid du Colombier 		if(type != SSL2ClientHello || len < 22)
757*1a4050f5SDavid du Colombier 			rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
758*1a4050f5SDavid du Colombier 	}else{  /* normal SSL3 record format */
759*1a4050f5SDavid du Colombier 		type = header[0];
760*1a4050f5SDavid du Colombier 		ver = get16(header+1);
761*1a4050f5SDavid du Colombier 		len = get16(header+3);
762*1a4050f5SDavid du Colombier 	}
763*1a4050f5SDavid du Colombier 	if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
764*1a4050f5SDavid du Colombier 		rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
765*1a4050f5SDavid du Colombier 			tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
766*1a4050f5SDavid du Colombier 	if(len > MaxCipherRecLen || len < 0)
767*1a4050f5SDavid du Colombier 		rcvError(tr, ERecordOverflow, "record message too long %d", len);
768*1a4050f5SDavid du Colombier 	ensure(tr, &tr->unprocessed, len);
769*1a4050f5SDavid du Colombier 	nconsumed = 0;
770*1a4050f5SDavid du Colombier 	poperror();
771*1a4050f5SDavid du Colombier 
772*1a4050f5SDavid du Colombier 	/*
773*1a4050f5SDavid du Colombier 	 * If an Eintr happens after this, we'll get out of sync.
774*1a4050f5SDavid du Colombier 	 * Make sure nothing we call can sleep.
775*1a4050f5SDavid du Colombier 	 * Errors are ok, as they kill the connection.
776*1a4050f5SDavid du Colombier 	 * Luckily, allocb won't sleep, it'll just error out.
777*1a4050f5SDavid du Colombier 	 */
778*1a4050f5SDavid du Colombier 	b = nil;
779*1a4050f5SDavid du Colombier 	if(waserror()){
780*1a4050f5SDavid du Colombier 		if(b != nil)
781*1a4050f5SDavid du Colombier 			freeb(b);
782*1a4050f5SDavid du Colombier 		tlsError(tr, "channel error");
783*1a4050f5SDavid du Colombier 		nexterror();
784*1a4050f5SDavid du Colombier 	}
785*1a4050f5SDavid du Colombier 	b = qgrab(&tr->unprocessed, len);
786*1a4050f5SDavid du Colombier if(tr->debug) pprint("consumed unprocessed %d\n", len);
787*1a4050f5SDavid du Colombier 
788*1a4050f5SDavid du Colombier 	in = &tr->in;
789*1a4050f5SDavid du Colombier 	if(waserror()){
790*1a4050f5SDavid du Colombier 		qunlock(&in->seclock);
791*1a4050f5SDavid du Colombier 		nexterror();
792*1a4050f5SDavid du Colombier 	}
793*1a4050f5SDavid du Colombier 	qlock(&in->seclock);
794*1a4050f5SDavid du Colombier 	p = b->rp;
795*1a4050f5SDavid du Colombier 	if(in->sec != nil) {
796*1a4050f5SDavid du Colombier 		/* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
797*1a4050f5SDavid du Colombier 		        should look alike, including timing of the response. */
798*1a4050f5SDavid du Colombier 		unpad_len = (*in->sec->dec)(in->sec, p, len);
799*1a4050f5SDavid du Colombier 		if(unpad_len >= in->sec->maclen)
800*1a4050f5SDavid du Colombier 			len = unpad_len - in->sec->maclen;
801*1a4050f5SDavid du Colombier if(tr->debug) pprint("decrypted %d\n", unpad_len);
802*1a4050f5SDavid du Colombier if(tr->debug) pdump(unpad_len, p, "decrypted:");
803*1a4050f5SDavid du Colombier 
804*1a4050f5SDavid du Colombier 		/* update length */
805*1a4050f5SDavid du Colombier 		put16(header+3, len);
806*1a4050f5SDavid du Colombier 		put64(seq, in->seq);
807*1a4050f5SDavid du Colombier 		in->seq++;
808*1a4050f5SDavid du Colombier 		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
809*1a4050f5SDavid du Colombier 		if(unpad_len < in->sec->maclen)
810*1a4050f5SDavid du Colombier 			rcvError(tr, EBadRecordMac, "short record mac");
811*1a4050f5SDavid du Colombier 		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
812*1a4050f5SDavid du Colombier 			rcvError(tr, EBadRecordMac, "record mac mismatch");
813*1a4050f5SDavid du Colombier 		b->wp = b->rp + len;
814*1a4050f5SDavid du Colombier 	}
815*1a4050f5SDavid du Colombier 	qunlock(&in->seclock);
816*1a4050f5SDavid du Colombier 	poperror();
817*1a4050f5SDavid du Colombier 	if(len < 0)
818*1a4050f5SDavid du Colombier 		rcvError(tr, EDecodeError, "runt record message");
819*1a4050f5SDavid du Colombier 
820*1a4050f5SDavid du Colombier 	switch(type) {
821*1a4050f5SDavid du Colombier 	default:
822*1a4050f5SDavid du Colombier 		rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
823*1a4050f5SDavid du Colombier 		break;
824*1a4050f5SDavid du Colombier 	case RChangeCipherSpec:
825*1a4050f5SDavid du Colombier 		if(len != 1 || p[0] != 1)
826*1a4050f5SDavid du Colombier 			rcvError(tr, EDecodeError, "invalid change cipher spec");
827*1a4050f5SDavid du Colombier 		qlock(&in->seclock);
828*1a4050f5SDavid du Colombier 		if(in->new == nil){
829*1a4050f5SDavid du Colombier 			qunlock(&in->seclock);
830*1a4050f5SDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
831*1a4050f5SDavid du Colombier 		}
832*1a4050f5SDavid du Colombier 		freeSec(in->sec);
833*1a4050f5SDavid du Colombier 		in->sec = in->new;
834*1a4050f5SDavid du Colombier 		in->new = nil;
835*1a4050f5SDavid du Colombier 		in->seq = 0;
836*1a4050f5SDavid du Colombier 		qunlock(&in->seclock);
837*1a4050f5SDavid du Colombier 		break;
838*1a4050f5SDavid du Colombier 	case RAlert:
839*1a4050f5SDavid du Colombier 		if(len != 2)
840*1a4050f5SDavid du Colombier 			rcvError(tr, EDecodeError, "invalid alert");
841*1a4050f5SDavid du Colombier 		if(p[0] == 2)
842*1a4050f5SDavid du Colombier 			rcvAlert(tr, p[1]);
843*1a4050f5SDavid du Colombier 		if(p[0] != 1)
844*1a4050f5SDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert fatal code");
845*1a4050f5SDavid du Colombier 
846*1a4050f5SDavid du Colombier 		/*
847*1a4050f5SDavid du Colombier 		 * propate non-fatal alerts to handshaker
848*1a4050f5SDavid du Colombier 		 */
849*1a4050f5SDavid du Colombier 		if(p[1] == ECloseNotify) {
850*1a4050f5SDavid du Colombier 			tlsclosed(tr, SRClose);
851*1a4050f5SDavid du Colombier 			if(tr->opened)
852*1a4050f5SDavid du Colombier 				error("tls hungup");
853*1a4050f5SDavid du Colombier 			error("close notify");
854*1a4050f5SDavid du Colombier 		}
855*1a4050f5SDavid du Colombier 		if(p[1] == ENoRenegotiation)
856*1a4050f5SDavid du Colombier 			alertHand(tr, "no renegotiation");
857*1a4050f5SDavid du Colombier 		else if(p[1] == EUserCanceled)
858*1a4050f5SDavid du Colombier 			alertHand(tr, "handshake canceled by user");
859*1a4050f5SDavid du Colombier 		else
860*1a4050f5SDavid du Colombier 			rcvError(tr, EIllegalParameter, "invalid alert code");
861*1a4050f5SDavid du Colombier 		break;
862*1a4050f5SDavid du Colombier 	case RHandshake:
863*1a4050f5SDavid du Colombier 		/*
864*1a4050f5SDavid du Colombier 		 * don't worry about dropping the block
865*1a4050f5SDavid du Colombier 		 * qbwrite always queues even if flow controlled and interrupted.
866*1a4050f5SDavid du Colombier 		 *
867*1a4050f5SDavid du Colombier 		 * if there isn't any handshaker, ignore the request,
868*1a4050f5SDavid du Colombier 		 * but notify the other side we are doing so.
869*1a4050f5SDavid du Colombier 		 */
870*1a4050f5SDavid du Colombier 		lock(&tr->hqlock);
871*1a4050f5SDavid du Colombier 		if(tr->handq != nil){
872*1a4050f5SDavid du Colombier 			tr->hqref++;
873*1a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
874*1a4050f5SDavid du Colombier 			if(waserror()){
875*1a4050f5SDavid du Colombier 				dechandq(tr);
876*1a4050f5SDavid du Colombier 				nexterror();
877*1a4050f5SDavid du Colombier 			}
878*1a4050f5SDavid du Colombier 			b = padblock(b, 1);
879*1a4050f5SDavid du Colombier 			*b->rp = RHandshake;
880*1a4050f5SDavid du Colombier 			qbwrite(tr->handq, b);
881*1a4050f5SDavid du Colombier 			b = nil;
882*1a4050f5SDavid du Colombier 			poperror();
883*1a4050f5SDavid du Colombier 			dechandq(tr);
884*1a4050f5SDavid du Colombier 		}else{
885*1a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
886*1a4050f5SDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
887*1a4050f5SDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
888*1a4050f5SDavid du Colombier 				poperror();
889*1a4050f5SDavid du Colombier 			}
890*1a4050f5SDavid du Colombier 		}
891*1a4050f5SDavid du Colombier 		break;
892*1a4050f5SDavid du Colombier 	case SSL2ClientHello:
893*1a4050f5SDavid du Colombier 		lock(&tr->hqlock);
894*1a4050f5SDavid du Colombier 		if(tr->handq != nil){
895*1a4050f5SDavid du Colombier 			tr->hqref++;
896*1a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
897*1a4050f5SDavid du Colombier 			if(waserror()){
898*1a4050f5SDavid du Colombier 				dechandq(tr);
899*1a4050f5SDavid du Colombier 				nexterror();
900*1a4050f5SDavid du Colombier 			}
901*1a4050f5SDavid du Colombier 			/* Pass the SSL2 format data, so that the handshake code can compute
902*1a4050f5SDavid du Colombier 				the correct checksums.  HSSL2ClientHello = HandshakeType 9 is
903*1a4050f5SDavid du Colombier 				unused in RFC2246. */
904*1a4050f5SDavid du Colombier 			b = padblock(b, 8);
905*1a4050f5SDavid du Colombier 			b->rp[0] = RHandshake;
906*1a4050f5SDavid du Colombier 			b->rp[1] = HSSL2ClientHello;
907*1a4050f5SDavid du Colombier 			put24(&b->rp[2], len+3);
908*1a4050f5SDavid du Colombier 			b->rp[5] = SSL2ClientHello;
909*1a4050f5SDavid du Colombier 			put16(&b->rp[6], ver);
910*1a4050f5SDavid du Colombier 			qbwrite(tr->handq, b);
911*1a4050f5SDavid du Colombier 			b = nil;
912*1a4050f5SDavid du Colombier 			poperror();
913*1a4050f5SDavid du Colombier 			dechandq(tr);
914*1a4050f5SDavid du Colombier 		}else{
915*1a4050f5SDavid du Colombier 			unlock(&tr->hqlock);
916*1a4050f5SDavid du Colombier 			if(tr->verset && tr->version != SSL3Version && !waserror()){
917*1a4050f5SDavid du Colombier 				sendAlert(tr, ENoRenegotiation);
918*1a4050f5SDavid du Colombier 				poperror();
919*1a4050f5SDavid du Colombier 			}
920*1a4050f5SDavid du Colombier 		}
921*1a4050f5SDavid du Colombier 		break;
922*1a4050f5SDavid du Colombier 	case RApplication:
923*1a4050f5SDavid du Colombier 		if(!tr->opened)
924*1a4050f5SDavid du Colombier 			rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
925*1a4050f5SDavid du Colombier 		if(BLEN(b) > 0){
926*1a4050f5SDavid du Colombier 			tr->processed = b;
927*1a4050f5SDavid du Colombier 			b = nil;
928*1a4050f5SDavid du Colombier 		}
929*1a4050f5SDavid du Colombier 		break;
930*1a4050f5SDavid du Colombier 	}
931*1a4050f5SDavid du Colombier 	if(b != nil)
932*1a4050f5SDavid du Colombier 		freeb(b);
933*1a4050f5SDavid du Colombier 	poperror();
934*1a4050f5SDavid du Colombier }
935*1a4050f5SDavid du Colombier 
936*1a4050f5SDavid du Colombier /*
937*1a4050f5SDavid du Colombier  * got a fatal alert message
938*1a4050f5SDavid du Colombier  */
939*1a4050f5SDavid du Colombier static void
940*1a4050f5SDavid du Colombier rcvAlert(TlsRec *tr, int err)
941*1a4050f5SDavid du Colombier {
942*1a4050f5SDavid du Colombier 	char *s;
943*1a4050f5SDavid du Colombier 	int i;
944*1a4050f5SDavid du Colombier 
945*1a4050f5SDavid du Colombier 	s = "unknown error";
946*1a4050f5SDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++){
947*1a4050f5SDavid du Colombier 		if(tlserrs[i].err == err){
948*1a4050f5SDavid du Colombier 			s = tlserrs[i].msg;
949*1a4050f5SDavid du Colombier 			break;
950*1a4050f5SDavid du Colombier 		}
951*1a4050f5SDavid du Colombier 	}
952*1a4050f5SDavid du Colombier if(tr->debug) pprint("rcvAlert: %s\n", s);
953*1a4050f5SDavid du Colombier 
954*1a4050f5SDavid du Colombier 	tlsError(tr, s);
955*1a4050f5SDavid du Colombier 	if(!tr->opened)
956*1a4050f5SDavid du Colombier 		error(s);
957*1a4050f5SDavid du Colombier 	error("tls error");
958*1a4050f5SDavid du Colombier }
959*1a4050f5SDavid du Colombier 
960*1a4050f5SDavid du Colombier /*
961*1a4050f5SDavid du Colombier  * found an error while decoding the input stream
962*1a4050f5SDavid du Colombier  */
963*1a4050f5SDavid du Colombier static void
964*1a4050f5SDavid du Colombier rcvError(TlsRec *tr, int err, char *fmt, ...)
965*1a4050f5SDavid du Colombier {
966*1a4050f5SDavid du Colombier 	char msg[ERRMAX];
967*1a4050f5SDavid du Colombier 	va_list arg;
968*1a4050f5SDavid du Colombier 
969*1a4050f5SDavid du Colombier 	va_start(arg, fmt);
970*1a4050f5SDavid du Colombier 	vseprint(msg, msg+sizeof(msg), fmt, arg);
971*1a4050f5SDavid du Colombier 	va_end(arg);
972*1a4050f5SDavid du Colombier if(tr->debug) pprint("rcvError: %s\n", msg);
973*1a4050f5SDavid du Colombier 
974*1a4050f5SDavid du Colombier 	sendAlert(tr, err);
975*1a4050f5SDavid du Colombier 
976*1a4050f5SDavid du Colombier 	if(!tr->opened)
977*1a4050f5SDavid du Colombier 		error(msg);
978*1a4050f5SDavid du Colombier 	error("tls error");
979*1a4050f5SDavid du Colombier }
980*1a4050f5SDavid du Colombier 
981*1a4050f5SDavid du Colombier /*
982*1a4050f5SDavid du Colombier  * make sure the next hand operation returns with a 'msg' error
983*1a4050f5SDavid du Colombier  */
984*1a4050f5SDavid du Colombier static void
985*1a4050f5SDavid du Colombier alertHand(TlsRec *tr, char *msg)
986*1a4050f5SDavid du Colombier {
987*1a4050f5SDavid du Colombier 	Block *b;
988*1a4050f5SDavid du Colombier 	int n;
989*1a4050f5SDavid du Colombier 
990*1a4050f5SDavid du Colombier 	lock(&tr->hqlock);
991*1a4050f5SDavid du Colombier 	if(tr->handq == nil){
992*1a4050f5SDavid du Colombier 		unlock(&tr->hqlock);
993*1a4050f5SDavid du Colombier 		return;
994*1a4050f5SDavid du Colombier 	}
995*1a4050f5SDavid du Colombier 	tr->hqref++;
996*1a4050f5SDavid du Colombier 	unlock(&tr->hqlock);
997*1a4050f5SDavid du Colombier 
998*1a4050f5SDavid du Colombier 	n = strlen(msg);
999*1a4050f5SDavid du Colombier 	if(waserror()){
1000*1a4050f5SDavid du Colombier 		dechandq(tr);
1001*1a4050f5SDavid du Colombier 		nexterror();
1002*1a4050f5SDavid du Colombier 	}
1003*1a4050f5SDavid du Colombier 	b = allocb(n + 2);
1004*1a4050f5SDavid du Colombier 	*b->wp++ = RAlert;
1005*1a4050f5SDavid du Colombier 	memmove(b->wp, msg, n + 1);
1006*1a4050f5SDavid du Colombier 	b->wp += n + 1;
1007*1a4050f5SDavid du Colombier 
1008*1a4050f5SDavid du Colombier 	qbwrite(tr->handq, b);
1009*1a4050f5SDavid du Colombier 
1010*1a4050f5SDavid du Colombier 	poperror();
1011*1a4050f5SDavid du Colombier 	dechandq(tr);
1012*1a4050f5SDavid du Colombier }
1013*1a4050f5SDavid du Colombier 
1014*1a4050f5SDavid du Colombier static void
1015*1a4050f5SDavid du Colombier checkstate(TlsRec *tr, int ishand, int ok)
1016*1a4050f5SDavid du Colombier {
1017*1a4050f5SDavid du Colombier 	int state;
1018*1a4050f5SDavid du Colombier 
1019*1a4050f5SDavid du Colombier 	lock(&tr->statelk);
1020*1a4050f5SDavid du Colombier 	state = tr->state;
1021*1a4050f5SDavid du Colombier 	unlock(&tr->statelk);
1022*1a4050f5SDavid du Colombier 	if(state & ok)
1023*1a4050f5SDavid du Colombier 		return;
1024*1a4050f5SDavid du Colombier 	switch(state){
1025*1a4050f5SDavid du Colombier 	case SHandshake:
1026*1a4050f5SDavid du Colombier 	case SOpen:
1027*1a4050f5SDavid du Colombier 		break;
1028*1a4050f5SDavid du Colombier 	case SError:
1029*1a4050f5SDavid du Colombier 	case SAlert:
1030*1a4050f5SDavid du Colombier 		if(ishand)
1031*1a4050f5SDavid du Colombier 			error(tr->err);
1032*1a4050f5SDavid du Colombier 		error("tls error");
1033*1a4050f5SDavid du Colombier 	case SRClose:
1034*1a4050f5SDavid du Colombier 	case SLClose:
1035*1a4050f5SDavid du Colombier 	case SClosed:
1036*1a4050f5SDavid du Colombier 		error("tls hungup");
1037*1a4050f5SDavid du Colombier 	}
1038*1a4050f5SDavid du Colombier 	error("tls improperly configured");
1039*1a4050f5SDavid du Colombier }
1040*1a4050f5SDavid du Colombier 
1041*1a4050f5SDavid du Colombier static Block*
1042*1a4050f5SDavid du Colombier tlsbread(Chan *c, long n, ulong offset)
1043*1a4050f5SDavid du Colombier {
1044*1a4050f5SDavid du Colombier 	int ty;
1045*1a4050f5SDavid du Colombier 	Block *b;
1046*1a4050f5SDavid du Colombier 	TlsRec *volatile tr;
1047*1a4050f5SDavid du Colombier 
1048*1a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
1049*1a4050f5SDavid du Colombier 	switch(ty) {
1050*1a4050f5SDavid du Colombier 	default:
1051*1a4050f5SDavid du Colombier 		return devbread(c, n, offset);
1052*1a4050f5SDavid du Colombier 	case Qhand:
1053*1a4050f5SDavid du Colombier 	case Qdata:
1054*1a4050f5SDavid du Colombier 		break;
1055*1a4050f5SDavid du Colombier 	}
1056*1a4050f5SDavid du Colombier 
1057*1a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
1058*1a4050f5SDavid du Colombier 	if(tr == nil)
1059*1a4050f5SDavid du Colombier 		panic("tlsbread");
1060*1a4050f5SDavid du Colombier 
1061*1a4050f5SDavid du Colombier 	if(waserror()){
1062*1a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
1063*1a4050f5SDavid du Colombier 		nexterror();
1064*1a4050f5SDavid du Colombier 	}
1065*1a4050f5SDavid du Colombier 	qlock(&tr->in.io);
1066*1a4050f5SDavid du Colombier 	if(ty == Qdata){
1067*1a4050f5SDavid du Colombier 		checkstate(tr, 0, SOpen);
1068*1a4050f5SDavid du Colombier 		while(tr->processed == nil)
1069*1a4050f5SDavid du Colombier 			tlsrecread(tr);
1070*1a4050f5SDavid du Colombier 
1071*1a4050f5SDavid du Colombier 		/* return at most what was asked for */
1072*1a4050f5SDavid du Colombier 		b = qgrab(&tr->processed, n);
1073*1a4050f5SDavid du Colombier if(tr->debug) pprint("consumed processed %d\n", BLEN(b));
1074*1a4050f5SDavid du Colombier if(tr->debug) pdump(BLEN(b), b->rp, "consumed:");
1075*1a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
1076*1a4050f5SDavid du Colombier 		poperror();
1077*1a4050f5SDavid du Colombier 		tr->datain += BLEN(b);
1078*1a4050f5SDavid du Colombier 	}else{
1079*1a4050f5SDavid du Colombier 		checkstate(tr, 1, SOpen|SHandshake|SLClose);
1080*1a4050f5SDavid du Colombier 
1081*1a4050f5SDavid du Colombier 		/*
1082*1a4050f5SDavid du Colombier 		 * it's ok to look at state without the lock
1083*1a4050f5SDavid du Colombier 		 * since it only protects reading records,
1084*1a4050f5SDavid du Colombier 		 * and we have that tr->in.io held.
1085*1a4050f5SDavid du Colombier 		 */
1086*1a4050f5SDavid du Colombier 		while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
1087*1a4050f5SDavid du Colombier 			tlsrecread(tr);
1088*1a4050f5SDavid du Colombier 
1089*1a4050f5SDavid du Colombier 		qunlock(&tr->in.io);
1090*1a4050f5SDavid du Colombier 		poperror();
1091*1a4050f5SDavid du Colombier 
1092*1a4050f5SDavid du Colombier 		if(waserror()){
1093*1a4050f5SDavid du Colombier 			qunlock(&tr->hqread);
1094*1a4050f5SDavid du Colombier 			nexterror();
1095*1a4050f5SDavid du Colombier 		}
1096*1a4050f5SDavid du Colombier 		qlock(&tr->hqread);
1097*1a4050f5SDavid du Colombier 		if(tr->hprocessed == nil){
1098*1a4050f5SDavid du Colombier 			b = qbread(tr->handq, MaxRecLen + 1);
1099*1a4050f5SDavid du Colombier 			if(*b->rp++ == RAlert){
1100*1a4050f5SDavid du Colombier 				kstrcpy(up->errstr, (char*)b->rp, ERRMAX);
1101*1a4050f5SDavid du Colombier 				freeb(b);
1102*1a4050f5SDavid du Colombier 				nexterror();
1103*1a4050f5SDavid du Colombier 			}
1104*1a4050f5SDavid du Colombier 			tr->hprocessed = b;
1105*1a4050f5SDavid du Colombier 		}
1106*1a4050f5SDavid du Colombier 		b = qgrab(&tr->hprocessed, n);
1107*1a4050f5SDavid du Colombier 		poperror();
1108*1a4050f5SDavid du Colombier 		qunlock(&tr->hqread);
1109*1a4050f5SDavid du Colombier 		tr->handin += BLEN(b);
1110*1a4050f5SDavid du Colombier 	}
1111*1a4050f5SDavid du Colombier 
1112*1a4050f5SDavid du Colombier 	return b;
1113*1a4050f5SDavid du Colombier }
1114*1a4050f5SDavid du Colombier 
1115*1a4050f5SDavid du Colombier static long
1116*1a4050f5SDavid du Colombier tlsread(Chan *c, void *a, long n, vlong off)
1117*1a4050f5SDavid du Colombier {
1118*1a4050f5SDavid du Colombier 	Block *volatile b;
1119*1a4050f5SDavid du Colombier 	Block *nb;
1120*1a4050f5SDavid du Colombier 	uchar *va;
1121*1a4050f5SDavid du Colombier 	int i, ty;
1122*1a4050f5SDavid du Colombier 	char *buf, *s, *e;
1123*1a4050f5SDavid du Colombier 	ulong offset = off;
1124*1a4050f5SDavid du Colombier 	TlsRec * tr;
1125*1a4050f5SDavid du Colombier 
1126*1a4050f5SDavid du Colombier 	if(c->qid.type & QTDIR)
1127*1a4050f5SDavid du Colombier 		return devdirread(c, a, n, 0, 0, tlsgen);
1128*1a4050f5SDavid du Colombier 
1129*1a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
1130*1a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
1131*1a4050f5SDavid du Colombier 	switch(ty) {
1132*1a4050f5SDavid du Colombier 	default:
1133*1a4050f5SDavid du Colombier 		error(Ebadusefd);
1134*1a4050f5SDavid du Colombier 	case Qstatus:
1135*1a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
1136*1a4050f5SDavid du Colombier 		qlock(&tr->in.seclock);
1137*1a4050f5SDavid du Colombier 		qlock(&tr->out.seclock);
1138*1a4050f5SDavid du Colombier 		s = buf;
1139*1a4050f5SDavid du Colombier 		e = buf + Statlen;
1140*1a4050f5SDavid du Colombier 		s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
1141*1a4050f5SDavid du Colombier 		s = seprint(s, e, "Version: 0x%x\n", tr->version);
1142*1a4050f5SDavid du Colombier 		if(tr->in.sec != nil)
1143*1a4050f5SDavid du Colombier 			s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
1144*1a4050f5SDavid du Colombier 		if(tr->in.new != nil)
1145*1a4050f5SDavid du Colombier 			s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
1146*1a4050f5SDavid du Colombier 		if(tr->out.sec != nil)
1147*1a4050f5SDavid du Colombier 			s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
1148*1a4050f5SDavid du Colombier 		if(tr->out.new != nil)
1149*1a4050f5SDavid du Colombier 			seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
1150*1a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
1151*1a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
1152*1a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
1153*1a4050f5SDavid du Colombier 		free(buf);
1154*1a4050f5SDavid du Colombier 		return n;
1155*1a4050f5SDavid du Colombier 	case Qstats:
1156*1a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
1157*1a4050f5SDavid du Colombier 		s = buf;
1158*1a4050f5SDavid du Colombier 		e = buf + Statlen;
1159*1a4050f5SDavid du Colombier 		s = seprint(s, e, "DataIn: %lld\n", tr->datain);
1160*1a4050f5SDavid du Colombier 		s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
1161*1a4050f5SDavid du Colombier 		s = seprint(s, e, "HandIn: %lld\n", tr->handin);
1162*1a4050f5SDavid du Colombier 		seprint(s, e, "HandOut: %lld\n", tr->handout);
1163*1a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
1164*1a4050f5SDavid du Colombier 		free(buf);
1165*1a4050f5SDavid du Colombier 		return n;
1166*1a4050f5SDavid du Colombier 	case Qctl:
1167*1a4050f5SDavid du Colombier 		buf = smalloc(Statlen);
1168*1a4050f5SDavid du Colombier 		snprint(buf, Statlen, "%llud", CONV(c->qid));
1169*1a4050f5SDavid du Colombier 		n = readstr(offset, a, n, buf);
1170*1a4050f5SDavid du Colombier 		free(buf);
1171*1a4050f5SDavid du Colombier 		return n;
1172*1a4050f5SDavid du Colombier 	case Qdata:
1173*1a4050f5SDavid du Colombier 	case Qhand:
1174*1a4050f5SDavid du Colombier 		b = tlsbread(c, n, offset);
1175*1a4050f5SDavid du Colombier 		break;
1176*1a4050f5SDavid du Colombier 	case Qencalgs:
1177*1a4050f5SDavid du Colombier 		return readstr(offset, a, n, encalgs);
1178*1a4050f5SDavid du Colombier 	case Qhashalgs:
1179*1a4050f5SDavid du Colombier 		return readstr(offset, a, n, hashalgs);
1180*1a4050f5SDavid du Colombier 	}
1181*1a4050f5SDavid du Colombier 
1182*1a4050f5SDavid du Colombier 	if(waserror()){
1183*1a4050f5SDavid du Colombier 		freeblist(b);
1184*1a4050f5SDavid du Colombier 		nexterror();
1185*1a4050f5SDavid du Colombier 	}
1186*1a4050f5SDavid du Colombier 
1187*1a4050f5SDavid du Colombier 	n = 0;
1188*1a4050f5SDavid du Colombier 	va = a;
1189*1a4050f5SDavid du Colombier 	for(nb = b; nb; nb = nb->next){
1190*1a4050f5SDavid du Colombier 		i = BLEN(nb);
1191*1a4050f5SDavid du Colombier 		memmove(va+n, nb->rp, i);
1192*1a4050f5SDavid du Colombier 		n += i;
1193*1a4050f5SDavid du Colombier 	}
1194*1a4050f5SDavid du Colombier 
1195*1a4050f5SDavid du Colombier 	freeblist(b);
1196*1a4050f5SDavid du Colombier 	poperror();
1197*1a4050f5SDavid du Colombier 
1198*1a4050f5SDavid du Colombier 	return n;
1199*1a4050f5SDavid du Colombier }
1200*1a4050f5SDavid du Colombier 
1201*1a4050f5SDavid du Colombier /*
1202*1a4050f5SDavid du Colombier  *  write a block in tls records
1203*1a4050f5SDavid du Colombier  */
1204*1a4050f5SDavid du Colombier static void
1205*1a4050f5SDavid du Colombier tlsrecwrite(TlsRec *tr, int type, Block *b)
1206*1a4050f5SDavid du Colombier {
1207*1a4050f5SDavid du Colombier 	Block *volatile bb;
1208*1a4050f5SDavid du Colombier 	Block *nb;
1209*1a4050f5SDavid du Colombier 	uchar *p, seq[8];
1210*1a4050f5SDavid du Colombier 	OneWay *volatile out;
1211*1a4050f5SDavid du Colombier 	int n, maclen, pad, ok;
1212*1a4050f5SDavid du Colombier 
1213*1a4050f5SDavid du Colombier 	out = &tr->out;
1214*1a4050f5SDavid du Colombier 	bb = b;
1215*1a4050f5SDavid du Colombier 	if(waserror()){
1216*1a4050f5SDavid du Colombier 		qunlock(&out->io);
1217*1a4050f5SDavid du Colombier 		if(bb != nil)
1218*1a4050f5SDavid du Colombier 			freeb(bb);
1219*1a4050f5SDavid du Colombier 		nexterror();
1220*1a4050f5SDavid du Colombier 	}
1221*1a4050f5SDavid du Colombier 	qlock(&out->io);
1222*1a4050f5SDavid du Colombier if(tr->debug)pprint("send %d\n", BLEN(b));
1223*1a4050f5SDavid du Colombier if(tr->debug)pdump(BLEN(b), b->rp, "sent:");
1224*1a4050f5SDavid du Colombier 
1225*1a4050f5SDavid du Colombier 
1226*1a4050f5SDavid du Colombier 	ok = SHandshake|SOpen|SRClose;
1227*1a4050f5SDavid du Colombier 	if(type == RAlert)
1228*1a4050f5SDavid du Colombier 		ok |= SAlert;
1229*1a4050f5SDavid du Colombier 	while(bb != nil){
1230*1a4050f5SDavid du Colombier 		checkstate(tr, type != RApplication, ok);
1231*1a4050f5SDavid du Colombier 
1232*1a4050f5SDavid du Colombier 		/*
1233*1a4050f5SDavid du Colombier 		 * get at most one maximal record's input,
1234*1a4050f5SDavid du Colombier 		 * with padding on the front for header and
1235*1a4050f5SDavid du Colombier 		 * back for mac and maximal block padding.
1236*1a4050f5SDavid du Colombier 		 */
1237*1a4050f5SDavid du Colombier 		if(waserror()){
1238*1a4050f5SDavid du Colombier 			qunlock(&out->seclock);
1239*1a4050f5SDavid du Colombier 			nexterror();
1240*1a4050f5SDavid du Colombier 		}
1241*1a4050f5SDavid du Colombier 		qlock(&out->seclock);
1242*1a4050f5SDavid du Colombier 		maclen = 0;
1243*1a4050f5SDavid du Colombier 		pad = 0;
1244*1a4050f5SDavid du Colombier 		if(out->sec != nil){
1245*1a4050f5SDavid du Colombier 			maclen = out->sec->maclen;
1246*1a4050f5SDavid du Colombier 			pad = maclen + out->sec->block;
1247*1a4050f5SDavid du Colombier 		}
1248*1a4050f5SDavid du Colombier 		n = BLEN(bb);
1249*1a4050f5SDavid du Colombier 		if(n > MaxRecLen){
1250*1a4050f5SDavid du Colombier 			n = MaxRecLen;
1251*1a4050f5SDavid du Colombier 			nb = allocb(n + pad + RecHdrLen);
1252*1a4050f5SDavid du Colombier 			memmove(nb->wp + RecHdrLen, bb->rp, n);
1253*1a4050f5SDavid du Colombier 			bb->rp += n;
1254*1a4050f5SDavid du Colombier 		}else{
1255*1a4050f5SDavid du Colombier 			/*
1256*1a4050f5SDavid du Colombier 			 * carefully reuse bb so it will get freed if we're out of memory
1257*1a4050f5SDavid du Colombier 			 */
1258*1a4050f5SDavid du Colombier 			bb = padblock(bb, RecHdrLen);
1259*1a4050f5SDavid du Colombier 			if(pad)
1260*1a4050f5SDavid du Colombier 				nb = padblock(bb, -pad);
1261*1a4050f5SDavid du Colombier 			else
1262*1a4050f5SDavid du Colombier 				nb = bb;
1263*1a4050f5SDavid du Colombier 			bb = nil;
1264*1a4050f5SDavid du Colombier 		}
1265*1a4050f5SDavid du Colombier 
1266*1a4050f5SDavid du Colombier 		p = nb->rp;
1267*1a4050f5SDavid du Colombier 		p[0] = type;
1268*1a4050f5SDavid du Colombier 		put16(p+1, tr->version);
1269*1a4050f5SDavid du Colombier 		put16(p+3, n);
1270*1a4050f5SDavid du Colombier 
1271*1a4050f5SDavid du Colombier 		if(out->sec != nil){
1272*1a4050f5SDavid du Colombier 			put64(seq, out->seq);
1273*1a4050f5SDavid du Colombier 			out->seq++;
1274*1a4050f5SDavid du Colombier 			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
1275*1a4050f5SDavid du Colombier 			n += maclen;
1276*1a4050f5SDavid du Colombier 
1277*1a4050f5SDavid du Colombier 			/* encrypt */
1278*1a4050f5SDavid du Colombier 			n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
1279*1a4050f5SDavid du Colombier 			nb->wp = p + RecHdrLen + n;
1280*1a4050f5SDavid du Colombier 
1281*1a4050f5SDavid du Colombier 			/* update length */
1282*1a4050f5SDavid du Colombier 			put16(p+3, n);
1283*1a4050f5SDavid du Colombier 		}
1284*1a4050f5SDavid du Colombier 		if(type == RChangeCipherSpec){
1285*1a4050f5SDavid du Colombier 			if(out->new == nil)
1286*1a4050f5SDavid du Colombier 				error("change cipher without a new cipher");
1287*1a4050f5SDavid du Colombier 			freeSec(out->sec);
1288*1a4050f5SDavid du Colombier 			out->sec = out->new;
1289*1a4050f5SDavid du Colombier 			out->new = nil;
1290*1a4050f5SDavid du Colombier 			out->seq = 0;
1291*1a4050f5SDavid du Colombier 		}
1292*1a4050f5SDavid du Colombier 		qunlock(&out->seclock);
1293*1a4050f5SDavid du Colombier 		poperror();
1294*1a4050f5SDavid du Colombier 
1295*1a4050f5SDavid du Colombier 		/*
1296*1a4050f5SDavid du Colombier 		 * if bwrite error's, we assume the block is queued.
1297*1a4050f5SDavid du Colombier 		 * if not, we're out of sync with the receiver and will not recover.
1298*1a4050f5SDavid du Colombier 		 */
1299*1a4050f5SDavid du Colombier 		if(waserror()){
1300*1a4050f5SDavid du Colombier 			if(strcmp(up->errstr, "interrupted") != 0)
1301*1a4050f5SDavid du Colombier 				tlsError(tr, "channel error");
1302*1a4050f5SDavid du Colombier 			nexterror();
1303*1a4050f5SDavid du Colombier 		}
1304*1a4050f5SDavid du Colombier 		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
1305*1a4050f5SDavid du Colombier 		poperror();
1306*1a4050f5SDavid du Colombier 	}
1307*1a4050f5SDavid du Colombier 	qunlock(&out->io);
1308*1a4050f5SDavid du Colombier 	poperror();
1309*1a4050f5SDavid du Colombier }
1310*1a4050f5SDavid du Colombier 
1311*1a4050f5SDavid du Colombier static long
1312*1a4050f5SDavid du Colombier tlsbwrite(Chan *c, Block *b, ulong offset)
1313*1a4050f5SDavid du Colombier {
1314*1a4050f5SDavid du Colombier 	int ty;
1315*1a4050f5SDavid du Colombier 	ulong n;
1316*1a4050f5SDavid du Colombier 	TlsRec *tr;
1317*1a4050f5SDavid du Colombier 
1318*1a4050f5SDavid du Colombier 	n = BLEN(b);
1319*1a4050f5SDavid du Colombier 
1320*1a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
1321*1a4050f5SDavid du Colombier 	if(tr == nil)
1322*1a4050f5SDavid du Colombier 		panic("tlsbread");
1323*1a4050f5SDavid du Colombier 
1324*1a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
1325*1a4050f5SDavid du Colombier 	switch(ty) {
1326*1a4050f5SDavid du Colombier 	default:
1327*1a4050f5SDavid du Colombier 		return devbwrite(c, b, offset);
1328*1a4050f5SDavid du Colombier 	case Qhand:
1329*1a4050f5SDavid du Colombier 		tlsrecwrite(tr, RHandshake, b);
1330*1a4050f5SDavid du Colombier 		tr->handout += n;
1331*1a4050f5SDavid du Colombier 		break;
1332*1a4050f5SDavid du Colombier 	case Qdata:
1333*1a4050f5SDavid du Colombier 		checkstate(tr, 0, SOpen);
1334*1a4050f5SDavid du Colombier 		tlsrecwrite(tr, RApplication, b);
1335*1a4050f5SDavid du Colombier 		tr->dataout += n;
1336*1a4050f5SDavid du Colombier 		break;
1337*1a4050f5SDavid du Colombier 	}
1338*1a4050f5SDavid du Colombier 
1339*1a4050f5SDavid du Colombier 	return n;
1340*1a4050f5SDavid du Colombier }
1341*1a4050f5SDavid du Colombier 
1342*1a4050f5SDavid du Colombier typedef struct Hashalg Hashalg;
1343*1a4050f5SDavid du Colombier struct Hashalg
1344*1a4050f5SDavid du Colombier {
1345*1a4050f5SDavid du Colombier 	char	*name;
1346*1a4050f5SDavid du Colombier 	int	maclen;
1347*1a4050f5SDavid du Colombier 	void	(*initkey)(Hashalg *, int, Secret *, uchar*);
1348*1a4050f5SDavid du Colombier };
1349*1a4050f5SDavid du Colombier 
1350*1a4050f5SDavid du Colombier static void
1351*1a4050f5SDavid du Colombier initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
1352*1a4050f5SDavid du Colombier {
1353*1a4050f5SDavid du Colombier 	s->maclen = ha->maclen;
1354*1a4050f5SDavid du Colombier 	if(version == SSL3Version)
1355*1a4050f5SDavid du Colombier 		s->mac = sslmac_md5;
1356*1a4050f5SDavid du Colombier 	else
1357*1a4050f5SDavid du Colombier 		s->mac = hmac_md5;
1358*1a4050f5SDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
1359*1a4050f5SDavid du Colombier }
1360*1a4050f5SDavid du Colombier 
1361*1a4050f5SDavid du Colombier static void
1362*1a4050f5SDavid du Colombier initclearmac(Hashalg *unused1, int unused2, Secret *s, uchar *unused3)
1363*1a4050f5SDavid du Colombier {
1364*1a4050f5SDavid du Colombier 	s->maclen = 0;
1365*1a4050f5SDavid du Colombier 	s->mac = nomac;
1366*1a4050f5SDavid du Colombier }
1367*1a4050f5SDavid du Colombier 
1368*1a4050f5SDavid du Colombier static void
1369*1a4050f5SDavid du Colombier initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
1370*1a4050f5SDavid du Colombier {
1371*1a4050f5SDavid du Colombier 	s->maclen = ha->maclen;
1372*1a4050f5SDavid du Colombier 	if(version == SSL3Version)
1373*1a4050f5SDavid du Colombier 		s->mac = sslmac_sha1;
1374*1a4050f5SDavid du Colombier 	else
1375*1a4050f5SDavid du Colombier 		s->mac = hmac_sha1;
1376*1a4050f5SDavid du Colombier 	memmove(s->mackey, p, ha->maclen);
1377*1a4050f5SDavid du Colombier }
1378*1a4050f5SDavid du Colombier 
1379*1a4050f5SDavid du Colombier static Hashalg hashtab[] =
1380*1a4050f5SDavid du Colombier {
1381*1a4050f5SDavid du Colombier 	{ "clear", 0, initclearmac, },
1382*1a4050f5SDavid du Colombier 	{ "md5", MD5dlen, initmd5key, },
1383*1a4050f5SDavid du Colombier 	{ "sha1", SHA1dlen, initsha1key, },
1384*1a4050f5SDavid du Colombier 	{ 0 }
1385*1a4050f5SDavid du Colombier };
1386*1a4050f5SDavid du Colombier 
1387*1a4050f5SDavid du Colombier static Hashalg*
1388*1a4050f5SDavid du Colombier parsehashalg(char *p)
1389*1a4050f5SDavid du Colombier {
1390*1a4050f5SDavid du Colombier 	Hashalg *ha;
1391*1a4050f5SDavid du Colombier 
1392*1a4050f5SDavid du Colombier 	for(ha = hashtab; ha->name; ha++)
1393*1a4050f5SDavid du Colombier 		if(strcmp(p, ha->name) == 0)
1394*1a4050f5SDavid du Colombier 			return ha;
1395*1a4050f5SDavid du Colombier 	error("unsupported hash algorithm");
1396*1a4050f5SDavid du Colombier 	return nil;
1397*1a4050f5SDavid du Colombier }
1398*1a4050f5SDavid du Colombier 
1399*1a4050f5SDavid du Colombier typedef struct Encalg Encalg;
1400*1a4050f5SDavid du Colombier struct Encalg
1401*1a4050f5SDavid du Colombier {
1402*1a4050f5SDavid du Colombier 	char	*name;
1403*1a4050f5SDavid du Colombier 	int	keylen;
1404*1a4050f5SDavid du Colombier 	int	ivlen;
1405*1a4050f5SDavid du Colombier 	void	(*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
1406*1a4050f5SDavid du Colombier };
1407*1a4050f5SDavid du Colombier 
1408*1a4050f5SDavid du Colombier static void
1409*1a4050f5SDavid du Colombier initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *unused1)
1410*1a4050f5SDavid du Colombier {
1411*1a4050f5SDavid du Colombier 	s->enckey = smalloc(sizeof(RC4state));
1412*1a4050f5SDavid du Colombier 	s->enc = rc4enc;
1413*1a4050f5SDavid du Colombier 	s->dec = rc4enc;
1414*1a4050f5SDavid du Colombier 	s->block = 0;
1415*1a4050f5SDavid du Colombier 	setupRC4state(s->enckey, p, ea->keylen);
1416*1a4050f5SDavid du Colombier }
1417*1a4050f5SDavid du Colombier 
1418*1a4050f5SDavid du Colombier static void
1419*1a4050f5SDavid du Colombier initDES3key(Encalg *unused1, Secret *s, uchar *p, uchar *iv)
1420*1a4050f5SDavid du Colombier {
1421*1a4050f5SDavid du Colombier 	s->enckey = smalloc(sizeof(DES3state));
1422*1a4050f5SDavid du Colombier 	s->enc = des3enc;
1423*1a4050f5SDavid du Colombier 	s->dec = des3dec;
1424*1a4050f5SDavid du Colombier 	s->block = 8;
1425*1a4050f5SDavid du Colombier 	setupDES3state(s->enckey, (uchar(*)[8])p, iv);
1426*1a4050f5SDavid du Colombier }
1427*1a4050f5SDavid du Colombier 
1428*1a4050f5SDavid du Colombier static void
1429*1a4050f5SDavid du Colombier initclearenc(Encalg *unused1, Secret *s, uchar *unused2, uchar *unused3)
1430*1a4050f5SDavid du Colombier {
1431*1a4050f5SDavid du Colombier 	s->enc = noenc;
1432*1a4050f5SDavid du Colombier 	s->dec = noenc;
1433*1a4050f5SDavid du Colombier 	s->block = 0;
1434*1a4050f5SDavid du Colombier }
1435*1a4050f5SDavid du Colombier 
1436*1a4050f5SDavid du Colombier static Encalg encrypttab[] =
1437*1a4050f5SDavid du Colombier {
1438*1a4050f5SDavid du Colombier 	{ "clear", 0, 0, initclearenc },
1439*1a4050f5SDavid du Colombier 	{ "rc4_128", 128/8, 0, initRC4key },
1440*1a4050f5SDavid du Colombier 	{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
1441*1a4050f5SDavid du Colombier 	{ 0 }
1442*1a4050f5SDavid du Colombier };
1443*1a4050f5SDavid du Colombier 
1444*1a4050f5SDavid du Colombier static Encalg*
1445*1a4050f5SDavid du Colombier parseencalg(char *p)
1446*1a4050f5SDavid du Colombier {
1447*1a4050f5SDavid du Colombier 	Encalg *ea;
1448*1a4050f5SDavid du Colombier 
1449*1a4050f5SDavid du Colombier 	for(ea = encrypttab; ea->name; ea++)
1450*1a4050f5SDavid du Colombier 		if(strcmp(p, ea->name) == 0)
1451*1a4050f5SDavid du Colombier 			return ea;
1452*1a4050f5SDavid du Colombier 	error("unsupported encryption algorithm");
1453*1a4050f5SDavid du Colombier 	return nil;
1454*1a4050f5SDavid du Colombier }
1455*1a4050f5SDavid du Colombier 
1456*1a4050f5SDavid du Colombier static long
1457*1a4050f5SDavid du Colombier tlswrite(Chan *c, void *a, long n, vlong off)
1458*1a4050f5SDavid du Colombier {
1459*1a4050f5SDavid du Colombier 	Encalg *ea;
1460*1a4050f5SDavid du Colombier 	Hashalg *ha;
1461*1a4050f5SDavid du Colombier 	TlsRec *volatile tr;
1462*1a4050f5SDavid du Colombier 	Secret *volatile tos, *volatile toc;
1463*1a4050f5SDavid du Colombier 	Block *volatile b;
1464*1a4050f5SDavid du Colombier 	Cmdbuf *volatile cb;
1465*1a4050f5SDavid du Colombier 	int m, ty;
1466*1a4050f5SDavid du Colombier 	char *p, *e;
1467*1a4050f5SDavid du Colombier 	uchar *volatile x;
1468*1a4050f5SDavid du Colombier 	ulong offset = off;
1469*1a4050f5SDavid du Colombier 
1470*1a4050f5SDavid du Colombier 	tr = tlsdevs[CONV(c->qid)];
1471*1a4050f5SDavid du Colombier 	if(tr == nil)
1472*1a4050f5SDavid du Colombier 		panic("tlswrite");
1473*1a4050f5SDavid du Colombier 
1474*1a4050f5SDavid du Colombier 	ty = TYPE(c->qid);
1475*1a4050f5SDavid du Colombier 	switch(ty){
1476*1a4050f5SDavid du Colombier 	case Qdata:
1477*1a4050f5SDavid du Colombier 	case Qhand:
1478*1a4050f5SDavid du Colombier 		p = a;
1479*1a4050f5SDavid du Colombier 		e = p + n;
1480*1a4050f5SDavid du Colombier 		do{
1481*1a4050f5SDavid du Colombier 			m = e - p;
1482*1a4050f5SDavid du Colombier 			if(m > MaxRecLen)
1483*1a4050f5SDavid du Colombier 				m = MaxRecLen;
1484*1a4050f5SDavid du Colombier 
1485*1a4050f5SDavid du Colombier 			b = allocb(m);
1486*1a4050f5SDavid du Colombier 			if(waserror()){
1487*1a4050f5SDavid du Colombier 				freeb(b);
1488*1a4050f5SDavid du Colombier 				nexterror();
1489*1a4050f5SDavid du Colombier 			}
1490*1a4050f5SDavid du Colombier 			memmove(b->wp, p, m);
1491*1a4050f5SDavid du Colombier 			poperror();
1492*1a4050f5SDavid du Colombier 			b->wp += m;
1493*1a4050f5SDavid du Colombier 
1494*1a4050f5SDavid du Colombier 			tlsbwrite(c, b, offset);
1495*1a4050f5SDavid du Colombier 
1496*1a4050f5SDavid du Colombier 			p += m;
1497*1a4050f5SDavid du Colombier 		}while(p < e);
1498*1a4050f5SDavid du Colombier 		return n;
1499*1a4050f5SDavid du Colombier 	case Qctl:
1500*1a4050f5SDavid du Colombier 		break;
1501*1a4050f5SDavid du Colombier 	default:
1502*1a4050f5SDavid du Colombier 		error(Ebadusefd);
1503*1a4050f5SDavid du Colombier 		return -1;
1504*1a4050f5SDavid du Colombier 	}
1505*1a4050f5SDavid du Colombier 
1506*1a4050f5SDavid du Colombier 	cb = parsecmd(a, n);
1507*1a4050f5SDavid du Colombier 	if(waserror()){
1508*1a4050f5SDavid du Colombier 		free(cb);
1509*1a4050f5SDavid du Colombier 		nexterror();
1510*1a4050f5SDavid du Colombier 	}
1511*1a4050f5SDavid du Colombier 	if(cb->nf < 1)
1512*1a4050f5SDavid du Colombier 		error("short control request");
1513*1a4050f5SDavid du Colombier 
1514*1a4050f5SDavid du Colombier 	/* mutex with operations using what we're about to change */
1515*1a4050f5SDavid du Colombier 	if(waserror()){
1516*1a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
1517*1a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
1518*1a4050f5SDavid du Colombier 		nexterror();
1519*1a4050f5SDavid du Colombier 	}
1520*1a4050f5SDavid du Colombier 	qlock(&tr->in.seclock);
1521*1a4050f5SDavid du Colombier 	qlock(&tr->out.seclock);
1522*1a4050f5SDavid du Colombier 
1523*1a4050f5SDavid du Colombier 	if(strcmp(cb->f[0], "fd") == 0){
1524*1a4050f5SDavid du Colombier 		if(cb->nf != 3)
1525*1a4050f5SDavid du Colombier 			error("usage: fd open-fd version");
1526*1a4050f5SDavid du Colombier 		if(tr->c != nil)
1527*1a4050f5SDavid du Colombier 			error(Einuse);
1528*1a4050f5SDavid du Colombier 		m = strtol(cb->f[2], nil, 0);
1529*1a4050f5SDavid du Colombier 		if(m < MinProtoVersion || m > MaxProtoVersion)
1530*1a4050f5SDavid du Colombier 			error("unsupported version");
1531*1a4050f5SDavid du Colombier 		tr->c = buftochan(cb->f[1]);
1532*1a4050f5SDavid du Colombier 		tr->version = m;
1533*1a4050f5SDavid du Colombier 		tlsSetState(tr, SHandshake, SClosed);
1534*1a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "version") == 0){
1535*1a4050f5SDavid du Colombier 		if(cb->nf != 2)
1536*1a4050f5SDavid du Colombier 			error("usage: version vers");
1537*1a4050f5SDavid du Colombier 		if(tr->c == nil)
1538*1a4050f5SDavid du Colombier 			error("must set fd before version");
1539*1a4050f5SDavid du Colombier 		if(tr->verset)
1540*1a4050f5SDavid du Colombier 			error("version already set");
1541*1a4050f5SDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
1542*1a4050f5SDavid du Colombier 		if(m == SSL3Version)
1543*1a4050f5SDavid du Colombier 			tr->packMac = sslPackMac;
1544*1a4050f5SDavid du Colombier 		else if(m == TLSVersion)
1545*1a4050f5SDavid du Colombier 			tr->packMac = tlsPackMac;
1546*1a4050f5SDavid du Colombier 		else
1547*1a4050f5SDavid du Colombier 			error("unsupported version");
1548*1a4050f5SDavid du Colombier 		tr->verset = 1;
1549*1a4050f5SDavid du Colombier 		tr->version = m;
1550*1a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "secret") == 0){
1551*1a4050f5SDavid du Colombier 		if(cb->nf != 5)
1552*1a4050f5SDavid du Colombier 			error("usage: secret hashalg encalg isclient secretdata");
1553*1a4050f5SDavid du Colombier 		if(tr->c == nil || !tr->verset)
1554*1a4050f5SDavid du Colombier 			error("must set fd and version before secrets");
1555*1a4050f5SDavid du Colombier 
1556*1a4050f5SDavid du Colombier 		if(tr->in.new != nil){
1557*1a4050f5SDavid du Colombier 			freeSec(tr->in.new);
1558*1a4050f5SDavid du Colombier 			tr->in.new = nil;
1559*1a4050f5SDavid du Colombier 		}
1560*1a4050f5SDavid du Colombier 		if(tr->out.new != nil){
1561*1a4050f5SDavid du Colombier 			freeSec(tr->out.new);
1562*1a4050f5SDavid du Colombier 			tr->out.new = nil;
1563*1a4050f5SDavid du Colombier 		}
1564*1a4050f5SDavid du Colombier 
1565*1a4050f5SDavid du Colombier 		ha = parsehashalg(cb->f[1]);
1566*1a4050f5SDavid du Colombier 		ea = parseencalg(cb->f[2]);
1567*1a4050f5SDavid du Colombier 
1568*1a4050f5SDavid du Colombier 		p = cb->f[4];
1569*1a4050f5SDavid du Colombier 		m = (strlen(p)*3)/2;
1570*1a4050f5SDavid du Colombier 		x = smalloc(m);
1571*1a4050f5SDavid du Colombier 		tos = nil;
1572*1a4050f5SDavid du Colombier 		toc = nil;
1573*1a4050f5SDavid du Colombier 		if(waserror()){
1574*1a4050f5SDavid du Colombier 			freeSec(tos);
1575*1a4050f5SDavid du Colombier 			freeSec(toc);
1576*1a4050f5SDavid du Colombier 			free(x);
1577*1a4050f5SDavid du Colombier 			nexterror();
1578*1a4050f5SDavid du Colombier 		}
1579*1a4050f5SDavid du Colombier 		m = dec64(x, m, p, strlen(p));
1580*1a4050f5SDavid du Colombier 		if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
1581*1a4050f5SDavid du Colombier 			error("not enough secret data provided");
1582*1a4050f5SDavid du Colombier 
1583*1a4050f5SDavid du Colombier 		tos = smalloc(sizeof(Secret));
1584*1a4050f5SDavid du Colombier 		toc = smalloc(sizeof(Secret));
1585*1a4050f5SDavid du Colombier 		if(!ha->initkey || !ea->initkey)
1586*1a4050f5SDavid du Colombier 			error("misimplemented secret algorithm");
1587*1a4050f5SDavid du Colombier 		(*ha->initkey)(ha, tr->version, tos, &x[0]);
1588*1a4050f5SDavid du Colombier 		(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
1589*1a4050f5SDavid du Colombier 		(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
1590*1a4050f5SDavid du Colombier 		(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
1591*1a4050f5SDavid du Colombier 
1592*1a4050f5SDavid du Colombier 		if(!tos->mac || !tos->enc || !tos->dec
1593*1a4050f5SDavid du Colombier 		|| !toc->mac || !toc->enc || !toc->dec)
1594*1a4050f5SDavid du Colombier 			error("missing algorithm implementations");
1595*1a4050f5SDavid du Colombier 		if(strtol(cb->f[3], nil, 0) == 0){
1596*1a4050f5SDavid du Colombier 			tr->in.new = tos;
1597*1a4050f5SDavid du Colombier 			tr->out.new = toc;
1598*1a4050f5SDavid du Colombier 		}else{
1599*1a4050f5SDavid du Colombier 			tr->in.new = toc;
1600*1a4050f5SDavid du Colombier 			tr->out.new = tos;
1601*1a4050f5SDavid du Colombier 		}
1602*1a4050f5SDavid du Colombier 		if(tr->version == SSL3Version){
1603*1a4050f5SDavid du Colombier 			toc->unpad = sslunpad;
1604*1a4050f5SDavid du Colombier 			tos->unpad = sslunpad;
1605*1a4050f5SDavid du Colombier 		}else{
1606*1a4050f5SDavid du Colombier 			toc->unpad = tlsunpad;
1607*1a4050f5SDavid du Colombier 			tos->unpad = tlsunpad;
1608*1a4050f5SDavid du Colombier 		}
1609*1a4050f5SDavid du Colombier 		toc->encalg = ea->name;
1610*1a4050f5SDavid du Colombier 		toc->hashalg = ha->name;
1611*1a4050f5SDavid du Colombier 		tos->encalg = ea->name;
1612*1a4050f5SDavid du Colombier 		tos->hashalg = ha->name;
1613*1a4050f5SDavid du Colombier 
1614*1a4050f5SDavid du Colombier 		free(x);
1615*1a4050f5SDavid du Colombier 		poperror();
1616*1a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "changecipher") == 0){
1617*1a4050f5SDavid du Colombier 		if(cb->nf != 1)
1618*1a4050f5SDavid du Colombier 			error("usage: changecipher");
1619*1a4050f5SDavid du Colombier 		if(tr->out.new == nil)
1620*1a4050f5SDavid du Colombier 			error("cannot change cipher spec without setting secret");
1621*1a4050f5SDavid du Colombier 
1622*1a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
1623*1a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
1624*1a4050f5SDavid du Colombier 		poperror();
1625*1a4050f5SDavid du Colombier 		free(cb);
1626*1a4050f5SDavid du Colombier 		poperror();
1627*1a4050f5SDavid du Colombier 
1628*1a4050f5SDavid du Colombier 		/*
1629*1a4050f5SDavid du Colombier 		 * the real work is done as the message is written
1630*1a4050f5SDavid du Colombier 		 * so the stream is encrypted in sync.
1631*1a4050f5SDavid du Colombier 		 */
1632*1a4050f5SDavid du Colombier 		b = allocb(1);
1633*1a4050f5SDavid du Colombier 		*b->wp++ = 1;
1634*1a4050f5SDavid du Colombier 		tlsrecwrite(tr, RChangeCipherSpec, b);
1635*1a4050f5SDavid du Colombier 		return n;
1636*1a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "opened") == 0){
1637*1a4050f5SDavid du Colombier 		if(cb->nf != 1)
1638*1a4050f5SDavid du Colombier 			error("usage: opened");
1639*1a4050f5SDavid du Colombier 		if(tr->in.sec == nil || tr->out.sec == nil)
1640*1a4050f5SDavid du Colombier 			error("cipher must be configured before enabling data messages");
1641*1a4050f5SDavid du Colombier 		lock(&tr->statelk);
1642*1a4050f5SDavid du Colombier 		if(tr->state != SHandshake && tr->state != SOpen){
1643*1a4050f5SDavid du Colombier 			unlock(&tr->statelk);
1644*1a4050f5SDavid du Colombier 			error("cannot enable data messages");
1645*1a4050f5SDavid du Colombier 		}
1646*1a4050f5SDavid du Colombier 		tr->state = SOpen;
1647*1a4050f5SDavid du Colombier 		unlock(&tr->statelk);
1648*1a4050f5SDavid du Colombier 		tr->opened = 1;
1649*1a4050f5SDavid du Colombier 	}else if(strcmp(cb->f[0], "alert") == 0){
1650*1a4050f5SDavid du Colombier 		if(cb->nf != 2)
1651*1a4050f5SDavid du Colombier 			error("usage: alert n");
1652*1a4050f5SDavid du Colombier 		if(tr->c == nil)
1653*1a4050f5SDavid du Colombier 			error("must set fd before sending alerts");
1654*1a4050f5SDavid du Colombier 		m = strtol(cb->f[1], nil, 0);
1655*1a4050f5SDavid du Colombier 
1656*1a4050f5SDavid du Colombier 		qunlock(&tr->in.seclock);
1657*1a4050f5SDavid du Colombier 		qunlock(&tr->out.seclock);
1658*1a4050f5SDavid du Colombier 		poperror();
1659*1a4050f5SDavid du Colombier 		free(cb);
1660*1a4050f5SDavid du Colombier 		poperror();
1661*1a4050f5SDavid du Colombier 
1662*1a4050f5SDavid du Colombier 		sendAlert(tr, m);
1663*1a4050f5SDavid du Colombier 
1664*1a4050f5SDavid du Colombier 		if(m == ECloseNotify)
1665*1a4050f5SDavid du Colombier 			tlsclosed(tr, SLClose);
1666*1a4050f5SDavid du Colombier 
1667*1a4050f5SDavid du Colombier 		return n;
1668*1a4050f5SDavid du Colombier 	} else if(strcmp(cb->f[0], "debug") == 0){
1669*1a4050f5SDavid du Colombier 		if(cb->nf == 2){
1670*1a4050f5SDavid du Colombier 			if(strcmp(cb->f[1], "on") == 0)
1671*1a4050f5SDavid du Colombier 				tr->debug = 1;
1672*1a4050f5SDavid du Colombier 			else
1673*1a4050f5SDavid du Colombier 				tr->debug = 0;
1674*1a4050f5SDavid du Colombier 		} else
1675*1a4050f5SDavid du Colombier 			tr->debug = 1;
1676*1a4050f5SDavid du Colombier 	} else
1677*1a4050f5SDavid du Colombier 		error(Ebadarg);
1678*1a4050f5SDavid du Colombier 
1679*1a4050f5SDavid du Colombier 	qunlock(&tr->in.seclock);
1680*1a4050f5SDavid du Colombier 	qunlock(&tr->out.seclock);
1681*1a4050f5SDavid du Colombier 	poperror();
1682*1a4050f5SDavid du Colombier 	free(cb);
1683*1a4050f5SDavid du Colombier 	poperror();
1684*1a4050f5SDavid du Colombier 
1685*1a4050f5SDavid du Colombier 	return n;
1686*1a4050f5SDavid du Colombier }
1687*1a4050f5SDavid du Colombier 
1688*1a4050f5SDavid du Colombier static void
1689*1a4050f5SDavid du Colombier tlsinit(void)
1690*1a4050f5SDavid du Colombier {
1691*1a4050f5SDavid du Colombier 	struct Encalg *e;
1692*1a4050f5SDavid du Colombier 	struct Hashalg *h;
1693*1a4050f5SDavid du Colombier 	int n;
1694*1a4050f5SDavid du Colombier 	char *cp;
1695*1a4050f5SDavid du Colombier 	static int already;
1696*1a4050f5SDavid du Colombier 
1697*1a4050f5SDavid du Colombier 	if(!already){
1698*1a4050f5SDavid du Colombier 		fmtinstall('H', encodefmt);
1699*1a4050f5SDavid du Colombier 		already = 1;
1700*1a4050f5SDavid du Colombier 	}
1701*1a4050f5SDavid du Colombier 
1702*1a4050f5SDavid du Colombier 	tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
1703*1a4050f5SDavid du Colombier 	trnames = smalloc((sizeof *trnames) * maxtlsdevs);
1704*1a4050f5SDavid du Colombier 
1705*1a4050f5SDavid du Colombier 	n = 1;
1706*1a4050f5SDavid du Colombier 	for(e = encrypttab; e->name != nil; e++)
1707*1a4050f5SDavid du Colombier 		n += strlen(e->name) + 1;
1708*1a4050f5SDavid du Colombier 	cp = encalgs = smalloc(n);
1709*1a4050f5SDavid du Colombier 	for(e = encrypttab;;){
1710*1a4050f5SDavid du Colombier 		strcpy(cp, e->name);
1711*1a4050f5SDavid du Colombier 		cp += strlen(e->name);
1712*1a4050f5SDavid du Colombier 		e++;
1713*1a4050f5SDavid du Colombier 		if(e->name == nil)
1714*1a4050f5SDavid du Colombier 			break;
1715*1a4050f5SDavid du Colombier 		*cp++ = ' ';
1716*1a4050f5SDavid du Colombier 	}
1717*1a4050f5SDavid du Colombier 	*cp = 0;
1718*1a4050f5SDavid du Colombier 
1719*1a4050f5SDavid du Colombier 	n = 1;
1720*1a4050f5SDavid du Colombier 	for(h = hashtab; h->name != nil; h++)
1721*1a4050f5SDavid du Colombier 		n += strlen(h->name) + 1;
1722*1a4050f5SDavid du Colombier 	cp = hashalgs = smalloc(n);
1723*1a4050f5SDavid du Colombier 	for(h = hashtab;;){
1724*1a4050f5SDavid du Colombier 		strcpy(cp, h->name);
1725*1a4050f5SDavid du Colombier 		cp += strlen(h->name);
1726*1a4050f5SDavid du Colombier 		h++;
1727*1a4050f5SDavid du Colombier 		if(h->name == nil)
1728*1a4050f5SDavid du Colombier 			break;
1729*1a4050f5SDavid du Colombier 		*cp++ = ' ';
1730*1a4050f5SDavid du Colombier 	}
1731*1a4050f5SDavid du Colombier 	*cp = 0;
1732*1a4050f5SDavid du Colombier }
1733*1a4050f5SDavid du Colombier 
1734*1a4050f5SDavid du Colombier Dev tlsdevtab = {
1735*1a4050f5SDavid du Colombier 	'a',
1736*1a4050f5SDavid du Colombier 	"tls",
1737*1a4050f5SDavid du Colombier 
1738*1a4050f5SDavid du Colombier 	devreset,
1739*1a4050f5SDavid du Colombier 	tlsinit,
1740*1a4050f5SDavid du Colombier 	devshutdown,
1741*1a4050f5SDavid du Colombier 	tlsattach,
1742*1a4050f5SDavid du Colombier 	tlswalk,
1743*1a4050f5SDavid du Colombier 	tlsstat,
1744*1a4050f5SDavid du Colombier 	tlsopen,
1745*1a4050f5SDavid du Colombier 	devcreate,
1746*1a4050f5SDavid du Colombier 	tlsclose,
1747*1a4050f5SDavid du Colombier 	tlsread,
1748*1a4050f5SDavid du Colombier 	tlsbread,
1749*1a4050f5SDavid du Colombier 	tlswrite,
1750*1a4050f5SDavid du Colombier 	tlsbwrite,
1751*1a4050f5SDavid du Colombier 	devremove,
1752*1a4050f5SDavid du Colombier 	tlswstat,
1753*1a4050f5SDavid du Colombier };
1754*1a4050f5SDavid du Colombier 
1755*1a4050f5SDavid du Colombier /* get channel associated with an fd */
1756*1a4050f5SDavid du Colombier static Chan*
1757*1a4050f5SDavid du Colombier buftochan(char *p)
1758*1a4050f5SDavid du Colombier {
1759*1a4050f5SDavid du Colombier 	Chan *c;
1760*1a4050f5SDavid du Colombier 	int fd;
1761*1a4050f5SDavid du Colombier 
1762*1a4050f5SDavid du Colombier 	if(p == 0)
1763*1a4050f5SDavid du Colombier 		error(Ebadarg);
1764*1a4050f5SDavid du Colombier 	fd = strtoul(p, 0, 0);
1765*1a4050f5SDavid du Colombier 	if(fd < 0)
1766*1a4050f5SDavid du Colombier 		error(Ebadarg);
1767*1a4050f5SDavid du Colombier 	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
1768*1a4050f5SDavid du Colombier 	return c;
1769*1a4050f5SDavid du Colombier }
1770*1a4050f5SDavid du Colombier 
1771*1a4050f5SDavid du Colombier static void
1772*1a4050f5SDavid du Colombier sendAlert(TlsRec *tr, int err)
1773*1a4050f5SDavid du Colombier {
1774*1a4050f5SDavid du Colombier 	Block *b;
1775*1a4050f5SDavid du Colombier 	int i, fatal;
1776*1a4050f5SDavid du Colombier 	char *msg;
1777*1a4050f5SDavid du Colombier 
1778*1a4050f5SDavid du Colombier if(tr->debug)pprint("sendAlert %d\n", err);
1779*1a4050f5SDavid du Colombier 	fatal = 1;
1780*1a4050f5SDavid du Colombier 	msg = "tls unknown alert";
1781*1a4050f5SDavid du Colombier 	for(i=0; i < nelem(tlserrs); i++) {
1782*1a4050f5SDavid du Colombier 		if(tlserrs[i].err == err) {
1783*1a4050f5SDavid du Colombier 			msg = tlserrs[i].msg;
1784*1a4050f5SDavid du Colombier 			if(tr->version == SSL3Version)
1785*1a4050f5SDavid du Colombier 				err = tlserrs[i].sslerr;
1786*1a4050f5SDavid du Colombier 			else
1787*1a4050f5SDavid du Colombier 				err = tlserrs[i].tlserr;
1788*1a4050f5SDavid du Colombier 			fatal = tlserrs[i].fatal;
1789*1a4050f5SDavid du Colombier 			break;
1790*1a4050f5SDavid du Colombier 		}
1791*1a4050f5SDavid du Colombier 	}
1792*1a4050f5SDavid du Colombier 
1793*1a4050f5SDavid du Colombier 	if(!waserror()){
1794*1a4050f5SDavid du Colombier 		b = allocb(2);
1795*1a4050f5SDavid du Colombier 		*b->wp++ = fatal + 1;
1796*1a4050f5SDavid du Colombier 		*b->wp++ = err;
1797*1a4050f5SDavid du Colombier 		if(fatal)
1798*1a4050f5SDavid du Colombier 			tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
1799*1a4050f5SDavid du Colombier 		tlsrecwrite(tr, RAlert, b);
1800*1a4050f5SDavid du Colombier 		poperror();
1801*1a4050f5SDavid du Colombier 	}
1802*1a4050f5SDavid du Colombier 	if(fatal)
1803*1a4050f5SDavid du Colombier 		tlsError(tr, msg);
1804*1a4050f5SDavid du Colombier }
1805*1a4050f5SDavid du Colombier 
1806*1a4050f5SDavid du Colombier static void
1807*1a4050f5SDavid du Colombier tlsError(TlsRec *tr, char *msg)
1808*1a4050f5SDavid du Colombier {
1809*1a4050f5SDavid du Colombier 	int s;
1810*1a4050f5SDavid du Colombier 
1811*1a4050f5SDavid du Colombier if(tr->debug)pprint("tleError %s\n", msg);
1812*1a4050f5SDavid du Colombier 	lock(&tr->statelk);
1813*1a4050f5SDavid du Colombier 	s = tr->state;
1814*1a4050f5SDavid du Colombier 	tr->state = SError;
1815*1a4050f5SDavid du Colombier 	if(s != SError){
1816*1a4050f5SDavid du Colombier 		strncpy(tr->err, msg, ERRMAX - 1);
1817*1a4050f5SDavid du Colombier 		tr->err[ERRMAX - 1] = '\0';
1818*1a4050f5SDavid du Colombier 	}
1819*1a4050f5SDavid du Colombier 	unlock(&tr->statelk);
1820*1a4050f5SDavid du Colombier 	if(s != SError)
1821*1a4050f5SDavid du Colombier 		alertHand(tr, msg);
1822*1a4050f5SDavid du Colombier }
1823*1a4050f5SDavid du Colombier 
1824*1a4050f5SDavid du Colombier static void
1825*1a4050f5SDavid du Colombier tlsSetState(TlsRec *tr, int new, int old)
1826*1a4050f5SDavid du Colombier {
1827*1a4050f5SDavid du Colombier 	lock(&tr->statelk);
1828*1a4050f5SDavid du Colombier 	if(tr->state & old)
1829*1a4050f5SDavid du Colombier 		tr->state = new;
1830*1a4050f5SDavid du Colombier 	unlock(&tr->statelk);
1831*1a4050f5SDavid du Colombier }
1832*1a4050f5SDavid du Colombier 
1833*1a4050f5SDavid du Colombier /* hand up a digest connection */
1834*1a4050f5SDavid du Colombier static void
1835*1a4050f5SDavid du Colombier tlshangup(TlsRec *tr)
1836*1a4050f5SDavid du Colombier {
1837*1a4050f5SDavid du Colombier 	Block *b;
1838*1a4050f5SDavid du Colombier 
1839*1a4050f5SDavid du Colombier 	qlock(&tr->in.io);
1840*1a4050f5SDavid du Colombier 	for(b = tr->processed; b; b = tr->processed){
1841*1a4050f5SDavid du Colombier 		tr->processed = b->next;
1842*1a4050f5SDavid du Colombier 		freeb(b);
1843*1a4050f5SDavid du Colombier 	}
1844*1a4050f5SDavid du Colombier 	if(tr->unprocessed != nil){
1845*1a4050f5SDavid du Colombier 		freeb(tr->unprocessed);
1846*1a4050f5SDavid du Colombier 		tr->unprocessed = nil;
1847*1a4050f5SDavid du Colombier 	}
1848*1a4050f5SDavid du Colombier 	qunlock(&tr->in.io);
1849*1a4050f5SDavid du Colombier 
1850*1a4050f5SDavid du Colombier 	tlsSetState(tr, SClosed, ~0);
1851*1a4050f5SDavid du Colombier }
1852*1a4050f5SDavid du Colombier 
1853*1a4050f5SDavid du Colombier static TlsRec*
1854*1a4050f5SDavid du Colombier newtls(Chan *ch)
1855*1a4050f5SDavid du Colombier {
1856*1a4050f5SDavid du Colombier 	TlsRec **pp, **ep, **np;
1857*1a4050f5SDavid du Colombier 	char **nmp;
1858*1a4050f5SDavid du Colombier 	int t, newmax;
1859*1a4050f5SDavid du Colombier 
1860*1a4050f5SDavid du Colombier 	if(waserror()) {
1861*1a4050f5SDavid du Colombier 		unlock(&tdlock);
1862*1a4050f5SDavid du Colombier 		nexterror();
1863*1a4050f5SDavid du Colombier 	}
1864*1a4050f5SDavid du Colombier 	lock(&tdlock);
1865*1a4050f5SDavid du Colombier 	ep = &tlsdevs[maxtlsdevs];
1866*1a4050f5SDavid du Colombier 	for(pp = tlsdevs; pp < ep; pp++)
1867*1a4050f5SDavid du Colombier 		if(*pp == nil)
1868*1a4050f5SDavid du Colombier 			break;
1869*1a4050f5SDavid du Colombier 	if(pp >= ep) {
1870*1a4050f5SDavid du Colombier 		if(maxtlsdevs >= MaxTlsDevs) {
1871*1a4050f5SDavid du Colombier 			unlock(&tdlock);
1872*1a4050f5SDavid du Colombier 			poperror();
1873*1a4050f5SDavid du Colombier 			return nil;
1874*1a4050f5SDavid du Colombier 		}
1875*1a4050f5SDavid du Colombier 		newmax = 2 * maxtlsdevs;
1876*1a4050f5SDavid du Colombier 		if(newmax > MaxTlsDevs)
1877*1a4050f5SDavid du Colombier 			newmax = MaxTlsDevs;
1878*1a4050f5SDavid du Colombier 		np = smalloc(sizeof(TlsRec*) * newmax);
1879*1a4050f5SDavid du Colombier 		memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
1880*1a4050f5SDavid du Colombier 		tlsdevs = np;
1881*1a4050f5SDavid du Colombier 		pp = &tlsdevs[maxtlsdevs];
1882*1a4050f5SDavid du Colombier 		memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
1883*1a4050f5SDavid du Colombier 
1884*1a4050f5SDavid du Colombier 		nmp = smalloc(sizeof *nmp * newmax);
1885*1a4050f5SDavid du Colombier 		memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
1886*1a4050f5SDavid du Colombier 		trnames = nmp;
1887*1a4050f5SDavid du Colombier 
1888*1a4050f5SDavid du Colombier 		maxtlsdevs = newmax;
1889*1a4050f5SDavid du Colombier 	}
1890*1a4050f5SDavid du Colombier 	*pp = mktlsrec();
1891*1a4050f5SDavid du Colombier 	if(pp - tlsdevs >= tdhiwat)
1892*1a4050f5SDavid du Colombier 		tdhiwat++;
1893*1a4050f5SDavid du Colombier 	t = TYPE(ch->qid);
1894*1a4050f5SDavid du Colombier 	if(t == Qclonus)
1895*1a4050f5SDavid du Colombier 		t = Qctl;
1896*1a4050f5SDavid du Colombier 	ch->qid.path = QID(pp - tlsdevs, t);
1897*1a4050f5SDavid du Colombier 	ch->qid.vers = 0;
1898*1a4050f5SDavid du Colombier 	unlock(&tdlock);
1899*1a4050f5SDavid du Colombier 	poperror();
1900*1a4050f5SDavid du Colombier 	return *pp;
1901*1a4050f5SDavid du Colombier }
1902*1a4050f5SDavid du Colombier 
1903*1a4050f5SDavid du Colombier static TlsRec *
1904*1a4050f5SDavid du Colombier mktlsrec(void)
1905*1a4050f5SDavid du Colombier {
1906*1a4050f5SDavid du Colombier 	TlsRec *tr;
1907*1a4050f5SDavid du Colombier 
1908*1a4050f5SDavid du Colombier 	tr = mallocz(sizeof(*tr), 1);
1909*1a4050f5SDavid du Colombier 	if(tr == nil)
1910*1a4050f5SDavid du Colombier 		error(Enomem);
1911*1a4050f5SDavid du Colombier 	tr->state = SClosed;
1912*1a4050f5SDavid du Colombier 	tr->ref = 1;
1913*1a4050f5SDavid du Colombier 	kstrdup(&tr->user, up->user);
1914*1a4050f5SDavid du Colombier 	tr->perm = 0660;
1915*1a4050f5SDavid du Colombier 	return tr;
1916*1a4050f5SDavid du Colombier }
1917*1a4050f5SDavid du Colombier 
1918*1a4050f5SDavid du Colombier static char*
1919*1a4050f5SDavid du Colombier tlsstate(int s)
1920*1a4050f5SDavid du Colombier {
1921*1a4050f5SDavid du Colombier 	switch(s){
1922*1a4050f5SDavid du Colombier 	case SHandshake:
1923*1a4050f5SDavid du Colombier 		return "Handshaking";
1924*1a4050f5SDavid du Colombier 	case SOpen:
1925*1a4050f5SDavid du Colombier 		return "Established";
1926*1a4050f5SDavid du Colombier 	case SRClose:
1927*1a4050f5SDavid du Colombier 		return "RemoteClosed";
1928*1a4050f5SDavid du Colombier 	case SLClose:
1929*1a4050f5SDavid du Colombier 		return "LocalClosed";
1930*1a4050f5SDavid du Colombier 	case SAlert:
1931*1a4050f5SDavid du Colombier 		return "Alerting";
1932*1a4050f5SDavid du Colombier 	case SError:
1933*1a4050f5SDavid du Colombier 		return "Errored";
1934*1a4050f5SDavid du Colombier 	case SClosed:
1935*1a4050f5SDavid du Colombier 		return "Closed";
1936*1a4050f5SDavid du Colombier 	}
1937*1a4050f5SDavid du Colombier 	return "Unknown";
1938*1a4050f5SDavid du Colombier }
1939*1a4050f5SDavid du Colombier 
1940*1a4050f5SDavid du Colombier static void
1941*1a4050f5SDavid du Colombier freeSec(Secret *s)
1942*1a4050f5SDavid du Colombier {
1943*1a4050f5SDavid du Colombier 	if(s != nil){
1944*1a4050f5SDavid du Colombier 		free(s->enckey);
1945*1a4050f5SDavid du Colombier 		free(s);
1946*1a4050f5SDavid du Colombier 	}
1947*1a4050f5SDavid du Colombier }
1948*1a4050f5SDavid du Colombier 
1949*1a4050f5SDavid du Colombier static int
1950*1a4050f5SDavid du Colombier noenc(Secret *unused1, uchar *unused2, int n)
1951*1a4050f5SDavid du Colombier {
1952*1a4050f5SDavid du Colombier 	return n;
1953*1a4050f5SDavid du Colombier }
1954*1a4050f5SDavid du Colombier 
1955*1a4050f5SDavid du Colombier static int
1956*1a4050f5SDavid du Colombier rc4enc(Secret *sec, uchar *buf, int n)
1957*1a4050f5SDavid du Colombier {
1958*1a4050f5SDavid du Colombier 	rc4(sec->enckey, buf, n);
1959*1a4050f5SDavid du Colombier 	return n;
1960*1a4050f5SDavid du Colombier }
1961*1a4050f5SDavid du Colombier 
1962*1a4050f5SDavid du Colombier static int
1963*1a4050f5SDavid du Colombier tlsunpad(uchar *buf, int n, int block)
1964*1a4050f5SDavid du Colombier {
1965*1a4050f5SDavid du Colombier 	int pad, nn;
1966*1a4050f5SDavid du Colombier 
1967*1a4050f5SDavid du Colombier 	pad = buf[n - 1];
1968*1a4050f5SDavid du Colombier 	nn = n - 1 - pad;
1969*1a4050f5SDavid du Colombier 	if(nn <= 0 || n % block)
1970*1a4050f5SDavid du Colombier 		return -1;
1971*1a4050f5SDavid du Colombier 	while(--n > nn)
1972*1a4050f5SDavid du Colombier 		if(pad != buf[n - 1])
1973*1a4050f5SDavid du Colombier 			return -1;
1974*1a4050f5SDavid du Colombier 	return nn;
1975*1a4050f5SDavid du Colombier }
1976*1a4050f5SDavid du Colombier 
1977*1a4050f5SDavid du Colombier static int
1978*1a4050f5SDavid du Colombier sslunpad(uchar *buf, int n, int block)
1979*1a4050f5SDavid du Colombier {
1980*1a4050f5SDavid du Colombier 	int pad, nn;
1981*1a4050f5SDavid du Colombier 
1982*1a4050f5SDavid du Colombier 	pad = buf[n - 1];
1983*1a4050f5SDavid du Colombier 	nn = n - 1 - pad;
1984*1a4050f5SDavid du Colombier 	if(nn <= 0 || n % block)
1985*1a4050f5SDavid du Colombier 		return -1;
1986*1a4050f5SDavid du Colombier 	return nn;
1987*1a4050f5SDavid du Colombier }
1988*1a4050f5SDavid du Colombier 
1989*1a4050f5SDavid du Colombier static int
1990*1a4050f5SDavid du Colombier blockpad(uchar *buf, int n, int block)
1991*1a4050f5SDavid du Colombier {
1992*1a4050f5SDavid du Colombier 	int pad, nn;
1993*1a4050f5SDavid du Colombier 
1994*1a4050f5SDavid du Colombier 	nn = n + block;
1995*1a4050f5SDavid du Colombier 	nn -= nn % block;
1996*1a4050f5SDavid du Colombier 	pad = nn - (n + 1);
1997*1a4050f5SDavid du Colombier 	while(n < nn)
1998*1a4050f5SDavid du Colombier 		buf[n++] = pad;
1999*1a4050f5SDavid du Colombier 	return nn;
2000*1a4050f5SDavid du Colombier }
2001*1a4050f5SDavid du Colombier 
2002*1a4050f5SDavid du Colombier static int
2003*1a4050f5SDavid du Colombier des3enc(Secret *sec, uchar *buf, int n)
2004*1a4050f5SDavid du Colombier {
2005*1a4050f5SDavid du Colombier 	n = blockpad(buf, n, 8);
2006*1a4050f5SDavid du Colombier 	des3CBCencrypt(buf, n, sec->enckey);
2007*1a4050f5SDavid du Colombier 	return n;
2008*1a4050f5SDavid du Colombier }
2009*1a4050f5SDavid du Colombier 
2010*1a4050f5SDavid du Colombier static int
2011*1a4050f5SDavid du Colombier des3dec(Secret *sec, uchar *buf, int n)
2012*1a4050f5SDavid du Colombier {
2013*1a4050f5SDavid du Colombier 	des3CBCdecrypt(buf, n, sec->enckey);
2014*1a4050f5SDavid du Colombier 	return (*sec->unpad)(buf, n, 8);
2015*1a4050f5SDavid du Colombier }
2016*1a4050f5SDavid du Colombier static DigestState*
2017*1a4050f5SDavid du Colombier nomac(uchar *unused1, ulong unused2, uchar *unused3, ulong unused4,
2018*1a4050f5SDavid du Colombier 	uchar *unused5, DigestState *unused6)
2019*1a4050f5SDavid du Colombier {
2020*1a4050f5SDavid du Colombier 	return nil;
2021*1a4050f5SDavid du Colombier }
2022*1a4050f5SDavid du Colombier 
2023*1a4050f5SDavid du Colombier /*
2024*1a4050f5SDavid du Colombier  * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
2025*1a4050f5SDavid du Colombier  */
2026*1a4050f5SDavid du Colombier static DigestState*
2027*1a4050f5SDavid du Colombier sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
2028*1a4050f5SDavid du Colombier 	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
2029*1a4050f5SDavid du Colombier {
2030*1a4050f5SDavid du Colombier 	int i;
2031*1a4050f5SDavid du Colombier 	uchar pad[48], innerdigest[20];
2032*1a4050f5SDavid du Colombier 
2033*1a4050f5SDavid du Colombier 	if(xlen > sizeof(innerdigest)
2034*1a4050f5SDavid du Colombier 	|| padlen > sizeof(pad))
2035*1a4050f5SDavid du Colombier 		return nil;
2036*1a4050f5SDavid du Colombier 
2037*1a4050f5SDavid du Colombier 	if(klen>64)
2038*1a4050f5SDavid du Colombier 		return nil;
2039*1a4050f5SDavid du Colombier 
2040*1a4050f5SDavid du Colombier 	/* first time through */
2041*1a4050f5SDavid du Colombier 	if(s == nil){
2042*1a4050f5SDavid du Colombier 		for(i=0; i<padlen; i++)
2043*1a4050f5SDavid du Colombier 			pad[i] = 0x36;
2044*1a4050f5SDavid du Colombier 		s = (*x)(key, klen, nil, nil);
2045*1a4050f5SDavid du Colombier 		s = (*x)(pad, padlen, nil, s);
2046*1a4050f5SDavid du Colombier 		if(s == nil)
2047*1a4050f5SDavid du Colombier 			return nil;
2048*1a4050f5SDavid du Colombier 	}
2049*1a4050f5SDavid du Colombier 
2050*1a4050f5SDavid du Colombier 	s = (*x)(p, len, nil, s);
2051*1a4050f5SDavid du Colombier 	if(digest == nil)
2052*1a4050f5SDavid du Colombier 		return s;
2053*1a4050f5SDavid du Colombier 
2054*1a4050f5SDavid du Colombier 	/* last time through */
2055*1a4050f5SDavid du Colombier 	for(i=0; i<padlen; i++)
2056*1a4050f5SDavid du Colombier 		pad[i] = 0x5c;
2057*1a4050f5SDavid du Colombier 	(*x)(nil, 0, innerdigest, s);
2058*1a4050f5SDavid du Colombier 	s = (*x)(key, klen, nil, nil);
2059*1a4050f5SDavid du Colombier 	s = (*x)(pad, padlen, nil, s);
2060*1a4050f5SDavid du Colombier 	(*x)(innerdigest, xlen, digest, s);
2061*1a4050f5SDavid du Colombier 	return nil;
2062*1a4050f5SDavid du Colombier }
2063*1a4050f5SDavid du Colombier 
2064*1a4050f5SDavid du Colombier static DigestState*
2065*1a4050f5SDavid du Colombier sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
2066*1a4050f5SDavid du Colombier {
2067*1a4050f5SDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
2068*1a4050f5SDavid du Colombier }
2069*1a4050f5SDavid du Colombier 
2070*1a4050f5SDavid du Colombier static DigestState*
2071*1a4050f5SDavid du Colombier sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
2072*1a4050f5SDavid du Colombier {
2073*1a4050f5SDavid du Colombier 	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
2074*1a4050f5SDavid du Colombier }
2075*1a4050f5SDavid du Colombier 
2076*1a4050f5SDavid du Colombier static void
2077*1a4050f5SDavid du Colombier sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
2078*1a4050f5SDavid du Colombier {
2079*1a4050f5SDavid du Colombier 	DigestState *s;
2080*1a4050f5SDavid du Colombier 	uchar buf[11];
2081*1a4050f5SDavid du Colombier 
2082*1a4050f5SDavid du Colombier 	memmove(buf, seq, 8);
2083*1a4050f5SDavid du Colombier 	buf[8] = header[0];
2084*1a4050f5SDavid du Colombier 	buf[9] = header[3];
2085*1a4050f5SDavid du Colombier 	buf[10] = header[4];
2086*1a4050f5SDavid du Colombier 
2087*1a4050f5SDavid du Colombier 	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
2088*1a4050f5SDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
2089*1a4050f5SDavid du Colombier }
2090*1a4050f5SDavid du Colombier 
2091*1a4050f5SDavid du Colombier static void
2092*1a4050f5SDavid du Colombier tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
2093*1a4050f5SDavid du Colombier {
2094*1a4050f5SDavid du Colombier 	DigestState *s;
2095*1a4050f5SDavid du Colombier 	uchar buf[13];
2096*1a4050f5SDavid du Colombier 
2097*1a4050f5SDavid du Colombier 	memmove(buf, seq, 8);
2098*1a4050f5SDavid du Colombier 	memmove(&buf[8], header, 5);
2099*1a4050f5SDavid du Colombier 
2100*1a4050f5SDavid du Colombier 	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
2101*1a4050f5SDavid du Colombier 	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
2102*1a4050f5SDavid du Colombier }
2103*1a4050f5SDavid du Colombier 
2104*1a4050f5SDavid du Colombier static void
2105*1a4050f5SDavid du Colombier put32(uchar *p, u32int x)
2106*1a4050f5SDavid du Colombier {
2107*1a4050f5SDavid du Colombier 	p[0] = x>>24;
2108*1a4050f5SDavid du Colombier 	p[1] = x>>16;
2109*1a4050f5SDavid du Colombier 	p[2] = x>>8;
2110*1a4050f5SDavid du Colombier 	p[3] = x;
2111*1a4050f5SDavid du Colombier }
2112*1a4050f5SDavid du Colombier 
2113*1a4050f5SDavid du Colombier static void
2114*1a4050f5SDavid du Colombier put64(uchar *p, vlong x)
2115*1a4050f5SDavid du Colombier {
2116*1a4050f5SDavid du Colombier 	put32(p, (u32int)(x >> 32));
2117*1a4050f5SDavid du Colombier 	put32(p+4, (u32int)x);
2118*1a4050f5SDavid du Colombier }
2119*1a4050f5SDavid du Colombier 
2120*1a4050f5SDavid du Colombier static void
2121*1a4050f5SDavid du Colombier put24(uchar *p, int x)
2122*1a4050f5SDavid du Colombier {
2123*1a4050f5SDavid du Colombier 	p[0] = x>>16;
2124*1a4050f5SDavid du Colombier 	p[1] = x>>8;
2125*1a4050f5SDavid du Colombier 	p[2] = x;
2126*1a4050f5SDavid du Colombier }
2127*1a4050f5SDavid du Colombier 
2128*1a4050f5SDavid du Colombier static void
2129*1a4050f5SDavid du Colombier put16(uchar *p, int x)
2130*1a4050f5SDavid du Colombier {
2131*1a4050f5SDavid du Colombier 	p[0] = x>>8;
2132*1a4050f5SDavid du Colombier 	p[1] = x;
2133*1a4050f5SDavid du Colombier }
2134*1a4050f5SDavid du Colombier 
2135*1a4050f5SDavid du Colombier static u32int
2136*1a4050f5SDavid du Colombier get32(uchar *p)
2137*1a4050f5SDavid du Colombier {
2138*1a4050f5SDavid du Colombier 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
2139*1a4050f5SDavid du Colombier }
2140*1a4050f5SDavid du Colombier 
2141*1a4050f5SDavid du Colombier static int
2142*1a4050f5SDavid du Colombier get16(uchar *p)
2143*1a4050f5SDavid du Colombier {
2144*1a4050f5SDavid du Colombier 	return (p[0]<<8)|p[1];
2145*1a4050f5SDavid du Colombier }
2146*1a4050f5SDavid du Colombier 
2147*1a4050f5SDavid du Colombier static char *charmap = "0123456789abcdef";
2148*1a4050f5SDavid du Colombier 
2149*1a4050f5SDavid du Colombier static void
2150*1a4050f5SDavid du Colombier pdump(int len, void *a, char *tag)
2151*1a4050f5SDavid du Colombier {
2152*1a4050f5SDavid du Colombier 	uchar *p;
2153*1a4050f5SDavid du Colombier 	int i;
2154*1a4050f5SDavid du Colombier 	char buf[65+32];
2155*1a4050f5SDavid du Colombier 	char *q;
2156*1a4050f5SDavid du Colombier 
2157*1a4050f5SDavid du Colombier 	p = a;
2158*1a4050f5SDavid du Colombier 	strcpy(buf, tag);
2159*1a4050f5SDavid du Colombier 	while(len > 0){
2160*1a4050f5SDavid du Colombier 		q = buf + strlen(tag);
2161*1a4050f5SDavid du Colombier 		for(i = 0; len > 0 && i < 32; i++){
2162*1a4050f5SDavid du Colombier 			if(*p >= ' ' && *p < 0x7f){
2163*1a4050f5SDavid du Colombier 				*q++ = ' ';
2164*1a4050f5SDavid du Colombier 				*q++ = *p;
2165*1a4050f5SDavid du Colombier 			} else {
2166*1a4050f5SDavid du Colombier 				*q++ = charmap[*p>>4];
2167*1a4050f5SDavid du Colombier 				*q++ = charmap[*p & 0xf];
2168*1a4050f5SDavid du Colombier 			}
2169*1a4050f5SDavid du Colombier 			len--;
2170*1a4050f5SDavid du Colombier 			p++;
2171*1a4050f5SDavid du Colombier 		}
2172*1a4050f5SDavid du Colombier 		*q = 0;
2173*1a4050f5SDavid du Colombier 
2174*1a4050f5SDavid du Colombier 		if(len > 0)
2175*1a4050f5SDavid du Colombier 			pprint("%s...\n", buf);
2176*1a4050f5SDavid du Colombier 		else
2177*1a4050f5SDavid du Colombier 			pprint("%s\n", buf);
2178*1a4050f5SDavid du Colombier 	}
2179*1a4050f5SDavid du Colombier }
2180