]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecKeychainItemExtendedAttributes.cpp
Security-57336.1.9.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 "SecKeychainItemExtendedAttributes.h"
25 #include "SecKeychainItemPriv.h"
26 #include "ExtendedAttribute.h"
27 #include "SecBridge.h"
28 #include "StorageManager.h"
29 #include "KCCursor.h"
30
31 /* I'm not sure we need this */
32 #if 0
33 CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void);
34
35 static CFTypeID SecKeychainItemExtendedAttributesGetTypeID(void)
36 {
37 BEGIN_SECAPI
38
39 return gTypes().ExtendedAttribute.typeID;
40
41 END_SECAPI1(_kCFRuntimeNotATypeID)
42 }
43 #endif
44
45 /*
46 * Determine if incoming itemRef can be considered for
47 * this mechanism; throw if not.
48 */
49 static void isItemRefCapable(
50 SecKeychainItemRef itemRef)
51 {
52 CFTypeID id = CFGetTypeID(itemRef);
53 if((id == gTypes().ItemImpl.typeID) ||
54 (id == gTypes().Certificate.typeID) ||
55 (id == gTypes().KeyItem.typeID)) {
56 return;
57 }
58 else {
59 MacOSError::throwMe(errSecNoSuchAttr);
60 }
61 }
62
63 static void cfStringToData(
64 CFStringRef cfStr,
65 CssmOwnedData &dst)
66 {
67 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, cfStr,
68 kCFStringEncodingUTF8, 0);
69 if(cfData == NULL) {
70 /* can't convert to UTF8!? */
71 MacOSError::throwMe(errSecParam);
72 }
73 dst.copy(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
74 CFRelease(cfData);
75 }
76
77 /*
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.
81 */
82 static bool lookupExtendedAttr(
83 SecKeychainItemRef itemRef,
84 CFStringRef attrName,
85 Item &foundItem)
86 {
87 isItemRefCapable(itemRef);
88
89 /*
90 * Get the info about the extended attribute to look up:
91 * -- RecordType
92 * -- ItemID (i.e., PrimaryKey blob)
93 * -- AttributeName
94 */
95
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);
102 }
103
104 CssmAutoData nameData(Allocator::standard());
105 cfStringToData(attrName, nameData);
106 CssmData nameCData = nameData;
107
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};
119
120 StorageManager::KeychainList kcList;
121 kcList.push_back(inItem->keychain());
122
123 KCCursor cursor(kcList, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
124 try {
125 return cursor->next(foundItem);
126 }
127 catch(const CssmError &err) {
128 if(err.error == CSSMERR_DL_INVALID_RECORDTYPE) {
129 /* this keychain not set up for extended attributes yet */
130 return false;
131 }
132 else {
133 throw;
134 }
135 }
136 }
137
138 OSStatus SecKeychainItemSetExtendedAttribute(
139 SecKeychainItemRef itemRef,
140 CFStringRef attrName,
141 CFDataRef attrValue) /* NULL means delete the attribute */
142 {
143 #if SECTRUST_OSX
144 #warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
145 #endif
146 BEGIN_SECAPI
147
148 if((itemRef == NULL) || (attrName == NULL)) {
149 return errSecParam;
150 }
151
152 /* is there already a matching ExtendedAttribute item? */
153 Item foundItem;
154 bool haveMatch = lookupExtendedAttr(itemRef, attrName, foundItem);
155 if(attrValue == NULL) {
156 /* caller asking us to delete existing record */
157 if(!foundItem) {
158 return errSecNoSuchAttr;
159 }
160 foundItem->keychain()->deleteItem(foundItem);
161 return errSecSuccess;
162 }
163
164 CSSM_DATA attrCValue = {CFDataGetLength(attrValue), (uint8 *)CFDataGetBytePtr(attrValue)};
165
166 if(haveMatch) {
167 /* update existing extended attribute record */
168 CssmDbAttributeInfo attrInfo(kExtendedAttrAttributeValueAttr, CSSM_DB_ATTRIBUTE_FORMAT_BLOB);
169 foundItem->setAttribute(attrInfo, attrCValue);
170 foundItem->update();
171 }
172 else {
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);
183 }
184
185 END_SECAPI
186 }
187
188 OSStatus SecKeychainItemCopyExtendedAttribute(
189 SecKeychainItemRef itemRef,
190 CFStringRef attrName,
191 CFDataRef *attrValue) /* RETURNED */
192 {
193 #if SECTRUST_OSX
194 #warning This needs to detect SecCertificateRef items
195 #endif
196 BEGIN_SECAPI
197
198 if((itemRef == NULL) || (attrName == NULL) || (attrValue == NULL)) {
199 return errSecParam;
200 }
201
202 Item foundItem;
203 if(!lookupExtendedAttr(itemRef, attrName, foundItem)) {
204 return errSecNoSuchAttr;
205 }
206
207 /*
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...
212 */
213 UInt32 tag = kExtendedAttrAttributeValueAttr;
214 UInt32 format = 0;
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);
221 }
222 *attrValue = CFDataCreate(NULL, (const UInt8 *)attrList->attr->data,
223 attrList->attr->length);
224 ItemImpl::freeAttributesAndData(attrList, NULL);
225 END_SECAPI
226 }
227
228 OSStatus SecKeychainItemCopyAllExtendedAttributes(
229 SecKeychainItemRef itemRef,
230 CFArrayRef *attrNames, /* RETURNED, each element is a CFStringRef */
231 CFArrayRef *attrValues) /* optional, RETURNED, each element is a
232 * CFDataRef */
233 {
234 #if SECTRUST_OSX
235 #warning This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated
236 #endif
237 BEGIN_SECAPI
238
239 if((itemRef == NULL) || (attrNames == NULL)) {
240 return errSecParam;
241 }
242
243 isItemRefCapable(itemRef);
244
245 /*
246 * Get the info about the extended attribute to look up:
247 * -- RecordType
248 * -- ItemID (i.e., PrimaryKey blob)
249 */
250
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);
257 }
258
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};
267
268 StorageManager::KeychainList kcList;
269 kcList.push_back(inItem->keychain());
270
271 CFMutableArrayRef outNames = NULL;
272 CFMutableArrayRef outValues = NULL;
273 OSStatus ourRtn = errSecSuccess;
274
275 KCCursor cursor(kcList, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList);
276 for(;;) {
277 bool gotOne = false;
278 Item foundItem;
279 try {
280 gotOne = cursor->next(foundItem);
281 }
282 catch(...) {
283 break;
284 }
285 if(!gotOne) {
286 break;
287 }
288
289 /*
290 * Found one - return its kExtendedAttrAttributeNameAttr and
291 * (optionally) kExtendedAttrAttributeValueAttr attribute values
292 * to caller.
293 */
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;
302 break;
303 }
304 if(outNames == NULL) {
305 outNames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
306 }
307 if((outValues == NULL) && (attrValues != NULL)) {
308 /* this one's optional */
309 outValues = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
310 }
311
312 /*
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
315 */
316 for(unsigned dex=0; dex<2; dex++) {
317 SecKeychainAttribute *attr = &attrList->attr[dex];
318 CFDataRef cfd = NULL;
319 CFStringRef cfs = NULL;
320 switch(attr->tag) {
321 case kExtendedAttrAttributeNameAttr:
322 cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
323
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);
328 CFRelease(cfd);
329 CFRelease(cfs);
330 break;
331 case kExtendedAttrAttributeValueAttr:
332 if(outValues == NULL) {
333 break;
334 }
335 cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length);
336 CFArrayAppendValue(outValues, cfd);
337 CFRelease(cfd);
338 break;
339 default:
340 /* should never happen, right? */
341 MacOSError::throwMe(errSecInternalComponent);
342 }
343 }
344 ItemImpl::freeAttributesAndData(attrList, NULL);
345 } /* main loop fetching matching Extended Attr records */
346
347 if(ourRtn) {
348 if(outNames) {
349 CFRelease(outNames);
350 }
351 if(outValues) {
352 CFRelease(outValues);
353 }
354 MacOSError::throwMe(ourRtn);
355 }
356
357 if(outNames == NULL) {
358 /* no extended attributes found */
359 return errSecNoSuchAttr;
360 }
361 *attrNames = outNames;
362 if(outValues) {
363 *attrValues = outValues;
364 }
365
366 END_SECAPI
367 }