]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 | ||
6b200bc3 | 24 | #include <security_utilities/casts.h> |
b1ab9ed8 A |
25 | #include "SecKeychainItemExtendedAttributes.h" |
26 | #include "SecKeychainItemPriv.h" | |
27 | #include "ExtendedAttribute.h" | |
28 | #include "SecBridge.h" | |
29 | #include "StorageManager.h" | |
30 | #include "KCCursor.h" | |
866f8763 | 31 | #include <os/activity.h> |
b1ab9ed8 | 32 | |
d64be36e | 33 | #include "LegacyAPICounts.h" |
b1ab9ed8 | 34 | |
fa7225c8 A |
35 | extern "C" Boolean SecKeyIsCDSAKey(SecKeyRef ref); |
36 | ||
b1ab9ed8 A |
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) || | |
fa7225c8 | 47 | (id == SecKeyGetTypeID() && SecKeyIsCDSAKey((SecKeyRef)itemRef))) { |
b1ab9ed8 A |
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!? */ | |
427c49bc | 63 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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; | |
427c49bc | 105 | attrs[1].length = (UInt32)itemID.Length; |
b1ab9ed8 A |
106 | attrs[1].data = itemID.Data; |
107 | attrs[2].tag = kExtendedAttrAttributeNameAttr; | |
427c49bc | 108 | attrs[2].length = (UInt32)nameCData.Length; |
b1ab9ed8 A |
109 | attrs[2].data = nameCData.Data; |
110 | SecKeychainAttributeList attrList = {3, attrs}; | |
111 | ||
112 | StorageManager::KeychainList kcList; | |
113 | kcList.push_back(inItem->keychain()); | |
114 | ||
6b200bc3 | 115 | KCCursor cursor(kcList, (SecItemClass) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList); |
b1ab9ed8 A |
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 | { | |
fa7225c8 A |
135 | // <rdar://25635468> |
136 | //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated | |
137 | ||
b1ab9ed8 | 138 | BEGIN_SECAPI |
866f8763 A |
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); | |
b1ab9ed8 A |
142 | |
143 | if((itemRef == NULL) || (attrName == NULL)) { | |
427c49bc | 144 | return errSecParam; |
b1ab9ed8 A |
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); | |
427c49bc | 156 | return errSecSuccess; |
b1ab9ed8 A |
157 | } |
158 | ||
6b200bc3 | 159 | CSSM_DATA attrCValue = {int_cast<CFIndex, CSSM_SIZE>(CFDataGetLength(attrValue)), (uint8 *)CFDataGetBytePtr(attrValue)}; |
b1ab9ed8 A |
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 | { | |
fa7225c8 A |
188 | // <rdar://25635468> |
189 | //%%% This needs to detect SecCertificateRef items | |
190 | ||
b1ab9ed8 | 191 | BEGIN_SECAPI |
866f8763 A |
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); | |
b1ab9ed8 A |
195 | |
196 | if((itemRef == NULL) || (attrName == NULL) || (attrValue == NULL)) { | |
427c49bc | 197 | return errSecParam; |
b1ab9ed8 A |
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 | { | |
fa7225c8 A |
232 | // <rdar://25635468> |
233 | //%%% This needs to detect SecCertificateRef items, and when it does, SecKeychainItemDelete must be updated | |
234 | ||
b1ab9ed8 | 235 | BEGIN_SECAPI |
866f8763 A |
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); | |
b1ab9ed8 A |
239 | |
240 | if((itemRef == NULL) || (attrNames == NULL)) { | |
427c49bc | 241 | return errSecParam; |
b1ab9ed8 A |
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; | |
427c49bc | 265 | attrs[1].length = (UInt32)itemID.Length; |
b1ab9ed8 A |
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; | |
427c49bc | 274 | OSStatus ourRtn = errSecSuccess; |
b1ab9ed8 | 275 | |
6b200bc3 | 276 | KCCursor cursor(kcList, (SecItemClass) CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList); |
b1ab9ed8 A |
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? */ | |
427c49bc | 342 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
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 | } |