OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/transport_security_state.h" | 5 #include "net/http/transport_security_state.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <memory> | 8 #include <memory> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/base64.h" | 11 #include "base/base64.h" |
12 #include "base/build_time.h" | 12 #include "base/build_time.h" |
13 #include "base/json/json_writer.h" | 13 #include "base/json/json_writer.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
17 #include "base/metrics/sparse_histogram.h" | 17 #include "base/metrics/sparse_histogram.h" |
18 #include "base/sha1.h" | 18 #include "base/sha1.h" |
19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
20 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
21 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
22 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
23 #include "base/values.h" | 23 #include "base/values.h" |
24 #include "crypto/sha2.h" | 24 #include "crypto/sha2.h" |
25 #include "net/base/host_port_pair.h" | 25 #include "net/base/host_port_pair.h" |
26 #include "net/cert/ct_policy_status.h" | 26 #include "net/cert/ct_policy_status.h" |
27 #include "net/cert/internal/parse_ocsp.h" | |
27 #include "net/cert/x509_cert_types.h" | 28 #include "net/cert/x509_cert_types.h" |
28 #include "net/cert/x509_certificate.h" | 29 #include "net/cert/x509_certificate.h" |
29 #include "net/dns/dns_util.h" | 30 #include "net/dns/dns_util.h" |
30 #include "net/http/http_security_headers.h" | 31 #include "net/http/http_security_headers.h" |
31 #include "net/ssl/ssl_info.h" | 32 #include "net/ssl/ssl_info.h" |
32 #include "url/gurl.h" | 33 #include "url/gurl.h" |
33 | 34 |
34 namespace net { | 35 namespace net { |
35 | 36 |
36 namespace { | 37 namespace { |
37 | 38 |
38 #include "net/http/transport_security_state_static.h" | 39 #include "net/http/transport_security_state_static.h" |
39 | 40 |
40 const size_t kMaxHPKPReportCacheEntries = 50; | 41 const size_t kMaxHPKPReportCacheEntries = 50; |
41 const int kTimeToRememberHPKPReportsMins = 60; | 42 const int kTimeToRememberHPKPReportsMins = 60; |
42 const size_t kReportCacheKeyLength = 16; | 43 const size_t kReportCacheKeyLength = 16; |
43 | 44 |
45 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
| |
46 OCSPStaple(); | |
47 ~OCSPStaple(); | |
48 | |
49 bool is_date_valid; | |
50 bool is_correct_certificate; | |
51 OCSPCertStatus::Status status; | |
52 }; | |
53 | |
54 OCSPStaple::OCSPStaple() | |
55 : is_date_valid(false), | |
56 is_correct_certificate(false), | |
57 status(OCSPCertStatus::Status::UNKNOWN) {} | |
58 | |
59 OCSPStaple::~OCSPStaple() {} | |
60 | |
44 void RecordUMAForHPKPReportFailure(const GURL& report_uri, int net_error) { | 61 void RecordUMAForHPKPReportFailure(const GURL& report_uri, int net_error) { |
45 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.PublicKeyPinReportSendingFailure", | 62 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.PublicKeyPinReportSendingFailure", |
46 net_error); | 63 net_error); |
47 } | 64 } |
48 | 65 |
66 der::GeneralizedTime ConvertExplodedTime(const base::Time::Exploded& exploded) { | |
67 der::GeneralizedTime result; | |
68 result.year = exploded.year; | |
69 result.month = exploded.month; | |
70 result.day = exploded.day_of_month; | |
71 result.hours = exploded.hour; | |
72 result.minutes = exploded.minute; | |
73 result.seconds = exploded.second; | |
74 return result; | |
75 } | |
76 | |
77 bool CheckOCSPDateValid(bool has_next_update, | |
svaldez
2016/06/13 14:03:03
Add a comment to the RFC referencing the this_upda
| |
78 const base::Time& check_time, | |
79 const der::GeneralizedTime& this_update, | |
80 const der::GeneralizedTime& next_update) { | |
81 if (has_next_update && !(this_update < next_update)) | |
82 return false; | |
83 base::Time::Exploded exploded; | |
84 check_time.UTCExplode(&exploded); | |
85 der::GeneralizedTime check_time_der = ConvertExplodedTime(exploded); | |
86 return (this_update < check_time_der) && | |
svaldez
2016/06/13 14:03:03
You possibly need a fudge factor in case of clock
| |
87 (!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.
| |
88 } | |
89 | |
90 bool CompareCertIDToCertificate(const OCSPCertID& cert_id, | |
91 const X509Certificate& certificate) { | |
92 // 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.
| |
93 der::Input serial(&certificate.serial_number()); | |
94 return serial == cert_id.serial_number; | |
95 } | |
96 | |
49 std::string TimeToISO8601(const base::Time& t) { | 97 std::string TimeToISO8601(const base::Time& t) { |
50 base::Time::Exploded exploded; | 98 base::Time::Exploded exploded; |
51 t.UTCExplode(&exploded); | 99 t.UTCExplode(&exploded); |
52 return base::StringPrintf( | 100 return base::StringPrintf( |
53 "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month, | 101 "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month, |
54 exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, | 102 exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, |
55 exploded.millisecond); | 103 exploded.millisecond); |
56 } | 104 } |
57 | 105 |
58 std::unique_ptr<base::ListValue> GetPEMEncodedChainAsList( | 106 std::unique_ptr<base::ListValue> GetPEMEncodedChainAsList( |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 report.SetString("effective-expiration-date", | 196 report.SetString("effective-expiration-date", |
149 TimeToISO8601(pkp_state.expiry)); | 197 TimeToISO8601(pkp_state.expiry)); |
150 if (!base::JSONWriter::Write(report, serialized_report)) { | 198 if (!base::JSONWriter::Write(report, serialized_report)) { |
151 LOG(ERROR) << "Failed to serialize HPKP violation report."; | 199 LOG(ERROR) << "Failed to serialize HPKP violation report."; |
152 return false; | 200 return false; |
153 } | 201 } |
154 | 202 |
155 return true; | 203 return true; |
156 } | 204 } |
157 | 205 |
206 std::string OCSPCertStatusToString(OCSPCertStatus::Status status) { | |
207 switch (status) { | |
208 case OCSPCertStatus::Status::GOOD: | |
209 return "GOOD"; | |
210 case OCSPCertStatus::Status::REVOKED: | |
211 return "REVOKED"; | |
212 case OCSPCertStatus::Status::UNKNOWN: | |
213 return "UNKNOWN"; | |
214 default: | |
215 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_
| |
216 return ""; | |
217 } | |
218 } | |
219 | |
220 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.
| |
221 const X509Certificate& certificate, | |
222 const base::Time& check_time, | |
223 std::vector<OCSPStaple>* staples) { | |
224 der::Input response_der(&raw_response); | |
225 OCSPResponse response; | |
226 if (!ParseOCSPResponse(response_der, &response)) | |
227 return false; | |
228 | |
229 // If the OCSP response isn't status SUCCESSFUL, don't parse the rest of the | |
230 // data. | |
231 if (response.status != OCSPResponse::ResponseStatus::SUCCESSFUL) | |
232 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
| |
233 OCSPResponseData response_data; | |
234 if (!ParseOCSPResponseData(response.data, &response_data)) | |
235 return false; | |
236 | |
237 bool contains_correct_response = false; | |
238 for (const auto& single_response_der : response_data.responses) { | |
239 OCSPSingleResponse single_response; | |
240 if (!ParseOCSPSingleResponse(single_response_der, &single_response)) | |
241 continue; | |
242 OCSPStaple staple; | |
243 staple.status = single_response.cert_status.status; | |
244 staple.is_date_valid = CheckOCSPDateValid( | |
245 single_response.has_next_update, check_time, | |
246 single_response.this_update, single_response.next_update); | |
247 OCSPCertID cert_id; | |
248 if (ParseOCSPCertID(single_response.cert_id_tlv, &cert_id)) { | |
249 staple.is_correct_certificate = | |
250 CompareCertIDToCertificate(cert_id, certificate); | |
251 } | |
252 if (staple.is_date_valid && staple.is_correct_certificate && | |
253 staple.status == OCSPCertStatus::Status::GOOD) { | |
254 contains_correct_response = true; | |
255 } | |
256 staples->push_back(staple); | |
257 } | |
258 return contains_correct_response; | |
259 } | |
260 | |
261 bool GetExpectStapleReport( | |
262 const HostPortPair& host_port_pair, | |
263 const TransportSecurityState::ExpectStapleState& expect_staple_state, | |
264 const X509Certificate& certificate, | |
265 const base::Time& check_time, | |
266 const std::vector<OCSPStaple>& ocsp_staples, | |
267 std::string* serialized_report) { | |
268 base::DictionaryValue report; | |
269 report.SetString("date-time", TimeToISO8601(check_time)); | |
270 report.SetString("hostname", host_port_pair.host()); | |
271 report.SetInteger("port", host_port_pair.port()); | |
272 | |
273 // Add the list of each stapled OCSP response | |
274 std::unique_ptr<base::Value> ocsp_responses(new base::ListValue); | |
275 for (const auto& staple : ocsp_staples) { | |
276 std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue); | |
277 response->SetBoolean("is-date-valid", staple.is_date_valid); | |
278 response->SetBoolean("is-correct-certificate", | |
279 staple.is_correct_certificate); | |
280 response->SetString("status", OCSPCertStatusToString(staple.status)); | |
281 base::ListValue* responses_list = | |
282 reinterpret_cast<base::ListValue*>(ocsp_responses.get()); | |
283 responses_list->Append(std::move(response)); | |
284 } | |
285 report.Set("ocsp-responses", std::move(ocsp_responses)); | |
286 report.Set("served-certificate-chain", | |
287 GetPEMEncodedChainAsList(&certificate)); | |
288 | |
289 if (!base::JSONWriter::Write(report, serialized_report)) { | |
290 LOG(ERROR) << "Failed to serialize Expect-Staple report"; | |
291 return false; | |
292 } | |
293 return true; | |
294 } | |
295 | |
158 // Do not send a report over HTTPS to the same host that set the | 296 // Do not send a report over HTTPS to the same host that set the |
159 // pin. Such report URIs will result in loops. (A.com has a pinning | 297 // pin. Such report URIs will result in loops. (A.com has a pinning |
160 // violation which results in a report being sent to A.com, which | 298 // violation which results in a report being sent to A.com, which |
161 // results in a pinning violation which results in a report being sent | 299 // results in a pinning violation which results in a report being sent |
162 // to A.com, etc.) | 300 // to A.com, etc.) |
163 bool IsReportUriValidForHost(const GURL& report_uri, const std::string& host) { | 301 bool IsReportUriValidForHost(const GURL& report_uri, const std::string& host) { |
164 return (report_uri.host_piece() != host || | 302 return (report_uri.host_piece() != host || |
165 !report_uri.SchemeIsCryptographic()); | 303 !report_uri.SchemeIsCryptographic()); |
166 } | 304 } |
167 | 305 |
(...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
610 return found; | 748 return found; |
611 } | 749 } |
612 | 750 |
613 } // namespace | 751 } // namespace |
614 | 752 |
615 TransportSecurityState::TransportSecurityState() | 753 TransportSecurityState::TransportSecurityState() |
616 : delegate_(nullptr), | 754 : delegate_(nullptr), |
617 report_sender_(nullptr), | 755 report_sender_(nullptr), |
618 enable_static_pins_(true), | 756 enable_static_pins_(true), |
619 enable_static_expect_ct_(true), | 757 enable_static_expect_ct_(true), |
758 expect_ct_reporter_(nullptr), | |
svaldez
2016/06/13 14:03:03
Extra change?
dadrian
2016/06/13 23:03:32
Formatting error. Fixed.
| |
620 enable_static_expect_staple_(false), | 759 enable_static_expect_staple_(false), |
621 expect_ct_reporter_(nullptr), | |
622 sent_reports_cache_(kMaxHPKPReportCacheEntries) { | 760 sent_reports_cache_(kMaxHPKPReportCacheEntries) { |
623 // Static pinning is only enabled for official builds to make sure that | 761 // Static pinning is only enabled for official builds to make sure that |
624 // others don't end up with pins that cannot be easily updated. | 762 // others don't end up with pins that cannot be easily updated. |
625 #if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS) | 763 #if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS) |
626 enable_static_pins_ = false; | 764 enable_static_pins_ = false; |
627 enable_static_expect_ct_ = false; | 765 enable_static_expect_ct_ = false; |
628 #endif | 766 #endif |
629 DCHECK(CalledOnValidThread()); | 767 DCHECK(CalledOnValidThread()); |
630 } | 768 } |
631 | 769 |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1091 } | 1229 } |
1092 | 1230 |
1093 ExpectCTState state; | 1231 ExpectCTState state; |
1094 if (!GetStaticExpectCTState(host_port_pair.host(), &state)) | 1232 if (!GetStaticExpectCTState(host_port_pair.host(), &state)) |
1095 return; | 1233 return; |
1096 | 1234 |
1097 expect_ct_reporter_->OnExpectCTFailed(host_port_pair, state.report_uri, | 1235 expect_ct_reporter_->OnExpectCTFailed(host_port_pair, state.report_uri, |
1098 ssl_info); | 1236 ssl_info); |
1099 } | 1237 } |
1100 | 1238 |
1239 void TransportSecurityState::CheckExpectStaple( | |
1240 const HostPortPair& host_port_pair, | |
1241 const ExpectStapleState& expect_staple_state, | |
1242 const X509Certificate& certificate, | |
1243 const std::string& ocsp_response) { | |
1244 DCHECK(CalledOnValidThread()); | |
1245 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.
| |
1246 return; | |
1247 | |
1248 // Check the stapled information. | |
1249 std::vector<OCSPStaple> ocsp_staples; | |
1250 base::Time check_time = base::Time::Now(); | |
1251 if (ParseAndCheckOCSPResponse(ocsp_response, certificate, check_time, | |
1252 &ocsp_staples)) | |
1253 return; | |
1254 | |
1255 // Report on failure. | |
1256 std::string serialized_report; | |
1257 if (!GetExpectStapleReport(host_port_pair, expect_staple_state, certificate, | |
1258 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.
| |
1259 return; | |
1260 } | |
1261 report_sender_->Send(expect_staple_state.report_uri, serialized_report); | |
1262 } | |
1263 | |
1101 // static | 1264 // static |
1102 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 1265 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
1103 PreloadResult result; | 1266 PreloadResult result; |
1104 if (!DecodeHSTSPreload(host, &result) || | 1267 if (!DecodeHSTSPreload(host, &result) || |
1105 !result.has_pins) { | 1268 !result.has_pins) { |
1106 return; | 1269 return; |
1107 } | 1270 } |
1108 | 1271 |
1109 DCHECK(result.domain_id != DOMAIN_NOT_PINNED); | 1272 DCHECK(result.domain_id != DOMAIN_NOT_PINNED); |
1110 | 1273 |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1400 TransportSecurityState::PKPStateIterator::PKPStateIterator( | 1563 TransportSecurityState::PKPStateIterator::PKPStateIterator( |
1401 const TransportSecurityState& state) | 1564 const TransportSecurityState& state) |
1402 : iterator_(state.enabled_pkp_hosts_.begin()), | 1565 : iterator_(state.enabled_pkp_hosts_.begin()), |
1403 end_(state.enabled_pkp_hosts_.end()) { | 1566 end_(state.enabled_pkp_hosts_.end()) { |
1404 } | 1567 } |
1405 | 1568 |
1406 TransportSecurityState::PKPStateIterator::~PKPStateIterator() { | 1569 TransportSecurityState::PKPStateIterator::~PKPStateIterator() { |
1407 } | 1570 } |
1408 | 1571 |
1409 } // namespace | 1572 } // namespace |
OLD | NEW |