Chromium Code Reviews| Index: net/socket/ssl_server_socket_nss.cc |
| diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc |
| index 7fa5835b43062f8ceecf0479582752609d907f0d..10005a4f25c97838286f9bff6cff0b473ec9f79c 100644 |
| --- a/net/socket/ssl_server_socket_nss.cc |
| +++ b/net/socket/ssl_server_socket_nss.cc |
| @@ -37,7 +37,11 @@ |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_log.h" |
| +#include "net/cert/cert_verifier.h" |
| +#include "net/cert/cert_verify_result.h" |
| #include "net/socket/nss_ssl_util.h" |
| +#include "net/ssl/ssl_connection_status_flags.h" |
| +#include "net/ssl/ssl_info.h" |
| // SSL plaintext fragments are shorter than 16KB. Although the record layer |
| // overhead is allowed to be 2K + 5 bytes, in practice the overhead is much |
| @@ -69,6 +73,9 @@ class NSSSSLServerInitSingleton { |
| } |
| }; |
| +void DoNothingOnCompletion(int ignore) { |
| +} |
| + |
| static base::LazyInstance<NSSSSLServerInitSingleton> |
| g_nss_ssl_server_init_singleton = LAZY_INSTANCE_INITIALIZER; |
| @@ -80,14 +87,14 @@ void EnableSSLServerSockets() { |
| scoped_ptr<SSLServerSocket> CreateSSLServerSocket( |
| scoped_ptr<StreamSocket> socket, |
| - X509Certificate* cert, |
| + X509Certificate* certificate, |
| crypto::RSAPrivateKey* key, |
| const SSLConfig& ssl_config) { |
| DCHECK(g_nss_server_sockets_init) << "EnableSSLServerSockets() has not been" |
| << " called yet!"; |
| return scoped_ptr<SSLServerSocket>( |
| - new SSLServerSocketNSS(socket.Pass(), cert, key, ssl_config)); |
| + new SSLServerSocketNSS(socket.Pass(), certificate, key, ssl_config)); |
| } |
| SSLServerSocketNSS::SSLServerSocketNSS( |
| @@ -105,7 +112,9 @@ SSLServerSocketNSS::SSLServerSocketNSS( |
| ssl_config_(ssl_config), |
| cert_(cert), |
| next_handshake_state_(STATE_NONE), |
| - completed_handshake_(false) { |
| + completed_handshake_(false), |
| + client_cert_ca_list_(), |
| + client_cert_verifier_(NULL) { |
| // TODO(hclam): Need a better way to clone a key. |
| std::vector<uint8> key_bytes; |
| CHECK(key->ExportPrivateKey(&key_bytes)); |
| @@ -154,6 +163,20 @@ int SSLServerSocketNSS::Handshake(const CompletionCallback& callback) { |
| return rv > OK ? OK : rv; |
| } |
| +void SSLServerSocketNSS::SetAllowClientCert(bool allow_client_cert) { |
| + ssl_config_.send_client_cert = allow_client_cert; |
| +} |
| + |
| +void SSLServerSocketNSS::SetClientCertCAList( |
| + const CertificateList& client_cert_ca_list) { |
| + client_cert_ca_list_ = client_cert_ca_list; |
| +} |
| + |
| +void SSLServerSocketNSS::SetClientCertVerifier( |
| + CertVerifier* client_cert_verifier) { |
| + client_cert_verifier_ = client_cert_verifier; |
| +} |
| + |
| int SSLServerSocketNSS::ExportKeyingMaterial(const base::StringPiece& label, |
| bool has_context, |
| const base::StringPiece& context, |
| @@ -303,8 +326,32 @@ NextProto SSLServerSocketNSS::GetNegotiatedProtocol() const { |
| } |
| bool SSLServerSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
|
Ryan Sleevi
2015/03/19 04:38:25
I'm vaguely uncomfortable with overloading SSLInfo
|
| - NOTIMPLEMENTED(); |
| - return false; |
| + ssl_info->Reset(); |
| + if (!completed_handshake_) { |
| + return false; |
| + } |
|
Ryan Sleevi
2015/03/19 04:38:25
No braces
|
| + ExtractClientCert(); |
| + ssl_info->cert = client_cert_; |
| + UpdateSSLConnectionStatus(nss_fd_, ssl_config_, &ssl_info->connection_status); |
| + ssl_info->client_cert_sent = client_cert_.get() ? true : false; |
|
Ryan Sleevi
2015/03/19 04:38:25
no .get()
ssl_info->client_cert_sent = client_cer
|
| + PRUint16 cipher_suite = |
| + SSLConnectionStatusToCipherSuite(ssl_info->connection_status); |
| + SSLCipherSuiteInfo cipher_info; |
| + SECStatus ok = |
| + SSL_GetCipherSuiteInfo(cipher_suite, &cipher_info, sizeof(cipher_info)); |
| + if (ok == SECSuccess) { |
| + ssl_info->security_bits = cipher_info.effectiveKeyBits; |
| + } else { |
| + ssl_info->security_bits = -1; |
| + } |
| + PRBool last_handshake_resumed; |
| + SECStatus rv = SSL_HandshakeResumedSession(nss_fd_, &last_handshake_resumed); |
| + if (rv == SECSuccess && last_handshake_resumed) { |
| + ssl_info->handshake_type = SSLInfo::HANDSHAKE_RESUME; |
| + } else { |
| + ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; |
| + } |
| + return true; |
|
Ryan Sleevi
2015/03/19 04:38:25
Feels pretty unreadable/ wall of text. Break it in
|
| } |
| int SSLServerSocketNSS::InitializeSSLOptions() { |
| @@ -394,13 +441,15 @@ int SSLServerSocketNSS::InitializeSSLOptions() { |
| return ERR_UNEXPECTED; |
| } |
| - rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_FALSE); |
| + rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, |
| + ssl_config_.send_client_cert ? PR_TRUE : PR_FALSE); |
| if (rv != SECSuccess) { |
| LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUEST_CERTIFICATE"); |
| return ERR_UNEXPECTED; |
| } |
| - rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_CERTIFICATE, PR_FALSE); |
| + rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_CERTIFICATE, |
| + client_cert_verifier_ ? PR_TRUE : PR_FALSE); |
| if (rv != SECSuccess) { |
| LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUIRE_CERTIFICATE"); |
| return ERR_UNEXPECTED; |
| @@ -490,6 +539,27 @@ int SSLServerSocketNSS::InitializeSSLOptions() { |
| return ERR_UNEXPECTED; |
| } |
| + // Set up the information needed for the CertificateRequest message |
| + if (!client_cert_ca_list_.empty()) { |
| + CERTCertList* certs = CERT_NewCertList(); |
| + if (!certs) { |
| + LogFailedNSSFunction(net_log_, "CERT_NewCertList", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + for (CertificateList::const_iterator it = client_cert_ca_list_.begin(); |
| + it != client_cert_ca_list_.end(); ++it) { |
|
Ryan Sleevi
2015/03/19 04:38:25
for (const auto& certificate : client_cert_ca_list
|
| + CERT_AddCertToListTail(certs, |
| + CERT_DupCertificate((*it)->os_cert_handle())); |
| + } |
| + |
| + rv = SSL_SetTrustAnchors(nss_fd_, certs); |
| + CERT_DestroyCertList(certs); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_SetTrustAnchors", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + } |
| + |
| return OK; |
| } |
| @@ -804,7 +874,6 @@ void SSLServerSocketNSS::DoWriteCallback(int rv) { |
| // static |
| // NSS calls this if an incoming certificate needs to be verified. |
| -// 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. |
| @@ -812,9 +881,27 @@ SECStatus SSLServerSocketNSS::OwnAuthCertHandler(void* arg, |
| PRFileDesc* socket, |
| PRBool checksig, |
| PRBool is_server) { |
| - // TODO(hclam): Implement. |
| - // Tell NSS to not verify the certificate. |
| - return SECSuccess; |
| + if (!is_server) |
| + return SECFailure; |
| + SSLServerSocketNSS* self = reinterpret_cast<SSLServerSocketNSS*>(arg); |
| + DCHECK(self); |
| + if (!self->client_cert_verifier_) |
| + return SECSuccess; |
| + self->ExtractClientCert(); |
| + X509Certificate* cert = self->client_cert_.get(); |
| + if (!cert) { |
| + PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0); |
| + return SECFailure; |
| + } |
| + CertVerifyResult ignore_result; |
| + CertVerifier::RequestHandle ignore_handle; |
| + int res = self->client_cert_verifier_->Verify( |
| + cert, std::string(), 0, NULL, &ignore_result, |
| + base::Bind(&DoNothingOnCompletion), &ignore_handle, self->net_log_); |
| + if (res != OK) { |
| + PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); |
| + } |
|
Ryan Sleevi
2015/03/19 04:38:25
no braces
|
| + return res == OK ? SECSuccess : SECFailure; |
| } |
| // static |
| @@ -836,4 +923,24 @@ int SSLServerSocketNSS::Init() { |
| return OK; |
| } |
| +void SSLServerSocketNSS::ExtractClientCert() { |
| + if (client_cert_.get()) |
| + return; |
| + if (!completed_handshake_) |
| + return; |
| + CERTCertList* list = SSL_PeerCertificateChain(nss_fd_); |
|
Ryan Sleevi
2015/03/19 04:38:25
Use a scoped type helper
|
| + if (list == NULL) |
| + return; |
| + std::vector<base::StringPiece> certs; |
| + for (CERTCertListNode* node = CERT_LIST_HEAD(list); |
| + !CERT_LIST_END(node, list); node = CERT_LIST_NEXT(node)) { |
| + SECItem& cert_der = node->cert->derCert; |
| + base::StringPiece cert(reinterpret_cast<char*>(cert_der.data), |
| + cert_der.len); |
| + certs.push_back(cert); |
| + } |
| + client_cert_ = X509Certificate::CreateFromDERCertChain(certs); |
| + CERT_DestroyCertList(list); |
| +} |
| + |
| } // namespace net |