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 #include "LegacyAPICounts.h"
35 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
38 * Determine if incoming itemRef can be considered for
39 * this mechanism; throw if not.
41 static void isItemRefCapable(
42 SecKeychainItemRef itemRef
)
44 CFTypeID id
= CFGetTypeID(itemRef
);
45 if((id
== gTypes().ItemImpl
.typeID
) ||
46 (id
== gTypes().Certificate
.typeID
) ||
47 (id
== SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef
)itemRef
))) {
51 MacOSError::throwMe(errSecNoSuchAttr
);
55 static void cfStringToData(
59 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
, cfStr
,
60 kCFStringEncodingUTF8
, 0);
62 /* can't convert to UTF8!? */
63 MacOSError::throwMe(errSecParam
);
65 dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));
70 * Look up an ExtendedAttribute item associated with specified item.
71 * Returns true if found, false if not.
72 * Throws errSecNoSuchAttr if item does not reside on a keychain.
74 static bool lookupExtendedAttr(
75 SecKeychainItemRef itemRef
,
79 isItemRefCapable(itemRef
);
82 * Get the info about the extended attribute to look up:
84 * -- ItemID (i.e., PrimaryKey blob)
88 Item inItem
= ItemImpl::required(itemRef
);
89 const CssmData
&itemID
= inItem
->itemID();
90 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
91 if(!inItem
->keychain()) {
92 /* item must reside on a keychain */
93 MacOSError::throwMe(errSecNoSuchAttr
);
96 CssmAutoData
nameData(Allocator::standard());
97 cfStringToData(attrName
, nameData
);
98 CssmData nameCData
= nameData
;
100 SecKeychainAttribute attrs
[3];
101 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
102 attrs
[0].length
= sizeof(UInt32
);
103 attrs
[0].data
= (void *)&recType
;
104 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
105 attrs
[1].length
= (UInt32
)itemID
.Length
;
106 attrs
[1].data
= itemID
.Data
;
107 attrs
[2].tag
= kExtendedAttrAttributeNameAttr
;
108 attrs
[2].length
= (UInt32
)nameCData
.Length
;
109 attrs
[2].data
= nameCData
.Data
;
110 SecKeychainAttributeList attrList
= {3, attrs
};
112 StorageManager::KeychainList kcList
;
113 kcList
.push_back(inItem
->keychain());
115 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
117 return cursor
->next(foundItem
);
119 catch(const CssmError
&err
) {
120 if(err
.error
== CSSMERR_DL_INVALID_RECORDTYPE
) {
121 /* this keychain not set up for extended attributes yet */
130 OSStatus
SecKeychainItemSetExtendedAttribute(
131 SecKeychainItemRef itemRef
,
132 CFStringRef attrName
,
133 CFDataRef attrValue
) /* NULL means delete the attribute */
136 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
139 os_activity_t activity
= os_activity_create("SecKeychainItemSetExtendedAttribute", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
140 os_activity_scope(activity
);
141 os_release(activity
);
143 if((itemRef
== NULL
) || (attrName
== NULL
)) {
147 /* is there already a matching ExtendedAttribute item? */
149 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
150 if(attrValue
== NULL
) {
151 /* caller asking us to delete existing record */
153 return errSecNoSuchAttr
;
155 foundItem
->keychain()->deleteItem(foundItem
);
156 return errSecSuccess
;
159 CSSM_DATA attrCValue
= {int_cast
<CFIndex
, CSSM_SIZE
>(CFDataGetLength(attrValue
)), (uint8
*)CFDataGetBytePtr(attrValue
)};
162 /* update existing extended attribute record */
163 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
164 foundItem
->setAttribute(attrInfo
, attrCValue
);
168 /* create a new one, add it to the same keychain as itemRef */
169 Item inItem
= ItemImpl::required(itemRef
);
170 CssmAutoData
nameData(Allocator::standard());
171 cfStringToData(attrName
, nameData
);
172 CssmData nameCData
= nameData
;
173 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
174 inItem
->recordType(), inItem
->itemID(), nameCData
,
175 CssmData::overlay(attrCValue
)));
176 Item
outItem(extAttr
);
177 inItem
->keychain()->add(outItem
);
183 OSStatus
SecKeychainItemCopyExtendedAttribute(
184 SecKeychainItemRef itemRef
,
185 CFStringRef attrName
,
186 CFDataRef
*attrValue
) /* RETURNED */
189 //%%% This needs to detect SecCertificateRef items
192 os_activity_t activity
= os_activity_create("SecKeychainItemCopyExtendedAttribute", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
193 os_activity_scope(activity
);
194 os_release(activity
);
196 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
201 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
202 return errSecNoSuchAttr
;
206 * Found it - its kExtendedAttrAttributeValueAttr value is what the
207 * caller is looking for.
208 * We'd like to use getAttribute() here, but that requires that we know
209 * the size of the attribute before hand...
211 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
213 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
214 SecKeychainAttributeList
*attrList
= NULL
;
215 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
216 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
217 /* should never happen... */
218 MacOSError::throwMe(errSecNoSuchAttr
);
220 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
221 attrList
->attr
->length
);
222 ItemImpl::freeAttributesAndData(attrList
, NULL
);
226 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
227 SecKeychainItemRef itemRef
,
228 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
229 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
233 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
236 os_activity_t activity
= os_activity_create("SecKeychainItemCopyAllExtendedAttributes", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
237 os_activity_scope(activity
);
238 os_release(activity
);
240 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
244 isItemRefCapable(itemRef
);
247 * Get the info about the extended attribute to look up:
249 * -- ItemID (i.e., PrimaryKey blob)
252 Item inItem
= ItemImpl::required(itemRef
);
253 const CssmData
&itemID
= inItem
->itemID();
254 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
255 if(!inItem
->keychain()) {
256 /* item must reside on a keychain */
257 MacOSError::throwMe(errSecNoSuchAttr
);
260 SecKeychainAttribute attrs
[2];
261 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
262 attrs
[0].length
= sizeof(UInt32
);
263 attrs
[0].data
= (void *)&recType
;
264 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
265 attrs
[1].length
= (UInt32
)itemID
.Length
;
266 attrs
[1].data
= itemID
.Data
;
267 SecKeychainAttributeList attrList
= {2, attrs
};
269 StorageManager::KeychainList kcList
;
270 kcList
.push_back(inItem
->keychain());
272 CFMutableArrayRef outNames
= NULL
;
273 CFMutableArrayRef outValues
= NULL
;
274 OSStatus ourRtn
= errSecSuccess
;
276 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
281 gotOne
= cursor
->next(foundItem
);
291 * Found one - return its kExtendedAttrAttributeNameAttr and
292 * (optionally) kExtendedAttrAttributeValueAttr attribute values
295 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
296 UInt32 formats
[2] = {0};
297 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
298 SecKeychainAttributeList
*attrList
= NULL
;
299 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
300 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
301 /* should never happen... */
302 ourRtn
= errSecNoSuchAttr
;
305 if(outNames
== NULL
) {
306 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
308 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
309 /* this one's optional */
310 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
314 * I don't see how we can assume that the order of the returned
315 * attributes is the same as the order of the tags we specified
317 for(unsigned dex
=0; dex
<2; dex
++) {
318 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
319 CFDataRef cfd
= NULL
;
320 CFStringRef cfs
= NULL
;
322 case kExtendedAttrAttributeNameAttr
:
323 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
325 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
326 * this should always work... */
327 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
328 CFArrayAppendValue(outNames
, cfs
);
332 case kExtendedAttrAttributeValueAttr
:
333 if(outValues
== NULL
) {
336 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
337 CFArrayAppendValue(outValues
, cfd
);
341 /* should never happen, right? */
342 MacOSError::throwMe(errSecInternalComponent
);
345 ItemImpl::freeAttributesAndData(attrList
, NULL
);
346 } /* main loop fetching matching Extended Attr records */
353 CFRelease(outValues
);
355 MacOSError::throwMe(ourRtn
);
358 if(outNames
== NULL
) {
359 /* no extended attributes found */
360 return errSecNoSuchAttr
;
362 *attrNames
= outNames
;
364 *attrValues
= outValues
;