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 */
145 if((itemRef
== NULL
) || (attrName
== NULL
)) {
149 /* is there already a matching ExtendedAttribute item? */
151 bool haveMatch
= lookupExtendedAttr(itemRef
, attrName
, foundItem
);
152 if(attrValue
== NULL
) {
153 /* caller asking us to delete existing record */
155 return errSecNoSuchAttr
;
157 foundItem
->keychain()->deleteItem(foundItem
);
158 return errSecSuccess
;
161 CSSM_DATA attrCValue
= {CFDataGetLength(attrValue
), (uint8
*)CFDataGetBytePtr(attrValue
)};
164 /* update existing extended attribute record */
165 CssmDbAttributeInfo
attrInfo(kExtendedAttrAttributeValueAttr
, CSSM_DB_ATTRIBUTE_FORMAT_BLOB
);
166 foundItem
->setAttribute(attrInfo
, attrCValue
);
170 /* create a new one, add it to the same keychain as itemRef */
171 Item inItem
= ItemImpl::required(itemRef
);
172 CssmAutoData
nameData(Allocator::standard());
173 cfStringToData(attrName
, nameData
);
174 CssmData nameCData
= nameData
;
175 SecPointer
<ExtendedAttribute
> extAttr(new ExtendedAttribute(
176 inItem
->recordType(), inItem
->itemID(), nameCData
,
177 CssmData::overlay(attrCValue
)));
178 Item
outItem(extAttr
);
179 inItem
->keychain()->add(outItem
);
185 OSStatus
SecKeychainItemCopyExtendedAttribute(
186 SecKeychainItemRef itemRef
,
187 CFStringRef attrName
,
188 CFDataRef
*attrValue
) /* RETURNED */
192 if((itemRef
== NULL
) || (attrName
== NULL
) || (attrValue
== NULL
)) {
197 if(!lookupExtendedAttr(itemRef
, attrName
, foundItem
)) {
198 return errSecNoSuchAttr
;
202 * Found it - its kExtendedAttrAttributeValueAttr value is what the
203 * caller is looking for.
204 * We'd like to use getAttribute() here, but that requires that we know
205 * the size of the attribute before hand...
207 UInt32 tag
= kExtendedAttrAttributeValueAttr
;
209 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
210 SecKeychainAttributeList
*attrList
= NULL
;
211 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
212 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
213 /* should never happen... */
214 MacOSError::throwMe(errSecNoSuchAttr
);
216 *attrValue
= CFDataCreate(NULL
, (const UInt8
*)attrList
->attr
->data
,
217 attrList
->attr
->length
);
218 ItemImpl::freeAttributesAndData(attrList
, NULL
);
222 OSStatus
SecKeychainItemCopyAllExtendedAttributes(
223 SecKeychainItemRef itemRef
,
224 CFArrayRef
*attrNames
, /* RETURNED, each element is a CFStringRef */
225 CFArrayRef
*attrValues
) /* optional, RETURNED, each element is a
230 if((itemRef
== NULL
) || (attrNames
== NULL
)) {
234 isItemRefCapable(itemRef
);
237 * Get the info about the extended attribute to look up:
239 * -- ItemID (i.e., PrimaryKey blob)
242 Item inItem
= ItemImpl::required(itemRef
);
243 const CssmData
&itemID
= inItem
->itemID();
244 CSSM_DB_RECORDTYPE recType
= inItem
->recordType();
245 if(!inItem
->keychain()) {
246 /* item must reside on a keychain */
247 MacOSError::throwMe(errSecNoSuchAttr
);
250 SecKeychainAttribute attrs
[2];
251 attrs
[0].tag
= kExtendedAttrRecordTypeAttr
;
252 attrs
[0].length
= sizeof(UInt32
);
253 attrs
[0].data
= (void *)&recType
;
254 attrs
[1].tag
= kExtendedAttrItemIDAttr
;
255 attrs
[1].length
= (UInt32
)itemID
.Length
;
256 attrs
[1].data
= itemID
.Data
;
257 SecKeychainAttributeList attrList
= {2, attrs
};
259 StorageManager::KeychainList kcList
;
260 kcList
.push_back(inItem
->keychain());
262 CFMutableArrayRef outNames
= NULL
;
263 CFMutableArrayRef outValues
= NULL
;
264 OSStatus ourRtn
= errSecSuccess
;
266 KCCursor
cursor(kcList
, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
, &attrList
);
271 gotOne
= cursor
->next(foundItem
);
281 * Found one - return its kExtendedAttrAttributeNameAttr and
282 * (optionally) kExtendedAttrAttributeValueAttr attribute values
285 UInt32 tags
[2] = { kExtendedAttrAttributeNameAttr
, kExtendedAttrAttributeValueAttr
};
286 UInt32 formats
[2] = {0};
287 SecKeychainAttributeInfo attrInfo
= {2, tags
, formats
};
288 SecKeychainAttributeList
*attrList
= NULL
;
289 foundItem
->getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
290 if((attrList
== NULL
) || (attrList
->count
!= 2)) {
291 /* should never happen... */
292 ourRtn
= errSecNoSuchAttr
;
295 if(outNames
== NULL
) {
296 outNames
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
298 if((outValues
== NULL
) && (attrValues
!= NULL
)) {
299 /* this one's optional */
300 outValues
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
304 * I don't see how we can assume that the order of the returned
305 * attributes is the same as the order of the tags we specified
307 for(unsigned dex
=0; dex
<2; dex
++) {
308 SecKeychainAttribute
*attr
= &attrList
->attr
[dex
];
309 CFDataRef cfd
= NULL
;
310 CFStringRef cfs
= NULL
;
312 case kExtendedAttrAttributeNameAttr
:
313 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
315 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
316 * this should always work... */
317 cfs
= CFStringCreateFromExternalRepresentation(NULL
, cfd
, kCFStringEncodingUTF8
);
318 CFArrayAppendValue(outNames
, cfs
);
322 case kExtendedAttrAttributeValueAttr
:
323 if(outValues
== NULL
) {
326 cfd
= CFDataCreate(NULL
, (const UInt8
*)attr
->data
, attr
->length
);
327 CFArrayAppendValue(outValues
, cfd
);
331 /* should never happen, right? */
332 MacOSError::throwMe(errSecInternalComponent
);
335 ItemImpl::freeAttributesAndData(attrList
, NULL
);
336 } /* main loop fetching matching Extended Attr records */
343 CFRelease(outValues
);
345 MacOSError::throwMe(ourRtn
);
348 if(outNames
== NULL
) {
349 /* no extended attributes found */
350 return errSecNoSuchAttr
;
352 *attrNames
= outNames
;
354 *attrValues
= outValues
;