| Index: chrome/browser/net/chrome_fraudulent_certificate_reporter.cc
|
| ===================================================================
|
| --- chrome/browser/net/chrome_fraudulent_certificate_reporter.cc (revision 0)
|
| +++ chrome/browser/net/chrome_fraudulent_certificate_reporter.cc (revision 0)
|
| @@ -0,0 +1,153 @@
|
| +// 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 "chrome/browser/net/chrome_fraudulent_certificate_reporter.h"
|
| +
|
| +#include <set>
|
| +
|
| +#include "base/base64.h"
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/time.h"
|
| +#include "chrome/browser/net/cert_logger.pb.h"
|
| +#include "net/base/ssl_info.h"
|
| +#include "net/base/x509_certificate.h"
|
| +#include "net/url_request/url_request_context.h"
|
| +
|
| +namespace chrome_browser_net {
|
| +
|
| +ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter(
|
| + net::URLRequestContext* request_context)
|
| + : request_context_(request_context),
|
| + upload_url_(FRAUDULENT_CERTIFICATE_UPLOAD_ENDPOINT) {
|
| +}
|
| +
|
| +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) {
|
| + CertLoggerRequest request;
|
| + base::Time now = base::Time::Now();
|
| + request.set_time_usec(now.ToInternalValue());
|
| + request.set_hostname(hostname);
|
| +
|
| + std::string der_encoded, pem_encoded;
|
| +
|
| + net::X509Certificate* certificate = ssl_info.cert;
|
| + if (!certificate->GetDEREncoded(&der_encoded) ||
|
| + !DerToPem(der_encoded, &pem_encoded)) {
|
| + LOG(ERROR) << "Could not PEM encode DER certificate";
|
| + }
|
| +
|
| + std::string* cert_chain = request.mutable_cert_chain();
|
| + *cert_chain += pem_encoded;
|
| +
|
| + const net::X509Certificate::OSCertHandles& intermediates =
|
| + certificate->GetIntermediateCertificates();
|
| +
|
| + for (net::X509Certificate::OSCertHandles::const_iterator
|
| + i = intermediates.begin(); i != intermediates.end(); ++i) {
|
| + scoped_refptr<net::X509Certificate> cert =
|
| + net::X509Certificate::CreateFromHandle(*i, intermediates);
|
| +
|
| + if (!cert->GetDEREncoded(&der_encoded) ||
|
| + !DerToPem(der_encoded, &pem_encoded)) {
|
| + LOG(ERROR) << "Could not PEM encode DER certificate";
|
| + continue;
|
| + }
|
| +
|
| + *cert_chain += pem_encoded;
|
| + }
|
| +
|
| + std::string out;
|
| + request.SerializeToString(&out);
|
| + return out;
|
| +}
|
| +
|
| +net::URLRequest* ChromeFraudulentCertificateReporter::CreateURLRequest() {
|
| + return new net::URLRequest(upload_url_, this);
|
| +}
|
| +
|
| +void ChromeFraudulentCertificateReporter::SendReport(
|
| + const std::string& hostname,
|
| + const net::SSLInfo& ssl_info,
|
| + bool sni_available) {
|
| + // We do silent/automatic reporting ONLY for Google properties. For other
|
| + // domains (when we start supporting that), we will ask for user permission.
|
| + if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname,
|
| + sni_available)) {
|
| + return;
|
| + }
|
| +
|
| + std::string report = BuildReport(hostname, ssl_info);
|
| +
|
| + net::URLRequest* url_request = CreateURLRequest();
|
| + url_request->set_context(request_context_);
|
| + url_request->set_method("POST");
|
| + url_request->AppendBytesToUpload(report.data(), report.size());
|
| +
|
| + net::HttpRequestHeaders headers;
|
| + headers.SetHeader(net::HttpRequestHeaders::kContentType,
|
| + "x-application/chrome-fraudulent-cert-report");
|
| + url_request->SetExtraRequestHeaders(headers);
|
| +
|
| + inflight_requests_.insert(url_request);
|
| + url_request->Start();
|
| +}
|
| +
|
| +void ChromeFraudulentCertificateReporter::RequestComplete(
|
| + net::URLRequest* request) {
|
| + std::set<net::URLRequest*>::iterator i = inflight_requests_.find(request);
|
| + DCHECK(i != inflight_requests_.end());
|
| + delete *i;
|
| + inflight_requests_.erase(i);
|
| +}
|
| +
|
| +// TODO(palmer): Currently, the upload is fire-and-forget but soon we will
|
| +// try to recover by retrying, and trying different endpoints, and
|
| +// appealing to the user.
|
| +void ChromeFraudulentCertificateReporter::OnResponseStarted(
|
| + net::URLRequest* request) {
|
| + const net::URLRequestStatus& status(request->status());
|
| + if (!status.is_success()) {
|
| + LOG(WARNING) << "Certificate upload failed"
|
| + << " status:" << status.status()
|
| + << " error:" << status.error();
|
| + } else if (request->GetResponseCode() != 200) {
|
| + LOG(WARNING) << "Certificate upload HTTP status: "
|
| + << request->GetResponseCode();
|
| + }
|
| + RequestComplete(request);
|
| +}
|
| +
|
| +void ChromeFraudulentCertificateReporter::OnReadCompleted(
|
| + net::URLRequest* request, int bytes_read) {}
|
| +
|
| +} // namespace chrome_browser_net
|
| +
|
|
|