OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" |
| 6 |
| 7 #include <set> |
| 8 |
| 9 #include "base/base64.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/stl_util.h" |
| 12 #include "base/time.h" |
| 13 #include "chrome/browser/net/cert_logger.pb.h" |
| 14 #include "net/base/ssl_info.h" |
| 15 #include "net/base/x509_certificate.h" |
| 16 #include "net/url_request/url_request_context.h" |
| 17 |
| 18 namespace chrome_browser_net { |
| 19 |
| 20 ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( |
| 21 net::URLRequestContext* request_context) |
| 22 : request_context_(request_context), |
| 23 upload_url_(FRAUDULENT_CERTIFICATE_UPLOAD_ENDPOINT) { |
| 24 } |
| 25 |
| 26 ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() { |
| 27 STLDeleteElements(&inflight_requests_); |
| 28 } |
| 29 |
| 30 // TODO(palmer): Move this to some globally-visible utility module. |
| 31 static bool DerToPem(const std::string& der_certificate, std::string* output) { |
| 32 std::string b64_encoded; |
| 33 if (!base::Base64Encode(der_certificate, &b64_encoded)) |
| 34 return false; |
| 35 |
| 36 *output = "-----BEGIN CERTIFICATE-----\r\n"; |
| 37 |
| 38 size_t size = b64_encoded.size(); |
| 39 for (size_t i = 0; i < size; ) { |
| 40 size_t todo = size - i; |
| 41 if (todo > 64) |
| 42 todo = 64; |
| 43 *output += b64_encoded.substr(i, todo); |
| 44 *output += "\r\n"; |
| 45 i += todo; |
| 46 } |
| 47 |
| 48 *output += "-----END CERTIFICATE-----\r\n"; |
| 49 return true; |
| 50 } |
| 51 |
| 52 static std::string BuildReport( |
| 53 const std::string& hostname, |
| 54 const net::SSLInfo& ssl_info) { |
| 55 CertLoggerRequest request; |
| 56 base::Time now = base::Time::Now(); |
| 57 request.set_time_usec(now.ToInternalValue()); |
| 58 request.set_hostname(hostname); |
| 59 |
| 60 std::string der_encoded, pem_encoded; |
| 61 |
| 62 net::X509Certificate* certificate = ssl_info.cert; |
| 63 if (!certificate->GetDEREncoded(&der_encoded) || |
| 64 !DerToPem(der_encoded, &pem_encoded)) { |
| 65 LOG(ERROR) << "Could not PEM encode DER certificate"; |
| 66 } |
| 67 |
| 68 std::string* cert_chain = request.mutable_cert_chain(); |
| 69 *cert_chain += pem_encoded; |
| 70 |
| 71 const net::X509Certificate::OSCertHandles& intermediates = |
| 72 certificate->GetIntermediateCertificates(); |
| 73 |
| 74 for (net::X509Certificate::OSCertHandles::const_iterator |
| 75 i = intermediates.begin(); i != intermediates.end(); ++i) { |
| 76 scoped_refptr<net::X509Certificate> cert = |
| 77 net::X509Certificate::CreateFromHandle(*i, intermediates); |
| 78 |
| 79 if (!cert->GetDEREncoded(&der_encoded) || |
| 80 !DerToPem(der_encoded, &pem_encoded)) { |
| 81 LOG(ERROR) << "Could not PEM encode DER certificate"; |
| 82 continue; |
| 83 } |
| 84 |
| 85 *cert_chain += pem_encoded; |
| 86 } |
| 87 |
| 88 std::string out; |
| 89 request.SerializeToString(&out); |
| 90 return out; |
| 91 } |
| 92 |
| 93 net::URLRequest* ChromeFraudulentCertificateReporter::CreateURLRequest() { |
| 94 return new net::URLRequest(upload_url_, this); |
| 95 } |
| 96 |
| 97 void ChromeFraudulentCertificateReporter::SendReport( |
| 98 const std::string& hostname, |
| 99 const net::SSLInfo& ssl_info, |
| 100 bool sni_available) { |
| 101 // We do silent/automatic reporting ONLY for Google properties. For other |
| 102 // domains (when we start supporting that), we will ask for user permission. |
| 103 if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname, |
| 104 sni_available)) { |
| 105 return; |
| 106 } |
| 107 |
| 108 std::string report = BuildReport(hostname, ssl_info); |
| 109 |
| 110 net::URLRequest* url_request = CreateURLRequest(); |
| 111 url_request->set_context(request_context_); |
| 112 url_request->set_method("POST"); |
| 113 url_request->AppendBytesToUpload(report.data(), report.size()); |
| 114 |
| 115 net::HttpRequestHeaders headers; |
| 116 headers.SetHeader(net::HttpRequestHeaders::kContentType, |
| 117 "x-application/chrome-fraudulent-cert-report"); |
| 118 url_request->SetExtraRequestHeaders(headers); |
| 119 |
| 120 inflight_requests_.insert(url_request); |
| 121 url_request->Start(); |
| 122 } |
| 123 |
| 124 void ChromeFraudulentCertificateReporter::RequestComplete( |
| 125 net::URLRequest* request) { |
| 126 std::set<net::URLRequest*>::iterator i = inflight_requests_.find(request); |
| 127 DCHECK(i != inflight_requests_.end()); |
| 128 delete *i; |
| 129 inflight_requests_.erase(i); |
| 130 } |
| 131 |
| 132 // TODO(palmer): Currently, the upload is fire-and-forget but soon we will |
| 133 // try to recover by retrying, and trying different endpoints, and |
| 134 // appealing to the user. |
| 135 void ChromeFraudulentCertificateReporter::OnResponseStarted( |
| 136 net::URLRequest* request) { |
| 137 const net::URLRequestStatus& status(request->status()); |
| 138 if (!status.is_success()) { |
| 139 LOG(WARNING) << "Certificate upload failed" |
| 140 << " status:" << status.status() |
| 141 << " error:" << status.error(); |
| 142 } else if (request->GetResponseCode() != 200) { |
| 143 LOG(WARNING) << "Certificate upload HTTP status: " |
| 144 << request->GetResponseCode(); |
| 145 } |
| 146 RequestComplete(request); |
| 147 } |
| 148 |
| 149 void ChromeFraudulentCertificateReporter::OnReadCompleted( |
| 150 net::URLRequest* request, int bytes_read) {} |
| 151 |
| 152 } // namespace chrome_browser_net |
| 153 |
OLD | NEW |