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 3ed4da199510e66d781dff74cc10e36b375fb92c..017838fda30eb9d30075a77cd07e901620230a90 100644 |
--- a/net/socket/ssl_server_socket_nss.cc |
+++ b/net/socket/ssl_server_socket_nss.cc |
@@ -37,8 +37,12 @@ |
#include "crypto/rsa_private_key.h" |
#include "net/base/io_buffer.h" |
#include "net/base/net_errors.h" |
+#include "net/cert/cert_verifier.h" |
+#include "net/cert/cert_verify_result.h" |
#include "net/log/net_log.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 |
@@ -70,6 +74,8 @@ class NSSSSLServerInitSingleton { |
} |
}; |
+void DoNothingOnCompletion(int ignore) {} |
+ |
static base::LazyInstance<NSSSSLServerInitSingleton>::Leaky |
g_nss_ssl_server_init_singleton = LAZY_INSTANCE_INITIALIZER; |
@@ -81,14 +87,14 @@ void EnableSSLServerSockets() { |
scoped_ptr<SSLServerSocket> CreateSSLServerSocket( |
scoped_ptr<StreamSocket> socket, |
- X509Certificate* cert, |
+ X509Certificate* certificate, |
crypto::RSAPrivateKey* key, |
const SSLServerConfig& 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( |
@@ -106,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)); |
@@ -155,6 +163,20 @@ int SSLServerSocketNSS::Handshake(const CompletionCallback& callback) { |
return rv > OK ? OK : rv; |
} |
+void SSLServerSocketNSS::SetRequireClientCert(bool require_client_cert) { |
+ ssl_config_.require_client_cert = require_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, |
@@ -304,8 +326,32 @@ NextProto SSLServerSocketNSS::GetNegotiatedProtocol() const { |
} |
bool SSLServerSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
- NOTIMPLEMENTED(); |
- return false; |
+ ssl_info->Reset(); |
+ if (!completed_handshake_) { |
+ return false; |
+ } |
+ 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; |
+ 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; |
} |
void SSLServerSocketNSS::GetConnectionAttempts(ConnectionAttempts* out) const { |
@@ -338,15 +384,6 @@ int SSLServerSocketNSS::InitializeSSLOptions() { |
int rv; |
- if (ssl_config_.require_client_cert) { |
- rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", |
- "SSL_REQUEST_CERTIFICATE"); |
- return ERR_UNEXPECTED; |
- } |
- } |
- |
rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); |
if (rv != SECSuccess) { |
LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_SECURITY"); |
@@ -412,13 +449,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_.require_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, |
+ ssl_config_.require_client_cert ? PR_TRUE : PR_FALSE); |
if (rv != SECSuccess) { |
LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUIRE_CERTIFICATE"); |
return ERR_UNEXPECTED; |
@@ -508,6 +547,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) { |
+ 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; |
} |
@@ -822,7 +882,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. |
@@ -830,9 +889,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; |
+ scoped_ptr<CertVerifier::Request> ignore_async; |
+ int res = self->client_cert_verifier_->Verify( |
+ cert, std::string(), std::string(), 0, NULL, &ignore_result, |
+ base::Bind(&DoNothingOnCompletion), &ignore_async, self->net_log_); |
+ if (res != OK) { |
+ PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); |
+ } |
+ return res == OK ? SECSuccess : SECFailure; |
} |
// static |
@@ -854,4 +931,24 @@ int SSLServerSocketNSS::Init() { |
return OK; |
} |
+void SSLServerSocketNSS::ExtractClientCert() { |
+ if (client_cert_.get()) |
+ return; |
+ if (!completed_handshake_) |
+ return; |
+ CERTCertList* list = SSL_PeerCertificateChain(nss_fd_); |
+ 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 |