]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/SecKeychainItem.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_keychain / lib / SecKeychainItem.cpp
1 /*
2 * Copyright (c) 2000-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 #include <Security/SecKeychainItem.h>
25 #include <Security/SecKeychainItemPriv.h>
26
27 #include <security_keychain/Keychains.h>
28 #include <security_keychain/KeyItem.h>
29 #include <security_keychain/Item.h>
30 #include <security_keychain/KCCursor.h> // @@@ Remove this when SecKeychainItemFindFirst moves to SecKeychainSearch
31
32 #include <securityd_client/dictionary.h>
33 #include <security_cdsa_utilities/Schema.h>
34 #include <Security/cssmapplePriv.h>
35
36 #include "SecBridge.h"
37 #include "KCExceptions.h"
38 #include "Access.h"
39 #include "SecKeychainItemExtendedAttributes.h"
40
41 //
42 // Given a polymorphic Sec type object, return
43 // its AclBearer component.
44 // Note: Login ACLs are not hooked into this layer;
45 // modules or attachments have no Sec* layer representation.
46 //
47 RefPointer<AclBearer> aclBearer(CFTypeRef itemRef)
48 {
49 // well, exactly what kind of something are you?
50 CFTypeID id = CFGetTypeID(itemRef);
51 if (id == gTypes().ItemImpl.typeID) {
52 // keychain item. If it's in a protected group, return the group key
53 if (SSGroup group = ItemImpl::required(SecKeychainItemRef(itemRef))->group())
54 return &*group;
55 } else if (id == gTypes().KeyItem.typeID) {
56 // key item, return the key itself.
57 if (CssmClient::Key key = KeyItem::required(SecKeyRef(itemRef))->key())
58 return &*key;
59 } else if (id == gTypes().KeychainImpl.typeID) {
60 // keychain (this yields the database ACL)
61 //@@@ not hooked up yet
62 }
63 // Guess not. Bummer
64 MacOSError::throwMe(errSecNoAccessForItem);
65 }
66
67
68 CFTypeID
69 SecKeychainItemGetTypeID(void)
70 {
71 BEGIN_SECAPI
72
73 return gTypes().ItemImpl.typeID;
74
75 END_SECAPI1(_kCFRuntimeNotATypeID)
76 }
77
78
79 OSStatus
80 SecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList *attrList,
81 UInt32 length, const void *data, SecKeychainRef keychainRef,
82 SecAccessRef initialAccess, SecKeychainItemRef *itemRef)
83 {
84 BEGIN_SECAPI
85 KCThrowParamErrIf_(length!=0 && data==NULL);
86 Item item(itemClass, attrList, length, data);
87 if (initialAccess)
88 item->setAccess(Access::required(initialAccess));
89
90 Keychain keychain = nil;
91 try
92 {
93 keychain = Keychain::optional(keychainRef);
94 if ( !keychain->exists() )
95 {
96 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
97 }
98 }
99 catch(...)
100 {
101 keychain = globals().storageManager.defaultKeychainUI(item);
102 }
103
104 keychain->add(item);
105 if (itemRef)
106 *itemRef = item->handle();
107 END_SECAPI
108 }
109
110
111 OSStatus
112 SecKeychainItemModifyContent(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data)
113 {
114 BEGIN_SECAPI
115 Item item = ItemImpl::required(itemRef);
116 item->modifyContent(attrList, length, data);
117 END_SECAPI
118 }
119
120
121 OSStatus
122 SecKeychainItemCopyContent(SecKeychainItemRef itemRef, SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
123 {
124 BEGIN_SECAPI
125 Item item = ItemImpl::required(itemRef);
126 item->getContent(itemClass, attrList, length, outData);
127 END_SECAPI
128 }
129
130
131 OSStatus
132 SecKeychainItemFreeContent(SecKeychainAttributeList *attrList, void *data)
133 {
134 BEGIN_SECAPI
135 ItemImpl::freeContent(attrList, data);
136 END_SECAPI
137 }
138
139
140 OSStatus
141 SecKeychainItemModifyAttributesAndData(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data)
142 {
143 BEGIN_SECAPI
144 Item item = ItemImpl::required(itemRef);
145 item->modifyAttributesAndData(attrList, length, data);
146 END_SECAPI
147 }
148
149
150 OSStatus
151 SecKeychainItemCopyAttributesAndData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
152 {
153 BEGIN_SECAPI
154 Item item = ItemImpl::required(itemRef);
155 item->getAttributesAndData(info, itemClass, attrList, length, outData);
156 END_SECAPI
157 }
158
159
160 OSStatus
161 SecKeychainItemFreeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
162 {
163 BEGIN_SECAPI
164 ItemImpl::freeAttributesAndData(attrList, data);
165 END_SECAPI
166 }
167
168
169 OSStatus
170 SecKeychainItemDelete(SecKeychainItemRef itemRef)
171 {
172 BEGIN_SECAPI
173 Item item = ItemImpl::required( itemRef );
174 Keychain keychain = item->keychain();
175 // item must be persistent.
176 KCThrowIf_( !keychain, errSecInvalidItemRef );
177
178 /*
179 * Before deleting the item, delete any existing Extended Attributes.
180 */
181 OSStatus ortn;
182 CFArrayRef attrNames = NULL;
183 ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, NULL);
184 if(ortn == noErr) {
185 CFIndex numAttrs = CFArrayGetCount(attrNames);
186 for(CFIndex dex=0; dex<numAttrs; dex++) {
187 CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex);
188 /* setting value to NULL ==> delete */
189 SecKeychainItemSetExtendedAttribute(itemRef, attrName, NULL);
190 }
191 }
192
193 /* now delete the item */
194 keychain->deleteItem( item );
195 END_SECAPI
196 }
197
198
199 OSStatus
200 SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef, SecKeychainRef* keychainRef)
201 {
202 BEGIN_SECAPI
203 // make sure this item has a keychain
204 Keychain kc = ItemImpl::required(itemRef)->keychain ();
205 if (kc == NULL)
206 {
207 MacOSError::throwMe(errSecNoSuchKeychain);
208 }
209
210 Required(keychainRef) = kc->handle();
211 END_SECAPI
212 }
213
214
215 OSStatus
216 SecKeychainItemCreateCopy(SecKeychainItemRef itemRef, SecKeychainRef destKeychainRef,
217 SecAccessRef initialAccess, SecKeychainItemRef *itemCopy)
218 {
219 BEGIN_SECAPI
220 Item copy = ItemImpl::required(itemRef)->copyTo(Keychain::optional(destKeychainRef), Access::optional(initialAccess));
221 if (itemCopy)
222 *itemCopy = copy->handle();
223 END_SECAPI
224 }
225
226
227 OSStatus
228 SecKeychainItemGetUniqueRecordID(SecKeychainItemRef itemRef, const CSSM_DB_UNIQUE_RECORD **uniqueRecordID)
229 {
230 BEGIN_SECAPI
231 Required(uniqueRecordID) = ItemImpl::required(itemRef)->dbUniqueRecord();
232 END_SECAPI
233 }
234
235
236 OSStatus
237 SecKeychainItemGetDLDBHandle(SecKeychainItemRef itemRef, CSSM_DL_DB_HANDLE* dldbHandle)
238 {
239 BEGIN_SECAPI
240 *dldbHandle = ItemImpl::required(itemRef)->keychain()->database()->handle();
241 END_SECAPI
242 }
243
244
245 OSStatus SecAccessCreateFromObject(CFTypeRef sourceRef,
246 SecAccessRef *accessRef)
247 {
248 BEGIN_SECAPI
249 Required(accessRef); // preflight
250 SecPointer<Access> access = new Access(*aclBearer(sourceRef));
251 *accessRef = access->handle();
252 END_SECAPI
253 }
254
255
256 /*!
257 */
258 OSStatus SecAccessModifyObject(SecAccessRef accessRef, CFTypeRef sourceRef)
259 {
260 BEGIN_SECAPI
261 Access::required(accessRef)->setAccess(*aclBearer(sourceRef), true);
262 END_SECAPI
263 }
264
265 OSStatus
266 SecKeychainItemCopyAccess(SecKeychainItemRef itemRef, SecAccessRef* accessRef)
267 {
268 BEGIN_SECAPI
269
270 Required(accessRef); // preflight
271 SecPointer<Access> access = new Access(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef)));
272 *accessRef = access->handle();
273
274 END_SECAPI
275 }
276
277
278 OSStatus
279 SecKeychainItemSetAccess(SecKeychainItemRef itemRef, SecAccessRef accessRef)
280 {
281 BEGIN_SECAPI
282
283 Access::required(accessRef)->setAccess(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef)), true);
284
285 ItemImpl::required(itemRef)->postItemEvent (kSecUpdateEvent);
286
287 END_SECAPI
288 }
289
290 /* Sets an item's data for legacy "KC" CoreServices APIs.
291 Note this version sets the data, but doesn't update the item
292 as the KC behavior dictates.
293 */
294 OSStatus SecKeychainItemSetData(SecKeychainItemRef itemRef, UInt32 length, const void* data)
295 {
296 BEGIN_SECAPI
297 ItemImpl::required(itemRef)->setData(length, data);
298 END_SECAPI
299 }
300
301 /* Gets an item's data for legacy "KC" CoreServices APIs.
302 Note this version doesn't take a SecItemClass parameter.
303 */
304 OSStatus SecKeychainItemGetData(SecKeychainItemRef itemRef, UInt32 maxLength, void* data, UInt32* actualLength)
305 {
306 BEGIN_SECAPI
307 /* The caller either needs to specify data and maxLength or an actualLength, so we return either the data itself or the actual length of the data or both. */
308 if (!((data && maxLength) || actualLength))
309 MacOSError::throwMe(paramErr);
310
311 CssmDataContainer aData;
312 ItemImpl::required(itemRef)->getData(aData);
313 if (actualLength)
314 *actualLength = aData.length();
315
316 if (data)
317 {
318 // Make sure the buffer is big enough
319 if (aData.length() > maxLength)
320 MacOSError::throwMe(errKCBufferTooSmall);
321 memcpy(data, aData.data(), aData.length());
322 }
323 END_SECAPI
324 }
325
326 /* Update a keychain item for legacy "KC" CoreServices APIs.
327 The "KC" API's do a 'set attribute', then an 'update'.
328 */
329 OSStatus SecKeychainItemUpdate(SecKeychainItemRef itemRef)
330 {
331 BEGIN_SECAPI
332 ItemImpl::required(itemRef)->update();
333 END_SECAPI
334 }
335
336 /* Add a 'floating' keychain item without UI for legacy "KC" CoreServices APIs.
337 */
338 OSStatus SecKeychainItemAddNoUI(SecKeychainRef keychainRef, SecKeychainItemRef itemRef)
339 {
340 BEGIN_SECAPI
341 Item item = ItemImpl::required(itemRef);
342 Keychain::optional(keychainRef)->add(item);
343 END_SECAPI
344 }
345
346 /* Add a 'floating' keychain item to the default keychain with possible UI for legacy "KC" Carbon APIs.
347 */
348 OSStatus SecKeychainItemAdd(SecKeychainItemRef itemRef)
349 {
350 BEGIN_SECAPI
351 Item item = ItemImpl::required(itemRef);
352 Keychain defaultKeychain = globals().storageManager.defaultKeychainUI(item);
353 defaultKeychain->add(item);
354 END_SECAPI
355 }
356
357 /* Creates a floating keychain item for legacy "KC" CoreServices APIs
358 */
359 OSStatus SecKeychainItemCreateNew(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, SecKeychainItemRef* itemRef)
360 {
361 BEGIN_SECAPI
362 RequiredParam(itemRef) = Item(itemClass, itemCreator, length, data, false)->handle();
363 END_SECAPI
364 }
365
366 /* Gets an individual attribute for legacy "KC" CoreServices APIs
367 */
368 OSStatus SecKeychainItemGetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute, UInt32* actualLength)
369 {
370 BEGIN_SECAPI
371 ItemImpl::required(itemRef)->getAttribute(RequiredParam(attribute), actualLength);
372 END_SECAPI
373 }
374
375 /* Sets an individual attribute for legacy "KC" CoreServices APIs
376 */
377 OSStatus SecKeychainItemSetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute)
378 {
379 BEGIN_SECAPI
380 ItemImpl::required(itemRef)->setAttribute(RequiredParam(attribute));
381 END_SECAPI
382 }
383
384 /* Finds a keychain item for legacy "KC" CoreServices APIs.
385 Note: This version doesn't take a SecItemClass because
386 SecKeychainSearchCreateFromAttributes() requires it.
387 @@@ This should move to SecKeychainSearch.cpp
388 */
389 OSStatus SecKeychainItemFindFirst(SecKeychainRef keychainRef, const SecKeychainAttributeList *attrList, SecKeychainSearchRef *searchRef, SecKeychainItemRef *itemRef)
390 {
391 BEGIN_SECAPI
392 KCCursor cursor;
393 if (keychainRef)
394 cursor = KeychainImpl::required(keychainRef)->createCursor(attrList);
395 else
396 cursor = globals().storageManager.createCursor(attrList);
397
398 Item item;
399 if (!cursor->next(item))
400 return errKCItemNotFound;
401
402 *itemRef=item->handle();
403 if (searchRef)
404 *searchRef=cursor->handle();
405 END_SECAPI
406 }
407
408 OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CFDataRef *persistentItemRef)
409 {
410 BEGIN_SECAPI
411 KCThrowParamErrIf_(!itemRef || !persistentItemRef);
412 Item item = ItemImpl::required(itemRef);
413 item->copyPersistentReference(*persistentItemRef);
414 END_SECAPI
415 }
416
417 OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef)
418 {
419 BEGIN_SECAPI
420 KCThrowParamErrIf_(!persistentItemRef || !itemRef);
421 // make a NameValueDictionary from the data we received
422 CssmData dictData((void*)::CFDataGetBytePtr(persistentItemRef), ::CFDataGetLength(persistentItemRef));
423 NameValueDictionary dict(dictData);
424
425 Keychain keychain;
426 Item item;
427
428 // make sure we have a database identifier
429 if (dict.FindByName(SSUID_KEY) != 0)
430 {
431 DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict);
432 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
433 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(),
434 dlDbIdentifier.dbLocation());
435
436 keychain = globals().storageManager.keychain(newDlDbIdentifier);
437
438 const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY);
439 if (aDictItem && keychain)
440 {
441 PrimaryKey primaryKey(aDictItem->Value());
442 item = keychain->item(primaryKey);
443 }
444 // We can safely release the global API lock now since keychain and item
445 // are CFRetained and will be until they go out of scope.
446 }
447 KCThrowIf_( !item, errSecItemNotFound );
448 *itemRef = item->handle();
449 END_SECAPI
450 }
451
452 OSStatus SecKeychainItemCopyRecordIdentifier(SecKeychainItemRef itemRef, CFDataRef *recordIdentifier)
453 {
454 BEGIN_SECAPI
455 CSSM_DATA data;
456 RequiredParam (recordIdentifier);
457 Item item = ItemImpl::required(itemRef);
458 item->copyRecordIdentifier (data);
459 *recordIdentifier = ::CFDataCreate(kCFAllocatorDefault, (UInt8*) data.Data, data.Length);
460 free (data.Data);
461 END_SECAPI
462 }
463
464 OSStatus
465 SecKeychainItemCopyFromRecordIdentifier(SecKeychainRef keychainRef,
466 SecKeychainItemRef *itemRef,
467 CFDataRef recordIdentifier)
468 {
469 BEGIN_SECAPI
470 // make a local Keychain reference
471 RequiredParam (keychainRef);
472 Keychain keychain = KeychainImpl::optional (keychainRef);
473 RequiredParam (itemRef);
474 RequiredParam (recordIdentifier);
475
476 Db db(keychain->database());
477
478 // make a raw database call to get the data
479 CSSM_DL_DB_HANDLE dbHandle = db.handle ();
480 CSSM_DB_UNIQUE_RECORD uniqueRecord;
481
482 // according to source, we should be able to reconsitute the uniqueRecord
483 // from the data we earlier retained
484
485 // prepare the record id
486 memset (&uniqueRecord, 0, sizeof (uniqueRecord));
487 uniqueRecord.RecordIdentifier.Data = (uint8*) CFDataGetBytePtr (recordIdentifier);
488 uniqueRecord.RecordIdentifier.Length = CFDataGetLength (recordIdentifier);
489
490 // convert this unique id to a CSSM_DB_UNIQUE_RECORD that works for the CSP/DL
491 CSSM_DB_UNIQUE_RECORD_PTR outputUniqueRecordPtr;
492 CSSM_RETURN result;
493 result = CSSM_DL_PassThrough (dbHandle, CSSM_APPLECSPDL_DB_CONVERT_RECORD_IDENTIFIER, &uniqueRecord, (void**) &outputUniqueRecordPtr);
494 KCThrowIf_(result != 0, errSecItemNotFound);
495
496 // from this, get the record type
497 CSSM_DB_RECORD_ATTRIBUTE_DATA attributeData;
498 memset (&attributeData, 0, sizeof (attributeData));
499
500 result = CSSM_DL_DataGetFromUniqueRecordId (dbHandle, outputUniqueRecordPtr, &attributeData, NULL);
501 KCThrowIf_(result != 0, errSecItemNotFound);
502 CSSM_DB_RECORDTYPE recordType = attributeData.DataRecordType;
503
504 // make the unique record item -- precursor to creation of a SecKeychainItemRef
505 DbUniqueRecord unique(db);
506 CSSM_DB_UNIQUE_RECORD_PTR *uniquePtr = unique;
507 *uniquePtr = outputUniqueRecordPtr;
508
509 unique->activate ();
510 Item item = keychain->item (recordType, unique);
511 if (itemRef)
512 {
513 *itemRef = item->handle();
514 }
515 END_SECAPI
516 }
517
518 OSStatus SecKeychainItemCreateFromEncryptedContent(SecItemClass itemClass,
519 UInt32 length, const void *data, SecKeychainRef keychainRef,
520 SecAccessRef initialAccess, SecKeychainItemRef *itemRef, CFDataRef *localID)
521 {
522 BEGIN_SECAPI
523 KCThrowParamErrIf_(length!=0 && data==NULL);
524
525 RequiredParam (localID);
526 RequiredParam (keychainRef);
527
528 Item item(itemClass, (uint32) 0, length, data, true);
529 if (initialAccess)
530 item->setAccess(Access::required(initialAccess));
531
532 Keychain keychain = Keychain::optional(keychainRef);
533 if (!keychain->exists())
534 {
535 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
536 }
537
538 item->doNotEncrypt ();
539 try
540 {
541 keychain->add(item);
542 }
543 catch (const CommonError &err)
544 {
545 if (err.osStatus () == errSecNoSuchClass)
546 {
547 // the only time this should happen is if the item is a certificate (for keychain syncing)
548 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE)
549 {
550 // create the certificate relation
551 Db db(keychain->database());
552
553 db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
554 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
555 Schema::X509CertificateSchemaAttributeCount,
556 Schema::X509CertificateSchemaAttributeList,
557 Schema::X509CertificateSchemaIndexCount,
558 Schema::X509CertificateSchemaIndexList);
559 keychain->keychainSchema()->didCreateRelation(
560 CSSM_DL_DB_RECORD_X509_CERTIFICATE,
561 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
562 Schema::X509CertificateSchemaAttributeCount,
563 Schema::X509CertificateSchemaAttributeList,
564 Schema::X509CertificateSchemaIndexCount,
565 Schema::X509CertificateSchemaIndexList);
566
567 // add the item again
568 keychain->add(item);
569 }
570 }
571 else
572 {
573 throw;
574 }
575 }
576
577 if (itemRef)
578 *itemRef = item->handle();
579
580 CSSM_DATA recordID;
581 item->copyRecordIdentifier (recordID);
582
583 *localID = CFDataCreate(kCFAllocatorDefault, (UInt8*) recordID.Data, recordID.Length);
584 free (recordID.Data);
585 END_SECAPI
586 }
587
588 OSStatus SecKeychainItemCopyAttributesAndEncryptedData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
589 SecItemClass *itemClass, SecKeychainAttributeList **attrList,
590 UInt32 *length, void **outData)
591 {
592 BEGIN_SECAPI
593 Item item = ItemImpl::required(itemRef);
594 item->doNotEncrypt ();
595 item->getAttributesAndData(info, itemClass, attrList, length, outData);
596 END_SECAPI
597 }
598
599 OSStatus SecKeychainItemModifyEncryptedData(SecKeychainItemRef itemRef, UInt32 length, const void *data)
600 {
601 BEGIN_SECAPI
602 Item item = ItemImpl::required(itemRef);
603 item->doNotEncrypt ();
604 item->modifyAttributesAndData(NULL, length, data);
605 END_SECAPI
606 }