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