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