]> git.saurik.com Git - apple/security.git/blob - OSX/include/security_pkcs12/pkcs12Keychain.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / include / security_pkcs12 / pkcs12Keychain.cpp
1 /*
2 * Copyright (c) 2003-2004,2011-2014 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 /*
25 * pkcs12Keychain.h - P12Coder keychain-related functions.
26 */
27
28 #include "pkcs12Coder.h"
29 #include "pkcs12Templates.h"
30 #include "pkcs12Utils.h"
31 #include "pkcs12Debug.h"
32 #include "pkcs12Crypto.h"
33 #include <Security/cssmerr.h>
34 #include <security_cdsa_utils/cuDbUtils.h> // cuAddCrlToDb()
35 #include <security_asn1/nssUtils.h>
36 #include <security_cdsa_utilities/KeySchema.h> /* private API */
37 #include <security_keychain/SecImportExportCrypto.h> /* private API */
38
39 /*
40 * Store the results of a successful decode in app-specified
41 * keychain per mImportFlags. Also assign public key hash attributes to any
42 * private keys found.
43 */
44 void P12Coder::storeDecodeResults()
45 {
46 assert(mKeychain != NULL);
47 assert(mDlDbHand.DLHandle != 0);
48 if(mImportFlags & kSecImportKeys) {
49 setPrivateKeyHashes();
50 }
51 if(mImportFlags & kSecImportCertificates) {
52 for(unsigned dex=0; dex<numCerts(); dex++) {
53 P12CertBag *certBag = mCerts[dex];
54 SecCertificateRef secCert = certBag->getSecCert();
55 OSStatus ortn = SecCertificateAddToKeychain(secCert, mKeychain);
56 CFRelease(secCert);
57 switch(ortn) {
58 case errSecSuccess: // normal
59 p12DecodeLog("cert added to keychain");
60 break;
61 case errSecDuplicateItem: // dup cert, OK< skip
62 p12DecodeLog("skipping dup cert");
63 break;
64 default:
65 p12ErrorLog("SecCertificateAddToKeychain failure\n");
66 MacOSError::throwMe(ortn);
67 }
68 }
69 }
70
71 if(mImportFlags & kSecImportCRLs) {
72 for(unsigned dex=0; dex<numCrls(); dex++) {
73 P12CrlBag *crlBag = mCrls[dex];
74 CSSM_RETURN crtn = cuAddCrlToDb(mDlDbHand,
75 clHand(),
76 &crlBag->crlData(),
77 NULL); // no URI known
78 switch(crtn) {
79 case CSSM_OK: // normal
80 p12DecodeLog("CRL added to keychain");
81 break;
82 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: // dup, ignore
83 p12DecodeLog("skipping dup CRL");
84 break;
85 default:
86 p12LogCssmError("Error adding CRL to keychain", crtn);
87 CssmError::throwMe(crtn);
88 }
89 }
90 }
91
92 /* If all of that succeeded, post notification for imported keys */
93 if(mImportFlags & kSecImportKeys) {
94 notifyKeyImport();
95 }
96 }
97
98 /*
99 * Assign appropriate public key hash attribute to each
100 * private key.
101 */
102 void P12Coder::setPrivateKeyHashes()
103 {
104 CSSM_KEY_PTR newKey;
105
106 for(unsigned dex=0; dex<numKeys(); dex++) {
107 P12KeyBag *keyBag = mKeys[dex];
108
109 CSSM_DATA newLabel = {0, NULL};
110 CFStringRef friendlyName = keyBag->friendlyName();
111 newKey = NULL;
112 CSSM_RETURN crtn = p12SetPubKeyHash(mCspHand,
113 mDlDbHand,
114 keyBag->label(),
115 p12StringToUtf8(friendlyName, mCoder),
116 mCoder,
117 newLabel,
118 newKey);
119 if(friendlyName) {
120 CFRelease(friendlyName);
121 }
122 switch(crtn) {
123 case CSSM_OK:
124 /* update key's label in case we have to delete on error */
125 keyBag->setLabel(newLabel);
126 p12DecodeLog("set pub key hash for private key");
127 break;
128 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
129 /*
130 * Special case: update keyBag's CSSM_KEY and proceed without error
131 */
132 p12DecodeLog("ignoring dup private key");
133 assert(newKey != NULL);
134 keyBag->setKey(newKey);
135 keyBag->dupKey(true);
136 /* update key's label in case we have to delete on error */
137 keyBag->setLabel(newLabel);
138 break;
139 default:
140 p12ErrorLog("p12SetPubKeyHash failure\n");
141 CssmError::throwMe(crtn);
142 }
143 }
144 }
145
146 /*
147 * Post keychain notification for imported keys.
148 */
149 void P12Coder::notifyKeyImport()
150 {
151 if(mKeychain == NULL) {
152 /* Can't notify if user only gave us DLDB */
153 return;
154 }
155 for(unsigned dex=0; dex<numKeys(); dex++) {
156 P12KeyBag *keyBag = mKeys[dex];
157 if(keyBag->dupKey()) {
158 /* no notification for keys we merely looked up */
159 continue;
160 }
161 CssmData &labelData = CssmData::overlay(keyBag->label());
162 OSStatus ortn = impExpKeyNotify(mKeychain, labelData, *keyBag->key());
163 if(ortn) {
164 p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn);
165 MacOSError::throwMe(ortn);
166 }
167 }
168 }
169
170 /*
171 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
172 * "match" if their localKeyIds match. Returns NULL if not found.
173 */
174 P12CertBag *P12Coder::findCertForKey(
175 P12KeyBag *keyBag)
176 {
177 assert(keyBag != NULL);
178 CSSM_DATA &keyKeyId = keyBag->localKeyIdCssm();
179
180 for(unsigned dex=0; dex<numCerts(); dex++) {
181 P12CertBag *certBag = mCerts[dex];
182 CSSM_DATA &certKeyId = certBag->localKeyIdCssm();
183 if(nssCompareCssmData(&keyKeyId, &certKeyId)) {
184 p12DecodeLog("findCertForKey SUCCESS");
185 return certBag;
186 }
187 }
188 p12DecodeLog("findCertForKey FAILURE");
189 return NULL;
190 }
191
192 /*
193 * Export items specified as SecKeychainItemRefs.
194 */
195 void P12Coder::exportKeychainItems(
196 CFArrayRef items)
197 {
198 assert(items != NULL);
199 CFIndex numItems = CFArrayGetCount(items);
200 for(CFIndex dex=0; dex<numItems; dex++) {
201 const void *item = CFArrayGetValueAtIndex(items, dex);
202 if(item == NULL) {
203 p12ErrorLog("exportKeychainItems: NULL item\n");
204 MacOSError::throwMe(errSecParam);
205 }
206 CFTypeID itemType = CFGetTypeID(item);
207 if(itemType == SecCertificateGetTypeID()) {
208 addSecCert((SecCertificateRef)item);
209 }
210 else if(itemType == SecKeyGetTypeID()) {
211 addSecKey((SecKeyRef)item);
212 }
213 else {
214 p12ErrorLog("exportKeychainItems: unknown item\n");
215 MacOSError::throwMe(errSecParam);
216 }
217 }
218 }
219
220 /*
221 * Gross kludge to work around the fact that SecKeyRefs have no attributes which
222 * are visible at the Sec layer. Not only are the attribute names we happen
223 * to know about (Label, PrintName) not publically visible anywhere in the
224 * system, but the *format* of the attr names for SecKeyRefs differs from
225 * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys,
226 * NAME_AS_INTEGER for everything else).
227 *
228 * So. We use the privately accessible schema definition table for
229 * keys to map from the attr name strings we happen to know about to a
230 * totally private name-as-int index which we can then use in the
231 * SecKeychainItemCopyAttributesAndData mechanism.
232 *
233 * This will go away if SecKeyRef defines its actual attrs as strings, AND
234 * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs
235 * as strings rather than integers.
236 */
237 static OSStatus attrNameToInt(
238 const char *name,
239 UInt32 *attrInt)
240 {
241 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList =
242 KeySchema::KeySchemaAttributeList;
243 unsigned numAttrs = KeySchema::KeySchemaAttributeCount;
244 for(unsigned dex=0; dex<numAttrs; dex++) {
245 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex];
246 if(!strcmp(name, info->AttributeName)) {
247 *attrInt = info->AttributeId;
248 return errSecSuccess;
249 }
250 }
251 return errSecParam;
252 }
253
254 void P12Coder::addSecKey(
255 SecKeyRef keyRef)
256 {
257 /* get the cert's attrs (not data) */
258
259 /*
260 * Convert the attr name strings we happen to know about to
261 * unknowable name-as-int values.
262 */
263 UInt32 printNameTag;
264 OSStatus ortn = attrNameToInt(P12_KEY_ATTR_PRINT_NAME, &printNameTag);
265 if(ortn) {
266 p12ErrorLog("addSecKey: problem looking up key attr name\n");
267 MacOSError::throwMe(ortn);
268 }
269 UInt32 labelHashTag;
270 ortn = attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH, &labelHashTag);
271 if(ortn) {
272 p12ErrorLog("addSecKey: problem looking up key attr name\n");
273 MacOSError::throwMe(ortn);
274 }
275
276 UInt32 tags[2];
277 tags[0] = printNameTag;
278 tags[1] = labelHashTag;
279
280 /* I don't know what the format field is for */
281 SecKeychainAttributeInfo attrInfo;
282 attrInfo.count = 2;
283 attrInfo.tag = tags;
284 attrInfo.format = NULL; // ???
285
286 /* FIXME header says this is an IN/OUT param, but it's not */
287 SecKeychainAttributeList *attrList = NULL;
288
289 ortn = SecKeychainItemCopyAttributesAndData(
290 (SecKeychainItemRef)keyRef,
291 &attrInfo,
292 NULL, // itemClass
293 &attrList,
294 NULL, // don't need the data
295 NULL);
296 if(ortn) {
297 p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData "
298 "error\n");
299 MacOSError::throwMe(ortn);
300 }
301
302 /* Snag the attrs, convert to something useful */
303 CFStringRef friendName = NULL;
304 CFDataRef localKeyId = NULL;
305 for(unsigned i=0; i<attrList->count; i++) {
306 SecKeychainAttribute *attr = &attrList->attr[i];
307 if(attr->tag == printNameTag) {
308 friendName = CFStringCreateWithBytes(NULL,
309 (UInt8 *)attr->data, attr->length,
310 kCFStringEncodingUTF8, false);
311 }
312 else if(attr->tag == labelHashTag) {
313 localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
314 }
315 else {
316 p12ErrorLog("addSecKey: unexpected attr tag\n");
317 MacOSError::throwMe(errSecParam);
318
319 }
320 }
321
322 /*
323 * Infer the CSP associated with this key.
324 * FIXME: this should be an attribute of the SecKeyRef itself,
325 * not inferred from the keychain it happens to be living on
326 * (SecKeyRefs should not have to be attached to Keychains at
327 * this point).
328 */
329 SecKeychainRef kcRef;
330 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
331 if(ortn) {
332 p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn);
333 MacOSError::throwMe(ortn);
334 }
335 CSSM_CSP_HANDLE cspHand;
336 ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
337 if(ortn) {
338 p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn);
339 MacOSError::throwMe(ortn);
340 }
341 CFRelease(kcRef);
342
343 /* and the CSSM_KEY itself */
344 const CSSM_KEY *cssmKey;
345 ortn = SecKeyGetCSSMKey(keyRef, &cssmKey);
346 if(ortn) {
347 p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn);
348 MacOSError::throwMe(ortn);
349 }
350
351 /* Cook up a key bag and save it */
352 P12KeyBag *keyBag = new P12KeyBag(cssmKey,
353 cspHand,
354 friendName, localKeyId,
355 NULL, // other attrs
356 mCoder,
357 keyRef);
358 addKey(keyBag);
359 SecKeychainItemFreeAttributesAndData(attrList, NULL);
360 if(friendName) {
361 CFRelease(friendName);
362 }
363 if(localKeyId) {
364 CFRelease(localKeyId);
365 }
366 }
367
368 void P12Coder::addSecCert(
369 SecCertificateRef certRef)
370 {
371 /* get the cert's attrs and data */
372 /* I don't know what the format field is for */
373 SecKeychainAttributeInfo attrInfo;
374 attrInfo.count = 2;
375 UInt32 tags[2] = {kSecLabelItemAttr, kSecPublicKeyHashItemAttr};
376 attrInfo.tag = tags;
377 attrInfo.format = NULL; // ???
378
379 /* FIXME header says this is an IN/OUT param, but it's not */
380 SecKeychainAttributeList *attrList = NULL;
381 UInt32 certLen;
382 void *certData;
383
384 OSStatus ortn = SecKeychainItemCopyAttributesAndData(
385 (SecKeychainItemRef)certRef,
386 &attrInfo,
387 NULL, // itemClass
388 &attrList,
389 &certLen,
390 &certData);
391 if(ortn) {
392 p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData "
393 "error\n");
394 MacOSError::throwMe(ortn);
395 }
396
397 /* Snag the attrs, convert to something useful */
398 CFStringRef friendName = NULL;
399 CFDataRef localKeyId = NULL;
400 for(unsigned i=0; i<attrList->count; i++) {
401 SecKeychainAttribute *attr = &attrList->attr[i];
402 switch(attr->tag) {
403 case kSecPublicKeyHashItemAttr:
404 localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
405 break;
406 case kSecLabelItemAttr:
407 /* FIXME: always in UTF8? */
408 friendName = CFStringCreateWithBytes(NULL,
409 (UInt8 *)attr->data, attr->length, kCFStringEncodingUTF8,
410 false);
411 break;
412 default:
413 p12ErrorLog("addSecCert: unexpected attr tag\n");
414 MacOSError::throwMe(errSecParam);
415
416 }
417 }
418
419 /* Cook up a cert bag and save it */
420 CSSM_DATA cData = {certLen, (uint8 *)certData};
421 P12CertBag *certBag = new P12CertBag(CT_X509, cData, friendName,
422 localKeyId, NULL, mCoder);
423 addCert(certBag);
424 SecKeychainItemFreeAttributesAndData(attrList, certData);
425 if(friendName) {
426 CFRelease(friendName);
427 }
428 if(localKeyId) {
429 CFRelease(localKeyId);
430 }
431 }
432
433 /*
434 * Delete anything stored in a keychain during decode, called on
435 * decode error.
436 * Currently the only thing we have to deal with is private keys,
437 * since certs and CRLs don't get stored until the end of a successful
438 * decode.
439 */
440 void P12Coder::deleteDecodedItems()
441 {
442 if(!(mImportFlags & kSecImportKeys)) {
443 /* no keys stored, done */
444 return;
445 }
446 if(mDlDbHand.DLHandle == 0) {
447 /* no keychain, done */
448 return;
449 }
450
451 unsigned nKeys = numKeys();
452 for(unsigned dex=0; dex<nKeys; dex++) {
453 P12KeyBag *keyBag = mKeys[dex];
454 p12DeleteKey(mDlDbHand, keyBag->label());
455 }
456
457 }
458