Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Unified Diff: net/base/cert_database_mac.cc

Issue 2668005: Bring the handling of <keygen> and support for the application/x-x509-user-ce... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Whitespace/style Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/ssl/ssl_host_state_unittest.cc ('k') | net/base/cert_database_nss.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/base/cert_database_mac.cc
===================================================================
--- net/base/cert_database_mac.cc (revision 49024)
+++ net/base/cert_database_mac.cc (working copy)
@@ -7,11 +7,163 @@
#include <Security/Security.h>
#include "base/logging.h"
+#include "base/scoped_cftyperef.h"
#include "net/base/net_errors.h"
#include "net/base/x509_certificate.h"
namespace net {
+namespace {
+
+const void* CertRetainProc(CFAllocatorRef allocator, const void* value) {
+ return CFRetain(value);
+}
+
+void CertReleaseProc(CFAllocatorRef allocator, const void* value) {
+ CFRelease(value);
+}
+
+// Compare two certificates for equality. This is used as part of a
+// CFArrayCallbacks structure, as the CFTypeRef returned by
+// SecTrustGetResult may not point to the same location as was supplied
+// to ImportIntermediates(), even when they are equivalent. This ensures
+// the two certificates are properly compared. On OS X 10.6+, CFEqual can
+// be safely used, as it will make a proper comparison between the two.
+Boolean CertEqualityProc(const void* value1, const void* value2) {
+ CFTypeID cert_type = SecCertificateGetTypeID();
+ if (CFGetTypeID(value1) != cert_type || CFGetTypeID(value2) != cert_type)
+ return CFEqual(value1, value2);
+ return X509Certificate::IsSameOSCert(
+ reinterpret_cast<SecCertificateRef>(value1),
+ reinterpret_cast<SecCertificateRef>(value2));
+}
+
+// From an arbitrary, unordered list of certificate, attempt to import every
+// certificate that:
+// 1) Is a CA
+// 2) Results in a valid certificate chain
+void ImportIntermediates(
+ const X509Certificate::OSCertHandles& intermediates) {
+ OSStatus rv = noErr;
+ CFArrayCallbacks array_cb = { 0, CertRetainProc, CertReleaseProc,
+ CFCopyDescription, CertEqualityProc };
+
+ // See the "AppleX509XP Trust Policies" reference for documentation, but
+ // this guarantees that the trust evaluation includes making sure the leaf
+ // is a valid CA certificate.
+ CSSM_APPLE_TP_ACTION_DATA tp_action;
+ tp_action.Version = CSSM_APPLE_TP_ACTION_VERSION;
+ tp_action.ActionFlags = CSSM_TP_ACTION_LEAF_IS_CA;
+
+ scoped_cftyperef<CFDataRef> tp_action_data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&tp_action),
+ sizeof(tp_action), kCFAllocatorDefault));
+ if (tp_action_data == NULL)
+ return;
+
+ // Create a temporary array so that it can be manipulated at will, and
+ // because Sec* functions expect CFArrays.
+ scoped_cftyperef<CFMutableArrayRef> certs_array(CFArrayCreateMutable(
+ kCFAllocatorDefault, 0, &array_cb));
+ for (X509Certificate::OSCertHandles::const_iterator it =
+ intermediates.begin(); it != intermediates.end(); ++it) {
+ CFArrayAppendValue(certs_array, *it);
+ }
+
+ // Create a basic X.509 policy, which will ensure that each constructed
+ // chain matches the minimal criteria for well-formedness.
+ scoped_cftyperef<SecPolicySearchRef> policy_search_ref;
+ SecPolicySearchRef temp_search_ref = NULL;
+ rv = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC,
+ NULL, &temp_search_ref);
+ if (rv != noErr || temp_search_ref == NULL)
+ return;
+ policy_search_ref.reset(temp_search_ref);
+
+ scoped_cftyperef<SecPolicyRef> policy_ref;
+ SecPolicyRef temp_policy_ref = NULL;
+ rv = SecPolicySearchCopyNext(policy_search_ref, &temp_policy_ref);
+ if (rv != noErr || temp_trust_ref == NULL)
+ return;
+ policy_ref.reset(temp_policy_ref);
+
+ // Enumerate all the certificates, building a chain for each certificate.
+ // If the constructed chain is trusted, and solely comprised of CA
+ // certificates, add each certificate that was in |intermediates| into the
+ // keychain.
+ CFIndex certs_count = CFArrayGetCount(certs_array);
+ size_t offset = 0;
+ for (CFIndex i = 0; i < certs_count; ++i, ++offset) {
+ if (i > 0 && certs_count > 1) {
+ // Rotate the array so that the last item becomes the first, and
+ // everything else is shifted forward by 1. This is because
+ // SecTrustCreateWithCertificates begins validation against the
+ // certificate at index 0.
+ for (CFIndex j = 0; j < certs_count - 1; ++j) {
+ CFArrayExchangeValuesAtIndices(certs_array, certs_count - 1 - j,
+ certs_count - 2 - j);
+ }
+ }
+
+ SecTrustRef temp_trust_ref = NULL;
+ rv = SecTrustCreateWithCertificates(certs_array, policy_ref,
+ &temp_trust_ref);
+ if (rv != noErr)
+ continue;
+ scoped_cftyperef<SecTrustRef> trust_ref(temp_trust_ref);
+ temp_trust_ref = NULL;
+
+ rv = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT,
+ tp_action_data);
+ if (rv != noErr)
+ continue;
+
+ SecTrustResultType result = kSecTrustResultInvalid;
+ rv = SecTrustEvalute(trust_ref, &result);
+ // Allowing kSecTrustResultConfirm, as it will be honored when a chain is
+ // built using this certificate that ends in the certificate that was
+ // flagged as such. However, kSecTrustResultDeny is honored here, to
+ // match behaviour of NSS/Windows implementations of rejecting chains the
+ // user has explicitly rejected.
+ if (rv != noErr || !(result == kSecTrustResultProceed ||
+ result == kSecTrustResultUnspecified ||
+ result == kSecTrustResultConfirm)) {
+ if (rv != noErr) {
+ DLOG(WARNING) << rv << " Unable to get certificate chain for "
+ << "intermediate " << offset;
+ } else {
+ DLOG(WARNING) << "Certificate chain for intermediate " << offset
+ << " is not trusted. " << result;
+ }
+ continue;
+ }
+
+ CFArrayRef temp_chain = NULL;
+ rv = SecTrustGetResult(trust_ref, &result, &temp_chain, NULL);
+ if (rv != noErr || temp_chain == NULL)
+ continue;
+
+ scoped_cftyperef<CFArrayRef> chain(temp_chain);
+ temp_chain = NULL;
+
+ for (CFIndex j = 0; j < CFArrayGetCount(chain); ++j) {
+ SecCertificateRef chain_cert = CFArrayGetValueAtIndex(chain, j);
+ CFIndex orig_index = CFArrayGetFirstIndexOfValue(certs_array,
+ CFRangeMake(0, 0),
+ chain_cert);
+ if (orig_index == -1)
+ continue;
+ // Not checking the result code, because it doesn't matter too much if
+ // the certificate cannot be imported.
+ rv = SecCertificateAddToKeychain(chain_cert, NULL);
+ if (rv != noErr && rv != errSecDuplicateItem) {
+ DLOG(WARNING) << rv << " Unable to import intermediate "
+ << offset;
+ }
+ }
+ }
+} // namespace
+
CertDatabase::CertDatabase() {
}
@@ -20,8 +172,6 @@
return ERR_CERT_INVALID;
if (cert->HasExpired())
return ERR_CERT_DATE_INVALID;
- if (!cert->SupportsSSLClientAuth())
- return ERR_CERT_INVALID;
// Verify the Keychain already has the corresponding private key:
SecIdentityRef identity = NULL;
@@ -42,15 +192,14 @@
int CertDatabase::AddUserCert(X509Certificate* cert) {
OSStatus err = SecCertificateAddToKeychain(cert->os_cert_handle(), NULL);
- switch (err) {
- case noErr:
- case errSecDuplicateItem:
- return OK;
- default:
- LOG(ERROR) << "CertDatabase failed to add cert to keychain: " << err;
- // TODO(snej): Map the error code more intelligently.
- return ERR_ADD_USER_CERT_FAILED;
+ if (err != noErr && err != errSecDuplicateItem) {
+ LOG(ERROR) << "CertDatabase failed to add cert to keychain: " << err;
+ // TODO(snej): Map the error code more intelligently.
+ return ERR_ADD_USER_CERT_FAILED;
}
+
+ ImportIntermediates(cert->GetIntermediateCertificates());
+ return OK;
}
} // namespace net
« no previous file with comments | « chrome/browser/ssl/ssl_host_state_unittest.cc ('k') | net/base/cert_database_nss.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698