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