]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslKeychain.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslKeychain.c
diff --git a/libsecurity_ssl/lib/sslKeychain.c b/libsecurity_ssl/lib/sslKeychain.c
new file mode 100644 (file)
index 0000000..a72fe82
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 1999-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * sslKeychain.c - Apple Keychain routines
+ */
+
+#include "ssl.h"
+#include "sslContext.h"
+#include "sslMemory.h"
+
+#include "sslCrypto.h"
+#ifdef USE_CDSA_CRYPTO
+#include <Security/Security.h>
+#else
+#include <Security/SecBase.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecPolicy.h>
+#include <Security/SecTrust.h>
+#endif /* !USE_CDSA_CRYPTO */
+#include <Security/SecInternal.h>
+
+#include "sslDebug.h"
+#include "sslKeychain.h"
+#include "sslUtils.h"
+#include <string.h>
+#include <assert.h>
+
+
+#ifdef USE_SSLCERTIFICATE
+
+/*
+ * Given an array of certs (as SecIdentityRefs, specified by caller
+ * in SSLSetCertificate or SSLSetEncryptionCertificate) and a
+ * destination SSLCertificate:
+ *
+ * -- free destCerts if we have any
+ * -- Get raw cert data, convert to array of SSLCertificates in *destCert
+ * -- validate cert chain
+ * -- get pub, priv keys from certRef[0], store in *pubKey, *privKey
+ */
+
+/* Convert a SecCertificateRef to an SSLCertificate * */
+static OSStatus secCertToSslCert(
+       SSLContext                      *ctx,
+       SecCertificateRef       certRef,
+       SSLCertificate          **sslCert)
+{
+       CSSM_DATA               certData;               // struct is transient, referent owned by
+                                                                       //   Sec layer
+       OSStatus                ortn;
+       SSLCertificate  *thisSslCert = NULL;
+
+       ortn = SecCertificateGetData(certRef, &certData);
+       if(ortn) {
+               sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn);
+               return ortn;
+       }
+
+       thisSslCert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
+       if(thisSslCert == NULL) {
+               return memFullErr;
+       }
+       if(SSLAllocBuffer(&thisSslCert->derCert, certData.Length,
+                       ctx)) {
+               return memFullErr;
+       }
+       memcpy(thisSslCert->derCert.data, certData.Data, certData.Length);
+       thisSslCert->derCert.length = certData.Length;
+       *sslCert = thisSslCert;
+       return noErr;
+}
+
+/*
+ * Determine the basic signing algorithm, without the digest, component, of
+ * a cert. The returned algorithm will be RSA, DSA, or ECDSA.
+ */
+static OSStatus sslCertSignerAlg(
+       SecCertificateRef certRef,
+       CSSM_ALGORITHMS *signerAlg)
+{
+       OSStatus ortn;
+       CSSM_DATA_PTR fieldPtr;
+       CSSM_X509_ALGORITHM_IDENTIFIER *algId;
+       CSSM_ALGORITHMS sigAlg;
+
+       /*
+        * Extract the full signature algorithm OID
+        */
+       *signerAlg = CSSM_ALGID_NONE;
+       ortn = SecCertificateCopyFirstFieldValue(certRef,
+               &CSSMOID_X509V1SignatureAlgorithm,
+               &fieldPtr);
+       if(ortn) {
+               return ortn;
+       }
+       if(fieldPtr->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) {
+               sslErrorLog("sslCertSignerAlg() length error\n");
+               ortn = errSSLCrypto;
+               goto errOut;
+       }
+       algId = (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldPtr->Data;
+       if(!cssmOidToAlg(&algId->algorithm, &sigAlg)) {
+               /* Only way this could happen is if we're given a bad cert */
+               sslErrorLog("sslCertSignerAlg() bad sigAlg OID\n");
+               ortn = paramErr;
+               goto errOut;
+       }
+
+       /*
+        * OK we have the full signature algorithm as a CSSM_ALGORITHMS.
+        * Extract the core signature alg.
+        */
+       switch(sigAlg) {
+               case CSSM_ALGID_RSA:
+               case CSSM_ALGID_MD2WithRSA:
+               case CSSM_ALGID_MD5WithRSA:
+               case CSSM_ALGID_SHA1WithRSA:
+               case CSSM_ALGID_SHA224WithRSA:
+               case CSSM_ALGID_SHA256WithRSA:
+               case CSSM_ALGID_SHA384WithRSA:
+               case CSSM_ALGID_SHA512WithRSA:
+                       *signerAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_SHA1WithECDSA:
+               case CSSM_ALGID_SHA224WithECDSA:
+               case CSSM_ALGID_SHA256WithECDSA:
+               case CSSM_ALGID_SHA384WithECDSA:
+               case CSSM_ALGID_SHA512WithECDSA:
+               case CSSM_ALGID_ECDSA:
+               case CSSM_ALGID_ECDSA_SPECIFIED:
+                       *signerAlg = CSSM_ALGID_ECDSA;
+                       break;
+               case CSSM_ALGID_DSA:
+               case CSSM_ALGID_SHA1WithDSA:
+                       *signerAlg = CSSM_ALGID_DSA;
+                       break;
+               default:
+                       sslErrorLog("sslCertSignerAlg() unknown sigAlg\n");
+                       ortn = paramErr;
+                       break;
+       }
+errOut:
+       SecCertificateReleaseFirstFieldValue(certRef,
+               &CSSMOID_X509V1SignatureAlgorithm, fieldPtr);
+       return ortn;
+}
+
+OSStatus
+parseIncomingCerts(
+       SSLContext              *ctx,
+       CFArrayRef              certs,
+       SSLCertificate  **destCert,             /* &ctx->{localCert,encryptCert} */
+       CSSM_KEY_PTR    *pubKey,                /* &ctx->signingPubKey, etc. */
+       SecKeyRef               *privKeyRef,    /* &ctx->signingPrivKeyRef, etc. */
+       CSSM_ALGORITHMS *signerAlg)             /* optional */
+{
+       CFIndex                         numCerts;
+       CFIndex                         cert;
+       SSLCertificate          *certChain = NULL;
+       SSLCertificate          *thisSslCert;
+       OSStatus                        ortn;
+       SecIdentityRef          identity;
+       SecCertificateRef       certRef;
+       SecKeyRef                       keyRef;
+       CSSM_DATA                       certData;
+       CSSM_CL_HANDLE          clHand;         // carefully derive from a SecCertificateRef
+       CSSM_RETURN                     crtn;
+       CSSM_KEY_PTR        *pubKey;
+       SecKeyRef           *privKeyRef;
+
+       assert(ctx != NULL);
+       assert(destCert != NULL);               /* though its referent may be NULL */
+       assert(sslPubKey != NULL);
+       assert(sslPrivKeyRef != NULL);
+
+       pubKey = &sslPubKey->key;
+       privKeyRef = &sslPrivKey->key;
+
+       sslDeleteCertificateChain(*destCert, ctx);
+       *destCert = NULL;
+       *pubKey   = NULL;
+       *privKeyRef = NULL;
+
+       if(certs == NULL) {
+               sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
+               return errSSLBadCert;
+       }
+       numCerts = CFArrayGetCount(certs);
+       if(numCerts == 0) {
+               sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
+               return errSSLBadCert;
+       }
+
+       /*
+        * Certs[0] is an SecIdentityRef from which we extract subject cert,
+        * privKeyRef, pubKey.
+        *
+        * 1. ensure the first element is a SecIdentityRef.
+        */
+       identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
+       if(identity == NULL) {
+               sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
+               return paramErr;
+       }
+       if(CFGetTypeID(identity) != SecIdentityGetTypeID()) {
+               sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
+               return paramErr;
+       }
+
+       /*
+        * 2. Extract cert, keys and convert to local format.
+        */
+       ortn = SecIdentityCopyCertificate(identity, &certRef);
+       if(ortn) {
+               sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
+               return ortn;
+       }
+       ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
+       if(ortn) {
+               sslErrorLog("parseIncomingCerts: bad cert array (4)\n");
+               return ortn;
+       }
+       /* enqueue onto head of cert chain */
+       thisSslCert->next = certChain;
+       certChain = thisSslCert;
+
+       if(signerAlg != NULL) {
+               ortn = sslCertSignerAlg(certRef, signerAlg);
+               if(ortn) {
+                       return ortn;
+               }
+       }
+
+       /* fetch private key from identity */
+       ortn = SecIdentityCopyPrivateKey(identity, &keyRef);
+       if(ortn) {
+               sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
+                       (int)ortn);
+               return ortn;
+       }
+       *privKeyRef = keyRef;
+
+       /* obtain public key from cert */
+       ortn = SecCertificateGetCLHandle(certRef, &clHand);
+       if(ortn) {
+               sslErrorLog("parseIncomingCerts: SecCertificateGetCLHandle err %d\n",
+                       (int)ortn);
+               return ortn;
+       }
+       certData.Data = thisSslCert->derCert.data;
+       certData.Length = thisSslCert->derCert.length;
+       crtn = CSSM_CL_CertGetKeyInfo(clHand, &certData, pubKey);
+       if(crtn) {
+               sslErrorLog("parseIncomingCerts: CSSM_CL_CertGetKeyInfo err\n");
+               return (OSStatus)crtn;
+       }
+
+       /* OK, that's the subject cert. Fetch optional remaining certs. */
+       /*
+        * Convert: CFArray of SecCertificateRefs --> chain of SSLCertificates.
+        * Incoming certs have root last; SSLCertificate chain has root
+        * first.
+        */
+       for(cert=1; cert<numCerts; cert++) {
+               certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, cert);
+               if(certRef == NULL) {
+                       sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
+                       return paramErr;
+               }
+               if(CFGetTypeID(certRef) != SecCertificateGetTypeID()) {
+                       sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
+                       return paramErr;
+               }
+
+               /* Extract cert, convert to local format.
+               */
+               ortn = secCertToSslCert(ctx, certRef, &thisSslCert);
+               if(ortn) {
+                       sslErrorLog("parseIncomingCerts: bad cert array (7)\n");
+                       return ortn;
+               }
+               /* enqueue onto head of cert chain */
+               thisSslCert->next = certChain;
+               certChain = thisSslCert;
+       }
+
+       /* SUCCESS */
+       *destCert = certChain;
+       return noErr;
+
+       /* free certChain, everything in it, other vars, return ortn */
+       sslDeleteCertificateChain(certChain, ctx);
+       /* FIXME - anything else? */
+       return ortn;
+}
+
+#else /* !USE_SSLCERTIFICATE */
+
+OSStatus
+parseIncomingCerts(
+       SSLContext                      *ctx,
+       CFArrayRef                      certs,
+       CFArrayRef                      *destCertChain, /* &ctx->{localCertChain,encryptCertChain} */
+       SSLPubKey                       **sslPubKey,    /* &ctx->signingPubKey, etc. */
+       SSLPrivKey                      **sslPrivKey,   /* &ctx->signingPrivKeyRef, etc. */
+       CFIndex                         *signerAlg)             /* optional */
+{
+       OSStatus                        ortn;
+       CFIndex                         ix, numCerts;
+       SecIdentityRef          identity;
+       CFMutableArrayRef       certChain = NULL;       /* Retained */
+       SecCertificateRef       leafCert = NULL;        /* Retained */
+       SecKeyRef                       pubKey = NULL;          /* Retained */
+       SecKeyRef           privKey = NULL;             /* Retained */
+       SecTrustRef         trust = NULL;               /* Retained */
+       SecTrustResultType      trustResult;
+
+       assert(ctx != NULL);
+       assert(destCertChain != NULL);          /* though its referent may be NULL */
+       assert(sslPubKey != NULL);
+       assert(sslPrivKey != NULL);
+
+       if (certs == NULL) {
+               sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
+               ortn = errSSLBadCert;
+               goto errOut;
+       }
+       numCerts = CFArrayGetCount(certs);
+       if (numCerts == 0) {
+               sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
+               ortn = errSSLBadCert;
+               goto errOut;
+       }
+
+       /*
+        * Certs[0] is an SecIdentityRef from which we extract subject cert,
+        * privKey, pubKey.
+        *
+        * 1. ensure the first element is a SecIdentityRef.
+        */
+       identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
+       if (identity == NULL) {
+               sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
+               ortn = paramErr;
+               goto errOut;
+       }
+       if (CFGetTypeID(identity) != SecIdentityGetTypeID()) {
+               sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
+               ortn = paramErr;
+               goto errOut;
+       }
+
+       /*
+        * 2. Extract cert, keys and convert to local format.
+        */
+       ortn = SecIdentityCopyCertificate(identity, &leafCert);
+       if (ortn) {
+               sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
+               goto errOut;
+       }
+
+       /* Fetch private key from identity */
+       ortn = SecIdentityCopyPrivateKey(identity, &privKey);
+       if (ortn) {
+               sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
+                       (int)ortn);
+               goto errOut;
+       }
+
+       /* Convert the input array of SecIdentityRef at the start to an array of
+          all certificates. */
+       certChain = CFArrayCreateMutable(kCFAllocatorDefault, numCerts,
+               &kCFTypeArrayCallBacks);
+       if (!certChain) {
+               ortn = memFullErr;
+               goto errOut;
+       }
+       CFArrayAppendValue(certChain, leafCert);
+       for (ix = 1; ix < numCerts; ++ix) {
+               SecCertificateRef intermediate =
+                       (SecCertificateRef)CFArrayGetValueAtIndex(certs, ix);
+               if (intermediate == NULL) {
+                       sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
+                       ortn = paramErr;
+                       goto errOut;
+               }
+               if (CFGetTypeID(intermediate) != SecCertificateGetTypeID()) {
+                       sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
+                       ortn = paramErr;
+                       goto errOut;
+               }
+
+               CFArrayAppendValue(certChain, intermediate);
+       }
+
+       /* Obtain public key from cert */
+#if TARGET_OS_IOS
+       ortn = SecTrustCreateWithCertificates(certChain, NULL, &trust);
+#else
+       {
+               SecPolicyRef policy = SecPolicyCreateBasicX509();
+               ortn = SecTrustCreateWithCertificates(certChain, policy, &trust);
+               CFReleaseSafe(policy);
+               if (!ortn) {
+                       /* We are only interested in getting the public key from the leaf
+                        * cert here, so for best performance, don't try to build a chain
+                        * or search any keychains.
+                        */
+                       CFArrayRef emptyArray = CFArrayCreate(NULL, NULL, 0, NULL);
+                       (void)SecTrustSetAnchorCertificates(trust, emptyArray);
+                       (void)SecTrustSetKeychains(trust, emptyArray);
+                       CFReleaseSafe(emptyArray);
+               }
+       }
+#endif
+       if (ortn) {
+               sslErrorLog("parseIncomingCerts: SecTrustCreateWithCertificates err %d\n",
+                       (int)ortn);
+               goto errOut;
+       }
+       ortn = SecTrustEvaluate(trust, &trustResult);
+       if (ortn) {
+               sslErrorLog("parseIncomingCerts: SecTrustEvaluate err %d\n",
+                       (int)ortn);
+               goto errOut;
+       }
+       pubKey = SecTrustCopyPublicKey(trust);
+       if (pubKey == NULL) {
+               sslErrorLog("parseIncomingCerts: SecTrustCopyPublicKey failed\n");
+               ortn = -67712; // errSecInvalidKeyRef
+               goto errOut;
+       }
+
+       /* SUCCESS */
+errOut:
+       CFReleaseSafe(trust);
+       CFReleaseSafe(leafCert);
+       CFReleaseSafe(*destCertChain);
+    sslFreePubKey(sslPubKey);
+    sslFreePrivKey(sslPrivKey);
+
+       if (ortn) {
+               CFReleaseSafe(certChain);
+               CFReleaseSafe(pubKey);
+               CFReleaseSafe(privKey);
+
+               *destCertChain = NULL;
+       } else {
+               *destCertChain = certChain;
+               *sslPubKey = (SSLPubKey*)pubKey;
+               *sslPrivKey = (SSLPrivKey*)privKey;
+       }
+
+       return ortn;
+}
+#endif /* !USE_SSLCERTIFICATE */