diff options
-rw-r--r-- | cpputil/tls_parser.h | 2 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_auth_unittest.cc | 79 | ||||
-rw-r--r-- | lib/ssl/ssl3con.c | 3 |
3 files changed, 69 insertions, 15 deletions
diff --git a/cpputil/tls_parser.h b/cpputil/tls_parser.h index 4c9dbce1b..cd9e28fc3 100644 --- a/cpputil/tls_parser.h +++ b/cpputil/tls_parser.h @@ -41,6 +41,8 @@ const uint8_t kTlsAlertBadRecordMac = 20; const uint8_t kTlsAlertRecordOverflow = 22; const uint8_t kTlsAlertHandshakeFailure = 40; const uint8_t kTlsAlertBadCertificate = 42; +const uint8_t kTlsAlertCertificateRevoked = 44; +const uint8_t kTlsAlertCertificateExpired = 45; const uint8_t kTlsAlertIllegalParameter = 47; const uint8_t kTlsAlertDecodeError = 50; const uint8_t kTlsAlertDecryptError = 51; diff --git a/gtests/ssl_gtest/ssl_auth_unittest.cc b/gtests/ssl_gtest/ssl_auth_unittest.cc index 1dbcccf4a..d024568a5 100644 --- a/gtests/ssl_gtest/ssl_auth_unittest.cc +++ b/gtests/ssl_gtest/ssl_auth_unittest.cc @@ -99,6 +99,69 @@ TEST_P(TlsConnectGeneric, ServerAuthRsaCARsaPssChain) { EXPECT_EQ(2UL, chain_length); } +TEST_P(TlsConnectGeneric, ServerAuthRejected) { + EnsureTlsSetup(); + client_->SetAuthCertificateCallback( + [](TlsAgent*, PRBool, PRBool) -> SECStatus { return SECFailure; }); + ConnectExpectAlert(client_, kTlsAlertBadCertificate); + client_->CheckErrorCode(SSL_ERROR_BAD_CERTIFICATE); + server_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT); + EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state()); +} + +struct AuthCompleteArgs : public PollTarget { + AuthCompleteArgs(const std::shared_ptr<TlsAgent>& a, PRErrorCode c) + : agent(a), code(c) {} + + std::shared_ptr<TlsAgent> agent; + PRErrorCode code; +}; + +static void CallAuthComplete(PollTarget* target, Event event) { + EXPECT_EQ(TIMER_EVENT, event); + auto args = reinterpret_cast<AuthCompleteArgs*>(target); + std::cerr << args->agent->role_str() << ": call SSL_AuthCertificateComplete " + << (args->code ? PR_ErrorToName(args->code) : "no error") + << std::endl; + EXPECT_EQ(SECSuccess, + SSL_AuthCertificateComplete(args->agent->ssl_fd(), args->code)); + args->agent->Handshake(); // Make the TlsAgent aware of the error. + delete args; +} + +// Install an AuthCertificateCallback that blocks when called. Then +// SSL_AuthCertificateComplete is called on a very short timer. This allows any +// processing that might follow the callback to complete. +static void SetDeferredAuthCertificateCallback(std::shared_ptr<TlsAgent> agent, + PRErrorCode code) { + auto args = new AuthCompleteArgs(agent, code); + agent->SetAuthCertificateCallback( + [args](TlsAgent*, PRBool, PRBool) -> SECStatus { + std::shared_ptr<Poller::Timer> timer_handle; + Poller::Instance()->SetTimer(0U, args, CallAuthComplete, &timer_handle); + return SECWouldBlock; + }); +} + +TEST_P(TlsConnectTls13, ServerAuthRejectAsync) { + SetDeferredAuthCertificateCallback(client_, SEC_ERROR_REVOKED_CERTIFICATE); + ConnectExpectAlert(client_, kTlsAlertCertificateRevoked); + // We only detect the error here when we attempt to handshake, so all the + // client learns is that the handshake has already failed. + client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILED); + server_->CheckErrorCode(SSL_ERROR_REVOKED_CERT_ALERT); +} + +// In TLS 1.2 and earlier, this will result in the client sending its Finished +// before learning that the server certificate is bad. That means that the +// server will believe that the handshake is complete. +TEST_P(TlsConnectGenericPre13, ServerAuthRejectAsync) { + SetDeferredAuthCertificateCallback(client_, SEC_ERROR_EXPIRED_CERTIFICATE); + client_->ExpectSendAlert(kTlsAlertCertificateExpired); + ConnectExpectFailOneSide(TlsAgent::CLIENT); + client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILED); +} + TEST_P(TlsConnectGeneric, ClientAuth) { client_->SetupClientAuth(); server_->RequestClientAuth(true); @@ -648,25 +711,11 @@ TEST_F(TlsConnectDatagram13, AuthCompleteBeforeFinished) { Connect(); } -static void TriggerAuthComplete(PollTarget* target, Event event) { - std::cerr << "client: call SSL_AuthCertificateComplete" << std::endl; - EXPECT_EQ(TIMER_EVENT, event); - TlsAgent* client = static_cast<TlsAgent*>(target); - EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client->ssl_fd(), 0)); -} - // This test uses a simple AuthCertificateCallback. Due to the way that the // entire server flight is processed, the call to SSL_AuthCertificateComplete // will trigger after the Finished message is processed. TEST_F(TlsConnectDatagram13, AuthCompleteAfterFinished) { - client_->SetAuthCertificateCallback( - [this](TlsAgent*, PRBool, PRBool) -> SECStatus { - std::shared_ptr<Poller::Timer> timer_handle; - // This is really just to unroll the stack. - Poller::Instance()->SetTimer(1U, client_.get(), TriggerAuthComplete, - &timer_handle); - return SECWouldBlock; - }); + SetDeferredAuthCertificateCallback(client_, 0); // 0 = success. Connect(); } diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index 0fbba35ae..48393d087 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -10669,6 +10669,9 @@ ssl3_AuthCertificate(sslSocket *ss) PR_TRUE, isServer); if (rv != SECSuccess) { errCode = PORT_GetError(); + if (errCode == 0) { + errCode = SSL_ERROR_BAD_CERTIFICATE; + } if (rv != SECWouldBlock) { if (ss->handleBadCert) { rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd); |