| 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 fe3a3694069ef21c987acd99c6d463b7527f9768..7a390bbcfe0adfdc42a6b595bec86c415b51d9f3 100644
|
| --- a/net/socket/ssl_server_socket_openssl.cc
|
| +++ b/net/socket/ssl_server_socket_openssl.cc
|
| @@ -14,13 +14,61 @@
|
| #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/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 {
|
| +
|
| +bool GetDERFromX509(X509* cert, base::StringPiece* sp) {
|
| + unsigned char* cert_data = NULL;
|
| + int cert_data_length = i2d_X509(cert, &cert_data);
|
| + if (!cert_data_length || !cert_data) {
|
| + return false;
|
| + }
|
| + sp->set(reinterpret_cast<char*>(cert_data), cert_data_length);
|
| + return true;
|
| +}
|
| +
|
| +scoped_refptr<X509Certificate> CreateX509Certificate(X509* cert,
|
| + STACK_OF(X509) * chain) {
|
| + DCHECK(cert);
|
| + std::vector<base::StringPiece> der_chain;
|
| + base::StringPiece der_cert;
|
| + scoped_refptr<X509Certificate> client_cert;
|
| + if (!GetDERFromX509(cert, &der_cert))
|
| + return client_cert;
|
| + der_chain.push_back(der_cert);
|
| +
|
| + ScopedX509Stack openssl_chain(X509_chain_up_ref(chain));
|
| + for (size_t i = 0; i < sk_X509_num(openssl_chain.get()); ++i) {
|
| + X509* x = sk_X509_value(openssl_chain.get(), i);
|
| + if (GetDERFromX509(x, &der_cert)) {
|
| + der_chain.push_back(der_cert);
|
| + }
|
| + }
|
| +
|
| + client_cert = X509Certificate::CreateFromDERCertChain(der_chain);
|
| +
|
| + for (size_t i = 0; i < der_chain.size(); ++i) {
|
| + OPENSSL_free(const_cast<char*>(der_chain[i].data()));
|
| + }
|
| + if (der_chain.size() - 1 !=
|
| + static_cast<size_t>(sk_X509_num(openssl_chain.get()))) {
|
| + client_cert = NULL;
|
| + }
|
| + return client_cert;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| void EnableSSLServerSockets() {
|
| // No-op because CreateSSLServerSocket() calls crypto::EnsureOpenSSLInit().
|
| }
|
| @@ -29,17 +77,30 @@ scoped_ptr<SSLServerSocket> CreateSSLServerSocket(
|
| scoped_ptr<StreamSocket> socket,
|
| X509Certificate* certificate,
|
| crypto::RSAPrivateKey* key,
|
| - const SSLServerConfig& ssl_config) {
|
| + const SSLServerConfig& ssl_server_config) {
|
| crypto::EnsureOpenSSLInit();
|
| - return scoped_ptr<SSLServerSocket>(
|
| - new SSLServerSocketOpenSSL(socket.Pass(), certificate, key, ssl_config));
|
| + SSLServerSocketContext context;
|
| + return scoped_ptr<SSLServerSocket>(new SSLServerSocketOpenSSL(
|
| + socket.Pass(), certificate, key, ssl_server_config, context));
|
| +}
|
| +
|
| +scoped_ptr<SSLServerSocket> CreateSSLServerSocket(
|
| + scoped_ptr<StreamSocket> socket,
|
| + X509Certificate* certificate,
|
| + crypto::RSAPrivateKey* key,
|
| + const SSLServerConfig& ssl_server_config,
|
| + const SSLServerSocketContext& context) {
|
| + crypto::EnsureOpenSSLInit();
|
| + return scoped_ptr<SSLServerSocket>(new SSLServerSocketOpenSSL(
|
| + socket.Pass(), certificate, key, ssl_server_config, context));
|
| }
|
|
|
| SSLServerSocketOpenSSL::SSLServerSocketOpenSSL(
|
| scoped_ptr<StreamSocket> transport_socket,
|
| scoped_refptr<X509Certificate> certificate,
|
| crypto::RSAPrivateKey* key,
|
| - const SSLServerConfig& ssl_config)
|
| + const SSLServerConfig& ssl_server_config,
|
| + const SSLServerSocketContext& context)
|
| : transport_send_busy_(false),
|
| transport_recv_busy_(false),
|
| transport_recv_eof_(false),
|
| @@ -49,7 +110,8 @@ SSLServerSocketOpenSSL::SSLServerSocketOpenSSL(
|
| ssl_(NULL),
|
| transport_bio_(NULL),
|
| transport_socket_(transport_socket.Pass()),
|
| - ssl_config_(ssl_config),
|
| + ssl_server_config_(ssl_server_config),
|
| + context_(context),
|
| cert_(certificate),
|
| next_handshake_state_(STATE_NONE),
|
| completed_handshake_(false) {
|
| @@ -244,8 +306,31 @@ NextProto SSLServerSocketOpenSSL::GetNegotiatedProtocol() const {
|
| }
|
|
|
| bool SSLServerSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
|
| - NOTIMPLEMENTED();
|
| - return false;
|
| + ssl_info->Reset();
|
| + if (!completed_handshake_) {
|
| + return false;
|
| + }
|
| + ExtractClientCert();
|
| + ssl_info->cert = client_cert_;
|
| + ssl_info->client_cert_sent =
|
| + ssl_server_config_.require_client_cert && client_cert_.get();
|
| +
|
| + const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_);
|
| + CHECK(cipher);
|
| + ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL);
|
| +
|
| + ssl_info->connection_status =
|
| + EncodeSSLConnectionStatus(SSL_CIPHER_get_id(cipher),
|
| + 0 /* no compression */, GetNetSSLVersion(ssl_));
|
| +
|
| + 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(
|
| @@ -573,6 +658,13 @@ int SSLServerSocketOpenSSL::DoHandshake() {
|
| 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.
|
| + 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);
|
| @@ -618,10 +710,9 @@ 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)
|
| + if (ssl_server_config_.require_client_cert)
|
| SSL_CTX_set_verify(ssl_ctx.get(), SSL_VERIFY_PEER, NULL);
|
| -
|
| + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, this);
|
| ssl_ = SSL_new(ssl_ctx.get());
|
| if (!ssl_)
|
| return ERR_UNEXPECTED;
|
| @@ -668,10 +759,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.
|
| @@ -695,11 +786,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(":!");
|
| @@ -714,7 +805,56 @@ int SSLServerSocketOpenSSL::Init() {
|
| LOG_IF(WARNING, rv != 1) << "SSL_set_cipher_list('" << command
|
| << "') returned " << rv;
|
|
|
| + if (ssl_server_config_.require_client_cert) {
|
| + if (context_.client_cert_verifier)
|
| + ssl_->verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
| + if (!ssl_server_config_.client_cert_ca_list.empty()) {
|
| + ScopedX509NameStack stack(sk_X509_NAME_new_null());
|
| + for (const auto& certificate : ssl_server_config_.client_cert_ca_list) {
|
| + ScopedX509 ca_cert =
|
| + OSCertHandleToOpenSSL(certificate->os_cert_handle());
|
| + ScopedX509Name subj(X509_NAME_dup(ca_cert->cert_info->subject));
|
| + sk_X509_NAME_push(stack.get(), subj.release());
|
| + }
|
| + SSL_set_client_CA_list(ssl_, stack.release());
|
| + }
|
| + }
|
| +
|
| return OK;
|
| }
|
|
|
| +void SSLServerSocketOpenSSL::ExtractClientCert() {
|
| + if (client_cert_.get() || !completed_handshake_) {
|
| + return;
|
| + }
|
| + X509* cert = SSL_get_peer_certificate(ssl_);
|
| + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_);
|
| + client_cert_ = CreateX509Certificate(cert, chain);
|
| +}
|
| +
|
| +// static
|
| +int SSLServerSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx,
|
| + void* arg) {
|
| + SSLServerSocketOpenSSL* self = reinterpret_cast<SSLServerSocketOpenSSL*>(arg);
|
| + DCHECK(self);
|
| + if (!self->context_.client_cert_verifier)
|
| + return 1;
|
| + SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
|
| + store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
|
| + DCHECK(ssl);
|
| + X509* x = store_ctx->cert;
|
| + STACK_OF(X509)* chain = store_ctx->chain;
|
| + scoped_refptr<X509Certificate> client_cert(CreateX509Certificate(x, chain));
|
| +
|
| + int res = self->context_.client_cert_verifier->Verify(client_cert.get(),
|
| + self->net_log_);
|
| + if (res == OK) {
|
| + self->client_cert_ = client_cert;
|
| + return 1;
|
| + } else {
|
| + X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED);
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| } // namespace net
|
|
|