1/*
2 * Copyright (c) 2007-2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <Security/SecBase.h>
25#include <Security/SecBasePriv.h>
26#include <Security/SecItem.h>
27//#include <Security/SecRSAKey.h>
28#include <Security/SecCertificate.h>
29#include <Security/SecIdentity.h>
30#include <Security/SecIdentityPriv.h>
31#include <Security/SecPolicy.h>
32#include <Security/SecTrust.h>
33#include <Security/SecKeyPriv.h>
34#include "SecInternal.h"
35
36//#include <AssertMacros.h>
37#include <CommonCrypto/CommonDigest.h>
38
39//#include "p12import.h"
40#include "SecImportExport.h"
41
42CFStringRef kSecImportExportPassphrase = CFSTR("passphrase");
43CFStringRef kSecImportExportKeychain = CFSTR("keychain");
44CFStringRef kSecImportExportAccess = CFSTR("access");
45
46CFStringRef kSecImportItemLabel = CFSTR("label");
47CFStringRef kSecImportItemKeyID = CFSTR("keyid");
48CFStringRef kSecImportItemTrust = CFSTR("trust");
49CFStringRef kSecImportItemCertChain = CFSTR("chain");
50CFStringRef kSecImportItemIdentity = CFSTR("identity");
51
52#if 0
53static void collect_certs(const void *key, const void *value, void *context)
54{
55    if (!CFDictionaryContainsKey(value, CFSTR("key"))) {
56        CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert"));
57        if (!cert_bytes)
58            return;
59        SecCertificateRef cert =
60            SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes);
61        if (!cert)
62            return;
63        CFMutableArrayRef cert_array = (CFMutableArrayRef)context;
64        CFArrayAppendValue(cert_array, cert);
65        CFRelease(cert);
66    }
67}
68
69typedef struct {
70    CFMutableArrayRef identities;
71    CFArrayRef certs;
72} build_trust_chains_context;
73
74static void build_trust_chains(const void *key, const void *value,
75    void *context)
76{
77    CFMutableDictionaryRef identity_dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
78        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
79    SecKeyRef private_key = NULL;
80    SecCertificateRef cert = NULL;
81    SecIdentityRef identity = NULL;
82    SecPolicyRef policy = NULL;
83    CFMutableArrayRef cert_chain = NULL, eval_chain = NULL;
84    SecTrustRef trust = NULL;
85    build_trust_chains_context * a_build_trust_chains_context = (build_trust_chains_context*)context;
86
87    CFDataRef key_bytes = CFDictionaryGetValue(value, CFSTR("key"));
88	if(!key_bytes) goto out; //require(key_bytes, out);
89    CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert"));
90    if(!cert_bytes) goto out; //require(cert_bytes, out);
91
92    /* p12import only passes up rsa keys */
93//FIXME: needs SecKeyCreateRSAPrivateKey implementation
94//#if 0
95//	private_key = SecKeyCreateRSAPrivateKey(kCFAllocatorDefault,
96//        CFDataGetBytePtr(key_bytes), CFDataGetLength(key_bytes),
97//        kSecKeyEncodingPkcs1);
98//#endif
99    if(!private_key) goto out; //require(private_key, out);
100    cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes);
101	if(!cert) goto out; //require(cert, out);
102    identity = SecIdentityCreate(kCFAllocatorDefault, cert, private_key);
103	if(!identity) goto out; //require(identity, out);
104    CFDictionarySetValue(identity_dict, kSecImportItemIdentity, identity);
105
106    eval_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
107	if(!eval_chain) goto out; //require(eval_chain, out);
108    CFArrayAppendValue(eval_chain, cert);
109    CFRange all_certs = { 0, CFArrayGetCount(a_build_trust_chains_context->certs) };
110    CFArrayAppendArray(eval_chain, a_build_trust_chains_context->certs, all_certs);
111    policy = SecPolicyCreateBasicX509();
112	if(!policy) goto out; //require(policy, out);
113    SecTrustResultType result;
114    SecTrustCreateWithCertificates(eval_chain, policy, &trust);
115	if(!trust) goto out; //require(trust, out);
116    SecTrustEvaluate(trust, &result);
117    CFDictionarySetValue(identity_dict, kSecImportItemTrust, trust);
118
119    cert_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
120	if(!cert_chain) goto out; //require(cert_chain, out);
121    CFIndex cert_chain_length = SecTrustGetCertificateCount(trust);
122    int i;
123    for (i = 0; i < cert_chain_length; i++)
124        CFArrayAppendValue(cert_chain, SecTrustGetCertificateAtIndex(trust, i));
125    CFDictionarySetValue(identity_dict, kSecImportItemCertChain, cert_chain);
126
127    CFArrayAppendValue(a_build_trust_chains_context->identities, identity_dict);
128out:
129    CFReleaseSafe(identity_dict);
130    CFReleaseSafe(identity);
131    CFReleaseSafe(private_key);
132    CFReleaseSafe(cert);
133    CFReleaseSafe(policy);
134    CFReleaseSafe(cert_chain);
135    CFReleaseSafe(eval_chain);
136    CFReleaseSafe(trust);
137}
138#endif // if 0
139
140OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items)
141{
142	// SecPKCS12Import is implemented on Mac OS X in terms of the existing
143	// SecKeychainItemImport API, which supports importing items into a
144	// specified keychain with initial access control settings for keys.
145	//
146	OSStatus status = errSecSuccess;
147	SecExternalFormat inputFormat = kSecFormatPKCS12;
148	SecExternalItemType itemType = kSecItemTypeAggregate;
149	SecItemImportExportFlags flags = 0; /* don't know if it's PEM armoured */
150	SecKeyImportExportParameters keyParams; /* filled in below... */
151	SecKeychainRef importKeychain = NULL;
152	SecAccessRef importAccess = NULL;
153	CFStringRef importPassword = NULL;
154	CFArrayRef tmpItems = NULL; /* items returned by SecKeychainItemImport */
155	CFMutableArrayRef certs = NULL; /* certificates imported by this function */
156	CFMutableArrayRef identities = NULL; /* items returned by this function */
157
158	if (options) {
159		importKeychain = (SecKeychainRef) CFDictionaryGetValue(options, kSecImportExportKeychain);
160		if (importKeychain)
161			CFRetain(importKeychain);
162		importAccess = (SecAccessRef) CFDictionaryGetValue(options, kSecImportExportAccess);
163		if (importAccess)
164			CFRetain(importAccess);
165		importPassword = (CFStringRef) CFDictionaryGetValue(options, kSecImportExportPassphrase);
166		if (importPassword)
167			CFRetain(importPassword);
168	}
169
170	if (!importKeychain) {
171		// SecKeychainItemImport requires a keychain, so use default
172		status = SecKeychainCopyDefault(&importKeychain);
173	}
174
175	memset(&keyParams, 0, sizeof(SecKeyImportExportParameters));
176	keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
177	keyParams.passphrase = importPassword;
178	keyParams.accessRef = importAccess;
179
180	status = SecKeychainItemImport(pkcs12_data,
181								   NULL,		/* no filename */
182								   &inputFormat,
183								   &itemType,
184								   flags,
185								   &keyParams,
186								   importKeychain,
187								   &tmpItems);
188
189	// build an array of all non-identity certificates which were imported
190	if (!status) {
191		certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
192		CFIndex i, count = CFArrayGetCount(tmpItems);
193		for (i=0; i<count; i++) {
194			CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i);
195			CFTypeID itemID = CFGetTypeID(anItem);
196			if (itemID == SecCertificateGetTypeID()) {
197				CFArrayAppendValue(certs, anItem);
198			}
199		}
200	}
201
202	// now build the output items (array of dictionaries)
203	if (!status) {
204		identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
205		CFIndex i, count = CFArrayGetCount(tmpItems);
206		for (i=0; i<count; i++) {
207			CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i);
208			CFTypeID itemID = CFGetTypeID(anItem);
209			if (itemID == SecIdentityGetTypeID()) {
210				CFMutableDictionaryRef itemDict;
211				itemDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
212													 0,
213													 &kCFTypeDictionaryKeyCallBacks,
214													 &kCFTypeDictionaryValueCallBacks);
215
216				SecCertificateRef itemCert = NULL;
217				status = SecIdentityCopyCertificate((SecIdentityRef)anItem, &itemCert);
218
219				// label
220				if (!status) {
221					CFStringRef label = SecCertificateCopySubjectSummary(itemCert);
222					if (label) {
223						CFDictionaryAddValue(itemDict, kSecImportItemLabel, label);
224						CFRelease(label);
225					}
226				}
227
228				// key ID
229				if (!status) {
230					SecKeyRef itemKey = NULL;
231					status = SecCertificateCopyPublicKey(itemCert, &itemKey);
232					if (!status) {
233						const CSSM_KEY *cssmKey;
234						status = SecKeyGetCSSMKey(itemKey, &cssmKey);
235						if (!status) {
236							unsigned char hash[CC_SHA1_DIGEST_LENGTH];
237							CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, &hash[0]);
238							CFDataRef digest = CFDataCreate(NULL, (const UInt8 *)hash, CC_SHA1_DIGEST_LENGTH);
239							if (digest) {
240								CFDictionaryAddValue(itemDict, kSecImportItemKeyID, digest);
241								CFRelease(digest);
242							}
243						}
244						CFRelease(itemKey);
245					}
246				}
247
248				// trust
249				SecTrustRef trust = NULL;
250				SecPolicyRef policy = SecPolicyCreateBasicX509();
251				CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
252				CFArrayAppendValue(certArray, itemCert);
253				if (certs) {
254					CFArrayAppendArray(certArray, certs, CFRangeMake(0, CFArrayGetCount(certs)));
255				}
256				status = SecTrustCreateWithCertificates(certArray, policy, &trust);
257				if (policy) {
258					CFRelease(policy);
259				}
260				if (trust) {
261					CFDictionaryAddValue(itemDict, kSecImportItemTrust, trust);
262					CFRelease(trust);
263				}
264
265				// certificate chain
266				if (certArray) {
267					CFDictionaryAddValue(itemDict, kSecImportItemCertChain, certArray);
268					CFRelease(certArray);
269				}
270
271				// identity
272				CFDictionaryAddValue(itemDict, kSecImportItemIdentity, anItem);
273
274				if (itemCert)
275					CFRelease(itemCert);
276				CFArrayAppendValue(identities, itemDict);
277				CFRelease(itemDict);
278			}
279		}
280	}
281
282	if (items)
283		*items = identities;
284	else if (identities)
285		CFRelease(identities);
286
287	if (certs)
288		CFRelease(certs);
289	if (tmpItems)
290		CFRelease(tmpItems);
291	if (importKeychain)
292		CFRelease(importKeychain);
293	if (importAccess)
294		CFRelease(importAccess);
295	if (importPassword)
296		CFRelease(importPassword);
297
298	return status;
299
300//FIXME: needs SecAsn1Coder implementation
301#if 0
302    pkcs12_context context = {};
303    SecAsn1CoderCreate(&context.coder);
304    if (options)
305        context.passphrase = CFDictionaryGetValue(options, kSecImportExportPassphrase);
306    context.items = CFDictionaryCreateMutable(kCFAllocatorDefault,
307        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
308    int status = p12decode(&context, pkcs12_data);
309    if (!status) {
310        CFMutableArrayRef certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
311        CFDictionaryApplyFunction(context.items, collect_certs, certs);
312
313        CFMutableArrayRef identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
314        build_trust_chains_context a_build_trust_chains_context = { identities, certs };
315        CFDictionaryApplyFunction(context.items, build_trust_chains, &a_build_trust_chains_context);
316        CFReleaseSafe(certs);
317
318        /* ignoring certs that weren't picked up as part of the certchain for found keys */
319
320        *items = identities;
321    }
322
323    CFReleaseSafe(context.items);
324    SecAsn1CoderRelease(context.coder);
325
326    switch (status) {
327    case p12_noErr: return errSecSuccess;
328    case p12_passwordErr: return errSecAuthFailed;
329    case p12_decodeErr: return errSecDecode;
330    default: return errSecInternal;
331    };
332    return errSecSuccess;
333#endif
334}
335
336