2 * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <security_utilities/casts.h>
25 #include "SecKeychainItemExtendedAttributes.h"
26 #include "SecKeychainItemPriv.h"
27 #include "ExtendedAttribute.h"
28 #include "SecBridge.h"
29 #include "StorageManager.h"
31 #include <os/activity.h>
33 /* I'm not sure we need this */
35 CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void);
37 static CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void)
41 return gTypes().ExtendedAttribute
.typeID
;
43 END_SECAPI1(_kCFRuntimeNotATypeID
)
47 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
50 * Determine if incoming itemRef can be considered for
51 * this mechanism; throw if not.
53 static void isItemRefCapable(
54 SecKeychainItemRef itemRef
)
56 CFTypeID id
= CFGetTypeID(itemRef
);
57 if((id
== gTypes().ItemImpl
.typeID
) ||
58 (id
== gTypes().Certificate
.typeID
) ||
59 (id
== SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef
)itemRef
))) {
63 MacOSError::throwMe(errSecNoSuchAttr
);
67 static void cfStringToData(
71 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
, cfStr
,
72 kCFStringEncodingUTF8
, 0);
74 /* can't convert to UTF8!? */
75 MacOSError::throwMe(errSecParam
);
77 dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));
82 * Look up an ExtendedAttribute item associated with specified item.
83 * Returns true if found, false if not.
84 * Throws errSecNoSuchAttr if item does not reside on a keychain.
86 static bool lookupExtendedAttr(
87 SecKeychainItemRef itemRef
,
91 isItemRefCapable(itemRef
);
94 * Get the info about the extended attribute to look up:
96 * -- ItemID (i.e., PrimaryKey blob)
100 Item inItem
= ItemImpl::required(itemRef
);
101 const CssmData
&itemID
= inItem
->itemID();
102 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
103 if(!inItem
->keychain()) {
104 /* item must reside on a keychain */
105 MacOSError::throwMe(errSecNoSuchAttr
);
108 CssmAutoData
nameData(Allocator::standard());
109 cfStringToData(attrName
, nameData
);
110 CssmData nameCData
= nameData
;
112 SecKeychainAttribute attrs
[3];
113 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
114 attrs
[0].length
= sizeof(UInt32
);
115 attrs
[0].data
= (void *)&recType
;
116 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
117 attrs
[1].length
= (UInt32
)itemID
.Length
;
118 attrs
[1].data
= itemID
.Data
;
119 attrs
[2].tag
= kExtendedAttrAttributeNameAttr
;
120 attrs
[2].length
= (UInt32
)nameCData
.Length
;
121 attrs
[2].data
= nameCData
.Data
;
122 SecKeychainAttributeList attrList
= {3, attrs
};
124 StorageManager::KeychainList kcList
;
125 kcList
.push_back(inItem
->keychain());
127 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
129 return cursor
->next(foundItem
);
131 catch(const CssmError
&err
) {
132 if(err
.error
== CSSMERR_DL_INVALID_RECORDTYPE
) {
133 /* this keychain not set up for extended attributes yet */
142 OSStatus
SecKeychainItemSetExtendedAttribute(
143 SecKeychainItemRef itemRef
,
144 CFStringRef attrName
,
145 CFDataRef attrValue
) /* NULL means delete the attribute */
148 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
151 os_activity_t activity
= os_activity_create("SecKeychainItemSetExtendedAttribute", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
152 os_activity_scope(activity
);
153 os_release(activity
);
155 if((itemRef
== NULL
) || (attrName
== NULL
)) {
159 /* is there already a matching ExtendedAttribute item? */
161 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
162 if(attrValue
== NULL
) {
163 /* caller asking us to delete existing record */
165 return errSecNoSuchAttr
;
167 foundItem
->keychain()->deleteItem(foundItem
);
168 return errSecSuccess
;
171 CSSM_DATA attrCValue
= {int_cast
<CFIndex
, CSSM_SIZE
>(CFDataGetLength(attrValue
)), (uint8
*)CFDataGetBytePtr(attrValue
)};
174 /* update existing extended attribute record */
175 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
176 foundItem
->setAttribute(attrInfo
, attrCValue
);
180 /* create a new one, add it to the same keychain as itemRef */
181 Item inItem
= ItemImpl::required(itemRef
);
182 CssmAutoData
nameData(Allocator::standard());
183 cfStringToData(attrName
, nameData
);
184 CssmData nameCData
= nameData
;
185 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
186 inItem
->recordType(), inItem
->itemID(), nameCData
,
187 CssmData::overlay(attrCValue
)));
188 Item
outItem(extAttr
);
189 inItem
->keychain()->add(outItem
);
195 OSStatus
SecKeychainItemCopyExtendedAttribute(
196 SecKeychainItemRef itemRef
,
197 CFStringRef attrName
,
198 CFDataRef
*attrValue
) /* RETURNED */
201 //%%% This needs to detect SecCertificateRef items
204 os_activity_t activity
= os_activity_create("SecKeychainItemCopyExtendedAttribute", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
205 os_activity_scope(activity
);
206 os_release(activity
);
208 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
213 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
214 return errSecNoSuchAttr
;
218 * Found it - its kExtendedAttrAttributeValueAttr value is what the
219 * caller is looking for.
220 * We'd like to use getAttribute() here, but that requires that we know
221 * the size of the attribute before hand...
223 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
225 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
226 SecKeychainAttributeList
*attrList
= NULL
;
227 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
228 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
229 /* should never happen... */
230 MacOSError::throwMe(errSecNoSuchAttr
);
232 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
233 attrList
->attr
->length
);
234 ItemImpl::freeAttributesAndData(attrList
, NULL
);
238 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
239 SecKeychainItemRef itemRef
,
240 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
241 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
245 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
248 os_activity_t activity
= os_activity_create("SecKeychainItemCopyAllExtendedAttributes", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
249 os_activity_scope(activity
);
250 os_release(activity
);
252 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
256 isItemRefCapable(itemRef
);
259 * Get the info about the extended attribute to look up:
261 * -- ItemID (i.e., PrimaryKey blob)
264 Item inItem
= ItemImpl::required(itemRef
);
265 const CssmData
&itemID
= inItem
->itemID();
266 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
267 if(!inItem
->keychain()) {
268 /* item must reside on a keychain */
269 MacOSError::throwMe(errSecNoSuchAttr
);
272 SecKeychainAttribute attrs
[2];
273 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
274 attrs
[0].length
= sizeof(UInt32
);
275 attrs
[0].data
= (void *)&recType
;
276 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
277 attrs
[1].length
= (UInt32
)itemID
.Length
;
278 attrs
[1].data
= itemID
.Data
;
279 SecKeychainAttributeList attrList
= {2, attrs
};
281 StorageManager::KeychainList kcList
;
282 kcList
.push_back(inItem
->keychain());
284 CFMutableArrayRef outNames
= NULL
;
285 CFMutableArrayRef outValues
= NULL
;
286 OSStatus ourRtn
= errSecSuccess
;
288 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
293 gotOne
= cursor
->next(foundItem
);
303 * Found one - return its kExtendedAttrAttributeNameAttr and
304 * (optionally) kExtendedAttrAttributeValueAttr attribute values
307 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
308 UInt32 formats
[2] = {0};
309 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
310 SecKeychainAttributeList
*attrList
= NULL
;
311 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
312 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
313 /* should never happen... */
314 ourRtn
= errSecNoSuchAttr
;
317 if(outNames
== NULL
) {
318 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
320 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
321 /* this one's optional */
322 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
326 * I don't see how we can assume that the order of the returned
327 * attributes is the same as the order of the tags we specified
329 for(unsigned dex
=0; dex
<2; dex
++) {
330 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
331 CFDataRef cfd
= NULL
;
332 CFStringRef cfs
= NULL
;
334 case kExtendedAttrAttributeNameAttr
:
335 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
337 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
338 * this should always work... */
339 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
340 CFArrayAppendValue(outNames
, cfs
);
344 case kExtendedAttrAttributeValueAttr
:
345 if(outValues
== NULL
) {
348 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
349 CFArrayAppendValue(outValues
, cfd
);
353 /* should never happen, right? */
354 MacOSError::throwMe(errSecInternalComponent
);
357 ItemImpl::freeAttributesAndData(attrList
, NULL
);
358 } /* main loop fetching matching Extended Attr records */
365 CFRelease(outValues
);
367 MacOSError::throwMe(ourRtn
);
370 if(outNames
== NULL
) {
371 /* no extended attributes found */
372 return errSecNoSuchAttr
;
374 *attrNames
= outNames
;
376 *attrValues
= outValues
;