summaryrefslogtreecommitdiff
path: root/nss/lib/ssl/ssl3exthandle.c
diff options
context:
space:
mode:
Diffstat (limited to 'nss/lib/ssl/ssl3exthandle.c')
-rw-r--r--nss/lib/ssl/ssl3exthandle.c1126
1 files changed, 586 insertions, 540 deletions
diff --git a/nss/lib/ssl/ssl3exthandle.c b/nss/lib/ssl/ssl3exthandle.c
index 2a80e26..c9c99ab 100644
--- a/nss/lib/ssl/ssl3exthandle.c
+++ b/nss/lib/ssl/ssl3exthandle.c
@@ -16,22 +16,16 @@
#include "ssl3exthandle.h"
#include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */
-static unsigned char key_name[SESS_TICKET_KEY_NAME_LEN];
-static PK11SymKey *session_ticket_enc_key = NULL;
-static PK11SymKey *session_ticket_mac_key = NULL;
-
-static PRCallOnceType generate_session_keys_once;
-
-static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss,
- SECItem *data, EncryptedSessionTicket *enc_session_ticket);
static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf,
PRUint32 bytes);
-static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes);
+static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf,
+ PRUint32 bytes);
static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num,
PRInt32 lenSize);
-static SECStatus ssl3_GetSessionTicketKeys(sslSocket *ss,
- PK11SymKey **aes_key, PK11SymKey **mac_key);
-static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes);
+
+PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
+#define TLS_EX_SESS_TICKET_VERSION (0x0105)
+#define TLS_EX_SESS_TICKET_MAC_LENGTH 32
/*
* Write bytes. Using this function means the SECItem structure
@@ -76,83 +70,6 @@ ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize)
return rv;
}
-SECStatus
-ssl3_SessionTicketShutdown(void *appData, void *nssData)
-{
- if (session_ticket_enc_key) {
- PK11_FreeSymKey(session_ticket_enc_key);
- session_ticket_enc_key = NULL;
- }
- if (session_ticket_mac_key) {
- PK11_FreeSymKey(session_ticket_mac_key);
- session_ticket_mac_key = NULL;
- }
- PORT_Memset(&generate_session_keys_once, 0,
- sizeof(generate_session_keys_once));
- return SECSuccess;
-}
-
-static PRStatus
-ssl3_GenerateSessionTicketKeys(void *data)
-{
- SECStatus rv;
- sslSocket *ss = (sslSocket *)data;
- sslServerCertType certType = { ssl_auth_rsa_decrypt, NULL };
- const sslServerCert *sc;
- SECKEYPrivateKey *svrPrivKey;
- SECKEYPublicKey *svrPubKey;
-
- sc = ssl_FindServerCert(ss, &certType);
- if (!sc || !sc->serverKeyPair) {
- SSL_DBG(("%d: SSL[%d]: No ssl_auth_rsa_decrypt cert and key pair",
- SSL_GETPID(), ss->fd));
- goto loser;
- }
- svrPrivKey = sc->serverKeyPair->privKey;
- svrPubKey = sc->serverKeyPair->pubKey;
- if (svrPrivKey == NULL || svrPubKey == NULL) {
- SSL_DBG(("%d: SSL[%d]: Pub or priv key(s) is NULL.",
- SSL_GETPID(), ss->fd));
- goto loser;
- }
-
- /* Get a copy of the session keys from shared memory. */
- PORT_Memcpy(key_name, SESS_TICKET_KEY_NAME_PREFIX,
- sizeof(SESS_TICKET_KEY_NAME_PREFIX));
- if (!ssl_GetSessionTicketKeys(svrPrivKey, svrPubKey, ss->pkcs11PinArg,
- &key_name[SESS_TICKET_KEY_NAME_PREFIX_LEN],
- &session_ticket_enc_key, &session_ticket_mac_key))
- return PR_FAILURE;
-
- rv = NSS_RegisterShutdown(ssl3_SessionTicketShutdown, NULL);
- if (rv != SECSuccess)
- goto loser;
-
- return PR_SUCCESS;
-
-loser:
- ssl3_SessionTicketShutdown(NULL, NULL);
- return PR_FAILURE;
-}
-
-static SECStatus
-ssl3_GetSessionTicketKeys(sslSocket *ss, PK11SymKey **aes_key,
- PK11SymKey **mac_key)
-{
- if (PR_CallOnceWithArg(&generate_session_keys_once,
- ssl3_GenerateSessionTicketKeys, ss) !=
- PR_SUCCESS)
- return SECFailure;
-
- if (session_ticket_enc_key == NULL ||
- session_ticket_mac_key == NULL)
- return SECFailure;
-
- *aes_key = session_ticket_enc_key;
- *mac_key = session_ticket_mac_key;
- return SECSuccess;
-}
-
/* Format an SNI extension, using the name from the socket's URL,
* unless that name is a dotted decimal string.
* Used by client and server.
@@ -223,7 +140,8 @@ SECStatus
ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
{
SECItem *names = NULL;
- PRInt32 listLenBytes = 0;
+ PRUint32 listLenBytes = 0;
+ SECStatus rv;
if (!ss->sec.isServer) {
return SECSuccess; /* ignore extension */
@@ -236,8 +154,8 @@ ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint1
}
/* length of server_name_list */
- listLenBytes = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
- if (listLenBytes < 0) {
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &listLenBytes, 2, &data->data, &data->len);
+ if (rv != SECSuccess) {
goto loser; /* alert already sent */
}
if (listLenBytes == 0 || listLenBytes != data->len) {
@@ -247,12 +165,11 @@ ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint1
/* Read ServerNameList. */
while (data->len > 0) {
SECItem tmp;
- SECStatus rv;
- PRInt32 type;
+ PRUint32 type;
/* Read Name Type. */
- type = ssl3_ExtConsumeHandshakeNumber(ss, 1, &data->data, &data->len);
- if (type < 0) { /* i.e., SECFailure cast to PRint32 */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &type, 1, &data->data, &data->len);
+ if (rv != SECSuccess) {
/* alert sent in ConsumeHandshakeNumber */
goto loser;
}
@@ -372,11 +289,7 @@ ssl3_SendSessionTicketXtn(
if (session_ticket->ticket.data) {
if (xtnData->ticketTimestampVerified) {
extension_length += session_ticket->ticket.len;
- } else if (!append &&
- (session_ticket->ticket_lifetime_hint == 0 ||
- (session_ticket->ticket_lifetime_hint +
- session_ticket->received_timestamp >
- ssl_Time()))) {
+ } else if (!append && ssl_TicketTimeValid(session_ticket)) {
extension_length += session_ticket->ticket.len;
xtnData->ticketTimestampVerified = PR_TRUE;
}
@@ -418,31 +331,54 @@ loser:
}
static SECStatus
-ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data,
- EncryptedSessionTicket *enc_session_ticket)
+ssl3_ParseEncryptedSessionTicket(sslSocket *ss, const SECItem *data,
+ EncryptedSessionTicket *encryptedTicket)
{
- if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name,
+ SECItem copy = *data;
+
+ if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->key_name,
SESS_TICKET_KEY_NAME_LEN) !=
SECSuccess)
return SECFailure;
- if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv,
+ if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->iv,
AES_BLOCK_SIZE) !=
SECSuccess)
return SECFailure;
- if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state,
- 2, &data->data, &data->len) !=
+ if (ssl3_ConsumeHandshakeVariable(ss, &encryptedTicket->encrypted_state,
+ 2, &copy.data, &copy.len) !=
SECSuccess)
return SECFailure;
- if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac,
+ if (ssl3_ConsumeFromItem(&copy, &encryptedTicket->mac,
TLS_EX_SESS_TICKET_MAC_LENGTH) !=
SECSuccess)
return SECFailure;
- if (data->len != 0) /* Make sure that we have consumed all bytes. */
+ if (copy.len != 0) /* Make sure that we have consumed all bytes. */
return SECFailure;
return SECSuccess;
}
+PRBool
+ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag)
+{
+ const unsigned char *data = ss->opt.nextProtoNego.data;
+ unsigned int length = ss->opt.nextProtoNego.len;
+ unsigned int offset = 0;
+
+ if (!tag->len)
+ return PR_TRUE;
+
+ while (offset < length) {
+ unsigned int taglen = (unsigned int)data[offset];
+ if ((taglen == tag->len) &&
+ !PORT_Memcmp(data + offset + 1, tag->data, tag->len))
+ return PR_TRUE;
+ offset += 1 + taglen;
+ }
+
+ return PR_FALSE;
+}
+
/* handle an incoming Next Protocol Negotiation extension. */
SECStatus
ssl3_ServerHandleNextProtoNegoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
@@ -542,7 +478,7 @@ ssl3_SelectAppProtocol(const sslSocket *ss, TLSExtensionData *xtnData,
SECStatus
ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
{
- int count;
+ PRUint32 count;
SECStatus rv;
/* We expressly don't want to allow ALPN on renegotiation,
@@ -556,8 +492,8 @@ ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
/* Unlike NPN, ALPN has extra redundant length information so that
* the extension is the same in both ClientHello and ServerHello. */
- count = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
- if (count != data->len) {
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &count, 2, &data->data, &data->len);
+ if (rv != SECSuccess || count != data->len) {
ssl3_ExtDecodeError(ss);
return SECFailure;
}
@@ -621,7 +557,7 @@ SECStatus
ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
{
SECStatus rv;
- PRInt32 list_len;
+ PRUint32 list_len;
SECItem protocol_name;
if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
@@ -639,9 +575,10 @@ ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
return SECFailure;
}
- list_len = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &list_len, 2, &data->data,
+ &data->len);
/* The list has to be the entire extension. */
- if (list_len != data->len) {
+ if (rv != SECSuccess || list_len != data->len) {
ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
return SECFailure;
@@ -656,6 +593,12 @@ ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRU
return SECFailure;
}
+ if (!ssl_AlpnTagAllowed(ss, &protocol_name)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+ return SECFailure;
+ }
+
SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
xtnData->nextProtoState = SSL_NEXT_PROTO_SELECTED;
xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
@@ -963,23 +906,20 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
PRUint32 cert_length = 0;
PRUint8 length_buf[4];
PRUint32 now;
+ unsigned char key_name[SESS_TICKET_KEY_NAME_LEN];
PK11SymKey *aes_key = NULL;
PK11SymKey *mac_key = NULL;
- CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
PK11Context *aes_ctx;
- CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
PK11Context *hmac_ctx = NULL;
unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
unsigned int computed_mac_length;
unsigned char iv[AES_BLOCK_SIZE];
SECItem ivItem;
SECItem *srvName = NULL;
- PRUint32 srvNameLen = 0;
CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value,
* must be >= 0 */
ssl3CipherSpec *spec;
- const sslServerCertType *certType;
- SECItem alpnSelection = { siBuffer, NULL, 0 };
+ SECItem *alpnSelection = NULL;
SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
SSL_GETPID(), ss->fd));
@@ -988,7 +928,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
- cert_length = 3 + ss->sec.ci.sid->peerCert->derCert.len;
+ cert_length = 2 + ss->sec.ci.sid->peerCert->derCert.len;
}
/* Get IV and encryption keys */
@@ -998,7 +938,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
if (rv != SECSuccess)
goto loser;
- rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key);
+ rv = ssl_GetSessionTicketKeys(ss, key_name, &aes_key, &mac_key);
if (rv != SECSuccess)
goto loser;
@@ -1017,8 +957,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
sslSessionID sid;
PORT_Memset(&sid, 0, sizeof(sslSessionID));
- rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec,
- ss->ssl3.hs.kea_def->authKeyType);
+ rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec);
if (rv == SECSuccess) {
if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
goto loser;
@@ -1035,17 +974,14 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
}
/* Prep to send negotiated name */
srvName = &ss->sec.ci.sid->u.ssl3.srvName;
- if (srvName->data && srvName->len) {
- srvNameLen = 2 + srvName->len; /* len bytes + name len */
- }
- if (ss->xtnData.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
- ss->xtnData.nextProto.data) {
- alpnSelection = ss->xtnData.nextProto;
- }
+ PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED ||
+ ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED ||
+ ss->xtnData.nextProto.len == 0);
+ alpnSelection = &ss->xtnData.nextProto;
ciphertext_length =
- sizeof(PRUint16) /* ticket_version */
+ sizeof(PRUint16) /* ticket version */
+ sizeof(SSL3ProtocolVersion) /* ssl_version */
+ sizeof(ssl3CipherSuite) /* ciphersuite */
+ 1 /* compression */
@@ -1057,15 +993,19 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
+ ms_item.len /* master_secret */
+ 1 /* client_auth_type */
+ cert_length /* cert */
- + 1 /* server name type */
- + srvNameLen /* name len + length field */
+ + 2 + srvName->len /* name len + length field */
+ 1 /* extendedMasterSecretUsed */
+ sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */
+ sizeof(ticket->flags) /* ticket flags */
- + 1 + alpnSelection.len; /* npn value + length field. */
+ + 1 + alpnSelection->len /* alpn value + length field */
+ + 4; /* maxEarlyData */
+#ifdef UNSAFE_FUZZER_MODE
+ padding_length = 0;
+#else
padding_length = AES_BLOCK_SIZE -
(ciphertext_length %
AES_BLOCK_SIZE);
+#endif
ciphertext_length += padding_length;
if (SECITEM_AllocItem(NULL, &plaintext_item, ciphertext_length) == NULL)
@@ -1073,7 +1013,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
plaintext = plaintext_item;
- /* ticket_version */
+ /* ticket version */
rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION,
sizeof(PRUint16));
if (rv != SECSuccess)
@@ -1111,22 +1051,15 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
goto loser;
/* certificate type */
- certType = &ss->sec.serverCert->certType;
- PORT_Assert(certType->authType == ss->sec.authType);
- switch (ss->sec.authType) {
- case ssl_auth_ecdsa:
- case ssl_auth_ecdh_rsa:
- case ssl_auth_ecdh_ecdsa:
- PORT_Assert(certType->namedCurve);
- PORT_Assert(certType->namedCurve->keaType == ssl_kea_ecdh);
- /* EC curves only use the second of the two bytes. */
- PORT_Assert(certType->namedCurve->name < 256);
- rv = ssl3_AppendNumberToItem(&plaintext,
- certType->namedCurve->name, 1);
- break;
- default:
- rv = ssl3_AppendNumberToItem(&plaintext, 0, 1);
- break;
+ PORT_Assert(SSL_CERT_IS(ss->sec.serverCert, ss->sec.authType));
+ if (SSL_CERT_IS_EC(ss->sec.serverCert)) {
+ const sslServerCert *cert = ss->sec.serverCert;
+ PORT_Assert(cert->namedCurve);
+ /* EC curves only use the second of the two bytes. */
+ PORT_Assert(cert->namedCurve->name < 256);
+ rv = ssl3_AppendNumberToItem(&plaintext, cert->namedCurve->name, 1);
+ } else {
+ rv = ssl3_AppendNumberToItem(&plaintext, 0, 1);
}
if (rv != SECSuccess)
goto loser;
@@ -1145,13 +1078,13 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
if (rv != SECSuccess)
goto loser;
- /* client_identity */
+ /* client identity */
if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
if (rv != SECSuccess)
goto loser;
rv = ssl3_AppendNumberToItem(&plaintext,
- ss->sec.ci.sid->peerCert->derCert.len, 3);
+ ss->sec.ci.sid->peerCert->derCert.len, 2);
if (rv != SECSuccess)
goto loser;
rv = ssl3_AppendToItem(&plaintext,
@@ -1172,23 +1105,14 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
if (rv != SECSuccess)
goto loser;
- if (srvNameLen) {
- /* Name Type (sni_host_name) */
- rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1);
- if (rv != SECSuccess)
- goto loser;
- /* HostName (length and value) */
- rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
- if (rv != SECSuccess)
- goto loser;
+ /* HostName (length and value) */
+ rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ if (srvName->len) {
rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len);
if (rv != SECSuccess)
goto loser;
- } else {
- /* No Name */
- rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME, 1);
- if (rv != SECSuccess)
- goto loser;
}
/* extendedMasterSecretUsed */
@@ -1203,17 +1127,22 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
if (rv != SECSuccess)
goto loser;
- /* NPN value. */
- PORT_Assert(alpnSelection.len < 256);
- rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection.len, 1);
+ /* ALPN value. */
+ PORT_Assert(alpnSelection->len < 256);
+ rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection->len, 1);
if (rv != SECSuccess)
goto loser;
- if (alpnSelection.len) {
- rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len);
+ if (alpnSelection->len) {
+ rv = ssl3_AppendToItem(&plaintext, alpnSelection->data,
+ alpnSelection->len);
if (rv != SECSuccess)
goto loser;
}
+ rv = ssl3_AppendNumberToItem(&plaintext, ssl_max_early_data_size, 4);
+ if (rv != SECSuccess)
+ goto loser;
+
PORT_Assert(plaintext.len == padding_length);
for (i = 0; i < padding_length; i++)
plaintext.data[i] = (unsigned char)padding_length;
@@ -1225,7 +1154,11 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
/* Generate encrypted portion of ticket. */
PORT_Assert(aes_key);
- aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, aes_key, &ivItem);
+#ifdef UNSAFE_FUZZER_MODE
+ ciphertext.len = plaintext_item.len;
+ PORT_Memcpy(ciphertext.data, plaintext_item.data, plaintext_item.len);
+#else
+ aes_ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_ENCRYPT, aes_key, &ivItem);
if (!aes_ctx)
goto loser;
@@ -1236,14 +1169,15 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
PK11_DestroyContext(aes_ctx, PR_TRUE);
if (rv != SECSuccess)
goto loser;
+#endif
/* Convert ciphertext length to network order. */
- length_buf[0] = (ciphertext.len >> 8) & 0xff;
- length_buf[1] = (ciphertext.len) & 0xff;
+ (void)ssl_EncodeUintX(ciphertext.len, 2, length_buf);
/* Compute MAC. */
PORT_Assert(mac_key);
- hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam);
+ hmac_ctx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, mac_key,
+ &macParam);
if (!hmac_ctx)
goto loser;
@@ -1338,434 +1272,525 @@ ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData
return SECSuccess;
}
-/* Generic ticket processing code, common to TLS 1.0-1.2 and
- * TLS 1.3. */
-SECStatus
-ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
+static SECStatus
+ssl_DecryptSessionTicket(sslSocket *ss, const SECItem *rawTicket,
+ const EncryptedSessionTicket *encryptedTicket,
+ SECItem *decryptedTicket)
{
SECStatus rv;
- SECItem *decrypted_state = NULL;
- SessionTicket *parsed_session_ticket = NULL;
- sslSessionID *sid = NULL;
- SSL3Statistics *ssl3stats;
- PRUint32 i;
- SECItem extension_data;
- EncryptedSessionTicket enc_session_ticket;
- unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH];
- unsigned int computed_mac_length;
- PK11SymKey *aes_key = NULL;
- PK11SymKey *mac_key = NULL;
- PK11Context *hmac_ctx;
- CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
- PK11Context *aes_ctx;
- CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
- unsigned char *padding;
- PRUint32 padding_length;
- unsigned char *buffer;
- unsigned int buffer_len;
- PRInt32 temp;
- SECItem cert_item;
- PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
+ unsigned char keyName[SESS_TICKET_KEY_NAME_LEN];
+
+ PK11SymKey *macKey = NULL;
+ PK11Context *hmacCtx;
+ unsigned char computedMac[TLS_EX_SESS_TICKET_MAC_LENGTH];
+ unsigned int computedMacLength;
SECItem macParam = { siBuffer, NULL, 0 };
- SECItem alpn_item;
- SECItem ivItem;
- /* Turn off stateless session resumption if the client sends a
- * SessionTicket extension, even if the extension turns out to be
- * malformed (ss->sec.ci.sid is non-NULL when doing session
- * renegotiation.)
- */
- if (ss->sec.ci.sid != NULL) {
- ss->sec.uncache(ss->sec.ci.sid);
- ssl_FreeSID(ss->sec.ci.sid);
- ss->sec.ci.sid = NULL;
- }
+ PK11SymKey *aesKey = NULL;
+#ifndef UNSAFE_FUZZER_MODE
+ PK11Context *aesCtx;
+ SECItem ivItem;
- extension_data.data = data->data; /* Keep a copy for future use. */
- extension_data.len = data->len;
+ unsigned int i;
+ SSL3Opaque *padding;
+ SSL3Opaque paddingLength;
+#endif
- if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) !=
- SECSuccess) {
- return SECSuccess; /* Pretend it isn't there */
+ PORT_Assert(!decryptedTicket->data);
+ PORT_Assert(!decryptedTicket->len);
+ if (rawTicket->len < TLS_EX_SESS_TICKET_MAC_LENGTH) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
}
/* Get session ticket keys. */
- rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key);
+ rv = ssl_GetSessionTicketKeys(ss, keyName, &aesKey, &macKey);
if (rv != SECSuccess) {
SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.",
SSL_GETPID(), ss->fd));
- goto loser;
+ return SECFailure; /* code already set */
}
- /* If the ticket sent by the client was generated under a key different
- * from the one we have, bypass ticket processing.
+ /* If the ticket sent by the client was generated under a key different from
+ * the one we have, bypass ticket processing. This reports success, meaning
+ * that the handshake completes, but doesn't resume.
*/
- if (PORT_Memcmp(enc_session_ticket.key_name, key_name,
+ if (PORT_Memcmp(encryptedTicket->key_name, keyName,
SESS_TICKET_KEY_NAME_LEN) != 0) {
+#ifndef UNSAFE_FUZZER_MODE
SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.",
SSL_GETPID(), ss->fd));
- goto no_ticket;
+ return SECSuccess;
+#endif
}
- /* Verify the MAC on the ticket. MAC verification may also
- * fail if the MAC key has been recently refreshed.
- */
- PORT_Assert(mac_key);
- hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam);
- if (!hmac_ctx) {
+ /* Verify the MAC on the ticket. */
+ PORT_Assert(macKey);
+ hmacCtx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, macKey,
+ &macParam);
+ if (!hmacCtx) {
SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.",
SSL_GETPID(), ss->fd, PORT_GetError()));
- goto no_ticket;
- } else {
- SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
- SSL_GETPID(), ss->fd));
- }
- rv = PK11_DigestBegin(hmac_ctx);
- if (rv != SECSuccess) {
- PK11_DestroyContext(hmac_ctx, PR_TRUE);
- goto no_ticket;
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
- rv = PK11_DigestOp(hmac_ctx, extension_data.data,
- extension_data.len -
- TLS_EX_SESS_TICKET_MAC_LENGTH);
+
+ SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.",
+ SSL_GETPID(), ss->fd));
+ do {
+ rv = PK11_DigestBegin(hmacCtx);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_DigestOp(hmacCtx, rawTicket->data,
+ rawTicket->len - TLS_EX_SESS_TICKET_MAC_LENGTH);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_DigestFinal(hmacCtx, computedMac, &computedMacLength,
+ sizeof(computedMac));
+ } while (0);
+ PK11_DestroyContext(hmacCtx, PR_TRUE);
if (rv != SECSuccess) {
- PK11_DestroyContext(hmac_ctx, PR_TRUE);
- goto no_ticket;
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
- rv = PK11_DigestFinal(hmac_ctx, computed_mac,
- &computed_mac_length, sizeof(computed_mac));
- PK11_DestroyContext(hmac_ctx, PR_TRUE);
- if (rv != SECSuccess)
- goto no_ticket;
- if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac,
- computed_mac_length) !=
- 0) {
+ if (NSS_SecureMemcmp(computedMac, encryptedTicket->mac,
+ computedMacLength) != 0) {
+#ifndef UNSAFE_FUZZER_MODE
SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.",
SSL_GETPID(), ss->fd));
- goto no_ticket;
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+#endif
}
- /* We ignore key_name for now.
- * This is ok as MAC verification succeeded.
- */
-
/* Decrypt the ticket. */
/* Plaintext is shorter than the ciphertext due to padding. */
- decrypted_state = SECITEM_AllocItem(NULL, NULL,
- enc_session_ticket.encrypted_state.len);
-
- PORT_Assert(aes_key);
- ivItem.data = enc_session_ticket.iv;
+ if (!SECITEM_AllocItem(NULL, decryptedTicket,
+ encryptedTicket->encrypted_state.len)) {
+ return SECFailure; /* code already set */
+ }
+
+ PORT_Assert(aesKey);
+#ifdef UNSAFE_FUZZER_MODE
+ decryptedTicket->len = encryptedTicket->encrypted_state.len;
+ PORT_Memcpy(decryptedTicket->data,
+ encryptedTicket->encrypted_state.data,
+ encryptedTicket->encrypted_state.len);
+#else
+ ivItem.data = encryptedTicket->iv;
ivItem.len = AES_BLOCK_SIZE;
- aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT,
- aes_key, &ivItem);
- if (!aes_ctx) {
+ aesCtx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_DECRYPT, aesKey,
+ &ivItem);
+ if (!aesCtx) {
SSL_DBG(("%d: SSL[%d]: Unable to create AES context.",
SSL_GETPID(), ss->fd));
- goto no_ticket;
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
- rv = PK11_CipherOp(aes_ctx, decrypted_state->data,
- (int *)&decrypted_state->len, decrypted_state->len,
- enc_session_ticket.encrypted_state.data,
- enc_session_ticket.encrypted_state.len);
- PK11_Finalize(aes_ctx);
- PK11_DestroyContext(aes_ctx, PR_TRUE);
- if (rv != SECSuccess)
- goto no_ticket;
+ do {
+ rv = PK11_CipherOp(aesCtx, decryptedTicket->data,
+ (int *)&decryptedTicket->len, decryptedTicket->len,
+ encryptedTicket->encrypted_state.data,
+ encryptedTicket->encrypted_state.len);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_Finalize(aesCtx);
+ if (rv != SECSuccess) {
+ break;
+ }
+ } while (0);
+ PK11_DestroyContext(aesCtx, PR_TRUE);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
/* Check padding. */
- padding_length =
- (PRUint32)decrypted_state->data[decrypted_state->len - 1];
- if (padding_length == 0 || padding_length > AES_BLOCK_SIZE)
- goto no_ticket;
+ paddingLength = decryptedTicket->data[decryptedTicket->len - 1];
+ if (paddingLength == 0 || paddingLength > AES_BLOCK_SIZE) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
- padding = &decrypted_state->data[decrypted_state->len - padding_length];
- for (i = 0; i < padding_length; i++, padding++) {
- if (padding_length != (PRUint32)*padding)
- goto no_ticket;
+ padding = &decryptedTicket->data[decryptedTicket->len - paddingLength];
+ for (i = 0; i < paddingLength; i++, padding++) {
+ if (paddingLength != *padding) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
}
+ decryptedTicket->len -= paddingLength;
+#endif /* UNSAFE_FUZZER_MODE */
- /* Deserialize session state. */
- buffer = decrypted_state->data;
- buffer_len = decrypted_state->len;
+ return SECSuccess;
+}
- parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
- if (parsed_session_ticket == NULL) {
- rv = SECFailure;
- goto loser;
+static SECStatus
+ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
+ SessionTicket *parsedTicket)
+{
+ PRUint32 temp;
+ SECStatus rv;
+
+ SSL3Opaque *buffer = decryptedTicket->data;
+ unsigned int len = decryptedTicket->len;
+
+ PORT_Memset(parsedTicket, 0, sizeof(*parsedTicket));
+ parsedTicket->valid = PR_FALSE;
+
+ /* If the decrypted ticket is empty, then report success, but leave the
+ * ticket marked as invalid. */
+ if (decryptedTicket->len == 0) {
+ return SECSuccess;
}
- /* Read ticket_version and reject if the version is wrong */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp != TLS_EX_SESS_TICKET_VERSION)
- goto no_ticket;
+ /* Read ticket version. */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
- parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
+ /* Skip the ticket if the version is wrong. This won't result in a
+ * handshake failure, just a failure to resume. */
+ if (temp != TLS_EX_SESS_TICKET_VERSION) {
+ return SECSuccess;
+ }
/* Read SSLVersion. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->ssl_version = (SSL3ProtocolVersion)temp;
+ if (!ssl3_VersionIsSupported(ss->protocolVariant,
+ parsedTicket->ssl_version)) {
+ /* This socket doesn't support the version from the ticket. */
+ return SECSuccess;
+ }
/* Read cipher_suite. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->cipher_suite = (ssl3CipherSuite)temp;
/* Read compression_method. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->compression_method = (SSLCompressionMethod)temp;
/* Read cipher spec parameters. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->authType = (SSLAuthType)temp;
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->authKeyBits = (PRUint32)temp;
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->keaType = (SSLKEAType)temp;
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->keaKeyBits = (PRUint32)temp;
-
- /* Read certificate slot */
- parsed_session_ticket->certType.authType = parsed_session_ticket->authType;
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- switch (parsed_session_ticket->authType) {
- case ssl_auth_ecdsa:
- case ssl_auth_ecdh_rsa:
- case ssl_auth_ecdh_ecdsa: {
- const sslNamedGroupDef *group =
- ssl_LookupNamedGroup((SSLNamedGroup)temp);
- if (!group || group->keaType != ssl_kea_ecdh) {
- goto no_ticket;
- }
- parsed_session_ticket->certType.namedCurve = group;
- } break;
- default:
- break;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->authType = (SSLAuthType)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
+ parsedTicket->authKeyBits = temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->keaType = (SSLKEAType)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->keaKeyBits = temp;
- /* Read wrapped master_secret. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
-
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
-
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->ms_length = (PRUint16)temp;
- if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
- parsed_session_ticket->ms_length >
- sizeof(parsed_session_ticket->master_secret))
- goto no_ticket;
-
- /* Allow for the wrapped master secret to be longer. */
- if (buffer_len < parsed_session_ticket->ms_length)
- goto no_ticket;
- PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
- parsed_session_ticket->ms_length);
- buffer += parsed_session_ticket->ms_length;
- buffer_len -= parsed_session_ticket->ms_length;
-
- /* Read client_identity */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->client_identity.client_auth_type =
- (ClientAuthenticationType)temp;
- switch (parsed_session_ticket->client_identity.client_auth_type) {
+ /* Read the optional named curve. */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (parsedTicket->authType == ssl_auth_ecdsa ||
+ parsedTicket->authType == ssl_auth_ecdh_rsa ||
+ parsedTicket->authType == ssl_auth_ecdh_ecdsa) {
+ const sslNamedGroupDef *group =
+ ssl_LookupNamedGroup((SSLNamedGroup)temp);
+ if (!group || group->keaType != ssl_kea_ecdh) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->namedCurve = group;
+ }
+
+ /* Read the master secret (and how it is wrapped). */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
+ parsedTicket->ms_is_wrapped = (PRBool)temp;
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->msWrapMech = (CK_MECHANISM_TYPE)temp;
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (temp == 0 || temp > sizeof(parsedTicket->master_secret)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->ms_length = (PRUint16)temp;
+
+ /* Read the master secret. */
+ rv = ssl3_ExtConsumeHandshake(ss, parsedTicket->master_secret,
+ parsedTicket->ms_length, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ /* Read client identity */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->client_auth_type = (ClientAuthenticationType)temp;
+ switch (parsedTicket->client_auth_type) {
case CLIENT_AUTH_ANONYMOUS:
break;
case CLIENT_AUTH_CERTIFICATE:
- rv = ssl3_ExtConsumeHandshakeVariable(ss, &cert_item, 3,
- &buffer, &buffer_len);
- if (rv != SECSuccess)
- goto no_ticket;
- rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert,
- &cert_item);
- if (rv != SECSuccess)
- goto no_ticket;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->peer_cert, 2,
+ &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
break;
default:
- goto no_ticket;
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
/* Read timestamp. */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
- parsed_session_ticket->timestamp = (PRUint32)temp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->timestamp = temp;
/* Read server name */
- nameType =
- ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (nameType != TLS_STE_NO_SERVER_NAME) {
- SECItem name_item;
- rv = ssl3_ExtConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
- &buffer_len);
- if (rv != SECSuccess)
- goto no_ticket;
- rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
- &name_item);
- if (rv != SECSuccess)
- goto no_ticket;
- parsed_session_ticket->srvName.type = nameType;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->srvName, 2,
+ &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
/* Read extendedMasterSecretUsed */
- temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
- if (temp < 0)
- goto no_ticket;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
- parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
+ parsedTicket->extendedMasterSecretUsed = (PRBool)temp;
- rv = ssl3_ExtConsumeHandshake(ss, &parsed_session_ticket->flags, 4,
- &buffer, &buffer_len);
- if (rv != SECSuccess)
- goto no_ticket;
- parsed_session_ticket->flags = PR_ntohl(parsed_session_ticket->flags);
+ rv = ssl3_ExtConsumeHandshake(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->flags = PR_ntohl(temp);
- rv = ssl3_ExtConsumeHandshakeVariable(ss, &alpn_item, 1, &buffer, &buffer_len);
- if (rv != SECSuccess)
- goto no_ticket;
- if (alpn_item.len != 0) {
- rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection,
- &alpn_item);
- if (rv != SECSuccess)
- goto no_ticket;
- if (alpn_item.len >= 256)
- goto no_ticket;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->alpnSelection, 1,
+ &buffer, &len);
+ PORT_Assert(parsedTicket->alpnSelection.len < 256);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
}
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ parsedTicket->maxEarlyData = temp;
+
+#ifndef UNSAFE_FUZZER_MODE
/* Done parsing. Check that all bytes have been consumed. */
- if (buffer_len != padding_length)
- goto no_ticket;
+ if (len != 0) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+#endif
- /* Use the ticket if it has not expired, otherwise free the allocated
- * memory since the ticket is of no use.
- */
- if (parsed_session_ticket->timestamp != 0 &&
- parsed_session_ticket->timestamp +
- TLS_EX_SESS_TICKET_LIFETIME_HINT >
- ssl_Time()) {
-
- sid = ssl3_NewSessionID(ss, PR_TRUE);
- if (sid == NULL) {
- rv = SECFailure;
+ parsedTicket->valid = PR_TRUE;
+ return SECSuccess;
+}
+
+static SECStatus
+ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket,
+ SessionTicket *parsedTicket, sslSessionID **out)
+{
+ sslSessionID *sid;
+ SECStatus rv;
+
+ sid = ssl3_NewSessionID(ss, PR_TRUE);
+ if (sid == NULL) {
+ return SECFailure;
+ }
+
+ /* Copy over parameters. */
+ sid->version = parsedTicket->ssl_version;
+ sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite;
+ sid->u.ssl3.compression = parsedTicket->compression_method;
+ sid->authType = parsedTicket->authType;
+ sid->authKeyBits = parsedTicket->authKeyBits;
+ sid->keaType = parsedTicket->keaType;
+ sid->keaKeyBits = parsedTicket->keaKeyBits;
+ sid->namedCurve = parsedTicket->namedCurve;
+
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
+ rawTicket);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ sid->u.ssl3.locked.sessionTicket.flags = parsedTicket->flags;
+ sid->u.ssl3.locked.sessionTicket.max_early_data_size =
+ parsedTicket->maxEarlyData;
+
+ if (parsedTicket->ms_length >
+ sizeof(sid->u.ssl3.keys.wrapped_master_secret)) {
+ goto loser;
+ }
+ PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
+ parsedTicket->master_secret, parsedTicket->ms_length);
+ sid->u.ssl3.keys.wrapped_master_secret_len = parsedTicket->ms_length;
+ sid->u.ssl3.masterWrapMech = parsedTicket->msWrapMech;
+ sid->u.ssl3.keys.msIsWrapped = parsedTicket->ms_is_wrapped;
+ sid->u.ssl3.masterValid = PR_TRUE;
+ sid->u.ssl3.keys.resumable = PR_TRUE;
+ sid->u.ssl3.keys.extendedMasterSecretUsed = parsedTicket->extendedMasterSecretUsed;
+
+ /* Copy over client cert from session ticket if there is one. */
+ if (parsedTicket->peer_cert.data != NULL) {
+ PORT_Assert(!sid->peerCert);
+ sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
+ &parsedTicket->peer_cert,
+ NULL, PR_FALSE, PR_TRUE);
+ if (!sid->peerCert) {
goto loser;
}
+ }
- /* Copy over parameters. */
- sid->version = parsed_session_ticket->ssl_version;
- sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite;
- sid->u.ssl3.compression = parsed_session_ticket->compression_method;
- sid->authType = parsed_session_ticket->authType;
- sid->authKeyBits = parsed_session_ticket->authKeyBits;
- sid->keaType = parsed_session_ticket->keaType;
- sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
- memcpy(&sid->certType, &parsed_session_ticket->certType,
- sizeof(sslServerCertType));
-
- if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
- &extension_data) != SECSuccess)
- goto no_ticket;
- sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags;
-
- if (parsed_session_ticket->ms_length >
- sizeof(sid->u.ssl3.keys.wrapped_master_secret))
- goto no_ticket;
- PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
- parsed_session_ticket->master_secret,
- parsed_session_ticket->ms_length);
- sid->u.ssl3.keys.wrapped_master_secret_len =
- parsed_session_ticket->ms_length;
- sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech;
- sid->u.ssl3.keys.msIsWrapped =
- parsed_session_ticket->ms_is_wrapped;
- sid->u.ssl3.masterValid = PR_TRUE;
- sid->u.ssl3.keys.resumable = PR_TRUE;
- sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed;
-
- /* Copy over client cert from session ticket if there is one. */
- if (parsed_session_ticket->peer_cert.data != NULL) {
- if (sid->peerCert != NULL)
- CERT_DestroyCertificate(sid->peerCert);
- sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
- &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE);
- if (sid->peerCert == NULL) {
- rv = SECFailure;
- goto loser;
- }
- }
- if (parsed_session_ticket->srvName.data != NULL) {
- if (sid->u.ssl3.srvName.data) {
- SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
- }
- sid->u.ssl3.srvName = parsed_session_ticket->srvName;
+ /* Transfer ownership of the remaining items. */
+ if (parsedTicket->srvName.data != NULL) {
+ SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.srvName,
+ &parsedTicket->srvName);
+ if (rv != SECSuccess) {
+ goto loser;
}
- if (parsed_session_ticket->alpnSelection.data != NULL) {
- sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection;
- /* So we don't free below. */
- parsed_session_ticket->alpnSelection.data = NULL;
+ }
+ if (parsedTicket->alpnSelection.data != NULL) {
+ rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.alpnSelection,
+ &parsedTicket->alpnSelection);
+ if (rv != SECSuccess) {
+ goto loser;
}
- ss->statelessResume = PR_TRUE;
- ss->sec.ci.sid = sid;
}
- if (0) {
- no_ticket:
+ *out = sid;
+ return SECSuccess;
+
+loser:
+ ssl_FreeSID(sid);
+ return SECFailure;
+}
+
+/* Generic ticket processing code, common to all TLS versions. */
+SECStatus
+ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data)
+{
+ EncryptedSessionTicket encryptedTicket;
+ SECItem decryptedTicket = { siBuffer, NULL, 0 };
+ SessionTicket parsedTicket;
+ SECStatus rv;
+
+ if (ss->sec.ci.sid != NULL) {
+ ss->sec.uncache(ss->sec.ci.sid);
+ ssl_FreeSID(ss->sec.ci.sid);
+ ss->sec.ci.sid = NULL;
+ }
+
+ rv = ssl3_ParseEncryptedSessionTicket(ss, data, &encryptedTicket);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ rv = ssl_DecryptSessionTicket(ss, data, &encryptedTicket,
+ &decryptedTicket);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket);
+ if (rv != SECSuccess) {
+ SSL3Statistics *ssl3stats;
+
SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
SSL_GETPID(), ss->fd));
ssl3stats = SSL_GetStatistics();
SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures);
+ goto loser; /* code already set */
}
- 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;
- }
+ /* Use the ticket if it is valid and unexpired. */
+ if (parsedTicket.valid &&
+ parsedTicket.timestamp + ssl_ticket_lifetime > ssl_Time()) {
+ sslSessionID *sid;
- if (parsed_session_ticket != NULL) {
- if (parsed_session_ticket->peer_cert.data) {
- SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE);
- }
- if (parsed_session_ticket->alpnSelection.data) {
- SECITEM_FreeItem(&parsed_session_ticket->alpnSelection, PR_FALSE);
+ rv = ssl_CreateSIDFromTicket(ss, data, &parsedTicket, &sid);
+ if (rv != SECSuccess) {
+ goto loser; /* code already set */
}
- PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket));
+ ss->statelessResume = PR_TRUE;
+ ss->sec.ci.sid = sid;
}
- return rv;
+ SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
+ PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
+ return SECSuccess;
+
+loser:
+ SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
+ PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
+ return SECFailure;
}
SECStatus
@@ -2145,7 +2170,8 @@ ssl3_ServerHandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUi
&xtnData->clientSigSchemes,
&xtnData->numClientSigScheme,
&data->data, &data->len);
- if (rv != SECSuccess) {
+ if (rv != SECSuccess || xtnData->numClientSigScheme == 0) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
return SECFailure;
}
@@ -2216,55 +2242,73 @@ ssl3_ClientSendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRBool
/* Takes the size of the ClientHello, less the record header, and determines how
* much padding is required. */
-unsigned int
-ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength)
+void
+ssl3_CalculatePaddingExtLen(sslSocket *ss,
+ unsigned int clientHelloLength)
{
unsigned int recordLength = 1 /* handshake message type */ +
3 /* handshake message length */ +
clientHelloLength;
- unsigned int extensionLength;
+ unsigned int extensionLen;
+
+ /* Don't pad for DTLS, for SSLv3, or for renegotiation. */
+ if (IS_DTLS(ss) ||
+ ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_0 ||
+ ss->firstHsDone) {
+ return;
+ }
+ /* A padding extension may be included to ensure that the record containing
+ * the ClientHello doesn't have a length between 256 and 511 bytes
+ * (inclusive). Initial ClientHello records with such lengths trigger bugs
+ * in F5 devices. */
if (recordLength < 256 || recordLength >= 512) {
- return 0;
+ return;
}
- extensionLength = 512 - recordLength;
+ extensionLen = 512 - recordLength;
/* Extensions take at least four bytes to encode. Always include at least
- * one byte of data if including the extension. Some servers (e.g.
- * WebSphere Application Server 7.0 and Tomcat) will time out or terminate
- * the connection if the last extension in the client hello is empty. */
- if (extensionLength < 4 + 1) {
- extensionLength = 4 + 1;
+ * one byte of data if we are padding. Some servers will time out or
+ * terminate the connection if the last ClientHello extension is empty. */
+ if (extensionLen < 4 + 1) {
+ extensionLen = 4 + 1;
}
- return extensionLength;
+ ss->xtnData.paddingLen = extensionLen - 4;
}
-/* ssl3_AppendPaddingExtension possibly adds an extension which ensures that a
+/* ssl3_SendPaddingExtension possibly adds an extension which ensures that a
* ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures
* that we don't trigger bugs in F5 products. */
PRInt32
-ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen,
- PRUint32 maxBytes)
+ssl3_ClientSendPaddingExtension(const sslSocket *ss, TLSExtensionData *xtnData,
+ PRBool append, PRUint32 maxBytes)
{
- unsigned int paddingLen = extensionLen - 4;
- static unsigned char padding[252];
+ static unsigned char padding[252] = { 0 };
+ unsigned int extensionLen;
+ SECStatus rv;
- if (extensionLen == 0) {
+ /* On the length-calculation pass, report zero total length. The record
+ * will be larger on the second pass if needed. */
+ if (!append || !xtnData->paddingLen) {
return 0;
}
+ extensionLen = xtnData->paddingLen + 4;
if (extensionLen > maxBytes ||
- !paddingLen ||
- paddingLen > sizeof(padding)) {
+ xtnData->paddingLen > sizeof(padding)) {
PORT_Assert(0);
return -1;
}
- if (SECSuccess != ssl3_ExtAppendHandshakeNumber(ss, ssl_padding_xtn, 2))
+ rv = ssl3_ExtAppendHandshakeNumber(ss, ssl_padding_xtn, 2);
+ if (rv != SECSuccess) {
return -1;
- if (SECSuccess != ssl3_ExtAppendHandshakeVariable(ss, padding, paddingLen, 2))
+ }
+ rv = ssl3_ExtAppendHandshakeVariable(ss, padding, xtnData->paddingLen, 2);
+ if (rv != SECSuccess) {
return -1;
+ }
return extensionLen;
}
@@ -2484,7 +2528,8 @@ ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnDa
static SECStatus
ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
{
- PRInt32 list_len;
+ SECStatus rv;
+ PRUint32 list_len;
unsigned int i;
const sslNamedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 };
PORT_Assert(SSL_NAMED_GROUP_COUNT == PR_ARRAY_SIZE(enabled));
@@ -2495,8 +2540,8 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
}
/* get the length of elliptic_curve_list */
- list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
- if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) {
+ rv = ssl3_ConsumeHandshakeNumber(ss, &list_len, 2, &data->data, &data->len);
+ if (rv != SECSuccess || data->len != list_len || (data->len % 2) != 0) {
(void)ssl3_DecodeError(ss);
return SECFailure;
}
@@ -2510,9 +2555,10 @@ ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
/* Read groups from data and enable if in |enabled| */
while (data->len) {
const sslNamedGroupDef *group;
- PRInt32 curve_name =
- ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
- if (curve_name < 0) {
+ PRUint32 curve_name;
+ rv = ssl3_ConsumeHandshakeNumber(ss, &curve_name, 2, &data->data,
+ &data->len);
+ if (rv != SECSuccess) {
return SECFailure; /* fatal alert already sent */
}
group = ssl_LookupNamedGroup(curve_name);