]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/Security/SecImportExport.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / Security / SecImportExport.c
diff --git a/OSX/libsecurity_keychain/Security/SecImportExport.c b/OSX/libsecurity_keychain/Security/SecImportExport.c
new file mode 100644 (file)
index 0000000..197a094
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2007-2014 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@
+ */
+
+#include <Security/SecBase.h>
+#include <Security/SecBasePriv.h>
+#include <Security/SecItem.h>
+//#include <Security/SecRSAKey.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecIdentityPriv.h>
+#include <Security/SecPolicy.h>
+#include <Security/SecTrust.h>
+#include <Security/SecKeyPriv.h>
+#include "SecInternal.h"
+
+//#include <AssertMacros.h>
+#include <CommonCrypto/CommonDigest.h>
+
+//#include "p12import.h"
+#include "SecImportExport.h"
+
+const CFStringRef __nonnull kSecImportExportPassphrase = CFSTR("passphrase");
+const CFStringRef __nonnull kSecImportExportKeychain = CFSTR("keychain");
+const CFStringRef __nonnull kSecImportExportAccess = CFSTR("access");
+
+const CFStringRef __nonnull kSecImportItemLabel = CFSTR("label");
+const CFStringRef __nonnull kSecImportItemKeyID = CFSTR("keyid");
+const CFStringRef __nonnull kSecImportItemTrust = CFSTR("trust");
+const CFStringRef __nonnull kSecImportItemCertChain = CFSTR("chain");
+const CFStringRef __nonnull kSecImportItemIdentity = CFSTR("identity");
+
+#if 0
+static void collect_certs(const void *key, const void *value, void *context)
+{
+    if (!CFDictionaryContainsKey(value, CFSTR("key"))) {
+        CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert"));
+        if (!cert_bytes)
+            return;
+        SecCertificateRef cert =
+            SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes);
+        if (!cert)
+            return;
+        CFMutableArrayRef cert_array = (CFMutableArrayRef)context;
+        CFArrayAppendValue(cert_array, cert);
+        CFRelease(cert);
+    }
+}
+
+typedef struct {
+    CFMutableArrayRef identities;
+    CFArrayRef certs;
+} build_trust_chains_context;
+
+static void build_trust_chains(const void *key, const void *value,
+    void *context)
+{
+    CFMutableDictionaryRef identity_dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
+        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    SecKeyRef private_key = NULL;
+    SecCertificateRef cert = NULL;
+    SecIdentityRef identity = NULL;
+    SecPolicyRef policy = NULL;
+    CFMutableArrayRef cert_chain = NULL, eval_chain = NULL;
+    SecTrustRef trust = NULL;
+    build_trust_chains_context * a_build_trust_chains_context = (build_trust_chains_context*)context;
+
+    CFDataRef key_bytes = CFDictionaryGetValue(value, CFSTR("key"));
+       if(!key_bytes) goto out; //require(key_bytes, out);
+    CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert"));
+    if(!cert_bytes) goto out; //require(cert_bytes, out);
+
+    /* p12import only passes up rsa keys */
+//FIXME: needs SecKeyCreateRSAPrivateKey implementation
+//#if 0
+//     private_key = SecKeyCreateRSAPrivateKey(kCFAllocatorDefault,
+//        CFDataGetBytePtr(key_bytes), CFDataGetLength(key_bytes),
+//        kSecKeyEncodingPkcs1);
+//#endif
+    if(!private_key) goto out; //require(private_key, out);
+    cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes);
+       if(!cert) goto out; //require(cert, out);
+    identity = SecIdentityCreate(kCFAllocatorDefault, cert, private_key);
+       if(!identity) goto out; //require(identity, out);
+    CFDictionarySetValue(identity_dict, kSecImportItemIdentity, identity);
+
+    eval_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       if(!eval_chain) goto out; //require(eval_chain, out);
+    CFArrayAppendValue(eval_chain, cert);
+    CFRange all_certs = { 0, CFArrayGetCount(a_build_trust_chains_context->certs) };
+    CFArrayAppendArray(eval_chain, a_build_trust_chains_context->certs, all_certs);
+    policy = SecPolicyCreateBasicX509();
+       if(!policy) goto out; //require(policy, out);
+    SecTrustResultType result;
+    SecTrustCreateWithCertificates(eval_chain, policy, &trust);
+       if(!trust) goto out; //require(trust, out);
+    SecTrustEvaluate(trust, &result);
+    CFDictionarySetValue(identity_dict, kSecImportItemTrust, trust);
+
+    cert_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       if(!cert_chain) goto out; //require(cert_chain, out);
+    CFIndex cert_chain_length = SecTrustGetCertificateCount(trust);
+    int i;
+    for (i = 0; i < cert_chain_length; i++)
+        CFArrayAppendValue(cert_chain, SecTrustGetCertificateAtIndex(trust, i));
+    CFDictionarySetValue(identity_dict, kSecImportItemCertChain, cert_chain);
+
+    CFArrayAppendValue(a_build_trust_chains_context->identities, identity_dict);
+out:
+    CFReleaseSafe(identity_dict);
+    CFReleaseSafe(identity);
+    CFReleaseSafe(private_key);
+    CFReleaseSafe(cert);
+    CFReleaseSafe(policy);
+    CFReleaseSafe(cert_chain);
+    CFReleaseSafe(eval_chain);
+    CFReleaseSafe(trust);
+}
+#endif // if 0
+
+OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items)
+{
+       // SecPKCS12Import is implemented on Mac OS X in terms of the existing
+       // SecKeychainItemImport API, which supports importing items into a
+       // specified keychain with initial access control settings for keys.
+       //
+       OSStatus status = errSecSuccess;
+       SecExternalFormat inputFormat = kSecFormatPKCS12;
+       SecExternalItemType itemType = kSecItemTypeAggregate;
+       SecItemImportExportFlags flags = 0; /* don't know if it's PEM armoured */
+       SecKeyImportExportParameters keyParams; /* filled in below... */
+       SecKeychainRef importKeychain = NULL;
+       SecAccessRef importAccess = NULL;
+       CFStringRef importPassword = NULL;
+       CFArrayRef tmpItems = NULL; /* items returned by SecKeychainItemImport */
+       CFMutableArrayRef certs = NULL; /* certificates imported by this function */
+       CFMutableArrayRef identities = NULL; /* items returned by this function */
+
+       if (options) {
+               importKeychain = (SecKeychainRef) CFDictionaryGetValue(options, kSecImportExportKeychain);
+               if (importKeychain)
+                       CFRetain(importKeychain);
+               importAccess = (SecAccessRef) CFDictionaryGetValue(options, kSecImportExportAccess);
+               if (importAccess)
+                       CFRetain(importAccess);
+               importPassword = (CFStringRef) CFDictionaryGetValue(options, kSecImportExportPassphrase);
+               if (importPassword)
+                       CFRetain(importPassword);
+       }
+
+       if (!importKeychain) {
+               // SecKeychainItemImport requires a keychain, so use default
+               status = SecKeychainCopyDefault(&importKeychain);
+       }
+
+       memset(&keyParams, 0, sizeof(SecKeyImportExportParameters));
+       keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+       keyParams.passphrase = importPassword;
+       keyParams.accessRef = importAccess;
+
+       status = SecKeychainItemImport(pkcs12_data,
+                                                                  NULL,                /* no filename */
+                                                                  &inputFormat,
+                                                                  &itemType,
+                                                                  flags,
+                                                                  &keyParams,
+                                                                  importKeychain,
+                                                                  &tmpItems);
+
+       // build an array of all non-identity certificates which were imported
+       if (!status) {
+               certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFIndex i, count = CFArrayGetCount(tmpItems);
+               for (i=0; i<count; i++) {
+                       CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i);
+                       CFTypeID itemID = CFGetTypeID(anItem);
+                       if (itemID == SecCertificateGetTypeID()) {
+                               CFArrayAppendValue(certs, anItem);
+                       }
+               }
+       }
+
+       // now build the output items (array of dictionaries)
+       if (!status) {
+               identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFIndex i, count = CFArrayGetCount(tmpItems);
+               for (i=0; i<count; i++) {
+                       CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i);
+                       CFTypeID itemID = CFGetTypeID(anItem);
+                       if (itemID == SecIdentityGetTypeID()) {
+                               CFMutableDictionaryRef itemDict;
+                               itemDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                                                                                        0,
+                                                                                                        &kCFTypeDictionaryKeyCallBacks,
+                                                                                                        &kCFTypeDictionaryValueCallBacks);
+
+                               SecCertificateRef itemCert = NULL;
+                               status = SecIdentityCopyCertificate((SecIdentityRef)anItem, &itemCert);
+
+                               // label
+                               if (!status) {
+                                       CFStringRef label = SecCertificateCopySubjectSummary(itemCert);
+                                       if (label) {
+                                               CFDictionaryAddValue(itemDict, kSecImportItemLabel, label);
+                                               CFRelease(label);
+                                       }
+                               }
+
+                               // key ID
+                               if (!status) {
+                                       SecKeyRef itemKey = NULL;
+                                       status = SecCertificateCopyPublicKey(itemCert, &itemKey);
+                                       if (!status) {
+                                               const CSSM_KEY *cssmKey;
+                                               status = SecKeyGetCSSMKey(itemKey, &cssmKey);
+                                               if (!status) {
+                                                       unsigned char hash[CC_SHA1_DIGEST_LENGTH];
+                                                       CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, &hash[0]);
+                                                       CFDataRef digest = CFDataCreate(NULL, (const UInt8 *)hash, CC_SHA1_DIGEST_LENGTH);
+                                                       if (digest) {
+                                                               CFDictionaryAddValue(itemDict, kSecImportItemKeyID, digest);
+                                                               CFRelease(digest);
+                                                       }
+                                               }
+                                               CFRelease(itemKey);
+                                       }
+                               }
+
+                               // trust
+                               SecTrustRef trust = NULL;
+                               SecPolicyRef policy = SecPolicyCreateBasicX509();
+                               CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                               CFArrayAppendValue(certArray, itemCert);
+                               if (certs) {
+                                       CFArrayAppendArray(certArray, certs, CFRangeMake(0, CFArrayGetCount(certs)));
+                               }
+                               status = SecTrustCreateWithCertificates(certArray, policy, &trust);
+                               if (policy) {
+                                       CFRelease(policy);
+                               }
+                               if (trust) {
+                                       CFDictionaryAddValue(itemDict, kSecImportItemTrust, trust);
+                                       CFRelease(trust);
+                               }
+
+                               // certificate chain
+                               if (certArray) {
+                                       CFDictionaryAddValue(itemDict, kSecImportItemCertChain, certArray);
+                                       CFRelease(certArray);
+                               }
+
+                               // identity
+                               CFDictionaryAddValue(itemDict, kSecImportItemIdentity, anItem);
+
+                               if (itemCert)
+                                       CFRelease(itemCert);
+                               CFArrayAppendValue(identities, itemDict);
+                               CFRelease(itemDict);
+                       }
+               }
+       }
+
+       if (items)
+               *items = identities;
+       else if (identities)
+               CFRelease(identities);
+
+       if (certs)
+               CFRelease(certs);
+       if (tmpItems)
+               CFRelease(tmpItems);
+       if (importKeychain)
+               CFRelease(importKeychain);
+       if (importAccess)
+               CFRelease(importAccess);
+       if (importPassword)
+               CFRelease(importPassword);
+
+       return status;
+
+//FIXME: needs SecAsn1Coder implementation
+#if 0
+    pkcs12_context context = {};
+    SecAsn1CoderCreate(&context.coder);
+    if (options)
+        context.passphrase = CFDictionaryGetValue(options, kSecImportExportPassphrase);
+    context.items = CFDictionaryCreateMutable(kCFAllocatorDefault,
+        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    int status = p12decode(&context, pkcs12_data);
+    if (!status) {
+        CFMutableArrayRef certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+        CFDictionaryApplyFunction(context.items, collect_certs, certs);
+
+        CFMutableArrayRef identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+        build_trust_chains_context a_build_trust_chains_context = { identities, certs };
+        CFDictionaryApplyFunction(context.items, build_trust_chains, &a_build_trust_chains_context);
+        CFReleaseSafe(certs);
+
+        /* ignoring certs that weren't picked up as part of the certchain for found keys */
+
+        *items = identities;
+    }
+
+    CFReleaseSafe(context.items);
+    SecAsn1CoderRelease(context.coder);
+
+    switch (status) {
+    case p12_noErr: return errSecSuccess;
+    case p12_passwordErr: return errSecAuthFailed;
+    case p12_decodeErr: return errSecDecode;
+    default: return errSecInternal;
+    };
+    return errSecSuccess;
+#endif
+}
+