Chromium Code Reviews| 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; |