2122 lines · 43.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* *  devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0 */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h" #include	<libsec.h> typedef struct OneWay	OneWay;typedef struct Secret		Secret;typedef struct TlsRec	TlsRec;typedef struct TlsErrs	TlsErrs; enum {	Statlen=	1024,		/* max. length of status or stats message */	/* buffer limits */	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */	MaxCipherRecLen	= MaxRecLen + 2048,	RecHdrLen		= 5,	MaxMacLen		= SHA1dlen, 	/* protocol versions we can accept */	TLSVersion		= 0x0301,	SSL3Version		= 0x0300,	ProtocolVersion	= 0x0301,	/* maximum version we speak */	MinProtoVersion	= 0x0300,	/* limits on version we accept */	MaxProtoVersion	= 0x03ff, 	/* connection states */	SHandshake	= 1 << 0,	/* doing handshake */	SOpen		= 1 << 1,	/* application data can be sent */	SRClose		= 1 << 2,	/* remote side has closed down */	SLClose		= 1 << 3,	/* sent a close notify alert */	SAlert		= 1 << 5,	/* sending or sent a fatal alert */	SError		= 1 << 6,	/* some sort of error has occured */	SClosed		= 1 << 7,	/* it is all over */ 	/* record types */	RChangeCipherSpec = 20,	RAlert,	RHandshake,	RApplication, 	SSL2ClientHello = 1,	HSSL2ClientHello = 9,  /* local convention;  see tlshand.c */ 	/* alerts */	ECloseNotify 			= 0,	EUnexpectedMessage 	= 10,	EBadRecordMac 		= 20,	EDecryptionFailed 		= 21,	ERecordOverflow 		= 22,	EDecompressionFailure 	= 30,	EHandshakeFailure 		= 40,	ENoCertificate 			= 41,	EBadCertificate 		= 42,	EUnsupportedCertificate 	= 43,	ECertificateRevoked 		= 44,	ECertificateExpired 		= 45,	ECertificateUnknown 	= 46,	EIllegalParameter 		= 47,	EUnknownCa 			= 48,	EAccessDenied 		= 49,	EDecodeError 			= 50,	EDecryptError 			= 51,	EExportRestriction 		= 60,	EProtocolVersion 		= 70,	EInsufficientSecurity 	= 71,	EInternalError 			= 80,	EUserCanceled 			= 90,	ENoRenegotiation 		= 100, 	EMAX = 256}; struct Secret{	char		*encalg;	/* name of encryption alg */	char		*hashalg;	/* name of hash alg */	int		(*enc)(Secret*, uchar*, int);	int		(*dec)(Secret*, uchar*, int);	int		(*unpad)(uchar*, int, int);	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);	int		block;		/* encryption block len, 0 if none */	int		maclen;	void		*enckey;	uchar	mackey[MaxMacLen];}; struct OneWay{	QLock		io;		/* locks io access */	QLock		seclock;	/* locks secret paramaters */	ulong		seq;	Secret		*sec;		/* cipher in use */	Secret		*new;		/* cipher waiting for enable */}; struct TlsRec{	Chan	*c;				/* io channel */	int		ref;				/* serialized by tdlock for atomic destroy */	int		version;			/* version of the protocol we are speaking */	char		verset;			/* version has been set */	char		opened;			/* opened command every issued? */	char		err[ERRMAX];		/* error message to return to handshake requests */	vlong	handin;			/* bytes communicated by the record layer */	vlong	handout;	vlong	datain;	vlong	dataout; 	Lock		statelk;	int		state; 	/* record layer mac functions for different protocol versions */	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*); 	/* input side -- protected by in.io */	OneWay		in;	Block		*processed;	/* next bunch of application data */	Block		*unprocessed;	/* data read from c but not parsed into records */ 	/* handshake queue */	Lock		hqlock;			/* protects hqref, alloc & free of handq, hprocessed */	int		hqref;	Queue		*handq;		/* queue of handshake messages */	Block		*hprocessed;	/* remainder of last block read from handq */	QLock		hqread;		/* protects reads for hprocessed, handq */ 	/* output side */	OneWay		out; 	/* protections */	char		*user;	int		perm;}; struct TlsErrs{	int	err;	int	sslerr;	int	tlserr;	int	fatal;	char	*msg;}; static TlsErrs tlserrs[] = {	{ECloseNotify,			ECloseNotify,			ECloseNotify,			0, 	"close notify"},	{EUnexpectedMessage,	EUnexpectedMessage,	EUnexpectedMessage, 	1, "unexpected message"},	{EBadRecordMac,		EBadRecordMac,		EBadRecordMac, 		1, "bad record mac"},	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,		1, "decryption failed"},	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,		1, "record too long"},	{EDecompressionFailure,	EDecompressionFailure,	EDecompressionFailure,	1, "decompression failed"},	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,		1, "could not negotiate acceptable security paramters"},	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,	1, "no appropriate certificate available"},	{EBadCertificate,		EBadCertificate,		EBadCertificate,		1, "corrupted or invalid certificate"},	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,	1, "unsupported certificate type"},	{ECertificateRevoked,	ECertificateRevoked,		ECertificateRevoked,		1, "revoked certificate"},	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,		1, "expired certificate"},	{ECertificateUnknown,	ECertificateUnknown,	ECertificateUnknown,	1, "unacceptable certificate"},	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,		1, "illegal parameter"},	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,			1, "unknown certificate authority"},	{EAccessDenied,		EHandshakeFailure,		EAccessDenied,		1, "access denied"},	{EDecodeError,			EIllegalParameter,		EDecodeError,			1, "error decoding message"},	{EDecryptError,			EIllegalParameter,		EDecryptError,			1, "error decrypting message"},	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,		1, "export restriction violated"},	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,		1, "protocol version not supported"},	{EInsufficientSecurity,	EHandshakeFailure,		EInsufficientSecurity,	1, "stronger security routines required"},	{EInternalError,			EHandshakeFailure,		EInternalError,			1, "internal error"},	{EUserCanceled,		ECloseNotify,			EUserCanceled,			0, "handshake canceled by user"},	{ENoRenegotiation,		EUnexpectedMessage,	ENoRenegotiation,		0, "no renegotiation"},}; enum{	/* max. open tls connections */	MaxTlsDevs	= 1024}; static	Lock	tdlock;static	int	tdhiwat;static	int	maxtlsdevs = 128;static	TlsRec	**tlsdevs;static	char	**trnames;static	char	*encalgs;static	char	*hashalgs; enum{	Qtopdir		= 1,	/* top level directory */	Qprotodir,	Qclonus,	Qencalgs,	Qhashalgs,	Qconvdir,		/* directory for a conversation */	Qdata,	Qctl,	Qhand,	Qstatus,	Qstats,}; #define TYPE(x) 	((x).path & 0xf)#define CONV(x) 	(((x).path >> 5)&(MaxTlsDevs-1))#define QID(c, y) 	(((c)<<5) | (y)) static void	checkstate(TlsRec *, int, int);static void	ensure(TlsRec*, Block**, int);static void	consume(Block**, uchar*, int);static Chan*	buftochan(char*);static void	tlshangup(TlsRec*);static void	tlsError(TlsRec*, char *);static void	alertHand(TlsRec*, char *);static TlsRec	*newtls(Chan *c);static TlsRec	*mktlsrec(void);static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);static void	put64(uchar *p, vlong x);static void	put32(uchar *p, u32int);static void	put24(uchar *p, int);static void	put16(uchar *p, int);static u32int	get32(uchar *p);static int	get16(uchar *p);static void	tlsSetState(TlsRec *tr, int new, int old);static void	rcvAlert(TlsRec *tr, int err);static void	sendAlert(TlsRec *tr, int err);static void	rcvError(TlsRec *tr, int err, char *msg, ...);static int	rc4enc(Secret *sec, uchar *buf, int n);static int	des3enc(Secret *sec, uchar *buf, int n);static int	des3dec(Secret *sec, uchar *buf, int n);static int	noenc(Secret *sec, uchar *buf, int n);static int	sslunpad(uchar *buf, int n, int block);static int	tlsunpad(uchar *buf, int n, int block);static void	freeSec(Secret *sec);static char	*tlsstate(int s); #pragma	varargck	argpos	rcvError	3 static char *tlsnames[] = {[Qclonus]		"clone",[Qencalgs]	"encalgs",[Qhashalgs]	"hashalgs",[Qdata]		"data",[Qctl]		"ctl",[Qhand]		"hand",[Qstatus]		"status",[Qstats]		"stats",}; static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats }; static inttlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp){	Qid q;	TlsRec *tr;	char *name, *nm;	int perm, t; 	q.vers = 0;	q.type = QTFILE; 	t = TYPE(c->qid);	switch(t) {	case Qtopdir:		if(s == DEVDOTDOT){			q.path = QID(0, Qtopdir);			q.type = QTDIR;			devdir(c, q, "#a", 0, eve, 0555, dp);			return 1;		}		if(s > 0)			return -1;		q.path = QID(0, Qprotodir);		q.type = QTDIR;		devdir(c, q, "tls", 0, eve, 0555, dp);		return 1;	case Qprotodir:		if(s == DEVDOTDOT){			q.path = QID(0, Qtopdir);			q.type = QTDIR;			devdir(c, q, ".", 0, eve, 0555, dp);			return 1;		}		if(s < 3){			switch(s) {			default:				return -1;			case 0:				q.path = QID(0, Qclonus);				break;			case 1:				q.path = QID(0, Qencalgs);				break;			case 2:				q.path = QID(0, Qhashalgs);				break;			}			perm = 0444;			if(TYPE(q) == Qclonus)				perm = 0555;			devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);			return 1;		}		s -= 3;		if(s >= tdhiwat)			return -1;		q.path = QID(s, Qconvdir);		q.type = QTDIR;		lock(&tdlock);		tr = tlsdevs[s];		if(tr != nil)			nm = tr->user;		else			nm = eve;		if((name = trnames[s]) == nil) {			name = trnames[s] = smalloc(16);			sprint(name, "%d", s);		}		devdir(c, q, name, 0, nm, 0555, dp);		unlock(&tdlock);		return 1;	case Qconvdir:		if(s == DEVDOTDOT){			q.path = QID(0, Qprotodir);			q.type = QTDIR;			devdir(c, q, "tls", 0, eve, 0555, dp);			return 1;		}		if(s < 0 || s >= nelem(convdir))			return -1;		lock(&tdlock);		tr = tlsdevs[CONV(c->qid)];		if(tr != nil){			nm = tr->user;			perm = tr->perm;		}else{			perm = 0;			nm = eve;		}		t = convdir[s];		if(t == Qstatus || t == Qstats)			perm &= 0444;		q.path = QID(CONV(c->qid), t);		devdir(c, q, tlsnames[t], 0, nm, perm, dp);		unlock(&tdlock);		return 1;	case Qclonus:	case Qencalgs:	case Qhashalgs:		perm = 0444;		if(t == Qclonus)			perm = 0555;		devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);		return 1;	default:		lock(&tdlock);		tr = tlsdevs[CONV(c->qid)];		if(tr != nil){			nm = tr->user;			perm = tr->perm;		}else{			perm = 0;			nm = eve;		}		if(t == Qstatus || t == Qstats)			perm &= 0444;		devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);		unlock(&tdlock);		return 1;	}	return -1;} static Chan*tlsattach(char *spec){	Chan *c; 	c = devattach('a', spec);	c->qid.path = QID(0, Qtopdir);	c->qid.type = QTDIR;	c->qid.vers = 0;	return c;} static Walkqid*tlswalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, nil, 0, tlsgen);} static inttlsstat(Chan *c, uchar *db, int n){	return devstat(c, db, n, nil, 0, tlsgen);} static Chan*tlsopen(Chan *c, int omode){	TlsRec *tr, **pp;	int t, perm; 	perm = 0;	omode &= 3;	switch(omode) {	case OREAD:		perm = 4;		break;	case OWRITE:		perm = 2;		break;	case ORDWR:		perm = 6;		break;	} 	t = TYPE(c->qid);	switch(t) {	default:		panic("tlsopen");	case Qtopdir:	case Qprotodir:	case Qconvdir:		if(omode != OREAD)			error(Eperm);		break;	case Qclonus:		tr = newtls(c);		if(tr == nil)			error(Enodev);		break;	case Qctl:	case Qdata:	case Qhand:	case Qstatus:	case Qstats:		if((t == Qstatus || t == Qstats) && omode != OREAD)			error(Eperm);		if(waserror()) {			unlock(&tdlock);			nexterror();		}		lock(&tdlock);		pp = &tlsdevs[CONV(c->qid)];		tr = *pp;		if(tr == nil)			error("must open connection using clone");		if((perm & (tr->perm>>6)) != perm		&& (strcmp(up->user, tr->user) != 0		    || (perm & tr->perm) != perm))			error(Eperm);		if(t == Qhand){			if(waserror()){				unlock(&tr->hqlock);				nexterror();			}			lock(&tr->hqlock);			if(tr->handq != nil)				error(Einuse);			tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);			if(tr->handq == nil)				error("can't allocate handshake queue");			tr->hqref = 1;			unlock(&tr->hqlock);			poperror();		}		tr->ref++;		unlock(&tdlock);		poperror();		break;	case Qencalgs:	case Qhashalgs:		if(omode != OREAD)			error(Eperm);		break;	}	c->mode = openmode(omode);	c->flag |= COPEN;	c->offset = 0;	c->iounit = qiomaxatomic;	return c;} static int

Lines 1–500 of 2122.