Skip to content

Commit

Permalink
Log the (server, to-be-verified) certificate chains to the NetLog whe…
Browse files Browse the repository at this point in the history
…n logging bytes.

BUG=91464


Review URL: http://codereview.chromium.org/8413072

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108615 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
rsleevi@chromium.org committed Nov 4, 2011
1 parent 9b15b2f commit 5946838
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 48 deletions.
49 changes: 5 additions & 44 deletions chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,6 @@ ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() {
STLDeleteElements(&inflight_requests_);
}

// TODO(palmer): Move this to some globally-visible utility module.
static bool DerToPem(const std::string& der_certificate, std::string* output) {
std::string b64_encoded;
if (!base::Base64Encode(der_certificate, &b64_encoded))
return false;

*output = "-----BEGIN CERTIFICATE-----\r\n";

size_t size = b64_encoded.size();
for (size_t i = 0; i < size; ) {
size_t todo = size - i;
if (todo > 64)
todo = 64;
*output += b64_encoded.substr(i, todo);
*output += "\r\n";
i += todo;
}

*output += "-----END CERTIFICATE-----\r\n";
return true;
}

static std::string BuildReport(
const std::string& hostname,
const net::SSLInfo& ssl_info) {
Expand All @@ -62,30 +40,13 @@ static std::string BuildReport(
request.set_time_usec(now.ToInternalValue());
request.set_hostname(hostname);

std::string der_encoded, pem_encoded;

net::X509Certificate* certificate = ssl_info.cert;
if (!net::X509Certificate::GetDEREncoded(certificate->os_cert_handle(),
&der_encoded) ||
!DerToPem(der_encoded, &pem_encoded)) {
LOG(ERROR) << "Could not PEM encode DER certificate";
std::vector<std::string> pem_encoded_chain;
if (!ssl_info.cert->GetPEMEncodedChain(&pem_encoded_chain)) {
LOG(ERROR) << "Could not get PEM encoded chain.";
}

std::string* cert_chain = request.mutable_cert_chain();
*cert_chain += pem_encoded;

const net::X509Certificate::OSCertHandles& intermediates =
certificate->GetIntermediateCertificates();
for (size_t i = 0; i < intermediates.size(); ++i) {
if (!net::X509Certificate::GetDEREncoded(intermediates[i],
&der_encoded) ||
!DerToPem(der_encoded, &pem_encoded)) {
LOG(ERROR) << "Could not PEM encode DER certificate";
continue;
}

*cert_chain += pem_encoded;
}
for (size_t i = 0; i < pem_encoded_chain.size(); ++i)
*cert_chain += pem_encoded_chain[i];

std::string out;
request.SerializeToString(&out);
Expand Down
10 changes: 9 additions & 1 deletion net/base/cert_verifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/x509_certificate.h"
#include "net/base/x509_certificate_net_log_param.h"

#if defined(USE_NSS)
#include <private/pprthred.h> // PR_DetachThread
Expand Down Expand Up @@ -155,6 +156,10 @@ class CertVerifierWorker {
error_(ERR_FAILED) {
}

// Returns the certificate being verified. May only be called /before/
// Start() is called.
X509Certificate* certificate() const { return cert_; }

bool Start() {
DCHECK_EQ(MessageLoop::current(), origin_loop_);

Expand Down Expand Up @@ -261,7 +266,10 @@ class CertVerifierJob {
: start_time_(base::TimeTicks::Now()),
worker_(worker),
net_log_(net_log) {
net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_JOB, NULL);
scoped_refptr<NetLog::EventParameters> params;
if (net_log_.IsLoggingBytes())
params = new X509CertificateNetLogParam(worker_->certificate());
net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_JOB, params);
}

~CertVerifierJob() {
Expand Down
16 changes: 16 additions & 0 deletions net/base/net_log_event_type_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ EVENT_TYPE(SSL_SOCKET_BYTES_SENT)
EVENT_TYPE(SOCKET_BYTES_RECEIVED)
EVENT_TYPE(SSL_SOCKET_BYTES_RECEIVED)

// Certificates were received from the SSL server (during a handshake or
// renegotiation). This event is only present when logging at LOG_ALL.
// The following parameters are attached to the event:
// {
// "certificates": <A list of PEM encoded certificates in the order that
// they were sent by the server>,
// }
EVENT_TYPE(SSL_CERTIFICATES_RECEIVED)

// ------------------------------------------------------------------------
// DatagramSocket
// ------------------------------------------------------------------------
Expand Down Expand Up @@ -1320,6 +1329,13 @@ EVENT_TYPE(CHROME_POLICY_ABORTED_REQUEST)
EVENT_TYPE(CERT_VERIFIER_REQUEST)

// This event is created when we start a CertVerifier job.
// The END phase event parameters are:
// {
// "certificates": <A list of PEM encoded certificates, the first one
// being the certificate to verify and the remaining
// being intermediate certificates to assist path
// building. Only present when byte logging is enabled.>
// }
EVENT_TYPE(CERT_VERIFIER_JOB)

// This event is created when a CertVerifier request attaches to a job.
Expand Down
41 changes: 41 additions & 0 deletions net/base/x509_certificate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string>
#include <vector>

#include "base/base64.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
Expand Down Expand Up @@ -601,6 +602,46 @@ bool X509Certificate::VerifyNameMatch(const std::string& hostname) const {
}
#endif

// static
bool X509Certificate::GetPEMEncoded(OSCertHandle cert_handle,
std::string* pem_encoded) {
std::string der_encoded;
if (!GetDEREncoded(cert_handle, &der_encoded) || der_encoded.empty())
return false;
std::string b64_encoded;
if (!base::Base64Encode(der_encoded, &b64_encoded) || b64_encoded.empty())
return false;
*pem_encoded = "-----BEGIN CERTIFICATE-----\n";

// Divide the Base-64 encoded data into 64-character chunks, as per
// 4.3.2.4 of RFC 1421.
static const size_t kChunkSize = 64;
size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize;
for (size_t i = 0, chunk_offset = 0; i < chunks;
++i, chunk_offset += kChunkSize) {
pem_encoded->append(b64_encoded, chunk_offset, kChunkSize);
pem_encoded->append("\n");
}
pem_encoded->append("-----END CERTIFICATE-----\n");
return true;
}

bool X509Certificate::GetPEMEncodedChain(
std::vector<std::string>* pem_encoded) const {
std::vector<std::string> encoded_chain;
std::string pem_data;
if (!GetPEMEncoded(os_cert_handle(), &pem_data))
return false;
encoded_chain.push_back(pem_data);
for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
if (!GetPEMEncoded(intermediate_ca_certs_[i], &pem_data))
return false;
encoded_chain.push_back(pem_data);
}
pem_encoded->swap(encoded_chain);
return true;
}

X509Certificate::X509Certificate(OSCertHandle cert_handle,
const OSCertHandles& intermediates)
: cert_handle_(DupOSCertHandle(cert_handle)) {
Expand Down
12 changes: 12 additions & 0 deletions net/base/x509_certificate.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,18 @@ class NET_EXPORT X509Certificate
static bool GetDEREncoded(OSCertHandle cert_handle,
std::string* der_encoded);

// Returns the PEM encoded data from an OSCertHandle. If the return value is
// true, then the PEM encoded certificate is written to |pem_encoded|.
static bool GetPEMEncoded(OSCertHandle cert_handle,
std::string* pem_encoded);

// Encodes the entire certificate chain (this certificate and any
// intermediate certificates stored in |intermediate_ca_certs_|) as a series
// of PEM encoded strings. Returns true if all certificates were encoded,
// storig the result in |*pem_encoded|, with this certificate stored as
// the first element.
bool GetPEMEncodedChain(std::vector<std::string>* pem_encoded) const;

// Returns the OSCertHandle of this object. Because of caching, this may
// differ from the OSCertHandle originally supplied during initialization.
// Note: On Windows, CryptoAPI may return unexpected results if this handle
Expand Down
28 changes: 28 additions & 0 deletions net/base/x509_certificate_net_log_param.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/x509_certificate_net_log_param.h"

#include "base/values.h"
#include "net/base/x509_certificate.h"

namespace net {

X509CertificateNetLogParam::X509CertificateNetLogParam(
X509Certificate* certificate) {
certificate->GetPEMEncodedChain(&encoded_chain_);
}

X509CertificateNetLogParam::~X509CertificateNetLogParam() {}

base::Value* X509CertificateNetLogParam::ToValue() const {
DictionaryValue* dict = new DictionaryValue();
ListValue* certs = new ListValue();
for (size_t i = 0; i < encoded_chain_.size(); ++i)
certs->Append(base::Value::CreateStringValue(encoded_chain_[i]));
dict->Set("certificates", certs);
return dict;
}

} // namespace net
35 changes: 35 additions & 0 deletions net/base/x509_certificate_net_log_param.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_BASE_X509_CERT_NET_LOG_PARAM_H_
#define NET_BASE_X509_CERT_NET_LOG_PARAM_H_
#pragma once

#include <string>
#include <vector>

#include "base/compiler_specific.h"
#include "net/base/net_log.h"

namespace net {

class X509Certificate;

// NetLog parameter to describe an X509Certificate.
// Note: These parameters are memory intensive, due to PEM-encoding each
// certificate, thus should only be used when logging at NetLog::LOG_ALL.
class X509CertificateNetLogParam : public NetLog::EventParameters {
public:
explicit X509CertificateNetLogParam(X509Certificate* certificate);
virtual ~X509CertificateNetLogParam();

virtual base::Value* ToValue() const OVERRIDE;

private:
std::vector<std::string> encoded_chain_;
};

} // namespace net

#endif // NET_BASE_X509_CERT_NET_LOG_PARAM_H_
2 changes: 2 additions & 0 deletions net/net.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@
'base/x509_certificate.cc',
'base/x509_certificate.h',
'base/x509_certificate_mac.cc',
'base/x509_certificate_net_log_param.cc',
'base/x509_certificate_net_log_param.h',
'base/x509_certificate_nss.cc',
'base/x509_certificate_openssl.cc',
'base/x509_certificate_win.cc',
Expand Down
6 changes: 6 additions & 0 deletions net/socket/ssl_client_socket_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "net/base/ssl_cert_request_info.h"
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/x509_certificate_net_log_param.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_error_params.h"

Expand Down Expand Up @@ -1284,6 +1285,11 @@ int SSLClientSocketMac::DidCompleteHandshake() {
GetServerCert(ssl_context_));
if (!new_server_cert)
return ERR_UNEXPECTED;
if (net_log_.IsLoggingBytes()) {
net_log_.AddEvent(
NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
make_scoped_refptr(new X509CertificateNetLogParam(new_server_cert)));
}

if (renegotiating_ &&
X509Certificate::IsSameOSCert(server_cert_->os_cert_handle(),
Expand Down
6 changes: 6 additions & 0 deletions net/socket/ssl_client_socket_nss.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/sys_addrinfo.h"
#include "net/base/x509_certificate_net_log_param.h"
#include "net/ocsp/nss_ocsp.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/dns_cert_provenance_checker.h"
Expand Down Expand Up @@ -1055,6 +1056,11 @@ void SSLClientSocketNSS::UpdateServerCert() {
// case CreateFromDERCertChain() returns NULL.
server_cert_ = X509Certificate::CreateFromDERCertChain(
certs.AsStringPieceVector());
if (server_cert_ && net_log_.IsLoggingBytes()) {
net_log_.AddEvent(
NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
make_scoped_refptr(new X509CertificateNetLogParam(server_cert_)));
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions net/socket/ssl_client_socket_openssl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "net/base/ssl_cert_request_info.h"
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/x509_certificate_net_log_param.h"
#include "net/socket/ssl_error_params.h"

namespace net {
Expand Down Expand Up @@ -760,6 +761,11 @@ int SSLClientSocketOpenSSL::DoHandshake() {
// SSL handshake is completed. Let's verify the certificate.
const bool got_cert = !!UpdateServerCert();
DCHECK(got_cert);
if (net_log_.IsLoggingBytes()) {
net_log_.AddEvent(
NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
make_scoped_refptr(new X509CertificateNetLogParam(server_cert_)));
}
GotoState(STATE_VERIFY_CERT);
} else {
int ssl_error = SSL_get_error(ssl_, rv);
Expand Down
13 changes: 10 additions & 3 deletions net/socket/ssl_client_socket_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "net/base/ssl_cert_request_info.h"
#include "net/base/ssl_connection_status_flags.h"
#include "net/base/ssl_info.h"
#include "net/base/x509_certificate_net_log_param.h"
#include "net/socket/client_socket_handle.h"

#pragma comment(lib, "secur32.lib")
Expand Down Expand Up @@ -1519,16 +1520,22 @@ int SSLClientSocketWin::DidCompleteHandshake() {
LOG(ERROR) << "QueryContextAttributes (remote cert) failed: " << status;
return MapSecurityError(status);
}
scoped_refptr<X509Certificate> new_server_cert(
X509Certificate::CreateFromHandle(server_cert_handle,
X509Certificate::OSCertHandles()));
if (net_log_.IsLoggingBytes()) {
net_log_.AddEvent(
NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
make_scoped_refptr(new X509CertificateNetLogParam(new_server_cert)));
}
if (renegotiating_ &&
X509Certificate::IsSameOSCert(server_cert_->os_cert_handle(),
server_cert_handle)) {
// We already verified the server certificate. Either it is good or the
// user has accepted the certificate error.
DidCompleteRenegotiation();
} else {
server_cert_ = X509Certificate::CreateFromHandle(
server_cert_handle, X509Certificate::OSCertHandles());

server_cert_ = new_server_cert;
next_state_ = STATE_VERIFY_CERT;
}
CertFreeCertificateContext(server_cert_handle);
Expand Down

0 comments on commit 5946838

Please sign in to comment.