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