Index: net/http/transport_security_state.cc |
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc |
index 16217a0c022e9f2149c698e9b45e9b1e1b6ed9ec..2663ec96d3413cb92b6aab4fb4ed1401554bbcd5 100644 |
--- a/net/http/transport_security_state.cc |
+++ b/net/http/transport_security_state.cc |
@@ -24,6 +24,7 @@ |
#include "crypto/sha2.h" |
#include "net/base/host_port_pair.h" |
#include "net/cert/ct_policy_status.h" |
+#include "net/cert/internal/parse_ocsp.h" |
#include "net/cert/x509_cert_types.h" |
#include "net/cert/x509_certificate.h" |
#include "net/dns/dns_util.h" |
@@ -41,11 +42,58 @@ const size_t kMaxHPKPReportCacheEntries = 50; |
const int kTimeToRememberHPKPReportsMins = 60; |
const size_t kReportCacheKeyLength = 16; |
+struct OCSPStaple { |
estark
2016/06/09 21:24:14
could probably use some comments
(in particular,
dadrian
2016/06/10 01:05:52
In retrospect, I think I'm going to move this and
|
+ OCSPStaple(); |
+ ~OCSPStaple(); |
+ |
+ bool is_date_valid; |
+ bool is_correct_certificate; |
+ OCSPCertStatus::Status status; |
+}; |
+ |
+OCSPStaple::OCSPStaple() |
+ : is_date_valid(false), |
+ is_correct_certificate(false), |
+ status(OCSPCertStatus::Status::UNKNOWN) {} |
+ |
+OCSPStaple::~OCSPStaple() {} |
+ |
void RecordUMAForHPKPReportFailure(const GURL& report_uri, int net_error) { |
UMA_HISTOGRAM_SPARSE_SLOWLY("Net.PublicKeyPinReportSendingFailure", |
net_error); |
} |
+der::GeneralizedTime ConvertExplodedTime(const base::Time::Exploded& exploded) { |
+ der::GeneralizedTime result; |
+ result.year = exploded.year; |
+ result.month = exploded.month; |
+ result.day = exploded.day_of_month; |
+ result.hours = exploded.hour; |
+ result.minutes = exploded.minute; |
+ result.seconds = exploded.second; |
+ return result; |
+} |
+ |
+bool CheckOCSPDateValid(bool has_next_update, |
svaldez
2016/06/13 14:03:03
Add a comment to the RFC referencing the this_upda
|
+ const base::Time& check_time, |
+ const der::GeneralizedTime& this_update, |
+ const der::GeneralizedTime& next_update) { |
+ if (has_next_update && !(this_update < next_update)) |
+ return false; |
+ base::Time::Exploded exploded; |
+ check_time.UTCExplode(&exploded); |
+ der::GeneralizedTime check_time_der = ConvertExplodedTime(exploded); |
+ return (this_update < check_time_der) && |
svaldez
2016/06/13 14:03:03
You possibly need a fudge factor in case of clock
|
+ (!has_next_update || check_time_der < next_update); |
estark
2016/06/09 21:24:14
Wait, so it's considered valid if there's no next
dadrian
2016/06/10 01:05:52
This part is still kind of early. nextUpdate is op
estark
2016/06/14 02:10:27
Is this what the new |max_age| parameter addresses
svaldez
2016/06/14 18:56:13
The validity period is set by the OCSP responder.
|
+} |
+ |
+bool CompareCertIDToCertificate(const OCSPCertID& cert_id, |
+ const X509Certificate& certificate) { |
+ // TODO: Verify name and key hashes |
estark
2016/06/09 21:24:14
todo format:
// TODO(dadrian): ... https://crbug.
dadrian
2016/06/10 01:05:52
Acknowledged.
|
+ der::Input serial(&certificate.serial_number()); |
+ return serial == cert_id.serial_number; |
+} |
+ |
std::string TimeToISO8601(const base::Time& t) { |
base::Time::Exploded exploded; |
t.UTCExplode(&exploded); |
@@ -155,6 +203,96 @@ bool GetHPKPReport(const HostPortPair& host_port_pair, |
return true; |
} |
+std::string OCSPCertStatusToString(OCSPCertStatus::Status status) { |
+ switch (status) { |
+ case OCSPCertStatus::Status::GOOD: |
+ return "GOOD"; |
+ case OCSPCertStatus::Status::REVOKED: |
+ return "REVOKED"; |
+ case OCSPCertStatus::Status::UNKNOWN: |
+ return "UNKNOWN"; |
+ default: |
+ NOTREACHED(); |
estark
2016/06/09 21:24:14
nit: I like to write these with no default and jus
dadrian
2016/06/10 01:05:52
Oh, that causes compile failure? Nice.
svaldez
2016/06/13 14:03:03
Might even make sense to move this into the parse_
|
+ return ""; |
+ } |
+} |
+ |
+static bool ParseAndCheckOCSPResponse(const std::string& raw_response, |
estark
2016/06/09 21:24:14
the 'static' shouldn't be necessary
dadrian
2016/06/10 01:05:52
Done.
|
+ const X509Certificate& certificate, |
+ const base::Time& check_time, |
+ std::vector<OCSPStaple>* staples) { |
+ der::Input response_der(&raw_response); |
+ OCSPResponse response; |
+ if (!ParseOCSPResponse(response_der, &response)) |
+ return false; |
+ |
+ // If the OCSP response isn't status SUCCESSFUL, don't parse the rest of the |
+ // data. |
+ if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) |
+ return false; |
estark
2016/06/09 21:24:14
Hrm. Should we be somehow reporting these error co
dadrian
2016/06/10 01:05:52
I was contemplating this as well. An extra top-lev
svaldez
2016/06/13 14:03:04
++, we should consider adding reporting to get a b
|
+ OCSPResponseData response_data; |
+ if (!ParseOCSPResponseData(response.data, &response_data)) |
+ return false; |
+ |
+ bool contains_correct_response = false; |
+ for (const auto& single_response_der : response_data.responses) { |
+ OCSPSingleResponse single_response; |
+ if (!ParseOCSPSingleResponse(single_response_der, &single_response)) |
+ continue; |
+ OCSPStaple staple; |
+ staple.status = single_response.cert_status.status; |
+ staple.is_date_valid = CheckOCSPDateValid( |
+ single_response.has_next_update, check_time, |
+ single_response.this_update, single_response.next_update); |
+ OCSPCertID cert_id; |
+ if (ParseOCSPCertID(single_response.cert_id_tlv, &cert_id)) { |
+ staple.is_correct_certificate = |
+ CompareCertIDToCertificate(cert_id, certificate); |
+ } |
+ if (staple.is_date_valid && staple.is_correct_certificate && |
+ staple.status == OCSPCertStatus::Status::GOOD) { |
+ contains_correct_response = true; |
+ } |
+ staples->push_back(staple); |
+ } |
+ return contains_correct_response; |
+} |
+ |
+bool GetExpectStapleReport( |
+ const HostPortPair& host_port_pair, |
+ const TransportSecurityState::ExpectStapleState& expect_staple_state, |
+ const X509Certificate& certificate, |
+ const base::Time& check_time, |
+ const std::vector<OCSPStaple>& ocsp_staples, |
+ std::string* serialized_report) { |
+ base::DictionaryValue report; |
+ report.SetString("date-time", TimeToISO8601(check_time)); |
+ report.SetString("hostname", host_port_pair.host()); |
+ report.SetInteger("port", host_port_pair.port()); |
+ |
+ // Add the list of each stapled OCSP response |
+ std::unique_ptr<base::Value> ocsp_responses(new base::ListValue); |
+ for (const auto& staple : ocsp_staples) { |
+ std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue); |
+ response->SetBoolean("is-date-valid", staple.is_date_valid); |
+ response->SetBoolean("is-correct-certificate", |
+ staple.is_correct_certificate); |
+ response->SetString("status", OCSPCertStatusToString(staple.status)); |
+ base::ListValue* responses_list = |
+ reinterpret_cast<base::ListValue*>(ocsp_responses.get()); |
+ responses_list->Append(std::move(response)); |
+ } |
+ report.Set("ocsp-responses", std::move(ocsp_responses)); |
+ report.Set("served-certificate-chain", |
+ GetPEMEncodedChainAsList(&certificate)); |
+ |
+ if (!base::JSONWriter::Write(report, serialized_report)) { |
+ LOG(ERROR) << "Failed to serialize Expect-Staple report"; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
// Do not send a report over HTTPS to the same host that set the |
// pin. Such report URIs will result in loops. (A.com has a pinning |
// violation which results in a report being sent to A.com, which |
@@ -617,8 +755,8 @@ TransportSecurityState::TransportSecurityState() |
report_sender_(nullptr), |
enable_static_pins_(true), |
enable_static_expect_ct_(true), |
- enable_static_expect_staple_(false), |
expect_ct_reporter_(nullptr), |
svaldez
2016/06/13 14:03:03
Extra change?
dadrian
2016/06/13 23:03:32
Formatting error. Fixed.
|
+ enable_static_expect_staple_(false), |
sent_reports_cache_(kMaxHPKPReportCacheEntries) { |
// Static pinning is only enabled for official builds to make sure that |
// others don't end up with pins that cannot be easily updated. |
@@ -1098,6 +1236,31 @@ void TransportSecurityState::ProcessExpectCTHeader( |
ssl_info); |
} |
+void TransportSecurityState::CheckExpectStaple( |
+ const HostPortPair& host_port_pair, |
+ const ExpectStapleState& expect_staple_state, |
+ const X509Certificate& certificate, |
+ const std::string& ocsp_response) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!enable_static_expect_staple_ || !report_sender_) |
estark
2016/06/09 21:24:14
might as well check for an empty report_uri here a
dadrian
2016/06/10 01:05:52
Done.
|
+ return; |
+ |
+ // Check the stapled information. |
+ std::vector<OCSPStaple> ocsp_staples; |
+ base::Time check_time = base::Time::Now(); |
+ if (ParseAndCheckOCSPResponse(ocsp_response, certificate, check_time, |
+ &ocsp_staples)) |
+ return; |
+ |
+ // Report on failure. |
+ std::string serialized_report; |
+ if (!GetExpectStapleReport(host_port_pair, expect_staple_state, certificate, |
+ check_time, ocsp_staples, &serialized_report)) { |
estark
2016/06/09 21:24:14
nit: curly braces inconsistent with line 1251
dadrian
2016/06/10 01:05:52
Done.
|
+ return; |
+ } |
+ report_sender_->Send(expect_staple_state.report_uri, serialized_report); |
+} |
+ |
// static |
void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
PreloadResult result; |