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 |