|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <auth.h> | 
|  | #include <mp.h> | 
|  | #include <libsec.h> | 
|  |  | 
|  | /* The main groups of functions are: */ | 
|  | /*		client/server - main handshake protocol definition */ | 
|  | /*		message functions - formating handshake messages */ | 
|  | /*		cipher choices - catalog of digest and encrypt algorithms */ | 
|  | /*		security functions - PKCS#1, sslHMAC, session keygen */ | 
|  | /*		general utility functions - malloc, serialization */ | 
|  | /* The handshake protocol builds on the TLS/SSL3 record layer protocol, */ | 
|  | /* which is implemented in kernel device #a.  See also /lib/rfc/rfc2246. */ | 
|  |  | 
|  | enum { | 
|  | TLSFinishedLen = 12, | 
|  | SSL3FinishedLen = MD5dlen+SHA1dlen, | 
|  | MaxKeyData = 104,	/* amount of secret we may need */ | 
|  | MaxChunk = 1<<14, | 
|  | RandomSize = 32, | 
|  | SidSize = 32, | 
|  | MasterSecretSize = 48, | 
|  | AQueue = 0, | 
|  | AFlush = 1 | 
|  | }; | 
|  |  | 
|  | typedef struct TlsSec TlsSec; | 
|  |  | 
|  | typedef struct Bytes{ | 
|  | int len; | 
|  | uchar data[1];  /* [len] */ | 
|  | } Bytes; | 
|  |  | 
|  | typedef struct Ints{ | 
|  | int len; | 
|  | int data[1];  /* [len] */ | 
|  | } Ints; | 
|  |  | 
|  | typedef struct Algs{ | 
|  | char *enc; | 
|  | char *digest; | 
|  | int nsecret; | 
|  | int tlsid; | 
|  | int ok; | 
|  | } Algs; | 
|  |  | 
|  | typedef struct Finished{ | 
|  | uchar verify[SSL3FinishedLen]; | 
|  | int n; | 
|  | } Finished; | 
|  |  | 
|  | typedef struct TlsConnection{ | 
|  | TlsSec *sec;	/* security management goo */ | 
|  | int hand, ctl;	/* record layer file descriptors */ | 
|  | int erred;		/* set when tlsError called */ | 
|  | int (*trace)(char*fmt, ...); /* for debugging */ | 
|  | int version;	/* protocol we are speaking */ | 
|  | int verset;		/* version has been set */ | 
|  | int ver2hi;		/* server got a version 2 hello */ | 
|  | int isClient;	/* is this the client or server? */ | 
|  | Bytes *sid;		/* SessionID */ | 
|  | Bytes *cert;	/* only last - no chain */ | 
|  |  | 
|  | Lock statelk; | 
|  | int state;		/* must be set using setstate */ | 
|  |  | 
|  | /* input buffer for handshake messages */ | 
|  | uchar buf[MaxChunk+2048]; | 
|  | uchar *rp, *ep; | 
|  |  | 
|  | uchar crandom[RandomSize];	/* client random */ | 
|  | uchar srandom[RandomSize];	/* server random */ | 
|  | int clientVersion;	/* version in ClientHello */ | 
|  | char *digest;	/* name of digest algorithm to use */ | 
|  | char *enc;		/* name of encryption algorithm to use */ | 
|  | int nsecret;	/* amount of secret data to init keys */ | 
|  |  | 
|  | /* for finished messages */ | 
|  | MD5state	hsmd5;	/* handshake hash */ | 
|  | SHAstate	hssha1;	/* handshake hash */ | 
|  | Finished	finished; | 
|  | } TlsConnection; | 
|  |  | 
|  | typedef struct Msg{ | 
|  | int tag; | 
|  | union { | 
|  | struct { | 
|  | int version; | 
|  | uchar 	random[RandomSize]; | 
|  | Bytes*	sid; | 
|  | Ints*	ciphers; | 
|  | Bytes*	compressors; | 
|  | } clientHello; | 
|  | struct { | 
|  | int version; | 
|  | uchar 	random[RandomSize]; | 
|  | Bytes*	sid; | 
|  | int cipher; | 
|  | int compressor; | 
|  | } serverHello; | 
|  | struct { | 
|  | int ncert; | 
|  | Bytes **certs; | 
|  | } certificate; | 
|  | struct { | 
|  | Bytes *types; | 
|  | int nca; | 
|  | Bytes **cas; | 
|  | } certificateRequest; | 
|  | struct { | 
|  | Bytes *key; | 
|  | } clientKeyExchange; | 
|  | Finished finished; | 
|  | } u; | 
|  | } Msg; | 
|  |  | 
|  | struct TlsSec{ | 
|  | char *server;	/* name of remote; nil for server */ | 
|  | int ok;	/* <0 killed; ==0 in progress; >0 reusable */ | 
|  | RSApub *rsapub; | 
|  | AuthRpc *rpc;	/* factotum for rsa private key */ | 
|  | uchar sec[MasterSecretSize];	/* master secret */ | 
|  | uchar crandom[RandomSize];	/* client random */ | 
|  | uchar srandom[RandomSize];	/* server random */ | 
|  | int clientVers;		/* version in ClientHello */ | 
|  | int vers;			/* final version */ | 
|  | /* byte generation and handshake checksum */ | 
|  | void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); | 
|  | void (*setFinished)(TlsSec*, MD5state, SHAstate, uchar*, int); | 
|  | int nfin; | 
|  | }; | 
|  |  | 
|  |  | 
|  | enum { | 
|  | TLSVersion = 0x0301, | 
|  | SSL3Version = 0x0300, | 
|  | ProtocolVersion = 0x0301,	/* maximum version we speak */ | 
|  | MinProtoVersion = 0x0300,	/* limits on version we accept */ | 
|  | MaxProtoVersion	= 0x03ff | 
|  | }; | 
|  |  | 
|  | /* handshake type */ | 
|  | enum { | 
|  | HHelloRequest, | 
|  | HClientHello, | 
|  | HServerHello, | 
|  | HSSL2ClientHello = 9,  /* local convention;  see devtls.c */ | 
|  | HCertificate = 11, | 
|  | HServerKeyExchange, | 
|  | HCertificateRequest, | 
|  | HServerHelloDone, | 
|  | HCertificateVerify, | 
|  | HClientKeyExchange, | 
|  | HFinished = 20, | 
|  | HMax | 
|  | }; | 
|  |  | 
|  | /* alerts */ | 
|  | enum { | 
|  | 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 | 
|  | }; | 
|  |  | 
|  | /* cipher suites */ | 
|  | enum { | 
|  | TLS_NULL_WITH_NULL_NULL	 		= 0x0000, | 
|  | TLS_RSA_WITH_NULL_MD5 			= 0x0001, | 
|  | TLS_RSA_WITH_NULL_SHA 			= 0x0002, | 
|  | TLS_RSA_EXPORT_WITH_RC4_40_MD5 		= 0x0003, | 
|  | TLS_RSA_WITH_RC4_128_MD5 		= 0x0004, | 
|  | TLS_RSA_WITH_RC4_128_SHA 		= 0x0005, | 
|  | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5	= 0X0006, | 
|  | TLS_RSA_WITH_IDEA_CBC_SHA 		= 0X0007, | 
|  | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X0008, | 
|  | TLS_RSA_WITH_DES_CBC_SHA		= 0X0009, | 
|  | TLS_RSA_WITH_3DES_EDE_CBC_SHA		= 0X000A, | 
|  | TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA	= 0X000B, | 
|  | TLS_DH_DSS_WITH_DES_CBC_SHA		= 0X000C, | 
|  | TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA	= 0X000D, | 
|  | TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X000E, | 
|  | TLS_DH_RSA_WITH_DES_CBC_SHA		= 0X000F, | 
|  | TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA	= 0X0010, | 
|  | TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA	= 0X0011, | 
|  | TLS_DHE_DSS_WITH_DES_CBC_SHA		= 0X0012, | 
|  | TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA	= 0X0013,	/* ZZZ must be implemented for tls1.0 compliance */ | 
|  | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA	= 0X0014, | 
|  | TLS_DHE_RSA_WITH_DES_CBC_SHA		= 0X0015, | 
|  | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA	= 0X0016, | 
|  | TLS_DH_anon_EXPORT_WITH_RC4_40_MD5	= 0x0017, | 
|  | TLS_DH_anon_WITH_RC4_128_MD5 		= 0x0018, | 
|  | TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA	= 0X0019, | 
|  | TLS_DH_anon_WITH_DES_CBC_SHA		= 0X001A, | 
|  | TLS_DH_anon_WITH_3DES_EDE_CBC_SHA	= 0X001B, | 
|  |  | 
|  | TLS_RSA_WITH_AES_128_CBC_SHA		= 0X002f,	/* aes, aka rijndael with 128 bit blocks */ | 
|  | TLS_DH_DSS_WITH_AES_128_CBC_SHA		= 0X0030, | 
|  | TLS_DH_RSA_WITH_AES_128_CBC_SHA		= 0X0031, | 
|  | TLS_DHE_DSS_WITH_AES_128_CBC_SHA	= 0X0032, | 
|  | TLS_DHE_RSA_WITH_AES_128_CBC_SHA	= 0X0033, | 
|  | TLS_DH_anon_WITH_AES_128_CBC_SHA	= 0X0034, | 
|  | TLS_RSA_WITH_AES_256_CBC_SHA		= 0X0035, | 
|  | TLS_DH_DSS_WITH_AES_256_CBC_SHA		= 0X0036, | 
|  | TLS_DH_RSA_WITH_AES_256_CBC_SHA		= 0X0037, | 
|  | TLS_DHE_DSS_WITH_AES_256_CBC_SHA	= 0X0038, | 
|  | TLS_DHE_RSA_WITH_AES_256_CBC_SHA	= 0X0039, | 
|  | TLS_DH_anon_WITH_AES_256_CBC_SHA	= 0X003A, | 
|  | CipherMax | 
|  | }; | 
|  |  | 
|  | /* compression methods */ | 
|  | enum { | 
|  | CompressionNull = 0, | 
|  | CompressionMax | 
|  | }; | 
|  |  | 
|  | static Algs cipherAlgs[] = { | 
|  | {"rc4_128", "md5",	2 * (16 + MD5dlen), TLS_RSA_WITH_RC4_128_MD5}, | 
|  | {"rc4_128", "sha1",	2 * (16 + SHA1dlen), TLS_RSA_WITH_RC4_128_SHA}, | 
|  | {"3des_ede_cbc","sha1",2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, | 
|  | }; | 
|  |  | 
|  | static uchar compressors[] = { | 
|  | CompressionNull, | 
|  | }; | 
|  |  | 
|  | static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chain); | 
|  | static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)); | 
|  |  | 
|  | static void	msgClear(Msg *m); | 
|  | static char* msgPrint(char *buf, int n, Msg *m); | 
|  | static int	msgRecv(TlsConnection *c, Msg *m); | 
|  | static int	msgSend(TlsConnection *c, Msg *m, int act); | 
|  | static void	tlsError(TlsConnection *c, int err, char *msg, ...); | 
|  | /* #pragma	varargck argpos	tlsError 3*/ | 
|  | static int setVersion(TlsConnection *c, int version); | 
|  | static int finishedMatch(TlsConnection *c, Finished *f); | 
|  | static void tlsConnectionFree(TlsConnection *c); | 
|  |  | 
|  | static int setAlgs(TlsConnection *c, int a); | 
|  | static int okCipher(Ints *cv); | 
|  | static int okCompression(Bytes *cv); | 
|  | static int initCiphers(void); | 
|  | static Ints* makeciphers(void); | 
|  |  | 
|  | static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom); | 
|  | static int	tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd); | 
|  | static TlsSec*	tlsSecInitc(int cvers, uchar *crandom); | 
|  | static int	tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd); | 
|  | static int	tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient); | 
|  | static void	tlsSecOk(TlsSec *sec); | 
|  | /* static void	tlsSecKill(TlsSec *sec); */ | 
|  | static void	tlsSecClose(TlsSec *sec); | 
|  | static void	setMasterSecret(TlsSec *sec, Bytes *pm); | 
|  | static void	serverMasterSecret(TlsSec *sec, uchar *epm, int nepm); | 
|  | static void	setSecrets(TlsSec *sec, uchar *kd, int nkd); | 
|  | static int	clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm); | 
|  | static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype); | 
|  | static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm); | 
|  | static void	tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); | 
|  | static void	sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); | 
|  | static void	sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, | 
|  | uchar *seed0, int nseed0, uchar *seed1, int nseed1); | 
|  | static int setVers(TlsSec *sec, int version); | 
|  |  | 
|  | static AuthRpc* factotum_rsa_open(uchar *cert, int certlen); | 
|  | static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher); | 
|  | static void factotum_rsa_close(AuthRpc*rpc); | 
|  |  | 
|  | static void* emalloc(int); | 
|  | static void* erealloc(void*, int); | 
|  | 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 get24(uchar *p); | 
|  | static int get16(uchar *p); | 
|  | static Bytes* newbytes(int len); | 
|  | static Bytes* makebytes(uchar* buf, int len); | 
|  | static void freebytes(Bytes* b); | 
|  | static Ints* newints(int len); | 
|  | /* static Ints* makeints(int* buf, int len); */ | 
|  | static void freeints(Ints* b); | 
|  |  | 
|  | /*================= client/server ======================== */ | 
|  |  | 
|  | /*	push TLS onto fd, returning new (application) file descriptor */ | 
|  | /*		or -1 if error. */ | 
|  | int | 
|  | tlsServer(int fd, TLSconn *conn) | 
|  | { | 
|  | char buf[8]; | 
|  | char dname[64]; | 
|  | int n, data, ctl, hand; | 
|  | TlsConnection *tls; | 
|  |  | 
|  | if(conn == nil) | 
|  | return -1; | 
|  | ctl = open("#a/tls/clone", ORDWR); | 
|  | if(ctl < 0) | 
|  | return -1; | 
|  | n = read(ctl, buf, sizeof(buf)-1); | 
|  | if(n < 0){ | 
|  | close(ctl); | 
|  | return -1; | 
|  | } | 
|  | buf[n] = 0; | 
|  | sprint(conn->dir, "#a/tls/%s", buf); | 
|  | sprint(dname, "#a/tls/%s/hand", buf); | 
|  | hand = open(dname, ORDWR); | 
|  | if(hand < 0){ | 
|  | close(ctl); | 
|  | return -1; | 
|  | } | 
|  | fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); | 
|  | tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace, conn->chain); | 
|  | sprint(dname, "#a/tls/%s/data", buf); | 
|  | data = open(dname, ORDWR); | 
|  | close(fd); | 
|  | close(hand); | 
|  | close(ctl); | 
|  | if(data < 0){ | 
|  | return -1; | 
|  | } | 
|  | if(tls == nil){ | 
|  | close(data); | 
|  | return -1; | 
|  | } | 
|  | if(conn->cert) | 
|  | free(conn->cert); | 
|  | conn->cert = 0;  /* client certificates are not yet implemented */ | 
|  | conn->certlen = 0; | 
|  | conn->sessionIDlen = tls->sid->len; | 
|  | conn->sessionID = emalloc(conn->sessionIDlen); | 
|  | memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); | 
|  | tlsConnectionFree(tls); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | /*	push TLS onto fd, returning new (application) file descriptor */ | 
|  | /*		or -1 if error. */ | 
|  | int | 
|  | tlsClient(int fd, TLSconn *conn) | 
|  | { | 
|  | char buf[8]; | 
|  | char dname[64]; | 
|  | int n, data, ctl, hand; | 
|  | TlsConnection *tls; | 
|  |  | 
|  | if(!conn) | 
|  | return -1; | 
|  | ctl = open("#a/tls/clone", ORDWR); | 
|  | if(ctl < 0) | 
|  | return -1; | 
|  | n = read(ctl, buf, sizeof(buf)-1); | 
|  | if(n < 0){ | 
|  | close(ctl); | 
|  | return -1; | 
|  | } | 
|  | buf[n] = 0; | 
|  | sprint(conn->dir, "#a/tls/%s", buf); | 
|  | sprint(dname, "#a/tls/%s/hand", buf); | 
|  | hand = open(dname, ORDWR); | 
|  | if(hand < 0){ | 
|  | close(ctl); | 
|  | return -1; | 
|  | } | 
|  | sprint(dname, "#a/tls/%s/data", buf); | 
|  | data = open(dname, ORDWR); | 
|  | if(data < 0) | 
|  | return -1; | 
|  | fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); | 
|  | tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace); | 
|  | close(fd); | 
|  | close(hand); | 
|  | close(ctl); | 
|  | if(tls == nil){ | 
|  | close(data); | 
|  | return -1; | 
|  | } | 
|  | conn->certlen = tls->cert->len; | 
|  | conn->cert = emalloc(conn->certlen); | 
|  | memcpy(conn->cert, tls->cert->data, conn->certlen); | 
|  | conn->sessionIDlen = tls->sid->len; | 
|  | conn->sessionID = emalloc(conn->sessionIDlen); | 
|  | memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); | 
|  | tlsConnectionFree(tls); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | static int | 
|  | countchain(PEMChain *p) | 
|  | { | 
|  | int i = 0; | 
|  |  | 
|  | while (p) { | 
|  | i++; | 
|  | p = p->next; | 
|  | } | 
|  | return i; | 
|  | } | 
|  |  | 
|  | static TlsConnection * | 
|  | tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chp) | 
|  | { | 
|  | TlsConnection *c; | 
|  | Msg m; | 
|  | Bytes *csid; | 
|  | uchar sid[SidSize], kd[MaxKeyData]; | 
|  | char *secrets; | 
|  | int cipher, compressor, nsid, rv, numcerts, i; | 
|  |  | 
|  | if(trace) | 
|  | trace("tlsServer2\n"); | 
|  | if(!initCiphers()) | 
|  | return nil; | 
|  | c = emalloc(sizeof(TlsConnection)); | 
|  | c->ctl = ctl; | 
|  | c->hand = hand; | 
|  | c->trace = trace; | 
|  | c->version = ProtocolVersion; | 
|  |  | 
|  | memset(&m, 0, sizeof(m)); | 
|  | if(!msgRecv(c, &m)){ | 
|  | if(trace) | 
|  | trace("initial msgRecv failed\n"); | 
|  | goto Err; | 
|  | } | 
|  | if(m.tag != HClientHello) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a client hello"); | 
|  | goto Err; | 
|  | } | 
|  | c->clientVersion = m.u.clientHello.version; | 
|  | if(trace) | 
|  | trace("ClientHello version %x\n", c->clientVersion); | 
|  | if(setVersion(c, m.u.clientHello.version) < 0) { | 
|  | tlsError(c, EIllegalParameter, "incompatible version"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | memmove(c->crandom, m.u.clientHello.random, RandomSize); | 
|  | cipher = okCipher(m.u.clientHello.ciphers); | 
|  | if(cipher < 0) { | 
|  | /* reply with EInsufficientSecurity if we know that's the case */ | 
|  | if(cipher == -2) | 
|  | tlsError(c, EInsufficientSecurity, "cipher suites too weak"); | 
|  | else | 
|  | tlsError(c, EHandshakeFailure, "no matching cipher suite"); | 
|  | goto Err; | 
|  | } | 
|  | if(!setAlgs(c, cipher)){ | 
|  | tlsError(c, EHandshakeFailure, "no matching cipher suite"); | 
|  | goto Err; | 
|  | } | 
|  | compressor = okCompression(m.u.clientHello.compressors); | 
|  | if(compressor < 0) { | 
|  | tlsError(c, EHandshakeFailure, "no matching compressor"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | csid = m.u.clientHello.sid; | 
|  | if(trace) | 
|  | trace("  cipher %d, compressor %d, csidlen %d\n", cipher, compressor, csid->len); | 
|  | c->sec = tlsSecInits(c->clientVersion, csid->data, csid->len, c->crandom, sid, &nsid, c->srandom); | 
|  | if(c->sec == nil){ | 
|  | tlsError(c, EHandshakeFailure, "can't initialize security: %r"); | 
|  | goto Err; | 
|  | } | 
|  | c->sec->rpc = factotum_rsa_open(cert, ncert); | 
|  | if(c->sec->rpc == nil){ | 
|  | tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r"); | 
|  | goto Err; | 
|  | } | 
|  | c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0); | 
|  | msgClear(&m); | 
|  |  | 
|  | m.tag = HServerHello; | 
|  | m.u.serverHello.version = c->version; | 
|  | memmove(m.u.serverHello.random, c->srandom, RandomSize); | 
|  | m.u.serverHello.cipher = cipher; | 
|  | m.u.serverHello.compressor = compressor; | 
|  | c->sid = makebytes(sid, nsid); | 
|  | m.u.serverHello.sid = makebytes(c->sid->data, c->sid->len); | 
|  | if(!msgSend(c, &m, AQueue)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  |  | 
|  | m.tag = HCertificate; | 
|  | numcerts = countchain(chp); | 
|  | m.u.certificate.ncert = 1 + numcerts; | 
|  | m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); | 
|  | m.u.certificate.certs[0] = makebytes(cert, ncert); | 
|  | for (i = 0; i < numcerts && chp; i++, chp = chp->next) | 
|  | m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen); | 
|  | if(!msgSend(c, &m, AQueue)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  |  | 
|  | m.tag = HServerHelloDone; | 
|  | if(!msgSend(c, &m, AFlush)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  |  | 
|  | if(!msgRecv(c, &m)) | 
|  | goto Err; | 
|  | if(m.tag != HClientKeyExchange) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a client key exchange"); | 
|  | goto Err; | 
|  | } | 
|  | if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){ | 
|  | tlsError(c, EHandshakeFailure, "couldn't set secrets: %r"); | 
|  | goto Err; | 
|  | } | 
|  | if(trace) | 
|  | trace("tls secrets\n"); | 
|  | secrets = (char*)emalloc(2*c->nsecret); | 
|  | enc64(secrets, 2*c->nsecret, kd, c->nsecret); | 
|  | rv = fprint(c->ctl, "secret %s %s 0 %s", c->digest, c->enc, secrets); | 
|  | memset(secrets, 0, 2*c->nsecret); | 
|  | free(secrets); | 
|  | memset(kd, 0, c->nsecret); | 
|  | if(rv < 0){ | 
|  | tlsError(c, EHandshakeFailure, "can't set keys: %r"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | /* no CertificateVerify; skip to Finished */ | 
|  | if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ | 
|  | tlsError(c, EInternalError, "can't set finished: %r"); | 
|  | goto Err; | 
|  | } | 
|  | if(!msgRecv(c, &m)) | 
|  | goto Err; | 
|  | if(m.tag != HFinished) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a finished"); | 
|  | goto Err; | 
|  | } | 
|  | if(!finishedMatch(c, &m.u.finished)) { | 
|  | tlsError(c, EHandshakeFailure, "finished verification failed"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | /* change cipher spec */ | 
|  | if(fprint(c->ctl, "changecipher") < 0){ | 
|  | tlsError(c, EInternalError, "can't enable cipher: %r"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ | 
|  | tlsError(c, EInternalError, "can't set finished: %r"); | 
|  | goto Err; | 
|  | } | 
|  | m.tag = HFinished; | 
|  | m.u.finished = c->finished; | 
|  | if(!msgSend(c, &m, AFlush)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  | if(trace) | 
|  | trace("tls finished\n"); | 
|  |  | 
|  | if(fprint(c->ctl, "opened") < 0) | 
|  | goto Err; | 
|  | tlsSecOk(c->sec); | 
|  | return c; | 
|  |  | 
|  | Err: | 
|  | msgClear(&m); | 
|  | tlsConnectionFree(c); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static TlsConnection * | 
|  | tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)) | 
|  | { | 
|  | TlsConnection *c; | 
|  | Msg m; | 
|  | uchar kd[MaxKeyData], *epm; | 
|  | char *secrets; | 
|  | int creq, nepm, rv; | 
|  |  | 
|  | if(!initCiphers()) | 
|  | return nil; | 
|  | epm = nil; | 
|  | c = emalloc(sizeof(TlsConnection)); | 
|  | c->version = ProtocolVersion; | 
|  | c->ctl = ctl; | 
|  | c->hand = hand; | 
|  | c->trace = trace; | 
|  | c->isClient = 1; | 
|  | c->clientVersion = c->version; | 
|  |  | 
|  | c->sec = tlsSecInitc(c->clientVersion, c->crandom); | 
|  | if(c->sec == nil) | 
|  | goto Err; | 
|  |  | 
|  | /* client hello */ | 
|  | memset(&m, 0, sizeof(m)); | 
|  | m.tag = HClientHello; | 
|  | m.u.clientHello.version = c->clientVersion; | 
|  | memmove(m.u.clientHello.random, c->crandom, RandomSize); | 
|  | m.u.clientHello.sid = makebytes(csid, ncsid); | 
|  | m.u.clientHello.ciphers = makeciphers(); | 
|  | m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors)); | 
|  | if(!msgSend(c, &m, AFlush)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  |  | 
|  | /* server hello */ | 
|  | if(!msgRecv(c, &m)) | 
|  | goto Err; | 
|  | if(m.tag != HServerHello) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a server hello"); | 
|  | goto Err; | 
|  | } | 
|  | if(setVersion(c, m.u.serverHello.version) < 0) { | 
|  | tlsError(c, EIllegalParameter, "incompatible version %r"); | 
|  | goto Err; | 
|  | } | 
|  | memmove(c->srandom, m.u.serverHello.random, RandomSize); | 
|  | c->sid = makebytes(m.u.serverHello.sid->data, m.u.serverHello.sid->len); | 
|  | if(c->sid->len != 0 && c->sid->len != SidSize) { | 
|  | tlsError(c, EIllegalParameter, "invalid server session identifier"); | 
|  | goto Err; | 
|  | } | 
|  | if(!setAlgs(c, m.u.serverHello.cipher)) { | 
|  | tlsError(c, EIllegalParameter, "invalid cipher suite"); | 
|  | goto Err; | 
|  | } | 
|  | if(m.u.serverHello.compressor != CompressionNull) { | 
|  | tlsError(c, EIllegalParameter, "invalid compression"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | /* certificate */ | 
|  | if(!msgRecv(c, &m) || m.tag != HCertificate) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a certificate"); | 
|  | goto Err; | 
|  | } | 
|  | if(m.u.certificate.ncert < 1) { | 
|  | tlsError(c, EIllegalParameter, "runt certificate"); | 
|  | goto Err; | 
|  | } | 
|  | c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len); | 
|  | msgClear(&m); | 
|  |  | 
|  | /* server key exchange (optional) */ | 
|  | if(!msgRecv(c, &m)) | 
|  | goto Err; | 
|  | if(m.tag == HServerKeyExchange) { | 
|  | tlsError(c, EUnexpectedMessage, "got an server key exchange"); | 
|  | goto Err; | 
|  | /* If implementing this later, watch out for rollback attack */ | 
|  | /* described in Wagner Schneier 1996, section 4.4. */ | 
|  | } | 
|  |  | 
|  | /* certificate request (optional) */ | 
|  | creq = 0; | 
|  | if(m.tag == HCertificateRequest) { | 
|  | creq = 1; | 
|  | msgClear(&m); | 
|  | if(!msgRecv(c, &m)) | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | if(m.tag != HServerHelloDone) { | 
|  | tlsError(c, EUnexpectedMessage, "expected a server hello done"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom, | 
|  | c->cert->data, c->cert->len, c->version, &epm, &nepm, | 
|  | kd, c->nsecret) < 0){ | 
|  | tlsError(c, EBadCertificate, "invalid x509/rsa certificate"); | 
|  | goto Err; | 
|  | } | 
|  | secrets = (char*)emalloc(2*c->nsecret); | 
|  | enc64(secrets, 2*c->nsecret, kd, c->nsecret); | 
|  | rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets); | 
|  | memset(secrets, 0, 2*c->nsecret); | 
|  | free(secrets); | 
|  | memset(kd, 0, c->nsecret); | 
|  | if(rv < 0){ | 
|  | tlsError(c, EHandshakeFailure, "can't set keys: %r"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | if(creq) { | 
|  | /* send a zero length certificate */ | 
|  | m.tag = HCertificate; | 
|  | if(!msgSend(c, &m, AFlush)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  | } | 
|  |  | 
|  | /* client key exchange */ | 
|  | m.tag = HClientKeyExchange; | 
|  | m.u.clientKeyExchange.key = makebytes(epm, nepm); | 
|  | free(epm); | 
|  | epm = nil; | 
|  | if(m.u.clientKeyExchange.key == nil) { | 
|  | tlsError(c, EHandshakeFailure, "can't set secret: %r"); | 
|  | goto Err; | 
|  | } | 
|  | if(!msgSend(c, &m, AFlush)) | 
|  | goto Err; | 
|  | msgClear(&m); | 
|  |  | 
|  | /* change cipher spec */ | 
|  | if(fprint(c->ctl, "changecipher") < 0){ | 
|  | tlsError(c, EInternalError, "can't enable cipher: %r"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | /* Cipherchange must occur immediately before Finished to avoid */ | 
|  | /* potential hole;  see section 4.3 of Wagner Schneier 1996. */ | 
|  | if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ | 
|  | tlsError(c, EInternalError, "can't set finished 1: %r"); | 
|  | goto Err; | 
|  | } | 
|  | m.tag = HFinished; | 
|  | m.u.finished = c->finished; | 
|  |  | 
|  | if(!msgSend(c, &m, AFlush)) { | 
|  | fprint(2, "tlsClient nepm=%d\n", nepm); | 
|  | tlsError(c, EInternalError, "can't flush after client Finished: %r"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ | 
|  | fprint(2, "tlsClient nepm=%d\n", nepm); | 
|  | tlsError(c, EInternalError, "can't set finished 0: %r"); | 
|  | goto Err; | 
|  | } | 
|  | if(!msgRecv(c, &m)) { | 
|  | fprint(2, "tlsClient nepm=%d\n", nepm); | 
|  | tlsError(c, EInternalError, "can't read server Finished: %r"); | 
|  | goto Err; | 
|  | } | 
|  | if(m.tag != HFinished) { | 
|  | fprint(2, "tlsClient nepm=%d\n", nepm); | 
|  | tlsError(c, EUnexpectedMessage, "expected a Finished msg from server"); | 
|  | goto Err; | 
|  | } | 
|  |  | 
|  | if(!finishedMatch(c, &m.u.finished)) { | 
|  | tlsError(c, EHandshakeFailure, "finished verification failed"); | 
|  | goto Err; | 
|  | } | 
|  | msgClear(&m); | 
|  |  | 
|  | if(fprint(c->ctl, "opened") < 0){ | 
|  | if(trace) | 
|  | trace("unable to do final open: %r\n"); | 
|  | goto Err; | 
|  | } | 
|  | tlsSecOk(c->sec); | 
|  | return c; | 
|  |  | 
|  | Err: | 
|  | free(epm); | 
|  | msgClear(&m); | 
|  | tlsConnectionFree(c); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*================= message functions ======================== */ | 
|  |  | 
|  | static uchar sendbuf[9000], *sendp; | 
|  |  | 
|  | static int | 
|  | msgSend(TlsConnection *c, Msg *m, int act) | 
|  | { | 
|  | uchar *p; /* sendp = start of new message;  p = write pointer */ | 
|  | int nn, n, i; | 
|  |  | 
|  | if(sendp == nil) | 
|  | sendp = sendbuf; | 
|  | p = sendp; | 
|  | if(c->trace) | 
|  | c->trace("send %s", msgPrint((char*)p, (sizeof sendbuf) - (p-sendbuf), m)); | 
|  |  | 
|  | p[0] = m->tag;	/* header - fill in size later */ | 
|  | p += 4; | 
|  |  | 
|  | switch(m->tag) { | 
|  | default: | 
|  | tlsError(c, EInternalError, "can't encode a %d", m->tag); | 
|  | goto Err; | 
|  | case HClientHello: | 
|  | /* version */ | 
|  | put16(p, m->u.clientHello.version); | 
|  | p += 2; | 
|  |  | 
|  | /* random */ | 
|  | memmove(p, m->u.clientHello.random, RandomSize); | 
|  | p += RandomSize; | 
|  |  | 
|  | /* sid */ | 
|  | n = m->u.clientHello.sid->len; | 
|  | assert(n < 256); | 
|  | p[0] = n; | 
|  | memmove(p+1, m->u.clientHello.sid->data, n); | 
|  | p += n+1; | 
|  |  | 
|  | n = m->u.clientHello.ciphers->len; | 
|  | assert(n > 0 && n < 200); | 
|  | put16(p, n*2); | 
|  | p += 2; | 
|  | for(i=0; i<n; i++) { | 
|  | put16(p, m->u.clientHello.ciphers->data[i]); | 
|  | p += 2; | 
|  | } | 
|  |  | 
|  | n = m->u.clientHello.compressors->len; | 
|  | assert(n > 0); | 
|  | p[0] = n; | 
|  | memmove(p+1, m->u.clientHello.compressors->data, n); | 
|  | p += n+1; | 
|  | break; | 
|  | case HServerHello: | 
|  | put16(p, m->u.serverHello.version); | 
|  | p += 2; | 
|  |  | 
|  | /* random */ | 
|  | memmove(p, m->u.serverHello.random, RandomSize); | 
|  | p += RandomSize; | 
|  |  | 
|  | /* sid */ | 
|  | n = m->u.serverHello.sid->len; | 
|  | assert(n < 256); | 
|  | p[0] = n; | 
|  | memmove(p+1, m->u.serverHello.sid->data, n); | 
|  | p += n+1; | 
|  |  | 
|  | put16(p, m->u.serverHello.cipher); | 
|  | p += 2; | 
|  | p[0] = m->u.serverHello.compressor; | 
|  | p += 1; | 
|  | break; | 
|  | case HServerHelloDone: | 
|  | break; | 
|  | case HCertificate: | 
|  | nn = 0; | 
|  | for(i = 0; i < m->u.certificate.ncert; i++) | 
|  | nn += 3 + m->u.certificate.certs[i]->len; | 
|  | if(p + 3 + nn - sendbuf > sizeof(sendbuf)) { | 
|  | tlsError(c, EInternalError, "output buffer too small for certificate"); | 
|  | goto Err; | 
|  | } | 
|  | put24(p, nn); | 
|  | p += 3; | 
|  | for(i = 0; i < m->u.certificate.ncert; i++){ | 
|  | put24(p, m->u.certificate.certs[i]->len); | 
|  | p += 3; | 
|  | memmove(p, m->u.certificate.certs[i]->data, m->u.certificate.certs[i]->len); | 
|  | p += m->u.certificate.certs[i]->len; | 
|  | } | 
|  | break; | 
|  | case HClientKeyExchange: | 
|  | n = m->u.clientKeyExchange.key->len; | 
|  | if(c->version != SSL3Version){ | 
|  | put16(p, n); | 
|  | p += 2; | 
|  | } | 
|  | memmove(p, m->u.clientKeyExchange.key->data, n); | 
|  | p += n; | 
|  | break; | 
|  | case HFinished: | 
|  | memmove(p, m->u.finished.verify, m->u.finished.n); | 
|  | p += m->u.finished.n; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* go back and fill in size */ | 
|  | n = p-sendp; | 
|  | assert(p <= sendbuf+sizeof(sendbuf)); | 
|  | put24(sendp+1, n-4); | 
|  |  | 
|  | /* remember hash of Handshake messages */ | 
|  | if(m->tag != HHelloRequest) { | 
|  | md5(sendp, n, 0, &c->hsmd5); | 
|  | sha1(sendp, n, 0, &c->hssha1); | 
|  | } | 
|  |  | 
|  | sendp = p; | 
|  | if(act == AFlush){ | 
|  | sendp = sendbuf; | 
|  | if(write(c->hand, sendbuf, p-sendbuf) < 0){ | 
|  | fprint(2, "write error: %r\n"); | 
|  | goto Err; | 
|  | } | 
|  | } | 
|  | msgClear(m); | 
|  | return 1; | 
|  | Err: | 
|  | msgClear(m); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uchar* | 
|  | tlsReadN(TlsConnection *c, int n) | 
|  | { | 
|  | uchar *p; | 
|  | int nn, nr; | 
|  |  | 
|  | nn = c->ep - c->rp; | 
|  | if(nn < n){ | 
|  | if(c->rp != c->buf){ | 
|  | memmove(c->buf, c->rp, nn); | 
|  | c->rp = c->buf; | 
|  | c->ep = &c->buf[nn]; | 
|  | } | 
|  | for(; nn < n; nn += nr) { | 
|  | nr = read(c->hand, &c->rp[nn], n - nn); | 
|  | if(nr <= 0) | 
|  | return nil; | 
|  | c->ep += nr; | 
|  | } | 
|  | } | 
|  | p = c->rp; | 
|  | c->rp += n; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | msgRecv(TlsConnection *c, Msg *m) | 
|  | { | 
|  | uchar *p; | 
|  | int type, n, nn, i, nsid, nrandom, nciph; | 
|  |  | 
|  | for(;;) { | 
|  | p = tlsReadN(c, 4); | 
|  | if(p == nil) | 
|  | return 0; | 
|  | type = p[0]; | 
|  | n = get24(p+1); | 
|  |  | 
|  | if(type != HHelloRequest) | 
|  | break; | 
|  | if(n != 0) { | 
|  | tlsError(c, EDecodeError, "invalid hello request during handshake"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(n > sizeof(c->buf)) { | 
|  | tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->buf)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if(type == HSSL2ClientHello){ | 
|  | /* Cope with an SSL3 ClientHello expressed in SSL2 record format. | 
|  | This is sent by some clients that we must interoperate | 
|  | with, such as Java's JSSE and Microsoft's Internet Explorer. */ | 
|  | p = tlsReadN(c, n); | 
|  | if(p == nil) | 
|  | return 0; | 
|  | md5(p, n, 0, &c->hsmd5); | 
|  | sha1(p, n, 0, &c->hssha1); | 
|  | m->tag = HClientHello; | 
|  | if(n < 22) | 
|  | goto Short; | 
|  | m->u.clientHello.version = get16(p+1); | 
|  | p += 3; | 
|  | n -= 3; | 
|  | nn = get16(p); /* cipher_spec_len */ | 
|  | nsid = get16(p + 2); | 
|  | nrandom = get16(p + 4); | 
|  | p += 6; | 
|  | n -= 6; | 
|  | if(nsid != 0 	/* no sid's, since shouldn't restart using ssl2 header */ | 
|  | || nrandom < 16 || nn % 3) | 
|  | goto Err; | 
|  | if(c->trace && (n - nrandom != nn)) | 
|  | c->trace("n-nrandom!=nn: n=%d nrandom=%d nn=%d\n", n, nrandom, nn); | 
|  | /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */ | 
|  | nciph = 0; | 
|  | for(i = 0; i < nn; i += 3) | 
|  | if(p[i] == 0) | 
|  | nciph++; | 
|  | m->u.clientHello.ciphers = newints(nciph); | 
|  | nciph = 0; | 
|  | for(i = 0; i < nn; i += 3) | 
|  | if(p[i] == 0) | 
|  | m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]); | 
|  | p += nn; | 
|  | m->u.clientHello.sid = makebytes(nil, 0); | 
|  | if(nrandom > RandomSize) | 
|  | nrandom = RandomSize; | 
|  | memset(m->u.clientHello.random, 0, RandomSize - nrandom); | 
|  | memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom); | 
|  | m->u.clientHello.compressors = newbytes(1); | 
|  | m->u.clientHello.compressors->data[0] = CompressionNull; | 
|  | goto Ok; | 
|  | } | 
|  |  | 
|  | md5(p, 4, 0, &c->hsmd5); | 
|  | sha1(p, 4, 0, &c->hssha1); | 
|  |  | 
|  | p = tlsReadN(c, n); | 
|  | if(p == nil) | 
|  | return 0; | 
|  |  | 
|  | md5(p, n, 0, &c->hsmd5); | 
|  | sha1(p, n, 0, &c->hssha1); | 
|  |  | 
|  | m->tag = type; | 
|  |  | 
|  | switch(type) { | 
|  | default: | 
|  | tlsError(c, EUnexpectedMessage, "can't decode a %d", type); | 
|  | goto Err; | 
|  | case HClientHello: | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | m->u.clientHello.version = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  |  | 
|  | if(n < RandomSize) | 
|  | goto Short; | 
|  | memmove(m->u.clientHello.random, p, RandomSize); | 
|  | p += RandomSize; | 
|  | n -= RandomSize; | 
|  | if(n < 1 || n < p[0]+1) | 
|  | goto Short; | 
|  | m->u.clientHello.sid = makebytes(p+1, p[0]); | 
|  | p += m->u.clientHello.sid->len+1; | 
|  | n -= m->u.clientHello.sid->len+1; | 
|  |  | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | nn = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  |  | 
|  | if((nn & 1) || n < nn || nn < 2) | 
|  | goto Short; | 
|  | m->u.clientHello.ciphers = newints(nn >> 1); | 
|  | for(i = 0; i < nn; i += 2) | 
|  | m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]); | 
|  | p += nn; | 
|  | n -= nn; | 
|  |  | 
|  | if(n < 1 || n < p[0]+1 || p[0] == 0) | 
|  | goto Short; | 
|  | nn = p[0]; | 
|  | m->u.clientHello.compressors = newbytes(nn); | 
|  | memmove(m->u.clientHello.compressors->data, p+1, nn); | 
|  | n -= nn + 1; | 
|  | break; | 
|  | case HServerHello: | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | m->u.serverHello.version = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  |  | 
|  | if(n < RandomSize) | 
|  | goto Short; | 
|  | memmove(m->u.serverHello.random, p, RandomSize); | 
|  | p += RandomSize; | 
|  | n -= RandomSize; | 
|  |  | 
|  | if(n < 1 || n < p[0]+1) | 
|  | goto Short; | 
|  | m->u.serverHello.sid = makebytes(p+1, p[0]); | 
|  | p += m->u.serverHello.sid->len+1; | 
|  | n -= m->u.serverHello.sid->len+1; | 
|  |  | 
|  | if(n < 3) | 
|  | goto Short; | 
|  | m->u.serverHello.cipher = get16(p); | 
|  | m->u.serverHello.compressor = p[2]; | 
|  | n -= 3; | 
|  | break; | 
|  | case HCertificate: | 
|  | if(n < 3) | 
|  | goto Short; | 
|  | nn = get24(p); | 
|  | p += 3; | 
|  | n -= 3; | 
|  | if(n != nn) | 
|  | goto Short; | 
|  | /* certs */ | 
|  | i = 0; | 
|  | while(n > 0) { | 
|  | if(n < 3) | 
|  | goto Short; | 
|  | nn = get24(p); | 
|  | p += 3; | 
|  | n -= 3; | 
|  | if(nn > n) | 
|  | goto Short; | 
|  | m->u.certificate.ncert = i+1; | 
|  | m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes)); | 
|  | m->u.certificate.certs[i] = makebytes(p, nn); | 
|  | p += nn; | 
|  | n -= nn; | 
|  | i++; | 
|  | } | 
|  | break; | 
|  | case HCertificateRequest: | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | nn = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  | if(nn < 1 || nn > n) | 
|  | goto Short; | 
|  | m->u.certificateRequest.types = makebytes(p, nn); | 
|  | nn = get24(p); | 
|  | p += 3; | 
|  | n -= 3; | 
|  | if(nn == 0 || n != nn) | 
|  | goto Short; | 
|  | /* cas */ | 
|  | i = 0; | 
|  | while(n > 0) { | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | nn = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  | if(nn < 1 || nn > n) | 
|  | goto Short; | 
|  | m->u.certificateRequest.nca = i+1; | 
|  | m->u.certificateRequest.cas = erealloc(m->u.certificateRequest.cas, (i+1)*sizeof(Bytes)); | 
|  | m->u.certificateRequest.cas[i] = makebytes(p, nn); | 
|  | p += nn; | 
|  | n -= nn; | 
|  | i++; | 
|  | } | 
|  | break; | 
|  | case HServerHelloDone: | 
|  | break; | 
|  | case HClientKeyExchange: | 
|  | /* | 
|  | * this message depends upon the encryption selected | 
|  | * assume rsa. | 
|  | */ | 
|  | if(c->version == SSL3Version) | 
|  | nn = n; | 
|  | else{ | 
|  | if(n < 2) | 
|  | goto Short; | 
|  | nn = get16(p); | 
|  | p += 2; | 
|  | n -= 2; | 
|  | } | 
|  | if(n < nn) | 
|  | goto Short; | 
|  | m->u.clientKeyExchange.key = makebytes(p, nn); | 
|  | n -= nn; | 
|  | break; | 
|  | case HFinished: | 
|  | m->u.finished.n = c->finished.n; | 
|  | if(n < m->u.finished.n) | 
|  | goto Short; | 
|  | memmove(m->u.finished.verify, p, m->u.finished.n); | 
|  | n -= m->u.finished.n; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(type != HClientHello && n != 0) | 
|  | goto Short; | 
|  | Ok: | 
|  | if(c->trace){ | 
|  | char buf[8000]; | 
|  | c->trace("recv %s", msgPrint(buf, sizeof buf, m)); | 
|  | } | 
|  | return 1; | 
|  | Short: | 
|  | tlsError(c, EDecodeError, "handshake message has invalid length"); | 
|  | Err: | 
|  | msgClear(m); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | msgClear(Msg *m) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | switch(m->tag) { | 
|  | default: | 
|  | sysfatal("msgClear: unknown message type: %d\n", m->tag); | 
|  | case HHelloRequest: | 
|  | break; | 
|  | case HClientHello: | 
|  | freebytes(m->u.clientHello.sid); | 
|  | freeints(m->u.clientHello.ciphers); | 
|  | freebytes(m->u.clientHello.compressors); | 
|  | break; | 
|  | case HServerHello: | 
|  | freebytes(m->u.clientHello.sid); | 
|  | break; | 
|  | case HCertificate: | 
|  | for(i=0; i<m->u.certificate.ncert; i++) | 
|  | freebytes(m->u.certificate.certs[i]); | 
|  | free(m->u.certificate.certs); | 
|  | break; | 
|  | case HCertificateRequest: | 
|  | freebytes(m->u.certificateRequest.types); | 
|  | for(i=0; i<m->u.certificateRequest.nca; i++) | 
|  | freebytes(m->u.certificateRequest.cas[i]); | 
|  | free(m->u.certificateRequest.cas); | 
|  | break; | 
|  | case HServerHelloDone: | 
|  | break; | 
|  | case HClientKeyExchange: | 
|  | freebytes(m->u.clientKeyExchange.key); | 
|  | break; | 
|  | case HFinished: | 
|  | break; | 
|  | } | 
|  | memset(m, 0, sizeof(Msg)); | 
|  | } | 
|  |  | 
|  | static char * | 
|  | bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(s0) | 
|  | bs = seprint(bs, be, "%s", s0); | 
|  | bs = seprint(bs, be, "["); | 
|  | if(b == nil) | 
|  | bs = seprint(bs, be, "nil"); | 
|  | else | 
|  | for(i=0; i<b->len; i++) | 
|  | bs = seprint(bs, be, "%.2x ", b->data[i]); | 
|  | bs = seprint(bs, be, "]"); | 
|  | if(s1) | 
|  | bs = seprint(bs, be, "%s", s1); | 
|  | return bs; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(s0) | 
|  | bs = seprint(bs, be, "%s", s0); | 
|  | bs = seprint(bs, be, "["); | 
|  | if(b == nil) | 
|  | bs = seprint(bs, be, "nil"); | 
|  | else | 
|  | for(i=0; i<b->len; i++) | 
|  | bs = seprint(bs, be, "%x ", b->data[i]); | 
|  | bs = seprint(bs, be, "]"); | 
|  | if(s1) | 
|  | bs = seprint(bs, be, "%s", s1); | 
|  | return bs; | 
|  | } | 
|  |  | 
|  | static char* | 
|  | msgPrint(char *buf, int n, Msg *m) | 
|  | { | 
|  | int i; | 
|  | char *bs = buf, *be = buf+n; | 
|  |  | 
|  | switch(m->tag) { | 
|  | default: | 
|  | bs = seprint(bs, be, "unknown %d\n", m->tag); | 
|  | break; | 
|  | case HClientHello: | 
|  | bs = seprint(bs, be, "ClientHello\n"); | 
|  | bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version); | 
|  | bs = seprint(bs, be, "\trandom: "); | 
|  | for(i=0; i<RandomSize; i++) | 
|  | bs = seprint(bs, be, "%.2x", m->u.clientHello.random[i]); | 
|  | bs = seprint(bs, be, "\n"); | 
|  | bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n"); | 
|  | bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n"); | 
|  | bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n"); | 
|  | break; | 
|  | case HServerHello: | 
|  | bs = seprint(bs, be, "ServerHello\n"); | 
|  | bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version); | 
|  | bs = seprint(bs, be, "\trandom: "); | 
|  | for(i=0; i<RandomSize; i++) | 
|  | bs = seprint(bs, be, "%.2x", m->u.serverHello.random[i]); | 
|  | bs = seprint(bs, be, "\n"); | 
|  | bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n"); | 
|  | bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher); | 
|  | bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor); | 
|  | break; | 
|  | case HCertificate: | 
|  | bs = seprint(bs, be, "Certificate\n"); | 
|  | for(i=0; i<m->u.certificate.ncert; i++) | 
|  | bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n"); | 
|  | break; | 
|  | case HCertificateRequest: | 
|  | bs = seprint(bs, be, "CertificateRequest\n"); | 
|  | bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n"); | 
|  | bs = seprint(bs, be, "\tcertificateauthorities\n"); | 
|  | for(i=0; i<m->u.certificateRequest.nca; i++) | 
|  | bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n"); | 
|  | break; | 
|  | case HServerHelloDone: | 
|  | bs = seprint(bs, be, "ServerHelloDone\n"); | 
|  | break; | 
|  | case HClientKeyExchange: | 
|  | bs = seprint(bs, be, "HClientKeyExchange\n"); | 
|  | bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n"); | 
|  | break; | 
|  | case HFinished: | 
|  | bs = seprint(bs, be, "HFinished\n"); | 
|  | for(i=0; i<m->u.finished.n; i++) | 
|  | bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]); | 
|  | bs = seprint(bs, be, "\n"); | 
|  | break; | 
|  | } | 
|  | USED(bs); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static void | 
|  | tlsError(TlsConnection *c, int err, char *fmt, ...) | 
|  | { | 
|  | char msg[512]; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | vseprint(msg, msg+sizeof(msg), fmt, arg); | 
|  | va_end(arg); | 
|  | if(c->trace) | 
|  | c->trace("tlsError: %s\n", msg); | 
|  | else if(c->erred) | 
|  | fprint(2, "double error: %r, %s", msg); | 
|  | else | 
|  | werrstr("tls: local %s", msg); | 
|  | c->erred = 1; | 
|  | fprint(c->ctl, "alert %d", err); | 
|  | } | 
|  |  | 
|  | /* commit to specific version number */ | 
|  | static int | 
|  | setVersion(TlsConnection *c, int version) | 
|  | { | 
|  | if(c->verset || version > MaxProtoVersion || version < MinProtoVersion) | 
|  | return -1; | 
|  | if(version > c->version) | 
|  | version = c->version; | 
|  | if(version == SSL3Version) { | 
|  | c->version = version; | 
|  | c->finished.n = SSL3FinishedLen; | 
|  | }else if(version == TLSVersion){ | 
|  | c->version = version; | 
|  | c->finished.n = TLSFinishedLen; | 
|  | }else | 
|  | return -1; | 
|  | c->verset = 1; | 
|  | return fprint(c->ctl, "version 0x%x", version); | 
|  | } | 
|  |  | 
|  | /* confirm that received Finished message matches the expected value */ | 
|  | static int | 
|  | finishedMatch(TlsConnection *c, Finished *f) | 
|  | { | 
|  | return memcmp(f->verify, c->finished.verify, f->n) == 0; | 
|  | } | 
|  |  | 
|  | /* free memory associated with TlsConnection struct */ | 
|  | /*		(but don't close the TLS channel itself) */ | 
|  | static void | 
|  | tlsConnectionFree(TlsConnection *c) | 
|  | { | 
|  | tlsSecClose(c->sec); | 
|  | freebytes(c->sid); | 
|  | freebytes(c->cert); | 
|  | memset(c, 0, sizeof(*c)); | 
|  | free(c); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*================= cipher choices ======================== */ | 
|  |  | 
|  | static int weakCipher[CipherMax] = | 
|  | { | 
|  | 1,	/* TLS_NULL_WITH_NULL_NULL */ | 
|  | 1,	/* TLS_RSA_WITH_NULL_MD5 */ | 
|  | 1,	/* TLS_RSA_WITH_NULL_SHA */ | 
|  | 1,	/* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */ | 
|  | 0,	/* TLS_RSA_WITH_RC4_128_MD5 */ | 
|  | 0,	/* TLS_RSA_WITH_RC4_128_SHA */ | 
|  | 1,	/* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */ | 
|  | 0,	/* TLS_RSA_WITH_IDEA_CBC_SHA */ | 
|  | 1,	/* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 0,	/* TLS_RSA_WITH_DES_CBC_SHA */ | 
|  | 0,	/* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ | 
|  | 1,	/* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 0,	/* TLS_DH_DSS_WITH_DES_CBC_SHA */ | 
|  | 0,	/* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */ | 
|  | 1,	/* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 0,	/* TLS_DH_RSA_WITH_DES_CBC_SHA */ | 
|  | 0,	/* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */ | 
|  | 1,	/* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 0,	/* TLS_DHE_DSS_WITH_DES_CBC_SHA */ | 
|  | 0,	/* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */ | 
|  | 1,	/* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 0,	/* TLS_DHE_RSA_WITH_DES_CBC_SHA */ | 
|  | 0,	/* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ | 
|  | 1,	/* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */ | 
|  | 1,	/* TLS_DH_anon_WITH_RC4_128_MD5 */ | 
|  | 1,	/* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */ | 
|  | 1,	/* TLS_DH_anon_WITH_DES_CBC_SHA */ | 
|  | 1,	/* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ | 
|  | }; | 
|  |  | 
|  | static int | 
|  | setAlgs(TlsConnection *c, int a) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < nelem(cipherAlgs); i++){ | 
|  | if(cipherAlgs[i].tlsid == a){ | 
|  | c->enc = cipherAlgs[i].enc; | 
|  | c->digest = cipherAlgs[i].digest; | 
|  | c->nsecret = cipherAlgs[i].nsecret; | 
|  | if(c->nsecret > MaxKeyData) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | okCipher(Ints *cv) | 
|  | { | 
|  | int weak, i, j, c; | 
|  |  | 
|  | weak = 1; | 
|  | for(i = 0; i < cv->len; i++) { | 
|  | c = cv->data[i]; | 
|  | if(c >= CipherMax) | 
|  | weak = 0; | 
|  | else | 
|  | weak &= weakCipher[c]; | 
|  | for(j = 0; j < nelem(cipherAlgs); j++) | 
|  | if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c) | 
|  | return c; | 
|  | } | 
|  | if(weak) | 
|  | return -2; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | okCompression(Bytes *cv) | 
|  | { | 
|  | int i, j, c; | 
|  |  | 
|  | for(i = 0; i < cv->len; i++) { | 
|  | c = cv->data[i]; | 
|  | for(j = 0; j < nelem(compressors); j++) { | 
|  | if(compressors[j] == c) | 
|  | return c; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static Lock	ciphLock; | 
|  | static int	nciphers; | 
|  |  | 
|  | static int | 
|  | initCiphers(void) | 
|  | { | 
|  | enum {MaxAlgF = 1024, MaxAlgs = 10}; | 
|  | char s[MaxAlgF], *flds[MaxAlgs]; | 
|  | int i, j, n, ok; | 
|  |  | 
|  | lock(&ciphLock); | 
|  | if(nciphers){ | 
|  | unlock(&ciphLock); | 
|  | return nciphers; | 
|  | } | 
|  | j = open("#a/tls/encalgs", OREAD); | 
|  | if(j < 0){ | 
|  | werrstr("can't open #a/tls/encalgs: %r"); | 
|  | return 0; | 
|  | } | 
|  | n = read(j, s, MaxAlgF-1); | 
|  | close(j); | 
|  | if(n <= 0){ | 
|  | werrstr("nothing in #a/tls/encalgs: %r"); | 
|  | return 0; | 
|  | } | 
|  | s[n] = 0; | 
|  | n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); | 
|  | for(i = 0; i < nelem(cipherAlgs); i++){ | 
|  | ok = 0; | 
|  | for(j = 0; j < n; j++){ | 
|  | if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){ | 
|  | ok = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | cipherAlgs[i].ok = ok; | 
|  | } | 
|  |  | 
|  | j = open("#a/tls/hashalgs", OREAD); | 
|  | if(j < 0){ | 
|  | werrstr("can't open #a/tls/hashalgs: %r"); | 
|  | return 0; | 
|  | } | 
|  | n = read(j, s, MaxAlgF-1); | 
|  | close(j); | 
|  | if(n <= 0){ | 
|  | werrstr("nothing in #a/tls/hashalgs: %r"); | 
|  | return 0; | 
|  | } | 
|  | s[n] = 0; | 
|  | n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); | 
|  | for(i = 0; i < nelem(cipherAlgs); i++){ | 
|  | ok = 0; | 
|  | for(j = 0; j < n; j++){ | 
|  | if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){ | 
|  | ok = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | cipherAlgs[i].ok &= ok; | 
|  | if(cipherAlgs[i].ok) | 
|  | nciphers++; | 
|  | } | 
|  | unlock(&ciphLock); | 
|  | return nciphers; | 
|  | } | 
|  |  | 
|  | static Ints* | 
|  | makeciphers(void) | 
|  | { | 
|  | Ints *is; | 
|  | int i, j; | 
|  |  | 
|  | is = newints(nciphers); | 
|  | j = 0; | 
|  | for(i = 0; i < nelem(cipherAlgs); i++){ | 
|  | if(cipherAlgs[i].ok) | 
|  | is->data[j++] = cipherAlgs[i].tlsid; | 
|  | } | 
|  | return is; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /*================= security functions ======================== */ | 
|  |  | 
|  | /* given X.509 certificate, set up connection to factotum */ | 
|  | /*	for using corresponding private key */ | 
|  | static AuthRpc* | 
|  | factotum_rsa_open(uchar *cert, int certlen) | 
|  | { | 
|  | char *s; | 
|  | mpint *pub = nil; | 
|  | RSApub *rsapub; | 
|  | AuthRpc *rpc; | 
|  |  | 
|  | if((rpc = auth_allocrpc()) == nil){ | 
|  | return nil; | 
|  | } | 
|  | s = "proto=rsa service=tls role=client"; | 
|  | if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){ | 
|  | factotum_rsa_close(rpc); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* roll factotum keyring around to match certificate */ | 
|  | rsapub = X509toRSApub(cert, certlen, nil, 0); | 
|  | while(1){ | 
|  | if(auth_rpc(rpc, "read", nil, 0) != ARok){ | 
|  | factotum_rsa_close(rpc); | 
|  | rpc = nil; | 
|  | goto done; | 
|  | } | 
|  | pub = strtomp(rpc->arg, nil, 16, nil); | 
|  | assert(pub != nil); | 
|  | if(mpcmp(pub,rsapub->n) == 0) | 
|  | break; | 
|  | } | 
|  | done: | 
|  | mpfree(pub); | 
|  | rsapubfree(rsapub); | 
|  | return rpc; | 
|  | } | 
|  |  | 
|  | static mpint* | 
|  | factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher) | 
|  | { | 
|  | char *p; | 
|  | int rv; | 
|  |  | 
|  | if((p = mptoa(cipher, 16, nil, 0)) == nil) | 
|  | return nil; | 
|  | rv = auth_rpc(rpc, "write", p, strlen(p)); | 
|  | free(p); | 
|  | if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok) | 
|  | return nil; | 
|  | mpfree(cipher); | 
|  | return strtomp(rpc->arg, nil, 16, nil); | 
|  | } | 
|  |  | 
|  | static void | 
|  | factotum_rsa_close(AuthRpc*rpc) | 
|  | { | 
|  | if(!rpc) | 
|  | return; | 
|  | close(rpc->afd); | 
|  | auth_freerpc(rpc); | 
|  | } | 
|  |  | 
|  | static void | 
|  | tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) | 
|  | { | 
|  | uchar ai[MD5dlen], tmp[MD5dlen]; | 
|  | int i, n; | 
|  | MD5state *s; | 
|  |  | 
|  | /* generate a1 */ | 
|  | s = hmac_md5(label, nlabel, key, nkey, nil, nil); | 
|  | s = hmac_md5(seed0, nseed0, key, nkey, nil, s); | 
|  | hmac_md5(seed1, nseed1, key, nkey, ai, s); | 
|  |  | 
|  | while(nbuf > 0) { | 
|  | s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); | 
|  | s = hmac_md5(label, nlabel, key, nkey, nil, s); | 
|  | s = hmac_md5(seed0, nseed0, key, nkey, nil, s); | 
|  | hmac_md5(seed1, nseed1, key, nkey, tmp, s); | 
|  | n = MD5dlen; | 
|  | if(n > nbuf) | 
|  | n = nbuf; | 
|  | for(i = 0; i < n; i++) | 
|  | buf[i] ^= tmp[i]; | 
|  | buf += n; | 
|  | nbuf -= n; | 
|  | hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); | 
|  | memmove(ai, tmp, MD5dlen); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) | 
|  | { | 
|  | uchar ai[SHA1dlen], tmp[SHA1dlen]; | 
|  | int i, n; | 
|  | SHAstate *s; | 
|  |  | 
|  | /* generate a1 */ | 
|  | s = hmac_sha1(label, nlabel, key, nkey, nil, nil); | 
|  | s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); | 
|  | hmac_sha1(seed1, nseed1, key, nkey, ai, s); | 
|  |  | 
|  | while(nbuf > 0) { | 
|  | s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); | 
|  | s = hmac_sha1(label, nlabel, key, nkey, nil, s); | 
|  | s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); | 
|  | hmac_sha1(seed1, nseed1, key, nkey, tmp, s); | 
|  | n = SHA1dlen; | 
|  | if(n > nbuf) | 
|  | n = nbuf; | 
|  | for(i = 0; i < n; i++) | 
|  | buf[i] ^= tmp[i]; | 
|  | buf += n; | 
|  | nbuf -= n; | 
|  | hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); | 
|  | memmove(ai, tmp, SHA1dlen); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* fill buf with md5(args)^sha1(args) */ | 
|  | static void | 
|  | tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) | 
|  | { | 
|  | int i; | 
|  | int nlabel = strlen(label); | 
|  | int n = (nkey + 1) >> 1; | 
|  |  | 
|  | for(i = 0; i < nbuf; i++) | 
|  | buf[i] = 0; | 
|  | tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); | 
|  | tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * for setting server session id's | 
|  | */ | 
|  | static Lock	sidLock; | 
|  | static long	maxSid = 1; | 
|  |  | 
|  | /* the keys are verified to have the same public components | 
|  | * and to function correctly with pkcs 1 encryption and decryption. */ | 
|  | static TlsSec* | 
|  | tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom) | 
|  | { | 
|  | TlsSec *sec = emalloc(sizeof(*sec)); | 
|  |  | 
|  | USED(csid); USED(ncsid);  /* ignore csid for now */ | 
|  |  | 
|  | memmove(sec->crandom, crandom, RandomSize); | 
|  | sec->clientVers = cvers; | 
|  |  | 
|  | put32(sec->srandom, time(0)); | 
|  | genrandom(sec->srandom+4, RandomSize-4); | 
|  | memmove(srandom, sec->srandom, RandomSize); | 
|  |  | 
|  | /* | 
|  | * make up a unique sid: use our pid, and and incrementing id | 
|  | * can signal no sid by setting nssid to 0. | 
|  | */ | 
|  | memset(ssid, 0, SidSize); | 
|  | put32(ssid, getpid()); | 
|  | lock(&sidLock); | 
|  | put32(ssid+4, maxSid++); | 
|  | unlock(&sidLock); | 
|  | *nssid = SidSize; | 
|  | return sec; | 
|  | } | 
|  |  | 
|  | static int | 
|  | tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd) | 
|  | { | 
|  | if(epm != nil){ | 
|  | if(setVers(sec, vers) < 0) | 
|  | goto Err; | 
|  | serverMasterSecret(sec, epm, nepm); | 
|  | }else if(sec->vers != vers){ | 
|  | werrstr("mismatched session versions"); | 
|  | goto Err; | 
|  | } | 
|  | setSecrets(sec, kd, nkd); | 
|  | return 0; | 
|  | Err: | 
|  | sec->ok = -1; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static TlsSec* | 
|  | tlsSecInitc(int cvers, uchar *crandom) | 
|  | { | 
|  | TlsSec *sec = emalloc(sizeof(*sec)); | 
|  | sec->clientVers = cvers; | 
|  | put32(sec->crandom, time(0)); | 
|  | genrandom(sec->crandom+4, RandomSize-4); | 
|  | memmove(crandom, sec->crandom, RandomSize); | 
|  | return sec; | 
|  | } | 
|  |  | 
|  | static int | 
|  | tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd) | 
|  | { | 
|  | RSApub *pub; | 
|  |  | 
|  | pub = nil; | 
|  |  | 
|  | USED(sid); | 
|  | USED(nsid); | 
|  |  | 
|  | memmove(sec->srandom, srandom, RandomSize); | 
|  |  | 
|  | if(setVers(sec, vers) < 0) | 
|  | goto Err; | 
|  |  | 
|  | pub = X509toRSApub(cert, ncert, nil, 0); | 
|  | if(pub == nil){ | 
|  | werrstr("invalid x509/rsa certificate"); | 
|  | goto Err; | 
|  | } | 
|  | if(clientMasterSecret(sec, pub, epm, nepm) < 0) | 
|  | goto Err; | 
|  | rsapubfree(pub); | 
|  | setSecrets(sec, kd, nkd); | 
|  | return 0; | 
|  |  | 
|  | Err: | 
|  | if(pub != nil) | 
|  | rsapubfree(pub); | 
|  | sec->ok = -1; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient) | 
|  | { | 
|  | if(sec->nfin != nfin){ | 
|  | sec->ok = -1; | 
|  | werrstr("invalid finished exchange"); | 
|  | return -1; | 
|  | } | 
|  | md5.malloced = 0; | 
|  | sha1.malloced = 0; | 
|  | (*sec->setFinished)(sec, md5, sha1, fin, isclient); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | tlsSecOk(TlsSec *sec) | 
|  | { | 
|  | if(sec->ok == 0) | 
|  | sec->ok = 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | static void | 
|  | tlsSecKill(TlsSec *sec) | 
|  | { | 
|  | if(!sec) | 
|  | return; | 
|  | factotum_rsa_close(sec->rpc); | 
|  | sec->ok = -1; | 
|  | } | 
|  | */ | 
|  |  | 
|  | static void | 
|  | tlsSecClose(TlsSec *sec) | 
|  | { | 
|  | if(!sec) | 
|  | return; | 
|  | factotum_rsa_close(sec->rpc); | 
|  | free(sec->server); | 
|  | free(sec); | 
|  | } | 
|  |  | 
|  | static int | 
|  | setVers(TlsSec *sec, int v) | 
|  | { | 
|  | if(v == SSL3Version){ | 
|  | sec->setFinished = sslSetFinished; | 
|  | sec->nfin = SSL3FinishedLen; | 
|  | sec->prf = sslPRF; | 
|  | }else if(v == TLSVersion){ | 
|  | sec->setFinished = tlsSetFinished; | 
|  | sec->nfin = TLSFinishedLen; | 
|  | sec->prf = tlsPRF; | 
|  | }else{ | 
|  | werrstr("invalid version"); | 
|  | return -1; | 
|  | } | 
|  | sec->vers = v; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * generate secret keys from the master secret. | 
|  | * | 
|  | * different crypto selections will require different amounts | 
|  | * of key expansion and use of key expansion data, | 
|  | * but it's all generated using the same function. | 
|  | */ | 
|  | static void | 
|  | setSecrets(TlsSec *sec, uchar *kd, int nkd) | 
|  | { | 
|  | (*sec->prf)(kd, nkd, sec->sec, MasterSecretSize, "key expansion", | 
|  | sec->srandom, RandomSize, sec->crandom, RandomSize); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * set the master secret from the pre-master secret. | 
|  | */ | 
|  | static void | 
|  | setMasterSecret(TlsSec *sec, Bytes *pm) | 
|  | { | 
|  | (*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret", | 
|  | sec->crandom, RandomSize, sec->srandom, RandomSize); | 
|  | } | 
|  |  | 
|  | static void | 
|  | serverMasterSecret(TlsSec *sec, uchar *epm, int nepm) | 
|  | { | 
|  | Bytes *pm; | 
|  |  | 
|  | pm = pkcs1_decrypt(sec, epm, nepm); | 
|  |  | 
|  | /* if the client messed up, just continue as if everything is ok, */ | 
|  | /* to prevent attacks to check for correctly formatted messages. */ | 
|  | /* Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client. */ | 
|  | if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){ | 
|  | fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n", | 
|  | sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm); | 
|  | sec->ok = -1; | 
|  | if(pm != nil) | 
|  | freebytes(pm); | 
|  | pm = newbytes(MasterSecretSize); | 
|  | genrandom(pm->data, MasterSecretSize); | 
|  | } | 
|  | setMasterSecret(sec, pm); | 
|  | memset(pm->data, 0, pm->len); | 
|  | freebytes(pm); | 
|  | } | 
|  |  | 
|  | static int | 
|  | clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm) | 
|  | { | 
|  | Bytes *pm, *key; | 
|  |  | 
|  | pm = newbytes(MasterSecretSize); | 
|  | put16(pm->data, sec->clientVers); | 
|  | genrandom(pm->data+2, MasterSecretSize - 2); | 
|  |  | 
|  | setMasterSecret(sec, pm); | 
|  |  | 
|  | key = pkcs1_encrypt(pm, pub, 2); | 
|  | memset(pm->data, 0, pm->len); | 
|  | freebytes(pm); | 
|  | if(key == nil){ | 
|  | werrstr("tls pkcs1_encrypt failed"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | *nepm = key->len; | 
|  | *epm = malloc(*nepm); | 
|  | if(*epm == nil){ | 
|  | freebytes(key); | 
|  | werrstr("out of memory"); | 
|  | return -1; | 
|  | } | 
|  | memmove(*epm, key->data, *nepm); | 
|  |  | 
|  | freebytes(key); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) | 
|  | { | 
|  | DigestState *s; | 
|  | uchar h0[MD5dlen], h1[SHA1dlen], pad[48]; | 
|  | char *label; | 
|  |  | 
|  | if(isClient) | 
|  | label = "CLNT"; | 
|  | else | 
|  | label = "SRVR"; | 
|  |  | 
|  | md5((uchar*)label, 4, nil, &hsmd5); | 
|  | md5(sec->sec, MasterSecretSize, nil, &hsmd5); | 
|  | memset(pad, 0x36, 48); | 
|  | md5(pad, 48, nil, &hsmd5); | 
|  | md5(nil, 0, h0, &hsmd5); | 
|  | memset(pad, 0x5C, 48); | 
|  | s = md5(sec->sec, MasterSecretSize, nil, nil); | 
|  | s = md5(pad, 48, nil, s); | 
|  | md5(h0, MD5dlen, finished, s); | 
|  |  | 
|  | sha1((uchar*)label, 4, nil, &hssha1); | 
|  | sha1(sec->sec, MasterSecretSize, nil, &hssha1); | 
|  | memset(pad, 0x36, 40); | 
|  | sha1(pad, 40, nil, &hssha1); | 
|  | sha1(nil, 0, h1, &hssha1); | 
|  | memset(pad, 0x5C, 40); | 
|  | s = sha1(sec->sec, MasterSecretSize, nil, nil); | 
|  | s = sha1(pad, 40, nil, s); | 
|  | sha1(h1, SHA1dlen, finished + MD5dlen, s); | 
|  | } | 
|  |  | 
|  | /* fill "finished" arg with md5(args)^sha1(args) */ | 
|  | static void | 
|  | tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) | 
|  | { | 
|  | uchar h0[MD5dlen], h1[SHA1dlen]; | 
|  | char *label; | 
|  |  | 
|  | /* get current hash value, but allow further messages to be hashed in */ | 
|  | md5(nil, 0, h0, &hsmd5); | 
|  | sha1(nil, 0, h1, &hssha1); | 
|  |  | 
|  | if(isClient) | 
|  | label = "client finished"; | 
|  | else | 
|  | label = "server finished"; | 
|  | tlsPRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); | 
|  | } | 
|  |  | 
|  | static void | 
|  | sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) | 
|  | { | 
|  | DigestState *s; | 
|  | uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; | 
|  | int i, n, len; | 
|  |  | 
|  | USED(label); | 
|  | len = 1; | 
|  | while(nbuf > 0){ | 
|  | if(len > 26) | 
|  | return; | 
|  | for(i = 0; i < len; i++) | 
|  | tmp[i] = 'A' - 1 + len; | 
|  | s = sha1(tmp, len, nil, nil); | 
|  | s = sha1(key, nkey, nil, s); | 
|  | s = sha1(seed0, nseed0, nil, s); | 
|  | sha1(seed1, nseed1, sha1dig, s); | 
|  | s = md5(key, nkey, nil, nil); | 
|  | md5(sha1dig, SHA1dlen, md5dig, s); | 
|  | n = MD5dlen; | 
|  | if(n > nbuf) | 
|  | n = nbuf; | 
|  | memmove(buf, md5dig, n); | 
|  | buf += n; | 
|  | nbuf -= n; | 
|  | len++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static mpint* | 
|  | bytestomp(Bytes* bytes) | 
|  | { | 
|  | mpint* ans; | 
|  |  | 
|  | ans = betomp(bytes->data, bytes->len, nil); | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert mpint* to Bytes, putting high order byte first. | 
|  | */ | 
|  | static Bytes* | 
|  | mptobytes(mpint* big) | 
|  | { | 
|  | int n, m; | 
|  | uchar *a; | 
|  | Bytes* ans; | 
|  |  | 
|  | n = (mpsignif(big)+7)/8; | 
|  | m = mptobe(big, nil, n, &a); | 
|  | ans = makebytes(a, m); | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | /* Do RSA computation on block according to key, and pad */ | 
|  | /* result on left with zeros to make it modlen long. */ | 
|  | static Bytes* | 
|  | rsacomp(Bytes* block, RSApub* key, int modlen) | 
|  | { | 
|  | mpint *x, *y; | 
|  | Bytes *a, *ybytes; | 
|  | int ylen; | 
|  |  | 
|  | x = bytestomp(block); | 
|  | y = rsaencrypt(key, x, nil); | 
|  | mpfree(x); | 
|  | ybytes = mptobytes(y); | 
|  | ylen = ybytes->len; | 
|  |  | 
|  | if(ylen < modlen) { | 
|  | a = newbytes(modlen); | 
|  | memset(a->data, 0, modlen-ylen); | 
|  | memmove(a->data+modlen-ylen, ybytes->data, ylen); | 
|  | freebytes(ybytes); | 
|  | ybytes = a; | 
|  | } | 
|  | else if(ylen > modlen) { | 
|  | /* assume it has leading zeros (mod should make it so) */ | 
|  | a = newbytes(modlen); | 
|  | memmove(a->data, ybytes->data, modlen); | 
|  | freebytes(ybytes); | 
|  | ybytes = a; | 
|  | } | 
|  | mpfree(y); | 
|  | return ybytes; | 
|  | } | 
|  |  | 
|  | /* encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1 */ | 
|  | static Bytes* | 
|  | pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype) | 
|  | { | 
|  | Bytes *pad, *eb, *ans; | 
|  | int i, dlen, padlen, modlen; | 
|  |  | 
|  | modlen = (mpsignif(key->n)+7)/8; | 
|  | dlen = data->len; | 
|  | if(modlen < 12 || dlen > modlen - 11) | 
|  | return nil; | 
|  | padlen = modlen - 3 - dlen; | 
|  | pad = newbytes(padlen); | 
|  | genrandom(pad->data, padlen); | 
|  | for(i = 0; i < padlen; i++) { | 
|  | if(blocktype == 0) | 
|  | pad->data[i] = 0; | 
|  | else if(blocktype == 1) | 
|  | pad->data[i] = 255; | 
|  | else if(pad->data[i] == 0) | 
|  | pad->data[i] = 1; | 
|  | } | 
|  | eb = newbytes(modlen); | 
|  | eb->data[0] = 0; | 
|  | eb->data[1] = blocktype; | 
|  | memmove(eb->data+2, pad->data, padlen); | 
|  | eb->data[padlen+2] = 0; | 
|  | memmove(eb->data+padlen+3, data->data, dlen); | 
|  | ans = rsacomp(eb, key, modlen); | 
|  | freebytes(eb); | 
|  | freebytes(pad); | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | /* decrypt data according to PKCS#1, with given key. */ | 
|  | /* expect a block type of 2. */ | 
|  | static Bytes* | 
|  | pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm) | 
|  | { | 
|  | Bytes *eb, *ans = nil; | 
|  | int i, modlen; | 
|  | mpint *x, *y; | 
|  |  | 
|  | modlen = (mpsignif(sec->rsapub->n)+7)/8; | 
|  | if(nepm != modlen) | 
|  | return nil; | 
|  | x = betomp(epm, nepm, nil); | 
|  | y = factotum_rsa_decrypt(sec->rpc, x); | 
|  | if(y == nil) | 
|  | return nil; | 
|  | eb = mptobytes(y); | 
|  | if(eb->len < modlen){ /* pad on left with zeros */ | 
|  | ans = newbytes(modlen); | 
|  | memset(ans->data, 0, modlen-eb->len); | 
|  | memmove(ans->data+modlen-eb->len, eb->data, eb->len); | 
|  | freebytes(eb); | 
|  | eb = ans; | 
|  | } | 
|  | if(eb->data[0] == 0 && eb->data[1] == 2) { | 
|  | for(i = 2; i < modlen; i++) | 
|  | if(eb->data[i] == 0) | 
|  | break; | 
|  | if(i < modlen - 1) | 
|  | ans = makebytes(eb->data+i+1, modlen-(i+1)); | 
|  | } | 
|  | freebytes(eb); | 
|  | return ans; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*================= general utility functions ======================== */ | 
|  |  | 
|  | static void * | 
|  | emalloc(int n) | 
|  | { | 
|  | void *p; | 
|  | if(n==0) | 
|  | n=1; | 
|  | p = malloc(n); | 
|  | if(p == nil){ | 
|  | exits("out of memory"); | 
|  | } | 
|  | memset(p, 0, n); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static void * | 
|  | erealloc(void *ReallocP, int ReallocN) | 
|  | { | 
|  | if(ReallocN == 0) | 
|  | ReallocN = 1; | 
|  | if(!ReallocP) | 
|  | ReallocP = emalloc(ReallocN); | 
|  | else if(!(ReallocP = realloc(ReallocP, ReallocN))){ | 
|  | exits("out of memory"); | 
|  | } | 
|  | return(ReallocP); | 
|  | } | 
|  |  | 
|  | static void | 
|  | put32(uchar *p, u32int x) | 
|  | { | 
|  | p[0] = x>>24; | 
|  | p[1] = x>>16; | 
|  | p[2] = x>>8; | 
|  | p[3] = x; | 
|  | } | 
|  |  | 
|  | static void | 
|  | put24(uchar *p, int x) | 
|  | { | 
|  | p[0] = x>>16; | 
|  | p[1] = x>>8; | 
|  | p[2] = x; | 
|  | } | 
|  |  | 
|  | static void | 
|  | put16(uchar *p, int x) | 
|  | { | 
|  | p[0] = x>>8; | 
|  | p[1] = x; | 
|  | } | 
|  |  | 
|  | /* | 
|  | static u32int | 
|  | get32(uchar *p) | 
|  | { | 
|  | return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; | 
|  | } | 
|  | */ | 
|  |  | 
|  | static int | 
|  | get24(uchar *p) | 
|  | { | 
|  | return (p[0]<<16)|(p[1]<<8)|p[2]; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get16(uchar *p) | 
|  | { | 
|  | return (p[0]<<8)|p[1]; | 
|  | } | 
|  |  | 
|  | /* ANSI offsetof() */ | 
|  | #define OFFSET(x, s) ((intptr)(&(((s*)0)->x))) | 
|  |  | 
|  | /* | 
|  | * malloc and return a new Bytes structure capable of | 
|  | * holding len bytes. (len >= 0) | 
|  | * Used to use crypt_malloc, which aborts if malloc fails. | 
|  | */ | 
|  | static Bytes* | 
|  | newbytes(int len) | 
|  | { | 
|  | Bytes* ans; | 
|  |  | 
|  | ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len); | 
|  | ans->len = len; | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * newbytes(len), with data initialized from buf | 
|  | */ | 
|  | static Bytes* | 
|  | makebytes(uchar* buf, int len) | 
|  | { | 
|  | Bytes* ans; | 
|  |  | 
|  | ans = newbytes(len); | 
|  | memmove(ans->data, buf, len); | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | static void | 
|  | freebytes(Bytes* b) | 
|  | { | 
|  | if(b != nil) | 
|  | free(b); | 
|  | } | 
|  |  | 
|  | /* len is number of ints */ | 
|  | static Ints* | 
|  | newints(int len) | 
|  | { | 
|  | Ints* ans; | 
|  |  | 
|  | ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int)); | 
|  | ans->len = len; | 
|  | return ans; | 
|  | } | 
|  |  | 
|  | /* | 
|  | static Ints* | 
|  | makeints(int* buf, int len) | 
|  | { | 
|  | Ints* ans; | 
|  |  | 
|  | ans = newints(len); | 
|  | if(len > 0) | 
|  | memmove(ans->data, buf, len*sizeof(int)); | 
|  | return ans; | 
|  | } | 
|  | */ | 
|  |  | 
|  | static void | 
|  | freeints(Ints* b) | 
|  | { | 
|  | if(b != nil) | 
|  | free(b); | 
|  | } |