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