diff options
author | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-04 09:18:47 +0000 |
---|---|---|
committer | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-04 09:18:47 +0000 |
commit | 2345cc551d3487a3e722b4a72c5ad17ff0405e69 (patch) | |
tree | b647ffc212f65ca3e88d433b054defeb4d49d3ab /net | |
parent | 4bb4e42b53911df68cfad9054c3c252b7620824a (diff) | |
download | chromium_src-2345cc551d3487a3e722b4a72c5ad17ff0405e69.zip chromium_src-2345cc551d3487a3e722b4a72c5ad17ff0405e69.tar.gz chromium_src-2345cc551d3487a3e722b4a72c5ad17ff0405e69.tar.bz2 |
Call CertVerifier to verify certificate.
BUG=10911
TEST=net_unittests passes
Review URL: http://codereview.chromium.org/115913
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17624 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/ssl_client_socket_nss.cc | 156 | ||||
-rw-r--r-- | net/base/ssl_client_socket_nss.h | 21 |
2 files changed, 102 insertions, 75 deletions
diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc index ec2b961..c8cd334 100644 --- a/net/base/ssl_client_socket_nss.cc +++ b/net/base/ssl_client_socket_nss.cc @@ -5,7 +5,7 @@ // This file includes code GetDefaultCertNickname(), derived from // nsNSSCertificate::defaultServerNickName() // in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp -// and SSLClientSocketNSS::OwnAuthCertHandler() derived from +// and SSLClientSocketNSS::DoVerifyCertComplete() derived from // AuthCertificateCallback() in // mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp. @@ -197,7 +197,6 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket, ssl_config_(ssl_config), user_callback_(NULL), user_buf_len_(0), - server_cert_error_(0), cert_list_(NULL), completed_handshake_(false), next_state_(STATE_NONE), @@ -332,9 +331,9 @@ int SSLClientSocketNSS::Connect(CompletionCallback* callback) { if (rv != SECSuccess) return ERR_UNEXPECTED; - rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, this); + rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); if (rv != SECSuccess) - return ERR_UNEXPECTED; + return ERR_UNEXPECTED; // Tell SSL the hostname we're trying to connect to. SSL_SetURL(nss_fd_, hostname_.c_str()); @@ -376,11 +375,11 @@ void SSLClientSocketNSS::Disconnect() { user_buf_ = NULL; user_buf_len_ = 0; server_cert_ = NULL; - server_cert_error_ = OK; if (cert_list_) { CERT_DestroyCertList(cert_list_); cert_list_ = NULL; } + server_cert_verify_result_.Reset(); completed_handshake_ = false; nss_bufs_ = NULL; @@ -462,6 +461,7 @@ X509Certificate *SSLClientSocketNSS::UpdateServerCert() { server_cert_ = X509Certificate::CreateFromHandle( nss_cert, X509Certificate::SOURCE_FROM_NETWORK); DCHECK(!cert_list_); + // TODO(ukai): don't need to copy cert list. cert_list_ = CERT_GetCertChainFromCert( nss_cert, PR_Now(), certUsageSSLCA); } @@ -472,6 +472,9 @@ X509Certificate *SSLClientSocketNSS::UpdateServerCert() { void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { EnterFunction(""); ssl_info->Reset(); + if (!server_cert_) + return; + SSLChannelInfo channel_info; SECStatus ok = SSL_GetChannelInfo(nss_fd_, &channel_info, sizeof(channel_info)); @@ -490,8 +493,7 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { } UpdateServerCert(); } - if (server_cert_error_ != net::OK) - ssl_info->SetCertError(server_cert_error_); + ssl_info->cert_status = server_cert_verify_result_.cert_status; DCHECK(server_cert_ != NULL); ssl_info->cert = server_cert_; LeaveFunction(""); @@ -626,6 +628,13 @@ int SSLClientSocketNSS::DoLoop(int last_io_result) { case STATE_HANDSHAKE_READ: rv = DoHandshakeRead(); break; + case STATE_VERIFY_CERT: + DCHECK(rv == OK); + rv = DoVerifyCert(rv); + break; + case STATE_VERIFY_CERT_COMPLETE: + rv = DoVerifyCertComplete(rv); + break; case STATE_PAYLOAD_READ: rv = DoPayloadRead(); break; @@ -652,27 +661,73 @@ int SSLClientSocketNSS::DoLoop(int last_io_result) { // static // NSS calls this if an incoming certificate needs to be verified. -// Derived from AuthCertificateCallback() in -// mozilla/source/security/manager/ssl/src/nsNSSCallbacks.cpp. +// Do nothing but return SECSuccess. +// This is called only in full handshake mode. +// Peer certificate is retrieved in HandshakeCallback() later, which is called +// in full handshake mode or in resumption handshake mode. SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, PRFileDesc* socket, PRBool checksig, PRBool is_server) { + // Tell NSS to not verify the certificate. + return SECSuccess; +} + +// static +// NSS calls this when handshake is completed. +// After the SSL handshake is finished, use CertVerifier to verify +// the saved server certificate. +void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, + void* arg) { SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); - // Remember the certificate as it will no longer be accessible if the - // handshake fails. - scoped_refptr<X509Certificate> cert = that->UpdateServerCert(); + that->UpdateServerCert(); +} + +int SSLClientSocketNSS::DoHandshakeRead() { + EnterFunction(""); + int net_error = net::OK; + int rv = SSL_ForceHandshake(nss_fd_); + + if (rv == SECSuccess) { + // SSL handshake is completed. Let's verify the certificate. + GotoState(STATE_VERIFY_CERT); + // Done! + } else { + PRErrorCode prerr = PR_GetError(); + net_error = NetErrorFromNSPRError(prerr); + + // If not done, stay in this state + if (net_error == ERR_IO_PENDING) { + GotoState(STATE_HANDSHAKE_READ); + } else { + LOG(ERROR) << "handshake failed; NSS error code " << prerr + << ", net_error " << net_error; + } + } + + LeaveFunction(""); + return net_error; +} - SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), socket, checksig, - is_server); - if (rv == SECSuccess && that->cert_list_) { +int SSLClientSocketNSS::DoVerifyCert(int result) { + DCHECK(server_cert_); + GotoState(STATE_VERIFY_CERT_COMPLETE); + return verifier_.Verify(server_cert_, hostname_, + ssl_config_.rev_checking_enabled, + &server_cert_verify_result_, &io_callback_); +} + +// Derived from AuthCertificateCallback() in +// mozilla/source/security/manager/ssl/src/nsNSSCallbacks.cpp. +int SSLClientSocketNSS::DoVerifyCertComplete(int result) { + if (result == OK && cert_list_) { // Remember the intermediate CA certs if the server sends them to us. - for (CERTCertListNode* node = CERT_LIST_HEAD(that->cert_list_); - !CERT_LIST_END(node, that->cert_list_); + for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list_); + !CERT_LIST_END(node, cert_list_); node = CERT_LIST_NEXT(node)) { if (node->cert->slot || node->cert->isRoot || node->cert->isperm || - node->cert == cert->os_cert_handle()) { + node->cert == server_cert_->os_cert_handle()) { // Some certs we don't want to remember are: // - found on a token. // - the root cert. @@ -694,58 +749,25 @@ SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, } } - return rv; -} - -// static -// NSS calls this if an incoming certificate is invalid. -SECStatus SSLClientSocketNSS::OwnBadCertHandler(void* arg, - PRFileDesc* socket) { - SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); - - if (that->server_cert_ && - that->ssl_config_.allowed_bad_certs_.count(that->server_cert_)) { + // If we have been explicitly told to accept this certificate, override the + // result of verifier_.Verify. + // Eventually, we should cache the cert verification results so that we don't + // need to call verifier_.Verify repeatedly. But for now we need to do this. + // Alternatively, we might be able to store the cert's status along with + // the cert in the allowed_bad_certs_ set. + if (IsCertificateError(result) && + ssl_config_.allowed_bad_certs_.count(server_cert_)) { LOG(INFO) << "accepting bad SSL certificate, as user told us to"; - - return SECSuccess; + result = OK; } - PRErrorCode prerr = PR_GetError(); - that->server_cert_error_ = NetErrorFromNSPRError(prerr); - LOG(INFO) << "server certificate is invalid; NSS error code " << prerr - << ", net error " << that->server_cert_error_; - return SECFailure; -} - -int SSLClientSocketNSS::DoHandshakeRead() { - EnterFunction(""); - int net_error = net::OK; - int rv = SSL_ForceHandshake(nss_fd_); - - if (rv == SECSuccess) { - DCHECK(server_cert_error_ == net::OK); - - InvalidateSessionIfBadCertificate(); - - // there's a callback for this, too - completed_handshake_ = true; - // Done! - } else { - PRErrorCode prerr = PR_GetError(); - net_error = NetErrorFromNSPRError(prerr); - - // If not done, stay in this state - if (net_error == ERR_IO_PENDING) { - GotoState(STATE_HANDSHAKE_READ); - } else { - server_cert_error_ = net_error; - LOG(ERROR) << "handshake failed; NSS error code " << prerr - << ", net_error " << net_error; - } - } - - LeaveFunction(""); - return net_error; + completed_handshake_ = true; + // TODO(ukai): we may not need this call because it is now harmless to have an + // session with a bad cert. + InvalidateSessionIfBadCertificate(); + // Exit DoLoop and return the result to the caller to Connect. + DCHECK(next_state_ == STATE_NONE); + return result; } int SSLClientSocketNSS::DoPayloadRead() { diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h index b3b6706..eef2ec2 100644 --- a/net/base/ssl_client_socket_nss.h +++ b/net/base/ssl_client_socket_nss.h @@ -15,6 +15,8 @@ #include <string> #include "base/scoped_ptr.h" +#include "net/base/cert_verifier.h" +#include "net/base/cert_verify_result.h" #include "net/base/completion_callback.h" #include "net/base/nss_memio.h" #include "net/base/ssl_client_socket.h" @@ -57,6 +59,8 @@ class SSLClientSocketNSS : public SSLClientSocket { int DoLoop(int last_io_result); int DoHandshakeRead(); + int DoVerifyCert(int result); + int DoVerifyCertComplete(int result); int DoPayloadRead(); int DoPayloadWrite(); int Init(); @@ -69,9 +73,9 @@ class SSLClientSocketNSS : public SSLClientSocket { // argument. static SECStatus OwnAuthCertHandler(void* arg, PRFileDesc* socket, PRBool checksig, PRBool is_server); - - // NSS calls this on error. We pass 'this' as the first argument. - static SECStatus OwnBadCertHandler(void* arg, PRFileDesc* socket); + // NSS calls this when handshake is completed. We pass 'this' as the second + // argument. + static void HandshakeCallback(PRFileDesc* socket, void* arg); CompletionCallbackImpl<SSLClientSocketNSS> buffer_send_callback_; CompletionCallbackImpl<SSLClientSocketNSS> buffer_recv_callback_; @@ -90,20 +94,21 @@ class SSLClientSocketNSS : public SSLClientSocket { scoped_refptr<IOBuffer> user_buf_; int user_buf_len_; - // Set when handshake finishes. Value is net error code, see net_errors.h - int server_cert_error_; - - // Set during handshake. + // Set when handshake finishes. scoped_refptr<X509Certificate> server_cert_; + CertVerifyResult server_cert_verify_result_; // Certificate chain. CERTCertList* cert_list_; + CertVerifier verifier_; + bool completed_handshake_; enum State { STATE_NONE, STATE_HANDSHAKE_READ, - // No STATE_HANDSHAKE_READ_COMPLETE needed, go to STATE_NONE instead. + STATE_VERIFY_CERT, + STATE_VERIFY_CERT_COMPLETE, STATE_PAYLOAD_WRITE, STATE_PAYLOAD_READ, }; |