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 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 // operated by Google and at least one log that is not operated by Google. This | 123 // operated by Google and at least one log that is not operated by Google. This |
124 // is required for SCTs after July 1st, 2015, as documented at | 124 // is required for SCTs after July 1st, 2015, as documented at |
125 // http://dev.chromium.org/Home/chromium-security/root-ca-policy/EVCTPlanMay2015
edition.pdf | 125 // http://dev.chromium.org/Home/chromium-security/root-ca-policy/EVCTPlanMay2015
edition.pdf |
126 bool HasEnoughDiverseSCTs(const ct::SCTList& verified_scts) { | 126 bool HasEnoughDiverseSCTs(const ct::SCTList& verified_scts) { |
127 size_t num_google_issued_scts = base::checked_cast<size_t>(std::count_if( | 127 size_t num_google_issued_scts = base::checked_cast<size_t>(std::count_if( |
128 verified_scts.begin(), verified_scts.end(), IsGoogleIssuedSCT)); | 128 verified_scts.begin(), verified_scts.end(), IsGoogleIssuedSCT)); |
129 return (num_google_issued_scts > 0) && | 129 return (num_google_issued_scts > 0) && |
130 (verified_scts.size() != num_google_issued_scts); | 130 (verified_scts.size() != num_google_issued_scts); |
131 } | 131 } |
132 | 132 |
133 enum CTComplianceStatus { | 133 const char* EVPolicyComplianceToString(ct::EVPolicyCompliance status) { |
134 CT_NOT_COMPLIANT = 0, | 134 switch (status) { |
135 CT_IN_WHITELIST = 1, | 135 case ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY: |
136 CT_ENOUGH_SCTS = 2, | 136 return "POLICY_DOES_NOT_APPLY"; |
137 CT_NOT_ENOUGH_DIVERSE_SCTS = 3, | 137 case ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST: |
138 CT_COMPLIANCE_MAX, | 138 return "WHITELISTED"; |
139 }; | 139 case ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS: |
| 140 return "COMPLIES_VIA_SCTS"; |
| 141 case ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS: |
| 142 return "NOT_ENOUGH_SCTS"; |
| 143 case ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS: |
| 144 return "SCTS_NOT_DIVERSE"; |
| 145 case ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY: |
| 146 return "BUILD_NOT_TIMELY"; |
| 147 case ct::EVPolicyCompliance::EV_POLICY_MAX: |
| 148 break; |
| 149 } |
140 | 150 |
141 const char* ComplianceStatusToString(CTComplianceStatus status) { | 151 return "unknown"; |
| 152 } |
| 153 |
| 154 const char* CertPolicyComplianceToString(ct::CertPolicyCompliance status) { |
142 switch (status) { | 155 switch (status) { |
143 case CT_NOT_COMPLIANT: | 156 case ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS: |
144 return "NOT_COMPLIANT"; | 157 return "COMPLIES_VIA_SCTS"; |
145 break; | 158 case ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS: |
146 case CT_IN_WHITELIST: | 159 return "NOT_ENOUGH_SCTS"; |
147 return "WHITELISTED"; | 160 case ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS: |
148 break; | 161 return "NOT_DIVERSE_SCTS"; |
149 case CT_ENOUGH_SCTS: | 162 case ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY: |
150 return "ENOUGH_SCTS"; | 163 return "BUILD_NOT_TIMELY"; |
151 break; | |
152 case CT_NOT_ENOUGH_DIVERSE_SCTS: | |
153 return "NOT_ENOUGH_DIVERSE_SCTS"; | |
154 break; | |
155 case CT_COMPLIANCE_MAX: | |
156 break; | |
157 } | 164 } |
158 | 165 |
159 return "unknown"; | 166 return "unknown"; |
160 } | 167 } |
161 | 168 |
162 enum EVWhitelistStatus { | 169 enum EVWhitelistStatus { |
163 EV_WHITELIST_NOT_PRESENT = 0, | 170 EV_WHITELIST_NOT_PRESENT = 0, |
164 EV_WHITELIST_INVALID = 1, | 171 EV_WHITELIST_INVALID = 1, |
165 EV_WHITELIST_VALID = 2, | 172 EV_WHITELIST_VALID = 2, |
166 EV_WHITELIST_MAX, | 173 EV_WHITELIST_MAX, |
167 }; | 174 }; |
168 | 175 |
169 void LogCTEVComplianceStatusToUMA(CTComplianceStatus status, | 176 void LogEVPolicyComplianceToUMA(ct::EVPolicyCompliance status, |
170 const ct::EVCertsWhitelist* ev_whitelist) { | 177 const ct::EVCertsWhitelist* ev_whitelist) { |
171 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, | 178 UMA_HISTOGRAM_ENUMERATION( |
172 CT_COMPLIANCE_MAX); | 179 "Net.SSL_EVCTCompliance", static_cast<int>(status), |
173 if (status == CT_NOT_COMPLIANT) { | 180 static_cast<int>(ct::EVPolicyCompliance::EV_POLICY_MAX)); |
| 181 if (status == ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS || |
| 182 status == ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS) { |
174 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT; | 183 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT; |
175 if (ev_whitelist != NULL) { | 184 if (ev_whitelist != NULL) { |
176 if (ev_whitelist->IsValid()) | 185 if (ev_whitelist->IsValid()) |
177 ev_whitelist_status = EV_WHITELIST_VALID; | 186 ev_whitelist_status = EV_WHITELIST_VALID; |
178 else | 187 else |
179 ev_whitelist_status = EV_WHITELIST_INVALID; | 188 ev_whitelist_status = EV_WHITELIST_INVALID; |
180 } | 189 } |
181 | 190 |
182 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert", | 191 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert", |
183 ev_whitelist_status, EV_WHITELIST_MAX); | 192 ev_whitelist_status, EV_WHITELIST_MAX); |
184 } | 193 } |
185 } | 194 } |
186 | 195 |
187 struct ComplianceDetails { | 196 struct EVComplianceDetails { |
188 ComplianceDetails() : build_timely(false), status(CT_NOT_COMPLIANT) {} | 197 EVComplianceDetails() |
| 198 : build_timely(false), |
| 199 status(ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY) {} |
189 | 200 |
190 // Whether the build is not older than 10 weeks. | 201 // Whether the build is not older than 10 weeks. |
191 bool build_timely; | 202 bool build_timely; |
192 // Compliance status - meaningful only if |build_timely| is true. | 203 // Compliance status - meaningful only if |build_timely| is true. |
193 CTComplianceStatus status; | 204 ct::EVPolicyCompliance status; |
194 // EV whitelist version. | 205 // EV whitelist version. |
195 base::Version whitelist_version; | 206 base::Version whitelist_version; |
196 }; | 207 }; |
197 | 208 |
198 scoped_ptr<base::Value> NetLogComplianceCheckResultCallback( | 209 scoped_ptr<base::Value> NetLogEVComplianceCheckResultCallback( |
199 X509Certificate* cert, | 210 X509Certificate* cert, |
200 ComplianceDetails* details, | 211 EVComplianceDetails* details, |
201 NetLogCaptureMode capture_mode) { | 212 NetLogCaptureMode capture_mode) { |
202 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 213 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
203 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); | 214 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); |
204 dict->SetBoolean("policy_enforcement_required", true); | 215 dict->SetBoolean("policy_enforcement_required", true); |
205 dict->SetBoolean("build_timely", details->build_timely); | 216 dict->SetBoolean("build_timely", details->build_timely); |
206 if (details->build_timely) { | 217 if (details->build_timely) { |
207 dict->SetString("ct_compliance_status", | 218 dict->SetString("ct_compliance_status", |
208 ComplianceStatusToString(details->status)); | 219 EVPolicyComplianceToString(details->status)); |
209 if (details->whitelist_version.IsValid()) | 220 if (details->whitelist_version.IsValid()) |
210 dict->SetString("ev_whitelist_version", | 221 dict->SetString("ev_whitelist_version", |
211 details->whitelist_version.GetString()); | 222 details->whitelist_version.GetString()); |
212 } | 223 } |
213 return std::move(dict); | 224 return std::move(dict); |
214 } | 225 } |
215 | 226 |
| 227 scoped_ptr<base::Value> NetLogCertComplianceCheckResultCallback( |
| 228 X509Certificate* cert, |
| 229 bool build_timely, |
| 230 ct::CertPolicyCompliance compliance, |
| 231 NetLogCaptureMode capture_mode) { |
| 232 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| 233 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); |
| 234 dict->SetBoolean("build_timely", build_timely); |
| 235 dict->SetString("ct_compliance_status", |
| 236 CertPolicyComplianceToString(compliance)); |
| 237 return std::move(dict); |
| 238 } |
| 239 |
216 // Returns true if all SCTs in |verified_scts| were issued on, or after, the | 240 // Returns true if all SCTs in |verified_scts| were issued on, or after, the |
217 // date specified in kDiverseSCTRequirementStartDate | 241 // date specified in kDiverseSCTRequirementStartDate |
218 bool AllSCTsPastDistinctSCTRequirementEnforcementDate( | 242 bool AllSCTsPastDistinctSCTRequirementEnforcementDate( |
219 const ct::SCTList& verified_scts) { | 243 const ct::SCTList& verified_scts) { |
220 // The date when diverse SCTs requirement is effective from. | 244 // The date when diverse SCTs requirement is effective from. |
221 // 2015-07-01 00:00:00 UTC. | 245 // 2015-07-01 00:00:00 UTC. |
222 base::Time kDiverseSCTRequirementStartDate = | 246 base::Time kDiverseSCTRequirementStartDate = |
223 base::Time::FromInternalValue(13080182400000000); | 247 base::Time::FromInternalValue(13080182400000000); |
224 | 248 |
225 for (const auto& it : verified_scts) { | 249 for (const auto& it : verified_scts) { |
(...skipping 14 matching lines...) Expand all Loading... |
240 std::string truncated_fp = | 264 std::string truncated_fp = |
241 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); | 265 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); |
242 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); | 266 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); |
243 | 267 |
244 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", | 268 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", |
245 cert_in_ev_whitelist); | 269 cert_in_ev_whitelist); |
246 } | 270 } |
247 return cert_in_ev_whitelist; | 271 return cert_in_ev_whitelist; |
248 } | 272 } |
249 | 273 |
| 274 ct::CertPolicyCompliance CheckCertPolicyCompliance( |
| 275 X509Certificate* cert, |
| 276 const ct::SCTList& verified_scts, |
| 277 const BoundNetLog& net_log) { |
| 278 if (!HasRequiredNumberOfSCTs(*cert, verified_scts)) |
| 279 return ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS; |
| 280 if (AllSCTsPastDistinctSCTRequirementEnforcementDate(verified_scts) && |
| 281 !HasEnoughDiverseSCTs(verified_scts)) { |
| 282 return ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS; |
| 283 } |
| 284 |
| 285 return ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS; |
| 286 } |
| 287 |
| 288 ct::EVPolicyCompliance CertPolicyComplianceToEVPolicyCompliance( |
| 289 ct::CertPolicyCompliance cert_policy_compliance) { |
| 290 switch (cert_policy_compliance) { |
| 291 case ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS: |
| 292 return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS; |
| 293 case ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS: |
| 294 return ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS; |
| 295 case ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS: |
| 296 return ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS; |
| 297 case ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY: |
| 298 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; |
| 299 } |
| 300 return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; |
| 301 } |
| 302 |
250 void CheckCTEVPolicyCompliance(X509Certificate* cert, | 303 void CheckCTEVPolicyCompliance(X509Certificate* cert, |
251 const ct::EVCertsWhitelist* ev_whitelist, | 304 const ct::EVCertsWhitelist* ev_whitelist, |
252 const ct::SCTList& verified_scts, | 305 const ct::SCTList& verified_scts, |
253 ComplianceDetails* result) { | 306 const BoundNetLog& net_log, |
254 if (!IsBuildTimely()) | 307 EVComplianceDetails* result) { |
255 return; | 308 result->status = CertPolicyComplianceToEVPolicyCompliance( |
256 result->build_timely = true; | 309 CheckCertPolicyCompliance(cert, verified_scts, net_log)); |
257 | |
258 if (ev_whitelist && ev_whitelist->IsValid()) | 310 if (ev_whitelist && ev_whitelist->IsValid()) |
259 result->whitelist_version = ev_whitelist->Version(); | 311 result->whitelist_version = ev_whitelist->Version(); |
260 | 312 |
261 if (IsCertificateInWhitelist(*cert, ev_whitelist)) { | 313 if (result->status != ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS && |
262 result->status = CT_IN_WHITELIST; | 314 IsCertificateInWhitelist(*cert, ev_whitelist)) { |
263 return; | 315 result->status = ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST; |
264 } | 316 } |
265 | |
266 if (!HasRequiredNumberOfSCTs(*cert, verified_scts)) { | |
267 result->status = CT_NOT_COMPLIANT; | |
268 return; | |
269 } | |
270 | |
271 if (AllSCTsPastDistinctSCTRequirementEnforcementDate(verified_scts) && | |
272 !HasEnoughDiverseSCTs(verified_scts)) { | |
273 result->status = CT_NOT_ENOUGH_DIVERSE_SCTS; | |
274 return; | |
275 } | |
276 | |
277 result->status = CT_ENOUGH_SCTS; | |
278 } | 317 } |
279 | 318 |
280 } // namespace | 319 } // namespace |
281 | 320 |
| 321 ct::CertPolicyCompliance CTPolicyEnforcer::DoesConformToCertPolicy( |
| 322 X509Certificate* cert, |
| 323 const ct::SCTList& verified_scts, |
| 324 const BoundNetLog& net_log) { |
| 325 // If the build is not timely, no certificate is considered compliant |
| 326 // with CT policy. The reasoning is that, for example, a log might |
| 327 // have been pulled and is no longer considered valid; thus, a client |
| 328 // needs up-to-date information about logs to consider certificates to |
| 329 // be compliant with policy. |
| 330 bool build_timely = IsBuildTimely(); |
| 331 ct::CertPolicyCompliance compliance; |
| 332 if (!build_timely) { |
| 333 compliance = ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY; |
| 334 } else { |
| 335 compliance = CheckCertPolicyCompliance(cert, verified_scts, net_log); |
| 336 } |
| 337 |
| 338 NetLog::ParametersCallback net_log_callback = |
| 339 base::Bind(&NetLogCertComplianceCheckResultCallback, |
| 340 base::Unretained(cert), build_timely, compliance); |
| 341 |
| 342 net_log.AddEvent(NetLog::TYPE_CERT_CT_COMPLIANCE_CHECKED, net_log_callback); |
| 343 |
| 344 return compliance; |
| 345 } |
| 346 |
282 ct::EVPolicyCompliance CTPolicyEnforcer::DoesConformToCTEVPolicy( | 347 ct::EVPolicyCompliance CTPolicyEnforcer::DoesConformToCTEVPolicy( |
283 X509Certificate* cert, | 348 X509Certificate* cert, |
284 const ct::EVCertsWhitelist* ev_whitelist, | 349 const ct::EVCertsWhitelist* ev_whitelist, |
285 const ct::SCTList& verified_scts, | 350 const ct::SCTList& verified_scts, |
286 const BoundNetLog& net_log) { | 351 const BoundNetLog& net_log) { |
287 ComplianceDetails details; | 352 EVComplianceDetails details; |
288 | 353 // If the build is not timely, no certificate is considered compliant |
289 CheckCTEVPolicyCompliance(cert, ev_whitelist, verified_scts, &details); | 354 // with EV policy. The reasoning is that, for example, a log might |
| 355 // have been pulled and is no longer considered valid; thus, a client |
| 356 // needs up-to-date information about logs to consider certificates to |
| 357 // be compliant with policy. |
| 358 details.build_timely = IsBuildTimely(); |
| 359 if (!details.build_timely) { |
| 360 details.status = ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; |
| 361 } else { |
| 362 CheckCTEVPolicyCompliance(cert, ev_whitelist, verified_scts, net_log, |
| 363 &details); |
| 364 } |
290 | 365 |
291 NetLog::ParametersCallback net_log_callback = | 366 NetLog::ParametersCallback net_log_callback = |
292 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert), | 367 base::Bind(&NetLogEVComplianceCheckResultCallback, base::Unretained(cert), |
293 base::Unretained(&details)); | 368 base::Unretained(&details)); |
294 | 369 |
295 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, | 370 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, |
296 net_log_callback); | 371 net_log_callback); |
297 | 372 |
298 if (!details.build_timely) | 373 if (!details.build_timely) |
299 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; | 374 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; |
300 | 375 |
301 LogCTEVComplianceStatusToUMA(details.status, ev_whitelist); | 376 LogEVPolicyComplianceToUMA(details.status, ev_whitelist); |
302 | 377 |
303 switch (details.status) { | 378 return details.status; |
304 case CT_NOT_COMPLIANT: | |
305 return ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS; | |
306 case CT_IN_WHITELIST: | |
307 return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST; | |
308 case CT_ENOUGH_SCTS: | |
309 return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS; | |
310 case CT_NOT_ENOUGH_DIVERSE_SCTS: | |
311 return ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS; | |
312 case CT_COMPLIANCE_MAX: | |
313 return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; | |
314 } | |
315 | |
316 return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; | |
317 } | 379 } |
318 | 380 |
319 } // namespace net | 381 } // namespace net |
OLD | NEW |