diff options
-rw-r--r-- | automation/abi-check/expected-report-libnspr4.so.txt | 4 | ||||
-rw-r--r-- | automation/abi-check/expected-report-libssl3.so.txt | 4 | ||||
-rw-r--r-- | cmd/tstclnt/tstclnt.c | 72 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_auth_unittest.cc | 274 | ||||
-rw-r--r-- | gtests/ssl_gtest/tls_agent.cc | 124 | ||||
-rw-r--r-- | gtests/ssl_gtest/tls_agent.h | 20 | ||||
-rw-r--r-- | lib/ssl/authcert.c | 10 | ||||
-rw-r--r-- | lib/ssl/dtlscon.c | 4 | ||||
-rw-r--r-- | lib/ssl/ssl.def | 6 | ||||
-rw-r--r-- | lib/ssl/ssl.h | 58 | ||||
-rw-r--r-- | lib/ssl/ssl3con.c | 181 | ||||
-rw-r--r-- | lib/ssl/sslimpl.h | 13 | ||||
-rw-r--r-- | lib/ssl/sslsecur.c | 37 | ||||
-rw-r--r-- | lib/ssl/sslsock.c | 13 | ||||
-rw-r--r-- | lib/ssl/tls13con.c | 114 |
15 files changed, 724 insertions, 210 deletions
diff --git a/automation/abi-check/expected-report-libnspr4.so.txt b/automation/abi-check/expected-report-libnspr4.so.txt index e69de29bb..7f09b9b89 100644 --- a/automation/abi-check/expected-report-libnspr4.so.txt +++ b/automation/abi-check/expected-report-libnspr4.so.txt @@ -0,0 +1,4 @@ + +1 Added function: + + 'function PRStatus PR_GetPrefLoopbackAddrInfo(PRNetAddr*, PRUint16)' {PR_GetPrefLoopbackAddrInfo} diff --git a/automation/abi-check/expected-report-libssl3.so.txt b/automation/abi-check/expected-report-libssl3.so.txt index e69de29bb..b31bca112 100644 --- a/automation/abi-check/expected-report-libssl3.so.txt +++ b/automation/abi-check/expected-report-libssl3.so.txt @@ -0,0 +1,4 @@ + +1 Added function: + + 'function SECStatus SSL_ClientCertCallbackComplete(PRFileDesc*, SECStatus, SECKEYPrivateKey*, CERTCertificate*)' {SSL_ClientCertCallbackComplete@@NSS_3.80} diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c index 453842b16..cbf824ec1 100644 --- a/cmd/tstclnt/tstclnt.c +++ b/cmd/tstclnt/tstclnt.c @@ -268,7 +268,7 @@ PrintParameterUsage() fprintf(stderr, "%-20s Send TLS_FALLBACK_SCSV\n", "-K"); fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S"); fprintf(stderr, "%-20s Client speaks first. \n", "-f"); - fprintf(stderr, "%-20s Use synchronous certificate validation\n", "-O"); + fprintf(stderr, "%-20s Use synchronous certificate selection & validation\n", "-O"); fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o"); fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s"); fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v"); @@ -735,6 +735,43 @@ ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, return SECWouldBlock; } +struct clientCertAsyncParamsStr { + void *arg; /* The nickname used for selection, not owned */ + struct CERTDistNamesStr *caNames; /* CA Names specified by Server, owned. */ +}; + +/* tstclnt can only have a single handshake in progress at any instant. */ +PRBool clientCertAsyncSelect = PR_TRUE; /* Async by default */ +PRBool clientCertIsBlocked = PR_FALSE; /* Whether we waiting to finish ClientAuth */ +struct clientCertAsyncParamsStr *clientCertParams = NULL; + +SECStatus +own_CompleteClientAuthData(PRFileDesc *fd, struct clientCertAsyncParamsStr *args) +{ + SECStatus rv; + CERTCertificate *pRetCert = NULL; + SECKEYPrivateKey *pRetKey = NULL; + rv = NSS_GetClientAuthData(args->arg, fd, args->caNames, &pRetCert, &pRetKey); + if (rv != SECSuccess) { + fprintf(stderr, "Failed to load a suitable client certificate \n"); + } + return SSL_ClientCertCallbackComplete(fd, rv, pRetKey, pRetCert); +} + +SECStatus +restartHandshakeAfterClientCertIfNeeded(PRFileDesc *fd) +{ + if (!clientCertIsBlocked) { + return SECFailure; + } + clientCertIsBlocked = PR_FALSE; + own_CompleteClientAuthData(fd, clientCertParams); + CERT_FreeDistNames(clientCertParams->caNames); + PORT_Free(clientCertParams); + clientCertParams = NULL; + return SECSuccess; +} + SECStatus own_GetClientAuthData(void *arg, PRFileDesc *socket, @@ -742,6 +779,26 @@ own_GetClientAuthData(void *arg, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { + if (clientCertAsyncSelect) { + PR_ASSERT(!clientCertIsBlocked); + PR_ASSERT(!clientCertParams); + + clientCertIsBlocked = PR_TRUE; + clientCertParams = PORT_Alloc(sizeof(struct clientCertAsyncParamsStr)); + if (!clientCertParams) { + fprintf(stderr, "Unable to allocate buffer for client cert callback\n"); + exit(1); + } + + clientCertParams->arg = arg; + clientCertParams->caNames = caNames ? CERT_DupDistNames(caNames) : NULL; + if (caNames && !clientCertParams->caNames) { + fprintf(stderr, "Unable to allocate buffer for client cert callback\n"); + exit(1); + } + return SECWouldBlock; + } + if (verbose > 1) { SECStatus rv; fprintf(stderr, "Server requested Client Authentication\n"); @@ -944,7 +1001,7 @@ restartHandshakeAfterServerCertIfNeeded(PRFileDesc *fd, SECStatus rv; PRErrorCode error = 0; - if (!serverCertAuth->isPaused) + if (!serverCertAuth->isPaused || clientCertIsBlocked) return SECSuccess; FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n", @@ -1065,6 +1122,11 @@ writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb) return EXIT_CODE_HANDSHAKE_FAILED; } + rv = restartHandshakeAfterClientCertIfNeeded(s); + if (rv == SECSuccess) { + continue; + } + pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; pollDesc.out_flags = 0; FPRINTF(stderr, @@ -1715,6 +1777,11 @@ run() goto done; } + rv = restartHandshakeAfterClientCertIfNeeded(s); + if (rv == SECSuccess) { + continue; + } + pollset[SSOCK_FD].out_flags = 0; pollset[STDIN_FD].out_flags = 0; @@ -1912,6 +1979,7 @@ main(int argc, char **argv) break; case 'O': + clientCertAsyncSelect = PR_FALSE; serverCertAuth.shouldPause = PR_FALSE; break; diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc index 925b82721..c71c0062e 100644 --- a/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -169,13 +169,6 @@ TEST_P(TlsConnectGenericPre13, ServerAuthRejectAsync) { server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning); } -TEST_P(TlsConnectGeneric, ClientAuth) { - client_->SetupClientAuth(); - server_->RequestClientAuth(true); - Connect(); - CheckKeys(); -} - class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter { public: TlsCertificateRequestContextRecorder(const std::shared_ptr<TlsAgent>& a, @@ -204,16 +197,135 @@ class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter { bool filtered_; }; -// All stream only tests; DTLS isn't supported yet. +using ClientAuthParam = + std::tuple<SSLProtocolVariant, uint16_t, ClientAuthCallbackType>; + +class TlsConnectClientAuth + : public TlsConnectTestBase, + public testing::WithParamInterface<ClientAuthParam> { + public: + TlsConnectClientAuth() + : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {} +}; + +// Wrapper classes for tests that target specific versions + +class TlsConnectClientAuth13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuth12 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuthStream13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuthPre13 : public TlsConnectClientAuth {}; + +class TlsConnectClientAuth12Plus : public TlsConnectClientAuth {}; + +std::string getClientAuthTestName( + testing::TestParamInfo<ClientAuthParam> info) { + auto param = info.param; + auto variant = std::get<0>(param); + auto version = std::get<1>(param); + auto callback_type = std::get<2>(param); + + std::string output = std::string(); + switch (variant) { + case ssl_variant_stream: + output.append("TLS"); + break; + case ssl_variant_datagram: + output.append("DTLS"); + break; + } + output.append(VersionString(version).replace(1, 1, "")); + switch (callback_type) { + case ClientAuthCallbackType::kAsyncImmediate: + output.append("AsyncImmediate"); + break; + case ClientAuthCallbackType::kAsyncDelay: + output.append("AsyncDelay"); + break; + case ClientAuthCallbackType::kSync: + output.append("Sync"); + break; + case ClientAuthCallbackType::kNone: + output.append("None"); + break; + } + return output; +} + +auto kClientAuthCallbacks = testing::Values( + ClientAuthCallbackType::kAsyncImmediate, + ClientAuthCallbackType::kAsyncDelay, ClientAuthCallbackType::kSync, + ClientAuthCallbackType::kNone); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthGenericStream, TlsConnectClientAuth, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsVAll, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthGenericDatagram, TlsConnectClientAuth, + testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, + TlsConnectTestBase::kTlsV11Plus, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth13, TlsConnectClientAuth13, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV13, + kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuth13, TlsConnectClientAuthStream13, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsV13, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth12, TlsConnectClientAuth12, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12, + kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthPre13Stream, TlsConnectClientAuthPre13, + testing::Combine(TlsConnectTestBase::kTlsVariantsStream, + TlsConnectTestBase::kTlsV10ToV12, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P( + ClientAuthPre13Datagram, TlsConnectClientAuthPre13, + testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram, + TlsConnectTestBase::kTlsV11V12, kClientAuthCallbacks), + getClientAuthTestName); + +INSTANTIATE_TEST_SUITE_P(ClientAuth12Plus, TlsConnectClientAuth12Plus, + testing::Combine(TlsConnectTestBase::kTlsVariantsAll, + TlsConnectTestBase::kTlsV12Plus, + kClientAuthCallbacks), + getClientAuthTestName); + +TEST_P(TlsConnectClientAuth, ClientAuth) { + EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); + server_->RequestClientAuth(true); + Connect(); + CheckKeys(); + client_->CheckClientAuthCompleted(); +} + +// All stream only tests; PostHandshakeAuth isn't supported for DTLS. -TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuth) { EnsureTlsSetup(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>( server_, kTlsHandshakeCertificateRequest); auto capture_certificate = MakeTlsFilter<TlsCertificateRequestContextRecorder>( client_, kTlsHandshakeCertificate); - client_->SetupClientAuth(); client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); size_t called = 0; server_->SetAuthCertificateCallback( @@ -232,11 +344,15 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) { // handled on both client and server. server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); client_->SendData(50); server_->ReadBytes(50); + EXPECT_EQ(1U, called); - EXPECT_TRUE(capture_cert_req->filtered()); - EXPECT_TRUE(capture_certificate->filtered()); + ASSERT_TRUE(capture_cert_req->filtered()); + ASSERT_TRUE(capture_certificate->filtered()); + + client_->CheckClientAuthCompleted(); // Check if a non-empty request context is generated and it is // properly sent back. EXPECT_LT(0U, capture_cert_req->buffer().len()); @@ -252,7 +368,7 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) { EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterResumption) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthAfterResumption) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); Connect(); @@ -267,7 +383,7 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterResumption) { ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); ExpectResumption(RESUME_TICKET); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); Connect(); SendReceive(); @@ -280,10 +396,14 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterResumption) { }); EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); client_->SendData(50); server_->ReadBytes(50); + + client_->CheckClientAuthCompleted(); EXPECT_EQ(1U, called); ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); @@ -429,8 +549,8 @@ TEST_P(TlsConnectTls12, AutoClientSelectDsa) { EXPECT_TRUE(dsa.hookCalled); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthMultiple) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); size_t called = 0; @@ -445,36 +565,42 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) { // Send 1st CertificateRequest. EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); client_->SendData(50); server_->ReadBytes(50); EXPECT_EQ(1U, called); + client_->CheckClientAuthCompleted(1); ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd())); ASSERT_NE(nullptr, cert1.get()); ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd())); ASSERT_NE(nullptr, cert2.get()); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); // Send 2nd CertificateRequest. - EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook( - client_->ssl_fd(), GetClientAuthDataHook, nullptr)); + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd())) << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); + server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); client_->SendData(50); server_->ReadBytes(50); + client_->CheckClientAuthCompleted(2); EXPECT_EQ(2U, called); ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd())); ASSERT_NE(nullptr, cert3.get()); ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd())); ASSERT_NE(nullptr, cert4.get()); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert)); - EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert)); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthConcurrent) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthConcurrent) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); Connect(); @@ -486,8 +612,8 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthConcurrent) { EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBeforeKeyUpdate) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthBeforeKeyUpdate) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); Connect(); @@ -499,8 +625,9 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBeforeKeyUpdate) { EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError()); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDuringClientKeyUpdate) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDuringClientKeyUpdate) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); + ; EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); Connect(); @@ -514,11 +641,14 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDuringClientKeyUpdate) { client_->SendData(50); // client sends KeyUpdate server_->ReadBytes(50); // server receives KeyUpdate and defers response CheckEpochs(4, 3); - client_->ReadBytes(50); // client receives CertificateRequest + client_->ReadBytes(60); // client receives CertificateRequest + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); // Finish reading the remaining bytes client_->SendData( 50); // client sends Certificate, CertificateVerify, Finished server_->ReadBytes( 50); // server receives Certificate, CertificateVerify, Finished + client_->CheckClientAuthCompleted(); client_->CheckEpochs(3, 4); server_->CheckEpochs(4, 4); server_->SendData(50); // server sends KeyUpdate @@ -526,8 +656,8 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDuringClientKeyUpdate) { client_->CheckEpochs(4, 4); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMissingExtension) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthMissingExtension) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); Connect(); // Send CertificateRequest, should fail due to missing // post_handshake_auth extension. @@ -535,8 +665,8 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMissingExtension) { EXPECT_EQ(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION, PORT_GetError()); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterClientAuth) { - client_->SetupClientAuth(); +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthAfterClientAuth) { + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); @@ -593,10 +723,10 @@ class TlsDamageCertificateRequestContextFilter : public TlsHandshakeFilter { } }; -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthContextMismatch) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthContextMismatch) { EnsureTlsSetup(); MakeTlsFilter<TlsDamageCertificateRequestContextFilter>(server_); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); Connect(); @@ -605,6 +735,8 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthContextMismatch) { << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); + client_->ReadBytes(50); client_->SendData(50); server_->ExpectSendAlert(kTlsAlertIllegalParameter); server_->ReadBytes(50); @@ -636,10 +768,10 @@ class TlsDamageSignatureFilter : public TlsHandshakeFilter { } }; -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBadSignature) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthBadSignature) { EnsureTlsSetup(); MakeTlsFilter<TlsDamageSignatureFilter>(client_); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); Connect(); @@ -648,20 +780,22 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBadSignature) { << "Unexpected error: " << PORT_ErrorToName(PORT_GetError()); server_->SendData(50); client_->ReadBytes(50); + client_->ClientAuthCallbackComplete(); client_->SendData(50); + client_->CheckClientAuthCompleted(); server_->ExpectSendAlert(kTlsAlertDecodeError); server_->ReadBytes(50); EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERT_VERIFY, PORT_GetError()); } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDecline) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDecline) { EnsureTlsSetup(); auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>( server_, kTlsHandshakeCertificateRequest); auto capture_certificate = MakeTlsFilter<TlsCertificateRequestContextRecorder>( client_, kTlsHandshakeCertificate); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); EXPECT_EQ(SECSuccess, @@ -709,9 +843,10 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDecline) { // Check if post-handshake auth still works when session tickets are enabled: // https://bugzilla.mozilla.org/show_bug.cgi?id=1553443 -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthWithSessionTicketsEnabled) { +TEST_P(TlsConnectClientAuthStream13, + PostHandshakeAuthWithSessionTicketsEnabled) { EnsureTlsSetup(); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE)); EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(), @@ -743,7 +878,8 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthWithSessionTicketsEnabled) { EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } -TEST_P(TlsConnectGenericPre13, ClientAuthRequiredRejected) { +TEST_P(TlsConnectClientAuthPre13, ClientAuthRequiredRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); server_->RequestClientAuth(true); ConnectExpectAlert(server_, kTlsAlertBadCertificate); client_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); @@ -752,13 +888,16 @@ TEST_P(TlsConnectGenericPre13, ClientAuthRequiredRejected) { // In TLS 1.3, the client will claim that the connection is done and then // receive the alert afterwards. So drive the handshake manually. -TEST_P(TlsConnectTls13, ClientAuthRequiredRejected) { +TEST_P(TlsConnectClientAuth13, ClientAuthRequiredRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); server_->RequestClientAuth(true); StartConnect(); client_->Handshake(); // CH server_->Handshake(); // SH.. (no resumption) + client_->Handshake(); // Next message ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state()); + client_->CheckClientAuthCompleted(); ExpectAlert(server_, kTlsAlertCertificateRequired); server_->Handshake(); // Alert server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE); @@ -766,33 +905,34 @@ TEST_P(TlsConnectTls13, ClientAuthRequiredRejected) { client_->CheckErrorCode(SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT); } -TEST_P(TlsConnectGeneric, ClientAuthRequestedRejected) { +TEST_P(TlsConnectClientAuth, ClientAuthRequestedRejected) { + client_->SetupClientAuth(std::get<2>(GetParam()), false); server_->RequestClientAuth(false); Connect(); CheckKeys(); } -TEST_P(TlsConnectGeneric, ClientAuthEcdsa) { +TEST_P(TlsConnectClientAuth, ClientAuthEcdsa) { Reset(TlsAgent::kServerEcdsa256); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); } -TEST_P(TlsConnectGeneric, ClientAuthWithEch) { +TEST_P(TlsConnectClientAuth, ClientAuthWithEch) { Reset(TlsAgent::kServerEcdsa256); EnsureTlsSetup(); SetupEch(client_, server_); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); } -TEST_P(TlsConnectGeneric, ClientAuthBigRsa) { +TEST_P(TlsConnectClientAuth, ClientAuthBigRsa) { Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); CheckKeys(); @@ -835,11 +975,11 @@ TEST_P(TlsConnectTls12, ServerAuthCheckSigAlg) { 1024); } -TEST_P(TlsConnectTls12, ClientAuthCheckSigAlg) { +TEST_P(TlsConnectClientAuth12, ClientAuthCheckSigAlg) { EnsureTlsSetup(); auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( client_, kTlsHandshakeCertificateVerify); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); CheckKeys(); @@ -847,11 +987,11 @@ TEST_P(TlsConnectTls12, ClientAuthCheckSigAlg) { CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pkcs1_sha1, 1024); } -TEST_P(TlsConnectTls12, ClientAuthBigRsaCheckSigAlg) { +TEST_P(TlsConnectClientAuth12, ClientAuthBigRsaCheckSigAlg) { Reset(TlsAgent::kServerRsa, TlsAgent::kRsa2048); auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( client_, kTlsHandshakeCertificateVerify); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); CheckKeys(); @@ -886,7 +1026,7 @@ class TlsReplaceSignatureSchemeFilter : public TlsHandshakeFilter { // This only works under TLS 1.2, because PSS doesn't work with TLS // 1.0 or TLS 1.1 and the TLS 1.3 1-RTT handshake is partially // successful at the client side. -TEST_P(TlsConnectTls12, ClientAuthInconsistentRsaeSignatureScheme) { +TEST_P(TlsConnectClientAuth12, ClientAuthInconsistentRsaeSignatureScheme) { static const SSLSignatureScheme kSignatureSchemePss[] = { ssl_sig_rsa_pss_pss_sha256, ssl_sig_rsa_pss_rsae_sha256}; @@ -895,7 +1035,7 @@ TEST_P(TlsConnectTls12, ClientAuthInconsistentRsaeSignatureScheme) { PR_ARRAY_SIZE(kSignatureSchemePss)); server_->SetSignatureSchemes(kSignatureSchemePss, PR_ARRAY_SIZE(kSignatureSchemePss)); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); EnsureTlsSetup(); @@ -912,7 +1052,7 @@ TEST_P(TlsConnectTls12, ClientAuthInconsistentRsaeSignatureScheme) { // This only works under TLS 1.2, because PSS doesn't work with TLS // 1.0 or TLS 1.1 and the TLS 1.3 1-RTT handshake is partially // successful at the client side. -TEST_P(TlsConnectTls12, ClientAuthInconsistentPssSignatureScheme) { +TEST_P(TlsConnectClientAuth12, ClientAuthInconsistentPssSignatureScheme) { static const SSLSignatureScheme kSignatureSchemePss[] = { ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_pss_sha256}; @@ -921,7 +1061,7 @@ TEST_P(TlsConnectTls12, ClientAuthInconsistentPssSignatureScheme) { PR_ARRAY_SIZE(kSignatureSchemePss)); server_->SetSignatureSchemes(kSignatureSchemePss, PR_ARRAY_SIZE(kSignatureSchemePss)); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); EnsureTlsSetup(); @@ -932,7 +1072,7 @@ TEST_P(TlsConnectTls12, ClientAuthInconsistentPssSignatureScheme) { ConnectExpectAlert(server_, kTlsAlertIllegalParameter); } -TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureScheme) { +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1SignatureScheme) { static const SSLSignatureScheme kSignatureScheme[] = { ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pss_rsae_sha256}; @@ -941,7 +1081,7 @@ TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureScheme) { PR_ARRAY_SIZE(kSignatureScheme)); server_->SetSignatureSchemes(kSignatureScheme, PR_ARRAY_SIZE(kSignatureScheme)); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( @@ -954,14 +1094,14 @@ TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureScheme) { } // Client should refuse to connect without a usable signature scheme. -TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureSchemeOnly) { +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1SignatureSchemeOnly) { static const SSLSignatureScheme kSignatureScheme[] = { ssl_sig_rsa_pkcs1_sha256}; Reset(TlsAgent::kServerRsa, "rsa"); client_->SetSignatureSchemes(kSignatureScheme, PR_ARRAY_SIZE(kSignatureScheme)); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); client_->StartConnect(); client_->Handshake(); EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); @@ -970,14 +1110,14 @@ TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureSchemeOnly) { // Though the client has a usable signature scheme, when a certificate is // requested, it can't produce one. -TEST_P(TlsConnectTls13, ClientAuthPkcs1AndEcdsaScheme) { +TEST_P(TlsConnectClientAuth13, ClientAuthPkcs1AndEcdsaScheme) { static const SSLSignatureScheme kSignatureScheme[] = { ssl_sig_rsa_pkcs1_sha256, ssl_sig_ecdsa_secp256r1_sha256}; Reset(TlsAgent::kServerRsa, "rsa"); client_->SetSignatureSchemes(kSignatureScheme, PR_ARRAY_SIZE(kSignatureScheme)); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); ConnectExpectAlert(server_, kTlsAlertHandshakeFailure); @@ -1031,12 +1171,12 @@ class TlsZeroCertificateRequestSigAlgsFilter : public TlsHandshakeFilter { // Check that we send an alert when the server doesn't provide any // supported_signature_algorithms in the CertificateRequest message. -TEST_P(TlsConnectTls12, ClientAuthNoSigAlgs) { +TEST_P(TlsConnectClientAuth12, ClientAuthNoSigAlgs) { EnsureTlsSetup(); MakeTlsFilter<TlsZeroCertificateRequestSigAlgsFilter>(server_); auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>( client_, kTlsHandshakeCertificateVerify); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); ConnectExpectAlert(client_, kTlsAlertHandshakeFailure); @@ -1061,9 +1201,9 @@ static SECStatus GetEcClientAuthDataHook(void* self, PRFileDesc* fd, return SECSuccess; } -TEST_P(TlsConnectTls12Plus, ClientAuthDisjointSchemes) { +TEST_P(TlsConnectClientAuth12Plus, ClientAuthDisjointSchemes) { EnsureTlsSetup(); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); SSLSignatureScheme server_scheme = ssl_sig_rsa_pss_rsae_sha256; @@ -1103,7 +1243,7 @@ TEST_P(TlsConnectTls12Plus, ClientAuthDisjointSchemes) { } } -TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDisjointSchemes) { +TEST_P(TlsConnectClientAuthStream13, PostHandshakeAuthDisjointSchemes) { EnsureTlsSetup(); SSLSignatureScheme server_scheme = ssl_sig_rsa_pss_rsae_sha256; std::vector<SSLSignatureScheme> client_schemes{ @@ -1116,7 +1256,7 @@ TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDisjointSchemes) { static_cast<unsigned int>(client_schemes.size())); EXPECT_EQ(SECSuccess, rv); - client_->SetupClientAuth(); + client_->SetupClientAuth(std::get<2>(GetParam()), true); client_->SetOption(SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE); // Select an EC cert that's incompatible with server schemes. diff --git a/gtests/ssl_gtest/tls_agent.cc b/gtests/ssl_gtest/tls_agent.cc index 650be8449..8ec2f40f7 100644 --- a/gtests/ssl_gtest/tls_agent.cc +++ b/gtests/ssl_gtest/tls_agent.cc @@ -324,10 +324,19 @@ void TlsAgent::SetAntiReplayContext(ScopedSSLAntiReplayContext& ctx) { EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(ssl_fd(), ctx.get())); } -void TlsAgent::SetupClientAuth() { +// Defaults to a Sync callback returning success +void TlsAgent::SetupClientAuth(ClientAuthCallbackType callbackType, + bool callbackSuccess) { EXPECT_TRUE(EnsureTlsSetup()); ASSERT_EQ(CLIENT, role_); + client_auth_callback_type_ = callbackType; + client_auth_callback_success_ = callbackSuccess; + + if (callbackType == ClientAuthCallbackType::kNone && !callbackSuccess) { + // Don't set a callback for this case. + return; + } EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(ssl_fd(), GetClientAuthDataHook, reinterpret_cast<void*>(this))); @@ -344,26 +353,95 @@ void CheckCertReqAgainstDefaultCAs(const CERTDistNames* caNames) { } } +// Complete processing of Client Certificate Selection +// A No-op if the agent is using synchronous client cert selection. +// Otherwise, calls SSL_ClientCertCallbackComplete. +// kAsyncDelay triggers a call to SSL_ForceHandshake prior to completion to +// ensure that the socket is correctly blocked. +void TlsAgent::ClientAuthCallbackComplete() { + ASSERT_EQ(CLIENT, role_); + + if (client_auth_callback_type_ != ClientAuthCallbackType::kAsyncDelay && + client_auth_callback_type_ != ClientAuthCallbackType::kAsyncImmediate) { + return; + } + client_auth_callback_fired_++; + EXPECT_TRUE(client_auth_callback_awaiting_); + + std::cerr << "client: calling SSL_ClientCertCallbackComplete with status " + << (client_auth_callback_success_ ? "success" : "failed") + << std::endl; + + client_auth_callback_awaiting_ = false; + + if (client_auth_callback_type_ == ClientAuthCallbackType::kAsyncDelay) { + std::cerr + << "Running Handshake prior to running SSL_ClientCertCallbackComplete" + << std::endl; + SECStatus rv = SSL_ForceHandshake(ssl_fd()); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(PORT_GetError(), PR_WOULD_BLOCK_ERROR); + } + + ScopedCERTCertificate cert; + ScopedSECKEYPrivateKey priv; + if (client_auth_callback_success_) { + ASSERT_TRUE(TlsAgent::LoadCertificate(name(), &cert, &priv)); + EXPECT_EQ(SECSuccess, + SSL_ClientCertCallbackComplete(ssl_fd(), SECSuccess, + priv.release(), cert.release())); + } else { + EXPECT_EQ(SECSuccess, SSL_ClientCertCallbackComplete(ssl_fd(), SECFailure, + nullptr, nullptr)); + } +} + SECStatus TlsAgent::GetClientAuthDataHook(void* self, PRFileDesc* fd, CERTDistNames* caNames, CERTCertificate** clientCert, SECKEYPrivateKey** clientKey) { TlsAgent* agent = reinterpret_cast<TlsAgent*>(self); - ScopedCERTCertificate peerCert(SSL_PeerCertificate(agent->ssl_fd())); - EXPECT_TRUE(peerCert) << "Client should be able to see the server cert"; + EXPECT_EQ(CLIENT, agent->role_); + agent->client_auth_callback_fired_++; + + switch (agent->client_auth_callback_type_) { + case ClientAuthCallbackType::kAsyncDelay: + case ClientAuthCallbackType::kAsyncImmediate: + std::cerr << "Waiting for complete call" << std::endl; + agent->client_auth_callback_awaiting_ = true; + return SECWouldBlock; + case ClientAuthCallbackType::kSync: + case ClientAuthCallbackType::kNone: + // Handle the sync case. None && Success is treated as Sync and Success. + if (!agent->client_auth_callback_success_) { + return SECFailure; + } + ScopedCERTCertificate peerCert(SSL_PeerCertificate(agent->ssl_fd())); + EXPECT_TRUE(peerCert) << "Client should be able to see the server cert"; - // See bug 1573945 - // CheckCertReqAgainstDefaultCAs(caNames); + // See bug 1573945 + // CheckCertReqAgainstDefaultCAs(caNames); - ScopedCERTCertificate cert; - ScopedSECKEYPrivateKey priv; - if (!TlsAgent::LoadCertificate(agent->name(), &cert, &priv)) { - return SECFailure; + ScopedCERTCertificate cert; + ScopedSECKEYPrivateKey priv; + if (!TlsAgent::LoadCertificate(agent->name(), &cert, &priv)) { + return SECFailure; + } + + *clientCert = cert.release(); + *clientKey = priv.release(); + return SECSuccess; } + /* This is unreachable, but some old compilers can't tell that. */ + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; +} - *clientCert = cert.release(); - *clientKey = priv.release(); - return SECSuccess; +// Increments by 1 for each callback +bool TlsAgent::CheckClientAuthCallbacksCompleted(uint8_t expected) { + EXPECT_EQ(CLIENT, role_); + return expected == client_auth_callback_fired_; } bool TlsAgent::GetPeerChainLength(size_t* count) { @@ -954,6 +1032,24 @@ void TlsAgent::Connected() { SetState(STATE_CONNECTED); } +void TlsAgent::CheckClientAuthCompleted(uint8_t handshakes) { + EXPECT_FALSE(client_auth_callback_awaiting_); + switch (client_auth_callback_type_) { + case ClientAuthCallbackType::kNone: + if (!client_auth_callback_success_) { + EXPECT_TRUE(CheckClientAuthCallbacksCompleted(0)); + break; + } + case ClientAuthCallbackType::kSync: + EXPECT_TRUE(CheckClientAuthCallbacksCompleted(handshakes)); + break; + case ClientAuthCallbackType::kAsyncDelay: + case ClientAuthCallbackType::kAsyncImmediate: + EXPECT_TRUE(CheckClientAuthCallbacksCompleted(2 * handshakes)); + break; + } +} + void TlsAgent::EnableExtendedMasterSecret() { SetOption(SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE); } @@ -988,6 +1084,10 @@ void TlsAgent::SetDowngradeCheckVersion(uint16_t ver) { void TlsAgent::Handshake() { LOGV("Handshake"); SECStatus rv = SSL_ForceHandshake(ssl_fd()); + if (client_auth_callback_awaiting_) { + ClientAuthCallbackComplete(); + rv = SSL_ForceHandshake(ssl_fd()); + } if (rv == SECSuccess) { Connected(); Poller::Instance()->Wait(READABLE_EVENT, adapter_, this, diff --git a/gtests/ssl_gtest/tls_agent.h b/gtests/ssl_gtest/tls_agent.h index 8b54155a5..35375e0c1 100644 --- a/gtests/ssl_gtest/tls_agent.h +++ b/gtests/ssl_gtest/tls_agent.h @@ -39,6 +39,13 @@ enum SessionResumptionMode { RESUME_BOTH = RESUME_SESSIONID | RESUME_TICKET }; +enum class ClientAuthCallbackType { + kAsyncImmediate, + kAsyncDelay, + kSync, + kNone, +}; + class PacketFilter; class TlsAgent; class TlsCipherSpec; @@ -144,9 +151,13 @@ class TlsAgent : public PollTarget { bool ConfigServerCertWithChain(const std::string& name); bool EnsureTlsSetup(PRFileDesc* modelSocket = nullptr); - void SetupClientAuth(); + void SetupClientAuth( + ClientAuthCallbackType callbackType = ClientAuthCallbackType::kSync, + bool callbackSuccess = true); void RequestClientAuth(bool requireAuth); - + void ClientAuthCallbackComplete(); + bool CheckClientAuthCallbacksCompleted(uint8_t expected); + void CheckClientAuthCompleted(uint8_t handshakes = 1); void SetOption(int32_t option, int value); void ConfigureSessionCache(SessionResumptionMode mode); void Set0RttEnabled(bool en); @@ -465,6 +476,11 @@ class TlsAgent : public PollTarget { std::vector<uint8_t> resumption_token_; NssPolicy policy_; NssOption option_; + ClientAuthCallbackType client_auth_callback_type_ = + ClientAuthCallbackType::kNone; + bool client_auth_callback_success_ = false; + uint8_t client_auth_callback_fired_ = 0; + bool client_auth_callback_awaiting_ = false; }; inline std::ostream& operator<<(std::ostream& stream, diff --git a/lib/ssl/authcert.c b/lib/ssl/authcert.c index 54b6f5950..073103d5a 100644 --- a/lib/ssl/authcert.c +++ b/lib/ssl/authcert.c @@ -82,7 +82,7 @@ ssl_CertIsUsable(sslSocket *ss, CERTCertificate *cert) * if (!ss->ssl3.hs.hashType == handshake_hash_record && * ss->ssl3.hs.hashType == handshake_hash_single) { * return PR_TRUE; - * 2) assume if ss->peerSignatureSchemesCount == 0 we are using the + * 2) assume if ss->ss->ssl3.hs.clientAuthSignatureSchemesLen == 0 we are using the * old handshake. * There is one case where using 2 will be wrong: we somehow call this * function outside the case where of out GetClientAuthData context. @@ -90,15 +90,15 @@ ssl_CertIsUsable(sslSocket *ss, CERTCertificate *cert) * best we can do is either always assume good or always assume bad. * I think the best results is to always assume good, so we use * option 2 here to handle that case as well.*/ - if (ss->peerSignatureSchemeCount == 0) { + if (ss->ssl3.hs.clientAuthSignatureSchemesLen == 0) { return PR_TRUE; } - if (ss->peerSignatureSchemes == NULL) { + if (ss->ssl3.hs.clientAuthSignatureSchemes == NULL) { return PR_FALSE; /* should this really be an assert? */ } rv = ssl_PickClientSignatureScheme(ss, cert, NULL, - ss->peerSignatureSchemes, - ss->peerSignatureSchemeCount, + ss->ssl3.hs.clientAuthSignatureSchemes, + ss->ssl3.hs.clientAuthSignatureSchemesLen, &scheme); if (rv != SECSuccess) { return PR_FALSE; diff --git a/lib/ssl/dtlscon.c b/lib/ssl/dtlscon.c index 10e550e0f..a4a7c998c 100644 --- a/lib/ssl/dtlscon.c +++ b/lib/ssl/dtlscon.c @@ -359,7 +359,7 @@ dtls_HandleHandshake(sslSocket *ss, DTLSEpoch epoch, sslSequenceNumber seqNum, rv = dtls_HandleHandshakeMessage(ss, buf.buf, buf.len == fragment_length); - if (rv == SECFailure) { + if (rv != SECSuccess) { goto loser; } } else { @@ -468,7 +468,7 @@ dtls_HandleHandshake(sslSocket *ss, DTLSEpoch epoch, sslSequenceNumber seqNum, rv = dtls_HandleHandshakeMessage(ss, ss->ssl3.hs.msg_body.buf, buf.len == fragment_length); - if (rv == SECFailure) { + if (rv != SECSuccess) { goto loser; } } diff --git a/lib/ssl/ssl.def b/lib/ssl/ssl.def index 14fc2b960..75c37f2be 100644 --- a/lib/ssl/ssl.def +++ b/lib/ssl/ssl.def @@ -247,3 +247,9 @@ SSL_FilterClientCertListBySocket; ;+ local: ;+*; ;+}; +;+NSS_3.80 { # NSS 3.80 release +;+ global: +SSL_ClientCertCallbackComplete; +;+ local: +;+*; +;+}; diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h index 90d44f11f..8b6aab20b 100644 --- a/lib/ssl/ssl.h +++ b/lib/ssl/ssl.h @@ -856,6 +856,20 @@ SSL_IMPORT SECStatus SSL_AuthCertificate(void *arg, PRFileDesc *fd, * caNames - pointer to distinguished names of CAs that the server likes * pRetCert - pointer to pointer to cert, for return of cert * pRetKey - pointer to key pointer, for return of key + * Return value can be one of {SECSuccess, SECFailure, SECWouldBlock} + * + * If SECSuccess, pRetCert and pRetKey should be set to the selected + * client cert and private key respectively. If SECFailure or SECWouldBlock + * they should not be changed. + * + * Ownership of pRetCert and pRetKey passes to NSS. The application must not + * mutate or free the structures after passing them to NSS. + * + * Returning SECWouldBlock will block the handshake until SSL_ClientCertCallbackComplete + * is called. Note that references to *caNames should not be kept after SSLGetClientAuthData + * returns. Instead, take a copy of the data. + * + * See also the comments for SSL_ClientCertCallbackComplete. */ typedef SECStatus(PR_CALLBACK *SSLGetClientAuthData)(void *arg, PRFileDesc *fd, @@ -1505,6 +1519,50 @@ extern const char *NSSSSL_GetVersion(void); SSL_IMPORT SECStatus SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error); +/* Restart an SSL connection which was paused to do asynchronous client + * certificate selection (when the client certificate hook returned SECWouldBlock). + * + * This function only works for non-blocking sockets; Do not use it for + * blocking sockets. This function works only for the client role of + * a connection; it does not work for the server role. + * + * If a certificate has been sucessfully selected, the application must call + * SSL_ClientCertCallbackComplete with: + * - SECSuccess (0) as the value of outcome + * - a valid SECKEYPrivateKey located at *clientPrivateKey + * - a valid CERTCertificate located at *clientCertificate + * The ownership of these latter structures will pass to NSS and the application + * MUST not retain any references to them or invalidate them. + * + * If a certificate has not been selected, the application must call + * SSL_ClientCertCallbackComplete with: + * - SECFailure (-1) as the value of outcome + * - *clientPrivateKey set to NULL. + * - *clientCertificate set to NULL + * + * Once the application has returned SECWouldBlock to getClientAuthData + * the handshake will not proceed until this function is called. It is an + * error to call this function when the handshake is not waiting on client + * certificate selection, or to call this function more than once. + + * This function will not complete the entire handshake. The application must + * call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function + * to force the handshake to complete. + * + * Be careful about converting an application from synchronous cert selection + * to asynchronous certificate selection. A naive conversion is likely to + * result in deadlocks; e.g. the application will wait in PR_Poll for network + * I/O on the connection while all network I/O on the connection is blocked + * waiting for this function to be called. + * + * Note that SSL_ClientCertCallbackComplete will (usually) return + * SECSuccess; SECFailure indicates that the function was invoked incorrectly or + * an error whilst processing the handshake. The return code does not indicate + * whether or not the provided private key and certificate were sucessfully loaded + * or accepted by the server. + */ +SSL_IMPORT SECStatus SSL_ClientCertCallbackComplete(PRFileDesc *fd, SECStatus outcome, SECKEYPrivateKey *clientPrivateKey, CERTCertificate *clientCertificate); + /* * This is used to access experimental APIs. Don't call this directly. This is * used to enable the experimental APIs that are defined in "sslexp.h". diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index de5eb3e97..aa419bc37 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -6954,6 +6954,12 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; } + // TODO(djackson) - Bob removed this. Why? + if (ss->ssl3.hs.clientAuthSignatureSchemes != NULL) { + PR_Free(ss->ssl3.hs.clientAuthSignatureSchemes); + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; + } /* Note that if the server selects TLS 1.3, this will set the version to TLS * 1.2. We will amend that once all other fields have been read. */ @@ -7821,9 +7827,9 @@ ssl3_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) ss->ssl3.hs.ws = wait_hello_done; - rv = ssl3_CompleteHandleCertificateRequest(ss, signatureSchemes, - signatureSchemeCount, &ca_list); - if (rv == SECFailure) { + rv = ssl3_BeginHandleCertificateRequest(ss, signatureSchemes, + signatureSchemeCount, &ca_list); + if (rv != SECSuccess) { PORT_Assert(0); errCode = SEC_ERROR_LIBRARY_FAILURE; desc = internal_error; @@ -7849,51 +7855,11 @@ done: return rv; } -SECStatus -ssl3_CompleteHandleCertificateRequest(sslSocket *ss, - const SSLSignatureScheme *signatureSchemes, - unsigned int signatureSchemeCount, - CERTDistNames *ca_list) +static void +ssl3_ClientAuthCallbackOutcome(sslSocket *ss, SECStatus outcome) { SECStatus rv; - - /* Should not send a client cert when (non-GREASE) ECH is rejected. */ - if (ss->ssl3.hs.echHpkeCtx && !ss->ssl3.hs.echAccepted) { - PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn)); - goto send_no_certificate; - } - - if (ss->getClientAuthData != NULL) { - PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) == - ssl_preinfo_all); - PORT_Assert(ss->ssl3.clientPrivateKey == NULL); - PORT_Assert(ss->ssl3.clientCertificate == NULL); - PORT_Assert(ss->ssl3.clientCertChain == NULL); - /* - * Peer signatures are only available while in the context of - * of a getClientAuthData callback. It is required for proper - * functioning of SSL_CertIsUsable and SSL_FilterClientCertListBySocket - * Calling these functions outside the context of a getClientAuthData - * callback will result in no filtering.*/ - ss->peerSignatureSchemes = signatureSchemes; - ss->peerSignatureSchemeCount = signatureSchemeCount; - /* XXX Should pass cert_types and algorithms in this call!! */ - rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg, - ss->fd, ca_list, - &ss->ssl3.clientCertificate, - &ss->ssl3.clientPrivateKey); - /* memory for the signature schemes will go away after the request, - * so don't leave dangling pointers around */ - ss->peerSignatureSchemes = NULL; - ss->peerSignatureSchemeCount = 0; - } else { - rv = SECFailure; /* force it to send a no_certificate alert */ - } - switch (rv) { - case SECWouldBlock: /* getClientAuthData has put up a dialog box. */ - ssl3_SetAlwaysBlock(ss); - break; /* not an error */ - + switch (outcome) { case SECSuccess: /* check what the callback function returned */ if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) { @@ -7914,17 +7880,18 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss, rv = ssl_PickClientSignatureScheme(ss, ss->ssl3.clientCertificate, ss->ssl3.clientPrivateKey, - signatureSchemes, - signatureSchemeCount, + ss->ssl3.hs.clientAuthSignatureSchemes, + ss->ssl3.hs.clientAuthSignatureSchemesLen, &ss->ssl3.hs.signatureScheme); if (rv != SECSuccess) { /* This should only happen if our schemes changed or * if an RSA-PSS cert was selected, but the token - * does not support PSS schemes. */ + * does not support PSS schemes. + */ goto send_no_certificate; } } - break; /* not an error */ + break; case SECFailure: default: @@ -7943,13 +7910,100 @@ ssl3_CompleteHandleCertificateRequest(sslSocket *ss, } else { (void)SSL3_SendAlert(ss, alert_warning, no_certificate); } - rv = SECSuccess; break; } + /* Release the cached parameters */ + PORT_Free(ss->ssl3.hs.clientAuthSignatureSchemes); + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; +} + +SECStatus +ssl3_BeginHandleCertificateRequest(sslSocket *ss, + const SSLSignatureScheme *signatureSchemes, + unsigned int signatureSchemeCount, + CERTDistNames *ca_list) +{ + SECStatus rv; + + PR_ASSERT(!ss->ssl3.hs.clientCertificatePending); + + /* Should not send a client cert when (non-GREASE) ECH is rejected. */ + if (ss->ssl3.hs.echHpkeCtx && !ss->ssl3.hs.echAccepted) { + PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn)); + rv = SECFailure; + } else if (ss->getClientAuthData != NULL) { + PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) == + ssl_preinfo_all); + PORT_Assert(ss->ssl3.clientPrivateKey == NULL); + PORT_Assert(ss->ssl3.clientCertificate == NULL); + PORT_Assert(ss->ssl3.clientCertChain == NULL); + + /* Previously cached parameters should be empty */ + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemes == NULL); + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemesLen == 0); + /* + * Peer signatures are only available while in the context of + * of a getClientAuthData callback. It is required for proper + * functioning of SSL_CertIsUsable and SSL_FilterClientCertListBySocket + * Calling these functions outside the context of a getClientAuthData + * callback will result in no filtering.*/ + + ss->ssl3.hs.clientAuthSignatureSchemes = PORT_ZNewArray(SSLSignatureScheme, signatureSchemeCount); + PORT_Memcpy(ss->ssl3.hs.clientAuthSignatureSchemes, signatureSchemes, signatureSchemeCount * sizeof(SSLSignatureScheme)); + ss->ssl3.hs.clientAuthSignatureSchemesLen = signatureSchemeCount; + + rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg, + ss->fd, ca_list, + &ss->ssl3.clientCertificate, + &ss->ssl3.clientPrivateKey); + } else { + rv = SECFailure; /* force it to send a no_certificate alert */ + } + + if (rv == SECWouldBlock) { + /* getClientAuthData needs more time (e.g. for user interaction) */ + + /* The out parameters should not have changed. */ + PORT_Assert(ss->ssl3.clientCertificate == NULL); + PORT_Assert(ss->ssl3.clientPrivateKey == NULL); + + /* Mark the handshake as blocked */ + ss->ssl3.hs.clientCertificatePending = PR_TRUE; + + rv = SECSuccess; + } else { + /* getClientAuthData returned SECSuccess or SECFailure immediately, handle accordingly */ + ssl3_ClientAuthCallbackOutcome(ss, rv); + rv = SECSuccess; + } return rv; } +/* Invoked by the application when client certificate selection is complete */ +SECStatus +ssl3_ClientCertCallbackComplete(sslSocket *ss, SECStatus outcome, SECKEYPrivateKey *clientPrivateKey, CERTCertificate *clientCertificate) +{ + PORT_Assert(ss->ssl3.hs.clientCertificatePending); + ss->ssl3.hs.clientCertificatePending = PR_FALSE; + + ss->ssl3.clientCertificate = clientCertificate; + ss->ssl3.clientPrivateKey = clientPrivateKey; + + ssl3_ClientAuthCallbackOutcome(ss, outcome); + + /* Continue the handshake */ + PORT_Assert(ss->ssl3.hs.restartTarget); + if (!ss->ssl3.hs.restartTarget) { + FATAL_ERROR(ss, PR_INVALID_STATE_ERROR, internal_error); + return SECFailure; + } + sslRestartTarget target = ss->ssl3.hs.restartTarget; + ss->ssl3.hs.restartTarget = NULL; + return target(ss); +} + static SECStatus ssl3_CheckFalseStart(sslSocket *ss) { @@ -8105,8 +8159,11 @@ ssl3_SendClientSecondRound(sslSocket *ss) PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - if (ss->ssl3.hs.authCertificatePending && - (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) { + /* Check whether waiting for client certificate selection OR + waiting on server certificate verification AND + going to send client cert */ + if ((ss->ssl3.hs.clientCertificatePending) || + (ss->ssl3.hs.authCertificatePending && (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone))) { SSL_TRC(3, ("%d: SSL3[%p]: deferring ssl3_SendClientSecondRound because" " certificate authentication is still pending.", SSL_GETPID(), ss->fd)); @@ -10992,6 +11049,7 @@ ssl3_SendCertificate(sslSocket *ss) PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PR_ASSERT(!ss->ssl3.hs.clientCertificatePending); if (ss->sec.localCert) CERT_DestroyCertificate(ss->sec.localCert); @@ -11902,6 +11960,7 @@ ssl3_SendFinished(sslSocket *ss, PRInt32 flags) PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PR_ASSERT(!ss->ssl3.hs.clientCertificatePending); ssl_GetSpecReadLock(ss); cwSpec = ss->ssl3.cwSpec; @@ -12490,8 +12549,11 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length, PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE); return SECFailure; } - - if (IS_DTLS(ss) && (rv != SECFailure)) { + /* We consider the record to have been handled if SECSuccess or else WOULD_BLOCK is set + * Whoever set WOULD_BLOCK must handle any remaining actions required to finsih processing the record. + * e.g. by setting restartTarget. + */ + if (IS_DTLS(ss) && (rv == SECSuccess || (rv == SECFailure && PR_GetError() == PR_WOULD_BLOCK_ERROR))) { /* Increment the expected sequence number */ ss->ssl3.hs.recvMessageSeq++; } @@ -13604,6 +13666,9 @@ ssl3_InitState(sslSocket *ss) ss->ssl3.hs.echAccepted = PR_FALSE; ss->ssl3.hs.echDecided = PR_FALSE; + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; + PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space); ss->ssl3.hs.messages.buf = NULL; ss->ssl3.hs.messages.space = 0; @@ -13917,6 +13982,12 @@ ssl3_DestroySSL3Info(sslSocket *ss) if (ss->ssl3.clientPrivateKey != NULL) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); + if (ss->ssl3.hs.clientAuthSignatureSchemes != NULL) { + PORT_Free(ss->ssl3.hs.clientAuthSignatureSchemes); + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; + } + if (ss->ssl3.peerCertArena != NULL) ssl3_CleanupPeerCerts(ss); diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index 7b4e73c88..5a9da21a0 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -666,6 +666,12 @@ typedef struct SSL3HandshakeStateStr { PRUint8 data[72]; } finishedMsgs; + /* True when handshake is blocked on client certificate selection */ + PRBool clientCertificatePending; + /* Parameters stored whilst waiting for client certificate */ + SSLSignatureScheme *clientAuthSignatureSchemes; + unsigned int clientAuthSignatureSchemesLen; + PRBool authCertificatePending; /* Which function should SSL_RestartHandshake* call if we're blocked? * One of NULL, ssl3_SendClientSecondRound, ssl3_FinishHandshake, @@ -1139,10 +1145,6 @@ struct sslSocketStr { /* An out-of-band PSK. */ sslPsk *psk; - - /* peer data passed in during getClientAuthData */ - const SSLSignatureScheme *peerSignatureSchemes; - unsigned int peerSignatureSchemeCount; }; struct sslSelfEncryptKeysStr { @@ -1468,6 +1470,7 @@ extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, extern SECStatus ssl3_DecodeError(sslSocket *ss); extern SECStatus ssl3_AuthCertificateComplete(sslSocket *ss, PRErrorCode error); +extern SECStatus ssl3_ClientCertCallbackComplete(sslSocket *ss, SECStatus outcome, SECKEYPrivateKey *clientPrivateKey, CERTCertificate *clientCertificate); /* * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos @@ -1750,7 +1753,7 @@ SECStatus ssl_GetCertificateRequestCAs(const sslSocket *ss, unsigned int *nnamesp); SECStatus ssl3_ParseCertificateRequestCAs(sslSocket *ss, PRUint8 **b, PRUint32 *length, CERTDistNames *ca_list); -SECStatus ssl3_CompleteHandleCertificateRequest( +SECStatus ssl3_BeginHandleCertificateRequest( sslSocket *ss, const SSLSignatureScheme *signatureSchemes, unsigned int signatureSchemeCount, CERTDistNames *ca_list); SECStatus ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry, diff --git a/lib/ssl/sslsecur.c b/lib/ssl/sslsecur.c index 281ea927a..0422b39c7 100644 --- a/lib/ssl/sslsecur.c +++ b/lib/ssl/sslsecur.c @@ -1293,6 +1293,43 @@ SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error) return rv; } +SECStatus +SSL_ClientCertCallbackComplete(PRFileDesc *fd, SECStatus outcome, SECKEYPrivateKey *clientPrivateKey, + CERTCertificate *clientCertificate) +{ + SECStatus rv; + sslSocket *ss = ssl_FindSocket(fd); + + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_ClientCertCallbackComplete", + SSL_GETPID(), fd)); + return SECFailure; + } + + /* There exists a codepath which exercises each lock. + * Socket is blocked whilst waiting on this callback anyway. */ + ssl_Get1stHandshakeLock(ss); + ssl_GetRecvBufLock(ss); + ssl_GetSSL3HandshakeLock(ss); + + if (!ss->ssl3.hs.clientCertificatePending) { + /* Application invoked callback at wrong time */ + SSL_DBG(("%d: SSL[%d]: socket not waiting for SSL_ClientCertCallbackComplete", + SSL_GETPID(), fd)); + PORT_SetError(PR_INVALID_STATE_ERROR); + rv = SECFailure; + goto cleanup; + } + + rv = ssl3_ClientCertCallbackComplete(ss, outcome, clientPrivateKey, clientCertificate); + +cleanup: + ssl_ReleaseRecvBufLock(ss); + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + return rv; +} + /* For more info see ssl.h */ SECStatus SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func, diff --git a/lib/ssl/sslsock.c b/lib/ssl/sslsock.c index f8afb7627..6b40be759 100644 --- a/lib/ssl/sslsock.c +++ b/lib/ssl/sslsock.c @@ -399,10 +399,6 @@ ssl_DupSocket(sslSocket *os) goto loser; } } - /* The original socket 'owns' the copy of these, so - * just set the target copies to zero */ - ss->peerSignatureSchemes = NULL; - ss->peerSignatureSchemeCount = 0; /* Create security data */ rv = ssl_CopySecurityInfo(ss, os); @@ -494,10 +490,6 @@ ssl_DestroySocketContents(sslSocket *ss) tls13_ReleaseAntiReplayContext(ss->antiReplay); tls13_DestroyPsk(ss->psk); - /* data in peer Signature schemes comes from the buffer system, - * so there is nothing to free here. Make sure that's the case */ - PORT_Assert(ss->peerSignatureSchemes == NULL); - PORT_Assert(ss->peerSignatureSchemeCount == 0); tls13_DestroyEchConfigs(&ss->echConfigs); SECKEY_DestroyPrivateKey(ss->echPrivKey); @@ -2572,8 +2564,7 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd) ss->handshakeCallbackData = sm->handshakeCallbackData; if (sm->pkcs11PinArg) ss->pkcs11PinArg = sm->pkcs11PinArg; - ss->peerSignatureSchemes = NULL; - ss->peerSignatureSchemeCount = 0; + return fd; } @@ -4245,8 +4236,6 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant) ss->echPubKey = NULL; ss->antiReplay = NULL; ss->psk = NULL; - ss->peerSignatureSchemes = NULL; - ss->peerSignatureSchemeCount = 0; if (makeLocks) { rv = ssl_MakeLocks(ss); diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 0188ac1d9..51dfebff3 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -97,9 +97,7 @@ static SECStatus tls13_ComputeFinished( const SSL3Hashes *hashes, PRBool sending, PRUint8 *output, unsigned int *outputLen, unsigned int maxOutputLen); static SECStatus tls13_SendClientSecondRound(sslSocket *ss); -static SECStatus tls13_SendClientSecondFlight(sslSocket *ss, - PRBool sendClientCert, - SSL3AlertDescription *sendAlert); +static SECStatus tls13_SendClientSecondFlight(sslSocket *ss); static SECStatus tls13_FinishHandshake(sslSocket *ss); const char kHkdfLabelClient[] = "c"; @@ -2639,6 +2637,38 @@ loser: } static SECStatus +tls13_SendPostHandshakeCertificate(sslSocket *ss) +{ + SECStatus rv; + if (ss->ssl3.hs.restartTarget) { + PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget"); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (ss->ssl3.hs.clientCertificatePending) { + SSL_TRC(3, ("%d: TLS13[%d]: deferring tls13_SendClientSecondFlight because" + " certificate authentication is still pending.", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.restartTarget = tls13_SendPostHandshakeCertificate; + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return SECFailure; + } + + ssl_GetXmitBufLock(ss); + rv = tls13_SendClientSecondFlight(ss); + ssl_ReleaseXmitBufLock(ss); + PORT_Assert(ss->ssl3.hs.ws == idle_handshake); + PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL); + PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE); + ss->ssl3.hs.shaPostHandshake = NULL; + if (rv != SECSuccess) { + return SECFailure; + } + return rv; +} + +static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) { SECStatus rv; @@ -2695,12 +2725,19 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; } + if (ss->ssl3.hs.clientAuthSignatureSchemes != NULL) { + PORT_Free(ss->ssl3.hs.clientAuthSignatureSchemes); + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; + } SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE); ss->xtnData.certReqContext.data = NULL; } else { PORT_Assert(ss->ssl3.clientCertChain == NULL); PORT_Assert(ss->ssl3.clientCertificate == NULL); PORT_Assert(ss->ssl3.clientPrivateKey == NULL); + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemes == NULL); + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemesLen == 0); PORT_Assert(!ss->ssl3.hs.clientCertRequested); PORT_Assert(ss->xtnData.certReqContext.data == NULL); } @@ -2748,33 +2785,16 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) ss->ssl3.hs.clientCertRequested = PR_TRUE; if (ss->firstHsDone) { - SSL3AlertDescription sendAlert = no_alert; /* Request a client certificate. */ - rv = ssl3_CompleteHandleCertificateRequest( + rv = ssl3_BeginHandleCertificateRequest( ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, &ss->xtnData.certReqAuthorities); if (rv != SECSuccess) { FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); return rv; } - - ssl_GetXmitBufLock(ss); - rv = tls13_SendClientSecondFlight(ss, !ss->ssl3.sendEmptyCert, - &sendAlert); - ssl_ReleaseXmitBufLock(ss); - if (rv != SECSuccess) { - if (sendAlert != no_alert) { - FATAL_ERROR(ss, PORT_GetError(), sendAlert); - } else { - LOG_ERROR(ss, PORT_GetError()); - } - return SECFailure; - } - PORT_Assert(ss->ssl3.hs.ws == idle_handshake); - PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL); - PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE); - ss->ssl3.hs.shaPostHandshake = NULL; + rv = tls13_SendPostHandshakeCertificate(ss); } else { TLS13_SET_HS_STATE(ss, wait_server_cert); } @@ -4553,7 +4573,7 @@ tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) /* Request a client certificate now if one was requested. */ if (ss->ssl3.hs.clientCertRequested) { PORT_Assert(!ss->sec.isServer); - rv = ssl3_CompleteHandleCertificateRequest( + rv = ssl3_BeginHandleCertificateRequest( ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, &ss->xtnData.certReqAuthorities); if (rv != SECSuccess) { @@ -5052,15 +5072,17 @@ tls13_FinishHandshake(sslSocket *ss) /* Do the parts of sending the client's second round that require * the XmitBuf lock. */ static SECStatus -tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, - SSL3AlertDescription *sendAlert) +tls13_SendClientSecondFlight(sslSocket *ss) { SECStatus rv; unsigned int offset = 0; PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(!ss->ssl3.hs.clientCertificatePending); - *sendAlert = internal_error; + PRBool sendClientCert = !ss->ssl3.sendEmptyCert && + ss->ssl3.clientCertChain != NULL && + ss->ssl3.clientPrivateKey != NULL; if (ss->firstHsDone) { offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf); @@ -5071,12 +5093,12 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, rv = ssl3_SendEmptyCertificate(ss); /* Don't send verify */ if (rv != SECSuccess) { - return SECFailure; /* error code is set. */ + goto alert_error; /* error code is set. */ } } else if (sendClientCert) { rv = tls13_SendCertificate(ss); if (rv != SECSuccess) { - return SECFailure; /* error code is set. */ + goto alert_error; /* err code was set. */ } } @@ -5085,7 +5107,7 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset, SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset); if (rv != SECSuccess) { - return SECFailure; /* error code is set. */ + goto alert_error; /* err code was set. */ } } @@ -5109,7 +5131,7 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); ss->ssl3.clientPrivateKey = NULL; if (rv != SECSuccess) { - return SECFailure; /* err is set. */ + goto alert_error; /* err code was set. */ } if (ss->firstHsDone) { @@ -5117,40 +5139,40 @@ tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset, SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset); if (rv != SECSuccess) { - return SECFailure; /* error is set. */ + goto alert_error; /* err code was set. */ } } } rv = tls13_SendFinished(ss, ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret); if (rv != SECSuccess) { - return SECFailure; /* err code was set. */ + goto alert_error; /* err code was set. */ } rv = ssl3_FlushHandshake(ss, 0); if (rv != SECSuccess) { /* No point in sending an alert here because we're not going to * be able to send it if we couldn't flush the handshake. */ - *sendAlert = no_alert; - return SECFailure; + goto error; } return SECSuccess; + +alert_error: + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; +error: + LOG_ERROR(ss, PORT_GetError()); + return SECFailure; } static SECStatus tls13_SendClientSecondRound(sslSocket *ss) { SECStatus rv; - PRBool sendClientCert; - SSL3AlertDescription sendAlert = no_alert; PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); - sendClientCert = !ss->ssl3.sendEmptyCert && - ss->ssl3.clientCertChain != NULL && - ss->ssl3.clientPrivateKey != NULL; - /* Defer client authentication sending if we are still waiting for server * authentication. This avoids unnecessary disclosure of client credentials * to an unauthenticated server. @@ -5160,8 +5182,8 @@ tls13_SendClientSecondRound(sslSocket *ss) PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - if (ss->ssl3.hs.authCertificatePending) { - SSL_TRC(3, ("%d: TLS13[%d]: deferring ssl3_SendClientSecondRound because" + if (ss->ssl3.hs.authCertificatePending || ss->ssl3.hs.clientCertificatePending) { + SSL_TRC(3, ("%d: TLS13[%d]: deferring tls13_SendClientSecondRound because" " certificate authentication is still pending.", SSL_GETPID(), ss->fd)); ss->ssl3.hs.restartTarget = tls13_SendClientSecondRound; @@ -5208,14 +5230,10 @@ tls13_SendClientSecondRound(sslSocket *ss) } ssl_GetXmitBufLock(ss); /*******************************/ - rv = tls13_SendClientSecondFlight(ss, sendClientCert, &sendAlert); + /* This call can't block, as clientAuthCertificatePending is checked above */ + rv = tls13_SendClientSecondFlight(ss); ssl_ReleaseXmitBufLock(ss); /*******************************/ if (rv != SECSuccess) { - if (sendAlert != no_alert) { - FATAL_ERROR(ss, PORT_GetError(), sendAlert); - } else { - LOG_ERROR(ss, PORT_GetError()); - } return SECFailure; } rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, |