Chromium Code Reviews| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 // operated by Google and at least one log that is not operated by Google. This | 127 // operated by Google and at least one log that is not operated by Google. This |
| 128 // is required for SCTs after July 1st, 2015, as documented at | 128 // is required for SCTs after July 1st, 2015, as documented at |
| 129 // http://dev.chromium.org/Home/chromium-security/root-ca-policy/EVCTPlanMay2015 edition.pdf | 129 // http://dev.chromium.org/Home/chromium-security/root-ca-policy/EVCTPlanMay2015 edition.pdf |
| 130 bool HasEnoughDiverseSCTs(const ct::SCTList& verified_scts) { | 130 bool HasEnoughDiverseSCTs(const ct::SCTList& verified_scts) { |
| 131 size_t num_google_issued_scts = base::checked_cast<size_t>(std::count_if( | 131 size_t num_google_issued_scts = base::checked_cast<size_t>(std::count_if( |
| 132 verified_scts.begin(), verified_scts.end(), IsGoogleIssuedSCT)); | 132 verified_scts.begin(), verified_scts.end(), IsGoogleIssuedSCT)); |
| 133 return (num_google_issued_scts > 0) && | 133 return (num_google_issued_scts > 0) && |
| 134 (verified_scts.size() != num_google_issued_scts); | 134 (verified_scts.size() != num_google_issued_scts); |
| 135 } | 135 } |
| 136 | 136 |
| 137 enum CTComplianceStatus { | 137 enum EVPolicyStatus { |
| 138 CT_NOT_COMPLIANT = 0, | 138 EV_POLICY_STATUS_NOT_COMPLIANT = 0, |
| 139 CT_IN_WHITELIST = 1, | 139 EV_POLICY_STATUS_IN_WHITELIST = 1, |
| 140 CT_ENOUGH_SCTS = 2, | 140 EV_POLICY_STATUS_COMPLIANT = 2, |
| 141 CT_NOT_ENOUGH_DIVERSE_SCTS = 3, | 141 EV_POLICY_STATUS_MAX, |
| 142 CT_COMPLIANCE_MAX, | |
| 143 }; | 142 }; |
| 144 | 143 |
| 145 const char* ComplianceStatusToString(CTComplianceStatus status) { | 144 const char* EVPolicyStatusToString(EVPolicyStatus status) { |
| 146 switch (status) { | 145 switch (status) { |
| 147 case CT_NOT_COMPLIANT: | 146 case EV_POLICY_STATUS_NOT_COMPLIANT: |
| 148 return "NOT_COMPLIANT"; | 147 return "NOT_COMPLIANT"; |
| 149 break; | 148 break; |
| 150 case CT_IN_WHITELIST: | 149 case EV_POLICY_STATUS_IN_WHITELIST: |
| 151 return "WHITELISTED"; | 150 return "WHITELISTED"; |
| 152 break; | 151 break; |
| 153 case CT_ENOUGH_SCTS: | 152 case EV_POLICY_STATUS_COMPLIANT: |
| 154 return "ENOUGH_SCTS"; | 153 return "COMPLIANT"; |
| 155 break; | 154 break; |
| 156 case CT_NOT_ENOUGH_DIVERSE_SCTS: | 155 case EV_POLICY_STATUS_MAX: |
| 157 return "NOT_ENOUGH_DIVERSE_SCTS"; | |
| 158 break; | |
| 159 case CT_COMPLIANCE_MAX: | |
| 160 break; | 156 break; |
| 161 } | 157 } |
| 162 | 158 |
| 163 return "unknown"; | 159 return "unknown"; |
| 164 } | 160 } |
| 165 | 161 |
| 166 enum EVWhitelistStatus { | 162 enum EVWhitelistStatus { |
| 167 EV_WHITELIST_NOT_PRESENT = 0, | 163 EV_WHITELIST_NOT_PRESENT = 0, |
| 168 EV_WHITELIST_INVALID = 1, | 164 EV_WHITELIST_INVALID = 1, |
| 169 EV_WHITELIST_VALID = 2, | 165 EV_WHITELIST_VALID = 2, |
| 170 EV_WHITELIST_MAX, | 166 EV_WHITELIST_MAX, |
| 171 }; | 167 }; |
| 172 | 168 |
| 173 void LogCTComplianceStatusToUMA(CTComplianceStatus status, | 169 void LogEVPolicyStatusToUMA(EVPolicyStatus status, |
| 174 const ct::EVCertsWhitelist* ev_whitelist) { | 170 const ct::EVCertsWhitelist* ev_whitelist) { |
| 175 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, | 171 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCTPolicyStatus", status, |
| 176 CT_COMPLIANCE_MAX); | 172 EV_POLICY_STATUS_MAX); |
| 177 if (status == CT_NOT_COMPLIANT) { | 173 if (status == EV_POLICY_STATUS_NOT_COMPLIANT) { |
| 178 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT; | 174 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT; |
| 179 if (ev_whitelist != NULL) { | 175 if (ev_whitelist != NULL) { |
| 180 if (ev_whitelist->IsValid()) | 176 if (ev_whitelist->IsValid()) |
| 181 ev_whitelist_status = EV_WHITELIST_VALID; | 177 ev_whitelist_status = EV_WHITELIST_VALID; |
| 182 else | 178 else |
| 183 ev_whitelist_status = EV_WHITELIST_INVALID; | 179 ev_whitelist_status = EV_WHITELIST_INVALID; |
| 184 } | 180 } |
| 185 | 181 |
| 186 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert", | 182 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert", |
| 187 ev_whitelist_status, EV_WHITELIST_MAX); | 183 ev_whitelist_status, EV_WHITELIST_MAX); |
| 188 } | 184 } |
| 189 } | 185 } |
| 190 | 186 |
| 191 struct ComplianceDetails { | 187 struct EVComplianceDetails { |
| 192 ComplianceDetails() | 188 EVComplianceDetails() |
| 193 : ct_presence_required(false), | 189 : build_timely(false), status(EV_POLICY_STATUS_NOT_COMPLIANT) {} |
| 194 build_timely(false), | |
| 195 status(CT_NOT_COMPLIANT) {} | |
| 196 | 190 |
| 197 // Whether enforcement of the policy was required or not. | 191 // True if the build is not older than 10 weeks. |
| 198 bool ct_presence_required; | |
| 199 // Whether the build is not older than 10 weeks. The value is meaningful only | |
| 200 // if |ct_presence_required| is true. | |
| 201 bool build_timely; | 192 bool build_timely; |
| 202 // Compliance status - meaningful only if |ct_presence_required| and | 193 // Compliance status. Cannot be EV_POLICY_STATUS_IS_WHITELISTED if |
| 203 // |build_timely| are true. | 194 // |build_timely| is false. |
| 204 CTComplianceStatus status; | 195 EVPolicyStatus status; |
| 205 // EV whitelist version. | 196 // EV whitelist version. Only set if |build_timely| is true. |
| 206 base::Version whitelist_version; | 197 base::Version whitelist_version; |
| 207 }; | 198 }; |
| 208 | 199 |
| 209 scoped_ptr<base::Value> NetLogComplianceCheckResultCallback( | 200 scoped_ptr<base::Value> NetLogComplianceCheckResultCallback( |
| 210 X509Certificate* cert, | 201 X509Certificate* cert, |
| 211 ComplianceDetails* details, | 202 EVComplianceDetails* details, |
| 212 NetLogCaptureMode capture_mode) { | 203 NetLogCaptureMode capture_mode) { |
| 213 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 204 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 214 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); | 205 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); |
| 215 dict->SetBoolean("policy_enforcement_required", | 206 dict->SetBoolean("policy_enforcement_required", true); |
| 216 details->ct_presence_required); | 207 dict->SetBoolean("build_timely", details->build_timely); |
| 217 if (details->ct_presence_required) { | 208 dict->SetString("ct_compliance_status", |
| 218 dict->SetBoolean("build_timely", details->build_timely); | 209 EVPolicyStatusToString(details->status)); |
| 219 if (details->build_timely) { | 210 if (details->whitelist_version.IsValid()) { |
| 220 dict->SetString("ct_compliance_status", | 211 dict->SetString("ev_whitelist_version", |
| 221 ComplianceStatusToString(details->status)); | 212 details->whitelist_version.GetString()); |
| 222 if (details->whitelist_version.IsValid()) | |
| 223 dict->SetString("ev_whitelist_version", | |
| 224 details->whitelist_version.GetString()); | |
| 225 } | |
| 226 } | 213 } |
| 227 return std::move(dict); | 214 return std::move(dict); |
| 228 } | 215 } |
| 229 | 216 |
| 230 // Returns true if all SCTs in |verified_scts| were issued on, or after, the | 217 // Returns true if all SCTs in |verified_scts| were issued on, or after, the |
| 231 // date specified in kDiverseSCTRequirementStartDate | 218 // date specified in kDiverseSCTRequirementStartDate |
| 232 bool AllSCTsPastDistinctSCTRequirementEnforcementDate( | 219 bool AllSCTsPastDistinctSCTRequirementEnforcementDate( |
| 233 const ct::SCTList& verified_scts) { | 220 const ct::SCTList& verified_scts) { |
| 234 // The date when diverse SCTs requirement is effective from. | 221 // The date when diverse SCTs requirement is effective from. |
| 235 // 2015-07-01 00:00:00 UTC. | 222 // 2015-07-01 00:00:00 UTC. |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 254 std::string truncated_fp = | 241 std::string truncated_fp = |
| 255 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); | 242 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); |
| 256 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); | 243 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); |
| 257 | 244 |
| 258 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", | 245 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", |
| 259 cert_in_ev_whitelist); | 246 cert_in_ev_whitelist); |
| 260 } | 247 } |
| 261 return cert_in_ev_whitelist; | 248 return cert_in_ev_whitelist; |
| 262 } | 249 } |
| 263 | 250 |
| 264 void CheckCTEVPolicyCompliance(X509Certificate* cert, | 251 bool CheckCertPolicyCompliance(X509Certificate* cert, |
| 265 const ct::EVCertsWhitelist* ev_whitelist, | 252 const ct::CTVerifyResult& ct_result) { |
| 266 const ct::CTVerifyResult& ct_result, | 253 if (!HasRequiredNumberOfSCTs(*cert, ct_result)) |
| 267 ComplianceDetails* result) { | 254 return false; |
| 268 result->ct_presence_required = true; | |
| 269 | |
| 270 if (!IsBuildTimely()) | |
| 271 return; | |
| 272 result->build_timely = true; | |
| 273 | |
| 274 if (ev_whitelist && ev_whitelist->IsValid()) | |
| 275 result->whitelist_version = ev_whitelist->Version(); | |
| 276 | |
| 277 if (IsCertificateInWhitelist(*cert, ev_whitelist)) { | |
| 278 result->status = CT_IN_WHITELIST; | |
| 279 return; | |
| 280 } | |
| 281 | |
| 282 if (!HasRequiredNumberOfSCTs(*cert, ct_result)) { | |
| 283 result->status = CT_NOT_COMPLIANT; | |
| 284 return; | |
| 285 } | |
| 286 | 255 |
| 287 if (AllSCTsPastDistinctSCTRequirementEnforcementDate( | 256 if (AllSCTsPastDistinctSCTRequirementEnforcementDate( |
| 288 ct_result.verified_scts) && | 257 ct_result.verified_scts) && |
| 289 !HasEnoughDiverseSCTs(ct_result.verified_scts)) { | 258 !HasEnoughDiverseSCTs(ct_result.verified_scts)) { |
| 290 result->status = CT_NOT_ENOUGH_DIVERSE_SCTS; | 259 return false; |
| 291 return; | |
| 292 } | 260 } |
| 293 | 261 |
| 294 result->status = CT_ENOUGH_SCTS; | 262 return true; |
| 263 } | |
| 264 | |
| 265 void CheckEVPolicyCompliance(X509Certificate* cert, | |
| 266 const ct::CTVerifyResult& ct_result, | |
| 267 const ct::EVCertsWhitelist* ev_whitelist, | |
| 268 EVComplianceDetails* result) { | |
| 269 result->status = EV_POLICY_STATUS_NOT_COMPLIANT; | |
| 270 if (CheckCertPolicyCompliance(cert, ct_result)) | |
| 271 result->status = EV_POLICY_STATUS_COMPLIANT; | |
| 272 if (ev_whitelist && ev_whitelist->IsValid()) | |
| 273 result->whitelist_version = ev_whitelist->Version(); | |
| 274 | |
| 275 if (result->status != EV_POLICY_STATUS_COMPLIANT && | |
| 276 IsCertificateInWhitelist(*cert, ev_whitelist)) { | |
| 277 result->status = EV_POLICY_STATUS_IN_WHITELIST; | |
| 278 } | |
| 295 } | 279 } |
| 296 | 280 |
| 297 } // namespace | 281 } // namespace |
| 298 | 282 |
| 299 bool CTPolicyEnforcer::DoesConformToCTEVPolicy( | 283 bool CTPolicyEnforcer::DoesConformToCertPolicy( |
| 300 X509Certificate* cert, | 284 X509Certificate* cert, |
| 285 const ct::CTVerifyResult& ct_result) { | |
| 286 if (!IsBuildTimely()) | |
|
Ryan Sleevi
2016/01/23 02:08:10
Perhaps it's worth documenting why we check build
estark
2016/01/30 17:01:56
Done.
| |
| 287 return false; | |
| 288 return CheckCertPolicyCompliance(cert, ct_result); | |
| 289 } | |
| 290 | |
| 291 bool CTPolicyEnforcer::DoesConformToEVPolicy( | |
| 292 X509Certificate* cert, | |
| 293 const ct::CTVerifyResult& ct_result, | |
| 301 const ct::EVCertsWhitelist* ev_whitelist, | 294 const ct::EVCertsWhitelist* ev_whitelist, |
| 302 const ct::CTVerifyResult& ct_result, | |
| 303 const BoundNetLog& net_log) { | 295 const BoundNetLog& net_log) { |
| 304 ComplianceDetails details; | 296 EVComplianceDetails details; |
| 305 | 297 details.build_timely = IsBuildTimely(); |
| 306 CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details); | 298 if (details.build_timely) |
| 299 CheckEVPolicyCompliance(cert, ct_result, ev_whitelist, &details); | |
| 300 else | |
| 301 details.status = EV_POLICY_STATUS_NOT_COMPLIANT; | |
| 307 | 302 |
| 308 NetLog::ParametersCallback net_log_callback = | 303 NetLog::ParametersCallback net_log_callback = |
| 309 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert), | 304 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert), |
| 310 base::Unretained(&details)); | 305 base::Unretained(&details)); |
| 311 | 306 |
| 312 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, | 307 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, |
| 313 net_log_callback); | 308 net_log_callback); |
| 314 | 309 |
| 315 if (!details.ct_presence_required) | 310 LogEVPolicyStatusToUMA(details.status, ev_whitelist); |
| 316 return true; | |
| 317 | 311 |
| 318 if (!details.build_timely) | 312 return (details.status == EV_POLICY_STATUS_IN_WHITELIST || |
| 319 return false; | 313 details.status == EV_POLICY_STATUS_COMPLIANT); |
| 320 | |
| 321 LogCTComplianceStatusToUMA(details.status, ev_whitelist); | |
| 322 | |
| 323 if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS) | |
| 324 return true; | |
| 325 | |
| 326 return false; | |
| 327 } | 314 } |
| 328 | 315 |
| 329 } // namespace net | 316 } // namespace net |
| OLD | NEW |