Index: net/third_party/nss/ssl/ssl3ext.c |
=================================================================== |
--- net/third_party/nss/ssl/ssl3ext.c (revision 124804) |
+++ net/third_party/nss/ssl/ssl3ext.c (working copy) |
@@ -41,7 +41,7 @@ |
* ***** END LICENSE BLOCK ***** */ |
/* TLS extension code moved here from ssl3ecc.c */ |
-/* $Id: ssl3ext.c,v 1.14 2010/04/03 19:19:07 nelson%bolyard.com Exp $ */ |
+/* $Id: ssl3ext.c,v 1.21 2012/02/15 21:52:08 kaie%kuix.de Exp $ */ |
#include "nssrenam.h" |
#include "nss.h" |
@@ -56,7 +56,7 @@ |
static PK11SymKey *session_ticket_enc_key_pkcs11 = NULL; |
static PK11SymKey *session_ticket_mac_key_pkcs11 = NULL; |
-static unsigned char session_ticket_enc_key[32]; |
+static unsigned char session_ticket_enc_key[AES_256_KEY_LENGTH]; |
static unsigned char session_ticket_mac_key[SHA256_LENGTH]; |
static PRBool session_ticket_keys_initialized = PR_FALSE; |
@@ -78,6 +78,12 @@ |
PRBool append, PRUint32 maxBytes); |
static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, |
PRUint16 ex_type, SECItem *data); |
+static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, |
+ PRUint16 ex_type, SECItem *data); |
+static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, |
+ PRUint16 ex_type, SECItem *data); |
+static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, |
+ PRUint32 maxBytes); |
static SECStatus ssl3_ServerHandleEncryptedClientCertsXtn(sslSocket *ss, |
PRUint16 ex_type, SECItem *data); |
static SECStatus ssl3_ClientHandleEncryptedClientCertsXtn(sslSocket *ss, |
@@ -242,8 +248,7 @@ |
{ ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, |
{ ssl_encrypted_client_certs, &ssl3_ServerHandleEncryptedClientCertsXtn }, |
{ ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, |
- { ssl_next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, |
- { ssl_cached_info_xtn, &ssl3_ServerHandleCachedInfoXtn }, |
+ { ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, |
{ ssl_ob_cert_xtn, &ssl3_ServerHandleOBCertXtn }, |
{ -1, NULL } |
}; |
@@ -256,8 +261,7 @@ |
{ ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, |
{ ssl_encrypted_client_certs, &ssl3_ClientHandleEncryptedClientCertsXtn }, |
{ ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, |
- { ssl_next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, |
- { ssl_cached_info_xtn, &ssl3_ClientHandleCachedInfoXtn }, |
+ { ssl_next_proto_nego_xtn, &ssl3_ClientHandleNextProtoNegoXtn }, |
{ ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, |
{ ssl_ob_cert_xtn, &ssl3_ClientHandleOBCertXtn }, |
{ -1, NULL } |
@@ -284,8 +288,7 @@ |
#endif |
{ ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, |
{ ssl_encrypted_client_certs, &ssl3_SendEncryptedClientCertsXtn }, |
- { ssl_next_proto_neg_xtn, &ssl3_ClientSendNextProtoNegoXtn }, |
- { ssl_cached_info_xtn, &ssl3_ClientSendCachedInfoXtn }, |
+ { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn }, |
{ ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, |
{ ssl_ob_cert_xtn, &ssl3_SendOBCertXtn } |
/* any extra entries will appear as { 0, NULL } */ |
@@ -555,11 +558,12 @@ |
} |
/* handle an incoming Next Protocol Negotiation extension. */ |
-SECStatus |
+static SECStatus |
ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) |
{ |
- if (data->len != 0) { |
+ if (ss->firstHsDone || data->len != 0) { |
/* Clients MUST send an empty NPN extension, if any. */ |
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); |
return SECFailure; |
} |
@@ -570,16 +574,20 @@ |
* of the lengths may be 0 and the sum of the lengths must equal the length of |
* the block. */ |
SECStatus |
-ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length) |
+ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned int length) |
{ |
unsigned int offset = 0; |
while (offset < length) { |
- if (data[offset] == 0) { |
+ unsigned int newOffset = offset + 1 + (unsigned int) data[offset]; |
+ /* Reject embedded nulls to protect against buggy applications that |
+ * store protocol identifiers in null-terminated strings. |
+ */ |
+ if (newOffset > length || data[offset] == 0) { |
PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); |
return SECFailure; |
} |
- offset += (unsigned int)data[offset] + 1; |
+ offset = newOffset; |
} |
if (offset > length) { |
@@ -590,39 +598,51 @@ |
return SECSuccess; |
} |
-SECStatus |
+static SECStatus |
ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, |
- SECItem *data) |
+ SECItem *data) |
{ |
SECStatus rv; |
- unsigned char result[255]; |
- unsigned int result_len; |
+ unsigned char resultBuffer[255]; |
+ SECItem result = { siBuffer, resultBuffer, 0 }; |
+ if (ss->firstHsDone) { |
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); |
+ return SECFailure; |
+ } |
+ |
rv = ssl3_ValidateNextProtoNego(data->data, data->len); |
if (rv != SECSuccess) |
return rv; |
- rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, |
- data->data, data->len, |
- result, &result_len); |
+ /* ss->nextProtoCallback cannot normally be NULL if we negotiated the |
+ * extension. However, It is possible that an application erroneously |
+ * cleared the callback between the time we sent the ClientHello and now. |
+ */ |
+ PORT_Assert(ss->nextProtoCallback != NULL); |
+ if (!ss->nextProtoCallback) { |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+ |
+ rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len, |
+ result.data, &result.len, sizeof resultBuffer); |
if (rv != SECSuccess) |
return rv; |
/* If the callback wrote more than allowed to |result| it has corrupted our |
* stack. */ |
- PORT_Assert(result_len <= sizeof(result)); |
+ if (result.len > sizeof result) { |
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
+ return SECFailure; |
+ } |
- if (ss->ssl3.nextProto.data) |
- PORT_Free(ss->ssl3.nextProto.data); |
- ss->ssl3.nextProto.data = PORT_Alloc(result_len); |
- PORT_Memcpy(ss->ssl3.nextProto.data, result, result_len); |
- ss->ssl3.nextProto.len = result_len; |
- return SECSuccess; |
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); |
+ return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); |
} |
-PRInt32 |
-ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, |
- PRBool append, |
- PRUint32 maxBytes) |
+static PRInt32 |
+ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append, |
+ PRUint32 maxBytes) |
{ |
PRInt32 extension_length; |
@@ -635,21 +655,21 @@ |
if (append && maxBytes >= extension_length) { |
SECStatus rv; |
- rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2); |
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_nego_xtn, 2); |
if (rv != SECSuccess) |
goto loser; |
rv = ssl3_AppendHandshakeNumber(ss, 0, 2); |
if (rv != SECSuccess) |
goto loser; |
ss->xtnData.advertised[ss->xtnData.numAdvertised++] = |
- ssl_next_proto_neg_xtn; |
+ ssl_next_proto_nego_xtn; |
} else if (maxBytes < extension_length) { |
return 0; |
} |
return extension_length; |
- loser: |
+loser: |
return -1; |
} |
@@ -673,261 +693,6 @@ |
return SECSuccess; |
} |
-/* ssl3_ClientSendCachedInfoXtn builds the cached_info extension on the |
- * client side. */ |
-PRInt32 |
-ssl3_ClientSendCachedInfoXtn(sslSocket * ss, PRBool append, |
- PRUint32 maxBytes) |
-{ |
- PRInt32 extension_length; |
- PRBool send_empty; |
- CERTCertificate ** predictedCertChain; |
- |
- if (!ss->opt.enableCachedInfo) |
- return 0; |
- |
- predictedCertChain = ss->ssl3.predictedCertChain; |
- send_empty = (predictedCertChain == NULL); |
- |
- /* minimum extension: |
- * extension_type (2-bytes) + |
- * length(extension_data) (2-bytes) + |
- * length(cached_info) (2-bytes) + |
- */ |
- extension_length = send_empty ? 6 : 16; |
- |
- if (append && maxBytes >= extension_length) { |
- SECStatus rv; |
- |
- /* ExtensionType */ |
- rv = ssl3_AppendHandshakeNumber(ss, ssl_cached_info_xtn, 2); |
- if (rv != SECSuccess) |
- return -1; |
- /* Extension Length */ |
- rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); |
- if (rv != SECSuccess) |
- return -1; |
- if (send_empty) { |
- /* Cached Information Length */ |
- rv = ssl3_AppendHandshakeNumber(ss, 0, 2); |
- if (rv != SECSuccess) |
- return -1; |
- } else { |
- PRUint64 certChainHash; |
- int i; |
- PRUint8* digestPtr = (PRUint8*) &certChainHash; |
- |
- /* Cached Information Length */ |
- rv = ssl3_AppendHandshakeNumber(ss, 10, 2); |
- if (rv != SECSuccess) |
- return -1; |
- /* Cached Information Type */ |
- rv = ssl3_AppendHandshakeNumber(ss, 1 /* certificate_chain */, 1); |
- if (rv != SECSuccess) |
- return -1; |
- /* hash length */ |
- rv = ssl3_AppendHandshakeNumber(ss, 8, 1); |
- if (rv != SECSuccess) |
- return -1; |
- /* hash */ |
- FNV1A64_Init(&certChainHash); |
- for (i = 0; predictedCertChain[i] != NULL; i++) { |
- unsigned int certLen = predictedCertChain[i]->derCert.len; |
- unsigned char certLenArray[3] = { |
- certLen >> 16, |
- certLen >> 8, |
- certLen |
- }; |
- FNV1A64_Update(&certChainHash, certLenArray, 3); |
- FNV1A64_Update(&certChainHash, |
- predictedCertChain[i]->derCert.data, certLen); |
- } |
- FNV1A64_Final(&certChainHash); |
- rv = ssl3_AppendHandshake(ss, &certChainHash, 8); |
- if (rv != SECSuccess) |
- return -1; |
- for (i = 0; i < 8; i++) { |
- ss->ssl3.certChainDigest[i] = digestPtr[i]; |
- } |
- } |
- |
- } else if (maxBytes < extension_length) { |
- PORT_Assert(0); |
- return 0; |
- } |
- ss->xtnData.advertised[ss->xtnData.numAdvertised++] = |
- ssl_cached_info_xtn; |
- return extension_length; |
-} |
- |
-SECStatus |
-ssl3_ServerHandleCachedInfoXtn(sslSocket *ss, PRUint16 ex_type, |
- SECItem *data) |
-{ |
- SECStatus rv; |
- unsigned char *cached_info = data->data; |
- unsigned int remaining_len; |
- |
- /* Ignore the extension if it isn't enabled. */ |
- if (!ss->opt.enableCachedInfo) |
- return SECSuccess; |
- |
- if (data->len < 2) |
- return SECFailure; |
- remaining_len = (cached_info[0] << 8) | cached_info[1]; |
- if (remaining_len > 2048 || remaining_len != data->len - 2) |
- return SECFailure; |
- cached_info += 2; |
- |
- /* Handle reconnaissance case. */ |
- if (remaining_len == 0) { |
- /* The client supports information caching, but provides no information |
- * about what information types it supports */ |
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
- rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, |
- ssl3_ServerSendCachedInfoXtn); |
- return rv; |
- } |
- |
- /* Iterate over the CachedObjects and pick the first item of type |
- * certificate_chain, while ignoring everything else. */ |
- while (remaining_len >= 2) { |
- unsigned char cached_object_type = *cached_info++; |
- unsigned int cached_object_length = *cached_info++; |
- remaining_len -= 2; |
- if (remaining_len < cached_object_length) |
- return SECFailure; |
- if (cached_object_length != 8) /* The digest must be present. */ |
- return SECFailure; |
- if (cached_object_type == cached_info_certificate_chain && |
- !ss->ssl3.cachedInfoCertChainDigestReceived) { |
- ss->ssl3.cachedInfoCertChainDigestReceived = PR_TRUE; |
- memcpy(ss->ssl3.certChainDigest, cached_info, 8); |
- } |
- remaining_len -= cached_object_length; |
- cached_info += cached_object_length; |
- } |
- |
- if (remaining_len != 0) |
- return SECFailure; |
- |
- if (ss->ssl3.cachedInfoCertChainDigestReceived) { |
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
- rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, |
- ssl3_ServerSendCachedInfoXtn); |
- return SECSuccess; |
- } |
- |
- return SECSuccess; |
-} |
- |
-/* ssl3_ServerSendCachedInfoXtn builds the cached_info extension on the |
- * server side. */ |
-PRInt32 |
-ssl3_ServerSendCachedInfoXtn(sslSocket * ss, PRBool append, |
- PRUint32 maxBytes) |
-{ |
- PRInt32 extension_length = 2 /* extension type */ + |
- 2 /* extension length */ + |
- 2 /* cached_info length */ + |
- 1 /* CachedInformationType */ + |
- 1 /* hash value length (0) */; |
- SECStatus rv; |
- |
- PORT_Assert(ss->opt.enableCachedInfo); |
- |
- if (append && maxBytes >= extension_length) { |
- /* ExtensionType */ |
- rv = ssl3_AppendHandshakeNumber(ss, ssl_cached_info_xtn, 2); |
- if (rv != SECSuccess) |
- return -1; |
- /* Extension Length */ |
- rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); |
- if (rv != SECSuccess) |
- return -1; |
- /* Cached Information Length */ |
- rv = ssl3_AppendHandshakeNumber(ss, 2, 2); |
- if (rv != SECSuccess) |
- return -1; |
- /* Cached Information Type */ |
- rv = ssl3_AppendHandshakeNumber(ss, 1 /* certificate_chain */, 1); |
- if (rv != SECSuccess) |
- return -1; |
- /* hash length */ |
- rv = ssl3_AppendHandshakeNumber(ss, 0, 1); |
- if (rv != SECSuccess) |
- return -1; |
- } else if (maxBytes < extension_length) { |
- PORT_Assert(0); |
- return 0; |
- } |
- |
- return extension_length; |
-} |
- |
-SECStatus |
-ssl3_ClientHandleCachedInfoXtn(sslSocket *ss, PRUint16 ex_type, |
- SECItem *data) |
-{ |
- unsigned char * cached_info = data->data; |
- unsigned int remaining_cached_info_length; |
- PRBool has_correct_cert_chain = PR_FALSE; |
- |
- /* If we didn't request this extension, then the server may not echo it. */ |
- if (!ss->opt.enableCachedInfo) |
- return SECFailure; |
- |
- if (data->len == 0) { |
- /* The server supports information caching, but provides no information |
- * about what information types it supports */ |
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
- return SECSuccess; |
- } |
- |
- if (data->len < 2) |
- return SECFailure; |
- remaining_cached_info_length = (cached_info[0] << 8) | cached_info[1]; |
- if (remaining_cached_info_length != data->len - 2) |
- return SECFailure; |
- cached_info += 2; |
- while (remaining_cached_info_length >= 2) { |
- /* The server supports only those CachedInformationType types that are |
- * identified by a present CachedObject */ |
- unsigned char cached_object_type; |
- unsigned int cached_object_length; |
- unsigned char cached_object_digest[8]; |
- cached_object_type = *cached_info++; |
- cached_object_length = *cached_info++; |
- remaining_cached_info_length -= 2; |
- if (remaining_cached_info_length < cached_object_length) |
- return SECFailure; |
- if (cached_object_length != 0 && cached_object_length != 8) |
- return SECFailure; |
- remaining_cached_info_length -= cached_object_length; |
- if (cached_object_type == cached_info_certificate_chain) { |
- if (cached_object_length == 0) |
- has_correct_cert_chain = PR_TRUE; |
- else { /* Hashes must match */ |
- int i; |
- for (i = 0; i < 8; i++) |
- cached_object_digest[i] = *cached_info++; |
- if (!memcmp(cached_object_digest, ss->ssl3.certChainDigest, 8)) |
- has_correct_cert_chain = PR_TRUE; |
- } |
- } |
- } |
- |
- if (remaining_cached_info_length != 0) |
- return SECFailure; |
- |
- if (has_correct_cert_chain) { |
- ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
- return SECSuccess; |
- } |
- |
- return SECFailure; |
-} |
- |
/* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the |
* client side. See RFC 4366 section 3.6. */ |
PRInt32 |
@@ -1726,14 +1491,17 @@ |
SSL_GETPID(), ss->fd)); |
ssl3stats = SSL_GetStatistics(); |
SSL_AtomicIncrementLong(& ssl3stats->hch_sid_ticket_parse_failures ); |
- if (sid) { |
- ssl_FreeSID(sid); |
- sid = NULL; |
- } |
} |
rv = SECSuccess; |
loser: |
+ /* ss->sec.ci.sid == sid if it did NOT come here via goto statement |
+ * in that case do not free sid |
+ */ |
+ if (sid && (ss->sec.ci.sid != sid)) { |
+ ssl_FreeSID(sid); |
+ sid = NULL; |
+ } |
if (decrypted_state != NULL) { |
SECITEM_FreeItem(decrypted_state, PR_TRUE); |
decrypted_state = NULL; |