diff options
Diffstat (limited to 'chromium/net/third_party/nss/ssl/sslcon.c')
-rw-r--r-- | chromium/net/third_party/nss/ssl/sslcon.c | 3696 |
1 files changed, 3696 insertions, 0 deletions
diff --git a/chromium/net/third_party/nss/ssl/sslcon.c b/chromium/net/third_party/nss/ssl/sslcon.c new file mode 100644 index 00000000000..2fc6602a2b6 --- /dev/null +++ b/chromium/net/third_party/nss/ssl/sslcon.c @@ -0,0 +1,3696 @@ +/* + * SSL v2 handshake functions, and functions common to SSL2 and SSL3. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nssrenam.h" +#include "cert.h" +#include "secitem.h" +#include "sechash.h" +#include "cryptohi.h" /* for SGN_ funcs */ +#include "keyhi.h" /* for SECKEY_ high level functions. */ +#include "ssl.h" +#include "sslimpl.h" +#include "sslproto.h" +#include "ssl3prot.h" +#include "sslerr.h" +#include "pk11func.h" +#include "prinit.h" +#include "prtime.h" /* for PR_Now() */ + +#define XXX +static PRBool policyWasSet; + +/* This ordered list is indexed by (SSL_CK_xx * 3) */ +/* Second and third bytes are MSB and LSB of master key length. */ +static const PRUint8 allCipherSuites[] = { + 0, 0, 0, + SSL_CK_RC4_128_WITH_MD5, 0x00, 0x80, + SSL_CK_RC4_128_EXPORT40_WITH_MD5, 0x00, 0x80, + SSL_CK_RC2_128_CBC_WITH_MD5, 0x00, 0x80, + SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, 0x00, 0x80, + SSL_CK_IDEA_128_CBC_WITH_MD5, 0x00, 0x80, + SSL_CK_DES_64_CBC_WITH_MD5, 0x00, 0x40, + SSL_CK_DES_192_EDE3_CBC_WITH_MD5, 0x00, 0xC0, + 0, 0, 0 +}; + +#define ssl2_NUM_SUITES_IMPLEMENTED 6 + +/* This list is sent back to the client when the client-hello message + * contains no overlapping ciphers, so the client can report what ciphers + * are supported by the server. Unlike allCipherSuites (above), this list + * is sorted by descending preference, not by cipherSuite number. + */ +static const PRUint8 implementedCipherSuites[ssl2_NUM_SUITES_IMPLEMENTED * 3] = { + SSL_CK_RC4_128_WITH_MD5, 0x00, 0x80, + SSL_CK_RC2_128_CBC_WITH_MD5, 0x00, 0x80, + SSL_CK_DES_192_EDE3_CBC_WITH_MD5, 0x00, 0xC0, + SSL_CK_DES_64_CBC_WITH_MD5, 0x00, 0x40, + SSL_CK_RC4_128_EXPORT40_WITH_MD5, 0x00, 0x80, + SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, 0x00, 0x80 +}; + +typedef struct ssl2SpecsStr { + PRUint8 nkm; /* do this many hashes to generate key material. */ + PRUint8 nkd; /* size of readKey and writeKey in bytes. */ + PRUint8 blockSize; + PRUint8 blockShift; + CK_MECHANISM_TYPE mechanism; + PRUint8 keyLen; /* cipher symkey size in bytes. */ + PRUint8 pubLen; /* publicly reveal this many bytes of key. */ + PRUint8 ivLen; /* length of IV data at *ca. */ +} ssl2Specs; + +static const ssl2Specs ssl_Specs[] = { +/* NONE */ + { 0, 0, 0, 0, }, +/* SSL_CK_RC4_128_WITH_MD5 */ + { 2, 16, 1, 0, CKM_RC4, 16, 0, 0, }, +/* SSL_CK_RC4_128_EXPORT40_WITH_MD5 */ + { 2, 16, 1, 0, CKM_RC4, 16, 11, 0, }, +/* SSL_CK_RC2_128_CBC_WITH_MD5 */ + { 2, 16, 8, 3, CKM_RC2_CBC, 16, 0, 8, }, +/* SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 */ + { 2, 16, 8, 3, CKM_RC2_CBC, 16, 11, 8, }, +/* SSL_CK_IDEA_128_CBC_WITH_MD5 */ + { 0, 0, 0, 0, }, +/* SSL_CK_DES_64_CBC_WITH_MD5 */ + { 1, 8, 8, 3, CKM_DES_CBC, 8, 0, 8, }, +/* SSL_CK_DES_192_EDE3_CBC_WITH_MD5 */ + { 3, 24, 8, 3, CKM_DES3_CBC, 24, 0, 8, }, +}; + +#define SET_ERROR_CODE /* reminder */ +#define TEST_FOR_FAILURE /* reminder */ + +/* +** Put a string tag in the library so that we can examine an executable +** and see what kind of security it supports. +*/ +const char *ssl_version = "SECURITY_VERSION:" + " +us" + " +export" +#ifdef TRACE + " +trace" +#endif +#ifdef DEBUG + " +debug" +#endif + ; + +const char * const ssl_cipherName[] = { + "unknown", + "RC4", + "RC4-Export", + "RC2-CBC", + "RC2-CBC-Export", + "IDEA-CBC", + "DES-CBC", + "DES-EDE3-CBC", + "unknown", + "unknown", /* was fortezza, NO LONGER USED */ +}; + + +/* bit-masks, showing which SSLv2 suites are allowed. + * lsb corresponds to first cipher suite in allCipherSuites[]. + */ +static PRUint16 allowedByPolicy; /* all off by default */ +static PRUint16 maybeAllowedByPolicy; /* all off by default */ +static PRUint16 chosenPreference = 0xff; /* all on by default */ + +/* bit values for the above two bit masks */ +#define SSL_CB_RC4_128_WITH_MD5 (1 << SSL_CK_RC4_128_WITH_MD5) +#define SSL_CB_RC4_128_EXPORT40_WITH_MD5 (1 << SSL_CK_RC4_128_EXPORT40_WITH_MD5) +#define SSL_CB_RC2_128_CBC_WITH_MD5 (1 << SSL_CK_RC2_128_CBC_WITH_MD5) +#define SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5 (1 << SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5) +#define SSL_CB_IDEA_128_CBC_WITH_MD5 (1 << SSL_CK_IDEA_128_CBC_WITH_MD5) +#define SSL_CB_DES_64_CBC_WITH_MD5 (1 << SSL_CK_DES_64_CBC_WITH_MD5) +#define SSL_CB_DES_192_EDE3_CBC_WITH_MD5 (1 << SSL_CK_DES_192_EDE3_CBC_WITH_MD5) +#define SSL_CB_IMPLEMENTED \ + (SSL_CB_RC4_128_WITH_MD5 | \ + SSL_CB_RC4_128_EXPORT40_WITH_MD5 | \ + SSL_CB_RC2_128_CBC_WITH_MD5 | \ + SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5 | \ + SSL_CB_DES_64_CBC_WITH_MD5 | \ + SSL_CB_DES_192_EDE3_CBC_WITH_MD5) + + +/* Construct a socket's list of cipher specs from the global default values. + */ +static SECStatus +ssl2_ConstructCipherSpecs(sslSocket *ss) +{ + PRUint8 * cs = NULL; + unsigned int allowed; + unsigned int count; + int ssl3_count = 0; + int final_count; + int i; + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + count = 0; + PORT_Assert(ss != 0); + allowed = !ss->opt.enableSSL2 ? 0 : + (ss->allowedByPolicy & ss->chosenPreference & SSL_CB_IMPLEMENTED); + while (allowed) { + if (allowed & 1) + ++count; + allowed >>= 1; + } + + /* Call ssl3_config_match_init() once here, + * instead of inside ssl3_ConstructV2CipherSpecsHack(), + * because the latter gets called twice below, + * and then again in ssl2_BeginClientHandshake(). + */ + ssl3_config_match_init(ss); + + /* ask SSL3 how many cipher suites it has. */ + rv = ssl3_ConstructV2CipherSpecsHack(ss, NULL, &ssl3_count); + if (rv < 0) + return rv; + count += ssl3_count; + + /* Allocate memory to hold cipher specs */ + if (count > 0) + cs = (PRUint8*) PORT_Alloc(count * 3); + else + PORT_SetError(SSL_ERROR_SSL_DISABLED); + if (cs == NULL) + return SECFailure; + + if (ss->cipherSpecs != NULL) { + PORT_Free(ss->cipherSpecs); + } + ss->cipherSpecs = cs; + ss->sizeCipherSpecs = count * 3; + + /* fill in cipher specs for SSL2 cipher suites */ + allowed = !ss->opt.enableSSL2 ? 0 : + (ss->allowedByPolicy & ss->chosenPreference & SSL_CB_IMPLEMENTED); + for (i = 0; i < ssl2_NUM_SUITES_IMPLEMENTED * 3; i += 3) { + const PRUint8 * hs = implementedCipherSuites + i; + int ok = allowed & (1U << hs[0]); + if (ok) { + cs[0] = hs[0]; + cs[1] = hs[1]; + cs[2] = hs[2]; + cs += 3; + } + } + + /* now have SSL3 add its suites onto the end */ + rv = ssl3_ConstructV2CipherSpecsHack(ss, cs, &final_count); + + /* adjust for any difference between first pass and second pass */ + ss->sizeCipherSpecs -= (ssl3_count - final_count) * 3; + + return rv; +} + +/* This function is called immediately after ssl2_ConstructCipherSpecs() +** at the beginning of a handshake. It detects cases where a protocol +** (e.g. SSL2 or SSL3) is logically enabled, but all its cipher suites +** for that protocol have been disabled. If such cases, it clears the +** enable bit for the protocol. If no protocols remain enabled, or +** if no cipher suites are found, it sets the error code and returns +** SECFailure, otherwise it returns SECSuccess. +*/ +static SECStatus +ssl2_CheckConfigSanity(sslSocket *ss) +{ + unsigned int allowed; + int ssl3CipherCount = 0; + SECStatus rv; + + /* count the SSL2 and SSL3 enabled ciphers. + * if either is zero, clear the socket's enable for that protocol. + */ + if (!ss->cipherSpecs) + goto disabled; + + allowed = ss->allowedByPolicy & ss->chosenPreference; + if (! allowed) + ss->opt.enableSSL2 = PR_FALSE; /* not really enabled if no ciphers */ + + /* ssl3_config_match_init was called in ssl2_ConstructCipherSpecs(). */ + /* Ask how many ssl3 CipherSuites were enabled. */ + rv = ssl3_ConstructV2CipherSpecsHack(ss, NULL, &ssl3CipherCount); + if (rv != SECSuccess || ssl3CipherCount <= 0) { + /* SSL3/TLS not really enabled if no ciphers */ + ss->vrange.min = SSL_LIBRARY_VERSION_NONE; + ss->vrange.max = SSL_LIBRARY_VERSION_NONE; + } + + if (!ss->opt.enableSSL2 && SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { + SSL_DBG(("%d: SSL[%d]: Can't handshake! all versions disabled.", + SSL_GETPID(), ss->fd)); +disabled: + PORT_SetError(SSL_ERROR_SSL_DISABLED); + return SECFailure; + } + return SECSuccess; +} + +/* + * Since this is a global (not per-socket) setting, we cannot use the + * HandshakeLock to protect this. Probably want a global lock. + */ +SECStatus +ssl2_SetPolicy(PRInt32 which, PRInt32 policy) +{ + PRUint32 bitMask; + SECStatus rv = SECSuccess; + + which &= 0x000f; + bitMask = 1 << which; + + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + return SECFailure; + } + + if (policy == SSL_ALLOWED) { + allowedByPolicy |= bitMask; + maybeAllowedByPolicy |= bitMask; + } else if (policy == SSL_RESTRICTED) { + allowedByPolicy &= ~bitMask; + maybeAllowedByPolicy |= bitMask; + } else { + allowedByPolicy &= ~bitMask; + maybeAllowedByPolicy &= ~bitMask; + } + allowedByPolicy &= SSL_CB_IMPLEMENTED; + maybeAllowedByPolicy &= SSL_CB_IMPLEMENTED; + + policyWasSet = PR_TRUE; + return rv; +} + +SECStatus +ssl2_GetPolicy(PRInt32 which, PRInt32 *oPolicy) +{ + PRUint32 bitMask; + PRInt32 policy; + + which &= 0x000f; + bitMask = 1 << which; + + /* Caller assures oPolicy is not null. */ + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + *oPolicy = SSL_NOT_ALLOWED; + return SECFailure; + } + + if (maybeAllowedByPolicy & bitMask) { + policy = (allowedByPolicy & bitMask) ? SSL_ALLOWED : SSL_RESTRICTED; + } else { + policy = SSL_NOT_ALLOWED; + } + + *oPolicy = policy; + return SECSuccess; +} + +/* + * Since this is a global (not per-socket) setting, we cannot use the + * HandshakeLock to protect this. Probably want a global lock. + * Called from SSL_CipherPrefSetDefault in sslsock.c + * These changes have no effect on any sslSockets already created. + */ +SECStatus +ssl2_CipherPrefSetDefault(PRInt32 which, PRBool enabled) +{ + PRUint32 bitMask; + + which &= 0x000f; + bitMask = 1 << which; + + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + return SECFailure; + } + + if (enabled) + chosenPreference |= bitMask; + else + chosenPreference &= ~bitMask; + chosenPreference &= SSL_CB_IMPLEMENTED; + + return SECSuccess; +} + +SECStatus +ssl2_CipherPrefGetDefault(PRInt32 which, PRBool *enabled) +{ + PRBool rv = PR_FALSE; + PRUint32 bitMask; + + which &= 0x000f; + bitMask = 1 << which; + + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + *enabled = PR_FALSE; + return SECFailure; + } + + rv = (PRBool)((chosenPreference & bitMask) != 0); + *enabled = rv; + return SECSuccess; +} + +SECStatus +ssl2_CipherPrefSet(sslSocket *ss, PRInt32 which, PRBool enabled) +{ + PRUint32 bitMask; + + which &= 0x000f; + bitMask = 1 << which; + + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + return SECFailure; + } + + if (enabled) + ss->chosenPreference |= bitMask; + else + ss->chosenPreference &= ~bitMask; + ss->chosenPreference &= SSL_CB_IMPLEMENTED; + + return SECSuccess; +} + +SECStatus +ssl2_CipherPrefGet(sslSocket *ss, PRInt32 which, PRBool *enabled) +{ + PRBool rv = PR_FALSE; + PRUint32 bitMask; + + which &= 0x000f; + bitMask = 1 << which; + + if (!(bitMask & SSL_CB_IMPLEMENTED)) { + PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE); + *enabled = PR_FALSE; + return SECFailure; + } + + rv = (PRBool)((ss->chosenPreference & bitMask) != 0); + *enabled = rv; + return SECSuccess; +} + + +/* copy global default policy into socket. */ +void +ssl2_InitSocketPolicy(sslSocket *ss) +{ + ss->allowedByPolicy = allowedByPolicy; + ss->maybeAllowedByPolicy = maybeAllowedByPolicy; + ss->chosenPreference = chosenPreference; +} + + +/************************************************************************/ + +/* Called from ssl2_CreateSessionCypher(), which already holds handshake lock. + */ +static SECStatus +ssl2_CreateMAC(sslSecurityInfo *sec, SECItem *readKey, SECItem *writeKey, + int cipherChoice) +{ + switch (cipherChoice) { + + case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: + case SSL_CK_RC2_128_CBC_WITH_MD5: + case SSL_CK_RC4_128_EXPORT40_WITH_MD5: + case SSL_CK_RC4_128_WITH_MD5: + case SSL_CK_DES_64_CBC_WITH_MD5: + case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: + sec->hash = HASH_GetHashObject(HASH_AlgMD5); + SECITEM_CopyItem(0, &sec->sendSecret, writeKey); + SECITEM_CopyItem(0, &sec->rcvSecret, readKey); + break; + + default: + PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); + return SECFailure; + } + sec->hashcx = (*sec->hash->create)(); + if (sec->hashcx == NULL) + return SECFailure; + return SECSuccess; +} + +/************************************************************************ + * All the Send functions below must acquire and release the socket's + * xmitBufLock. + */ + +/* Called from all the Send* functions below. */ +static SECStatus +ssl2_GetSendBuffer(sslSocket *ss, unsigned int len) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + if (len < 128) { + len = 128; + } + if (len > ss->sec.ci.sendBuf.space) { + rv = sslBuffer_Grow(&ss->sec.ci.sendBuf, len); + if (rv != SECSuccess) { + SSL_DBG(("%d: SSL[%d]: ssl2_GetSendBuffer failed, tried to get %d bytes", + SSL_GETPID(), ss->fd, len)); + rv = SECFailure; + } + } + return rv; +} + +/* Called from: + * ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage() + * ssl2_HandleRequestCertificate() <- ssl2_HandleMessage() <- + ssl_Do1stHandshake() + * ssl2_HandleMessage() <- ssl_Do1stHandshake() + * ssl2_HandleServerHelloMessage() <- ssl_Do1stHandshake() + after ssl2_BeginClientHandshake() + * ssl2_HandleClientHelloMessage() <- ssl_Do1stHandshake() + after ssl2_BeginServerHandshake() + * + * Acquires and releases the socket's xmitBufLock. + */ +int +ssl2_SendErrorMessage(sslSocket *ss, int error) +{ + int rv; + PRUint8 msg[SSL_HL_ERROR_HBYTES]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + msg[0] = SSL_MT_ERROR; + msg[1] = MSB(error); + msg[2] = LSB(error); + + ssl_GetXmitBufLock(ss); /***************************************/ + + SSL_TRC(3, ("%d: SSL[%d]: sending error %d", SSL_GETPID(), ss->fd, error)); + + ss->handshakeBegun = 1; + rv = (*ss->sec.send)(ss, msg, sizeof(msg), 0); + if (rv >= 0) { + rv = SECSuccess; + } + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from ssl2_TryToFinish(). + * Acquires and releases the socket's xmitBufLock. + */ +static SECStatus +ssl2_SendClientFinishedMessage(sslSocket *ss) +{ + SECStatus rv = SECSuccess; + int sent; + PRUint8 msg[1 + SSL_CONNECTIONID_BYTES]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + if (ss->sec.ci.sentFinished == 0) { + ss->sec.ci.sentFinished = 1; + + SSL_TRC(3, ("%d: SSL[%d]: sending client-finished", + SSL_GETPID(), ss->fd)); + + msg[0] = SSL_MT_CLIENT_FINISHED; + PORT_Memcpy(msg+1, ss->sec.ci.connectionID, + sizeof(ss->sec.ci.connectionID)); + + DUMP_MSG(29, (ss, msg, 1 + sizeof(ss->sec.ci.connectionID))); + sent = (*ss->sec.send)(ss, msg, 1 + sizeof(ss->sec.ci.connectionID), 0); + rv = (sent >= 0) ? SECSuccess : (SECStatus)sent; + } + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from + * ssl2_HandleClientSessionKeyMessage() <- ssl2_HandleClientHelloMessage() + * ssl2_HandleClientHelloMessage() <- ssl_Do1stHandshake() + after ssl2_BeginServerHandshake() + * Acquires and releases the socket's xmitBufLock. + */ +static SECStatus +ssl2_SendServerVerifyMessage(sslSocket *ss) +{ + PRUint8 * msg; + int sendLen; + int sent; + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + sendLen = 1 + SSL_CHALLENGE_BYTES; + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv != SECSuccess) { + goto done; + } + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_SERVER_VERIFY; + PORT_Memcpy(msg+1, ss->sec.ci.clientChallenge, SSL_CHALLENGE_BYTES); + + DUMP_MSG(29, (ss, msg, sendLen)); + sent = (*ss->sec.send)(ss, msg, sendLen, 0); + + rv = (sent >= 0) ? SECSuccess : (SECStatus)sent; + +done: + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from ssl2_TryToFinish(). + * Acquires and releases the socket's xmitBufLock. + */ +static SECStatus +ssl2_SendServerFinishedMessage(sslSocket *ss) +{ + sslSessionID * sid; + PRUint8 * msg; + int sendLen, sent; + SECStatus rv = SECSuccess; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + if (ss->sec.ci.sentFinished == 0) { + ss->sec.ci.sentFinished = 1; + PORT_Assert(ss->sec.ci.sid != 0); + sid = ss->sec.ci.sid; + + SSL_TRC(3, ("%d: SSL[%d]: sending server-finished", + SSL_GETPID(), ss->fd)); + + sendLen = 1 + sizeof(sid->u.ssl2.sessionID); + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv != SECSuccess) { + goto done; + } + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_SERVER_FINISHED; + PORT_Memcpy(msg+1, sid->u.ssl2.sessionID, + sizeof(sid->u.ssl2.sessionID)); + + DUMP_MSG(29, (ss, msg, sendLen)); + sent = (*ss->sec.send)(ss, msg, sendLen, 0); + + if (sent < 0) { + /* If send failed, it is now a bogus session-id */ + if (ss->sec.uncache) + (*ss->sec.uncache)(sid); + rv = (SECStatus)sent; + } else if (!ss->opt.noCache) { + /* Put the sid in session-id cache, (may already be there) */ + (*ss->sec.cache)(sid); + rv = SECSuccess; + } + ssl_FreeSID(sid); + ss->sec.ci.sid = 0; + } +done: + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from ssl2_ClientSetupSessionCypher() <- + * ssl2_HandleServerHelloMessage() + * after ssl2_BeginClientHandshake() + * Acquires and releases the socket's xmitBufLock. + */ +static SECStatus +ssl2_SendSessionKeyMessage(sslSocket *ss, int cipher, int keySize, + PRUint8 *ca, int caLen, + PRUint8 *ck, int ckLen, + PRUint8 *ek, int ekLen) +{ + PRUint8 * msg; + int sendLen; + int sent; + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + sendLen = SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen + caLen; + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv != SECSuccess) + goto done; + + SSL_TRC(3, ("%d: SSL[%d]: sending client-session-key", + SSL_GETPID(), ss->fd)); + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_CLIENT_MASTER_KEY; + msg[1] = cipher; + msg[2] = MSB(keySize); + msg[3] = LSB(keySize); + msg[4] = MSB(ckLen); + msg[5] = LSB(ckLen); + msg[6] = MSB(ekLen); + msg[7] = LSB(ekLen); + msg[8] = MSB(caLen); + msg[9] = LSB(caLen); + PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES, ck, ckLen); + PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen, ek, ekLen); + PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen+ekLen, ca, caLen); + + DUMP_MSG(29, (ss, msg, sendLen)); + sent = (*ss->sec.send)(ss, msg, sendLen, 0); + rv = (sent >= 0) ? SECSuccess : (SECStatus)sent; +done: + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from ssl2_TriggerNextMessage() <- ssl2_HandleMessage() + * Acquires and releases the socket's xmitBufLock. + */ +static SECStatus +ssl2_SendCertificateRequestMessage(sslSocket *ss) +{ + PRUint8 * msg; + int sent; + int sendLen; + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + sendLen = SSL_HL_REQUEST_CERTIFICATE_HBYTES + SSL_CHALLENGE_BYTES; + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv != SECSuccess) + goto done; + + SSL_TRC(3, ("%d: SSL[%d]: sending certificate request", + SSL_GETPID(), ss->fd)); + + /* Generate random challenge for client to encrypt */ + PK11_GenerateRandom(ss->sec.ci.serverChallenge, SSL_CHALLENGE_BYTES); + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_REQUEST_CERTIFICATE; + msg[1] = SSL_AT_MD5_WITH_RSA_ENCRYPTION; + PORT_Memcpy(msg + SSL_HL_REQUEST_CERTIFICATE_HBYTES, + ss->sec.ci.serverChallenge, SSL_CHALLENGE_BYTES); + + DUMP_MSG(29, (ss, msg, sendLen)); + sent = (*ss->sec.send)(ss, msg, sendLen, 0); + rv = (sent >= 0) ? SECSuccess : (SECStatus)sent; +done: + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/* Called from ssl2_HandleRequestCertificate() <- ssl2_HandleMessage() + * Acquires and releases the socket's xmitBufLock. + */ +static int +ssl2_SendCertificateResponseMessage(sslSocket *ss, SECItem *cert, + SECItem *encCode) +{ + PRUint8 *msg; + int rv, sendLen; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetXmitBufLock(ss); /***************************************/ + + sendLen = SSL_HL_CLIENT_CERTIFICATE_HBYTES + encCode->len + cert->len; + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv) + goto done; + + SSL_TRC(3, ("%d: SSL[%d]: sending certificate response", + SSL_GETPID(), ss->fd)); + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_CLIENT_CERTIFICATE; + msg[1] = SSL_CT_X509_CERTIFICATE; + msg[2] = MSB(cert->len); + msg[3] = LSB(cert->len); + msg[4] = MSB(encCode->len); + msg[5] = LSB(encCode->len); + PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES, cert->data, cert->len); + PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES + cert->len, + encCode->data, encCode->len); + + DUMP_MSG(29, (ss, msg, sendLen)); + rv = (*ss->sec.send)(ss, msg, sendLen, 0); + if (rv >= 0) { + rv = SECSuccess; + } +done: + ssl_ReleaseXmitBufLock(ss); /***************************************/ + return rv; +} + +/******************************************************************** +** Send functions above this line must aquire & release the socket's +** xmitBufLock. +** All the ssl2_Send functions below this line are called vis ss->sec.send +** and require that the caller hold the xmitBufLock. +*/ + +/* +** Called from ssl2_SendStream, ssl2_SendBlock, but not from ssl2_SendClear. +*/ +static SECStatus +ssl2_CalcMAC(PRUint8 * result, + sslSecurityInfo * sec, + const PRUint8 * data, + unsigned int dataLen, + unsigned int paddingLen) +{ + const PRUint8 * secret = sec->sendSecret.data; + unsigned int secretLen = sec->sendSecret.len; + unsigned long sequenceNumber = sec->sendSequence; + unsigned int nout; + PRUint8 seq[4]; + PRUint8 padding[32];/* XXX max blocksize? */ + + if (!sec->hash || !sec->hash->length) + return SECSuccess; + if (!sec->hashcx) + return SECFailure; + + /* Reset hash function */ + (*sec->hash->begin)(sec->hashcx); + + /* Feed hash the data */ + (*sec->hash->update)(sec->hashcx, secret, secretLen); + (*sec->hash->update)(sec->hashcx, data, dataLen); + PORT_Memset(padding, paddingLen, paddingLen); + (*sec->hash->update)(sec->hashcx, padding, paddingLen); + + seq[0] = (PRUint8) (sequenceNumber >> 24); + seq[1] = (PRUint8) (sequenceNumber >> 16); + seq[2] = (PRUint8) (sequenceNumber >> 8); + seq[3] = (PRUint8) (sequenceNumber); + + PRINT_BUF(60, (0, "calc-mac secret:", secret, secretLen)); + PRINT_BUF(60, (0, "calc-mac data:", data, dataLen)); + PRINT_BUF(60, (0, "calc-mac padding:", padding, paddingLen)); + PRINT_BUF(60, (0, "calc-mac seq:", seq, 4)); + + (*sec->hash->update)(sec->hashcx, seq, 4); + + /* Get result */ + (*sec->hash->end)(sec->hashcx, result, &nout, sec->hash->length); + + return SECSuccess; +} + +/* +** Maximum transmission amounts. These are tiny bit smaller than they +** need to be (they account for the MAC length plus some padding), +** assuming the MAC is 16 bytes long and the padding is a max of 7 bytes +** long. This gives an additional 9 bytes of slop to work within. +*/ +#define MAX_STREAM_CYPHER_LEN 0x7fe0 +#define MAX_BLOCK_CYPHER_LEN 0x3fe0 + +/* +** Send some data in the clear. +** Package up data with the length header and send it. +** +** Return count of bytes successfully written, or negative number (failure). +*/ +static PRInt32 +ssl2_SendClear(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags) +{ + PRUint8 * out; + int rv; + int amount; + int count = 0; + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); + + SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes in the clear", + SSL_GETPID(), ss->fd, len)); + PRINT_BUF(50, (ss, "clear data:", (PRUint8*) in, len)); + + while (len) { + amount = PR_MIN( len, MAX_STREAM_CYPHER_LEN ); + if (amount + 2 > ss->sec.writeBuf.space) { + rv = sslBuffer_Grow(&ss->sec.writeBuf, amount + 2); + if (rv != SECSuccess) { + count = rv; + break; + } + } + out = ss->sec.writeBuf.buf; + + /* + ** Construct message. + */ + out[0] = 0x80 | MSB(amount); + out[1] = LSB(amount); + PORT_Memcpy(&out[2], in, amount); + + /* Now send the data */ + rv = ssl_DefSend(ss, out, amount + 2, flags & ~ssl_SEND_FLAG_MASK); + if (rv < 0) { + if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) { + rv = 0; + } else { + /* Return short write if some data already went out... */ + if (count == 0) + count = rv; + break; + } + } + + if ((unsigned)rv < (amount + 2)) { + /* Short write. Save the data and return. */ + if (ssl_SaveWriteData(ss, out + rv, amount + 2 - rv) + == SECFailure) { + count = SECFailure; + } else { + count += amount; + ss->sec.sendSequence++; + } + break; + } + + ss->sec.sendSequence++; + in += amount; + count += amount; + len -= amount; + } + + return count; +} + +/* +** Send some data, when using a stream cipher. Stream ciphers have a +** block size of 1. Package up the data with the length header +** and send it. +*/ +static PRInt32 +ssl2_SendStream(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags) +{ + PRUint8 * out; + int rv; + int count = 0; + + int amount; + PRUint8 macLen; + int nout; + int buflen; + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); + + SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes using stream cipher", + SSL_GETPID(), ss->fd, len)); + PRINT_BUF(50, (ss, "clear data:", (PRUint8*) in, len)); + + while (len) { + ssl_GetSpecReadLock(ss); /*************************************/ + + macLen = ss->sec.hash->length; + amount = PR_MIN( len, MAX_STREAM_CYPHER_LEN ); + buflen = amount + 2 + macLen; + if (buflen > ss->sec.writeBuf.space) { + rv = sslBuffer_Grow(&ss->sec.writeBuf, buflen); + if (rv != SECSuccess) { + goto loser; + } + } + out = ss->sec.writeBuf.buf; + nout = amount + macLen; + out[0] = 0x80 | MSB(nout); + out[1] = LSB(nout); + + /* Calculate MAC */ + rv = ssl2_CalcMAC(out+2, /* put MAC here */ + &ss->sec, + in, amount, /* input addr & length */ + 0); /* no padding */ + if (rv != SECSuccess) + goto loser; + + /* Encrypt MAC */ + rv = (*ss->sec.enc)(ss->sec.writecx, out+2, &nout, macLen, out+2, macLen); + if (rv) goto loser; + + /* Encrypt data from caller */ + rv = (*ss->sec.enc)(ss->sec.writecx, out+2+macLen, &nout, amount, in, amount); + if (rv) goto loser; + + ssl_ReleaseSpecReadLock(ss); /*************************************/ + + PRINT_BUF(50, (ss, "encrypted data:", out, buflen)); + + rv = ssl_DefSend(ss, out, buflen, flags & ~ssl_SEND_FLAG_MASK); + if (rv < 0) { + if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) { + SSL_TRC(50, ("%d: SSL[%d]: send stream would block, " + "saving data", SSL_GETPID(), ss->fd)); + rv = 0; + } else { + SSL_TRC(10, ("%d: SSL[%d]: send stream error %d", + SSL_GETPID(), ss->fd, PORT_GetError())); + /* Return short write if some data already went out... */ + if (count == 0) + count = rv; + goto done; + } + } + + if ((unsigned)rv < buflen) { + /* Short write. Save the data and return. */ + if (ssl_SaveWriteData(ss, out + rv, buflen - rv) == SECFailure) { + count = SECFailure; + } else { + count += amount; + ss->sec.sendSequence++; + } + goto done; + } + + ss->sec.sendSequence++; + in += amount; + count += amount; + len -= amount; + } + +done: + return count; + +loser: + ssl_ReleaseSpecReadLock(ss); + return SECFailure; +} + +/* +** Send some data, when using a block cipher. Package up the data with +** the length header and send it. +*/ +/* XXX assumes blocksize is > 7 */ +static PRInt32 +ssl2_SendBlock(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags) +{ + PRUint8 * out; /* begining of output buffer. */ + PRUint8 * op; /* next output byte goes here. */ + int rv; /* value from funcs we called. */ + int count = 0; /* this function's return value. */ + + unsigned int hlen; /* output record hdr len, 2 or 3 */ + unsigned int macLen; /* MAC is this many bytes long. */ + int amount; /* of plaintext to go in record. */ + unsigned int padding; /* add this many padding byte. */ + int nout; /* ciphertext size after header. */ + int buflen; /* size of generated record. */ + + PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); + + SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes using block cipher", + SSL_GETPID(), ss->fd, len)); + PRINT_BUF(50, (ss, "clear data:", in, len)); + + while (len) { + ssl_GetSpecReadLock(ss); /*************************************/ + + macLen = ss->sec.hash->length; + /* Figure out how much to send, including mac and padding */ + amount = PR_MIN( len, MAX_BLOCK_CYPHER_LEN ); + nout = amount + macLen; + padding = nout & (ss->sec.blockSize - 1); + if (padding) { + hlen = 3; + padding = ss->sec.blockSize - padding; + nout += padding; + } else { + hlen = 2; + } + buflen = hlen + nout; + if (buflen > ss->sec.writeBuf.space) { + rv = sslBuffer_Grow(&ss->sec.writeBuf, buflen); + if (rv != SECSuccess) { + goto loser; + } + } + out = ss->sec.writeBuf.buf; + + /* Construct header */ + op = out; + if (padding) { + *op++ = MSB(nout); + *op++ = LSB(nout); + *op++ = padding; + } else { + *op++ = 0x80 | MSB(nout); + *op++ = LSB(nout); + } + + /* Calculate MAC */ + rv = ssl2_CalcMAC(op, /* MAC goes here. */ + &ss->sec, + in, amount, /* intput addr, len */ + padding); + if (rv != SECSuccess) + goto loser; + op += macLen; + + /* Copy in the input data */ + /* XXX could eliminate the copy by folding it into the encryption */ + PORT_Memcpy(op, in, amount); + op += amount; + if (padding) { + PORT_Memset(op, padding, padding); + op += padding; + } + + /* Encrypt result */ + rv = (*ss->sec.enc)(ss->sec.writecx, out+hlen, &nout, buflen-hlen, + out+hlen, op - (out + hlen)); + if (rv) + goto loser; + + ssl_ReleaseSpecReadLock(ss); /*************************************/ + + PRINT_BUF(50, (ss, "final xmit data:", out, op - out)); + + rv = ssl_DefSend(ss, out, op - out, flags & ~ssl_SEND_FLAG_MASK); + if (rv < 0) { + if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) { + rv = 0; + } else { + SSL_TRC(10, ("%d: SSL[%d]: send block error %d", + SSL_GETPID(), ss->fd, PORT_GetError())); + /* Return short write if some data already went out... */ + if (count == 0) + count = rv; + goto done; + } + } + + if (rv < (op - out)) { + /* Short write. Save the data and return. */ + if (ssl_SaveWriteData(ss, out + rv, op - out - rv) == SECFailure) { + count = SECFailure; + } else { + count += amount; + ss->sec.sendSequence++; + } + goto done; + } + + ss->sec.sendSequence++; + in += amount; + count += amount; + len -= amount; + } + +done: + return count; + +loser: + ssl_ReleaseSpecReadLock(ss); + return SECFailure; +} + +/* +** Called from: ssl2_HandleServerHelloMessage, +** ssl2_HandleClientSessionKeyMessage, +** ssl2_HandleClientHelloMessage, +** +*/ +static void +ssl2_UseEncryptedSendFunc(sslSocket *ss) +{ + ssl_GetXmitBufLock(ss); + PORT_Assert(ss->sec.hashcx != 0); + + ss->gs.encrypted = 1; + ss->sec.send = (ss->sec.blockSize > 1) ? ssl2_SendBlock : ssl2_SendStream; + ssl_ReleaseXmitBufLock(ss); +} + +/* Called while initializing socket in ssl_CreateSecurityInfo(). +** This function allows us to keep the name of ssl2_SendClear static. +*/ +void +ssl2_UseClearSendFunc(sslSocket *ss) +{ + ss->sec.send = ssl2_SendClear; +} + +/************************************************************************ +** END of Send functions. * +*************************************************************************/ + +/*********************************************************************** + * For SSL3, this gathers in and handles records/messages until either + * the handshake is complete or application data is available. + * + * For SSL2, this gathers in only the next SSLV2 record. + * + * Called from ssl_Do1stHandshake() via function pointer ss->handshake. + * Caller must hold handshake lock. + * This function acquires and releases the RecvBufLock. + * + * returns SECSuccess for success. + * returns SECWouldBlock when that value is returned by ssl2_GatherRecord() or + * ssl3_GatherCompleteHandshake(). + * returns SECFailure on all other errors. + * + * The gather functions called by ssl_GatherRecord1stHandshake are expected + * to return values interpreted as follows: + * 1 : the function completed without error. + * 0 : the function read EOF. + * -1 : read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error. + * -2 : the function wants ssl_GatherRecord1stHandshake to be called again + * immediately, by ssl_Do1stHandshake. + * + * This code is similar to, and easily confused with, DoRecv() in sslsecur.c + * + * This function is called from ssl_Do1stHandshake(). + * The following functions put ssl_GatherRecord1stHandshake into ss->handshake: + * ssl2_HandleMessage + * ssl2_HandleVerifyMessage + * ssl2_HandleServerHelloMessage + * ssl2_BeginClientHandshake + * ssl2_HandleClientSessionKeyMessage + * ssl3_RestartHandshakeAfterCertReq + * ssl3_RestartHandshakeAfterServerCert + * ssl2_HandleClientHelloMessage + * ssl2_BeginServerHandshake + */ +SECStatus +ssl_GatherRecord1stHandshake(sslSocket *ss) +{ + int rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetRecvBufLock(ss); + + /* The special case DTLS logic is needed here because the SSL/TLS + * version wants to auto-detect SSL2 vs. SSL3 on the initial handshake + * (ss->version == 0) but with DTLS it gets confused, so we force the + * SSL3 version. + */ + if ((ss->version >= SSL_LIBRARY_VERSION_3_0) || IS_DTLS(ss)) { + /* Wait for handshake to complete, or application data to arrive. */ + rv = ssl3_GatherCompleteHandshake(ss, 0); + } else { + /* See if we have a complete record */ + rv = ssl2_GatherRecord(ss, 0); + } + SSL_TRC(10, ("%d: SSL[%d]: handshake gathering, rv=%d", + SSL_GETPID(), ss->fd, rv)); + + ssl_ReleaseRecvBufLock(ss); + + if (rv <= 0) { + if (rv == SECWouldBlock) { + /* Progress is blocked waiting for callback completion. */ + SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)", + SSL_GETPID(), ss->fd, ss->gs.remainder)); + return SECWouldBlock; + } + if (rv == 0) { + /* EOF. Loser */ + PORT_SetError(PR_END_OF_FILE_ERROR); + } + return SECFailure; /* rv is < 0 here. */ + } + + SSL_TRC(10, ("%d: SSL[%d]: got handshake record of %d bytes", + SSL_GETPID(), ss->fd, ss->gs.recordLen)); + + ss->handshake = 0; /* makes ssl_Do1stHandshake call ss->nextHandshake.*/ + return SECSuccess; +} + +/************************************************************************/ + +/* Called from ssl2_ServerSetupSessionCypher() + * ssl2_ClientSetupSessionCypher() + */ +static SECStatus +ssl2_FillInSID(sslSessionID * sid, + int cipher, + PRUint8 *keyData, + int keyLen, + PRUint8 *ca, + int caLen, + int keyBits, + int secretKeyBits, + SSLSignType authAlgorithm, + PRUint32 authKeyBits, + SSLKEAType keaType, + PRUint32 keaKeyBits) +{ + PORT_Assert(sid->references == 1); + PORT_Assert(sid->cached == never_cached); + PORT_Assert(sid->u.ssl2.masterKey.data == 0); + PORT_Assert(sid->u.ssl2.cipherArg.data == 0); + + sid->version = SSL_LIBRARY_VERSION_2; + + sid->u.ssl2.cipherType = cipher; + sid->u.ssl2.masterKey.data = (PRUint8*) PORT_Alloc(keyLen); + if (!sid->u.ssl2.masterKey.data) { + return SECFailure; + } + PORT_Memcpy(sid->u.ssl2.masterKey.data, keyData, keyLen); + sid->u.ssl2.masterKey.len = keyLen; + sid->u.ssl2.keyBits = keyBits; + sid->u.ssl2.secretKeyBits = secretKeyBits; + sid->authAlgorithm = authAlgorithm; + sid->authKeyBits = authKeyBits; + sid->keaType = keaType; + sid->keaKeyBits = keaKeyBits; + sid->lastAccessTime = sid->creationTime = ssl_Time(); + sid->expirationTime = sid->creationTime + ssl_sid_timeout; + + if (caLen) { + sid->u.ssl2.cipherArg.data = (PRUint8*) PORT_Alloc(caLen); + if (!sid->u.ssl2.cipherArg.data) { + return SECFailure; + } + sid->u.ssl2.cipherArg.len = caLen; + PORT_Memcpy(sid->u.ssl2.cipherArg.data, ca, caLen); + } + return SECSuccess; +} + +/* +** Construct session keys given the masterKey (tied to the session-id), +** the client's challenge and the server's nonce. +** +** Called from ssl2_CreateSessionCypher() <- +*/ +static SECStatus +ssl2_ProduceKeys(sslSocket * ss, + SECItem * readKey, + SECItem * writeKey, + SECItem * masterKey, + PRUint8 * challenge, + PRUint8 * nonce, + int cipherType) +{ + PK11Context * cx = 0; + unsigned nkm = 0; /* number of hashes to generate key mat. */ + unsigned nkd = 0; /* size of readKey and writeKey. */ + unsigned part; + unsigned i; + unsigned off; + SECStatus rv; + PRUint8 countChar; + PRUint8 km[3*16]; /* buffer for key material. */ + + readKey->data = 0; + writeKey->data = 0; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + rv = SECSuccess; + cx = PK11_CreateDigestContext(SEC_OID_MD5); + if (cx == NULL) { + ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); + return SECFailure; + } + + nkm = ssl_Specs[cipherType].nkm; + nkd = ssl_Specs[cipherType].nkd; + + readKey->data = (PRUint8*) PORT_Alloc(nkd); + if (!readKey->data) + goto loser; + readKey->len = nkd; + + writeKey->data = (PRUint8*) PORT_Alloc(nkd); + if (!writeKey->data) + goto loser; + writeKey->len = nkd; + + /* Produce key material */ + countChar = '0'; + for (i = 0, off = 0; i < nkm; i++, off += 16) { + rv = PK11_DigestBegin(cx); + rv |= PK11_DigestOp(cx, masterKey->data, masterKey->len); + rv |= PK11_DigestOp(cx, &countChar, 1); + rv |= PK11_DigestOp(cx, challenge, SSL_CHALLENGE_BYTES); + rv |= PK11_DigestOp(cx, nonce, SSL_CONNECTIONID_BYTES); + rv |= PK11_DigestFinal(cx, km+off, &part, MD5_LENGTH); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); + rv = SECFailure; + goto loser; + } + countChar++; + } + + /* Produce keys */ + PORT_Memcpy(readKey->data, km, nkd); + PORT_Memcpy(writeKey->data, km + nkd, nkd); + +loser: + PK11_DestroyContext(cx, PR_TRUE); + return rv; +} + +/* Called from ssl2_ServerSetupSessionCypher() +** <- ssl2_HandleClientSessionKeyMessage() +** <- ssl2_HandleClientHelloMessage() +** and from ssl2_ClientSetupSessionCypher() +** <- ssl2_HandleServerHelloMessage() +*/ +static SECStatus +ssl2_CreateSessionCypher(sslSocket *ss, sslSessionID *sid, PRBool isClient) +{ + SECItem * rk = NULL; + SECItem * wk = NULL; + SECItem * param; + SECStatus rv; + int cipherType = sid->u.ssl2.cipherType; + PK11SlotInfo * slot = NULL; + CK_MECHANISM_TYPE mechanism; + SECItem readKey; + SECItem writeKey; + + void *readcx = 0; + void *writecx = 0; + readKey.data = 0; + writeKey.data = 0; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + if (ss->sec.ci.sid == 0) + goto sec_loser; /* don't crash if asserts are off */ + + /* Trying to cut down on all these switch statements that should be tables. + * So, test cipherType once, here, and then use tables below. + */ + switch (cipherType) { + case SSL_CK_RC4_128_EXPORT40_WITH_MD5: + case SSL_CK_RC4_128_WITH_MD5: + case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: + case SSL_CK_RC2_128_CBC_WITH_MD5: + case SSL_CK_DES_64_CBC_WITH_MD5: + case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: + break; + + default: + SSL_DBG(("%d: SSL[%d]: ssl2_CreateSessionCypher: unknown cipher=%d", + SSL_GETPID(), ss->fd, cipherType)); + PORT_SetError(isClient ? SSL_ERROR_BAD_SERVER : SSL_ERROR_BAD_CLIENT); + goto sec_loser; + } + + rk = isClient ? &readKey : &writeKey; + wk = isClient ? &writeKey : &readKey; + + /* Produce the keys for this session */ + rv = ssl2_ProduceKeys(ss, &readKey, &writeKey, &sid->u.ssl2.masterKey, + ss->sec.ci.clientChallenge, ss->sec.ci.connectionID, + cipherType); + if (rv != SECSuccess) + goto loser; + PRINT_BUF(7, (ss, "Session read-key: ", rk->data, rk->len)); + PRINT_BUF(7, (ss, "Session write-key: ", wk->data, wk->len)); + + PORT_Memcpy(ss->sec.ci.readKey, readKey.data, readKey.len); + PORT_Memcpy(ss->sec.ci.writeKey, writeKey.data, writeKey.len); + ss->sec.ci.keySize = readKey.len; + + /* Setup the MAC */ + rv = ssl2_CreateMAC(&ss->sec, rk, wk, cipherType); + if (rv != SECSuccess) + goto loser; + + /* First create the session key object */ + SSL_TRC(3, ("%d: SSL[%d]: using %s", SSL_GETPID(), ss->fd, + ssl_cipherName[cipherType])); + + + mechanism = ssl_Specs[cipherType].mechanism; + + /* set destructer before we call loser... */ + ss->sec.destroy = (void (*)(void*, PRBool)) PK11_DestroyContext; + slot = PK11_GetBestSlot(mechanism, ss->pkcs11PinArg); + if (slot == NULL) + goto loser; + + param = PK11_ParamFromIV(mechanism, &sid->u.ssl2.cipherArg); + if (param == NULL) + goto loser; + readcx = PK11_CreateContextByRawKey(slot, mechanism, PK11_OriginUnwrap, + CKA_DECRYPT, rk, param, + ss->pkcs11PinArg); + SECITEM_FreeItem(param, PR_TRUE); + if (readcx == NULL) + goto loser; + + /* build the client context */ + param = PK11_ParamFromIV(mechanism, &sid->u.ssl2.cipherArg); + if (param == NULL) + goto loser; + writecx = PK11_CreateContextByRawKey(slot, mechanism, PK11_OriginUnwrap, + CKA_ENCRYPT, wk, param, + ss->pkcs11PinArg); + SECITEM_FreeItem(param,PR_TRUE); + if (writecx == NULL) + goto loser; + PK11_FreeSlot(slot); + + rv = SECSuccess; + ss->sec.enc = (SSLCipher) PK11_CipherOp; + ss->sec.dec = (SSLCipher) PK11_CipherOp; + ss->sec.readcx = (void *) readcx; + ss->sec.writecx = (void *) writecx; + ss->sec.blockSize = ssl_Specs[cipherType].blockSize; + ss->sec.blockShift = ssl_Specs[cipherType].blockShift; + ss->sec.cipherType = sid->u.ssl2.cipherType; + ss->sec.keyBits = sid->u.ssl2.keyBits; + ss->sec.secretKeyBits = sid->u.ssl2.secretKeyBits; + goto done; + + loser: + if (ss->sec.destroy) { + if (readcx) (*ss->sec.destroy)(readcx, PR_TRUE); + if (writecx) (*ss->sec.destroy)(writecx, PR_TRUE); + } + ss->sec.destroy = NULL; + if (slot) PK11_FreeSlot(slot); + + sec_loser: + rv = SECFailure; + + done: + if (rk) { + SECITEM_ZfreeItem(rk, PR_FALSE); + } + if (wk) { + SECITEM_ZfreeItem(wk, PR_FALSE); + } + return rv; +} + +/* +** Setup the server ciphers given information from a CLIENT-MASTER-KEY +** message. +** "ss" pointer to the ssl-socket object +** "cipher" the cipher type to use +** "keyBits" the size of the final cipher key +** "ck" the clear-key data +** "ckLen" the number of bytes of clear-key data +** "ek" the encrypted-key data +** "ekLen" the number of bytes of encrypted-key data +** "ca" the cipher-arg data +** "caLen" the number of bytes of cipher-arg data +** +** The MASTER-KEY is constructed by first decrypting the encrypted-key +** data. This produces the SECRET-KEY-DATA. The MASTER-KEY is composed by +** concatenating the clear-key data with the SECRET-KEY-DATA. This code +** checks to make sure that the client didn't send us an improper amount +** of SECRET-KEY-DATA (it restricts the length of that data to match the +** spec). +** +** Called from ssl2_HandleClientSessionKeyMessage(). +*/ +static SECStatus +ssl2_ServerSetupSessionCypher(sslSocket *ss, int cipher, unsigned int keyBits, + PRUint8 *ck, unsigned int ckLen, + PRUint8 *ek, unsigned int ekLen, + PRUint8 *ca, unsigned int caLen) +{ + PRUint8 * dk = NULL; /* decrypted master key */ + sslSessionID * sid; + sslServerCerts * sc = ss->serverCerts + kt_rsa; + PRUint8 * kbuf = 0; /* buffer for RSA decrypted data. */ + unsigned int ddLen; /* length of RSA decrypted data in kbuf */ + unsigned int keySize; + unsigned int dkLen; /* decrypted key length in bytes */ + int modulusLen; + SECStatus rv; + PRUint16 allowed; /* cipher kinds enabled and allowed by policy */ + PRUint8 mkbuf[SSL_MAX_MASTER_KEY_BYTES]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + PORT_Assert((sc->SERVERKEY != 0)); + PORT_Assert((ss->sec.ci.sid != 0)); + sid = ss->sec.ci.sid; + + /* Trying to cut down on all these switch statements that should be tables. + * So, test cipherType once, here, and then use tables below. + */ + switch (cipher) { + case SSL_CK_RC4_128_EXPORT40_WITH_MD5: + case SSL_CK_RC4_128_WITH_MD5: + case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: + case SSL_CK_RC2_128_CBC_WITH_MD5: + case SSL_CK_DES_64_CBC_WITH_MD5: + case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: + break; + + default: + SSL_DBG(("%d: SSL[%d]: ssl2_ServerSetupSessionCypher: unknown cipher=%d", + SSL_GETPID(), ss->fd, cipher)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + allowed = ss->allowedByPolicy & ss->chosenPreference & SSL_CB_IMPLEMENTED; + if (!(allowed & (1 << cipher))) { + /* client chose a kind we don't allow! */ + SSL_DBG(("%d: SSL[%d]: disallowed cipher=%d", + SSL_GETPID(), ss->fd, cipher)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + keySize = ssl_Specs[cipher].keyLen; + if (keyBits != keySize * BPB) { + SSL_DBG(("%d: SSL[%d]: invalid master secret key length=%d (bits)!", + SSL_GETPID(), ss->fd, keyBits)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + if (ckLen != ssl_Specs[cipher].pubLen) { + SSL_DBG(("%d: SSL[%d]: invalid clear key length, ckLen=%d (bytes)!", + SSL_GETPID(), ss->fd, ckLen)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + if (caLen != ssl_Specs[cipher].ivLen) { + SSL_DBG(("%d: SSL[%d]: invalid key args length, caLen=%d (bytes)!", + SSL_GETPID(), ss->fd, caLen)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + modulusLen = PK11_GetPrivateModulusLen(sc->SERVERKEY); + if (modulusLen == -1) { + /* XXX If the key is bad, then PK11_PubDecryptRaw will fail below. */ + modulusLen = ekLen; + } + if (ekLen > modulusLen || ekLen + ckLen < keySize) { + SSL_DBG(("%d: SSL[%d]: invalid encrypted key length, ekLen=%d (bytes)!", + SSL_GETPID(), ss->fd, ekLen)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto loser; + } + + /* allocate the buffer to hold the decrypted portion of the key. */ + kbuf = (PRUint8*)PORT_Alloc(modulusLen); + if (!kbuf) { + goto loser; + } + dkLen = keySize - ckLen; + dk = kbuf + modulusLen - dkLen; + + /* Decrypt encrypted half of the key. + ** NOTE: PK11_PubDecryptRaw will barf on a non-RSA key. This is + ** desired behavior here. + */ + rv = PK11_PubDecryptRaw(sc->SERVERKEY, kbuf, &ddLen, modulusLen, ek, ekLen); + if (rv != SECSuccess) + goto hide_loser; + + /* Is the length of the decrypted data (ddLen) the expected value? */ + if (modulusLen != ddLen) + goto hide_loser; + + /* Cheaply verify that PKCS#1 was used to format the encryption block */ + if ((kbuf[0] != 0x00) || (kbuf[1] != 0x02) || (dk[-1] != 0x00)) { + SSL_DBG(("%d: SSL[%d]: strange encryption block", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto hide_loser; + } + + /* Make sure we're not subject to a version rollback attack. */ + if (!SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { + static const PRUint8 threes[8] = { 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03 }; + + if (PORT_Memcmp(dk - 8 - 1, threes, 8) == 0) { + PORT_SetError(SSL_ERROR_BAD_CLIENT); + goto hide_loser; + } + } + if (0) { +hide_loser: + /* Defense against the Bleichenbacher attack. + * Provide the client with NO CLUES that the decrypted master key + * was erroneous. Don't send any error messages. + * Instead, Generate a completely bogus master key . + */ + PK11_GenerateRandom(dk, dkLen); + } + + /* + ** Construct master key out of the pieces. + */ + if (ckLen) { + PORT_Memcpy(mkbuf, ck, ckLen); + } + PORT_Memcpy(mkbuf + ckLen, dk, dkLen); + + /* Fill in session-id */ + rv = ssl2_FillInSID(sid, cipher, mkbuf, keySize, ca, caLen, + keyBits, keyBits - (ckLen<<3), + ss->sec.authAlgorithm, ss->sec.authKeyBits, + ss->sec.keaType, ss->sec.keaKeyBits); + if (rv != SECSuccess) { + goto loser; + } + + /* Create session ciphers */ + rv = ssl2_CreateSessionCypher(ss, sid, PR_FALSE); + if (rv != SECSuccess) { + goto loser; + } + + SSL_TRC(1, ("%d: SSL[%d]: server, using %s cipher, clear=%d total=%d", + SSL_GETPID(), ss->fd, ssl_cipherName[cipher], + ckLen<<3, keySize<<3)); + rv = SECSuccess; + goto done; + + loser: + rv = SECFailure; + + done: + PORT_Free(kbuf); + return rv; +} + +/************************************************************************/ + +/* +** Rewrite the incoming cipher specs, comparing to list of specs we support, +** (ss->cipherSpecs) and eliminating anything we don't support +** +* Note: Our list may contain SSL v3 ciphers. +* We MUST NOT match on any of those. +* Fortunately, this is easy to detect because SSLv3 ciphers have zero +* in the first byte, and none of the SSLv2 ciphers do. +* +* Called from ssl2_HandleClientHelloMessage(). +* Returns the number of bytes of "qualified cipher specs", +* which is typically a multiple of 3, but will be zero if there are none. +*/ +static int +ssl2_QualifyCypherSpecs(sslSocket *ss, + PRUint8 * cs, /* cipher specs in client hello msg. */ + int csLen) +{ + PRUint8 * ms; + PRUint8 * hs; + PRUint8 * qs; + int mc; + int hc; + PRUint8 qualifiedSpecs[ssl2_NUM_SUITES_IMPLEMENTED * 3]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + + if (!ss->cipherSpecs) { + SECStatus rv = ssl2_ConstructCipherSpecs(ss); + if (rv != SECSuccess || !ss->cipherSpecs) + return 0; + } + + PRINT_BUF(10, (ss, "specs from client:", cs, csLen)); + qs = qualifiedSpecs; + ms = ss->cipherSpecs; + for (mc = ss->sizeCipherSpecs; mc > 0; mc -= 3, ms += 3) { + if (ms[0] == 0) + continue; + for (hs = cs, hc = csLen; hc > 0; hs += 3, hc -= 3) { + if ((hs[0] == ms[0]) && + (hs[1] == ms[1]) && + (hs[2] == ms[2])) { + /* Copy this cipher spec into the "keep" section */ + qs[0] = hs[0]; + qs[1] = hs[1]; + qs[2] = hs[2]; + qs += 3; + break; + } + } + } + hc = qs - qualifiedSpecs; + PRINT_BUF(10, (ss, "qualified specs from client:", qualifiedSpecs, hc)); + PORT_Memcpy(cs, qualifiedSpecs, hc); + return hc; +} + +/* +** Pick the best cipher we can find, given the array of server cipher +** specs. Returns cipher number (e.g. SSL_CK_*), or -1 for no overlap. +** If successful, stores the master key size (bytes) in *pKeyLen. +** +** This is correct only for the client side, but presently +** this function is only called from +** ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage() +** +** Note that most servers only return a single cipher suite in their +** ServerHello messages. So, the code below for finding the "best" cipher +** suite usually has only one choice. The client and server should send +** their cipher suite lists sorted in descending order by preference. +*/ +static int +ssl2_ChooseSessionCypher(sslSocket *ss, + int hc, /* number of cs's in hs. */ + PRUint8 * hs, /* server hello's cipher suites. */ + int * pKeyLen) /* out: sym key size in bytes. */ +{ + PRUint8 * ms; + unsigned int i; + int bestKeySize; + int bestRealKeySize; + int bestCypher; + int keySize; + int realKeySize; + PRUint8 * ohs = hs; + const PRUint8 * preferred; + static const PRUint8 noneSuch[3] = { 0, 0, 0 }; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + + if (!ss->cipherSpecs) { + SECStatus rv = ssl2_ConstructCipherSpecs(ss); + if (rv != SECSuccess || !ss->cipherSpecs) + goto loser; + } + + if (!ss->preferredCipher) { + unsigned int allowed = ss->allowedByPolicy & ss->chosenPreference & + SSL_CB_IMPLEMENTED; + if (allowed) { + preferred = implementedCipherSuites; + for (i = ssl2_NUM_SUITES_IMPLEMENTED; i > 0; --i) { + if (0 != (allowed & (1U << preferred[0]))) { + ss->preferredCipher = preferred; + break; + } + preferred += 3; + } + } + } + preferred = ss->preferredCipher ? ss->preferredCipher : noneSuch; + /* + ** Scan list of ciphers received from peer and look for a match in + ** our list. + * Note: Our list may contain SSL v3 ciphers. + * We MUST NOT match on any of those. + * Fortunately, this is easy to detect because SSLv3 ciphers have zero + * in the first byte, and none of the SSLv2 ciphers do. + */ + bestKeySize = bestRealKeySize = 0; + bestCypher = -1; + while (--hc >= 0) { + for (i = 0, ms = ss->cipherSpecs; i < ss->sizeCipherSpecs; i += 3, ms += 3) { + if ((hs[0] == preferred[0]) && + (hs[1] == preferred[1]) && + (hs[2] == preferred[2]) && + hs[0] != 0) { + /* Pick this cipher immediately! */ + *pKeyLen = (((hs[1] << 8) | hs[2]) + 7) >> 3; + return hs[0]; + } + if ((hs[0] == ms[0]) && (hs[1] == ms[1]) && (hs[2] == ms[2]) && + hs[0] != 0) { + /* Found a match */ + + /* Use secret keySize to determine which cipher is best */ + realKeySize = (hs[1] << 8) | hs[2]; + switch (hs[0]) { + case SSL_CK_RC4_128_EXPORT40_WITH_MD5: + case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: + keySize = 40; + break; + default: + keySize = realKeySize; + break; + } + if (keySize > bestKeySize) { + bestCypher = hs[0]; + bestKeySize = keySize; + bestRealKeySize = realKeySize; + } + } + } + hs += 3; + } + if (bestCypher < 0) { + /* + ** No overlap between server and client. Re-examine server list + ** to see what kind of ciphers it does support so that we can set + ** the error code appropriately. + */ + if ((ohs[0] == SSL_CK_RC4_128_WITH_MD5) || + (ohs[0] == SSL_CK_RC2_128_CBC_WITH_MD5)) { + PORT_SetError(SSL_ERROR_US_ONLY_SERVER); + } else if ((ohs[0] == SSL_CK_RC4_128_EXPORT40_WITH_MD5) || + (ohs[0] == SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)) { + PORT_SetError(SSL_ERROR_EXPORT_ONLY_SERVER); + } else { + PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); + } + SSL_DBG(("%d: SSL[%d]: no cipher overlap", SSL_GETPID(), ss->fd)); + goto loser; + } + *pKeyLen = (bestRealKeySize + 7) >> 3; + return bestCypher; + + loser: + return -1; +} + +static SECStatus +ssl2_ClientHandleServerCert(sslSocket *ss, PRUint8 *certData, int certLen) +{ + CERTCertificate *cert = NULL; + SECItem certItem; + + certItem.data = certData; + certItem.len = certLen; + + /* decode the certificate */ + cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, + PR_FALSE, PR_TRUE); + + if (cert == NULL) { + SSL_DBG(("%d: SSL[%d]: decode of server certificate fails", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; + } + +#ifdef TRACE + { + if (ssl_trace >= 1) { + char *issuer; + char *subject; + issuer = CERT_NameToAscii(&cert->issuer); + subject = CERT_NameToAscii(&cert->subject); + SSL_TRC(1,("%d: server certificate issuer: '%s'", + SSL_GETPID(), issuer ? issuer : "OOPS")); + SSL_TRC(1,("%d: server name: '%s'", + SSL_GETPID(), subject ? subject : "OOPS")); + PORT_Free(issuer); + PORT_Free(subject); + } + } +#endif + + ss->sec.peerCert = cert; + return SECSuccess; +} + + +/* + * Format one block of data for public/private key encryption using + * the rules defined in PKCS #1. SSL2 does this itself to handle the + * rollback detection. + */ +#define RSA_BLOCK_MIN_PAD_LEN 8 +#define RSA_BLOCK_FIRST_OCTET 0x00 +#define RSA_BLOCK_AFTER_PAD_OCTET 0x00 +#define RSA_BLOCK_PUBLIC_OCTET 0x02 +unsigned char * +ssl_FormatSSL2Block(unsigned modulusLen, SECItem *data) +{ + unsigned char *block; + unsigned char *bp; + int padLen; + SECStatus rv; + int i; + + if (modulusLen < data->len + (3 + RSA_BLOCK_MIN_PAD_LEN)) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return NULL; + } + block = (unsigned char *) PORT_Alloc(modulusLen); + if (block == NULL) + return NULL; + + bp = block; + + /* + * All RSA blocks start with two octets: + * 0x00 || BlockType + */ + *bp++ = RSA_BLOCK_FIRST_OCTET; + *bp++ = RSA_BLOCK_PUBLIC_OCTET; + + /* + * 0x00 || BT || Pad || 0x00 || ActualData + * 1 1 padLen 1 data->len + * Pad is all non-zero random bytes. + */ + padLen = modulusLen - data->len - 3; + PORT_Assert (padLen >= RSA_BLOCK_MIN_PAD_LEN); + rv = PK11_GenerateRandom(bp, padLen); + if (rv == SECFailure) goto loser; + /* replace all the 'zero' bytes */ + for (i = 0; i < padLen; i++) { + while (bp[i] == RSA_BLOCK_AFTER_PAD_OCTET) { + rv = PK11_GenerateRandom(bp+i, 1); + if (rv == SECFailure) goto loser; + } + } + bp += padLen; + *bp++ = RSA_BLOCK_AFTER_PAD_OCTET; + PORT_Memcpy (bp, data->data, data->len); + + return block; +loser: + if (block) PORT_Free(block); + return NULL; +} + +/* +** Given the server's public key and cipher specs, generate a session key +** that is ready to use for encrypting/decrypting the byte stream. At +** the same time, generate the SSL_MT_CLIENT_MASTER_KEY message and +** send it to the server. +** +** Called from ssl2_HandleServerHelloMessage() +*/ +static SECStatus +ssl2_ClientSetupSessionCypher(sslSocket *ss, PRUint8 *cs, int csLen) +{ + sslSessionID * sid; + PRUint8 * ca; /* points to iv data, or NULL if none. */ + PRUint8 * ekbuf = 0; + CERTCertificate * cert = 0; + SECKEYPublicKey * serverKey = 0; + unsigned modulusLen = 0; + SECStatus rv; + int cipher; + int keyLen; /* cipher symkey size in bytes. */ + int ckLen; /* publicly reveal this many bytes of key. */ + int caLen; /* length of IV data at *ca. */ + int nc; + + unsigned char *eblock; /* holds unencrypted PKCS#1 formatted key. */ + SECItem rek; /* holds portion of symkey to be encrypted. */ + + PRUint8 keyData[SSL_MAX_MASTER_KEY_BYTES]; + PRUint8 iv [8]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + eblock = NULL; + + sid = ss->sec.ci.sid; + PORT_Assert(sid != 0); + + cert = ss->sec.peerCert; + + serverKey = CERT_ExtractPublicKey(cert); + if (!serverKey) { + SSL_DBG(("%d: SSL[%d]: extract public key failed: error=%d", + SSL_GETPID(), ss->fd, PORT_GetError())); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + rv = SECFailure; + goto loser2; + } + + ss->sec.authAlgorithm = ssl_sign_rsa; + ss->sec.keaType = ssl_kea_rsa; + ss->sec.keaKeyBits = \ + ss->sec.authKeyBits = SECKEY_PublicKeyStrengthInBits(serverKey); + + /* Choose a compatible cipher with the server */ + nc = csLen / 3; + cipher = ssl2_ChooseSessionCypher(ss, nc, cs, &keyLen); + if (cipher < 0) { + /* ssl2_ChooseSessionCypher has set error code. */ + ssl2_SendErrorMessage(ss, SSL_PE_NO_CYPHERS); + goto loser; + } + + /* Generate the random keys */ + PK11_GenerateRandom(keyData, sizeof(keyData)); + + /* + ** Next, carve up the keys into clear and encrypted portions. The + ** clear data is taken from the start of keyData and the encrypted + ** portion from the remainder. Note that each of these portions is + ** carved in half, one half for the read-key and one for the + ** write-key. + */ + ca = 0; + + /* We know that cipher is a legit value here, because + * ssl2_ChooseSessionCypher doesn't return bogus values. + */ + ckLen = ssl_Specs[cipher].pubLen; /* cleartext key length. */ + caLen = ssl_Specs[cipher].ivLen; /* IV length. */ + if (caLen) { + PORT_Assert(sizeof iv >= caLen); + PK11_GenerateRandom(iv, caLen); + ca = iv; + } + + /* Fill in session-id */ + rv = ssl2_FillInSID(sid, cipher, keyData, keyLen, + ca, caLen, keyLen << 3, (keyLen - ckLen) << 3, + ss->sec.authAlgorithm, ss->sec.authKeyBits, + ss->sec.keaType, ss->sec.keaKeyBits); + if (rv != SECSuccess) { + goto loser; + } + + SSL_TRC(1, ("%d: SSL[%d]: client, using %s cipher, clear=%d total=%d", + SSL_GETPID(), ss->fd, ssl_cipherName[cipher], + ckLen<<3, keyLen<<3)); + + /* Now setup read and write ciphers */ + rv = ssl2_CreateSessionCypher(ss, sid, PR_TRUE); + if (rv != SECSuccess) { + goto loser; + } + + /* + ** Fill in the encryption buffer with some random bytes. Then + ** copy in the portion of the session key we are encrypting. + */ + modulusLen = SECKEY_PublicKeyStrength(serverKey); + rek.data = keyData + ckLen; + rek.len = keyLen - ckLen; + eblock = ssl_FormatSSL2Block(modulusLen, &rek); + if (eblock == NULL) + goto loser; + + /* Set up the padding for version 2 rollback detection. */ + /* XXX We should really use defines here */ + if (!SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { + PORT_Assert((modulusLen - rek.len) > 12); + PORT_Memset(eblock + modulusLen - rek.len - 8 - 1, 0x03, 8); + } + ekbuf = (PRUint8*) PORT_Alloc(modulusLen); + if (!ekbuf) + goto loser; + PRINT_BUF(10, (ss, "master key encryption block:", + eblock, modulusLen)); + + /* Encrypt ekitem */ + rv = PK11_PubEncryptRaw(serverKey, ekbuf, eblock, modulusLen, + ss->pkcs11PinArg); + if (rv) + goto loser; + + /* Now we have everything ready to send */ + rv = ssl2_SendSessionKeyMessage(ss, cipher, keyLen << 3, ca, caLen, + keyData, ckLen, ekbuf, modulusLen); + if (rv != SECSuccess) { + goto loser; + } + rv = SECSuccess; + goto done; + + loser: + rv = SECFailure; + + loser2: + done: + PORT_Memset(keyData, 0, sizeof(keyData)); + PORT_ZFree(ekbuf, modulusLen); + PORT_ZFree(eblock, modulusLen); + SECKEY_DestroyPublicKey(serverKey); + return rv; +} + +/************************************************************************/ + +/* + * Called from ssl2_HandleMessage in response to SSL_MT_SERVER_FINISHED message. + * Caller holds recvBufLock and handshakeLock + */ +static void +ssl2_ClientRegSessionID(sslSocket *ss, PRUint8 *s) +{ + sslSessionID *sid = ss->sec.ci.sid; + + /* Record entry in nonce cache */ + if (sid->peerCert == NULL) { + PORT_Memcpy(sid->u.ssl2.sessionID, s, sizeof(sid->u.ssl2.sessionID)); + sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); + + } + if (!ss->opt.noCache) + (*ss->sec.cache)(sid); +} + +/* Called from ssl2_HandleMessage() */ +static SECStatus +ssl2_TriggerNextMessage(sslSocket *ss) +{ + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + if ((ss->sec.ci.requiredElements & CIS_HAVE_CERTIFICATE) && + !(ss->sec.ci.sentElements & CIS_HAVE_CERTIFICATE)) { + ss->sec.ci.sentElements |= CIS_HAVE_CERTIFICATE; + rv = ssl2_SendCertificateRequestMessage(ss); + return rv; + } + return SECSuccess; +} + +/* See if it's time to send our finished message, or if the handshakes are +** complete. Send finished message if appropriate. +** Returns SECSuccess unless anything goes wrong. +** +** Called from ssl2_HandleMessage, +** ssl2_HandleVerifyMessage +** ssl2_HandleServerHelloMessage +** ssl2_HandleClientSessionKeyMessage +*/ +static SECStatus +ssl2_TryToFinish(sslSocket *ss) +{ + SECStatus rv; + char e, ef; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + e = ss->sec.ci.elements; + ef = e | CIS_HAVE_FINISHED; + if ((ef & ss->sec.ci.requiredElements) == ss->sec.ci.requiredElements) { + if (ss->sec.isServer) { + /* Send server finished message if we already didn't */ + rv = ssl2_SendServerFinishedMessage(ss); + } else { + /* Send client finished message if we already didn't */ + rv = ssl2_SendClientFinishedMessage(ss); + } + if (rv != SECSuccess) { + return rv; + } + if ((e & ss->sec.ci.requiredElements) == ss->sec.ci.requiredElements) { + /* Totally finished */ + ss->handshake = 0; + return SECSuccess; + } + } + return SECSuccess; +} + +/* +** Called from ssl2_HandleRequestCertificate +*/ +static SECStatus +ssl2_SignResponse(sslSocket *ss, + SECKEYPrivateKey *key, + SECItem *response) +{ + SGNContext * sgn = NULL; + PRUint8 * challenge; + unsigned int len; + SECStatus rv = SECFailure; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + challenge = ss->sec.ci.serverChallenge; + len = ss->sec.ci.serverChallengeLen; + + /* Sign the expected data... */ + sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,key); + if (!sgn) + goto done; + rv = SGN_Begin(sgn); + if (rv != SECSuccess) + goto done; + rv = SGN_Update(sgn, ss->sec.ci.readKey, ss->sec.ci.keySize); + if (rv != SECSuccess) + goto done; + rv = SGN_Update(sgn, ss->sec.ci.writeKey, ss->sec.ci.keySize); + if (rv != SECSuccess) + goto done; + rv = SGN_Update(sgn, challenge, len); + if (rv != SECSuccess) + goto done; + rv = SGN_Update(sgn, ss->sec.peerCert->derCert.data, + ss->sec.peerCert->derCert.len); + if (rv != SECSuccess) + goto done; + rv = SGN_End(sgn, response); + if (rv != SECSuccess) + goto done; + +done: + SGN_DestroyContext(sgn, PR_TRUE); + return rv == SECSuccess ? SECSuccess : SECFailure; +} + +/* +** Try to handle a request-certificate message. Get client's certificate +** and private key and sign a message for the server to see. +** Caller must hold handshakeLock +** +** Called from ssl2_HandleMessage(). +*/ +static int +ssl2_HandleRequestCertificate(sslSocket *ss) +{ + CERTCertificate * cert = NULL; /* app-selected client cert. */ + SECKEYPrivateKey *key = NULL; /* priv key for cert. */ + SECStatus rv; + SECItem response; + int ret = 0; + PRUint8 authType; + + + /* + * These things all need to be initialized before we can "goto loser". + */ + response.data = NULL; + + /* get challenge info from connectionInfo */ + authType = ss->sec.ci.authType; + + if (authType != SSL_AT_MD5_WITH_RSA_ENCRYPTION) { + SSL_TRC(7, ("%d: SSL[%d]: unsupported auth type 0x%x", SSL_GETPID(), + ss->fd, authType)); + goto no_cert_error; + } + + /* Get certificate and private-key from client */ + if (!ss->getClientAuthData) { + SSL_TRC(7, ("%d: SSL[%d]: client doesn't support client-auth", + SSL_GETPID(), ss->fd)); + goto no_cert_error; + } + ret = (*ss->getClientAuthData)(ss->getClientAuthDataArg, ss->fd, + NULL, &cert, &key); + if ( ret == SECWouldBlock ) { + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); + ret = -1; + goto loser; + } + + if (ret) { + goto no_cert_error; + } + + /* check what the callback function returned */ + if ((!cert) || (!key)) { + /* we are missing either the key or cert */ + if (cert) { + /* got a cert, but no key - free it */ + CERT_DestroyCertificate(cert); + cert = NULL; + } + if (key) { + /* got a key, but no cert - free it */ + SECKEY_DestroyPrivateKey(key); + key = NULL; + } + goto no_cert_error; + } + + rv = ssl2_SignResponse(ss, key, &response); + if ( rv != SECSuccess ) { + ret = -1; + goto loser; + } + + /* Send response message */ + ret = ssl2_SendCertificateResponseMessage(ss, &cert->derCert, &response); + + /* Now, remember the cert we sent. But first, forget any previous one. */ + if (ss->sec.localCert) { + CERT_DestroyCertificate(ss->sec.localCert); + } + ss->sec.localCert = CERT_DupCertificate(cert); + PORT_Assert(!ss->sec.ci.sid->localCert); + if (ss->sec.ci.sid->localCert) { + CERT_DestroyCertificate(ss->sec.ci.sid->localCert); + } + ss->sec.ci.sid->localCert = cert; + cert = NULL; + + goto done; + + no_cert_error: + SSL_TRC(7, ("%d: SSL[%d]: no certificate (ret=%d)", SSL_GETPID(), + ss->fd, ret)); + ret = ssl2_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE); + + loser: + done: + if ( cert ) { + CERT_DestroyCertificate(cert); + } + if ( key ) { + SECKEY_DestroyPrivateKey(key); + } + if ( response.data ) { + PORT_Free(response.data); + } + + return ret; +} + +/* +** Called from ssl2_HandleMessage for SSL_MT_CLIENT_CERTIFICATE message. +** Caller must hold HandshakeLock and RecvBufLock, since cd and response +** are contained in the gathered input data. +*/ +static SECStatus +ssl2_HandleClientCertificate(sslSocket * ss, + PRUint8 certType, /* XXX unused */ + PRUint8 * cd, + unsigned int cdLen, + PRUint8 * response, + unsigned int responseLen) +{ + CERTCertificate *cert = NULL; + SECKEYPublicKey *pubKey = NULL; + VFYContext * vfy = NULL; + SECItem * derCert; + SECStatus rv = SECFailure; + SECItem certItem; + SECItem rep; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); + + /* Extract the certificate */ + certItem.data = cd; + certItem.len = cdLen; + + cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, + PR_FALSE, PR_TRUE); + if (cert == NULL) { + goto loser; + } + + /* save the certificate, since the auth routine will need it */ + ss->sec.peerCert = cert; + + /* Extract the public key */ + pubKey = CERT_ExtractPublicKey(cert); + if (!pubKey) + goto loser; + + /* Verify the response data... */ + rep.data = response; + rep.len = responseLen; + /* SSL 2.0 only supports RSA certs, so we don't have to worry about + * DSA here. */ + vfy = VFY_CreateContext(pubKey, &rep, SEC_OID_PKCS1_RSA_ENCRYPTION, + ss->pkcs11PinArg); + if (!vfy) + goto loser; + rv = VFY_Begin(vfy); + if (rv) + goto loser; + + rv = VFY_Update(vfy, ss->sec.ci.readKey, ss->sec.ci.keySize); + if (rv) + goto loser; + rv = VFY_Update(vfy, ss->sec.ci.writeKey, ss->sec.ci.keySize); + if (rv) + goto loser; + rv = VFY_Update(vfy, ss->sec.ci.serverChallenge, SSL_CHALLENGE_BYTES); + if (rv) + goto loser; + + derCert = &ss->serverCerts[kt_rsa].serverCert->derCert; + rv = VFY_Update(vfy, derCert->data, derCert->len); + if (rv) + goto loser; + rv = VFY_End(vfy); + if (rv) + goto loser; + + /* Now ask the server application if it likes the certificate... */ + rv = (SECStatus) (*ss->authCertificate)(ss->authCertificateArg, + ss->fd, PR_TRUE, PR_TRUE); + /* Hey, it liked it. */ + if (SECSuccess == rv) + goto done; + +loser: + ss->sec.peerCert = NULL; + CERT_DestroyCertificate(cert); + +done: + VFY_DestroyContext(vfy, PR_TRUE); + SECKEY_DestroyPublicKey(pubKey); + return rv; +} + +/* +** Handle remaining messages between client/server. Process finished +** messages from either side and any authentication requests. +** This should only be called for SSLv2 handshake messages, +** not for application data records. +** Caller must hold handshake lock. +** +** Called from ssl_Do1stHandshake(). +** +*/ +static SECStatus +ssl2_HandleMessage(sslSocket *ss) +{ + PRUint8 * data; + PRUint8 * cid; + unsigned len, certType, certLen, responseLen; + int rv; + int rv2; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ssl_GetRecvBufLock(ss); + + data = ss->gs.buf.buf + ss->gs.recordOffset; + + if (ss->gs.recordLen < 1) { + goto bad_peer; + } + SSL_TRC(3, ("%d: SSL[%d]: received %d message", + SSL_GETPID(), ss->fd, data[0])); + DUMP_MSG(29, (ss, data, ss->gs.recordLen)); + + switch (data[0]) { + case SSL_MT_CLIENT_FINISHED: + if (ss->sec.ci.elements & CIS_HAVE_FINISHED) { + SSL_DBG(("%d: SSL[%d]: dup client-finished message", + SSL_GETPID(), ss->fd)); + goto bad_peer; + } + + /* See if nonce matches */ + len = ss->gs.recordLen - 1; + cid = data + 1; + if ((len != sizeof(ss->sec.ci.connectionID)) || + (PORT_Memcmp(ss->sec.ci.connectionID, cid, len) != 0)) { + SSL_DBG(("%d: SSL[%d]: bad connection-id", SSL_GETPID(), ss->fd)); + PRINT_BUF(5, (ss, "sent connection-id", + ss->sec.ci.connectionID, + sizeof(ss->sec.ci.connectionID))); + PRINT_BUF(5, (ss, "rcvd connection-id", cid, len)); + goto bad_peer; + } + + SSL_TRC(5, ("%d: SSL[%d]: got client finished, waiting for 0x%d", + SSL_GETPID(), ss->fd, + ss->sec.ci.requiredElements ^ ss->sec.ci.elements)); + ss->sec.ci.elements |= CIS_HAVE_FINISHED; + break; + + case SSL_MT_SERVER_FINISHED: + if (ss->sec.ci.elements & CIS_HAVE_FINISHED) { + SSL_DBG(("%d: SSL[%d]: dup server-finished message", + SSL_GETPID(), ss->fd)); + goto bad_peer; + } + + if (ss->gs.recordLen - 1 != SSL2_SESSIONID_BYTES) { + SSL_DBG(("%d: SSL[%d]: bad server-finished message, len=%d", + SSL_GETPID(), ss->fd, ss->gs.recordLen)); + goto bad_peer; + } + ssl2_ClientRegSessionID(ss, data+1); + SSL_TRC(5, ("%d: SSL[%d]: got server finished, waiting for 0x%d", + SSL_GETPID(), ss->fd, + ss->sec.ci.requiredElements ^ ss->sec.ci.elements)); + ss->sec.ci.elements |= CIS_HAVE_FINISHED; + break; + + case SSL_MT_REQUEST_CERTIFICATE: + len = ss->gs.recordLen - 2; + if ((len < SSL_MIN_CHALLENGE_BYTES) || + (len > SSL_MAX_CHALLENGE_BYTES)) { + /* Bad challenge */ + SSL_DBG(("%d: SSL[%d]: bad cert request message: code len=%d", + SSL_GETPID(), ss->fd, len)); + goto bad_peer; + } + + /* save auth request info */ + ss->sec.ci.authType = data[1]; + ss->sec.ci.serverChallengeLen = len; + PORT_Memcpy(ss->sec.ci.serverChallenge, data + 2, len); + + rv = ssl2_HandleRequestCertificate(ss); + if (rv == SECWouldBlock) { + SSL_TRC(3, ("%d: SSL[%d]: async cert request", + SSL_GETPID(), ss->fd)); + /* someone is handling this asynchronously */ + ssl_ReleaseRecvBufLock(ss); + return SECWouldBlock; + } + if (rv) { + SET_ERROR_CODE + goto loser; + } + break; + + case SSL_MT_CLIENT_CERTIFICATE: + if (!ss->authCertificate) { + /* Server asked for authentication and can't handle it */ + PORT_SetError(SSL_ERROR_BAD_SERVER); + goto loser; + } + if (ss->gs.recordLen < SSL_HL_CLIENT_CERTIFICATE_HBYTES) { + SET_ERROR_CODE + goto loser; + } + certType = data[1]; + certLen = (data[2] << 8) | data[3]; + responseLen = (data[4] << 8) | data[5]; + if (certType != SSL_CT_X509_CERTIFICATE) { + PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + goto loser; + } + if (certLen + responseLen + SSL_HL_CLIENT_CERTIFICATE_HBYTES + > ss->gs.recordLen) { + /* prevent overflow crash. */ + rv = SECFailure; + } else + rv = ssl2_HandleClientCertificate(ss, data[1], + data + SSL_HL_CLIENT_CERTIFICATE_HBYTES, + certLen, + data + SSL_HL_CLIENT_CERTIFICATE_HBYTES + certLen, + responseLen); + if (rv) { + rv2 = ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE); + SET_ERROR_CODE + goto loser; + } + ss->sec.ci.elements |= CIS_HAVE_CERTIFICATE; + break; + + case SSL_MT_ERROR: + rv = (data[1] << 8) | data[2]; + SSL_TRC(2, ("%d: SSL[%d]: got error message, error=0x%x", + SSL_GETPID(), ss->fd, rv)); + + /* Convert protocol error number into API error number */ + switch (rv) { + case SSL_PE_NO_CYPHERS: + rv = SSL_ERROR_NO_CYPHER_OVERLAP; + break; + case SSL_PE_NO_CERTIFICATE: + rv = SSL_ERROR_NO_CERTIFICATE; + break; + case SSL_PE_BAD_CERTIFICATE: + rv = SSL_ERROR_BAD_CERTIFICATE; + break; + case SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE: + rv = SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE; + break; + default: + goto bad_peer; + } + /* XXX make certificate-request optionally fail... */ + PORT_SetError(rv); + goto loser; + + default: + SSL_DBG(("%d: SSL[%d]: unknown message %d", + SSL_GETPID(), ss->fd, data[0])); + goto loser; + } + + SSL_TRC(3, ("%d: SSL[%d]: handled %d message, required=0x%x got=0x%x", + SSL_GETPID(), ss->fd, data[0], + ss->sec.ci.requiredElements, ss->sec.ci.elements)); + + rv = ssl2_TryToFinish(ss); + if (rv != SECSuccess) + goto loser; + + ss->gs.recordLen = 0; + ssl_ReleaseRecvBufLock(ss); + + if (ss->handshake == 0) { + return SECSuccess; + } + + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleMessage; + return ssl2_TriggerNextMessage(ss); + + bad_peer: + PORT_SetError(ss->sec.isServer ? SSL_ERROR_BAD_CLIENT : SSL_ERROR_BAD_SERVER); + /* FALL THROUGH */ + + loser: + ssl_ReleaseRecvBufLock(ss); + return SECFailure; +} + +/************************************************************************/ + +/* Called from ssl_Do1stHandshake, after ssl2_HandleServerHelloMessage. +*/ +static SECStatus +ssl2_HandleVerifyMessage(sslSocket *ss) +{ + PRUint8 * data; + SECStatus rv; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + ssl_GetRecvBufLock(ss); + + data = ss->gs.buf.buf + ss->gs.recordOffset; + DUMP_MSG(29, (ss, data, ss->gs.recordLen)); + if ((ss->gs.recordLen != 1 + SSL_CHALLENGE_BYTES) || + (data[0] != SSL_MT_SERVER_VERIFY) || + NSS_SecureMemcmp(data+1, ss->sec.ci.clientChallenge, + SSL_CHALLENGE_BYTES)) { + /* Bad server */ + PORT_SetError(SSL_ERROR_BAD_SERVER); + goto loser; + } + ss->sec.ci.elements |= CIS_HAVE_VERIFY; + + SSL_TRC(5, ("%d: SSL[%d]: got server-verify, required=0x%d got=0x%x", + SSL_GETPID(), ss->fd, ss->sec.ci.requiredElements, + ss->sec.ci.elements)); + + rv = ssl2_TryToFinish(ss); + if (rv) + goto loser; + + ss->gs.recordLen = 0; + ssl_ReleaseRecvBufLock(ss); + + if (ss->handshake == 0) { + return SECSuccess; + } + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleMessage; + return SECSuccess; + + + loser: + ssl_ReleaseRecvBufLock(ss); + return SECFailure; +} + +/* Not static because ssl2_GatherData() tests ss->nextHandshake for this value. + * ICK! + * Called from ssl_Do1stHandshake after ssl2_BeginClientHandshake() + */ +SECStatus +ssl2_HandleServerHelloMessage(sslSocket *ss) +{ + sslSessionID * sid; + PRUint8 * cert; + PRUint8 * cs; + PRUint8 * data; + SECStatus rv; + int needed, sidHit, certLen, csLen, cidLen, certType, err; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + if (!ss->opt.enableSSL2) { + PORT_SetError(SSL_ERROR_SSL2_DISABLED); + return SECFailure; + } + + ssl_GetRecvBufLock(ss); + + PORT_Assert(ss->sec.ci.sid != 0); + sid = ss->sec.ci.sid; + + data = ss->gs.buf.buf + ss->gs.recordOffset; + DUMP_MSG(29, (ss, data, ss->gs.recordLen)); + + /* Make sure first message has some data and is the server hello message */ + if ((ss->gs.recordLen < SSL_HL_SERVER_HELLO_HBYTES) + || (data[0] != SSL_MT_SERVER_HELLO)) { + if ((data[0] == SSL_MT_ERROR) && (ss->gs.recordLen == 3)) { + err = (data[1] << 8) | data[2]; + if (err == SSL_PE_NO_CYPHERS) { + PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); + goto loser; + } + } + goto bad_server; + } + + sidHit = data[1]; + certType = data[2]; + ss->version = (data[3] << 8) | data[4]; + certLen = (data[5] << 8) | data[6]; + csLen = (data[7] << 8) | data[8]; + cidLen = (data[9] << 8) | data[10]; + cert = data + SSL_HL_SERVER_HELLO_HBYTES; + cs = cert + certLen; + + SSL_TRC(5, + ("%d: SSL[%d]: server-hello, hit=%d vers=%x certLen=%d csLen=%d cidLen=%d", + SSL_GETPID(), ss->fd, sidHit, ss->version, certLen, + csLen, cidLen)); + if (ss->version != SSL_LIBRARY_VERSION_2) { + if (ss->version < SSL_LIBRARY_VERSION_2) { + SSL_TRC(3, ("%d: SSL[%d]: demoting self (%x) to server version (%x)", + SSL_GETPID(), ss->fd, SSL_LIBRARY_VERSION_2, + ss->version)); + } else { + SSL_TRC(1, ("%d: SSL[%d]: server version is %x (we are %x)", + SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2)); + /* server claims to be newer but does not follow protocol */ + PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION); + goto loser; + } + } + + if ((SSL_HL_SERVER_HELLO_HBYTES + certLen + csLen + cidLen + > ss->gs.recordLen) + || (csLen % 3) != 0 + /* || cidLen < SSL_CONNECTIONID_BYTES || cidLen > 32 */ + ) { + goto bad_server; + } + + /* Save connection-id. + ** This code only saves the first 16 byte of the connectionID. + ** If the connectionID is shorter than 16 bytes, it is zero-padded. + */ + if (cidLen < sizeof ss->sec.ci.connectionID) + memset(ss->sec.ci.connectionID, 0, sizeof ss->sec.ci.connectionID); + cidLen = PR_MIN(cidLen, sizeof ss->sec.ci.connectionID); + PORT_Memcpy(ss->sec.ci.connectionID, cs + csLen, cidLen); + + /* See if session-id hit */ + needed = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED | CIS_HAVE_VERIFY; + if (sidHit) { + if (certLen || csLen) { + /* Uh oh - bogus server */ + SSL_DBG(("%d: SSL[%d]: client, huh? hit=%d certLen=%d csLen=%d", + SSL_GETPID(), ss->fd, sidHit, certLen, csLen)); + goto bad_server; + } + + /* Total winner. */ + SSL_TRC(1, ("%d: SSL[%d]: client, using nonce for peer=0x%08x " + "port=0x%04x", + SSL_GETPID(), ss->fd, ss->sec.ci.peer, ss->sec.ci.port)); + ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); + ss->sec.authAlgorithm = sid->authAlgorithm; + ss->sec.authKeyBits = sid->authKeyBits; + ss->sec.keaType = sid->keaType; + ss->sec.keaKeyBits = sid->keaKeyBits; + rv = ssl2_CreateSessionCypher(ss, sid, PR_TRUE); + if (rv != SECSuccess) { + goto loser; + } + } else { + if (certType != SSL_CT_X509_CERTIFICATE) { + PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + goto loser; + } + if (csLen == 0) { + PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); + SSL_DBG(("%d: SSL[%d]: no cipher overlap", + SSL_GETPID(), ss->fd)); + goto loser; + } + if (certLen == 0) { + SSL_DBG(("%d: SSL[%d]: client, huh? certLen=%d csLen=%d", + SSL_GETPID(), ss->fd, certLen, csLen)); + goto bad_server; + } + + if (sid->cached != never_cached) { + /* Forget our session-id - server didn't like it */ + SSL_TRC(7, ("%d: SSL[%d]: server forgot me, uncaching session-id", + SSL_GETPID(), ss->fd)); + if (ss->sec.uncache) + (*ss->sec.uncache)(sid); + ssl_FreeSID(sid); + ss->sec.ci.sid = sid = PORT_ZNew(sslSessionID); + if (!sid) { + goto loser; + } + sid->references = 1; + sid->addr = ss->sec.ci.peer; + sid->port = ss->sec.ci.port; + } + + /* decode the server's certificate */ + rv = ssl2_ClientHandleServerCert(ss, cert, certLen); + if (rv != SECSuccess) { + if (PORT_GetError() == SSL_ERROR_BAD_CERTIFICATE) { + (void) ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE); + } + goto loser; + } + + /* Setup new session cipher */ + rv = ssl2_ClientSetupSessionCypher(ss, cs, csLen); + if (rv != SECSuccess) { + if (PORT_GetError() == SSL_ERROR_BAD_CERTIFICATE) { + (void) ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE); + } + goto loser; + } + } + + /* Build up final list of required elements */ + ss->sec.ci.elements = CIS_HAVE_MASTER_KEY; + ss->sec.ci.requiredElements = needed; + + if (!sidHit) { + /* verify the server's certificate. if sidHit, don't check signatures */ + rv = (* ss->authCertificate)(ss->authCertificateArg, ss->fd, + (PRBool)(!sidHit), PR_FALSE); + if (rv) { + if (ss->handleBadCert) { + rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd); + if ( rv ) { + if ( rv == SECWouldBlock ) { + SSL_DBG(("%d: SSL[%d]: SSL2 bad cert handler returned " + "SECWouldBlock", SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); + rv = SECFailure; + } else { + /* cert is bad */ + SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d", + SSL_GETPID(), ss->fd, PORT_GetError())); + } + goto loser; + } + /* cert is good */ + } else { + SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d", + SSL_GETPID(), ss->fd, PORT_GetError())); + goto loser; + } + } + } + /* + ** At this point we have a completed session key and our session + ** cipher is setup and ready to go. Switch to encrypted write routine + ** as all future message data is to be encrypted. + */ + ssl2_UseEncryptedSendFunc(ss); + + rv = ssl2_TryToFinish(ss); + if (rv != SECSuccess) + goto loser; + + ss->gs.recordLen = 0; + + ssl_ReleaseRecvBufLock(ss); + + if (ss->handshake == 0) { + return SECSuccess; + } + + SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x", + SSL_GETPID(), ss->fd, ss->sec.ci.requiredElements, + ss->sec.ci.elements)); + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleVerifyMessage; + return SECSuccess; + + bad_server: + PORT_SetError(SSL_ERROR_BAD_SERVER); + /* FALL THROUGH */ + + loser: + ssl_ReleaseRecvBufLock(ss); + return SECFailure; +} + +/* Sends out the initial client Hello message on the connection. + * Acquires and releases the socket's xmitBufLock. + */ +SECStatus +ssl2_BeginClientHandshake(sslSocket *ss) +{ + sslSessionID *sid; + PRUint8 *msg; + PRUint8 *cp; + PRUint8 *localCipherSpecs = NULL; + unsigned int localCipherSize; + unsigned int i; + int sendLen, sidLen = 0; + SECStatus rv; + TLSExtensionData *xtnData; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + ss->sec.isServer = 0; + ss->sec.sendSequence = 0; + ss->sec.rcvSequence = 0; + ssl_ChooseSessionIDProcs(&ss->sec); + + if (!ss->cipherSpecs) { + rv = ssl2_ConstructCipherSpecs(ss); + if (rv != SECSuccess) + goto loser; + } + + /* count the SSL2 and SSL3 enabled ciphers. + * if either is zero, clear the socket's enable for that protocol. + */ + rv = ssl2_CheckConfigSanity(ss); + if (rv != SECSuccess) + goto loser; + + /* Get peer name of server */ + rv = ssl_GetPeerInfo(ss); + if (rv < 0) { +#ifdef HPUX11 + /* + * On some HP-UX B.11.00 systems, getpeername() occasionally + * fails with ENOTCONN after a successful completion of + * non-blocking connect. I found that if we do a write() + * and then retry getpeername(), it will work. + */ + if (PR_GetError() == PR_NOT_CONNECTED_ERROR) { + char dummy; + (void) PR_Write(ss->fd->lower, &dummy, 0); + rv = ssl_GetPeerInfo(ss); + if (rv < 0) { + goto loser; + } + } +#else + goto loser; +#endif + } + + SSL_TRC(3, ("%d: SSL[%d]: sending client-hello", SSL_GETPID(), ss->fd)); + + /* Try to find server in our session-id cache */ + if (ss->opt.noCache) { + sid = NULL; + } else { + sid = ssl_LookupSID(&ss->sec.ci.peer, ss->sec.ci.port, ss->peerID, + ss->url); + } + while (sid) { /* this isn't really a loop */ + PRBool sidVersionEnabled = + (!SSL3_ALL_VERSIONS_DISABLED(&ss->vrange) && + sid->version >= ss->vrange.min && + sid->version <= ss->vrange.max) || + (sid->version < SSL_LIBRARY_VERSION_3_0 && ss->opt.enableSSL2); + + /* if we're not doing this SID's protocol any more, drop it. */ + if (!sidVersionEnabled) { + if (ss->sec.uncache) + ss->sec.uncache(sid); + ssl_FreeSID(sid); + sid = NULL; + break; + } + if (sid->version < SSL_LIBRARY_VERSION_3_0) { + /* If the cipher in this sid is not enabled, drop it. */ + for (i = 0; i < ss->sizeCipherSpecs; i += 3) { + if (ss->cipherSpecs[i] == sid->u.ssl2.cipherType) + break; + } + if (i >= ss->sizeCipherSpecs) { + if (ss->sec.uncache) + ss->sec.uncache(sid); + ssl_FreeSID(sid); + sid = NULL; + break; + } + } + sidLen = sizeof(sid->u.ssl2.sessionID); + PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl2.sessionID, + sidLen)); + ss->version = sid->version; + PORT_Assert(!ss->sec.localCert); + if (ss->sec.localCert) { + CERT_DestroyCertificate(ss->sec.localCert); + } + ss->sec.localCert = CERT_DupCertificate(sid->localCert); + break; /* this isn't really a loop */ + } + if (!sid) { + sidLen = 0; + sid = PORT_ZNew(sslSessionID); + if (!sid) { + goto loser; + } + sid->references = 1; + sid->cached = never_cached; + sid->addr = ss->sec.ci.peer; + sid->port = ss->sec.ci.port; + if (ss->peerID != NULL) { + sid->peerID = PORT_Strdup(ss->peerID); + } + if (ss->url != NULL) { + sid->urlSvrName = PORT_Strdup(ss->url); + } + } + ss->sec.ci.sid = sid; + + PORT_Assert(sid != NULL); + + if ((sid->version >= SSL_LIBRARY_VERSION_3_0 || !ss->opt.v2CompatibleHello) && + !SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { + ss->gs.state = GS_INIT; + ss->handshake = ssl_GatherRecord1stHandshake; + + /* ssl3_SendClientHello will override this if it succeeds. */ + ss->version = SSL_LIBRARY_VERSION_3_0; + + ssl_GetSSL3HandshakeLock(ss); + ssl_GetXmitBufLock(ss); + rv = ssl3_SendClientHello(ss, PR_FALSE); + ssl_ReleaseXmitBufLock(ss); + ssl_ReleaseSSL3HandshakeLock(ss); + + return rv; + } +#if defined(NSS_ENABLE_ECC) + /* ensure we don't neogtiate ECC cipher suites with SSL2 hello */ + ssl3_DisableECCSuites(ss, NULL); /* disable all ECC suites */ + if (ss->cipherSpecs != NULL) { + PORT_Free(ss->cipherSpecs); + ss->cipherSpecs = NULL; + ss->sizeCipherSpecs = 0; + } +#endif + + if (!ss->cipherSpecs) { + rv = ssl2_ConstructCipherSpecs(ss); + if (rv < 0) { + return rv; + } + } + localCipherSpecs = ss->cipherSpecs; + localCipherSize = ss->sizeCipherSpecs; + + /* Add 3 for SCSV */ + sendLen = SSL_HL_CLIENT_HELLO_HBYTES + localCipherSize + 3 + sidLen + + SSL_CHALLENGE_BYTES; + + /* Generate challenge bytes for server */ + PK11_GenerateRandom(ss->sec.ci.clientChallenge, SSL_CHALLENGE_BYTES); + + ssl_GetXmitBufLock(ss); /***************************************/ + + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv) + goto unlock_loser; + + /* Construct client-hello message */ + cp = msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_CLIENT_HELLO; + ss->clientHelloVersion = SSL3_ALL_VERSIONS_DISABLED(&ss->vrange) ? + SSL_LIBRARY_VERSION_2 : ss->vrange.max; + + msg[1] = MSB(ss->clientHelloVersion); + msg[2] = LSB(ss->clientHelloVersion); + /* Add 3 for SCSV */ + msg[3] = MSB(localCipherSize + 3); + msg[4] = LSB(localCipherSize + 3); + msg[5] = MSB(sidLen); + msg[6] = LSB(sidLen); + msg[7] = MSB(SSL_CHALLENGE_BYTES); + msg[8] = LSB(SSL_CHALLENGE_BYTES); + cp += SSL_HL_CLIENT_HELLO_HBYTES; + PORT_Memcpy(cp, localCipherSpecs, localCipherSize); + cp += localCipherSize; + /* + * Add SCSV. SSL 2.0 cipher suites are listed before SSL 3.0 cipher + * suites in localCipherSpecs for compatibility with SSL 2.0 servers. + * Since SCSV looks like an SSL 3.0 cipher suite, we can't add it at + * the beginning. + */ + cp[0] = 0x00; + cp[1] = 0x00; + cp[2] = 0xff; + cp += 3; + if (sidLen) { + PORT_Memcpy(cp, sid->u.ssl2.sessionID, sidLen); + cp += sidLen; + } + PORT_Memcpy(cp, ss->sec.ci.clientChallenge, SSL_CHALLENGE_BYTES); + + /* Send it to the server */ + DUMP_MSG(29, (ss, msg, sendLen)); + ss->handshakeBegun = 1; + rv = (*ss->sec.send)(ss, msg, sendLen, 0); + + ssl_ReleaseXmitBufLock(ss); /***************************************/ + + if (rv < 0) { + goto loser; + } + + rv = ssl3_StartHandshakeHash(ss, msg, sendLen); + if (rv < 0) { + goto loser; + } + + /* + * Since we sent the SCSV, pretend we sent empty RI extension. We need + * to record the extension has been advertised after ssl3_InitState has + * been called, which ssl3_StartHandshakeHash took care for us above. + */ + xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = ssl_renegotiation_info_xtn; + + /* Setup to receive servers hello message */ + ssl_GetRecvBufLock(ss); + ss->gs.recordLen = 0; + ssl_ReleaseRecvBufLock(ss); + + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleServerHelloMessage; + return SECSuccess; + +unlock_loser: + ssl_ReleaseXmitBufLock(ss); +loser: + return SECFailure; +} + +/************************************************************************/ + +/* Handle the CLIENT-MASTER-KEY message. +** Acquires and releases RecvBufLock. +** Called from ssl2_HandleClientHelloMessage(). +*/ +static SECStatus +ssl2_HandleClientSessionKeyMessage(sslSocket *ss) +{ + PRUint8 * data; + unsigned int caLen; + unsigned int ckLen; + unsigned int ekLen; + unsigned int keyBits; + int cipher; + SECStatus rv; + + + ssl_GetRecvBufLock(ss); + + data = ss->gs.buf.buf + ss->gs.recordOffset; + DUMP_MSG(29, (ss, data, ss->gs.recordLen)); + + if ((ss->gs.recordLen < SSL_HL_CLIENT_MASTER_KEY_HBYTES) + || (data[0] != SSL_MT_CLIENT_MASTER_KEY)) { + goto bad_client; + } + cipher = data[1]; + keyBits = (data[2] << 8) | data[3]; + ckLen = (data[4] << 8) | data[5]; + ekLen = (data[6] << 8) | data[7]; + caLen = (data[8] << 8) | data[9]; + + SSL_TRC(5, ("%d: SSL[%d]: session-key, cipher=%d keyBits=%d ckLen=%d ekLen=%d caLen=%d", + SSL_GETPID(), ss->fd, cipher, keyBits, ckLen, ekLen, caLen)); + + if (ss->gs.recordLen < + SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen + caLen) { + SSL_DBG(("%d: SSL[%d]: protocol size mismatch dataLen=%d", + SSL_GETPID(), ss->fd, ss->gs.recordLen)); + goto bad_client; + } + + /* Use info from client to setup session key */ + rv = ssl2_ServerSetupSessionCypher(ss, cipher, keyBits, + data + SSL_HL_CLIENT_MASTER_KEY_HBYTES, ckLen, + data + SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen, ekLen, + data + SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen, caLen); + ss->gs.recordLen = 0; /* we're done with this record. */ + + ssl_ReleaseRecvBufLock(ss); + + if (rv != SECSuccess) { + goto loser; + } + ss->sec.ci.elements |= CIS_HAVE_MASTER_KEY; + ssl2_UseEncryptedSendFunc(ss); + + /* Send server verify message now that keys are established */ + rv = ssl2_SendServerVerifyMessage(ss); + if (rv != SECSuccess) + goto loser; + + rv = ssl2_TryToFinish(ss); + if (rv != SECSuccess) + goto loser; + if (ss->handshake == 0) { + return SECSuccess; + } + + SSL_TRC(5, ("%d: SSL[%d]: server: waiting for elements=0x%d", + SSL_GETPID(), ss->fd, + ss->sec.ci.requiredElements ^ ss->sec.ci.elements)); + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleMessage; + + return ssl2_TriggerNextMessage(ss); + +bad_client: + ssl_ReleaseRecvBufLock(ss); + PORT_SetError(SSL_ERROR_BAD_CLIENT); + /* FALLTHROUGH */ + +loser: + return SECFailure; +} + +/* +** Handle the initial hello message from the client +** +** not static because ssl2_GatherData() tests ss->nextHandshake for this value. +*/ +SECStatus +ssl2_HandleClientHelloMessage(sslSocket *ss) +{ + sslSessionID *sid; + sslServerCerts * sc; + CERTCertificate *serverCert; + PRUint8 *msg; + PRUint8 *data; + PRUint8 *cs; + PRUint8 *sd; + PRUint8 *cert = NULL; + PRUint8 *challenge; + unsigned int challengeLen; + SECStatus rv; + int csLen; + int sendLen; + int sdLen; + int certLen; + int pid; + int sent; + int gotXmitBufLock = 0; +#if defined(SOLARIS) && defined(i386) + volatile PRUint8 hit; +#else + int hit; +#endif + PRUint8 csImpl[sizeof implementedCipherSuites]; + + PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); + + sc = ss->serverCerts + kt_rsa; + serverCert = sc->serverCert; + + ssl_GetRecvBufLock(ss); + + + data = ss->gs.buf.buf + ss->gs.recordOffset; + DUMP_MSG(29, (ss, data, ss->gs.recordLen)); + + /* Make sure first message has some data and is the client hello message */ + if ((ss->gs.recordLen < SSL_HL_CLIENT_HELLO_HBYTES) + || (data[0] != SSL_MT_CLIENT_HELLO)) { + goto bad_client; + } + + /* Get peer name of client */ + rv = ssl_GetPeerInfo(ss); + if (rv != SECSuccess) { + goto loser; + } + + /* Examine version information */ + /* + * See if this might be a V2 client hello asking to use the V3 protocol + */ + if ((data[0] == SSL_MT_CLIENT_HELLO) && + (data[1] >= MSB(SSL_LIBRARY_VERSION_3_0)) && + !SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { + rv = ssl3_HandleV2ClientHello(ss, data, ss->gs.recordLen); + if (rv != SECFailure) { /* Success */ + ss->handshake = NULL; + ss->nextHandshake = ssl_GatherRecord1stHandshake; + ss->securityHandshake = NULL; + ss->gs.state = GS_INIT; + + /* ssl3_HandleV3ClientHello has set ss->version, + ** and has gotten us a brand new sid. + */ + ss->sec.ci.sid->version = ss->version; + } + ssl_ReleaseRecvBufLock(ss); + return rv; + } + /* Previously, there was a test here to see if SSL2 was enabled. + ** If not, an error code was set, and SECFailure was returned, + ** without sending any error code to the other end of the connection. + ** That test has been removed. If SSL2 has been disabled, there + ** should be no SSL2 ciphers enabled, and consequently, the code + ** below should send the ssl2 error message SSL_PE_NO_CYPHERS. + ** We now believe this is the correct thing to do, even when SSL2 + ** has been explicitly disabled by the application. + */ + + /* Extract info from message */ + ss->version = (data[1] << 8) | data[2]; + + /* If some client thinks ssl v2 is 2.0 instead of 0.2, we'll allow it. */ + if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + ss->version = SSL_LIBRARY_VERSION_2; + } + + csLen = (data[3] << 8) | data[4]; + sdLen = (data[5] << 8) | data[6]; + challengeLen = (data[7] << 8) | data[8]; + cs = data + SSL_HL_CLIENT_HELLO_HBYTES; + sd = cs + csLen; + challenge = sd + sdLen; + PRINT_BUF(7, (ss, "server, client session-id value:", sd, sdLen)); + + if (!csLen || (csLen % 3) != 0 || + (sdLen != 0 && sdLen != SSL2_SESSIONID_BYTES) || + challengeLen < SSL_MIN_CHALLENGE_BYTES || + challengeLen > SSL_MAX_CHALLENGE_BYTES || + (unsigned)ss->gs.recordLen != + SSL_HL_CLIENT_HELLO_HBYTES + csLen + sdLen + challengeLen) { + SSL_DBG(("%d: SSL[%d]: bad client hello message, len=%d should=%d", + SSL_GETPID(), ss->fd, ss->gs.recordLen, + SSL_HL_CLIENT_HELLO_HBYTES+csLen+sdLen+challengeLen)); + goto bad_client; + } + + SSL_TRC(3, ("%d: SSL[%d]: client version is %x", + SSL_GETPID(), ss->fd, ss->version)); + if (ss->version != SSL_LIBRARY_VERSION_2) { + if (ss->version > SSL_LIBRARY_VERSION_2) { + /* + ** Newer client than us. Things are ok because new clients + ** are required to be backwards compatible with old servers. + ** Change version number to our version number so that client + ** knows whats up. + */ + ss->version = SSL_LIBRARY_VERSION_2; + } else { + SSL_TRC(1, ("%d: SSL[%d]: client version is %x (we are %x)", + SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2)); + PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION); + goto loser; + } + } + + /* Qualify cipher specs before returning them to client */ + csLen = ssl2_QualifyCypherSpecs(ss, cs, csLen); + if (csLen == 0) { + /* no overlap, send client our list of supported SSL v2 ciphers. */ + cs = csImpl; + csLen = sizeof implementedCipherSuites; + PORT_Memcpy(cs, implementedCipherSuites, csLen); + csLen = ssl2_QualifyCypherSpecs(ss, cs, csLen); + if (csLen == 0) { + /* We don't support any SSL v2 ciphers! */ + ssl2_SendErrorMessage(ss, SSL_PE_NO_CYPHERS); + PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); + goto loser; + } + /* Since this handhsake is going to fail, don't cache it. */ + ss->opt.noCache = 1; + } + + /* Squirrel away the challenge for later */ + PORT_Memcpy(ss->sec.ci.clientChallenge, challenge, challengeLen); + + /* Examine message and see if session-id is good */ + ss->sec.ci.elements = 0; + if (sdLen > 0 && !ss->opt.noCache) { + SSL_TRC(7, ("%d: SSL[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x", + SSL_GETPID(), ss->fd, ss->sec.ci.peer.pr_s6_addr32[0], + ss->sec.ci.peer.pr_s6_addr32[1], + ss->sec.ci.peer.pr_s6_addr32[2], + ss->sec.ci.peer.pr_s6_addr32[3])); + sid = (*ssl_sid_lookup)(&ss->sec.ci.peer, sd, sdLen, ss->dbHandle); + } else { + sid = NULL; + } + if (sid) { + /* Got a good session-id. Short cut! */ + SSL_TRC(1, ("%d: SSL[%d]: server, using session-id for 0x%08x (age=%d)", + SSL_GETPID(), ss->fd, ss->sec.ci.peer, + ssl_Time() - sid->creationTime)); + PRINT_BUF(1, (ss, "session-id value:", sd, sdLen)); + ss->sec.ci.sid = sid; + ss->sec.ci.elements = CIS_HAVE_MASTER_KEY; + hit = 1; + certLen = 0; + csLen = 0; + + ss->sec.authAlgorithm = sid->authAlgorithm; + ss->sec.authKeyBits = sid->authKeyBits; + ss->sec.keaType = sid->keaType; + ss->sec.keaKeyBits = sid->keaKeyBits; + + rv = ssl2_CreateSessionCypher(ss, sid, PR_FALSE); + if (rv != SECSuccess) { + goto loser; + } + } else { + SECItem * derCert = &serverCert->derCert; + + SSL_TRC(7, ("%d: SSL[%d]: server, lookup nonce missed", + SSL_GETPID(), ss->fd)); + if (!serverCert) { + SET_ERROR_CODE + goto loser; + } + hit = 0; + sid = PORT_ZNew(sslSessionID); + if (!sid) { + goto loser; + } + sid->references = 1; + sid->addr = ss->sec.ci.peer; + sid->port = ss->sec.ci.port; + + /* Invent a session-id */ + ss->sec.ci.sid = sid; + PK11_GenerateRandom(sid->u.ssl2.sessionID+2, SSL2_SESSIONID_BYTES-2); + + pid = SSL_GETPID(); + sid->u.ssl2.sessionID[0] = MSB(pid); + sid->u.ssl2.sessionID[1] = LSB(pid); + cert = derCert->data; + certLen = derCert->len; + + /* pretend that server sids remember the local cert. */ + PORT_Assert(!sid->localCert); + if (sid->localCert) { + CERT_DestroyCertificate(sid->localCert); + } + sid->localCert = CERT_DupCertificate(serverCert); + + ss->sec.authAlgorithm = ssl_sign_rsa; + ss->sec.keaType = ssl_kea_rsa; + ss->sec.keaKeyBits = \ + ss->sec.authKeyBits = ss->serverCerts[kt_rsa].serverKeyBits; + } + + /* server sids don't remember the local cert, so whether we found + ** a sid or not, just "remember" we used the rsa server cert. + */ + if (ss->sec.localCert) { + CERT_DestroyCertificate(ss->sec.localCert); + } + ss->sec.localCert = CERT_DupCertificate(serverCert); + + /* Build up final list of required elements */ + ss->sec.ci.requiredElements = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED; + if (ss->opt.requestCertificate) { + ss->sec.ci.requiredElements |= CIS_HAVE_CERTIFICATE; + } + ss->sec.ci.sentElements = 0; + + /* Send hello message back to client */ + sendLen = SSL_HL_SERVER_HELLO_HBYTES + certLen + csLen + + SSL_CONNECTIONID_BYTES; + + ssl_GetXmitBufLock(ss); gotXmitBufLock = 1; + rv = ssl2_GetSendBuffer(ss, sendLen); + if (rv != SECSuccess) { + goto loser; + } + + SSL_TRC(3, ("%d: SSL[%d]: sending server-hello (%d)", + SSL_GETPID(), ss->fd, sendLen)); + + msg = ss->sec.ci.sendBuf.buf; + msg[0] = SSL_MT_SERVER_HELLO; + msg[1] = hit; + msg[2] = SSL_CT_X509_CERTIFICATE; + msg[3] = MSB(ss->version); + msg[4] = LSB(ss->version); + msg[5] = MSB(certLen); + msg[6] = LSB(certLen); + msg[7] = MSB(csLen); + msg[8] = LSB(csLen); + msg[9] = MSB(SSL_CONNECTIONID_BYTES); + msg[10] = LSB(SSL_CONNECTIONID_BYTES); + if (certLen) { + PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES, cert, certLen); + } + if (csLen) { + PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen, cs, csLen); + } + PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen+csLen, + ss->sec.ci.connectionID, SSL_CONNECTIONID_BYTES); + + DUMP_MSG(29, (ss, msg, sendLen)); + + ss->handshakeBegun = 1; + sent = (*ss->sec.send)(ss, msg, sendLen, 0); + if (sent < 0) { + goto loser; + } + ssl_ReleaseXmitBufLock(ss); gotXmitBufLock = 0; + + ss->gs.recordLen = 0; + ss->handshake = ssl_GatherRecord1stHandshake; + if (hit) { + /* Old SID Session key is good. Go encrypted */ + ssl2_UseEncryptedSendFunc(ss); + + /* Send server verify message now that keys are established */ + rv = ssl2_SendServerVerifyMessage(ss); + if (rv != SECSuccess) + goto loser; + + ss->nextHandshake = ssl2_HandleMessage; + ssl_ReleaseRecvBufLock(ss); + rv = ssl2_TriggerNextMessage(ss); + return rv; + } + ss->nextHandshake = ssl2_HandleClientSessionKeyMessage; + ssl_ReleaseRecvBufLock(ss); + return SECSuccess; + + bad_client: + PORT_SetError(SSL_ERROR_BAD_CLIENT); + /* FALLTHROUGH */ + + loser: + if (gotXmitBufLock) { + ssl_ReleaseXmitBufLock(ss); gotXmitBufLock = 0; + } + SSL_TRC(10, ("%d: SSL[%d]: server, wait for client-hello lossage", + SSL_GETPID(), ss->fd)); + ssl_ReleaseRecvBufLock(ss); + return SECFailure; +} + +SECStatus +ssl2_BeginServerHandshake(sslSocket *ss) +{ + SECStatus rv; + sslServerCerts * rsaAuth = ss->serverCerts + kt_rsa; + + ss->sec.isServer = 1; + ssl_ChooseSessionIDProcs(&ss->sec); + ss->sec.sendSequence = 0; + ss->sec.rcvSequence = 0; + + /* don't turn on SSL2 if we don't have an RSA key and cert */ + if (!rsaAuth->serverKeyPair || !rsaAuth->SERVERKEY || + !rsaAuth->serverCert) { + ss->opt.enableSSL2 = PR_FALSE; + } + + if (!ss->cipherSpecs) { + rv = ssl2_ConstructCipherSpecs(ss); + if (rv != SECSuccess) + goto loser; + } + + /* count the SSL2 and SSL3 enabled ciphers. + * if either is zero, clear the socket's enable for that protocol. + */ + rv = ssl2_CheckConfigSanity(ss); + if (rv != SECSuccess) + goto loser; + + /* + ** Generate connection-id. Always do this, even if things fail + ** immediately. This way the random number generator is always + ** rolling around, every time we get a connection. + */ + PK11_GenerateRandom(ss->sec.ci.connectionID, + sizeof(ss->sec.ci.connectionID)); + + ss->gs.recordLen = 0; + ss->handshake = ssl_GatherRecord1stHandshake; + ss->nextHandshake = ssl2_HandleClientHelloMessage; + return SECSuccess; + +loser: + return SECFailure; +} + +/* This function doesn't really belong in this file. +** It's here to keep AIX compilers from optimizing it away, +** and not including it in the DSO. +*/ + +#include "nss.h" +extern const char __nss_ssl_rcsid[]; +extern const char __nss_ssl_sccsid[]; + +PRBool +NSSSSL_VersionCheck(const char *importedVersion) +{ + /* + * This is the secret handshake algorithm. + * + * This release has a simple version compatibility + * check algorithm. This release is not backward + * compatible with previous major releases. It is + * not compatible with future major, minor, or + * patch releases. + */ + volatile char c; /* force a reference that won't get optimized away */ + + c = __nss_ssl_rcsid[0] + __nss_ssl_sccsid[0]; + return NSS_VersionCheck(importedVersion); +} + +const char * +NSSSSL_GetVersion(void) +{ + return NSS_VERSION; +} |