Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(370)

Side by Side Diff: net/http/transport_security_state.cc

Issue 2040513003: Implement Expect-Staple (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Start writing tests Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698