Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(557)

Unified Diff: net/socket/ssl_server_socket_openssl.cc

Issue 1474983003: Support for client certs in ssl_server_socket. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase only Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/socket/ssl_server_socket_openssl.cc
diff --git a/net/socket/ssl_server_socket_openssl.cc b/net/socket/ssl_server_socket_openssl.cc
index c3869cd865dd25f0611837585e46a8c418896b48..9736b603753951373803b8fb27099e46f63883ab 100644
--- a/net/socket/ssl_server_socket_openssl.cc
+++ b/net/socket/ssl_server_socket_openssl.cc
@@ -15,13 +15,54 @@
#include "crypto/rsa_private_key.h"
#include "crypto/scoped_openssl_types.h"
#include "net/base/net_errors.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/client_cert_verifier.h"
+#include "net/cert/x509_util_openssl.h"
#include "net/ssl/openssl_ssl_util.h"
#include "net/ssl/scoped_openssl_types.h"
+#include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_info.h"
#define GotoState(s) next_handshake_state_ = s
namespace net {
+namespace {
+
+scoped_refptr<X509Certificate> CreateX509Certificate(X509* cert,
+ STACK_OF(X509) * chain) {
+ std::vector<base::StringPiece> der_chain;
+ base::StringPiece der_cert;
+ scoped_refptr<X509Certificate> client_cert;
+ if (cert) {
davidben 2016/01/25 20:56:10 Bleh. This probably wants to be documented like:
ryanchung 2016/01/29 23:22:12 Done.
+ if (!x509_util::GetDER(cert, &der_cert))
+ return client_cert;
davidben 2016/01/25 20:56:10 If this fails, we should fail the connection, not
ryanchung 2016/01/29 23:22:12 Ok. I'm adding code to CertVerifyCallback to handl
+ der_chain.push_back(der_cert);
+ }
+
+ ScopedX509_STACK openssl_chain(X509_chain_up_ref(chain));
davidben 2016/01/25 20:56:10 What's the point of this copy?
ryanchung 2016/01/29 23:22:12 Done. Shouldn't need to copy. Removed.
+ for (size_t i = 0; i < sk_X509_num(openssl_chain.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain.get(), i);
+ if (x509_util::GetDER(x, &der_cert)) {
+ der_chain.push_back(der_cert);
+ } else {
+ return nullptr;
davidben 2016/01/25 20:56:10 Ditto re failing the connection.
ryanchung 2016/01/29 23:22:13 Ok. I'm adding code to CertVerifyCallback to handl
+ }
+ }
+
+ return X509Certificate::CreateFromDERCertChain(der_chain);
+}
+
+scoped_refptr<X509Certificate> GetClientCert(SSL* ssl) {
+ X509* cert = SSL_get_peer_certificate(ssl);
ryanchung 2016/01/29 23:22:13 I think this was a memory leak. SSL_get_peer_certi
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
davidben 2016/01/25 20:56:10 Check if cert is NULL and, if so, this needs to re
ryanchung 2016/01/29 23:22:12 Done. Thanks.
+ return CreateX509Certificate(cert, chain);
+}
+
+void DoNothingOnCompletion(int ignore) {}
+
+} // namespace
+
void EnableSSLServerSockets() {
// No-op because CreateSSLServerSocket() calls crypto::EnsureOpenSSLInit().
}
@@ -30,17 +71,17 @@ scoped_ptr<SSLServerSocket> CreateSSLServerSocket(
scoped_ptr<StreamSocket> socket,
X509Certificate* certificate,
const crypto::RSAPrivateKey& key,
- const SSLServerConfig& ssl_config) {
+ const SSLServerConfig& ssl_server_config) {
crypto::EnsureOpenSSLInit();
return scoped_ptr<SSLServerSocket>(new SSLServerSocketOpenSSL(
- std::move(socket), certificate, key, ssl_config));
+ std::move(socket), certificate, key, ssl_server_config));
}
SSLServerSocketOpenSSL::SSLServerSocketOpenSSL(
scoped_ptr<StreamSocket> transport_socket,
scoped_refptr<X509Certificate> certificate,
const crypto::RSAPrivateKey& key,
- const SSLServerConfig& ssl_config)
+ const SSLServerConfig& ssl_server_config)
: transport_send_busy_(false),
transport_recv_busy_(false),
transport_recv_eof_(false),
@@ -50,7 +91,7 @@ SSLServerSocketOpenSSL::SSLServerSocketOpenSSL(
ssl_(NULL),
transport_bio_(NULL),
transport_socket_(std::move(transport_socket)),
- ssl_config_(ssl_config),
+ ssl_server_config_(ssl_server_config),
cert_(certificate),
key_(key.Copy()),
next_handshake_state_(STATE_NONE),
@@ -242,8 +283,30 @@ NextProto SSLServerSocketOpenSSL::GetNegotiatedProtocol() const {
}
bool SSLServerSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
- NOTIMPLEMENTED();
- return false;
+ ssl_info->Reset();
+ if (!completed_handshake_)
+ return false;
+
+ ssl_info->cert = client_cert_;
+
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_);
+ CHECK(cipher);
+ ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL);
+
+ SSLConnectionStatusSetCipherSuite(
+ static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)),
+ &ssl_info->connection_status);
+ SSLConnectionStatusSetVersion(GetNetSSLVersion(ssl_),
+ &ssl_info->connection_status);
+
+ if (!SSL_get_secure_renegotiation_support(ssl_))
+ ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION;
+
+ ssl_info->handshake_type = SSL_session_reused(ssl_)
+ ? SSLInfo::HANDSHAKE_RESUME
+ : SSLInfo::HANDSHAKE_FULL;
+
+ return true;
}
void SSLServerSocketOpenSSL::GetConnectionAttempts(
@@ -566,11 +629,19 @@ int SSLServerSocketOpenSSL::DoHandshake() {
if (rv == 1) {
completed_handshake_ = true;
+ client_cert_ = GetClientCert(ssl_);
} else {
int ssl_error = SSL_get_error(ssl_, rv);
OpenSSLErrorInfo error_info;
net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info);
+ // This hack is necessary because the mapping of SSL error codes to
+ // net_errors assumes (correctly for client sockets, but erroneously for
+ // server sockets) that peer cert verification failure can only occur if
+ // the cert changed during a renego. crbug.com/570351
+ if (net_error == ERR_SSL_SERVER_CERT_CHANGED)
+ net_error = ERR_BAD_SSL_CLIENT_AUTH_CERT;
+
// If not done, stay in this state
if (net_error == ERR_IO_PENDING) {
GotoState(STATE_HANDSHAKE);
@@ -616,10 +687,12 @@ int SSLServerSocketOpenSSL::Init() {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
ScopedSSL_CTX ssl_ctx(SSL_CTX_new(SSLv23_server_method()));
-
- if (ssl_config_.require_client_cert)
- SSL_CTX_set_verify(ssl_ctx.get(), SSL_VERIFY_PEER, NULL);
-
+ if (ssl_server_config_.require_client_cert) {
+ SSL_CTX_set_verify(ssl_ctx.get(),
+ SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
davidben 2016/01/25 20:56:10 NULL -> nullptr
ryanchung 2016/01/29 23:22:12 Done.
+ }
+ SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback,
+ ssl_server_config_.client_cert_verifier);
davidben 2016/01/25 20:56:10 This probably should also go in the conditional, n
ryanchung 2016/01/29 23:22:13 Done.
ssl_ = SSL_new(ssl_ctx.get());
if (!ssl_)
return ERR_UNEXPECTED;
@@ -666,10 +739,10 @@ int SSLServerSocketOpenSSL::Init() {
return ERR_UNEXPECTED;
}
- DCHECK_LT(SSL3_VERSION, ssl_config_.version_min);
- DCHECK_LT(SSL3_VERSION, ssl_config_.version_max);
- SSL_set_min_version(ssl_, ssl_config_.version_min);
- SSL_set_max_version(ssl_, ssl_config_.version_max);
+ DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_min);
+ DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_max);
+ SSL_set_min_version(ssl_, ssl_server_config_.version_min);
+ SSL_set_max_version(ssl_, ssl_server_config_.version_max);
// OpenSSL defaults some options to on, others to off. To avoid ambiguity,
// set everything we care about to an absolute value.
@@ -693,11 +766,11 @@ int SSLServerSocketOpenSSL::Init() {
// as the handshake hash.
std::string command("DEFAULT:!SHA256:!SHA384:!AESGCM+AES256:!aPSK");
- if (ssl_config_.require_ecdhe)
+ if (ssl_server_config_.require_ecdhe)
command.append(":!kRSA:!kDHE");
// Remove any disabled ciphers.
- for (uint16_t id : ssl_config_.disabled_cipher_suites) {
+ for (uint16_t id : ssl_server_config_.disabled_cipher_suites) {
const SSL_CIPHER* cipher = SSL_get_cipher_by_value(id);
if (cipher) {
command.append(":!");
@@ -712,7 +785,43 @@ int SSLServerSocketOpenSSL::Init() {
LOG_IF(WARNING, rv != 1) << "SSL_set_cipher_list('" << command
<< "') returned " << rv;
+ if (ssl_server_config_.require_client_cert &&
+ !ssl_server_config_.cert_authorities_.empty()) {
+ ScopedX509_NAME_STACK stack(sk_X509_NAME_new_null());
+ for (const auto& authority : ssl_server_config_.cert_authorities_) {
+ const unsigned char* name =
+ reinterpret_cast<const unsigned char*>(authority.c_str());
davidben 2016/01/25 20:56:10 uint8_t, not unsigned char
ryanchung 2016/01/29 23:22:13 Done.
+ ScopedX509_NAME subj(d2i_X509_NAME(NULL, &name, authority.length()));
davidben 2016/01/25 20:56:10 This needs error-handling. d2i_X509_NAME parses th
davidben 2016/01/25 20:56:10 Nit: NULL -> nullptr
ryanchung 2016/01/29 23:22:13 Done.
ryanchung 2016/01/29 23:22:13 Done.
+ sk_X509_NAME_push(stack.get(), subj.release());
+ }
+ SSL_set_client_CA_list(ssl_, stack.release());
+ }
+
return OK;
}
+// static
+int SSLServerSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx,
+ void* arg) {
+ ClientCertVerifier* verifier = reinterpret_cast<ClientCertVerifier*>(arg);
+ // If a verifier was not supplied, all certificates are accepted.
+ if (!verifier)
+ return 1;
+ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
davidben 2016/01/25 20:56:10 You don't seem to be doing anything with this vari
ryanchung 2016/01/29 23:22:12 Done. Removed.
+ DCHECK(ssl);
+ STACK_OF(X509)* chain = store_ctx->untrusted;
+ scoped_ptr<ClientCertVerifier::Request> ignore_async;
davidben 2016/01/25 20:56:10 Very nitpicky nit: declare variables near where th
ryanchung 2016/01/29 23:22:13 Done.
+ scoped_refptr<X509Certificate> client_cert(
+ CreateX509Certificate(nullptr, chain));
+
+ int res = verifier->Verify(client_cert.get(),
davidben 2016/01/25 20:56:10 This really needs a comment as to what's going on
ryanchung 2016/01/29 23:22:12 Done.
+ base::Bind(&DoNothingOnCompletion), &ignore_async);
+ if (res != OK) {
+ X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED);
+ return 0;
+ }
+ return 1;
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698