blob: 0cc3ba913b30403492a42e8b196d854bccb05cbb [file] [log] [blame]
// Copyright 2014 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/web_state/wk_web_view_security_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_info.h"
namespace web {
// This key was determined by inspecting userInfo dict of an SSL error.
NSString* const kNSErrorPeerCertificateChainKey =
@"NSErrorPeerCertificateChainKey";
}
namespace {
// Creates certificate from subject string.
net::X509Certificate* CreateCertFromSubject(NSString* subject) {
std::string issuer = "";
base::Time start_date;
base::Time expiration_date;
return new net::X509Certificate(base::SysNSStringToUTF8(subject),
issuer,
start_date,
expiration_date);
}
// Creates certificate using information extracted from NSError.
scoped_refptr<net::X509Certificate> CreateCertFromSSLError(NSError* error) {
scoped_refptr<net::X509Certificate> cert = web::CreateCertFromChain(
error.userInfo[web::kNSErrorPeerCertificateChainKey]);
if (cert)
return cert;
return CreateCertFromSubject(
error.userInfo[NSURLErrorFailingURLStringErrorKey]);
}
// Maps NSError code to net::CertStatus.
net::CertStatus GetCertStatusFromNSErrorCode(NSInteger code) {
switch (code) {
// Regardless of real certificate problem the system always returns
// NSURLErrorServerCertificateUntrusted. The mapping is done in case this
// bug is fixed (rdar://18517043).
case NSURLErrorServerCertificateUntrusted:
case NSURLErrorSecureConnectionFailed:
case NSURLErrorServerCertificateHasUnknownRoot:
case NSURLErrorClientCertificateRejected:
case NSURLErrorClientCertificateRequired:
return net::CERT_STATUS_INVALID;
case NSURLErrorServerCertificateHasBadDate:
case NSURLErrorServerCertificateNotYetValid:
return net::CERT_STATUS_DATE_INVALID;
}
NOTREACHED();
return 0;
}
} // namespace
namespace web {
scoped_refptr<net::X509Certificate> CreateCertFromChain(NSArray* certs) {
if (certs.count == 0)
return nullptr;
net::X509Certificate::OSCertHandles intermediates;
for (NSUInteger i = 1; i < certs.count; i++) {
intermediates.push_back(reinterpret_cast<SecCertificateRef>(certs[i]));
}
return net::X509Certificate::CreateFromHandle(
reinterpret_cast<SecCertificateRef>(certs[0]), intermediates);
}
scoped_refptr<net::X509Certificate> CreateCertFromTrust(SecTrustRef trust) {
if (!trust)
return nullptr;
CFIndex cert_count = SecTrustGetCertificateCount(trust);
if (cert_count == 0) {
// At the moment there is no API which allows trust creation w/o certs.
return nullptr;
}
net::X509Certificate::OSCertHandles intermediates;
for (CFIndex i = 1; i < cert_count; i++) {
intermediates.push_back(SecTrustGetCertificateAtIndex(trust, i));
}
return net::X509Certificate::CreateFromHandle(
SecTrustGetCertificateAtIndex(trust, 0), intermediates);
}
void EnsureFutureTrustEvaluationSucceeds(SecTrustRef trust) {
base::ScopedCFTypeRef<CFDataRef> exceptions(SecTrustCopyExceptions(trust));
SecTrustSetExceptions(trust, exceptions);
}
BOOL IsWKWebViewSSLError(NSError* error) {
// SSL errors range is (-2000..-1200], represented by kCFURLError constants:
// (kCFURLErrorCannotLoadFromNetwork..kCFURLErrorSecureConnectionFailed].
// It's reasonable to expect that all SSL errors will have the error code
// less or equal to NSURLErrorSecureConnectionFailed but greater than
// NSURLErrorCannotLoadFromNetwork.
return [error.domain isEqualToString:NSURLErrorDomain] &&
(error.code <= NSURLErrorSecureConnectionFailed &&
NSURLErrorCannotLoadFromNetwork < error.code);
}
void GetSSLInfoFromWKWebViewSSLError(NSError* error, net::SSLInfo* ssl_info) {
DCHECK(IsWKWebViewSSLError(error));
ssl_info->cert_status = GetCertStatusFromNSErrorCode(error.code);
ssl_info->cert = CreateCertFromSSLError(error);
}
} // namespace web