| 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 |