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 "SecKeychainItemExtendedAttributes.h"
25 #include "SecKeychainItemPriv.h"
26 #include "ExtendedAttribute.h"
27 #include "SecBridge.h"
28 #include "StorageManager.h"
31 /* I'm not sure we need this */
33 CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void);
35 static CFTypeID
SecKeychainItemExtendedAttributesGetTypeID(void)
39 return gTypes().ExtendedAttribute
.typeID
;
41 END_SECAPI1(_kCFRuntimeNotATypeID
)
45 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
48 * Determine if incoming itemRef can be considered for
49 * this mechanism; throw if not.
51 static void isItemRefCapable(
52 SecKeychainItemRef itemRef
)
54 CFTypeID id
= CFGetTypeID(itemRef
);
55 if((id
== gTypes().ItemImpl
.typeID
) ||
56 (id
== gTypes().Certificate
.typeID
) ||
57 (id
== SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef
)itemRef
))) {
61 MacOSError::throwMe(errSecNoSuchAttr
);
65 static void cfStringToData(
69 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
, cfStr
,
70 kCFStringEncodingUTF8
, 0);
72 /* can't convert to UTF8!? */
73 MacOSError::throwMe(errSecParam
);
75 dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));
80 * Look up an ExtendedAttribute item associated with specified item.
81 * Returns true if found, false if not.
82 * Throws errSecNoSuchAttr if item does not reside on a keychain.
84 static bool lookupExtendedAttr(
85 SecKeychainItemRef itemRef
,
89 isItemRefCapable(itemRef
);
92 * Get the info about the extended attribute to look up:
94 * -- ItemID (i.e., PrimaryKey blob)
98 Item inItem
= ItemImpl::required(itemRef
);
99 const CssmData
&itemID
= inItem
->itemID();
100 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
101 if(!inItem
->keychain()) {
102 /* item must reside on a keychain */
103 MacOSError::throwMe(errSecNoSuchAttr
);
106 CssmAutoData
nameData(Allocator::standard());
107 cfStringToData(attrName
, nameData
);
108 CssmData nameCData
= nameData
;
110 SecKeychainAttribute attrs
[3];
111 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
112 attrs
[0].length
= sizeof(UInt32
);
113 attrs
[0].data
= (void *)&recType
;
114 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
115 attrs
[1].length
= (UInt32
)itemID
.Length
;
116 attrs
[1].data
= itemID
.Data
;
117 attrs
[2].tag
= kExtendedAttrAttributeNameAttr
;
118 attrs
[2].length
= (UInt32
)nameCData
.Length
;
119 attrs
[2].data
= nameCData
.Data
;
120 SecKeychainAttributeList attrList
= {3, attrs
};
122 StorageManager::KeychainList kcList
;
123 kcList
.push_back(inItem
->keychain());
125 KCCursor
cursor(kcList
, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
127 return cursor
->next(foundItem
);
129 catch(const CssmError
&err
) {
130 if(err
.error
== CSSMERR_DL_INVALID_RECORDTYPE
) {
131 /* this keychain not set up for extended attributes yet */
140 OSStatus
SecKeychainItemSetExtendedAttribute(
141 SecKeychainItemRef itemRef
,
142 CFStringRef attrName
,
143 CFDataRef attrValue
) /* NULL means delete the attribute */
146 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
150 if((itemRef
== NULL
) || (attrName
== NULL
)) {
154 /* is there already a matching ExtendedAttribute item? */
156 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
157 if(attrValue
== NULL
) {
158 /* caller asking us to delete existing record */
160 return errSecNoSuchAttr
;
162 foundItem
->keychain()->deleteItem(foundItem
);
163 return errSecSuccess
;
166 CSSM_DATA attrCValue
= {CFDataGetLength(attrValue
), (uint8
*)CFDataGetBytePtr(attrValue
)};
169 /* update existing extended attribute record */
170 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
171 foundItem
->setAttribute(attrInfo
, attrCValue
);
175 /* create a new one, add it to the same keychain as itemRef */
176 Item inItem
= ItemImpl::required(itemRef
);
177 CssmAutoData
nameData(Allocator::standard());
178 cfStringToData(attrName
, nameData
);
179 CssmData nameCData
= nameData
;
180 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
181 inItem
->recordType(), inItem
->itemID(), nameCData
,
182 CssmData::overlay(attrCValue
)));
183 Item
outItem(extAttr
);
184 inItem
->keychain()->add(outItem
);
190 OSStatus
SecKeychainItemCopyExtendedAttribute(
191 SecKeychainItemRef itemRef
,
192 CFStringRef attrName
,
193 CFDataRef
*attrValue
) /* RETURNED */
196 //%%% This needs to detect SecCertificateRef items
200 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
205 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
206 return errSecNoSuchAttr
;
210 * Found it - its kExtendedAttrAttributeValueAttr value is what the
211 * caller is looking for.
212 * We'd like to use getAttribute() here, but that requires that we know
213 * the size of the attribute before hand...
215 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
217 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
218 SecKeychainAttributeList
*attrList
= NULL
;
219 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
220 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
221 /* should never happen... */
222 MacOSError::throwMe(errSecNoSuchAttr
);
224 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
225 attrList
->attr
->length
);
226 ItemImpl::freeAttributesAndData(attrList
, NULL
);
230 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
231 SecKeychainItemRef itemRef
,
232 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
233 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
237 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
241 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
245 isItemRefCapable(itemRef
);
248 * Get the info about the extended attribute to look up:
250 * -- ItemID (i.e., PrimaryKey blob)
253 Item inItem
= ItemImpl::required(itemRef
);
254 const CssmData
&itemID
= inItem
->itemID();
255 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
256 if(!inItem
->keychain()) {
257 /* item must reside on a keychain */
258 MacOSError::throwMe(errSecNoSuchAttr
);
261 SecKeychainAttribute attrs
[2];
262 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
263 attrs
[0].length
= sizeof(UInt32
);
264 attrs
[0].data
= (void *)&recType
;
265 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
266 attrs
[1].length
= (UInt32
)itemID
.Length
;
267 attrs
[1].data
= itemID
.Data
;
268 SecKeychainAttributeList attrList
= {2, attrs
};
270 StorageManager::KeychainList kcList
;
271 kcList
.push_back(inItem
->keychain());
273 CFMutableArrayRef outNames
= NULL
;
274 CFMutableArrayRef outValues
= NULL
;
275 OSStatus ourRtn
= errSecSuccess
;
277 KCCursor
cursor(kcList
, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
282 gotOne
= cursor
->next(foundItem
);
292 * Found one - return its kExtendedAttrAttributeNameAttr and
293 * (optionally) kExtendedAttrAttributeValueAttr attribute values
296 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
297 UInt32 formats
[2] = {0};
298 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
299 SecKeychainAttributeList
*attrList
= NULL
;
300 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
301 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
302 /* should never happen... */
303 ourRtn
= errSecNoSuchAttr
;
306 if(outNames
== NULL
) {
307 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
309 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
310 /* this one's optional */
311 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
315 * I don't see how we can assume that the order of the returned
316 * attributes is the same as the order of the tags we specified
318 for(unsigned dex
=0; dex
<2; dex
++) {
319 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
320 CFDataRef cfd
= NULL
;
321 CFStringRef cfs
= NULL
;
323 case kExtendedAttrAttributeNameAttr
:
324 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
326 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
327 * this should always work... */
328 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
329 CFArrayAppendValue(outNames
, cfs
);
333 case kExtendedAttrAttributeValueAttr
:
334 if(outValues
== NULL
) {
337 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
338 CFArrayAppendValue(outValues
, cfd
);
342 /* should never happen, right? */
343 MacOSError::throwMe(errSecInternalComponent
);
346 ItemImpl::freeAttributesAndData(attrList
, NULL
);
347 } /* main loop fetching matching Extended Attr records */
354 CFRelease(outValues
);
356 MacOSError::throwMe(ourRtn
);
359 if(outNames
== NULL
) {
360 /* no extended attributes found */
361 return errSecNoSuchAttr
;
363 *attrNames
= outNames
;
365 *attrValues
= outValues
;