diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2013-11-23 01:05:33 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2013-11-23 01:05:33 +0100 |
commit | 2f9ae0f8ac8610e13ced184847b57b87fe5db580 (patch) | |
tree | f9ad2223d518585a2cfe9ea1c73e1e37d07bf637 /sys/src/cmd/unix/drawterm/libsec/tlshand.c | |
parent | ea5797c0731203c09ec5fb7172e77eab2750f1a9 (diff) |
removing (outdated) drawterm
drawterm is much better maintained by russ cox,
so removing this outdated copy.
for a more recent version, go to:
http://swtch.com/drawterm/
Diffstat (limited to 'sys/src/cmd/unix/drawterm/libsec/tlshand.c')
-rw-r--r-- | sys/src/cmd/unix/drawterm/libsec/tlshand.c | 2291 |
1 files changed, 0 insertions, 2291 deletions
diff --git a/sys/src/cmd/unix/drawterm/libsec/tlshand.c b/sys/src/cmd/unix/drawterm/libsec/tlshand.c deleted file mode 100644 index 0d04a66e9..000000000 --- a/sys/src/cmd/unix/drawterm/libsec/tlshand.c +++ /dev/null @@ -1,2291 +0,0 @@ -#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; - -typedef 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; -} TlsSec; - - -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, ...)); -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); - 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 TlsConnection * -tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...)) -{ - TlsConnection *c; - Msg m; - Bytes *csid; - uchar sid[SidSize], kd[MaxKeyData]; - char *secrets; - int cipher, compressor, nsid, rv; - - 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; - m.u.certificate.ncert = 1; - m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); - m.u.certificate.certs[0] = makebytes(cert, ncert); - 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", 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) -{ - int afd; - char *s; - mpint *pub = nil; - RSApub *rsapub; - AuthRpc *rpc; - - // start talking to factotum - if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) - return nil; - if((rpc = auth_allocrpc(afd)) == nil){ - close(afd); - 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) ((int)(&(((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); -} |