Index: net/third_party/nss/ssl/ssl3con.c |
=================================================================== |
--- net/third_party/nss/ssl/ssl3con.c (revision 124804) |
+++ net/third_party/nss/ssl/ssl3con.c (working copy) |
@@ -39,7 +39,7 @@ |
* the terms of any one of the MPL, the GPL or the LGPL. |
* |
* ***** END LICENSE BLOCK ***** */ |
-/* $Id: ssl3con.c,v 1.142.2.4 2010/09/01 19:47:11 wtc%google.com Exp $ */ |
+/* $Id: ssl3con.c,v 1.164 2012/02/17 09:50:04 kaie%kuix.de Exp $ */ |
#include "cert.h" |
#include "ssl.h" |
@@ -88,7 +88,8 @@ |
static SECStatus ssl3_SendServerHelloDone( sslSocket *ss); |
static SECStatus ssl3_SendServerKeyExchange( sslSocket *ss); |
static SECStatus ssl3_NewHandshakeHashes( sslSocket *ss); |
-static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss, unsigned char *b, |
+static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss, |
+ const unsigned char *b, |
unsigned int l); |
static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen, |
@@ -238,9 +239,6 @@ |
#define EXPORT_RSA_KEY_LENGTH 64 /* bytes */ |
-/* This is a hack to make sure we don't do double handshakes for US policy */ |
-PRBool ssl3_global_policy_some_restricted = PR_FALSE; |
- |
/* This global item is used only in servers. It is is initialized by |
** SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest(). |
*/ |
@@ -930,8 +928,7 @@ |
key = CERT_ExtractPublicKey(cert); |
if (key == NULL) { |
- /* CERT_ExtractPublicKey doesn't set error code */ |
- PORT_SetError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); |
+ ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); |
return SECFailure; |
} |
@@ -2042,9 +2039,7 @@ |
return isPresent; |
} |
-/* Caller must hold the spec read lock. wrBuf is sometimes, but not always, |
- * ss->sec.writeBuf. |
- */ |
+/* Caller must hold the spec read lock. */ |
static SECStatus |
ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, |
PRBool isServer, |
@@ -2229,8 +2224,7 @@ |
ssl_GetSpecReadLock(ss); /********************************/ |
- if (nIn > 1 && |
- ss->opt.enableFalseStart && |
+ if (nIn > 1 && ss->opt.cbcRandomIV && |
ss->ssl3.cwSpec->version <= SSL_LIBRARY_VERSION_3_1_TLS && |
type == content_application_data && |
ss->ssl3.cwSpec->cipher_def->type == type_block /* CBC mode */) { |
@@ -2248,7 +2242,7 @@ |
if (rv != SECSuccess) { |
SSL_DBG(("%d: SSL3[%d]: SendRecord, tried to get %d bytes", |
SSL_GETPID(), ss->fd, spaceNeeded)); |
- goto spec_locked_loser; /* sslBuffer_Grow set a memory error code. */ |
+ goto spec_locked_loser; /* sslBuffer_Grow set error code. */ |
} |
} |
@@ -2281,7 +2275,7 @@ |
ss->sec.isServer, type, pIn, |
contentLen, wrBuf); |
if (rv == SECSuccess) { |
- PRINT_BUF(50, (ss, "send (encrypted) record data [1/1]:", |
+ PRINT_BUF(50, (ss, "send (encrypted) record data:", |
wrBuf->buf, wrBuf->len)); |
} |
} |
@@ -2617,6 +2611,40 @@ |
return SECFailure; |
} |
+static void |
+ssl3_SendAlertForCertError(sslSocket * ss, PRErrorCode errCode) |
+{ |
+ SSL3AlertDescription desc = bad_certificate; |
+ PRBool isTLS = ss->version >= SSL_LIBRARY_VERSION_3_1_TLS; |
+ |
+ switch (errCode) { |
+ case SEC_ERROR_LIBRARY_FAILURE: desc = unsupported_certificate; break; |
+ case SEC_ERROR_EXPIRED_CERTIFICATE: desc = certificate_expired; break; |
+ case SEC_ERROR_REVOKED_CERTIFICATE: desc = certificate_revoked; break; |
+ case SEC_ERROR_INADEQUATE_KEY_USAGE: |
+ case SEC_ERROR_INADEQUATE_CERT_TYPE: |
+ desc = certificate_unknown; break; |
+ case SEC_ERROR_UNTRUSTED_CERT: |
+ desc = isTLS ? access_denied : certificate_unknown; break; |
+ case SEC_ERROR_UNKNOWN_ISSUER: |
+ case SEC_ERROR_UNTRUSTED_ISSUER: |
+ desc = isTLS ? unknown_ca : certificate_unknown; break; |
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: |
+ desc = isTLS ? unknown_ca : certificate_expired; break; |
+ |
+ case SEC_ERROR_CERT_NOT_IN_NAME_SPACE: |
+ case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID: |
+ case SEC_ERROR_CA_CERT_INVALID: |
+ case SEC_ERROR_BAD_SIGNATURE: |
+ default: desc = bad_certificate; break; |
+ } |
+ SSL_DBG(("%d: SSL3[%d]: peer certificate is no good: error=%d", |
+ SSL_GETPID(), ss->fd, errCode)); |
+ |
+ (void) SSL3_SendAlert(ss, alert_fatal, desc); |
+} |
+ |
+ |
/* |
* Send handshake_Failure alert. Set generic error number. |
*/ |
@@ -3233,7 +3261,8 @@ |
** Caller must hold the ssl3Handshake lock. |
*/ |
static SECStatus |
-ssl3_UpdateHandshakeHashes(sslSocket *ss, unsigned char *b, unsigned int l) |
+ssl3_UpdateHandshakeHashes(sslSocket *ss, const unsigned char *b, |
+ unsigned int l) |
{ |
SECStatus rv = SECSuccess; |
@@ -3773,7 +3802,6 @@ |
**************************************************************************/ |
/* Called from ssl3_HandleHelloRequest(), |
- * ssl3_HandleFinished() (for step-up) |
* ssl3_RedoHandshake() |
* ssl2_BeginClientHandshake (when resuming ssl3 session) |
*/ |
@@ -4346,6 +4374,12 @@ |
SECStatus rv; |
SECItem wrappedKey; |
SSLWrappedSymWrappingKey wswk; |
+#ifdef NSS_ENABLE_ECC |
+ PK11SymKey * Ks = NULL; |
+ SECKEYPublicKey *pubWrapKey = NULL; |
+ SECKEYPrivateKey *privWrapKey = NULL; |
+ ECCWrappedKeyInfo *ecWrapped; |
+#endif /* NSS_ENABLE_ECC */ |
svrPrivKey = ss->serverCerts[exchKeyType].SERVERKEY; |
PORT_Assert(svrPrivKey != NULL); |
@@ -4422,13 +4456,6 @@ |
/* wrap symmetric wrapping key in server's public key. */ |
switch (exchKeyType) { |
-#ifdef NSS_ENABLE_ECC |
- PK11SymKey * Ks = NULL; |
- SECKEYPublicKey *pubWrapKey = NULL; |
- SECKEYPrivateKey *privWrapKey = NULL; |
- ECCWrappedKeyInfo *ecWrapped; |
-#endif /* NSS_ENABLE_ECC */ |
- |
case kt_rsa: |
asymWrapMechanism = CKM_RSA_PKCS; |
rv = PK11_PubWrapSymKey(asymWrapMechanism, svrPubKey, |
@@ -4796,7 +4823,7 @@ |
if (ss->sec.peerKey == NULL) { |
serverKey = CERT_ExtractPublicKey(ss->sec.peerCert); |
if (serverKey == NULL) { |
- PORT_SetError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); |
+ ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); |
return SECFailure; |
} |
} else { |
@@ -5226,6 +5253,7 @@ |
ssl3_CopyPeerCertsFromSID(ss, sid); |
} |
+ |
/* NULL value for PMS signifies re-use of the old MS */ |
rv = ssl3_InitPendingCipherSpec(ss, NULL); |
if (rv != SECSuccess) { |
@@ -5640,7 +5668,7 @@ |
#endif /* NSS_PLATFORM_CLIENT_AUTH */ |
switch (rv) { |
case SECWouldBlock: /* getClientAuthData has put up a dialog box. */ |
- ssl_SetAlwaysBlock(ss); |
+ ssl3_SetAlwaysBlock(ss); |
break; /* not an error */ |
case SECSuccess: |
@@ -5786,7 +5814,7 @@ |
SECKEYPrivateKey * key, |
CERTCertificateList *certChain) |
{ |
- SECStatus rv = SECFailure; |
+ SECStatus rv = SECSuccess; |
/* XXX This code only works on the initial handshake on a connection, |
** XXX It does not work on a subsequent handshake (redo). |
@@ -5816,11 +5844,6 @@ |
(void)SSL3_SendAlert(ss, alert_warning, no_certificate); |
} |
} |
- ssl_GetRecvBufLock(ss); |
- if (ss->ssl3.hs.msgState.buf != NULL) { |
- rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf); |
- } |
- ssl_ReleaseRecvBufLock(ss); |
} else { |
if (cert) { |
CERT_DestroyCertificate(cert); |
@@ -5831,22 +5854,38 @@ |
if (certChain) { |
CERT_DestroyCertificateList(certChain); |
} |
+ rv = SECFailure; |
} |
return rv; |
} |
PRBool |
ssl3_CanFalseStart(sslSocket *ss) { |
- return ss->opt.enableFalseStart && |
- !ss->sec.isServer && |
- !ss->ssl3.hs.isResuming && |
- ss->ssl3.cwSpec && |
- ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 && |
- (ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa || |
- ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh || |
- ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh); |
+ PRBool rv; |
+ |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); |
+ |
+ /* XXX: does not take into account whether we are waiting for |
+ * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when |
+ * that is done, this function could return different results each time it |
+ * would be called. |
+ */ |
+ |
+ ssl_GetSpecReadLock(ss); |
+ rv = ss->opt.enableFalseStart && |
+ !ss->sec.isServer && |
+ !ss->ssl3.hs.isResuming && |
+ ss->ssl3.cwSpec && |
+ ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 && |
+ (ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa || |
+ ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh || |
+ ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh); |
+ ssl_ReleaseSpecReadLock(ss); |
+ return rv; |
} |
+static SECStatus ssl3_SendClientSecondRound(sslSocket *ss); |
+ |
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete |
* ssl3 Server Hello Done message. |
* Caller must hold Handshake and RecvBuf locks. |
@@ -5856,10 +5895,6 @@ |
{ |
SECStatus rv; |
SSL3WaitState ws = ss->ssl3.hs.ws; |
- PRBool sendEmptyCert, sendCert; |
- int n = 0, i; |
- typedef SECStatus (*SendFunction)(sslSocket*); |
- SendFunction send_funcs[5]; |
SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake", |
SSL_GETPID(), ss->fd)); |
@@ -5875,14 +5910,72 @@ |
return SECFailure; |
} |
+ rv = ssl3_SendClientSecondRound(ss); |
+ |
+ return rv; |
+} |
+ |
+/* Called from ssl3_HandleServerHelloDone and ssl3_AuthCertificateComplete. |
+ * |
+ * Caller must hold Handshake and RecvBuf locks. |
+ */ |
+static SECStatus |
+ssl3_SendClientSecondRound(sslSocket *ss) |
+{ |
+ SECStatus rv; |
+ PRBool sendClientCert; |
+ PRBool sendEmptyCert; |
+ int n = 0, i; |
+ typedef SECStatus (*SendFunction)(sslSocket*); |
+ SendFunction send_funcs[5]; |
+ |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); |
+ |
+ sendClientCert = !ss->ssl3.sendEmptyCert && |
+ ss->ssl3.clientCertChain != NULL && |
+ (ss->ssl3.platformClientKey || |
+ ss->ssl3.clientPrivateKey != NULL); |
+ |
+ /* We must wait for the server's certificate to be authenticated before |
+ * sending the client certificate in order to disclosing the client |
+ * certificate to an attacker that does not have a valid cert for the |
+ * domain we are connecting to. |
+ * |
+ * XXX: We should do the same for the NPN extension, but for that we |
+ * need an option to give the application the ability to leak the NPN |
+ * information to get better performance. |
+ * |
+ * During the initial handshake on a connection, we never send/receive |
+ * application data until we have authenticated the server's certificate; |
+ * i.e. we have fully authenticated the handshake before using the cipher |
+ * specs agreed upon for that handshake. During a renegotiation, we may |
+ * continue sending and receiving application data during the handshake |
+ * interleaved with the handshake records. If we were to send the client's |
+ * second round for a renegotiation before the server's certificate was |
+ * authenticated, then the application data sent/received after this point |
+ * would be using cipher spec that hadn't been authenticated. By waiting |
+ * until the server's certificate has been authenticated during |
+ * renegotiations, we ensure that renegotiations have the same property |
+ * as initial handshakes; i.e. we have fully authenticated the handshake |
+ * before using the cipher specs agreed upon for that handshake for |
+ * application data. |
+ */ |
+ if (ss->ssl3.hs.restartTarget) { |
+ PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget"); |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+ if (ss->ssl3.hs.authCertificatePending && |
+ (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) { |
+ ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound; |
+ return SECWouldBlock; |
+ } |
+ |
ssl_GetXmitBufLock(ss); /*******************************/ |
sendEmptyCert = ss->ssl3.sendEmptyCert; |
ss->ssl3.sendEmptyCert = PR_FALSE; |
- sendCert = !sendEmptyCert && |
- ss->ssl3.clientCertChain != NULL && |
- (ss->ssl3.platformClientKey || |
- ss->ssl3.clientPrivateKey != NULL); |
if (ssl3_ExtensionNegotiated(ss, ssl_encrypted_client_certs)) { |
send_funcs[n++] = ssl3_SendClientKeyExchange; |
@@ -5890,7 +5983,7 @@ |
if (sendEmptyCert) { |
send_funcs[n++] = ssl3_SendEmptyCertificate; |
} |
- if (sendCert) { |
+ if (sendClientCert) { |
send_funcs[n++] = ssl3_SendCertificate; |
send_funcs[n++] = ssl3_SendCertificateVerify; |
} |
@@ -5898,11 +5991,11 @@ |
if (sendEmptyCert) { |
send_funcs[n++] = ssl3_SendEmptyCertificate; |
} |
- if (sendCert) { |
+ if (sendClientCert) { |
send_funcs[n++] = ssl3_SendCertificate; |
} |
send_funcs[n++] = ssl3_SendClientKeyExchange; |
- if (sendCert) { |
+ if (sendClientCert) { |
send_funcs[n++] = ssl3_SendCertificateVerify; |
} |
send_funcs[n++] = ssl3_SendChangeCipherSpecs; |
@@ -5917,8 +6010,9 @@ |
} |
} |
- /* We don't send NPN in a renegotiation as it's explicitly disallowed by |
- * the spec. */ |
+ /* XXX: If the server's certificate hasn't been authenticated by this |
+ * point, then we may be leaking this NPN message to an attacker. |
+ */ |
if (!ss->firstHsDone) { |
rv = ssl3_SendNextProto(ss); |
if (rv != SECSuccess) { |
@@ -7899,69 +7993,6 @@ |
} |
} |
- if (ss->ssl3.cachedInfoCertChainDigestReceived) { |
- /* Compute hash. */ |
- PRUint64 certChainHash; |
- int i; |
- FNV1A64_Init(&certChainHash); |
- for (i = 0; i < certChain->len; i++) { |
- unsigned int certLen = certChain->certs[i].len; |
- unsigned char certLenArray[3] = { |
- certLen >> 16, |
- certLen >> 8, |
- certLen |
- }; |
- FNV1A64_Update(&certChainHash, certLenArray, sizeof(certLenArray)); |
- FNV1A64_Update(&certChainHash, certChain->certs[i].data, certLen); |
- } |
- FNV1A64_Final(&certChainHash); |
- |
- /* Both |&certChainHash| and |ss->ssl3.certChainDigest| should be in |
- * network byte order since both are computed with the FNV1A64 hash, |
- * which calls the function htonll. |
- */ |
- if (memcmp(&certChainHash, ss->ssl3.certChainDigest, |
- sizeof(certChainHash)) == 0) { |
- /* The client correctly predicted the certificate chain. */ |
- |
- /* Handshake type: certificate. */ |
- rv = ssl3_AppendHandshakeNumber(ss, certificate, 1); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- /* Handshake message length. */ |
- rv = ssl3_AppendHandshakeNumber(ss, 15, 3); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- /* CertChainLen(3) + ASN.1CertLen(3) + DigestLen(1) + Digest(8) */ |
- rv = ssl3_AppendHandshakeNumber(ss, 12, 3); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- /* ASN.1CertLen(3) + DigestLen(1) + Digest(8) */ |
- rv = ssl3_AppendHandshakeNumber(ss, 9, 3); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- /* Digest Length Byte */ |
- rv = ssl3_AppendHandshakeNumber(ss, sizeof(certChainHash), 1); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- /* Digest */ |
- rv = ssl3_AppendHandshake(ss, &certChainHash, |
- sizeof(certChainHash)); |
- if (rv != SECSuccess) { |
- return rv; /* err set by AppendHandshake. */ |
- } |
- |
- return SECSuccess; |
- } |
- } |
- |
- /* Send the entire certificate as usual. */ |
- |
rv = ssl3_AppendHandshakeHeader(ss, certificate, len + 3); |
if (rv != SECSuccess) { |
return rv; /* err set by AppendHandshake. */ |
@@ -8109,15 +8140,13 @@ |
{ |
ssl3CertNode * c; |
ssl3CertNode * lastCert = NULL; |
- ssl3CertNode * certs = NULL; |
- PRArenaPool * arena = NULL; |
- CERTCertificate *cert; |
PRInt32 remaining = 0; |
PRInt32 size; |
SECStatus rv; |
PRBool isServer = (PRBool)(!!ss->sec.isServer); |
+ PRBool trusted = PR_FALSE; |
PRBool isTLS; |
- SSL3AlertDescription desc = bad_certificate; |
+ SSL3AlertDescription desc; |
int errCode = SSL_ERROR_RX_MALFORMED_CERTIFICATE; |
SECItem certItem; |
@@ -8167,54 +8196,43 @@ |
errCode = PORT_GetError(); |
goto loser; |
} |
- goto cert_block; |
+ goto server_no_cert; |
} |
- ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
- if ( arena == NULL ) { |
+ ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
+ if (ss->ssl3.peerCertArena == NULL) { |
goto loser; /* don't send alerts on memory errors */ |
} |
- if (length == 12 && ssl3_ExtensionNegotiated(ss, ssl_cached_info_xtn)) { |
- /* We are dealing with a certificate_chain digest */ |
- int i; |
+ /* First get the peer cert. */ |
+ remaining -= 3; |
+ if (remaining < 0) |
+ goto decode_loser; |
- ss->ssl3.cachedInfoCertChainDigestReceived = PR_TRUE; |
+ size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length); |
+ if (size <= 0) |
+ goto loser; /* fatal alert already sent by ConsumeHandshake. */ |
- /* Make sure the digests match. */ |
- if (memcmp(b + 4, ss->ssl3.certChainDigest, 8)) { |
- desc = handshake_failure; |
- goto alert_loser; |
- } |
+ if (remaining < size) |
+ goto decode_loser; |
- /* First get the peer cert. */ |
- if (ss->ssl3.predictedCertChain[0] == NULL) { |
- desc = handshake_failure; |
- goto alert_loser; |
- } |
- ss->sec.peerCert = CERT_DupCertificate(ss->ssl3.predictedCertChain[0]); |
+ certItem.data = b; |
+ certItem.len = size; |
+ b += size; |
+ length -= size; |
+ remaining -= size; |
- /* Now get all of the CA certs. */ |
- ss->ssl3.peerCertChain = NULL; |
- for (i = 1; ss->ssl3.predictedCertChain[i] != NULL; i++) { |
- c = PORT_ArenaNew(arena, ssl3CertNode); |
- if (c == NULL) { |
- goto loser; /* don't send alerts on memory errors */ |
- } |
- c->cert = CERT_DupCertificate(ss->ssl3.predictedCertChain[i]); |
- c->next = NULL; |
- if (lastCert) { |
- lastCert->next = c; |
- } else { |
- ss->ssl3.peerCertChain = c; |
- } |
- lastCert = c; |
- } |
- } else { |
- /* We are dealing with a regular certificate message */ |
- ss->ssl3.cachedInfoCertChainDigestReceived = PR_FALSE; |
+ ss->sec.peerCert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, |
+ PR_FALSE, PR_TRUE); |
+ if (ss->sec.peerCert == NULL) { |
+ /* We should report an alert if the cert was bad, but not if the |
+ * problem was just some local problem, like memory error. |
+ */ |
+ goto ambiguous_err; |
+ } |
- /* First get the peer cert. */ |
+ /* Now get all of the CA certs. */ |
+ while (remaining > 0) { |
remaining -= 3; |
if (remaining < 0) |
goto decode_loser; |
@@ -8228,66 +8246,40 @@ |
certItem.data = b; |
certItem.len = size; |
- b += size; |
+ b += size; |
length -= size; |
remaining -= size; |
- ss->sec.peerCert = CERT_NewTempCertificate(ss->dbHandle, &certItem, |
- NULL, PR_FALSE, PR_TRUE); |
- if (ss->sec.peerCert == NULL) { |
- /* We should report an alert if the cert was bad, but not if the |
- * problem was just some local problem, like memory error. |
- */ |
- goto ambiguous_err; |
+ c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode); |
+ if (c == NULL) { |
+ goto loser; /* don't send alerts on memory errors */ |
} |
- /* Now get all of the CA certs. */ |
- while (remaining > 0) { |
- remaining -= 3; |
- if (remaining < 0) |
- goto decode_loser; |
- |
- size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length); |
- if (size <= 0) |
- goto loser; /* fatal alert already sent by ConsumeHandshake. */ |
- |
- if (remaining < size) |
- goto decode_loser; |
- |
- certItem.data = b; |
- certItem.len = size; |
- b += size; |
- length -= size; |
- remaining -= size; |
- |
- c = PORT_ArenaNew(arena, ssl3CertNode); |
- if (c == NULL) { |
- goto loser; /* don't send alerts on memory errors */ |
- } |
- |
- c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, |
- PR_FALSE, PR_TRUE); |
- if (c->cert == NULL) { |
- goto ambiguous_err; |
- } |
- |
- c->next = NULL; |
- if (lastCert) { |
- lastCert->next = c; |
- } else { |
- certs = c; |
- } |
- lastCert = c; |
+ c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL, |
+ PR_FALSE, PR_TRUE); |
+ if (c->cert == NULL) { |
+ goto ambiguous_err; |
} |
- if (remaining != 0) |
- goto decode_loser; |
+ if (c->cert->trust) |
+ trusted = PR_TRUE; |
- ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; |
+ c->next = NULL; |
+ if (lastCert) { |
+ lastCert->next = c; |
+ } else { |
+ ss->ssl3.peerCertChain = c; |
+ } |
+ lastCert = c; |
} |
+ if (remaining != 0) |
+ goto decode_loser; |
+ |
SECKEY_UpdateCertPQG(ss->sec.peerCert); |
+ ss->ssl3.hs.authCertificatePending = PR_FALSE; |
+ |
/* |
* Ask caller-supplied callback function to validate cert chain. |
*/ |
@@ -8295,42 +8287,42 @@ |
PR_TRUE, isServer); |
if (rv) { |
errCode = PORT_GetError(); |
- if (!ss->handleBadCert) { |
- goto bad_cert; |
+ if (rv != SECWouldBlock) { |
+ if (ss->handleBadCert) { |
+ rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd); |
+ } |
} |
- rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd); |
- if ( rv ) { |
- if ( rv == SECWouldBlock ) { |
- /* someone will handle this connection asynchronously*/ |
- SSL_DBG(("%d: SSL3[%d]: go to async cert handler", |
- SSL_GETPID(), ss->fd)); |
- ssl_SetAlwaysBlock(ss); |
- goto cert_block; |
+ |
+ if (rv == SECWouldBlock) { |
+ if (ss->sec.isServer) { |
+ errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS; |
+ rv = SECFailure; |
+ goto loser; |
} |
- /* cert is bad */ |
- goto bad_cert; |
+ |
+ ss->ssl3.hs.authCertificatePending = PR_TRUE; |
+ rv = SECSuccess; |
+ |
+ /* XXX: Async cert validation and False Start don't work together |
+ * safely yet; if we leave False Start enabled, we may end up false |
+ * starting (sending application data) before we |
+ * SSL_AuthCertificateComplete has been called. |
+ */ |
+ ss->opt.enableFalseStart = PR_FALSE; |
} |
- /* cert is good */ |
- } |
- /* start SSL Step Up, if appropriate */ |
- cert = ss->sec.peerCert; |
- if (!isServer && |
- ssl3_global_policy_some_restricted && |
- ss->ssl3.policy == SSL_ALLOWED && |
- anyRestrictedEnabled(ss) && |
- SECSuccess == CERT_VerifyCertNow(cert->dbhandle, cert, |
- PR_FALSE, /* checkSig */ |
- certUsageSSLServerWithStepUp, |
-/*XXX*/ ss->authCertificateArg) ) { |
- ss->ssl3.policy = SSL_RESTRICTED; |
- ss->ssl3.hs.rehandshake = PR_TRUE; |
+ if (rv != SECSuccess) { |
+ ssl3_SendAlertForCertError(ss, errCode); |
+ goto loser; |
+ } |
} |
ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); |
ssl3_CopyPeerCertsToSID(ss->ssl3.peerCertChain, ss->sec.ci.sid); |
if (!ss->sec.isServer) { |
+ CERTCertificate *cert = ss->sec.peerCert; |
+ |
/* set the server authentication and key exchange types and sizes |
** from the value in the cert. If the key exchange key is different, |
** it will get fixed when we handle the server key exchange message. |
@@ -8371,16 +8363,7 @@ |
SECKEY_DestroyPublicKey(pubKey); |
pubKey = NULL; |
} |
- } |
-cert_block: |
- if (ss->sec.isServer) { |
- if (ssl3_ExtensionNegotiated(ss, ssl_encrypted_client_certs)) { |
- ss->ssl3.hs.ws = wait_cert_verify; |
- } else { |
- ss->ssl3.hs.ws = wait_client_key; |
- } |
- } else { |
ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */ |
if (ss->ssl3.hs.kea_def->is_limited || |
/* XXX OR server cert is signing only. */ |
@@ -8391,11 +8374,22 @@ |
ss->ssl3.hs.kea_def->exchKeyType == kt_dh) { |
ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */ |
} |
+ } else { |
+server_no_cert: |
+ if (ssl3_ExtensionNegotiated(ss, ssl_encrypted_client_certs)) { |
+ ss->ssl3.hs.ws = wait_cert_verify; |
+ } else { |
+ ss->ssl3.hs.ws = wait_client_key; |
+ } |
} |
- /* rv must normally be equal to SECSuccess here. If we called |
- * handleBadCert, it can also be SECWouldBlock. |
- */ |
+ PORT_Assert(rv == SECSuccess); |
+ if (rv != SECSuccess) { |
+ errCode = SEC_ERROR_LIBRARY_FAILURE; |
+ rv = SECFailure; |
+ goto loser; |
+ } |
+ |
return rv; |
ambiguous_err: |
@@ -8410,35 +8404,9 @@ |
} |
goto loser; |
} |
- /* fall through to bad_cert. */ |
+ ssl3_SendAlertForCertError(ss, errCode); |
+ goto loser; |
-bad_cert: /* caller has set errCode. */ |
- switch (errCode) { |
- case SEC_ERROR_LIBRARY_FAILURE: desc = unsupported_certificate; break; |
- case SEC_ERROR_EXPIRED_CERTIFICATE: desc = certificate_expired; break; |
- case SEC_ERROR_REVOKED_CERTIFICATE: desc = certificate_revoked; break; |
- case SEC_ERROR_INADEQUATE_KEY_USAGE: |
- case SEC_ERROR_INADEQUATE_CERT_TYPE: |
- desc = certificate_unknown; break; |
- case SEC_ERROR_UNTRUSTED_CERT: |
- desc = isTLS ? access_denied : certificate_unknown; break; |
- case SEC_ERROR_UNKNOWN_ISSUER: |
- case SEC_ERROR_UNTRUSTED_ISSUER: |
- desc = isTLS ? unknown_ca : certificate_unknown; break; |
- case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: |
- desc = isTLS ? unknown_ca : certificate_expired; break; |
- |
- case SEC_ERROR_CERT_NOT_IN_NAME_SPACE: |
- case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID: |
- case SEC_ERROR_CA_CERT_INVALID: |
- case SEC_ERROR_BAD_SIGNATURE: |
- default: desc = bad_certificate; break; |
- } |
- SSL_DBG(("%d: SSL3[%d]: peer certificate is no good: error=%d", |
- SSL_GETPID(), ss->fd, errCode)); |
- |
- goto alert_loser; |
- |
decode_loser: |
desc = isTLS ? decode_error : bad_certificate; |
@@ -8446,10 +8414,6 @@ |
(void)SSL3_SendAlert(ss, alert_fatal, desc); |
loser: |
- if (ss->ssl3.peerCertChain == NULL) { |
- ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL; |
- } |
- PORT_Assert(certs == NULL); |
ssl3_CleanupPeerCerts(ss); |
if (ss->sec.peerCert != NULL) { |
@@ -8460,61 +8424,63 @@ |
return SECFailure; |
} |
+static SECStatus ssl3_FinishHandshake(sslSocket *ss); |
-/* 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). |
- * |
- * XXX This code only works on the initial handshake on a connection, XXX |
- * It does not work on a subsequent handshake (redo). |
- * |
- * Return value: XXX |
- * |
- * Caller holds 1stHandshakeLock. |
+static SECStatus |
+ssl3_AlwaysFail(sslSocket * ss) |
+{ |
+ PORT_SetError(PR_INVALID_STATE_ERROR); |
+ return SECFailure; |
+} |
+ |
+/* Caller must hold 1stHandshakeLock. |
*/ |
-int |
-ssl3_RestartHandshakeAfterServerCert(sslSocket *ss) |
+SECStatus |
+ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error) |
{ |
- CERTCertificate * cert; |
- int rv = SECSuccess; |
+ SECStatus rv; |
- if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) { |
- SET_ERROR_CODE |
- return SECFailure; |
+ PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss)); |
+ |
+ if (ss->sec.isServer) { |
+ PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS); |
+ return SECFailure; |
} |
- if (!ss->ssl3.initialized) { |
- SET_ERROR_CODE |
- return SECFailure; |
- } |
- cert = ss->sec.peerCert; |
+ ssl_GetRecvBufLock(ss); |
+ ssl_GetSSL3HandshakeLock(ss); |
- /* Permit step up if user decided to accept the cert */ |
- if (!ss->sec.isServer && |
- ssl3_global_policy_some_restricted && |
- ss->ssl3.policy == SSL_ALLOWED && |
- anyRestrictedEnabled(ss) && |
- (SECSuccess == CERT_VerifyCertNow(cert->dbhandle, cert, |
- PR_FALSE, /* checksig */ |
- certUsageSSLServerWithStepUp, |
-/*XXX*/ ss->authCertificateArg) )) { |
- ss->ssl3.policy = SSL_RESTRICTED; |
- ss->ssl3.hs.rehandshake = PR_TRUE; |
+ if (!ss->ssl3.hs.authCertificatePending) { |
+ PORT_SetError(PR_INVALID_STATE_ERROR); |
+ rv = SECFailure; |
+ goto done; |
} |
- if (ss->handshake != NULL) { |
- ss->handshake = ssl_GatherRecord1stHandshake; |
- ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); |
- ssl3_CopyPeerCertsToSID((ssl3CertNode *)ss->ssl3.peerCertChain, |
- ss->sec.ci.sid); |
+ ss->ssl3.hs.authCertificatePending = PR_FALSE; |
- ssl_GetRecvBufLock(ss); |
- if (ss->ssl3.hs.msgState.buf != NULL) { |
- rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf); |
+ if (error != 0) { |
+ ss->ssl3.hs.restartTarget = ssl3_AlwaysFail; |
+ ssl3_SendAlertForCertError(ss, error); |
+ rv = SECSuccess; |
+ } else if (ss->ssl3.hs.restartTarget != NULL) { |
+ sslRestartTarget target = ss->ssl3.hs.restartTarget; |
+ ss->ssl3.hs.restartTarget = NULL; |
+ rv = target(ss); |
+ /* Even if we blocked here, we have accomplished enough to claim |
+ * success. Any remaining work will be taken care of by subsequent |
+ * calls to SSL_ForceHandshake/PR_Send/PR_Read/etc. |
+ */ |
+ if (rv == SECWouldBlock) { |
+ rv = SECSuccess; |
} |
- ssl_ReleaseRecvBufLock(ss); |
+ } else { |
+ rv = SECSuccess; |
} |
+done: |
+ ssl_ReleaseSSL3HandshakeLock(ss); |
+ ssl_ReleaseRecvBufLock(ss); |
+ |
return rv; |
} |
@@ -8779,7 +8745,6 @@ |
SECStatus rv = SECSuccess; |
PRBool isServer = ss->sec.isServer; |
PRBool isTLS; |
- PRBool doStepUp; |
SSL3KEAType effectiveExchKeyType; |
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
@@ -8835,8 +8800,6 @@ |
} |
} |
- doStepUp = (PRBool)(!isServer && ss->ssl3.hs.rehandshake); |
- |
ssl_GetXmitBufLock(ss); /*************************************/ |
if ((isServer && !ss->ssl3.hs.isResuming) || |
@@ -8862,19 +8825,18 @@ |
goto xmit_loser; /* err is set. */ |
} |
/* If this thread is in SSL_SecureSend (trying to write some data) |
- ** or if it is going to step up, |
** then set the ssl_SEND_FLAG_FORCE_INTO_BUFFER flag, so that the |
** last two handshake messages (change cipher spec and finished) |
** will be sent in the same send/write call as the application data. |
*/ |
- if (doStepUp || ss->writerThread == PR_GetCurrentThread()) { |
+ if (ss->writerThread == PR_GetCurrentThread()) { |
flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER; |
} |
if (!isServer && !ss->firstHsDone) { |
rv = ssl3_SendNextProto(ss); |
if (rv != SECSuccess) { |
- goto xmit_loser; /* err code was set. */ |
+ goto xmit_loser; /* err code was set. */ |
} |
} |
@@ -8884,22 +8846,12 @@ |
} |
} |
- /* Optimization: don't cache this connection if we're going to step up. */ |
- if (doStepUp) { |
- ssl_FreeSID(sid); |
- ss->sec.ci.sid = sid = NULL; |
- ss->ssl3.hs.rehandshake = PR_FALSE; |
- rv = ssl3_SendClientHello(ss); |
xmit_loser: |
- ssl_ReleaseXmitBufLock(ss); |
- return rv; /* err code is set if appropriate. */ |
+ ssl_ReleaseXmitBufLock(ss); /*************************************/ |
+ if (rv != SECSuccess) { |
+ return rv; |
} |
- ssl_ReleaseXmitBufLock(ss); /*************************************/ |
- |
- /* The first handshake is now completed. */ |
- ss->handshake = NULL; |
- ss->firstHsDone = PR_TRUE; |
ss->gs.writeOffset = 0; |
ss->gs.readOffset = 0; |
@@ -8949,10 +8901,42 @@ |
/* If the wrap failed, we don't cache the sid. |
* The connection continues normally however. |
*/ |
- if (rv == SECSuccess) { |
- (*ss->sec.cache)(sid); |
+ ss->ssl3.hs.cacheSID = rv == SECSuccess; |
+ } |
+ |
+ if (ss->ssl3.hs.authCertificatePending) { |
+ if (ss->ssl3.hs.restartTarget) { |
+ PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget"); |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
} |
+ |
+ ss->ssl3.hs.restartTarget = ssl3_FinishHandshake; |
+ return SECWouldBlock; |
} |
+ |
+ rv = ssl3_FinishHandshake(ss); |
+ return rv; |
+} |
+ |
+SECStatus |
+ssl3_FinishHandshake(sslSocket * ss) |
+{ |
+ SECStatus rv; |
+ |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); |
+ PORT_Assert( ss->ssl3.hs.restartTarget == NULL ); |
+ |
+ /* The first handshake is now completed. */ |
+ ss->handshake = NULL; |
+ ss->firstHsDone = PR_TRUE; |
+ |
+ if (ss->sec.ci.sid->cached == never_cached && |
+ !ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) { |
+ (*ss->sec.cache)(ss->sec.ci.sid); |
+ } |
+ |
ss->ssl3.hs.ws = idle_handshake; |
/* Do the handshake callback for sslv3 here, if we cannot false start. */ |
@@ -9317,7 +9301,6 @@ |
* |
* Called from ssl3_GatherCompleteHandshake |
* ssl3_RestartHandshakeAfterCertReq |
- * ssl3_RestartHandshakeAfterServerCert |
* |
* Caller must hold the RecvBufLock. |
* |
@@ -9667,7 +9650,6 @@ |
ssl_GetSpecWriteLock(ss); |
ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0]; |
ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1]; |
- ss->ssl3.hs.rehandshake = PR_FALSE; |
ss->ssl3.hs.sendingSCSV = PR_FALSE; |
ssl3_InitCipherSpec(ss, ss->ssl3.crSpec); |
ssl3_InitCipherSpec(ss, ss->ssl3.prSpec); |
@@ -9776,10 +9758,6 @@ |
} |
suite->policy = policy; |
- if (policy == SSL_RESTRICTED) { |
- ssl3_global_policy_some_restricted = PR_TRUE; |
- } |
- |
return SECSuccess; |
} |
@@ -9956,21 +9934,6 @@ |
return rv; |
} |
-static void |
-ssl3_CleanupPredictedPeerCertificates(sslSocket *ss) { |
- unsigned int i; |
- |
- if (!ss->ssl3.predictedCertChain) |
- return; |
- |
- for (i = 0; ss->ssl3.predictedCertChain[i]; i++) { |
- CERT_DestroyCertificate(ss->ssl3.predictedCertChain[i]); |
- } |
- |
- PORT_Free(ss->ssl3.predictedCertChain); |
- ss->ssl3.predictedCertChain = NULL; |
-} |
- |
/* Called from ssl_DestroySocketContents() in sslsock.c */ |
void |
ssl3_DestroySSL3Info(sslSocket *ss) |
@@ -9994,9 +9957,6 @@ |
ss->ssl3.clientCertChain = NULL; |
} |
- if (ss->ssl3.predictedCertChain != NULL) |
- ssl3_CleanupPredictedPeerCertificates(ss); |
- |
/* clean up handshake */ |
if (ss->opt.bypassPKCS11) { |
SHA1_DestroyContext((SHA1Context *)ss->ssl3.hs.sha_cx, PR_FALSE); |
@@ -10030,10 +9990,7 @@ |
ss->ssl3.initialized = PR_FALSE; |
- if (ss->ssl3.nextProto.data) { |
- PORT_Free(ss->ssl3.nextProto.data); |
- ss->ssl3.nextProto.data = NULL; |
- } |
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); |
} |
/* End of ssl3con.c */ |