1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/web/navigation/crw_session_certificate_policy_manager.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "ios/web/public/certificate_policy_cache.h"
15 #include "ios/web/public/web_thread.h"
16 #include "net/cert/x509_certificate.h"
18 // Break if we detect that CertStatus values changed, because we persist them on
19 // disk and thus require them to be consistent.
20 COMPILE_ASSERT(net::CERT_STATUS_ALL_ERRORS == 0xFFFF,
21 cert_status_value_changed);
22 COMPILE_ASSERT(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
23 cert_status_value_changed);
24 COMPILE_ASSERT(net::CERT_STATUS_DATE_INVALID == 1 << 1,
25 cert_status_value_changed);
26 COMPILE_ASSERT(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
27 cert_status_value_changed);
28 COMPILE_ASSERT(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
29 cert_status_value_changed);
30 COMPILE_ASSERT(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
31 cert_status_value_changed);
32 COMPILE_ASSERT(net::CERT_STATUS_REVOKED == 1 << 6,
33 cert_status_value_changed);
34 COMPILE_ASSERT(net::CERT_STATUS_INVALID == 1 << 7,
35 cert_status_value_changed);
36 COMPILE_ASSERT(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
37 cert_status_value_changed);
38 COMPILE_ASSERT(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
39 cert_status_value_changed);
40 COMPILE_ASSERT(net::CERT_STATUS_WEAK_KEY == 1 << 11,
41 cert_status_value_changed);
42 COMPILE_ASSERT(net::CERT_STATUS_IS_EV == 1 << 16,
43 cert_status_value_changed);
44 COMPILE_ASSERT(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
45 cert_status_value_changed);
49 NSString* const kAllowedCertificatesKey = @"allowedCertificates";
51 struct AllowedCertificate {
52 scoped_refptr<net::X509Certificate> certificate;
58 bool operator() (const AllowedCertificate& lhs,
59 const AllowedCertificate& rhs) const {
60 if (lhs.host != rhs.host)
61 return lhs.host < rhs.host;
62 return certificateCompare_(lhs.certificate, rhs.certificate);
65 net::X509Certificate::LessThan certificateCompare_;
68 typedef std::map<AllowedCertificate, net::CertStatus, LessThan>
71 NSData* CertificateToNSData(net::X509Certificate* certificate) {
74 net::X509Certificate::GetDEREncoded(certificate->os_cert_handle(), &s);
76 return [NSData dataWithBytes:s.c_str() length:s.length()];
79 net::X509Certificate* NSDataToCertificate(NSData* data) {
80 return net::X509Certificate::CreateFromBytes((const char *)[data bytes],
84 void AddToCertificatePolicyCache(
85 scoped_refptr<web::CertificatePolicyCache> policy_cache,
86 AllowedCertificates certs) {
88 AllowedCertificates::iterator it;
89 for (it = certs.begin(); it != certs.end(); ++it) {
90 policy_cache->AllowCertForHost(
91 it->first.certificate.get(), it->first.host, it->second);
97 @implementation CRWSessionCertificatePolicyManager {
99 AllowedCertificates allowed_;
102 - (void)registerAllowedCertificate:(net::X509Certificate*)certificate
103 forHost:(const std::string&)host
104 status:(net::CertStatus)status {
105 DCHECK([NSThread isMainThread]);
107 AllowedCertificate allowedCertificate = {certificate, host};
108 allowed_[allowedCertificate] = status;
111 - (void)clearCertificates {
112 DCHECK([NSThread isMainThread]);
116 - (void)updateCertificatePolicyCache:
117 (const scoped_refptr<web::CertificatePolicyCache>&)cache {
118 DCHECK([NSThread isMainThread]);
120 // Make a copy of allowed_ and access the policy cache from the IOThread.
121 web::WebThread::PostTask(
122 web::WebThread::IO, FROM_HERE,
123 base::Bind(&AddToCertificatePolicyCache, cache, allowed_));
127 #pragma mark NSCoding and NSCopying methods
129 - (id)initWithCoder:(NSCoder*)aDecoder {
130 DCHECK([NSThread isMainThread]);
133 NSMutableSet* allowed = [aDecoder
134 decodeObjectForKey:kAllowedCertificatesKey];
135 for (NSArray* fields in allowed) {
136 if ([fields count] == 2) {
137 DVLOG(2) << "Dropping cached certificate policy (old format).";
139 } else if ([fields count] != 3) {
143 net::X509Certificate* c = NSDataToCertificate([fields objectAtIndex:0]);
144 std::string host = base::SysNSStringToUTF8([fields objectAtIndex:1]);
145 net::CertStatus status = (net::CertStatus)[[fields objectAtIndex:2]
146 unsignedIntegerValue];
147 [self registerAllowedCertificate:c forHost:host status:status];
153 - (void)encodeWithCoder:(NSCoder*)aCoder {
154 if (allowed_.size() == 0)
157 // Simple serialization of the set. If a same certificate is duplicated in the
158 // set (for a different host), the serialization will be duplicated as well.
159 NSMutableSet* allowedToEncode = [NSMutableSet set];
160 AllowedCertificates::iterator it;
161 for (it = allowed_.begin(); it != allowed_.end(); ++it) {
162 NSData* c = CertificateToNSData(it->first.certificate.get());
163 NSString* h = base::SysUTF8ToNSString(it->first.host);
168 const NSUInteger status = (NSUInteger)it->second;
169 NSArray* fields = [NSArray arrayWithObjects:c, h, @(status), nil];
170 [allowedToEncode addObject:fields];
172 [aCoder encodeObject:allowedToEncode forKey:kAllowedCertificatesKey];
175 - (id)copyWithZone:(NSZone*)zone {
176 DCHECK([NSThread isMainThread]);
177 CRWSessionCertificatePolicyManager* copy = [[[self class] alloc] init];
178 copy->allowed_ = allowed_;