summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/ssl_client_auth_cache.cc15
-rw-r--r--net/base/ssl_client_auth_cache.h18
-rw-r--r--net/base/ssl_client_auth_cache_unittest.cc89
-rw-r--r--net/http/http_network_transaction.cc53
4 files changed, 131 insertions, 44 deletions
diff --git a/net/base/ssl_client_auth_cache.cc b/net/base/ssl_client_auth_cache.cc
index d2f47cc..355073f 100644
--- a/net/base/ssl_client_auth_cache.cc
+++ b/net/base/ssl_client_auth_cache.cc
@@ -4,15 +4,26 @@
#include "net/base/ssl_client_auth_cache.h"
+#include "base/logging.h"
+#include "net/base/x509_certificate.h"
+
namespace net {
SSLClientAuthCache::SSLClientAuthCache() {}
SSLClientAuthCache::~SSLClientAuthCache() {}
-X509Certificate* SSLClientAuthCache::Lookup(const std::string& server) {
+bool SSLClientAuthCache::Lookup(
+ const std::string& server,
+ scoped_refptr<X509Certificate>* certificate) {
+ DCHECK(certificate);
+
AuthCacheMap::iterator iter = cache_.find(server);
- return (iter == cache_.end()) ? NULL : iter->second;
+ if (iter == cache_.end())
+ return false;
+
+ *certificate = iter->second;
+ return true;
}
void SSLClientAuthCache::Add(const std::string& server,
diff --git a/net/base/ssl_client_auth_cache.h b/net/base/ssl_client_auth_cache.h
index 023480b..2b276a2 100644
--- a/net/base/ssl_client_auth_cache.h
+++ b/net/base/ssl_client_auth_cache.h
@@ -10,10 +10,11 @@
#include <map>
#include "base/ref_counted.h"
-#include "net/base/x509_certificate.h"
namespace net {
+class X509Certificate;
+
// The SSLClientAuthCache class is a simple cache structure to store SSL
// client certificates. Provides lookup, insertion, and deletion of entries.
// The parameter for doing lookups, insertions, and deletions is the server's
@@ -26,13 +27,18 @@ class SSLClientAuthCache {
SSLClientAuthCache();
~SSLClientAuthCache();
- // Check if we have a client certificate for SSL server at |server|.
- // Returns the client certificate (if found) or NULL (if not found).
- X509Certificate* Lookup(const std::string& server);
+ // Checks for a client certificate preference for SSL server at |server|.
+ // Returns true if a preference is found, and sets |*certificate| to the
+ // desired client certificate. The desired certificate may be NULL, which
+ // indicates a preference to not send any certificate to |server|.
+ // If a certificate preference is not found, returns false.
+ bool Lookup(const std::string& server,
+ scoped_refptr<X509Certificate>* certificate);
// Add a client certificate for |server| to the cache. If there is already
- // a client certificate for |server|, it will be overwritten. Both parameters
- // are IN only.
+ // a client certificate for |server|, it will be overwritten. A NULL
+ // |client_cert| indicates a preference that no client certificate should
+ // be sent to |server|.
void Add(const std::string& server, X509Certificate* client_cert);
// Remove the client certificate for |server| from the cache, if one exists.
diff --git a/net/base/ssl_client_auth_cache_unittest.cc b/net/base/ssl_client_auth_cache_unittest.cc
index 85b3d5e..f528d58 100644
--- a/net/base/ssl_client_auth_cache_unittest.cc
+++ b/net/base/ssl_client_auth_cache_unittest.cc
@@ -5,6 +5,7 @@
#include "net/base/ssl_client_auth_cache.h"
#include "base/time.h"
+#include "net/base/x509_certificate.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -27,32 +28,50 @@ TEST(SSLClientAuthCacheTest, LookupAddRemove) {
scoped_refptr<X509Certificate> cert3(
new X509Certificate("foo3", "CA", start_date, expiration_date));
+ scoped_refptr<X509Certificate> cached_cert;
// Lookup non-existent client certificate.
- EXPECT_TRUE(cache.Lookup(server1) == NULL);
+ cached_cert = NULL;
+ EXPECT_FALSE(cache.Lookup(server1, &cached_cert));
// Add client certificate for server1.
- cache.Add(server1, cert1.get());
- EXPECT_EQ(cert1.get(), cache.Lookup(server1));
+ cache.Add(server1, cert1);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(cert1, cached_cert);
// Add client certificate for server2.
- cache.Add(server2, cert2.get());
- EXPECT_EQ(cert1.get(), cache.Lookup(server1));
- EXPECT_EQ(cert2.get(), cache.Lookup(server2));
+ cache.Add(server2, cert2);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(cert1, cached_cert.get());
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server2, &cached_cert));
+ EXPECT_EQ(cert2, cached_cert);
// Overwrite the client certificate for server1.
- cache.Add(server1, cert3.get());
- EXPECT_EQ(cert3.get(), cache.Lookup(server1));
- EXPECT_EQ(cert2.get(), cache.Lookup(server2));
+ cache.Add(server1, cert3);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(cert3, cached_cert);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server2, &cached_cert));
+ EXPECT_EQ(cert2, cached_cert);
// Remove client certificate of server1.
cache.Remove(server1);
- EXPECT_TRUE(cache.Lookup(server1) == NULL);
- EXPECT_EQ(cert2.get(), cache.Lookup(server2));
+ cached_cert = NULL;
+ EXPECT_FALSE(cache.Lookup(server1, &cached_cert));
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server2, &cached_cert));
+ EXPECT_EQ(cert2, cached_cert);
// Remove non-existent client certificate.
cache.Remove(server1);
- EXPECT_TRUE(cache.Lookup(server1) == NULL);
- EXPECT_EQ(cert2.get(), cache.Lookup(server2));
+ cached_cert = NULL;
+ EXPECT_FALSE(cache.Lookup(server1, &cached_cert));
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server2, &cached_cert));
+ EXPECT_EQ(cert2, cached_cert);
}
// Check that if the server differs only by port number, it is considered
@@ -74,8 +93,48 @@ TEST(SSLClientAuthCacheTest, LookupWithPort) {
cache.Add(server1, cert1.get());
cache.Add(server2, cert2.get());
- EXPECT_EQ(cert1.get(), cache.Lookup(server1));
- EXPECT_EQ(cert2.get(), cache.Lookup(server2));
+ scoped_refptr<X509Certificate> cached_cert;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(cert1.get(), cached_cert);
+ EXPECT_TRUE(cache.Lookup(server2, &cached_cert));
+ EXPECT_EQ(cert2.get(), cached_cert);
+}
+
+// Check that the a NULL certificate, indicating the user has declined to send
+// a certificate, is properly cached.
+TEST(SSLClientAuthCacheTest, LookupNullPreference) {
+ SSLClientAuthCache cache;
+ base::Time start_date = base::Time::Now();
+ base::Time expiration_date = start_date + base::TimeDelta::FromDays(1);
+
+ std::string server1("foo:443");
+ scoped_refptr<X509Certificate> cert1(
+ new X509Certificate("foo", "CA", start_date, expiration_date));
+
+ cache.Add(server1, NULL);
+
+ scoped_refptr<X509Certificate> cached_cert(cert1);
+ // Make sure that |cached_cert| is updated to NULL, indicating the user
+ // declined to send a certificate to |server1|.
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(NULL, cached_cert.get());
+
+ // Remove the existing cached certificate.
+ cache.Remove(server1);
+ cached_cert = NULL;
+ EXPECT_FALSE(cache.Lookup(server1, &cached_cert));
+
+ // Add a new preference for a specific certificate.
+ cache.Add(server1, cert1);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(cert1, cached_cert);
+
+ // Replace the specific preference with a NULL certificate.
+ cache.Add(server1, NULL);
+ cached_cert = NULL;
+ EXPECT_TRUE(cache.Lookup(server1, &cached_cert));
+ EXPECT_EQ(NULL, cached_cert.get());
}
} // namespace net
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 3e1d8d1..c84dfa4 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -96,7 +96,6 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
session->ssl_config_service()->GetSSLConfig(&ssl_config_);
if (session->http_stream_factory()->next_protos())
ssl_config_.next_protos = *session->http_stream_factory()->next_protos();
-
}
HttpNetworkTransaction::~HttpNetworkTransaction() {
@@ -171,10 +170,8 @@ int HttpNetworkTransaction::RestartWithCertificate(
DCHECK_EQ(STATE_NONE, next_state_);
ssl_config_.client_cert = client_cert;
- if (client_cert) {
- session_->ssl_client_auth_cache()->Add(
- response_.cert_request_info->host_and_port, client_cert);
- }
+ session_->ssl_client_auth_cache()->Add(
+ response_.cert_request_info->host_and_port, client_cert);
ssl_config_.send_client_cert = true;
// Reset the other member variables.
// Note: this is necessary only with SSL renegotiation.
@@ -971,29 +968,43 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) {
// handshake.
stream_request_.reset();
- // If the user selected one of the certificate in client_certs for this
- // server before, use it automatically.
- X509Certificate* client_cert = session_->ssl_client_auth_cache()->Lookup(
- response_.cert_request_info->host_and_port);
+ // If the user selected one of the certificates in client_certs or declined
+ // to provide one for this server before, use the past decision
+ // automatically.
+ scoped_refptr<X509Certificate> client_cert;
+ bool found_cached_cert = session_->ssl_client_auth_cache()->Lookup(
+ response_.cert_request_info->host_and_port, &client_cert);
+ if (!found_cached_cert)
+ return error;
+
+ // Check that the certificate selected is still a certificate the server
+ // is likely to accept, based on the criteria supplied in the
+ // CertificateRequest message.
if (client_cert) {
const std::vector<scoped_refptr<X509Certificate> >& client_certs =
response_.cert_request_info->client_certs;
+ bool cert_still_valid = false;
for (size_t i = 0; i < client_certs.size(); ++i) {
- if (client_cert->fingerprint().Equals(client_certs[i]->fingerprint())) {
- // TODO(davidben): Add a unit test which covers this path; we need to be
- // able to send a legitimate certificate and also bypass/clear the
- // SSL session cache.
- ssl_config_.client_cert = client_cert;
- ssl_config_.send_client_cert = true;
- next_state_ = STATE_CREATE_STREAM;
- // Reset the other member variables.
- // Note: this is necessary only with SSL renegotiation.
- ResetStateForRestart();
- return OK;
+ if (client_cert->Equals(client_certs[i])) {
+ cert_still_valid = true;
+ break;
}
}
+
+ if (!cert_still_valid)
+ return error;
}
- return error;
+
+ // TODO(davidben): Add a unit test which covers this path; we need to be
+ // able to send a legitimate certificate and also bypass/clear the
+ // SSL session cache.
+ ssl_config_.client_cert = client_cert;
+ ssl_config_.send_client_cert = true;
+ next_state_ = STATE_CREATE_STREAM;
+ // Reset the other member variables.
+ // Note: this is necessary only with SSL renegotiation.
+ ResetStateForRestart();
+ return OK;
}
// This method determines whether it is safe to resend the request after an