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"
32 /* I'm not sure we need this */
34 CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void);
36 static CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void)
40 return gTypes().ExtendedAttribute
.typeID
;
42 END_SECAPI1(_kCFRuntimeNotATypeID
)
46 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
49 * Determine if incoming itemRef can be considered for
50 * this mechanism; throw if not.
52 static void isItemRefCapable(
53 SecKeychainItemRef itemRef
)
55 CFTypeID id
= CFGetTypeID(itemRef
);
56 if((id
== gTypes().ItemImpl
.typeID
) ||
57 (id
== gTypes().Certificate
.typeID
) ||
58 (id
== SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef
)itemRef
))) {
62 MacOSError::throwMe(errSecNoSuchAttr
);
66 static void cfStringToData(
70 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
, cfStr
,
71 kCFStringEncodingUTF8
, 0);
73 /* can't convert to UTF8!? */
74 MacOSError::throwMe(errSecParam
);
76 dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));
81 * Look up an ExtendedAttribute item associated with specified item.
82 * Returns true if found, false if not.
83 * Throws errSecNoSuchAttr if item does not reside on a keychain.
85 static bool lookupExtendedAttr(
86 SecKeychainItemRef itemRef
,
90 isItemRefCapable(itemRef
);
93 * Get the info about the extended attribute to look up:
95 * -- ItemID (i.e., PrimaryKey blob)
99 Item inItem
= ItemImpl::required(itemRef
);
100 const CssmData
&itemID
= inItem
->itemID();
101 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
102 if(!inItem
->keychain()) {
103 /* item must reside on a keychain */
104 MacOSError::throwMe(errSecNoSuchAttr
);
107 CssmAutoData
nameData(Allocator::standard());
108 cfStringToData(attrName
, nameData
);
109 CssmData nameCData
= nameData
;
111 SecKeychainAttribute attrs
[3];
112 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
113 attrs
[0].length
= sizeof(UInt32
);
114 attrs
[0].data
= (void *)&recType
;
115 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
116 attrs
[1].length
= (UInt32
)itemID
.Length
;
117 attrs
[1].data
= itemID
.Data
;
118 attrs
[2].tag
= kExtendedAttrAttributeNameAttr
;
119 attrs
[2].length
= (UInt32
)nameCData
.Length
;
120 attrs
[2].data
= nameCData
.Data
;
121 SecKeychainAttributeList attrList
= {3, attrs
};
123 StorageManager::KeychainList kcList
;
124 kcList
.push_back(inItem
->keychain());
126 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
128 return cursor
->next(foundItem
);
130 catch(const CssmError
&err
) {
131 if(err
.error
== CSSMERR_DL_INVALID_RECORDTYPE
) {
132 /* this keychain not set up for extended attributes yet */
141 OSStatus
SecKeychainItemSetExtendedAttribute(
142 SecKeychainItemRef itemRef
,
143 CFStringRef attrName
,
144 CFDataRef attrValue
) /* NULL means delete the attribute */
147 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
151 if((itemRef
== NULL
) || (attrName
== NULL
)) {
155 /* is there already a matching ExtendedAttribute item? */
157 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
158 if(attrValue
== NULL
) {
159 /* caller asking us to delete existing record */
161 return errSecNoSuchAttr
;
163 foundItem
->keychain()->deleteItem(foundItem
);
164 return errSecSuccess
;
167 CSSM_DATA attrCValue
= {int_cast
<CFIndex
, CSSM_SIZE
>(CFDataGetLength(attrValue
)), (uint8
*)CFDataGetBytePtr(attrValue
)};
170 /* update existing extended attribute record */
171 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
172 foundItem
->setAttribute(attrInfo
, attrCValue
);
176 /* create a new one, add it to the same keychain as itemRef */
177 Item inItem
= ItemImpl::required(itemRef
);
178 CssmAutoData
nameData(Allocator::standard());
179 cfStringToData(attrName
, nameData
);
180 CssmData nameCData
= nameData
;
181 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
182 inItem
->recordType(), inItem
->itemID(), nameCData
,
183 CssmData::overlay(attrCValue
)));
184 Item
outItem(extAttr
);
185 inItem
->keychain()->add(outItem
);
191 OSStatus
SecKeychainItemCopyExtendedAttribute(
192 SecKeychainItemRef itemRef
,
193 CFStringRef attrName
,
194 CFDataRef
*attrValue
) /* RETURNED */
197 //%%% This needs to detect SecCertificateRef items
201 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
206 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
207 return errSecNoSuchAttr
;
211 * Found it - its kExtendedAttrAttributeValueAttr value is what the
212 * caller is looking for.
213 * We'd like to use getAttribute() here, but that requires that we know
214 * the size of the attribute before hand...
216 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
218 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
219 SecKeychainAttributeList
*attrList
= NULL
;
220 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
221 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
222 /* should never happen... */
223 MacOSError::throwMe(errSecNoSuchAttr
);
225 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
226 attrList
->attr
->length
);
227 ItemImpl::freeAttributesAndData(attrList
, NULL
);
231 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
232 SecKeychainItemRef itemRef
,
233 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
234 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
238 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
242 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
246 isItemRefCapable(itemRef
);
249 * Get the info about the extended attribute to look up:
251 * -- ItemID (i.e., PrimaryKey blob)
254 Item inItem
= ItemImpl::required(itemRef
);
255 const CssmData
&itemID
= inItem
->itemID();
256 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
257 if(!inItem
->keychain()) {
258 /* item must reside on a keychain */
259 MacOSError::throwMe(errSecNoSuchAttr
);
262 SecKeychainAttribute attrs
[2];
263 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
264 attrs
[0].length
= sizeof(UInt32
);
265 attrs
[0].data
= (void *)&recType
;
266 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
267 attrs
[1].length
= (UInt32
)itemID
.Length
;
268 attrs
[1].data
= itemID
.Data
;
269 SecKeychainAttributeList attrList
= {2, attrs
};
271 StorageManager::KeychainList kcList
;
272 kcList
.push_back(inItem
->keychain());
274 CFMutableArrayRef outNames
= NULL
;
275 CFMutableArrayRef outValues
= NULL
;
276 OSStatus ourRtn
= errSecSuccess
;
278 KCCursor
cursor(kcList
, (SecItemClass
) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
283 gotOne
= cursor
->next(foundItem
);
293 * Found one - return its kExtendedAttrAttributeNameAttr and
294 * (optionally) kExtendedAttrAttributeValueAttr attribute values
297 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
298 UInt32 formats
[2] = {0};
299 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
300 SecKeychainAttributeList
*attrList
= NULL
;
301 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
302 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
303 /* should never happen... */
304 ourRtn
= errSecNoSuchAttr
;
307 if(outNames
== NULL
) {
308 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
310 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
311 /* this one's optional */
312 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
316 * I don't see how we can assume that the order of the returned
317 * attributes is the same as the order of the tags we specified
319 for(unsigned dex
=0; dex
<2; dex
++) {
320 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
321 CFDataRef cfd
= NULL
;
322 CFStringRef cfs
= NULL
;
324 case kExtendedAttrAttributeNameAttr
:
325 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
327 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
328 * this should always work... */
329 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
330 CFArrayAppendValue(outNames
, cfs
);
334 case kExtendedAttrAttributeValueAttr
:
335 if(outValues
== NULL
) {
338 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
339 CFArrayAppendValue(outValues
, cfd
);
343 /* should never happen, right? */
344 MacOSError::throwMe(errSecInternalComponent
);
347 ItemImpl::freeAttributesAndData(attrList
, NULL
);
348 } /* main loop fetching matching Extended Attr records */
355 CFRelease(outValues
);
357 MacOSError::throwMe(ourRtn
);
360 if(outNames
== NULL
) {
361 /* no extended attributes found */
362 return errSecNoSuchAttr
;
364 *attrNames
= outNames
;
366 *attrValues
= outValues
;