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

Unified Diff: net/socket/ssl_client_socket_openssl.cc

Issue 1178193002: Sign CertificateVerify messages on a background thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: more mac build fix, this is blind while mac checkout syncs Created 5 years, 6 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
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | net/ssl/openssl_platform_key.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/socket/ssl_client_socket_openssl.cc
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index f2c9cbc664cc4a856916a2b8f2d871950e526894..5489ead7e36861501d492ac80037a7361e452859 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -17,11 +17,14 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/environment.h"
+#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram_macros.h"
#include "base/profiler/scoped_tracker.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_local.h"
#include "base/values.h"
#include "crypto/ec_private_key.h"
@@ -42,6 +45,7 @@
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_failure_state.h"
#include "net/ssl/ssl_info.h"
+#include "net/ssl/ssl_private_key.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
@@ -50,7 +54,7 @@
#if defined(USE_OPENSSL_CERTS)
#include "net/ssl/openssl_client_key_store.h"
#else
-#include "net/ssl/openssl_platform_key.h"
+#include "net/ssl/ssl_platform_key.h"
#endif
namespace net {
@@ -68,7 +72,7 @@ namespace {
// This constant can be any non-negative/non-zero value (eg: it does not
// overlap with any value of the net::Error range, including net::OK).
-const int kNoPendingReadResult = 1;
+const int kNoPendingResult = 1;
// If a client doesn't have a list of protocols that it supports, but
// the server supports NPN, choosing "http/1.1" is the best answer.
@@ -145,6 +149,55 @@ int LogErrorCallback(const char* str, size_t len, void* context) {
return 1;
}
+bool EVP_MDToPrivateKeyHash(const EVP_MD* md, SSLPrivateKey::Hash* hash) {
+ switch (EVP_MD_type(md)) {
+ case NID_md5_sha1:
+ *hash = SSLPrivateKey::Hash::MD5_SHA1;
+ return true;
+ case NID_sha1:
+ *hash = SSLPrivateKey::Hash::SHA1;
+ return true;
+ case NID_sha256:
+ *hash = SSLPrivateKey::Hash::SHA256;
+ return true;
+ case NID_sha384:
+ *hash = SSLPrivateKey::Hash::SHA384;
+ return true;
+ case NID_sha512:
+ *hash = SSLPrivateKey::Hash::SHA512;
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if !defined(USE_OPENSSL_CERTS)
+class PlatformKeyTaskRunner {
+ public:
+ PlatformKeyTaskRunner() {
+ // Serialize all the private key operations on a single background
+ // thread to avoid problems with buggy smartcards.
+ worker_pool_ = new base::SequencedWorkerPool(1, "Platform Key Thread");
+ task_runner_ = worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool_->GetSequenceToken(),
+ base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+ }
+
+ scoped_refptr<base::SequencedTaskRunner> task_runner() {
+ return task_runner_;
+ }
+
+ private:
+ scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformKeyTaskRunner);
+};
+
+base::LazyInstance<PlatformKeyTaskRunner>::Leaky g_platform_key_task_runner =
+ LAZY_INSTANCE_INITIALIZER;
+#endif // !USE_OPENSSL_CERTS
+
} // namespace
class SSLClientSocketOpenSSL::SSLContext {
@@ -165,6 +218,8 @@ class SSLClientSocketOpenSSL::SSLContext {
return SSL_set_ex_data(ssl, ssl_socket_data_index_, socket) != 0;
}
+ static const SSL_PRIVATE_KEY_METHOD kPrivateKeyMethod;
+
private:
friend struct DefaultSingletonTraits<SSLContext>;
@@ -236,6 +291,42 @@ class SSLClientSocketOpenSSL::SSLContext {
return socket->NewSessionCallback(session);
}
+ static int PrivateKeyTypeCallback(SSL* ssl) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ return socket->PrivateKeyTypeCallback();
+ }
+
+ static int PrivateKeySupportsDigestCallback(SSL* ssl, const EVP_MD* md) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ return socket->PrivateKeySupportsDigestCallback(md);
+ }
+
+ static size_t PrivateKeyMaxSignatureLenCallback(SSL* ssl) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ return socket->PrivateKeyMaxSignatureLenCallback();
+ }
+
+ static ssl_private_key_result_t PrivateKeySignCallback(SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ const EVP_MD* md,
+ const uint8_t* in,
+ size_t in_len) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ return socket->PrivateKeySignCallback(out, out_len, max_out, md, in,
+ in_len);
+ }
+
+ static ssl_private_key_result_t PrivateKeySignCompleteCallback(
+ SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ return socket->PrivateKeySignCompleteCallback(out, out_len, max_out);
+ }
+
// This is the index used with SSL_get_ex_data to retrieve the owner
// SSLClientSocketOpenSSL object from an SSL instance.
int ssl_socket_data_index_;
@@ -250,6 +341,15 @@ class SSLClientSocketOpenSSL::SSLContext {
SSLClientSessionCacheOpenSSL session_cache_;
};
+const SSL_PRIVATE_KEY_METHOD
+ SSLClientSocketOpenSSL::SSLContext::kPrivateKeyMethod = {
+ &SSLClientSocketOpenSSL::SSLContext::PrivateKeyTypeCallback,
+ &SSLClientSocketOpenSSL::SSLContext::PrivateKeySupportsDigestCallback,
+ &SSLClientSocketOpenSSL::SSLContext::PrivateKeyMaxSignatureLenCallback,
+ &SSLClientSocketOpenSSL::SSLContext::PrivateKeySignCallback,
+ &SSLClientSocketOpenSSL::SSLContext::PrivateKeySignCompleteCallback,
+};
+
// PeerCertificateChain is a helper object which extracts the certificate
// chain, as given by the server, from an OpenSSL socket and performs the needed
// resource management. The first element of the chain is the leaf certificate
@@ -350,7 +450,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
const SSLClientSocketContext& context)
: transport_send_busy_(false),
transport_recv_busy_(false),
- pending_read_error_(kNoPendingReadResult),
+ pending_read_error_(kNoPendingResult),
pending_read_ssl_error_(SSL_ERROR_NONE),
transport_read_error_(OK),
transport_write_error_(OK),
@@ -372,6 +472,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
session_pending_(false),
certificate_verified_(false),
ssl_failure_state_(SSL_FAILURE_NONE),
+ signature_result_(kNoPendingResult),
transport_security_state_(context.transport_security_state),
policy_enforcer_(context.cert_policy_enforcer),
net_log_(transport_->socket()->NetLog()),
@@ -493,7 +594,7 @@ void SSLClientSocketOpenSSL::Disconnect() {
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
- pending_read_error_ = kNoPendingReadResult;
+ pending_read_error_ = kNoPendingResult;
pending_read_ssl_error_ = SSL_ERROR_NONE;
pending_read_error_info_ = OpenSSLErrorInfo();
@@ -516,6 +617,10 @@ void SSLClientSocketOpenSSL::Disconnect() {
certificate_verified_ = false;
channel_id_request_.Cancel();
ssl_failure_state_ = SSL_FAILURE_NONE;
+
+ private_key_.reset();
+ signature_result_ = kNoPendingResult;
+ signature_.clear();
}
bool SSLClientSocketOpenSSL::IsConnected() const {
@@ -937,6 +1042,12 @@ int SSLClientSocketOpenSSL::DoHandshake() {
!ssl_config_.send_client_cert) {
return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
}
+ if (ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
+ DCHECK(private_key_);
+ DCHECK_NE(kNoPendingResult, signature_result_);
+ GotoState(STATE_HANDSHAKE);
+ return ERR_IO_PENDING;
+ }
OpenSSLErrorInfo error_info;
net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info);
@@ -1254,32 +1365,9 @@ void SSLClientSocketOpenSSL::OnSendComplete(int result) {
return;
}
- // OnSendComplete may need to call DoPayloadRead while the renegotiation
- // handshake is in progress.
- int rv_read = ERR_IO_PENDING;
- int rv_write = ERR_IO_PENDING;
- bool network_moved;
- do {
- if (user_read_buf_.get())
- rv_read = DoPayloadRead();
- if (user_write_buf_.get())
- rv_write = DoPayloadWrite();
- network_moved = DoTransportIO();
- } while (rv_read == ERR_IO_PENDING && rv_write == ERR_IO_PENDING &&
- (user_read_buf_.get() || user_write_buf_.get()) && network_moved);
-
- // Performing the Read callback may cause |this| to be deleted. If this
- // happens, the Write callback should not be invoked. Guard against this by
- // holding a WeakPtr to |this| and ensuring it's still valid.
- base::WeakPtr<SSLClientSocketOpenSSL> guard(weak_factory_.GetWeakPtr());
- if (user_read_buf_.get() && rv_read != ERR_IO_PENDING)
- DoReadCallback(rv_read);
-
- if (!guard.get())
- return;
-
- if (user_write_buf_.get() && rv_write != ERR_IO_PENDING)
- DoWriteCallback(rv_write);
+ // During a renegotiation, a Read call may also be blocked on a transport
+ // write, so retry both operations.
+ PumpReadWriteEvents();
}
void SSLClientSocketOpenSSL::OnRecvComplete(int result) {
@@ -1377,9 +1465,9 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
DCHECK(user_read_buf_.get());
int rv;
- if (pending_read_error_ != kNoPendingReadResult) {
+ if (pending_read_error_ != kNoPendingResult) {
rv = pending_read_error_;
- pending_read_error_ = kNoPendingReadResult;
+ pending_read_error_ = kNoPendingResult;
if (rv == 0) {
net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_RECEIVED,
rv, user_read_buf_->data());
@@ -1421,6 +1509,11 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
} else if (pending_read_ssl_error_ == SSL_ERROR_WANT_X509_LOOKUP &&
!ssl_config_.send_client_cert) {
pending_read_error_ = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+ } else if (pending_read_ssl_error_ ==
+ SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
+ DCHECK(private_key_);
+ DCHECK_NE(kNoPendingResult, signature_result_);
+ pending_read_error_ = ERR_IO_PENDING;
} else {
pending_read_error_ = MapOpenSSLErrorWithDetails(
pending_read_ssl_error_, err_tracer, &pending_read_error_info_);
@@ -1445,12 +1538,12 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
// call to DoPayloadRead(), and thus it is important to check SSL_read() on
// subsequent invocations to see if a complete record may now be read.
if (pending_read_error_ == ERR_IO_PENDING)
- pending_read_error_ = kNoPendingReadResult;
+ pending_read_error_ = kNoPendingResult;
} else {
// No bytes were returned. Return the pending read error immediately.
- DCHECK_NE(kNoPendingReadResult, pending_read_error_);
+ DCHECK_NE(kNoPendingResult, pending_read_error_);
rv = pending_read_error_;
- pending_read_error_ = kNoPendingReadResult;
+ pending_read_error_ = kNoPendingResult;
}
if (rv >= 0) {
@@ -1478,6 +1571,8 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() {
}
int ssl_error = SSL_get_error(ssl_, rv);
+ if (ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION)
+ return ERR_IO_PENDING;
OpenSSLErrorInfo error_info;
int net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer,
&error_info);
@@ -1490,6 +1585,33 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() {
return net_error;
}
+void SSLClientSocketOpenSSL::PumpReadWriteEvents() {
+ int rv_read = ERR_IO_PENDING;
+ int rv_write = ERR_IO_PENDING;
+ bool network_moved;
+ do {
+ if (user_read_buf_.get())
+ rv_read = DoPayloadRead();
+ if (user_write_buf_.get())
+ rv_write = DoPayloadWrite();
+ network_moved = DoTransportIO();
+ } while (rv_read == ERR_IO_PENDING && rv_write == ERR_IO_PENDING &&
+ (user_read_buf_.get() || user_write_buf_.get()) && network_moved);
+
+ // Performing the Read callback may cause |this| to be deleted. If this
+ // happens, the Write callback should not be invoked. Guard against this by
+ // holding a WeakPtr to |this| and ensuring it's still valid.
+ base::WeakPtr<SSLClientSocketOpenSSL> guard(weak_factory_.GetWeakPtr());
+ if (user_read_buf_.get() && rv_read != ERR_IO_PENDING)
+ DoReadCallback(rv_read);
+
+ if (!guard.get())
+ return;
+
+ if (user_write_buf_.get() && rv_write != ERR_IO_PENDING)
+ DoWriteCallback(rv_write);
+}
+
int SSLClientSocketOpenSSL::BufferSend(void) {
if (transport_send_busy_)
return ERR_IO_PENDING;
@@ -1673,18 +1795,18 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) {
return -1;
}
- // TODO(davidben): With Linux client auth support, this should be
- // conditioned on OS_ANDROID and then, with https://crbug.com/394131,
- // removed altogether. OpenSSLClientKeyStore is mostly an artifact of the
- // net/ client auth API lacking a private key handle.
+ if (!SSL_use_certificate(ssl_, leaf_x509.get()) ||
+ !SSL_set1_chain(ssl_, chain.get())) {
+ LOG(WARNING) << "Failed to set client certificate";
+ return -1;
+ }
+
#if defined(USE_OPENSSL_CERTS)
+ // TODO(davidben): Move Android to the SSLPrivateKey codepath and disable
+ // client auth on NaCl altogether.
crypto::ScopedEVP_PKEY privkey =
OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
ssl_config_.client_cert.get());
-#else // !defined(USE_OPENSSL_CERTS)
- crypto::ScopedEVP_PKEY privkey =
- FetchClientCertPrivateKey(ssl_config_.client_cert.get());
-#endif // defined(USE_OPENSSL_CERTS)
if (!privkey) {
// Could not find the private key. Fail the handshake and surface an
// appropriate error to the caller.
@@ -1692,13 +1814,26 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) {
OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY);
return -1;
}
-
- if (!SSL_use_certificate(ssl_, leaf_x509.get()) ||
- !SSL_use_PrivateKey(ssl_, privkey.get()) ||
- !SSL_set1_chain(ssl_, chain.get())) {
- LOG(WARNING) << "Failed to set client certificate";
+ if (!SSL_use_PrivateKey(ssl_, privkey.get())) {
+ LOG(WARNING) << "Failed to set private key";
return -1;
}
+#else // !USE_OPENSSL_CERTS
+ // TODO(davidben): Lift this call up to the embedder so we can actually test
+ // this code. https://crbug.com/394131
+ private_key_ = FetchClientCertPrivateKey(
+ ssl_config_.client_cert.get(),
+ g_platform_key_task_runner.Get().task_runner());
+ if (!private_key_) {
+ // Could not find the private key. Fail the handshake and surface an
+ // appropriate error to the caller.
+ LOG(WARNING) << "Client cert found without private key";
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY);
+ return -1;
+ }
+
+ SSL_set_private_key_method(ssl_, &SSLContext::kPrivateKeyMethod);
+#endif // USE_OPENSSL_CERTS
int cert_count = 1 + sk_X509_num(chain.get());
net_log_.AddEvent(NetLog::TYPE_SSL_CLIENT_CERT_PROVIDED,
@@ -1924,4 +2059,98 @@ bool SSLClientSocketOpenSSL::IsRenegotiationAllowed() const {
return false;
}
+int SSLClientSocketOpenSSL::PrivateKeyTypeCallback() {
+ switch (private_key_->GetType()) {
+ case SSLPrivateKey::Type::RSA:
+ return EVP_PKEY_RSA;
+ case SSLPrivateKey::Type::ECDSA:
+ return EVP_PKEY_EC;
+ }
+ NOTREACHED();
+ return EVP_PKEY_NONE;
+}
+
+int SSLClientSocketOpenSSL::PrivateKeySupportsDigestCallback(const EVP_MD* md) {
+ SSLPrivateKey::Hash hash;
+ return EVP_MDToPrivateKeyHash(md, &hash) && private_key_->SupportsHash(hash);
+}
+
+size_t SSLClientSocketOpenSSL::PrivateKeyMaxSignatureLenCallback() {
+ return private_key_->GetMaxSignatureLengthInBytes();
+}
+
+ssl_private_key_result_t SSLClientSocketOpenSSL::PrivateKeySignCallback(
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ const EVP_MD* md,
+ const uint8_t* in,
+ size_t in_len) {
+ DCHECK_EQ(kNoPendingResult, signature_result_);
+ DCHECK(signature_.empty());
+ DCHECK(private_key_);
+
+ net_log_.BeginEvent(NetLog::TYPE_SSL_PRIVATE_KEY_OPERATION);
+
+ SSLPrivateKey::Hash hash;
+ if (!EVP_MDToPrivateKeyHash(md, &hash)) {
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
+ return ssl_private_key_failure;
+ }
+
+ signature_result_ = ERR_IO_PENDING;
+ private_key_->SignDigest(
+ hash, base::StringPiece(reinterpret_cast<const char*>(in), in_len),
+ base::Bind(&SSLClientSocketOpenSSL::OnPrivateKeySignComplete,
+ weak_factory_.GetWeakPtr()));
+ return ssl_private_key_retry;
+}
+
+ssl_private_key_result_t SSLClientSocketOpenSSL::PrivateKeySignCompleteCallback(
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out) {
+ DCHECK_NE(kNoPendingResult, signature_result_);
+ DCHECK(private_key_);
+
+ if (signature_result_ == ERR_IO_PENDING)
+ return ssl_private_key_retry;
+ if (signature_result_ != OK) {
+ OpenSSLPutNetError(FROM_HERE, signature_result_);
+ return ssl_private_key_failure;
+ }
+ if (signature_.size() > max_out) {
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
+ return ssl_private_key_failure;
+ }
+ memcpy(out, vector_as_array(&signature_), signature_.size());
+ *out_len = signature_.size();
+ signature_.clear();
+ return ssl_private_key_success;
+}
+
+void SSLClientSocketOpenSSL::OnPrivateKeySignComplete(
+ Error error,
+ const std::vector<uint8_t>& signature) {
+ DCHECK_EQ(ERR_IO_PENDING, signature_result_);
+ DCHECK(signature_.empty());
+ DCHECK(private_key_);
+
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_PRIVATE_KEY_OPERATION,
+ error);
+
+ signature_result_ = error;
+ if (signature_result_ == OK)
+ signature_ = signature;
+
+ if (next_handshake_state_ == STATE_HANDSHAKE) {
+ OnHandshakeIOComplete(signature_result_);
+ return;
+ }
+
+ // During a renegotiation, either Read or Write calls may be blocked on an
+ // asynchronous private key operation.
+ PumpReadWriteEvents();
+}
+
} // namespace net
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | net/ssl/openssl_platform_key.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698