]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecKeychainItemExtendedAttributes.cpp
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecKeychainItemExtendedAttributes.cpp
1 /*
2 * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
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"
30 #include "KCCursor.h"
31 #include <os/activity.h>
32
33 /* I'm not sure we need this */
34 #if 0
35 CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void);
36
37 static CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void)
38 {
39 BEGIN_SECAPI
40
41 return gTypes().ExtendedAttribute.typeID;
42
43 END_SECAPI1(_kCFRuntimeNotATypeID)
44 }
45 #endif
46
47 extern "C" Boolean SecKeyIsCDSAKey(SecKeyRef ref);
48
49 /*
50 * Determine if incoming itemRef can be considered for
51 * this mechanism; throw if not.
52 */
53 static void isItemRefCapable(
54 SecKeychainItemRef itemRef)
55 {
56 CFTypeID id = CFGetTypeID(itemRef);
57 if((id == gTypes().ItemImpl.typeID) ||
58 (id == gTypes().Certificate.typeID) ||
59 (id == SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef)itemRef))) {
60 return;
61 }
62 else {
63 MacOSError::throwMe(errSecNoSuchAttr);
64 }
65 }
66
67 static void cfStringToData(
68 CFStringRef cfStr,
69 CssmOwnedData &dst)
70 {
71 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, cfStr,
72 kCFStringEncodingUTF8, 0);
73 if(cfData == NULL) {
74 /* can't convert to UTF8!? */
75 MacOSError::throwMe(errSecParam);
76 }
77 dst.copy(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
78 CFRelease(cfData);
79 }
80
81 /*
82 * Look up an ExtendedAttribute item associated with specified item.
83 * Returns true if found, false if not.
84 * Throws errSecNoSuchAttr if item does not reside on a keychain.
85 */
86 static bool lookupExtendedAttr(
87 SecKeychainItemRef itemRef,
88 CFStringRef attrName,
89 Item &foundItem)
90 {
91 isItemRefCapable(itemRef);
92
93 /*
94 * Get the info about the extended attribute to look up:
95 * -- RecordType
96 * -- ItemID (i.e., PrimaryKey blob)
97 * -- AttributeName
98 */
99
100 Item inItem = ItemImpl::required(itemRef);
101 const CssmData &itemID = inItem->itemID();
102 CSSM_DB_RECORDTYPE recType = inItem->recordType();
103 if(!inItem->keychain()) {
104 /* item must reside on a keychain */
105 MacOSError::throwMe(errSecNoSuchAttr);
106 }
107
108 CssmAutoData nameData(Allocator::standard());
109 cfStringToData(attrName, nameData);
110 CssmData nameCData = nameData;
111
112 SecKeychainAttribute attrs[3];
113 attrs[0].tag = kExtendedAttrRecordTypeAttr;
114 attrs[0].length = sizeof(UInt32);
115 attrs[0].data = (void *)&recType;
116 attrs[1].tag = kExtendedAttrItemIDAttr;
117 attrs[1].length = (UInt32)itemID.Length;
118 attrs[1].data = itemID.Data;
119 attrs[2].tag = kExtendedAttrAttributeNameAttr;
120 attrs[2].length = (UInt32)nameCData.Length;
121 attrs[2].data = nameCData.Data;
122 SecKeychainAttributeList attrList = {3, attrs};
123
124 StorageManager::KeychainList kcList;
125 kcList.push_back(inItem->keychain());
126
127 KCCursor cursor(kcList, (SecItemClass) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
128 try {
129 return cursor->next(foundItem);
130 }
131 catch(const CssmError &err) {
132 if(err.error == CSSMERR_DL_INVALID_RECORDTYPE) {
133 /* this keychain not set up for extended attributes yet */
134 return false;
135 }
136 else {
137 throw;
138 }
139 }
140 }
141
142 OSStatus SecKeychainItemSetExtendedAttribute(
143 SecKeychainItemRef itemRef,
144 CFStringRef attrName,
145 CFDataRef attrValue) /* NULL means delete the attribute */
146 {
147 // <rdar://25635468>
148 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
149
150 BEGIN_SECAPI
151 os_activity_t activity = os_activity_create("SecKeychainItemSetExtendedAttribute", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
152 os_activity_scope(activity);
153 os_release(activity);
154
155 if((itemRef == NULL) || (attrName == NULL)) {
156 return errSecParam;
157 }
158
159 /* is there already a matching ExtendedAttribute item? */
160 Item foundItem;
161 bool haveMatch = lookupExtendedAttr(itemRef, attrName, foundItem);
162 if(attrValue == NULL) {
163 /* caller asking us to delete existing record */
164 if(!foundItem) {
165 return errSecNoSuchAttr;
166 }
167 foundItem->keychain()->deleteItem(foundItem);
168 return errSecSuccess;
169 }
170
171 CSSM_DATA attrCValue = {int_cast<CFIndex, CSSM_SIZE>(CFDataGetLength(attrValue)), (uint8 *)CFDataGetBytePtr(attrValue)};
172
173 if(haveMatch) {
174 /* update existing extended attribute record */
175 CssmDbAttributeInfo attrInfo(kExtendedAttrAttributeValueAttr, CSSM_DB_ATTRIBUTE_FORMAT_BLOB);
176 foundItem->setAttribute(attrInfo, attrCValue);
177 foundItem->update();
178 }
179 else {
180 /* create a new one, add it to the same keychain as itemRef */
181 Item inItem = ItemImpl::required(itemRef);
182 CssmAutoData nameData(Allocator::standard());
183 cfStringToData(attrName, nameData);
184 CssmData nameCData = nameData;
185 SecPointer<ExtendedAttribute> extAttr(new ExtendedAttribute(
186 inItem->recordType(), inItem->itemID(), nameCData,
187 CssmData::overlay(attrCValue)));
188 Item outItem(extAttr);
189 inItem->keychain()->add(outItem);
190 }
191
192 END_SECAPI
193 }
194
195 OSStatus SecKeychainItemCopyExtendedAttribute(
196 SecKeychainItemRef itemRef,
197 CFStringRef attrName,
198 CFDataRef *attrValue) /* RETURNED */
199 {
200 // <rdar://25635468>
201 //%%% This needs to detect SecCertificateRef items
202
203 BEGIN_SECAPI
204 os_activity_t activity = os_activity_create("SecKeychainItemCopyExtendedAttribute", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
205 os_activity_scope(activity);
206 os_release(activity);
207
208 if((itemRef == NULL) || (attrName == NULL) || (attrValue == NULL)) {
209 return errSecParam;
210 }
211
212 Item foundItem;
213 if(!lookupExtendedAttr(itemRef, attrName, foundItem)) {
214 return errSecNoSuchAttr;
215 }
216
217 /*
218 * Found it - its kExtendedAttrAttributeValueAttr value is what the
219 * caller is looking for.
220 * We'd like to use getAttribute() here, but that requires that we know
221 * the size of the attribute before hand...
222 */
223 UInt32 tag = kExtendedAttrAttributeValueAttr;
224 UInt32 format = 0;
225 SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
226 SecKeychainAttributeList *attrList = NULL;
227 foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
228 if((attrList == NULL) || (attrList->count != 1)) {
229 /* should never happen... */
230 MacOSError::throwMe(errSecNoSuchAttr);
231 }
232 *attrValue = CFDataCreate(NULL, (const UInt8 *)attrList->attr->data,
233 attrList->attr->length);
234 ItemImpl::freeAttributesAndData(attrList, NULL);
235 END_SECAPI
236 }
237
238 OSStatus SecKeychainItemCopyAllExtendedAttributes(
239 SecKeychainItemRef itemRef,
240 CFArrayRef *attrNames, /* RETURNED, each element is a CFStringRef */
241 CFArrayRef *attrValues) /* optional, RETURNED, each element is a
242 * CFDataRef */
243 {
244 // <rdar://25635468>
245 //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
246
247 BEGIN_SECAPI
248 os_activity_t activity = os_activity_create("SecKeychainItemCopyAllExtendedAttributes", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
249 os_activity_scope(activity);
250 os_release(activity);
251
252 if((itemRef == NULL) || (attrNames == NULL)) {
253 return errSecParam;
254 }
255
256 isItemRefCapable(itemRef);
257
258 /*
259 * Get the info about the extended attribute to look up:
260 * -- RecordType
261 * -- ItemID (i.e., PrimaryKey blob)
262 */
263
264 Item inItem = ItemImpl::required(itemRef);
265 const CssmData &itemID = inItem->itemID();
266 CSSM_DB_RECORDTYPE recType = inItem->recordType();
267 if(!inItem->keychain()) {
268 /* item must reside on a keychain */
269 MacOSError::throwMe(errSecNoSuchAttr);
270 }
271
272 SecKeychainAttribute attrs[2];
273 attrs[0].tag = kExtendedAttrRecordTypeAttr;
274 attrs[0].length = sizeof(UInt32);
275 attrs[0].data = (void *)&recType;
276 attrs[1].tag = kExtendedAttrItemIDAttr;
277 attrs[1].length = (UInt32)itemID.Length;
278 attrs[1].data = itemID.Data;
279 SecKeychainAttributeList attrList = {2, attrs};
280
281 StorageManager::KeychainList kcList;
282 kcList.push_back(inItem->keychain());
283
284 CFMutableArrayRef outNames = NULL;
285 CFMutableArrayRef outValues = NULL;
286 OSStatus ourRtn = errSecSuccess;
287
288 KCCursor cursor(kcList, (SecItemClass) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
289 for(;;) {
290 bool gotOne = false;
291 Item foundItem;
292 try {
293 gotOne = cursor->next(foundItem);
294 }
295 catch(...) {
296 break;
297 }
298 if(!gotOne) {
299 break;
300 }
301
302 /*
303 * Found one - return its kExtendedAttrAttributeNameAttr and
304 * (optionally) kExtendedAttrAttributeValueAttr attribute values
305 * to caller.
306 */
307 UInt32 tags[2] = { kExtendedAttrAttributeNameAttr, kExtendedAttrAttributeValueAttr };
308 UInt32 formats[2] = {0};
309 SecKeychainAttributeInfo attrInfo = {2, tags, formats};
310 SecKeychainAttributeList *attrList = NULL;
311 foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
312 if((attrList == NULL) || (attrList->count != 2)) {
313 /* should never happen... */
314 ourRtn = errSecNoSuchAttr;
315 break;
316 }
317 if(outNames == NULL) {
318 outNames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
319 }
320 if((outValues == NULL) && (attrValues != NULL)) {
321 /* this one's optional */
322 outValues = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
323 }
324
325 /*
326 * I don't see how we can assume that the order of the returned
327 * attributes is the same as the order of the tags we specified
328 */
329 for(unsigned dex=0; dex<2; dex++) {
330 SecKeychainAttribute *attr = &attrList->attr[dex];
331 CFDataRef cfd = NULL;
332 CFStringRef cfs = NULL;
333 switch(attr->tag) {
334 case kExtendedAttrAttributeNameAttr:
335 cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
336
337 /* We created this attribute's data via CFStringCreateExternalRepresentation, so
338 * this should always work... */
339 cfs = CFStringCreateFromExternalRepresentation(NULL, cfd, kCFStringEncodingUTF8);
340 CFArrayAppendValue(outNames, cfs);
341 CFRelease(cfd);
342 CFRelease(cfs);
343 break;
344 case kExtendedAttrAttributeValueAttr:
345 if(outValues == NULL) {
346 break;
347 }
348 cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
349 CFArrayAppendValue(outValues, cfd);
350 CFRelease(cfd);
351 break;
352 default:
353 /* should never happen, right? */
354 MacOSError::throwMe(errSecInternalComponent);
355 }
356 }
357 ItemImpl::freeAttributesAndData(attrList, NULL);
358 } /* main loop fetching matching Extended Attr records */
359
360 if(ourRtn) {
361 if(outNames) {
362 CFRelease(outNames);
363 }
364 if(outValues) {
365 CFRelease(outValues);
366 }
367 MacOSError::throwMe(ourRtn);
368 }
369
370 if(outNames == NULL) {
371 /* no extended attributes found */
372 return errSecNoSuchAttr;
373 }
374 *attrNames = outNames;
375 if(outValues) {
376 *attrValues = outValues;
377 }
378
379 END_SECAPI
380 }