OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/cert/ct_policy_enforcer.h" | 5 #include "net/cert/ct_policy_enforcer.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <memory> | 10 #include <memory> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind.h" | 13 #include "base/bind.h" |
14 #include "base/build_time.h" | 14 #include "base/build_time.h" |
15 #include "base/callback_helpers.h" | 15 #include "base/callback_helpers.h" |
16 #include "base/metrics/field_trial.h" | 16 #include "base/metrics/field_trial.h" |
17 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
18 #include "base/numerics/safe_conversions.h" | 18 #include "base/numerics/safe_conversions.h" |
19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
21 #include "base/values.h" | 21 #include "base/values.h" |
22 #include "base/version.h" | 22 #include "base/version.h" |
23 #include "net/cert/ct_ev_whitelist.h" | |
24 #include "net/cert/ct_known_logs.h" | 23 #include "net/cert/ct_known_logs.h" |
25 #include "net/cert/ct_policy_status.h" | 24 #include "net/cert/ct_policy_status.h" |
26 #include "net/cert/ct_verify_result.h" | 25 #include "net/cert/ct_verify_result.h" |
27 #include "net/cert/signed_certificate_timestamp.h" | 26 #include "net/cert/signed_certificate_timestamp.h" |
28 #include "net/cert/x509_certificate.h" | 27 #include "net/cert/x509_certificate.h" |
29 #include "net/cert/x509_certificate_net_log_param.h" | 28 #include "net/cert/x509_certificate_net_log_param.h" |
30 #include "net/log/net_log_capture_mode.h" | 29 #include "net/log/net_log_capture_mode.h" |
31 #include "net/log/net_log_event_type.h" | 30 #include "net/log/net_log_event_type.h" |
32 #include "net/log/net_log_parameters_callback.h" | 31 #include "net/log/net_log_parameters_callback.h" |
33 #include "net/log/net_log_with_source.h" | 32 #include "net/log/net_log_with_source.h" |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + | 67 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + |
69 (exploded_expiry.month - exploded_start.month); | 68 (exploded_expiry.month - exploded_start.month); |
70 if (exploded_expiry.day_of_month < exploded_start.day_of_month) | 69 if (exploded_expiry.day_of_month < exploded_start.day_of_month) |
71 --month_diff; | 70 --month_diff; |
72 else if (exploded_expiry.day_of_month == exploded_start.day_of_month) | 71 else if (exploded_expiry.day_of_month == exploded_start.day_of_month) |
73 *has_partial_month = false; | 72 *has_partial_month = false; |
74 | 73 |
75 *rounded_months_difference = month_diff; | 74 *rounded_months_difference = month_diff; |
76 } | 75 } |
77 | 76 |
78 const char* EVPolicyComplianceToString(ct::EVPolicyCompliance status) { | |
79 switch (status) { | |
80 case ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY: | |
81 return "POLICY_DOES_NOT_APPLY"; | |
82 case ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST: | |
83 return "WHITELISTED"; | |
84 case ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS: | |
85 return "COMPLIES_VIA_SCTS"; | |
86 case ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS: | |
87 return "NOT_ENOUGH_SCTS"; | |
88 case ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS: | |
89 return "SCTS_NOT_DIVERSE"; | |
90 case ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY: | |
91 return "BUILD_NOT_TIMELY"; | |
92 case ct::EVPolicyCompliance::EV_POLICY_MAX: | |
93 break; | |
94 } | |
95 | |
96 return "unknown"; | |
97 } | |
98 | |
99 const char* CertPolicyComplianceToString(ct::CertPolicyCompliance status) { | 77 const char* CertPolicyComplianceToString(ct::CertPolicyCompliance status) { |
100 switch (status) { | 78 switch (status) { |
101 case ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS: | 79 case ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS: |
102 return "COMPLIES_VIA_SCTS"; | 80 return "COMPLIES_VIA_SCTS"; |
103 case ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS: | 81 case ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS: |
104 return "NOT_ENOUGH_SCTS"; | 82 return "NOT_ENOUGH_SCTS"; |
105 case ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS: | 83 case ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS: |
106 return "NOT_DIVERSE_SCTS"; | 84 return "NOT_DIVERSE_SCTS"; |
107 case ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY: | 85 case ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY: |
108 return "BUILD_NOT_TIMELY"; | 86 return "BUILD_NOT_TIMELY"; |
109 } | 87 } |
110 | 88 |
111 return "unknown"; | 89 return "unknown"; |
112 } | 90 } |
113 | 91 |
114 enum EVWhitelistStatus { | |
115 EV_WHITELIST_NOT_PRESENT = 0, | |
116 EV_WHITELIST_INVALID = 1, | |
117 EV_WHITELIST_VALID = 2, | |
118 EV_WHITELIST_MAX, | |
119 }; | |
120 | |
121 void LogEVPolicyComplianceToUMA(ct::EVPolicyCompliance status, | |
122 const ct::EVCertsWhitelist* ev_whitelist) { | |
123 UMA_HISTOGRAM_ENUMERATION( | |
124 "Net.SSL_EVCTCompliance", static_cast<int>(status), | |
125 static_cast<int>(ct::EVPolicyCompliance::EV_POLICY_MAX)); | |
126 if (status == ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS || | |
127 status == ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS) { | |
128 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT; | |
129 if (ev_whitelist != NULL) { | |
130 if (ev_whitelist->IsValid()) | |
131 ev_whitelist_status = EV_WHITELIST_VALID; | |
132 else | |
133 ev_whitelist_status = EV_WHITELIST_INVALID; | |
134 } | |
135 | |
136 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert", | |
137 ev_whitelist_status, EV_WHITELIST_MAX); | |
138 } | |
139 } | |
140 | |
141 struct EVComplianceDetails { | |
142 EVComplianceDetails() | |
143 : build_timely(false), | |
144 status(ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY) {} | |
145 | |
146 // Whether the build is not older than 10 weeks. | |
147 bool build_timely; | |
148 // Compliance status - meaningful only if |build_timely| is true. | |
149 ct::EVPolicyCompliance status; | |
150 // EV whitelist version. | |
151 base::Version whitelist_version; | |
152 }; | |
153 | |
154 std::unique_ptr<base::Value> NetLogEVComplianceCheckResultCallback( | |
155 X509Certificate* cert, | |
156 EVComplianceDetails* details, | |
157 NetLogCaptureMode capture_mode) { | |
158 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | |
159 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); | |
160 dict->SetBoolean("policy_enforcement_required", true); | |
161 dict->SetBoolean("build_timely", details->build_timely); | |
162 if (details->build_timely) { | |
163 dict->SetString("ct_compliance_status", | |
164 EVPolicyComplianceToString(details->status)); | |
165 if (details->whitelist_version.IsValid()) | |
166 dict->SetString("ev_whitelist_version", | |
167 details->whitelist_version.GetString()); | |
168 } | |
169 return std::move(dict); | |
170 } | |
171 | |
172 std::unique_ptr<base::Value> NetLogCertComplianceCheckResultCallback( | 92 std::unique_ptr<base::Value> NetLogCertComplianceCheckResultCallback( |
173 X509Certificate* cert, | 93 X509Certificate* cert, |
174 bool build_timely, | 94 bool build_timely, |
175 ct::CertPolicyCompliance compliance, | 95 ct::CertPolicyCompliance compliance, |
176 NetLogCaptureMode capture_mode) { | 96 NetLogCaptureMode capture_mode) { |
177 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 97 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
178 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); | 98 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); |
179 dict->SetBoolean("build_timely", build_timely); | 99 dict->SetBoolean("build_timely", build_timely); |
180 dict->SetString("ct_compliance_status", | 100 dict->SetString("ct_compliance_status", |
181 CertPolicyComplianceToString(compliance)); | 101 CertPolicyComplianceToString(compliance)); |
182 return std::move(dict); | 102 return std::move(dict); |
183 } | 103 } |
184 | 104 |
185 bool IsCertificateInWhitelist(const X509Certificate& cert, | |
186 const ct::EVCertsWhitelist* ev_whitelist) { | |
187 if (!ev_whitelist || !ev_whitelist->IsValid()) | |
188 return false; | |
189 | |
190 const SHA256HashValue fingerprint( | |
191 X509Certificate::CalculateFingerprint256(cert.os_cert_handle())); | |
192 | |
193 std::string truncated_fp = | |
194 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); | |
195 bool cert_in_ev_whitelist = | |
196 ev_whitelist->ContainsCertificateHash(truncated_fp); | |
197 | |
198 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", | |
199 cert_in_ev_whitelist); | |
200 return cert_in_ev_whitelist; | |
201 } | |
202 | |
203 // Evaluates against the policy specified at | 105 // Evaluates against the policy specified at |
204 // https://sites.google.com/a/chromium.org/dev/Home/chromium-security/root-ca-po
licy/EVCTPlanMay2015edition.pdf?attredirects=0 | 106 // https://sites.google.com/a/chromium.org/dev/Home/chromium-security/root-ca-po
licy/EVCTPlanMay2015edition.pdf?attredirects=0 |
205 ct::CertPolicyCompliance CheckCertPolicyCompliance( | 107 ct::CertPolicyCompliance CheckCertPolicyCompliance( |
206 const X509Certificate& cert, | 108 const X509Certificate& cert, |
207 const ct::SCTList& verified_scts) { | 109 const ct::SCTList& verified_scts) { |
208 // Cert is outside the bounds of parsable; reject it. | 110 // Cert is outside the bounds of parsable; reject it. |
209 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() || | 111 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() || |
210 cert.valid_start().is_max() || cert.valid_expiry().is_max()) { | 112 cert.valid_start().is_max() || cert.valid_expiry().is_max()) { |
211 return ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS; | 113 return ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS; |
212 } | 114 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 return ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS; | 260 return ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS; |
359 | 261 |
360 // Under Option 2, there weren't enough SCTs, and potentially under Option | 262 // Under Option 2, there weren't enough SCTs, and potentially under Option |
361 // 1, there weren't diverse enough SCTs. Try to signal the error that is | 263 // 1, there weren't diverse enough SCTs. Try to signal the error that is |
362 // most easily fixed. | 264 // most easily fixed. |
363 return has_valid_nonembedded_sct | 265 return has_valid_nonembedded_sct |
364 ? ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS | 266 ? ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS |
365 : ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS; | 267 : ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS; |
366 } | 268 } |
367 | 269 |
368 ct::EVPolicyCompliance CertPolicyComplianceToEVPolicyCompliance( | |
369 ct::CertPolicyCompliance cert_policy_compliance) { | |
370 switch (cert_policy_compliance) { | |
371 case ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS: | |
372 return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS; | |
373 case ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS: | |
374 return ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS; | |
375 case ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS: | |
376 return ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS; | |
377 case ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY: | |
378 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; | |
379 } | |
380 return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; | |
381 } | |
382 | |
383 void CheckCTEVPolicyCompliance(X509Certificate* cert, | |
384 const ct::EVCertsWhitelist* ev_whitelist, | |
385 const ct::SCTList& verified_scts, | |
386 const NetLogWithSource& net_log, | |
387 EVComplianceDetails* result) { | |
388 result->status = CertPolicyComplianceToEVPolicyCompliance( | |
389 CheckCertPolicyCompliance(*cert, verified_scts)); | |
390 if (ev_whitelist && ev_whitelist->IsValid()) | |
391 result->whitelist_version = ev_whitelist->Version(); | |
392 | |
393 if (result->status != ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS && | |
394 IsCertificateInWhitelist(*cert, ev_whitelist)) { | |
395 result->status = ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST; | |
396 } | |
397 } | |
398 | |
399 } // namespace | 270 } // namespace |
400 | 271 |
401 ct::CertPolicyCompliance CTPolicyEnforcer::DoesConformToCertPolicy( | 272 ct::CertPolicyCompliance CTPolicyEnforcer::DoesConformToCertPolicy( |
402 X509Certificate* cert, | 273 X509Certificate* cert, |
403 const ct::SCTList& verified_scts, | 274 const ct::SCTList& verified_scts, |
404 const NetLogWithSource& net_log) { | 275 const NetLogWithSource& net_log) { |
405 // If the build is not timely, no certificate is considered compliant | 276 // If the build is not timely, no certificate is considered compliant |
406 // with CT policy. The reasoning is that, for example, a log might | 277 // with CT policy. The reasoning is that, for example, a log might |
407 // have been pulled and is no longer considered valid; thus, a client | 278 // have been pulled and is no longer considered valid; thus, a client |
408 // needs up-to-date information about logs to consider certificates to | 279 // needs up-to-date information about logs to consider certificates to |
409 // be compliant with policy. | 280 // be compliant with policy. |
410 bool build_timely = IsBuildTimely(); | 281 bool build_timely = IsBuildTimely(); |
411 ct::CertPolicyCompliance compliance; | 282 ct::CertPolicyCompliance compliance; |
412 if (!build_timely) { | 283 if (!build_timely) { |
413 compliance = ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY; | 284 compliance = ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY; |
414 } else { | 285 } else { |
415 compliance = CheckCertPolicyCompliance(*cert, verified_scts); | 286 compliance = CheckCertPolicyCompliance(*cert, verified_scts); |
416 } | 287 } |
417 | 288 |
418 NetLogParametersCallback net_log_callback = | 289 NetLogParametersCallback net_log_callback = |
419 base::Bind(&NetLogCertComplianceCheckResultCallback, | 290 base::Bind(&NetLogCertComplianceCheckResultCallback, |
420 base::Unretained(cert), build_timely, compliance); | 291 base::Unretained(cert), build_timely, compliance); |
421 | 292 |
422 net_log.AddEvent(NetLogEventType::CERT_CT_COMPLIANCE_CHECKED, | 293 net_log.AddEvent(NetLogEventType::CERT_CT_COMPLIANCE_CHECKED, |
423 net_log_callback); | 294 net_log_callback); |
424 | 295 |
425 return compliance; | 296 return compliance; |
426 } | 297 } |
427 | 298 |
428 ct::EVPolicyCompliance CTPolicyEnforcer::DoesConformToCTEVPolicy( | |
429 X509Certificate* cert, | |
430 const ct::EVCertsWhitelist* ev_whitelist, | |
431 const ct::SCTList& verified_scts, | |
432 const NetLogWithSource& net_log) { | |
433 EVComplianceDetails details; | |
434 // If the build is not timely, no certificate is considered compliant | |
435 // with EV policy. The reasoning is that, for example, a log might | |
436 // have been pulled and is no longer considered valid; thus, a client | |
437 // needs up-to-date information about logs to consider certificates to | |
438 // be compliant with policy. | |
439 details.build_timely = IsBuildTimely(); | |
440 if (!details.build_timely) { | |
441 details.status = ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; | |
442 } else { | |
443 CheckCTEVPolicyCompliance(cert, ev_whitelist, verified_scts, net_log, | |
444 &details); | |
445 } | |
446 | |
447 NetLogParametersCallback net_log_callback = | |
448 base::Bind(&NetLogEVComplianceCheckResultCallback, base::Unretained(cert), | |
449 base::Unretained(&details)); | |
450 | |
451 net_log.AddEvent(NetLogEventType::EV_CERT_CT_COMPLIANCE_CHECKED, | |
452 net_log_callback); | |
453 | |
454 if (!details.build_timely) | |
455 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; | |
456 | |
457 LogEVPolicyComplianceToUMA(details.status, ev_whitelist); | |
458 | |
459 return details.status; | |
460 } | |
461 | |
462 } // namespace net | 299 } // namespace net |
OLD | NEW |