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