From f83f62e1bff0c2aedc32e67fe369ba923c5b104a Mon Sep 17 00:00:00 2001 From: JustOff Date: Sat, 9 Jun 2018 15:11:22 +0300 Subject: Update NSS to 3.36.4-RTM --- security/nss/lib/ssl/SSLerrs.h | 3 + security/nss/lib/ssl/selfencrypt.c | 44 +- security/nss/lib/ssl/ssl3con.c | 180 +++++--- security/nss/lib/ssl/ssl3encode.c | 85 ---- security/nss/lib/ssl/ssl3encode.h | 26 -- security/nss/lib/ssl/ssl3ext.c | 2 + security/nss/lib/ssl/ssl3exthandle.c | 14 +- security/nss/lib/ssl/ssl3gthr.c | 7 + security/nss/lib/ssl/sslcon.c | 17 +- security/nss/lib/ssl/sslencode.c | 64 ++- security/nss/lib/ssl/sslencode.h | 30 +- security/nss/lib/ssl/sslerr.h | 2 + security/nss/lib/ssl/sslexp.h | 107 ++++- security/nss/lib/ssl/sslimpl.h | 63 +-- security/nss/lib/ssl/sslinit.c | 1 + security/nss/lib/ssl/sslnonce.c | 756 +++++++++++++++++++++++++++++++++- security/nss/lib/ssl/sslsecur.c | 5 +- security/nss/lib/ssl/sslsnce.c | 20 +- security/nss/lib/ssl/sslsock.c | 231 +++++++++-- security/nss/lib/ssl/sslt.h | 24 +- security/nss/lib/ssl/tls13con.c | 30 +- security/nss/lib/ssl/tls13hashstate.c | 34 +- 22 files changed, 1377 insertions(+), 368 deletions(-) delete mode 100644 security/nss/lib/ssl/ssl3encode.c delete mode 100644 security/nss/lib/ssl/ssl3encode.h (limited to 'security/nss/lib/ssl') diff --git a/security/nss/lib/ssl/SSLerrs.h b/security/nss/lib/ssl/SSLerrs.h index c95fe661a..d3f087544 100644 --- a/security/nss/lib/ssl/SSLerrs.h +++ b/security/nss/lib/ssl/SSLerrs.h @@ -540,3 +540,6 @@ ER3(SSL_ERROR_RX_MALFORMED_KEY_UPDATE, (SSL_ERROR_BASE + 170), ER3(SSL_ERROR_TOO_MANY_KEY_UPDATES, (SSL_ERROR_BASE + 171), "SSL attempted too many key updates.") + +ER3(SSL_ERROR_HANDSHAKE_FAILED, (SSL_ERROR_BASE + 172), + "SSL handshake has already failed. No more operations possible.") diff --git a/security/nss/lib/ssl/selfencrypt.c b/security/nss/lib/ssl/selfencrypt.c index 97217b4a6..1c70f7635 100644 --- a/security/nss/lib/ssl/selfencrypt.c +++ b/security/nss/lib/ssl/selfencrypt.c @@ -192,75 +192,71 @@ ssl_SelfEncryptUnprotectInt( const PRUint8 *in, unsigned int inLen, PRUint8 *out, unsigned int *outLen, unsigned int maxOutLen) { - unsigned char *encodedKeyName; - unsigned char *iv; - SECItem ivItem = { siBuffer, NULL, 0 }; - SECItem inItem = { siBuffer, (unsigned char *)in, inLen }; - unsigned char *cipherText; - PRUint32 cipherTextLen; - unsigned char *encodedMac; - unsigned char computedMac[SHA256_LENGTH]; - unsigned int computedMacLen; - unsigned int bytesToMac; - SECStatus rv; + sslReader reader = SSL_READER(in, inLen); - rv = ssl3_ConsumeFromItem(&inItem, &encodedKeyName, - SELF_ENCRYPT_KEY_NAME_LEN); + sslReadBuffer encodedKeyNameBuffer = { 0 }; + SECStatus rv = sslRead_Read(&reader, SELF_ENCRYPT_KEY_NAME_LEN, + &encodedKeyNameBuffer); if (rv != SECSuccess) { return SECFailure; } - rv = ssl3_ConsumeFromItem(&inItem, &iv, AES_BLOCK_SIZE); + sslReadBuffer ivBuffer = { 0 }; + rv = sslRead_Read(&reader, AES_BLOCK_SIZE, &ivBuffer); if (rv != SECSuccess) { return SECFailure; } - rv = ssl3_ConsumeNumberFromItem(&inItem, &cipherTextLen, 2); + PRUint64 cipherTextLen = 0; + rv = sslRead_ReadNumber(&reader, 2, &cipherTextLen); if (rv != SECSuccess) { return SECFailure; } - rv = ssl3_ConsumeFromItem(&inItem, &cipherText, cipherTextLen); + sslReadBuffer cipherTextBuffer = { 0 }; + rv = sslRead_Read(&reader, (unsigned int)cipherTextLen, &cipherTextBuffer); if (rv != SECSuccess) { return SECFailure; } - bytesToMac = inItem.data - in; + unsigned int bytesToMac = reader.offset; - rv = ssl3_ConsumeFromItem(&inItem, &encodedMac, SHA256_LENGTH); + sslReadBuffer encodedMacBuffer = { 0 }; + rv = sslRead_Read(&reader, SHA256_LENGTH, &encodedMacBuffer); if (rv != SECSuccess) { return SECFailure; } /* Make sure we're at the end of the block. */ - if (inItem.len) { + if (reader.offset != reader.buf.len) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } /* Now that everything is decoded, we can make progress. */ /* 1. Check that we have the right key. */ - if (PORT_Memcmp(keyName, encodedKeyName, SELF_ENCRYPT_KEY_NAME_LEN)) { + if (PORT_Memcmp(keyName, encodedKeyNameBuffer.buf, SELF_ENCRYPT_KEY_NAME_LEN)) { PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT); return SECFailure; } /* 2. Check the MAC */ + unsigned char computedMac[SHA256_LENGTH]; + unsigned int computedMacLen = 0; rv = ssl_MacBuffer(macKey, CKM_SHA256_HMAC, in, bytesToMac, computedMac, &computedMacLen, sizeof(computedMac)); if (rv != SECSuccess) { return SECFailure; } PORT_Assert(computedMacLen == SHA256_LENGTH); - if (NSS_SecureMemcmp(computedMac, encodedMac, computedMacLen) != 0) { + if (NSS_SecureMemcmp(computedMac, encodedMacBuffer.buf, computedMacLen) != 0) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } /* 3. OK, it verifies, now decrypt. */ - ivItem.data = iv; - ivItem.len = AES_BLOCK_SIZE; + SECItem ivItem = { siBuffer, (unsigned char *)ivBuffer.buf, AES_BLOCK_SIZE }; rv = PK11_Decrypt(encKey, CKM_AES_CBC_PAD, &ivItem, - out, outLen, maxOutLen, cipherText, cipherTextLen); + out, outLen, maxOutLen, cipherTextBuffer.buf, cipherTextLen); if (rv != SECSuccess) { return SECFailure; } diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index 61878ae99..2593bbacc 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -183,9 +183,9 @@ static const SSLSignatureScheme defaultSignatureSchemes[] = { ssl_sig_ecdsa_secp384r1_sha384, ssl_sig_ecdsa_secp521r1_sha512, ssl_sig_ecdsa_sha1, - ssl_sig_rsa_pss_sha256, - ssl_sig_rsa_pss_sha384, - ssl_sig_rsa_pss_sha512, + ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_rsae_sha384, + ssl_sig_rsa_pss_rsae_sha512, ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pkcs1_sha384, ssl_sig_rsa_pkcs1_sha512, @@ -852,7 +852,7 @@ ssl3_config_match_init(sslSocket *ss) * enabled, has a certificate (as needed), has a viable key agreement method, is * usable with the negotiated TLS version, and is otherwise usable. */ static PRBool -config_match(const ssl3CipherSuiteCfg *suite, int policy, +config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy, const SSLVersionRange *vrange, const sslSocket *ss) { const ssl3CipherSuiteDef *cipher_def; @@ -888,7 +888,7 @@ config_match(const ssl3CipherSuiteCfg *suite, int policy, /* Return the number of cipher suites that are usable. */ /* called from ssl3_SendClientHello */ static unsigned int -count_cipher_suites(sslSocket *ss, int policy) +count_cipher_suites(sslSocket *ss, PRUint8 policy) { unsigned int i, count = 0; @@ -2336,6 +2336,11 @@ ssl3_SendRecord(sslSocket *ss, if (ss->ssl3.fatalAlertSent) { SSL_TRC(3, ("%d: SSL3[%d] Suppress write, fatal alert already sent", SSL_GETPID(), ss->fd)); + if (type != content_alert) { + /* If we are sending an alert, then we already have an + * error, so don't overwrite. */ + PORT_SetError(SSL_ERROR_HANDSHAKE_FAILED); + } return SECFailure; } @@ -2647,9 +2652,7 @@ ssl3_HandleNoCertificate(sslSocket *ss) (ss->opt.requireCertificate == SSL_REQUIRE_FIRST_HANDSHAKE))) { PRFileDesc *lower; - if (!ss->opt.noCache) { - ss->sec.uncache(ss->sec.ci.sid); - } + ssl_UncacheSessionID(ss); SSL3_SendAlert(ss, alert_fatal, bad_certificate); lower = ss->fd->lower; @@ -2711,8 +2714,8 @@ SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc) ssl_GetSSL3HandshakeLock(ss); } if (level == alert_fatal) { - if (!ss->opt.noCache && ss->sec.ci.sid) { - ss->sec.uncache(ss->sec.ci.sid); + if (ss->sec.ci.sid) { + ssl_UncacheSessionID(ss); } } @@ -2976,9 +2979,7 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf) } } if (level == alert_fatal) { - if (!ss->opt.noCache) { - ss->sec.uncache(ss->sec.ci.sid); - } + ssl_UncacheSessionID(ss); if ((ss->ssl3.hs.ws == wait_server_hello) && (desc == handshake_failure)) { /* XXX This is a hack. We're assuming that any handshake failure @@ -3962,17 +3963,20 @@ ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme) return ssl_hash_sha1; case ssl_sig_rsa_pkcs1_sha256: case ssl_sig_ecdsa_secp256r1_sha256: - case ssl_sig_rsa_pss_sha256: + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_pss_sha256: case ssl_sig_dsa_sha256: return ssl_hash_sha256; case ssl_sig_rsa_pkcs1_sha384: case ssl_sig_ecdsa_secp384r1_sha384: - case ssl_sig_rsa_pss_sha384: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_pss_sha384: case ssl_sig_dsa_sha384: return ssl_hash_sha384; case ssl_sig_rsa_pkcs1_sha512: case ssl_sig_ecdsa_secp521r1_sha512: - case ssl_sig_rsa_pss_sha512: + case ssl_sig_rsa_pss_rsae_sha512: + case ssl_sig_rsa_pss_pss_sha512: case ssl_sig_dsa_sha512: return ssl_hash_sha512; case ssl_sig_rsa_pkcs1_sha1md5: @@ -3994,9 +3998,12 @@ ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme) case ssl_sig_rsa_pkcs1_sha384: case ssl_sig_rsa_pkcs1_sha512: case ssl_sig_rsa_pkcs1_sha1: - case ssl_sig_rsa_pss_sha256: - case ssl_sig_rsa_pss_sha384: - case ssl_sig_rsa_pss_sha512: + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_rsae_sha512: + case ssl_sig_rsa_pss_pss_sha256: + case ssl_sig_rsa_pss_pss_sha384: + case ssl_sig_rsa_pss_pss_sha512: case ssl_sig_rsa_pkcs1_sha1md5: return rsaKey; case ssl_sig_ecdsa_secp256r1_sha256: @@ -4131,9 +4138,9 @@ ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme) case ssl_sig_rsa_pkcs1_sha256: case ssl_sig_rsa_pkcs1_sha384: case ssl_sig_rsa_pkcs1_sha512: - case ssl_sig_rsa_pss_sha256: - case ssl_sig_rsa_pss_sha384: - case ssl_sig_rsa_pss_sha512: + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_rsae_sha512: case ssl_sig_ecdsa_secp256r1_sha256: case ssl_sig_ecdsa_secp384r1_sha384: case ssl_sig_ecdsa_secp521r1_sha512: @@ -4145,6 +4152,9 @@ ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme) return PR_TRUE; case ssl_sig_rsa_pkcs1_sha1md5: + case ssl_sig_rsa_pss_pss_sha256: + case ssl_sig_rsa_pss_pss_sha384: + case ssl_sig_rsa_pss_pss_sha512: case ssl_sig_none: case ssl_sig_ed25519: case ssl_sig_ed448: @@ -4157,9 +4167,9 @@ PRBool ssl_IsRsaPssSignatureScheme(SSLSignatureScheme scheme) { switch (scheme) { - case ssl_sig_rsa_pss_sha256: - case ssl_sig_rsa_pss_sha384: - case ssl_sig_rsa_pss_sha512: + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_rsae_sha512: return PR_TRUE; default: @@ -4262,6 +4272,7 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss, sizeof(stackBuf), &stateLen); if (stateBuf == NULL) { ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); + rv = SECFailure; goto tls12_loser; } rv |= PK11_DigestFinal(h, hashes->u.raw, &hashes->len, @@ -4273,7 +4284,6 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss, } hashes->hashAlg = ssl3_GetSuitePrfHash(ss); - rv = SECSuccess; tls12_loser: if (stateBuf) { @@ -4305,6 +4315,7 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss, sizeof md5StackBuf, &md5StateLen); if (md5StateBuf == NULL) { ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE); + rv = SECFailure; goto loser; } md5 = ss->ssl3.hs.md5; @@ -4313,6 +4324,7 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss, sizeof shaStackBuf, &shaStateLen); if (shaStateBuf == NULL) { ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + rv = SECFailure; goto loser; } sha = ss->ssl3.hs.sha; @@ -4408,7 +4420,6 @@ ssl3_ComputeHandshakeHashes(sslSocket *ss, PRINT_BUF(60, (NULL, "SHA outer: result", hashes->u.s.sha, SHA1_LENGTH)); hashes->len = MD5_LENGTH + SHA1_LENGTH; - rv = SECSuccess; loser: if (md5StateBuf) { @@ -4589,13 +4600,24 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) } } - /* We ignore ss->sec.ci.sid here, and use ssl_Lookup because Lookup - * handles expired entries and other details. - * XXX If we've been called from ssl_BeginClientHandshake, then - * this lookup is duplicative and wasteful. - */ - sid = (ss->opt.noCache) ? NULL - : ssl_LookupSID(&ss->sec.ci.peer, ss->sec.ci.port, ss->peerID, ss->url); + /* Check if we have a ss->sec.ci.sid. + * Check that it's not expired. + * If we have an sid and it comes from an external cache, we use it. */ + if (ss->sec.ci.sid && ss->sec.ci.sid->cached == in_external_cache) { + PORT_Assert(!ss->sec.isServer); + sid = ss->sec.ci.sid; + SSL_TRC(3, ("%d: SSL3[%d]: using external resumption token in ClientHello", + SSL_GETPID(), ss->fd)); + } else if (!ss->opt.noCache) { + /* We ignore ss->sec.ci.sid here, and use ssl_Lookup because Lookup + * handles expired entries and other details. + * XXX If we've been called from ssl_BeginClientHandshake, then + * this lookup is duplicative and wasteful. + */ + sid = ssl_LookupSID(&ss->sec.ci.peer, ss->sec.ci.port, ss->peerID, ss->url); + } else { + sid = NULL; + } /* We can't resume based on a different token. If the sid exists, * make sure the token that holds the master secret still exists ... @@ -4686,7 +4708,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) if (!sidOK) { SSL_AtomicIncrementLong(&ssl3stats.sch_sid_cache_not_ok); - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); sid = NULL; } @@ -5017,7 +5039,7 @@ ssl3_HandleHelloRequest(sslSocket *ss) } if (sid) { - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); ss->sec.ci.sid = NULL; } @@ -6130,6 +6152,48 @@ ssl_ClientSetCipherSuite(sslSocket *ss, SSL3ProtocolVersion version, return ssl3_SetupCipherSuite(ss, initHashes); } +/* Check that session ID we received from the server, if any, matches our + * expectations, depending on whether we're in compat mode and whether we + * negotiated TLS 1.3+ or TLS 1.2-. + */ +static PRBool +ssl_CheckServerSessionIdCorrectness(sslSocket *ss, SECItem *sidBytes) +{ + sslSessionID *sid = ss->sec.ci.sid; + PRBool sidMatch = PR_FALSE; + PRBool sentFakeSid = PR_FALSE; + PRBool sentRealSid = sid && sid->version < SSL_LIBRARY_VERSION_TLS_1_3; + + /* If attempting to resume a TLS 1.2 connection, the session ID won't be a + * fake. Check for the real value. */ + if (sentRealSid) { + sidMatch = (sidBytes->len == sid->u.ssl3.sessionIDLength) && + PORT_Memcmp(sid->u.ssl3.sessionID, sidBytes->data, sidBytes->len) == 0; + } else { + /* Otherwise, the session ID was a fake if TLS 1.3 compat mode is + * enabled. If so, check for the fake value. */ + sentFakeSid = ss->opt.enableTls13CompatMode && !IS_DTLS(ss); + if (sentFakeSid && sidBytes->len == SSL3_SESSIONID_BYTES) { + PRUint8 buf[SSL3_SESSIONID_BYTES]; + ssl_MakeFakeSid(ss, buf); + sidMatch = PORT_Memcmp(buf, sidBytes->data, sidBytes->len) == 0; + } + } + + /* TLS 1.2: Session ID shouldn't match if we sent a fake. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return !sentFakeSid || !sidMatch; + } + + /* TLS 1.3: We sent a session ID. The server's should match. */ + if (sentRealSid || sentFakeSid) { + return sidMatch; + } + + /* TLS 1.3: The server shouldn't send a session ID. */ + return sidBytes->len == 0; +} + /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete * ssl3 ServerHello message. * Caller must hold Handshake and RecvBuf locks. @@ -6337,22 +6401,10 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length) } /* Check that the session ID is as expected. */ - if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - PRUint8 buf[SSL3_SESSIONID_BYTES]; - unsigned int expectedSidLen; - if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) { - expectedSidLen = SSL3_SESSIONID_BYTES; - ssl_MakeFakeSid(ss, buf); - } else { - expectedSidLen = 0; - } - if (sidBytes.len != expectedSidLen || - (expectedSidLen > 0 && - PORT_Memcmp(buf, sidBytes.data, expectedSidLen) != 0)) { - desc = illegal_parameter; - errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO; - goto alert_loser; - } + if (!ssl_CheckServerSessionIdCorrectness(ss, &sidBytes)) { + desc = illegal_parameter; + errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO; + goto alert_loser; } /* Only initialize hashes if this isn't a Hello Retry. */ @@ -6592,7 +6644,7 @@ ssl3_HandleServerHelloPart2(sslSocket *ss, const SECItem *sidBytes, /* throw the old one away */ sid->u.ssl3.keys.resumable = PR_FALSE; - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); /* get a new sid */ @@ -7502,8 +7554,6 @@ ssl3_NewSessionID(sslSocket *ss, PRBool is_server) sid->u.ssl3.keys.resumable = PR_TRUE; sid->u.ssl3.policy = SSL_ALLOWED; - sid->u.ssl3.clientWriteKey = NULL; - sid->u.ssl3.serverWriteKey = NULL; sid->u.ssl3.keys.extendedMasterSecretUsed = PR_FALSE; if (is_server) { @@ -8102,6 +8152,9 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) rv = ssl3_HandleParsedExtensions(ss, ssl_hs_client_hello); ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions); if (rv != SECSuccess) { + if (PORT_GetError() == SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM) { + errCode = SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM; + } goto loser; /* malformed */ } @@ -8229,7 +8282,7 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) !ss->firstHsDone))) { SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_not_ok); - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); sid = NULL; } @@ -8435,7 +8488,7 @@ cipher_found: } if (ss->sec.ci.sid) { - ss->sec.uncache(ss->sec.ci.sid); + ssl_UncacheSessionID(ss); PORT_Assert(ss->sec.ci.sid != sid); /* should be impossible, but ... */ if (ss->sec.ci.sid != sid) { ssl_FreeSID(ss->sec.ci.sid); @@ -8531,7 +8584,7 @@ cipher_found: if (sid) { /* we had a sid, but it's no longer valid, free it */ ss->statelessResume = PR_FALSE; SSL_AtomicIncrementLong(&ssl3stats.hch_sid_cache_not_ok); - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); sid = NULL; } @@ -8597,7 +8650,7 @@ alert_loser: /* FALLTHRU */ loser: if (sid && sid != ss->sec.ci.sid) { - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); } @@ -11137,6 +11190,7 @@ ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid, PK11SymKey *secret) if (ss->xtnData.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && ss->xtnData.nextProto.data) { + SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE); if (SECITEM_CopyItem( NULL, &sid->u.ssl3.alpnSelection, &ss->xtnData.nextProto) != SECSuccess) { return SECFailure; /* error already set. */ @@ -11166,7 +11220,7 @@ ssl3_FinishHandshake(sslSocket *ss) * the handshake is finished (we have verified the server's Finished * AND the server's certificate) before we update the ticket in the sid. * - * This must be done before we call ss->sec.cache(ss->sec.ci.sid) + * This must be done before we call ssl_CacheSessionID(ss) * because CacheSID requires the session ticket to already be set, and also * because of the lazy lock creation scheme used by CacheSID and * ssl3_SetSIDSessionTicket. @@ -11181,7 +11235,7 @@ ssl3_FinishHandshake(sslSocket *ss) if (ss->ssl3.hs.cacheSID) { PORT_Assert(ss->sec.ci.sid->cached == never_cached); - ss->sec.cache(ss->sec.ci.sid); + ssl_CacheSessionID(ss); ss->ssl3.hs.cacheSID = PR_FALSE; } @@ -12645,8 +12699,8 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache) } if (sid && flushCache) { - ss->sec.uncache(sid); /* remove it from whichever cache it's in. */ - ssl_FreeSID(sid); /* dec ref count and free if zero. */ + ssl_UncacheSessionID(ss); /* remove it from whichever cache it's in. */ + ssl_FreeSID(sid); /* dec ref count and free if zero. */ ss->sec.ci.sid = NULL; } diff --git a/security/nss/lib/ssl/ssl3encode.c b/security/nss/lib/ssl/ssl3encode.c deleted file mode 100644 index 960208a0f..000000000 --- a/security/nss/lib/ssl/ssl3encode.c +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is PRIVATE to SSL. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "prnetdb.h" -#include "seccomon.h" -#include "secerr.h" -#include "ssl3encode.h" - -SECStatus -ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes) -{ - if (bytes > item->len) { - PORT_SetError(SEC_ERROR_INVALID_ARGS); - return SECFailure; - } - - PORT_Memcpy(item->data, buf, bytes); - item->data += bytes; - item->len -= bytes; - return SECSuccess; -} - -SECStatus -ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize) -{ - SECStatus rv; - PRUint8 b[4]; - - ssl_EncodeUintX(num, lenSize, b); - rv = ssl3_AppendToItem(item, &b[0], lenSize); - return rv; -} - -SECStatus -ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes) -{ - if (bytes > item->len) { - PORT_SetError(SEC_ERROR_BAD_DATA); - return SECFailure; - } - - *buf = item->data; - item->data += bytes; - item->len -= bytes; - return SECSuccess; -} - -SECStatus -ssl3_ConsumeNumberFromItem(SECItem *item, PRUint32 *num, PRUint32 bytes) -{ - int i; - - if (bytes > item->len || bytes > sizeof(*num)) { - PORT_SetError(SEC_ERROR_BAD_DATA); - return SECFailure; - } - - *num = 0; - for (i = 0; i < bytes; i++) { - *num = (*num << 8) + item->data[i]; - } - - item->data += bytes; - item->len -= bytes; - - return SECSuccess; -} - -/* Helper function to encode an unsigned integer into a buffer. */ -PRUint8 * -ssl_EncodeUintX(PRUint64 value, unsigned int bytes, PRUint8 *to) -{ - PRUint64 encoded; - - PORT_Assert(bytes > 0 && bytes <= sizeof(encoded)); - - encoded = PR_htonll(value); - memcpy(to, ((unsigned char *)(&encoded)) + (sizeof(encoded) - bytes), bytes); - return to + bytes; -} diff --git a/security/nss/lib/ssl/ssl3encode.h b/security/nss/lib/ssl/ssl3encode.h deleted file mode 100644 index 3b88f7e7b..000000000 --- a/security/nss/lib/ssl/ssl3encode.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is PRIVATE to SSL. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __ssl3encode_h_ -#define __ssl3encode_h_ - -#include "seccomon.h" - -/* All of these functions modify the underlying SECItem, and so should - * be performed on a shallow copy.*/ -SECStatus ssl3_AppendToItem(SECItem *item, - const unsigned char *buf, PRUint32 bytes); -SECStatus ssl3_AppendNumberToItem(SECItem *item, - PRUint32 num, PRInt32 lenSize); -SECStatus ssl3_ConsumeFromItem(SECItem *item, - unsigned char **buf, PRUint32 bytes); -SECStatus ssl3_ConsumeNumberFromItem(SECItem *item, - PRUint32 *num, PRUint32 bytes); -PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes, PRUint8 *to); - -#endif diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index ade280903..5a5077998 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -519,6 +519,8 @@ ssl3_HandleParsedExtensions(sslSocket *ss, SSLHandshakeType message) } /* Fall through. */ case tls13_extension_disallowed: + SSL_TRC(3, ("%d: TLS13: unexpected extension %d in message %d", + SSL_GETPID(), extension, message)); tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, unsupported_extension); return SECFailure; diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c index c0fbda7ab..e6388945e 100644 --- a/security/nss/lib/ssl/ssl3exthandle.c +++ b/security/nss/lib/ssl/ssl3exthandle.c @@ -182,7 +182,7 @@ ssl3_ClientSendSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData, /* Never send an extension with a ticket for TLS 1.3, but * OK to send the empty one in case the server does 1.2. */ - if (sid->cached == in_client_cache && + if ((sid->cached == in_client_cache || sid->cached == in_external_cache) && sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) { return SECSuccess; } @@ -821,7 +821,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss, const NewSessionTicket *ticket, if (rv != SECSuccess) goto loser; - rv = sslBuffer_AppendNumber(&plaintext, ssl_max_early_data_size, 4); + rv = sslBuffer_AppendNumber(&plaintext, ss->opt.maxEarlyDataSize, 4); if (rv != SECSuccess) goto loser; @@ -1219,6 +1219,7 @@ ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket, } } if (parsedTicket->alpnSelection.data != NULL) { + SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE); rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.alpnSelection, &parsedTicket->alpnSelection); if (rv != SECSuccess) { @@ -1245,7 +1246,7 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket, SECStatus rv; if (ss->sec.ci.sid != NULL) { - ss->sec.uncache(ss->sec.ci.sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(ss->sec.ci.sid); ss->sec.ci.sid = NULL; } @@ -1652,11 +1653,16 @@ ssl3_HandleSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData, &xtnData->sigSchemes, &xtnData->numSigSchemes, &data->data, &data->len); - if (rv != SECSuccess || xtnData->numSigSchemes == 0) { + if (rv != SECSuccess) { ssl3_ExtSendAlert(ss, alert_fatal, decode_error); PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); return SECFailure; } + if (xtnData->numSigSchemes == 0) { + ssl3_ExtSendAlert(ss, alert_fatal, handshake_failure); + PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + return SECFailure; + } /* Check for trailing data. */ if (data->len != 0) { ssl3_ExtSendAlert(ss, alert_fatal, decode_error); diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c index 20404f4da..8b323bb05 100644 --- a/security/nss/lib/ssl/ssl3gthr.c +++ b/security/nss/lib/ssl/ssl3gthr.c @@ -386,6 +386,13 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) SSL3Ciphertext cText; PRBool keepGoing = PR_TRUE; + if (ss->ssl3.fatalAlertSent) { + SSL_TRC(3, ("%d: SSL3[%d] Cannot gather data; fatal alert already sent", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_HANDSHAKE_FAILED); + return SECFailure; + } + SSL_TRC(30, ("%d: SSL3[%d]: ssl3_GatherCompleteHandshake", SSL_GETPID(), ss->fd)); diff --git a/security/nss/lib/ssl/sslcon.c b/security/nss/lib/ssl/sslcon.c index 448170640..bc63e1537 100644 --- a/security/nss/lib/ssl/sslcon.c +++ b/security/nss/lib/ssl/sslcon.c @@ -119,13 +119,12 @@ ssl_CheckConfigSanity(sslSocket *ss) SECStatus ssl_BeginClientHandshake(sslSocket *ss) { - sslSessionID *sid; + sslSessionID *sid = NULL; SECStatus rv; PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss)); ss->sec.isServer = PR_FALSE; - ssl_ChooseSessionIDProcs(&ss->sec); rv = ssl_CheckConfigSanity(ss); if (rv != SECSuccess) @@ -156,19 +155,22 @@ ssl_BeginClientHandshake(sslSocket *ss) SSL_TRC(3, ("%d: SSL[%d]: sending client-hello", SSL_GETPID(), ss->fd)); - /* Try to find server in our session-id cache */ - if (ss->opt.noCache) { - sid = NULL; - } else { + /* If there's an sid set from an external cache, use it. */ + if (ss->sec.ci.sid && ss->sec.ci.sid->cached == in_external_cache) { + sid = ss->sec.ci.sid; + SSL_TRC(3, ("%d: SSL[%d]: using external token", SSL_GETPID(), ss->fd)); + } else if (!ss->opt.noCache) { + /* Try to find server in our session-id cache */ sid = ssl_LookupSID(&ss->sec.ci.peer, ss->sec.ci.port, ss->peerID, ss->url); } + if (sid) { if (sid->version >= ss->vrange.min && sid->version <= ss->vrange.max) { PORT_Assert(!ss->sec.localCert); ss->sec.localCert = CERT_DupCertificate(sid->localCert); } else { - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); sid = NULL; } @@ -218,7 +220,6 @@ ssl_BeginServerHandshake(sslSocket *ss) ss->sec.isServer = PR_TRUE; ss->ssl3.hs.ws = wait_client_hello; - ssl_ChooseSessionIDProcs(&ss->sec); rv = ssl_CheckConfigSanity(ss); if (rv != SECSuccess) diff --git a/security/nss/lib/ssl/sslencode.c b/security/nss/lib/ssl/sslencode.c index 2f127fe8f..e50880451 100644 --- a/security/nss/lib/ssl/sslencode.c +++ b/security/nss/lib/ssl/sslencode.c @@ -29,6 +29,7 @@ ssl_EncodeUintX(PRUint8 *to, PRUint64 value, unsigned int bytes) SECStatus sslBuffer_Grow(sslBuffer *b, unsigned int newLen) { + PORT_Assert(b); if (b->fixed) { PORT_Assert(newLen <= b->space); if (newLen > b->space) { @@ -84,6 +85,7 @@ sslBuffer_AppendVariable(sslBuffer *b, const PRUint8 *data, unsigned int len, unsigned int size) { PORT_Assert(size <= 4 && size > 0); + PORT_Assert(b); if (len >= (1ULL << (8 * size))) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; @@ -95,7 +97,11 @@ sslBuffer_AppendVariable(sslBuffer *b, const PRUint8 *data, unsigned int len, ssl_EncodeUintX(SSL_BUFFER_NEXT(b), len, size); b->len += size; - PORT_Memcpy(SSL_BUFFER_NEXT(b), data, len); + if (len != 0) { + PORT_Assert(data); + /* We sometimes pass NULL, 0 and memcpy() doesn't want NULL. */ + PORT_Memcpy(SSL_BUFFER_NEXT(b), data, len); + } b->len += len; return SECSuccess; } @@ -169,37 +175,63 @@ sslBuffer_Clear(sslBuffer *b) } SECStatus -ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, unsigned int size) +sslRead_Read(sslReader *reader, unsigned int count, sslReadBuffer *out) { - if (size > item->len) { + if (!reader || !out) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (reader->buf.len < reader->offset || + count > SSL_READER_REMAINING(reader)) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } - *buf = item->data; - item->data += size; - item->len -= size; + out->buf = SSL_READER_CURRENT(reader); + out->len = count; + reader->offset += count; + return SECSuccess; } SECStatus -ssl3_ConsumeNumberFromItem(SECItem *item, PRUint32 *num, unsigned int size) +sslRead_ReadVariable(sslReader *reader, unsigned int sizeLen, sslReadBuffer *out) { - int i; - - if (size > item->len || size > sizeof(*num)) { + PRUint64 variableLen = 0; + SECStatus rv = sslRead_ReadNumber(reader, sizeLen, &variableLen); + if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } - - *num = 0; - for (i = 0; i < size; i++) { - *num = (*num << 8) + item->data[i]; + if (!variableLen) { + // It is ok to have an empty variable. + out->len = variableLen; + return SECSuccess; } + return sslRead_Read(reader, variableLen, out); +} - item->data += size; - item->len -= size; +SECStatus +sslRead_ReadNumber(sslReader *reader, unsigned int bytes, PRUint64 *num) +{ + if (!reader || !num) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (reader->buf.len < reader->offset || + bytes > SSL_READER_REMAINING(reader) || + bytes > 8) { + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + unsigned int i; + PRUint64 number = 0; + for (i = 0; i < bytes; i++) { + number = (number << 8) + reader->buf.buf[i + reader->offset]; + } + reader->offset = reader->offset + bytes; + *num = number; return SECSuccess; } diff --git a/security/nss/lib/ssl/sslencode.h b/security/nss/lib/ssl/sslencode.h index a1b04d88f..f43e1c54b 100644 --- a/security/nss/lib/ssl/sslencode.h +++ b/security/nss/lib/ssl/sslencode.h @@ -47,13 +47,6 @@ SECStatus sslBuffer_InsertLength(sslBuffer *b, unsigned int at, unsigned int size); void sslBuffer_Clear(sslBuffer *b); -/* All of these functions modify the underlying SECItem, and so should - * be performed on a shallow copy.*/ -SECStatus ssl3_ConsumeFromItem(SECItem *item, - PRUint8 **buf, unsigned int size); -SECStatus ssl3_ConsumeNumberFromItem(SECItem *item, - PRUint32 *num, unsigned int size); - SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src, unsigned int bytes); SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss, @@ -66,4 +59,27 @@ SECStatus ssl3_AppendBufferToHandshake(sslSocket *ss, sslBuffer *buf); SECStatus ssl3_AppendBufferToHandshakeVariable(sslSocket *ss, sslBuffer *buf, unsigned int lenSize); +typedef struct { + const PRUint8 *buf; + unsigned int len; +} sslReadBuffer; +typedef struct { + sslReadBuffer buf; + unsigned int offset; +} sslReader; +#define SSL_READER(b, l) \ + { \ + { b, l }, 0 \ + } +#define SSL_READER_CURRENT(r) \ + ((r)->buf.buf + (r)->offset) +#define SSL_READER_REMAINING(r) \ + ((r)->buf.len - (r)->offset) +SECStatus sslRead_Read(sslReader *reader, unsigned int count, + sslReadBuffer *out); +SECStatus sslRead_ReadVariable(sslReader *reader, unsigned int sizeLen, + sslReadBuffer *out); +SECStatus sslRead_ReadNumber(sslReader *reader, unsigned int bytes, + PRUint64 *val); + #endif /* __sslencode_h_ */ diff --git a/security/nss/lib/ssl/sslerr.h b/security/nss/lib/ssl/sslerr.h index 90815dd79..b94d0cc62 100644 --- a/security/nss/lib/ssl/sslerr.h +++ b/security/nss/lib/ssl/sslerr.h @@ -260,6 +260,8 @@ typedef enum { SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE = (SSL_ERROR_BASE + 169), SSL_ERROR_RX_MALFORMED_KEY_UPDATE = (SSL_ERROR_BASE + 170), SSL_ERROR_TOO_MANY_KEY_UPDATES = (SSL_ERROR_BASE + 171), + SSL_ERROR_HANDSHAKE_FAILED = (SSL_ERROR_BASE + 172), + SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR = (SSL_ERROR_BASE + 173), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; #endif /* NO_SECURITY_ERROR_ENUM */ diff --git a/security/nss/lib/ssl/sslexp.h b/security/nss/lib/ssl/sslexp.h index 569add861..08654f885 100644 --- a/security/nss/lib/ssl/sslexp.h +++ b/security/nss/lib/ssl/sslexp.h @@ -350,8 +350,111 @@ typedef SSLHelloRetryRequestAction(PR_CALLBACK *SSLHelloRetryRequestCallback)( (PRFileDesc * _fd, PRBool _requestUpdate), \ (fd, requestUpdate)) -#define SSL_UseAltServerHelloType(fd, enable) \ - SSL_DEPRECATED_EXPERIMENTAL_API +/* + * Session cache API. + */ + +/* + * Information that can be retrieved about a resumption token. + * See SSL_GetResumptionTokenInfo for details about how to use this API. + * Note that peerCert points to a certificate in the NSS database and must be + * copied by the application if it should be used after NSS shutdown or after + * calling SSL_DestroyResumptionTokenInfo. + */ +typedef struct SSLResumptionTokenInfoStr { + PRUint16 length; + CERTCertificate *peerCert; + PRUint8 *alpnSelection; + PRUint32 alpnSelectionLen; + PRUint32 maxEarlyDataSize; +} SSLResumptionTokenInfo; + +/* + * Allows applications to retrieve information about a resumption token. + * This does not require a TLS session. + * + * - The |tokenData| argument is a pointer to the resumption token as byte array + * of length |tokenLen|. + * - The |token| argument is a pointer to a SSLResumptionTokenInfo struct of + * of |len|. The struct gets filled by this function. + * See SSL_DestroyResumptionTokenInfo for information about how to manage the + * |token| memory. + */ +#define SSL_GetResumptionTokenInfo(tokenData, tokenLen, token, len) \ + SSL_EXPERIMENTAL_API("SSL_GetResumptionTokenInfo", \ + (const PRUint8 *_tokenData, unsigned int _tokenLen, \ + SSLResumptionTokenInfo *_token, PRUintn _len), \ + (tokenData, tokenLen, token, len)) + +/* + * SSL_GetResumptionTokenInfo allocates memory in order to populate |tokenInfo|. + * Any SSLResumptionTokenInfo struct filled with SSL_GetResumptionTokenInfo + * has to be freed with SSL_DestroyResumptionTokenInfo. + */ +#define SSL_DestroyResumptionTokenInfo(tokenInfo) \ + SSL_EXPERIMENTAL_API( \ + "SSL_DestroyResumptionTokenInfo", \ + (SSLResumptionTokenInfo * _tokenInfo), \ + (tokenInfo)) + +/* + * This is the function signature for function pointers used as resumption + * token callback. The caller has to copy the memory at |resumptionToken| with + * length |len| before returning. + * + * - The |fd| argument is the socket file descriptor. + * - The |resumptionToken| is a pointer to the resumption token as byte array + * of length |len|. + * - The |ctx| is a void pointer to the context set by the application in + * SSL_SetResumptionTokenCallback. + */ +typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)( + PRFileDesc *fd, const PRUint8 *resumptionToken, unsigned int len, + void *ctx); + +/* + * This allows setting a callback for external session caches to store + * resumption tokens. + * + * - The |fd| argument is the socket file descriptor. + * - The |cb| is a function pointer to an implementation of + * SSLResumptionTokenCallback. + * - The |ctx| is a pointer to some application specific context, which is + * returned when |cb| is called. + */ +#define SSL_SetResumptionTokenCallback(fd, cb, ctx) \ + SSL_EXPERIMENTAL_API( \ + "SSL_SetResumptionTokenCallback", \ + (PRFileDesc * _fd, SSLResumptionTokenCallback _cb, void *_ctx), \ + (fd, cb, ctx)) + +/* + * This allows setting a resumption token for a session. + * The function returns SECSuccess iff the resumption token can be used, + * SECFailure in any other case. The caller should remove the |token| from its + * cache when the function returns SECFailure. + * + * - The |fd| argument is the socket file descriptor. + * - The |token| is a pointer to the resumption token as byte array + * of length |len|. + */ +#define SSL_SetResumptionToken(fd, token, len) \ + SSL_EXPERIMENTAL_API( \ + "SSL_SetResumptionToken", \ + (PRFileDesc * _fd, const PRUint8 *_token, const unsigned int _len), \ + (fd, token, len)) + +/* TLS 1.3 allows a server to set a limit on the number of bytes of early data + * that can be received. This allows that limit to be set. This function has no + * effect on a client. */ +#define SSL_SetMaxEarlyDataSize(fd, size) \ + SSL_EXPERIMENTAL_API("SSL_SetMaxEarlyDataSize", \ + (PRFileDesc * _fd, PRUint32 _size), \ + (fd, size)) + +/* Deprecated experimental APIs */ + +#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API SEC_END_PROTOS diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h index dee9aa20f..10d0333d9 100644 --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -168,8 +168,11 @@ struct ssl3CertNodeStr { typedef SECStatus (*sslHandshakeFunc)(sslSocket *ss); -typedef void (*sslSessionIDCacheFunc)(sslSessionID *sid); -typedef void (*sslSessionIDUncacheFunc)(sslSessionID *sid); +void ssl_CacheSessionID(sslSocket *ss); +void ssl_UncacheSessionID(sslSocket *ss); +void ssl_ServerCacheSessionID(sslSessionID *sid); +void ssl_ServerUncacheSessionID(sslSessionID *sid); + typedef sslSessionID *(*sslSessionIDLookupFunc)(const PRIPv6Addr *addr, unsigned char *sid, unsigned int sidLen, @@ -230,6 +233,7 @@ typedef struct sslOptionsStr { * list of supported protocols. */ SECItem nextProtoNego; + PRUint32 maxEarlyDataSize; unsigned int useSecurity : 1; unsigned int useSocks : 1; unsigned int requestCertificate : 1; @@ -341,9 +345,11 @@ struct sslGatherStr { #define GS_HEADER 1 #define GS_DATA 2 +#define WRAPPED_MASTER_SECRET_SIZE 48 + typedef struct { - PRUint8 wrapped_master_secret[48]; - PRUint16 wrapped_master_secret_len; + PRUint8 wrapped_master_secret[WRAPPED_MASTER_SECRET_SIZE]; + PRUint8 wrapped_master_secret_len; PRUint8 resumable; PRUint8 extendedMasterSecretUsed; } ssl3SidKeys; /* 52 bytes */ @@ -351,7 +357,8 @@ typedef struct { typedef enum { never_cached, in_client_cache, in_server_cache, - invalid_cache /* no longer in any cache. */ + invalid_cache, /* no longer in any cache. */ + in_external_cache } Cached; #include "sslcert.h" @@ -398,17 +405,11 @@ struct sslSessionIDStr { PRUint8 sessionID[SSL3_SESSIONID_BYTES]; ssl3CipherSuite cipherSuite; - int policy; + PRUint8 policy; ssl3SidKeys keys; /* mechanism used to wrap master secret */ CK_MECHANISM_TYPE masterWrapMech; - /* The following values are NOT restored from the server's on-disk - * session cache, but are restored from the client's cache. - */ - PK11SymKey *clientWriteKey; - PK11SymKey *serverWriteKey; - /* The following values pertain to the slot that wrapped the ** master secret. (used only in client) */ @@ -740,7 +741,7 @@ struct ssl3StateStr { CERTCertificateList *clientCertChain; /* used by client */ PRBool sendEmptyCert; /* used by client */ - int policy; + PRUint8 policy; /* This says what cipher suites we can do, and should * be either SSL_ALLOWED or SSL_RESTRICTED */ @@ -897,14 +898,6 @@ struct sslSecurityInfoStr { /* The selected certificate (for servers only). */ const sslServerCert *serverCert; - /* - ** Procs used for SID cache (nonce) management. - ** Different implementations exist for clients/servers - ** The lookup proc is only used for servers. Baloney! - */ - sslSessionIDCacheFunc cache; - sslSessionIDUncacheFunc uncache; - /* These are used during a connection handshake */ sslConnectInfo ci; }; @@ -982,6 +975,8 @@ struct sslSocketStr { SSLHelloRetryRequestCallback hrrCallback; void *hrrCallbackArg; PRCList extensionHooks; + SSLResumptionTokenCallback resumptionTokenCallback; + void *resumptionTokenContext; PRIntervalTime rTimeout; /* timeout for NSPR I/O */ PRIntervalTime wTimeout; /* timeout for NSPR I/O */ @@ -1075,13 +1070,10 @@ extern FILE *ssl_keylog_iob; extern PZLock *ssl_keylog_lock; extern PRUint32 ssl3_sid_timeout; extern PRUint32 ssl_ticket_lifetime; -extern PRUint32 ssl_max_early_data_size; extern const char *const ssl3_cipherName[]; extern sslSessionIDLookupFunc ssl_sid_lookup; -extern sslSessionIDCacheFunc ssl_sid_cache; -extern sslSessionIDUncacheFunc ssl_sid_uncache; extern const sslNamedGroupDef ssl_named_groups[]; @@ -1163,14 +1155,13 @@ extern SECStatus ssl_BeginClientHandshake(sslSocket *ss); extern SECStatus ssl_BeginServerHandshake(sslSocket *ss); extern int ssl_Do1stHandshake(sslSocket *ss); -extern void ssl_ChooseSessionIDProcs(sslSecurityInfo *sec); - extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret, PRBool derive); extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server); extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, const char *urlSvrName); extern void ssl_FreeSID(sslSessionID *sid); +extern void ssl_DestroySID(sslSessionID *sid, PRBool freeIt); extern int ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in, int len, int flags); @@ -1692,6 +1683,26 @@ PRBool ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag); void ssl_Trace(const char *format, ...); +void ssl_CacheExternalToken(sslSocket *ss); +SECStatus ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedTicket, + PRUint32 encodedTicketLen); +PRBool ssl_IsResumptionTokenValid(sslSocket *ss); + +/* Remove when stable. */ + +SECStatus SSLExp_SetResumptionTokenCallback(PRFileDesc *fd, + SSLResumptionTokenCallback cb, + void *ctx); +SECStatus SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token, + unsigned int len); + +SECStatus SSLExp_GetResumptionTokenInfo(const PRUint8 *tokenData, unsigned int tokenLen, + SSLResumptionTokenInfo *token, unsigned int version); + +SECStatus SSLExp_DestroyResumptionTokenInfo(SSLResumptionTokenInfo *token); + +#define SSLResumptionTokenVersion 2 + SEC_END_PROTOS #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) diff --git a/security/nss/lib/ssl/sslinit.c b/security/nss/lib/ssl/sslinit.c index 0f38c0b57..07d57ce6e 100644 --- a/security/nss/lib/ssl/sslinit.c +++ b/security/nss/lib/ssl/sslinit.c @@ -15,6 +15,7 @@ static int ssl_isInited = 0; static PRCallOnceType ssl_init = { 0 }; +PR_STATIC_ASSERT(sizeof(unsigned long) <= sizeof(PRUint64)); PRStatus ssl_InitCallOnce(void *arg) diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c index 228834e3d..f79c23fc7 100644 --- a/security/nss/lib/ssl/sslnonce.c +++ b/security/nss/lib/ssl/sslnonce.c @@ -15,6 +15,7 @@ #include "sslimpl.h" #include "sslproto.h" #include "nssilock.h" +#include "sslencode.h" #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS) #include #endif @@ -24,12 +25,13 @@ PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */ static sslSessionID *cache = NULL; static PZLock *cacheLock = NULL; -/* sids can be in one of 4 states: +/* sids can be in one of 5 states: * * never_cached, created, but not yet put into cache. * in_client_cache, in the client cache's linked list. * in_server_cache, entry came from the server's cache file. * invalid_cache has been removed from the cache. + * in_external_cache sid comes from an external cache. */ #define LOCK_CACHE lock_cache() @@ -164,8 +166,8 @@ lock_cache(void) /* BEWARE: This function gets called for both client and server SIDs !! * If the unreferenced sid is not in the cache, Free sid and its contents. */ -static void -ssl_DestroySID(sslSessionID *sid) +void +ssl_DestroySID(sslSessionID *sid, PRBool freeIt) { SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached)); PORT_Assert(sid->references == 0); @@ -186,11 +188,8 @@ ssl_DestroySID(sslSessionID *sid) PR_DestroyRWLock(sid->u.ssl3.lock); } - if (sid->peerID != NULL) - PORT_Free((void *)sid->peerID); /* CONST */ - - if (sid->urlSvrName != NULL) - PORT_Free((void *)sid->urlSvrName); /* CONST */ + PORT_Free((void *)sid->peerID); + PORT_Free((void *)sid->urlSvrName); if (sid->peerCert) { CERT_DestroyCertificate(sid->peerCert); @@ -205,7 +204,9 @@ ssl_DestroySID(sslSessionID *sid) SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE); - PORT_ZFree(sid, sizeof(sslSessionID)); + if (freeIt) { + PORT_ZFree(sid, sizeof(sslSessionID)); + } } /* BEWARE: This function gets called for both client and server SIDs !! @@ -220,7 +221,7 @@ ssl_FreeLockedSID(sslSessionID *sid) { PORT_Assert(sid->references >= 1); if (--sid->references == 0) { - ssl_DestroySID(sid); + ssl_DestroySID(sid, PR_TRUE); } } @@ -306,6 +307,7 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, static void CacheSID(sslSessionID *sid) { + PORT_Assert(sid); PORT_Assert(sid->cached == never_cached); SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x " @@ -400,7 +402,7 @@ UncacheSID(sslSessionID *zap) /* If sid "zap" is in the cache, * removes sid from cache, and decrements reference count. * Although this function is static, it is called externally via - * ss->sec.uncache(). + * ssl_UncacheSessionID. */ static void LockAndUncacheSID(sslSessionID *zap) @@ -410,16 +412,734 @@ LockAndUncacheSID(sslSessionID *zap) UNLOCK_CACHE; } -/* choose client or server cache functions for this sslsocket. */ +SECStatus +ReadVariableFromBuffer(sslReader *reader, sslReadBuffer *readerBuffer, + uint8_t lenBytes, SECItem *dest) +{ + if (sslRead_ReadVariable(reader, lenBytes, readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer->len) { + SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer->buf, + readerBuffer->len }; + SECStatus rv = SECITEM_CopyItem(NULL, dest, &tempItem); + if (rv != SECSuccess) { + return rv; + } + } + return SECSuccess; +} + +/* Fill sid with the values from the encoded resumption token. + * sid has to be allocated. + * We don't care about locks here as this cache entry is externally stored. + */ +SECStatus +ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedToken, + PRUint32 encodedTokenLen) +{ + PORT_Assert(encodedTokenLen); + PORT_Assert(encodedToken); + PORT_Assert(sid); + if (!sid || !encodedToken || !encodedTokenLen) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (encodedToken[0] != SSLResumptionTokenVersion) { + /* Unknown token format version. */ + PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); + return SECFailure; + } + + /* These variables are used across macros. Don't use them outside. */ + sslReader reader = SSL_READER(encodedToken, encodedTokenLen); + reader.offset += 1; // We read the version already. Skip the first byte. + sslReadBuffer readerBuffer = { 0 }; + PRUint64 tmpInt = 0; + + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->lastAccessTime = (PRTime)tmpInt; + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->expirationTime = (PRTime)tmpInt; + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.locked.sessionTicket.received_timestamp = (PRTime)tmpInt; + + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.locked.sessionTicket.flags = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.locked.sessionTicket.ticket_age_add = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.locked.sessionTicket.max_early_data_size = (PRUint32)tmpInt; + + if (sslRead_ReadVariable(&reader, 3, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + PORT_Assert(!sid->peerCert); + SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf, + readerBuffer.len }; + sid->peerCert = CERT_NewTempCertificate(NULL, /* dbHandle */ + &tempItem, + NULL, PR_FALSE, PR_TRUE); + if (!sid->peerCert) { + return SECFailure; + } + } + + if (sslRead_ReadVariable(&reader, 2, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + SECITEM_AllocArray(NULL, &sid->peerCertStatus, 1); + if (!sid->peerCertStatus.items) { + return SECFailure; + } + SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf, + readerBuffer.len }; + SECITEM_CopyItem(NULL, &sid->peerCertStatus.items[0], &tempItem); + } + + if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + PORT_Assert(readerBuffer.buf); + sid->peerID = PORT_Strdup((const char *)readerBuffer.buf); + } + + if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + if (sid->urlSvrName) { + PORT_Free((void *)sid->urlSvrName); + } + PORT_Assert(readerBuffer.buf); + sid->urlSvrName = PORT_Strdup((const char *)readerBuffer.buf); + } + + if (sslRead_ReadVariable(&reader, 3, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + PORT_Assert(!sid->localCert); + SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf, + readerBuffer.len }; + sid->localCert = CERT_NewTempCertificate(NULL, /* dbHandle */ + &tempItem, + NULL, PR_FALSE, PR_TRUE); + } + + if (sslRead_ReadNumber(&reader, 8, &sid->addr.pr_s6_addr64[0]) != SECSuccess) { + return SECFailure; + } + if (sslRead_ReadNumber(&reader, 8, &sid->addr.pr_s6_addr64[1]) != SECSuccess) { + return SECFailure; + } + + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->port = (PRUint16)tmpInt; + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->version = (PRUint16)tmpInt; + + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->creationTime = (PRTime)tmpInt; + + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->authType = (SSLAuthType)tmpInt; + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->authKeyBits = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->keaType = (SSLKEAType)tmpInt; + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->keaKeyBits = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 3, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->keaGroup = (SSLNamedGroup)tmpInt; + + if (sslRead_ReadNumber(&reader, 3, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->sigScheme = (SSLSignatureScheme)tmpInt; + + if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.sessionIDLength = (PRUint8)tmpInt; + + if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (readerBuffer.len) { + PORT_Assert(readerBuffer.buf); + PORT_Memcpy(sid->u.ssl3.sessionID, readerBuffer.buf, readerBuffer.len); + } + + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.cipherSuite = (PRUint16)tmpInt; + if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.policy = (PRUint8)tmpInt; + + if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + PORT_Assert(readerBuffer.len == WRAPPED_MASTER_SECRET_SIZE); + if (readerBuffer.len != WRAPPED_MASTER_SECRET_SIZE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + PORT_Assert(readerBuffer.buf); + PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, readerBuffer.buf, + readerBuffer.len); + + if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.keys.wrapped_master_secret_len = (PRUint8)tmpInt; + if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.keys.extendedMasterSecretUsed = (PRUint8)tmpInt; + + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterWrapMech = (unsigned long)tmpInt; + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterModuleID = (unsigned long)tmpInt; + if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterSlotID = (unsigned long)tmpInt; + + if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterWrapIndex = (PRUint32)tmpInt; + if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterWrapSeries = (PRUint16)tmpInt; + + if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) { + return SECFailure; + } + sid->u.ssl3.masterValid = (char)tmpInt; + + if (ReadVariableFromBuffer(&reader, &readerBuffer, 1, + &sid->u.ssl3.srvName) != SECSuccess) { + return SECFailure; + } + if (ReadVariableFromBuffer(&reader, &readerBuffer, 2, + &sid->u.ssl3.signedCertTimestamps) != SECSuccess) { + return SECFailure; + } + if (ReadVariableFromBuffer(&reader, &readerBuffer, 1, + &sid->u.ssl3.alpnSelection) != SECSuccess) { + return SECFailure; + } + if (ReadVariableFromBuffer(&reader, &readerBuffer, 2, + &sid->u.ssl3.locked.sessionTicket.ticket) != SECSuccess) { + return SECFailure; + } + if (!sid->u.ssl3.locked.sessionTicket.ticket.len) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* At this point we must have read everything. */ + PORT_Assert(reader.offset == reader.buf.len); + if (reader.offset != reader.buf.len) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + return SECSuccess; +} + +PRBool +ssl_IsResumptionTokenValid(sslSocket *ss) +{ + PORT_Assert(ss); + sslSessionID *sid = ss->sec.ci.sid; + PORT_Assert(sid); + + // Check that the ticket didn't expire. + PRTime endTime = 0; + NewSessionTicket *ticket = &sid->u.ssl3.locked.sessionTicket; + if (ticket->ticket_lifetime_hint != 0) { + endTime = ticket->received_timestamp + + (PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_SEC); + if (endTime < ssl_TimeUsec()) { + return PR_FALSE; + } + } + + // Check that the session entry didn't expire. + if (sid->expirationTime < ssl_TimeUsec()) { + return PR_FALSE; + } + + // Check that the server name (SNI) matches the one set for this session. + // Don't use the token if there's no server name. + if (sid->urlSvrName == NULL || PORT_Strcmp(ss->url, sid->urlSvrName) != 0) { + return PR_FALSE; + } + + // This shouldn't be false, but let's check it anyway. + if (!sid->u.ssl3.keys.resumable) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* Encode a session ticket into a byte array that can be handed out to a cache. + * Needed memory in encodedToken has to be allocated according to + * *encodedTokenLen. */ +static SECStatus +ssl_EncodeResumptionToken(sslSessionID *sid, sslBuffer *encodedTokenBuf) +{ + PORT_Assert(encodedTokenBuf); + PORT_Assert(sid); + if (!sid || !sid->u.ssl3.locked.sessionTicket.ticket.len || + !encodedTokenBuf || !sid->u.ssl3.keys.resumable || !sid->urlSvrName) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Encoding format: + * 0-byte: version + * Integers are encoded according to their length. + * SECItems are prepended with a 64-bit length field followed by the bytes. + * Optional bytes are encoded as a 0-length item if not present. + */ + SECStatus rv = sslBuffer_AppendNumber(encodedTokenBuf, + SSLResumptionTokenVersion, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->lastAccessTime, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->expirationTime, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // session ticket + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.received_timestamp, + 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint, + 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.flags, + 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.ticket_age_add, + 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.max_early_data_size, + 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->peerCert->derCert.data, + sid->peerCert->derCert.len, 3); + if (rv != SECSuccess) { + return SECFailure; + } + + if (sid->peerCertStatus.len > 1) { + /* This is not implemented so it shouldn't happen. + * If it gets implemented, this has to change. + */ + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (sid->peerCertStatus.len == 1 && sid->peerCertStatus.items[0].len) { + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->peerCertStatus.items[0].data, + sid->peerCertStatus.items[0].len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + } else { + rv = sslBuffer_AppendVariable(encodedTokenBuf, NULL, 0, 2); + if (rv != SECSuccess) { + return SECFailure; + } + } + + PRUint64 len = sid->peerID ? strlen(sid->peerID) : 0; + if (len > PR_UINT8_MAX) { + // This string really shouldn't be that long. + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + rv = sslBuffer_AppendVariable(encodedTokenBuf, + (const unsigned char *)sid->peerID, len, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + len = sid->urlSvrName ? strlen(sid->urlSvrName) : 0; + if (!len) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (len > PR_UINT8_MAX) { + // This string really shouldn't be that long. + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + rv = sslBuffer_AppendVariable(encodedTokenBuf, + (const unsigned char *)sid->urlSvrName, + len, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + if (sid->localCert) { + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->localCert->derCert.data, + sid->localCert->derCert.len, 3); + if (rv != SECSuccess) { + return SECFailure; + } + } else { + rv = sslBuffer_AppendVariable(encodedTokenBuf, NULL, 0, 3); + if (rv != SECSuccess) { + return SECFailure; + } + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->addr.pr_s6_addr64[0], 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->addr.pr_s6_addr64[1], 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->port, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->version, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->creationTime, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->authType, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->authKeyBits, 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaType, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaKeyBits, 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaGroup, 3); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->sigScheme, 3); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.sessionIDLength, 1); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->u.ssl3.sessionID, + SSL3_SESSIONID_BYTES, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.cipherSuite, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.policy, 1); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->u.ssl3.keys.wrapped_master_secret, + WRAPPED_MASTER_SECRET_SIZE, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.keys.wrapped_master_secret_len, + 1); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, + sid->u.ssl3.keys.extendedMasterSecretUsed, + 1); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapMech, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterModuleID, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterSlotID, 8); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapIndex, 4); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapSeries, 2); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterValid, 1); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->u.ssl3.srvName.data, + sid->u.ssl3.srvName.len, 1); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->u.ssl3.signedCertTimestamps.data, + sid->u.ssl3.signedCertTimestamps.len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->u.ssl3.alpnSelection.data, + sid->u.ssl3.alpnSelection.len, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + PORT_Assert(sid->u.ssl3.locked.sessionTicket.ticket.len > 1); + rv = sslBuffer_AppendVariable(encodedTokenBuf, + sid->u.ssl3.locked.sessionTicket.ticket.data, + sid->u.ssl3.locked.sessionTicket.ticket.len, + 2); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + void -ssl_ChooseSessionIDProcs(sslSecurityInfo *sec) +ssl_CacheExternalToken(sslSocket *ss) { + PORT_Assert(ss); + sslSessionID *sid = ss->sec.ci.sid; + PORT_Assert(sid); + PORT_Assert(sid->cached == never_cached); + PORT_Assert(ss->resumptionTokenCallback); + + SSL_TRC(8, ("SSL [%d]: Cache External: sid=0x%x cached=%d " + "addr=0x%08x%08x%08x%08x port=0x%04x time=%x cached=%d", + ss->fd, + sid, sid->cached, sid->addr.pr_s6_addr32[0], + sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2], + sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime, + sid->cached)); + + /* This is only available for stateless resumption. */ + if (sid->u.ssl3.locked.sessionTicket.ticket.data == NULL) { + return; + } + + /* Don't export token if the session used client authentication. */ + if (sid->u.ssl3.clAuthValid) { + return; + } + + if (!sid->creationTime) { + sid->lastAccessTime = sid->creationTime = ssl_TimeUsec(); + } + if (!sid->expirationTime) { + sid->expirationTime = sid->creationTime + ssl3_sid_timeout; + } + + sslBuffer encodedToken = SSL_BUFFER_EMPTY; + + if (ssl_EncodeResumptionToken(sid, &encodedToken) != SECSuccess) { + SSL_TRC(3, ("SSL [%d]: encoding resumption token failed", ss->fd)); + return; + } + PORT_Assert(SSL_BUFFER_LEN(&encodedToken) > 0); + PRINT_BUF(40, (ss, "SSL: encoded resumption token", + SSL_BUFFER_BASE(&encodedToken), + SSL_BUFFER_LEN(&encodedToken))); + ss->resumptionTokenCallback(ss->fd, SSL_BUFFER_BASE(&encodedToken), + SSL_BUFFER_LEN(&encodedToken), + ss->resumptionTokenContext); + + sslBuffer_Clear(&encodedToken); +} + +void +ssl_CacheSessionID(sslSocket *ss) +{ + sslSecurityInfo *sec = &ss->sec; + PORT_Assert(sec); + + if (sec->ci.sid && !sec->ci.sid->u.ssl3.keys.resumable) { + return; + } + + if (!ss->sec.isServer && ss->resumptionTokenCallback) { + ssl_CacheExternalToken(ss); + return; + } + + PORT_Assert(!ss->resumptionTokenCallback); if (sec->isServer) { - sec->cache = ssl_sid_cache; - sec->uncache = ssl_sid_uncache; - } else { - sec->cache = CacheSID; - sec->uncache = LockAndUncacheSID; + ssl_ServerCacheSessionID(sec->ci.sid); + return; + } + + CacheSID(sec->ci.sid); +} + +void +ssl_UncacheSessionID(sslSocket *ss) +{ + if (ss->opt.noCache) { + return; + } + + sslSecurityInfo *sec = &ss->sec; + PORT_Assert(sec); + + if (sec->ci.sid) { + if (sec->isServer) { + ssl_ServerUncacheSessionID(sec->ci.sid); + } else if (!ss->resumptionTokenCallback) { + LockAndUncacheSID(sec->ci.sid); + } } } diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c index 3f7060f22..f09ec067c 100644 --- a/security/nss/lib/ssl/sslsecur.c +++ b/security/nss/lib/ssl/sslsecur.c @@ -599,9 +599,6 @@ ssl_CopySecurityInfo(sslSocket *ss, sslSocket *os) if (os->sec.peerCert && !ss->sec.peerCert) goto loser; - ss->sec.cache = os->sec.cache; - ss->sec.uncache = os->sec.uncache; - return SECSuccess; loser: @@ -1159,7 +1156,7 @@ SSL_InvalidateSession(PRFileDesc *fd) ssl_GetSSL3HandshakeLock(ss); if (ss->sec.ci.sid) { - ss->sec.uncache(ss->sec.ci.sid); + ssl_UncacheSessionID(ss); rv = SECSuccess; } diff --git a/security/nss/lib/ssl/sslsnce.c b/security/nss/lib/ssl/sslsnce.c index 279f3c015..d7abb3dc3 100644 --- a/security/nss/lib/ssl/sslsnce.c +++ b/security/nss/lib/ssl/sslsnce.c @@ -495,12 +495,6 @@ ConvertToSID(sidCacheEntry *from, PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength); - /* the portions of the SID that are only restored on the client - * are set to invalid values on the server. - */ - to->u.ssl3.clientWriteKey = NULL; - to->u.ssl3.serverWriteKey = NULL; - to->urlSvrName = NULL; to->u.ssl3.masterModuleID = (SECMODModuleID)-1; /* invalid value */ @@ -735,9 +729,11 @@ ServerSessionIDLookup(const PRIPv6Addr *addr, /* ** Place a sid into the cache, if it isn't already there. */ -static void -ServerSessionIDCache(sslSessionID *sid) +void +ssl_ServerCacheSessionID(sslSessionID *sid) { + PORT_Assert(sid); + sidCacheEntry sce; PRUint32 now = 0; cacheDesc *cache = &globalCache; @@ -800,8 +796,8 @@ ServerSessionIDCache(sslSessionID *sid) ** Although this is static, it is called from ssl via global function pointer ** ssl_sid_uncache. This invalidates the referenced cache entry. */ -static void -ServerSessionIDUncache(sslSessionID *sid) +void +ssl_ServerUncacheSessionID(sslSessionID *sid) { cacheDesc *cache = &globalCache; PRUint8 *sessionID; @@ -1172,8 +1168,6 @@ ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache, } ssl_sid_lookup = ServerSessionIDLookup; - ssl_sid_cache = ServerSessionIDCache; - ssl_sid_uncache = ServerSessionIDUncache; return SECSuccess; } @@ -1356,8 +1350,6 @@ SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char *envString) ssl_InitSessionCacheLocks(PR_FALSE); ssl_sid_lookup = ServerSessionIDLookup; - ssl_sid_cache = ServerSessionIDCache; - ssl_sid_uncache = ServerSessionIDUncache; if (!envString) { envString = PR_GetEnvSecure(envVarName); diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c index 4893cb9f9..e08d5e232 100644 --- a/security/nss/lib/ssl/sslsock.c +++ b/security/nss/lib/ssl/sslsock.c @@ -53,34 +53,35 @@ static const sslSocketOps ssl_secure_ops = { /* SSL. */ ** default settings for socket enables */ static sslOptions ssl_defaults = { - { siBuffer, NULL, 0 }, /* nextProtoNego */ - PR_TRUE, /* useSecurity */ - PR_FALSE, /* useSocks */ - PR_FALSE, /* requestCertificate */ - 2, /* requireCertificate */ - PR_FALSE, /* handshakeAsClient */ - PR_FALSE, /* handshakeAsServer */ - PR_FALSE, /* noCache */ - PR_FALSE, /* fdx */ - PR_TRUE, /* detectRollBack */ - PR_FALSE, /* noLocks */ - PR_FALSE, /* enableSessionTickets */ - PR_FALSE, /* enableDeflate */ - 2, /* enableRenegotiation (default: requires extension) */ - PR_FALSE, /* requireSafeNegotiation */ - PR_FALSE, /* enableFalseStart */ - PR_TRUE, /* cbcRandomIV */ - PR_FALSE, /* enableOCSPStapling */ - PR_FALSE, /* enableNPN */ - PR_TRUE, /* enableALPN */ - PR_TRUE, /* reuseServerECDHEKey */ - PR_FALSE, /* enableFallbackSCSV */ - PR_TRUE, /* enableServerDhe */ - PR_FALSE, /* enableExtendedMS */ - PR_FALSE, /* enableSignedCertTimestamps */ - PR_FALSE, /* requireDHENamedGroups */ - PR_FALSE, /* enable0RttData */ - PR_FALSE /* enableTls13CompatMode */ + .nextProtoNego = { siBuffer, NULL, 0 }, + .maxEarlyDataSize = 1 << 16, + .useSecurity = PR_TRUE, + .useSocks = PR_FALSE, + .requestCertificate = PR_FALSE, + .requireCertificate = SSL_REQUIRE_FIRST_HANDSHAKE, + .handshakeAsClient = PR_FALSE, + .handshakeAsServer = PR_FALSE, + .noCache = PR_FALSE, + .fdx = PR_FALSE, + .detectRollBack = PR_TRUE, + .noLocks = PR_FALSE, + .enableSessionTickets = PR_FALSE, + .enableDeflate = PR_FALSE, + .enableRenegotiation = SSL_RENEGOTIATE_REQUIRES_XTN, + .requireSafeNegotiation = PR_FALSE, + .enableFalseStart = PR_FALSE, + .cbcRandomIV = PR_TRUE, + .enableOCSPStapling = PR_FALSE, + .enableNPN = PR_FALSE, + .enableALPN = PR_TRUE, + .reuseServerECDHEKey = PR_TRUE, + .enableFallbackSCSV = PR_FALSE, + .enableServerDhe = PR_TRUE, + .enableExtendedMS = PR_FALSE, + .enableSignedCertTimestamps = PR_FALSE, + .requireDHENamedGroups = PR_FALSE, + .enable0RttData = PR_FALSE, + .enableTls13CompatMode = PR_FALSE }; /* @@ -104,8 +105,6 @@ static SSLVersionRange versions_defaults_datagram = { (variant == ssl_variant_stream ? NSS_TLS_VERSION_MAX_POLICY : NSS_DTLS_VERSION_MAX_POLICY) sslSessionIDLookupFunc ssl_sid_lookup; -sslSessionIDCacheFunc ssl_sid_cache; -sslSessionIDUncacheFunc ssl_sid_uncache; static PRDescIdentity ssl_layer_id; @@ -356,6 +355,8 @@ ssl_DupSocket(sslSocket *os) os->namedGroupPreferences, sizeof(ss->namedGroupPreferences)); ss->additionalShares = os->additionalShares; + ss->resumptionTokenCallback = os->resumptionTokenCallback; + ss->resumptionTokenContext = os->resumptionTokenContext; /* Create security data */ rv = ssl_CopySecurityInfo(ss, os); @@ -1252,6 +1253,18 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val) return SECSuccess; } +SECStatus +SSLExp_SetMaxEarlyDataSize(PRFileDesc *fd, PRUint32 size) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; /* Error code already set. */ + } + + ss->opt.maxEarlyDataSize = size; + return SECSuccess; +} + /* function tells us if the cipher suite is one that we no longer support. */ static PRBool ssl_IsRemovedCipherSuite(PRInt32 suite) @@ -3932,7 +3945,12 @@ struct { EXP(InstallExtensionHooks), EXP(KeyUpdate), EXP(SendSessionTicket), + EXP(SetMaxEarlyDataSize), EXP(SetupAntiReplay), + EXP(SetResumptionTokenCallback), + EXP(SetResumptionToken), + EXP(GetResumptionTokenInfo), + EXP(DestroyResumptionTokenInfo), #endif { "", NULL } }; @@ -3967,3 +3985,156 @@ ssl_ClearPRCList(PRCList *list, void (*f)(void *)) PORT_Free(cursor); } } + +/* Experimental APIs for session cache handling. */ + +SECStatus +SSLExp_SetResumptionTokenCallback(PRFileDesc *fd, + SSLResumptionTokenCallback cb, + void *ctx) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetResumptionTokenCallback", + SSL_GETPID(), fd)); + return SECFailure; + } + + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + ss->resumptionTokenCallback = cb; + ss->resumptionTokenContext = ctx; + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return SECSuccess; +} + +SECStatus +SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token, + unsigned int len) +{ + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetResumptionToken", + SSL_GETPID(), fd)); + return SECFailure; + } + + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + + if (ss->firstHsDone || ss->ssl3.hs.ws != idle_handshake || + ss->sec.isServer || len == 0 || !token) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto done; + } + + // We override any previously set session. + if (ss->sec.ci.sid) { + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = NULL; + } + + PRINT_BUF(50, (ss, "incoming resumption token", token, len)); + + ss->sec.ci.sid = ssl3_NewSessionID(ss, PR_FALSE); + if (!ss->sec.ci.sid) { + goto done; + } + + /* Populate NewSessionTicket values */ + SECStatus rv = ssl_DecodeResumptionToken(ss->sec.ci.sid, token, len); + if (rv != SECSuccess) { + // If decoding fails, we assume the token is bad. + PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = NULL; + goto done; + } + + // Make sure that the token is valid. + if (!ssl_IsResumptionTokenValid(ss)) { + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = NULL; + PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); + goto done; + } + + /* Use the sid->cached as marker that this is from an external cache and + * we don't have to look up anything in the NSS internal cache. */ + ss->sec.ci.sid->cached = in_external_cache; + // This has to be 2 to not free this in sendClientHello. + ss->sec.ci.sid->references = 2; + ss->sec.ci.sid->lastAccessTime = ssl_TimeSec(); + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + return SECSuccess; + +done: + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return SECFailure; +} + +SECStatus +SSLExp_DestroyResumptionTokenInfo(SSLResumptionTokenInfo *token) +{ + if (!token) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (token->peerCert) { + CERT_DestroyCertificate(token->peerCert); + } + PORT_Free(token->alpnSelection); + PORT_Memset(token, 0, token->length); + return SECSuccess; +} + +SECStatus +SSLExp_GetResumptionTokenInfo(const PRUint8 *tokenData, unsigned int tokenLen, + SSLResumptionTokenInfo *tokenOut, PRUintn len) +{ + if (!tokenData || !tokenOut || !tokenLen || + len > sizeof(SSLResumptionTokenInfo)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + sslSessionID sid = { 0 }; + SSLResumptionTokenInfo token; + + /* Populate sid values */ + if (ssl_DecodeResumptionToken(&sid, tokenData, tokenLen) != SECSuccess) { + // If decoding fails, we assume the token is bad. + PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); + return SECFailure; + } + + token.peerCert = CERT_DupCertificate(sid.peerCert); + + token.alpnSelectionLen = sid.u.ssl3.alpnSelection.len; + token.alpnSelection = PORT_ZAlloc(token.alpnSelectionLen); + if (!token.alpnSelection) { + return SECFailure; + } + PORT_Memcpy(token.alpnSelection, sid.u.ssl3.alpnSelection.data, + token.alpnSelectionLen); + + if (sid.u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) { + token.maxEarlyDataSize = + sid.u.ssl3.locked.sessionTicket.max_early_data_size; + } else { + token.maxEarlyDataSize = 0; + } + + token.length = PR_MIN(sizeof(SSLResumptionTokenInfo), len); + PORT_Memcpy(tokenOut, &token, token.length); + + ssl_DestroySID(&sid, PR_FALSE); + return SECSuccess; +} diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h index ce8f6e281..e2b80fb43 100644 --- a/security/nss/lib/ssl/sslt.h +++ b/security/nss/lib/ssl/sslt.h @@ -123,11 +123,14 @@ typedef enum { ssl_sig_ecdsa_secp256r1_sha256 = 0x0403, ssl_sig_ecdsa_secp384r1_sha384 = 0x0503, ssl_sig_ecdsa_secp521r1_sha512 = 0x0603, - ssl_sig_rsa_pss_sha256 = 0x0804, - ssl_sig_rsa_pss_sha384 = 0x0805, - ssl_sig_rsa_pss_sha512 = 0x0806, + ssl_sig_rsa_pss_rsae_sha256 = 0x0804, + ssl_sig_rsa_pss_rsae_sha384 = 0x0805, + ssl_sig_rsa_pss_rsae_sha512 = 0x0806, ssl_sig_ed25519 = 0x0807, ssl_sig_ed448 = 0x0808, + ssl_sig_rsa_pss_pss_sha256 = 0x0809, + ssl_sig_rsa_pss_pss_sha384 = 0x080a, + ssl_sig_rsa_pss_pss_sha512 = 0x080b, ssl_sig_dsa_sha1 = 0x0202, ssl_sig_dsa_sha256 = 0x0402, @@ -143,20 +146,25 @@ typedef enum { ssl_sig_rsa_pkcs1_sha1md5 = 0x10101, } SSLSignatureScheme; +/* Deprecated names maintained only for source compatibility. */ +#define ssl_sig_rsa_pss_sha256 ssl_sig_rsa_pss_rsae_sha256 +#define ssl_sig_rsa_pss_sha384 ssl_sig_rsa_pss_rsae_sha384 +#define ssl_sig_rsa_pss_sha512 ssl_sig_rsa_pss_rsae_sha512 + /* ** SSLAuthType describes the type of key that is used to authenticate a ** connection. That is, the type of key in the end-entity certificate. */ typedef enum { ssl_auth_null = 0, - ssl_auth_rsa_decrypt = 1, /* static RSA */ + ssl_auth_rsa_decrypt = 1, /* RSA key exchange. */ ssl_auth_dsa = 2, ssl_auth_kea = 3, /* unused */ ssl_auth_ecdsa = 4, - ssl_auth_ecdh_rsa = 5, /* ECDH cert with an RSA signature */ - ssl_auth_ecdh_ecdsa = 6, /* ECDH cert with an ECDSA signature */ - ssl_auth_rsa_sign = 7, /* RSA PKCS#1.5 signing */ - ssl_auth_rsa_pss = 8, + ssl_auth_ecdh_rsa = 5, /* ECDH cert with an RSA signature. */ + ssl_auth_ecdh_ecdsa = 6, /* ECDH cert with an ECDSA signature. */ + ssl_auth_rsa_sign = 7, /* RSA signing with an rsaEncryption key. */ + ssl_auth_rsa_pss = 8, /* RSA signing with a PSS key. */ ssl_auth_psk = 9, ssl_auth_tls13_any = 10, ssl_auth_size /* number of authentication types */ diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c index 1fecaf3f8..c06acc83a 100644 --- a/security/nss/lib/ssl/tls13con.c +++ b/security/nss/lib/ssl/tls13con.c @@ -462,7 +462,7 @@ tls13_SetupClientHello(sslSocket *ss) if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); SSL_AtomicIncrementLong(&ssl3stats->sch_sid_cache_not_ok); - ss->sec.uncache(ss->sec.ci.sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(ss->sec.ci.sid); ss->sec.ci.sid = NULL; return SECFailure; @@ -1426,9 +1426,9 @@ ssl_SignatureSchemeToAuthType(SSLSignatureScheme scheme) case ssl_sig_rsa_pkcs1_sha384: case ssl_sig_rsa_pkcs1_sha512: /* We report PSS signatures as being just RSA signatures. */ - case ssl_sig_rsa_pss_sha256: - case ssl_sig_rsa_pss_sha384: - case ssl_sig_rsa_pss_sha512: + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_rsae_sha512: return ssl_auth_rsa_sign; case ssl_sig_ecdsa_secp256r1_sha256: case ssl_sig_ecdsa_secp384r1_sha384: @@ -1719,7 +1719,7 @@ tls13_HandleClientHelloPart2(sslSocket *ss, } if (hrr) { if (sid) { /* Free the sid. */ - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); } PORT_Assert(ss->ssl3.hs.helloRetry); @@ -1769,8 +1769,7 @@ tls13_HandleClientHelloPart2(sslSocket *ss, } else { if (sid) { /* we had a sid, but it's no longer valid, free it */ SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok); - if (ss->sec.uncache) - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); sid = NULL; } @@ -1830,7 +1829,7 @@ tls13_HandleClientHelloPart2(sslSocket *ss, if (sid) { /* We had a sid, but it's no longer valid, free it. */ SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok); - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); } else { SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_misses); @@ -1866,7 +1865,7 @@ tls13_HandleClientHelloPart2(sslSocket *ss, loser: if (sid) { - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(sid); } return SECFailure; @@ -2539,7 +2538,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss) } if (sid->cached == in_client_cache) { /* If we tried to resume and failed, let's not try again. */ - ss->sec.uncache(sid); + ssl_UncacheSessionID(ss); } } @@ -4418,8 +4417,6 @@ tls13_SendClientSecondRound(sslSocket *ss) * } NewSessionTicket; */ -PRUint32 ssl_max_early_data_size = (2 << 16); /* Arbitrary limit. */ - static SECStatus tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken, unsigned int appTokenLen) @@ -4521,7 +4518,7 @@ tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken, if (rv != SECSuccess) goto loser; - rv = ssl3_AppendHandshakeNumber(ss, ssl_max_early_data_size, 4); + rv = ssl3_AppendHandshakeNumber(ss, ss->opt.maxEarlyDataSize, 4); if (rv != SECSuccess) goto loser; } @@ -4681,7 +4678,7 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length) } /* Destroy the old SID. */ - ss->sec.uncache(ss->sec.ci.sid); + ssl_UncacheSessionID(ss); ssl_FreeSID(ss->sec.ci.sid); ss->sec.ci.sid = sid; } @@ -4707,7 +4704,7 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length) } /* Cache the session. */ - ss->sec.cache(ss->sec.ci.sid); + ssl_CacheSessionID(ss); } return SECSuccess; @@ -4772,9 +4769,6 @@ tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message) /* Return "disallowed" if the message mask bit isn't set. */ if (!(_M(message) & KnownExtensions[i].messages)) { - SSL_TRC(3, ("%d: TLS13: unexpected extension %d in message %d", - SSL_GETPID(), extension, message)); - return tls13_extension_disallowed; } diff --git a/security/nss/lib/ssl/tls13hashstate.c b/security/nss/lib/ssl/tls13hashstate.c index e3232f524..cc0ed286b 100644 --- a/security/nss/lib/ssl/tls13hashstate.c +++ b/security/nss/lib/ssl/tls13hashstate.c @@ -88,36 +88,37 @@ tls13_RecoverHashState(sslSocket *ss, { SECStatus rv; unsigned char plaintext[1024]; - SECItem ptItem = { siBuffer, plaintext, 0 }; + unsigned int plaintextLen = 0; sslBuffer messageBuf = SSL_BUFFER_EMPTY; - PRUint32 sentinel; - PRUint32 cipherSuite; - PRUint32 group; + PRUint64 sentinel; + PRUint64 cipherSuite; + PRUint64 group; const sslNamedGroupDef *selectedGroup; - PRUint32 appTokenLen; - PRUint8 *appToken; + PRUint64 appTokenLen; rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen, - ptItem.data, &ptItem.len, sizeof(plaintext)); + plaintext, &plaintextLen, sizeof(plaintext)); if (rv != SECSuccess) { return SECFailure; } + sslReader reader = SSL_READER(plaintext, plaintextLen); + /* Should start with 0xff. */ - rv = ssl3_ConsumeNumberFromItem(&ptItem, &sentinel, 1); + rv = sslRead_ReadNumber(&reader, 1, &sentinel); if ((rv != SECSuccess) || (sentinel != 0xff)) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; } /* The cipher suite should be the same or there are some shenanigans. */ - rv = ssl3_ConsumeNumberFromItem(&ptItem, &cipherSuite, 2); + rv = sslRead_ReadNumber(&reader, 2, &cipherSuite); if (rv != SECSuccess) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; } /* The named group, if any. */ - rv = ssl3_ConsumeNumberFromItem(&ptItem, &group, 2); + rv = sslRead_ReadNumber(&reader, 2, &group); if (rv != SECSuccess) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; @@ -126,7 +127,7 @@ tls13_RecoverHashState(sslSocket *ss, /* Application token. */ PORT_Assert(ss->xtnData.applicationToken.len == 0); - rv = ssl3_ConsumeNumberFromItem(&ptItem, &appTokenLen, 2); + rv = sslRead_ReadNumber(&reader, 2, &appTokenLen); if (rv != SECSuccess) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; @@ -137,15 +138,18 @@ tls13_RecoverHashState(sslSocket *ss, return SECFailure; } ss->xtnData.applicationToken.len = appTokenLen; - rv = ssl3_ConsumeFromItem(&ptItem, &appToken, appTokenLen); + sslReadBuffer appTokenReader = { 0 }; + rv = sslRead_Read(&reader, appTokenLen, &appTokenReader); if (rv != SECSuccess) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; } - PORT_Memcpy(ss->xtnData.applicationToken.data, appToken, appTokenLen); + PORT_Assert(appTokenReader.len == appTokenLen); + PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen); /* The remainder is the hash. */ - if (ptItem.len != tls13_GetHashSize(ss)) { + unsigned int hashLen = SSL_READER_REMAINING(&reader); + if (hashLen != tls13_GetHashSize(ss)) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); return SECFailure; } @@ -153,7 +157,7 @@ tls13_RecoverHashState(sslSocket *ss, /* Now reinject the message. */ SSL_ASSERT_HASHES_EMPTY(ss); rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0, - ptItem.data, ptItem.len); + SSL_READER_CURRENT(&reader), hashLen); if (rv != SECSuccess) { return SECFailure; } -- cgit v1.2.3