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
)
46 * Determine if incoming itemRef can be considered for
47 * this mechanism; throw if not.
49 static void isItemRefCapable(
50 SecKeychainItemRef itemRef
)
52 CFTypeID id
= CFGetTypeID(itemRef
);
53 if((id
== gTypes().ItemImpl
.typeID
) ||
54 (id
== gTypes().Certificate
.typeID
) ||
55 (id
== gTypes().KeyItem
.typeID
)) {
59 MacOSError::throwMe(errSecNoSuchAttr
);
63 static void cfStringToData(
67 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
, cfStr
,
68 kCFStringEncodingUTF8
, 0);
70 /* can't convert to UTF8!? */
71 MacOSError::throwMe(errSecParam
);
73 dst
.copy(CFDataGetBytePtr(cfData
), CFDataGetLength(cfData
));
78 * Look up an ExtendedAttribute item associated with specified item.
79 * Returns true if found, false if not.
80 * Throws errSecNoSuchAttr if item does not reside on a keychain.
82 static bool lookupExtendedAttr(
83 SecKeychainItemRef itemRef
,
87 isItemRefCapable(itemRef
);
90 * Get the info about the extended attribute to look up:
92 * -- ItemID (i.e., PrimaryKey blob)
96 Item inItem
= ItemImpl::required(itemRef
);
97 const CssmData
&itemID
= inItem
->itemID();
98 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
99 if(!inItem
->keychain()) {
100 /* item must reside on a keychain */
101 MacOSError::throwMe(errSecNoSuchAttr
);
104 CssmAutoData
nameData(Allocator::standard());
105 cfStringToData(attrName
, nameData
);
106 CssmData nameCData
= nameData
;
108 SecKeychainAttribute attrs
[3];
109 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
110 attrs
[0].length
= sizeof(UInt32
);
111 attrs
[0].data
= (void *)&recType
;
112 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
113 attrs
[1].length
= (UInt32
)itemID
.Length
;
114 attrs
[1].data
= itemID
.Data
;
115 attrs
[2].tag
= kExtendedAttrAttributeNameAttr
;
116 attrs
[2].length
= (UInt32
)nameCData
.Length
;
117 attrs
[2].data
= nameCData
.Data
;
118 SecKeychainAttributeList attrList
= {3, attrs
};
120 StorageManager::KeychainList kcList
;
121 kcList
.push_back(inItem
->keychain());
123 KCCursor
cursor(kcList
, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
125 return cursor
->next(foundItem
);
127 catch(const CssmError
&err
) {
128 if(err
.error
== CSSMERR_DL_INVALID_RECORDTYPE
) {
129 /* this keychain not set up for extended attributes yet */
138 OSStatus
SecKeychainItemSetExtendedAttribute(
139 SecKeychainItemRef itemRef
,
140 CFStringRef attrName
,
141 CFDataRef attrValue
) /* NULL means delete the attribute */
144 #warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
148 if((itemRef
== NULL
) || (attrName
== NULL
)) {
152 /* is there already a matching ExtendedAttribute item? */
154 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
155 if(attrValue
== NULL
) {
156 /* caller asking us to delete existing record */
158 return errSecNoSuchAttr
;
160 foundItem
->keychain()->deleteItem(foundItem
);
161 return errSecSuccess
;
164 CSSM_DATA attrCValue
= {CFDataGetLength(attrValue
), (uint8
*)CFDataGetBytePtr(attrValue
)};
167 /* update existing extended attribute record */
168 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
169 foundItem
->setAttribute(attrInfo
, attrCValue
);
173 /* create a new one, add it to the same keychain as itemRef */
174 Item inItem
= ItemImpl::required(itemRef
);
175 CssmAutoData
nameData(Allocator::standard());
176 cfStringToData(attrName
, nameData
);
177 CssmData nameCData
= nameData
;
178 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
179 inItem
->recordType(), inItem
->itemID(), nameCData
,
180 CssmData::overlay(attrCValue
)));
181 Item
outItem(extAttr
);
182 inItem
->keychain()->add(outItem
);
188 OSStatus
SecKeychainItemCopyExtendedAttribute(
189 SecKeychainItemRef itemRef
,
190 CFStringRef attrName
,
191 CFDataRef
*attrValue
) /* RETURNED */
194 #warning This needs to detect SecCertificateRef items
198 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
203 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
204 return errSecNoSuchAttr
;
208 * Found it - its kExtendedAttrAttributeValueAttr value is what the
209 * caller is looking for.
210 * We'd like to use getAttribute() here, but that requires that we know
211 * the size of the attribute before hand...
213 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
215 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
216 SecKeychainAttributeList
*attrList
= NULL
;
217 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
218 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
219 /* should never happen... */
220 MacOSError::throwMe(errSecNoSuchAttr
);
222 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
223 attrList
->attr
->length
);
224 ItemImpl::freeAttributesAndData(attrList
, NULL
);
228 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
229 SecKeychainItemRef itemRef
,
230 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
231 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
235 #warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
239 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
243 isItemRefCapable(itemRef
);
246 * Get the info about the extended attribute to look up:
248 * -- ItemID (i.e., PrimaryKey blob)
251 Item inItem
= ItemImpl::required(itemRef
);
252 const CssmData
&itemID
= inItem
->itemID();
253 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
254 if(!inItem
->keychain()) {
255 /* item must reside on a keychain */
256 MacOSError::throwMe(errSecNoSuchAttr
);
259 SecKeychainAttribute attrs
[2];
260 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
261 attrs
[0].length
= sizeof(UInt32
);
262 attrs
[0].data
= (void *)&recType
;
263 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
264 attrs
[1].length
= (UInt32
)itemID
.Length
;
265 attrs
[1].data
= itemID
.Data
;
266 SecKeychainAttributeList attrList
= {2, attrs
};
268 StorageManager::KeychainList kcList
;
269 kcList
.push_back(inItem
->keychain());
271 CFMutableArrayRef outNames
= NULL
;
272 CFMutableArrayRef outValues
= NULL
;
273 OSStatus ourRtn
= errSecSuccess
;
275 KCCursor
cursor(kcList
, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
280 gotOne
= cursor
->next(foundItem
);
290 * Found one - return its kExtendedAttrAttributeNameAttr and
291 * (optionally) kExtendedAttrAttributeValueAttr attribute values
294 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
295 UInt32 formats
[2] = {0};
296 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
297 SecKeychainAttributeList
*attrList
= NULL
;
298 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
299 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
300 /* should never happen... */
301 ourRtn
= errSecNoSuchAttr
;
304 if(outNames
== NULL
) {
305 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
307 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
308 /* this one's optional */
309 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
313 * I don't see how we can assume that the order of the returned
314 * attributes is the same as the order of the tags we specified
316 for(unsigned dex
=0; dex
<2; dex
++) {
317 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
318 CFDataRef cfd
= NULL
;
319 CFStringRef cfs
= NULL
;
321 case kExtendedAttrAttributeNameAttr
:
322 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
324 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
325 * this should always work... */
326 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
327 CFArrayAppendValue(outNames
, cfs
);
331 case kExtendedAttrAttributeValueAttr
:
332 if(outValues
== NULL
) {
335 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
336 CFArrayAppendValue(outValues
, cfd
);
340 /* should never happen, right? */
341 MacOSError::throwMe(errSecInternalComponent
);
344 ItemImpl::freeAttributesAndData(attrList
, NULL
);
345 } /* main loop fetching matching Extended Attr records */
352 CFRelease(outValues
);
354 MacOSError::throwMe(ourRtn
);
357 if(outNames
== NULL
) {
358 /* no extended attributes found */
359 return errSecNoSuchAttr
;
361 *attrNames
= outNames
;
363 *attrValues
= outValues
;