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