| 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
|
|
|