| Index: ios/web/navigation/crw_session_certificate_policy_manager.mm
|
| diff --git a/ios/web/navigation/crw_session_certificate_policy_manager.mm b/ios/web/navigation/crw_session_certificate_policy_manager.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3547536bc816307a8f4a3a248aefb547adea7697
|
| --- /dev/null
|
| +++ b/ios/web/navigation/crw_session_certificate_policy_manager.mm
|
| @@ -0,0 +1,182 @@
|
| +// Copyright 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/web/navigation/crw_session_certificate_policy_manager.h"
|
| +
|
| +#include <map>
|
| +#include <set>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "ios/web/public/certificate_policy_cache.h"
|
| +#include "ios/web/public/web_thread.h"
|
| +#include "net/cert/x509_certificate.h"
|
| +
|
| +// Break if we detect that CertStatus values changed, because we persist them on
|
| +// disk and thus require them to be consistent.
|
| +COMPILE_ASSERT(net::CERT_STATUS_ALL_ERRORS == 0xFFFF,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_DATE_INVALID == 1 << 1,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_REVOKED == 1 << 6,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_INVALID == 1 << 7,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_WEAK_KEY == 1 << 11,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_IS_EV == 1 << 16,
|
| + cert_status_value_changed);
|
| +COMPILE_ASSERT(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
|
| + cert_status_value_changed);
|
| +
|
| +namespace {
|
| +
|
| +NSString* const kAllowedCertificatesKey = @"allowedCertificates";
|
| +
|
| +struct AllowedCertificate {
|
| + scoped_refptr<net::X509Certificate> certificate;
|
| + std::string host;
|
| +};
|
| +
|
| +class LessThan {
|
| + public:
|
| + bool operator() (const AllowedCertificate& lhs,
|
| + const AllowedCertificate& rhs) const {
|
| + if (lhs.host != rhs.host)
|
| + return lhs.host < rhs.host;
|
| + return certificateCompare_(lhs.certificate, rhs.certificate);
|
| + }
|
| + private:
|
| + net::X509Certificate::LessThan certificateCompare_;
|
| +};
|
| +
|
| +typedef std::map<AllowedCertificate, net::CertStatus, LessThan>
|
| + AllowedCertificates;
|
| +
|
| +NSData* CertificateToNSData(net::X509Certificate* certificate) {
|
| + std::string s;
|
| + bool success =
|
| + net::X509Certificate::GetDEREncoded(certificate->os_cert_handle(), &s);
|
| + DCHECK(success);
|
| + return [NSData dataWithBytes:s.c_str() length:s.length()];
|
| +}
|
| +
|
| +net::X509Certificate* NSDataToCertificate(NSData* data) {
|
| + return net::X509Certificate::CreateFromBytes((const char *)[data bytes],
|
| + [data length]);
|
| +}
|
| +
|
| +void AddToCertificatePolicyCache(
|
| + scoped_refptr<web::CertificatePolicyCache> policy_cache,
|
| + AllowedCertificates certs) {
|
| + DCHECK(policy_cache);
|
| + AllowedCertificates::iterator it;
|
| + for (it = certs.begin(); it != certs.end(); ++it) {
|
| + policy_cache->AllowCertForHost(
|
| + it->first.certificate.get(), it->first.host, it->second);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +@implementation CRWSessionCertificatePolicyManager {
|
| + @private
|
| + AllowedCertificates allowed_;
|
| +}
|
| +
|
| +- (void)registerAllowedCertificate:(net::X509Certificate*)certificate
|
| + forHost:(const std::string&)host
|
| + status:(net::CertStatus)status {
|
| + DCHECK([NSThread isMainThread]);
|
| + DCHECK(certificate);
|
| + AllowedCertificate allowedCertificate = {certificate, host};
|
| + allowed_[allowedCertificate] = status;
|
| +}
|
| +
|
| +- (void)clearCertificates {
|
| + DCHECK([NSThread isMainThread]);
|
| + allowed_.clear();
|
| +}
|
| +
|
| +- (void)updateCertificatePolicyCache:
|
| + (const scoped_refptr<web::CertificatePolicyCache>&)cache {
|
| + DCHECK([NSThread isMainThread]);
|
| + DCHECK(cache);
|
| + // Make a copy of allowed_ and access the policy cache from the IOThread.
|
| + web::WebThread::PostTask(
|
| + web::WebThread::IO, FROM_HERE,
|
| + base::Bind(&AddToCertificatePolicyCache, cache, allowed_));
|
| +}
|
| +
|
| +#pragma mark -
|
| +#pragma mark NSCoding and NSCopying methods
|
| +
|
| +- (id)initWithCoder:(NSCoder*)aDecoder {
|
| + DCHECK([NSThread isMainThread]);
|
| + self = [super init];
|
| + if (self) {
|
| + NSMutableSet* allowed = [aDecoder
|
| + decodeObjectForKey:kAllowedCertificatesKey];
|
| + for (NSArray* fields in allowed) {
|
| + if ([fields count] == 2) {
|
| + DVLOG(2) << "Dropping cached certificate policy (old format).";
|
| + continue;
|
| + } else if ([fields count] != 3) {
|
| + NOTREACHED();
|
| + continue;
|
| + }
|
| + net::X509Certificate* c = NSDataToCertificate([fields objectAtIndex:0]);
|
| + std::string host = base::SysNSStringToUTF8([fields objectAtIndex:1]);
|
| + net::CertStatus status = (net::CertStatus)[[fields objectAtIndex:2]
|
| + unsignedIntegerValue];
|
| + [self registerAllowedCertificate:c forHost:host status:status];
|
| + }
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)encodeWithCoder:(NSCoder*)aCoder {
|
| + if (allowed_.size() == 0)
|
| + return;
|
| +
|
| + // Simple serialization of the set. If a same certificate is duplicated in the
|
| + // set (for a different host), the serialization will be duplicated as well.
|
| + NSMutableSet* allowedToEncode = [NSMutableSet set];
|
| + AllowedCertificates::iterator it;
|
| + for (it = allowed_.begin(); it != allowed_.end(); ++it) {
|
| + NSData* c = CertificateToNSData(it->first.certificate.get());
|
| + NSString* h = base::SysUTF8ToNSString(it->first.host);
|
| + DCHECK(c);
|
| + DCHECK(h);
|
| + if (!c || !h)
|
| + continue;
|
| + const NSUInteger status = (NSUInteger)it->second;
|
| + NSArray* fields = [NSArray arrayWithObjects:c, h, @(status), nil];
|
| + [allowedToEncode addObject:fields];
|
| + }
|
| + [aCoder encodeObject:allowedToEncode forKey:kAllowedCertificatesKey];
|
| +}
|
| +
|
| +- (id)copyWithZone:(NSZone*)zone {
|
| + DCHECK([NSThread isMainThread]);
|
| + CRWSessionCertificatePolicyManager* copy = [[[self class] alloc] init];
|
| + copy->allowed_ = allowed_;
|
| + return copy;
|
| +}
|
| +
|
| +@end
|
|
|