summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--automation/abi-check/expected-report-libnspr4.so.txt4
-rw-r--r--automation/abi-check/expected-report-libssl3.so.txt4
-rw-r--r--cmd/tstclnt/tstclnt.c72
-rw-r--r--gtests/ssl_gtest/ssl_auth_unittest.cc274
-rw-r--r--gtests/ssl_gtest/tls_agent.cc124
-rw-r--r--gtests/ssl_gtest/tls_agent.h20
-rw-r--r--lib/ssl/authcert.c10
-rw-r--r--lib/ssl/dtlscon.c4
-rw-r--r--lib/ssl/ssl.def6
-rw-r--r--lib/ssl/ssl.h58
-rw-r--r--lib/ssl/ssl3con.c181
-rw-r--r--lib/ssl/sslimpl.h13
-rw-r--r--lib/ssl/sslsecur.c37
-rw-r--r--lib/ssl/sslsock.c13
-rw-r--r--lib/ssl/tls13con.c114
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,