]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2006 Apple Computer, 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!? */ | |
427c49bc | 71 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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; | |
427c49bc | 113 | attrs[1].length = (UInt32)itemID.Length; |
b1ab9ed8 A |
114 | attrs[1].data = itemID.Data; |
115 | attrs[2].tag = kExtendedAttrAttributeNameAttr; | |
427c49bc | 116 | attrs[2].length = (UInt32)nameCData.Length; |
b1ab9ed8 A |
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 | BEGIN_SECAPI | |
144 | ||
145 | if((itemRef == NULL) || (attrName == NULL)) { | |
427c49bc | 146 | return errSecParam; |
b1ab9ed8 A |
147 | } |
148 | ||
149 | /* is there already a matching ExtendedAttribute item? */ | |
150 | Item foundItem; | |
151 | bool haveMatch = lookupExtendedAttr(itemRef, attrName, foundItem); | |
152 | if(attrValue == NULL) { | |
153 | /* caller asking us to delete existing record */ | |
154 | if(!foundItem) { | |
155 | return errSecNoSuchAttr; | |
156 | } | |
157 | foundItem->keychain()->deleteItem(foundItem); | |
427c49bc | 158 | return errSecSuccess; |
b1ab9ed8 A |
159 | } |
160 | ||
161 | CSSM_DATA attrCValue = {CFDataGetLength(attrValue), (uint8 *)CFDataGetBytePtr(attrValue)}; | |
162 | ||
163 | if(haveMatch) { | |
164 | /* update existing extended attribute record */ | |
165 | CssmDbAttributeInfo attrInfo(kExtendedAttrAttributeValueAttr, CSSM_DB_ATTRIBUTE_FORMAT_BLOB); | |
166 | foundItem->setAttribute(attrInfo, attrCValue); | |
167 | foundItem->update(); | |
168 | } | |
169 | else { | |
170 | /* create a new one, add it to the same keychain as itemRef */ | |
171 | Item inItem = ItemImpl::required(itemRef); | |
172 | CssmAutoData nameData(Allocator::standard()); | |
173 | cfStringToData(attrName, nameData); | |
174 | CssmData nameCData = nameData; | |
175 | SecPointer<ExtendedAttribute> extAttr(new ExtendedAttribute( | |
176 | inItem->recordType(), inItem->itemID(), nameCData, | |
177 | CssmData::overlay(attrCValue))); | |
178 | Item outItem(extAttr); | |
179 | inItem->keychain()->add(outItem); | |
180 | } | |
181 | ||
182 | END_SECAPI | |
183 | } | |
184 | ||
185 | OSStatus SecKeychainItemCopyExtendedAttribute( | |
186 | SecKeychainItemRef itemRef, | |
187 | CFStringRef attrName, | |
188 | CFDataRef *attrValue) /* RETURNED */ | |
189 | { | |
190 | BEGIN_SECAPI | |
191 | ||
192 | if((itemRef == NULL) || (attrName == NULL) || (attrValue == NULL)) { | |
427c49bc | 193 | return errSecParam; |
b1ab9ed8 A |
194 | } |
195 | ||
196 | Item foundItem; | |
197 | if(!lookupExtendedAttr(itemRef, attrName, foundItem)) { | |
198 | return errSecNoSuchAttr; | |
199 | } | |
200 | ||
201 | /* | |
202 | * Found it - its kExtendedAttrAttributeValueAttr value is what the | |
203 | * caller is looking for. | |
204 | * We'd like to use getAttribute() here, but that requires that we know | |
205 | * the size of the attribute before hand... | |
206 | */ | |
207 | UInt32 tag = kExtendedAttrAttributeValueAttr; | |
208 | UInt32 format = 0; | |
209 | SecKeychainAttributeInfo attrInfo = {1, &tag, &format}; | |
210 | SecKeychainAttributeList *attrList = NULL; | |
211 | foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL); | |
212 | if((attrList == NULL) || (attrList->count != 1)) { | |
213 | /* should never happen... */ | |
214 | MacOSError::throwMe(errSecNoSuchAttr); | |
215 | } | |
216 | *attrValue = CFDataCreate(NULL, (const UInt8 *)attrList->attr->data, | |
217 | attrList->attr->length); | |
218 | ItemImpl::freeAttributesAndData(attrList, NULL); | |
219 | END_SECAPI | |
220 | } | |
221 | ||
222 | OSStatus SecKeychainItemCopyAllExtendedAttributes( | |
223 | SecKeychainItemRef itemRef, | |
224 | CFArrayRef *attrNames, /* RETURNED, each element is a CFStringRef */ | |
225 | CFArrayRef *attrValues) /* optional, RETURNED, each element is a | |
226 | * CFDataRef */ | |
227 | { | |
228 | BEGIN_SECAPI | |
229 | ||
230 | if((itemRef == NULL) || (attrNames == NULL)) { | |
427c49bc | 231 | return errSecParam; |
b1ab9ed8 A |
232 | } |
233 | ||
234 | isItemRefCapable(itemRef); | |
235 | ||
236 | /* | |
237 | * Get the info about the extended attribute to look up: | |
238 | * -- RecordType | |
239 | * -- ItemID (i.e., PrimaryKey blob) | |
240 | */ | |
241 | ||
242 | Item inItem = ItemImpl::required(itemRef); | |
243 | const CssmData &itemID = inItem->itemID(); | |
244 | CSSM_DB_RECORDTYPE recType = inItem->recordType(); | |
245 | if(!inItem->keychain()) { | |
246 | /* item must reside on a keychain */ | |
247 | MacOSError::throwMe(errSecNoSuchAttr); | |
248 | } | |
249 | ||
250 | SecKeychainAttribute attrs[2]; | |
251 | attrs[0].tag = kExtendedAttrRecordTypeAttr; | |
252 | attrs[0].length = sizeof(UInt32); | |
253 | attrs[0].data = (void *)&recType; | |
254 | attrs[1].tag = kExtendedAttrItemIDAttr; | |
427c49bc | 255 | attrs[1].length = (UInt32)itemID.Length; |
b1ab9ed8 A |
256 | attrs[1].data = itemID.Data; |
257 | SecKeychainAttributeList attrList = {2, attrs}; | |
258 | ||
259 | StorageManager::KeychainList kcList; | |
260 | kcList.push_back(inItem->keychain()); | |
261 | ||
262 | CFMutableArrayRef outNames = NULL; | |
263 | CFMutableArrayRef outValues = NULL; | |
427c49bc | 264 | OSStatus ourRtn = errSecSuccess; |
b1ab9ed8 A |
265 | |
266 | KCCursor cursor(kcList, CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE, &attrList); | |
267 | for(;;) { | |
268 | bool gotOne = false; | |
269 | Item foundItem; | |
270 | try { | |
271 | gotOne = cursor->next(foundItem); | |
272 | } | |
273 | catch(...) { | |
274 | break; | |
275 | } | |
276 | if(!gotOne) { | |
277 | break; | |
278 | } | |
279 | ||
280 | /* | |
281 | * Found one - return its kExtendedAttrAttributeNameAttr and | |
282 | * (optionally) kExtendedAttrAttributeValueAttr attribute values | |
283 | * to caller. | |
284 | */ | |
285 | UInt32 tags[2] = { kExtendedAttrAttributeNameAttr, kExtendedAttrAttributeValueAttr }; | |
286 | UInt32 formats[2] = {0}; | |
287 | SecKeychainAttributeInfo attrInfo = {2, tags, formats}; | |
288 | SecKeychainAttributeList *attrList = NULL; | |
289 | foundItem->getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL); | |
290 | if((attrList == NULL) || (attrList->count != 2)) { | |
291 | /* should never happen... */ | |
292 | ourRtn = errSecNoSuchAttr; | |
293 | break; | |
294 | } | |
295 | if(outNames == NULL) { | |
296 | outNames = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
297 | } | |
298 | if((outValues == NULL) && (attrValues != NULL)) { | |
299 | /* this one's optional */ | |
300 | outValues = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
301 | } | |
302 | ||
303 | /* | |
304 | * I don't see how we can assume that the order of the returned | |
305 | * attributes is the same as the order of the tags we specified | |
306 | */ | |
307 | for(unsigned dex=0; dex<2; dex++) { | |
308 | SecKeychainAttribute *attr = &attrList->attr[dex]; | |
309 | CFDataRef cfd = NULL; | |
310 | CFStringRef cfs = NULL; | |
311 | switch(attr->tag) { | |
312 | case kExtendedAttrAttributeNameAttr: | |
313 | cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length); | |
314 | ||
315 | /* We created this attribute's data via CFStringCreateExternalRepresentation, so | |
316 | * this should always work... */ | |
317 | cfs = CFStringCreateFromExternalRepresentation(NULL, cfd, kCFStringEncodingUTF8); | |
318 | CFArrayAppendValue(outNames, cfs); | |
319 | CFRelease(cfd); | |
320 | CFRelease(cfs); | |
321 | break; | |
322 | case kExtendedAttrAttributeValueAttr: | |
323 | if(outValues == NULL) { | |
324 | break; | |
325 | } | |
326 | cfd = CFDataCreate(NULL, (const UInt8 *)attr->data, attr->length); | |
327 | CFArrayAppendValue(outValues, cfd); | |
328 | CFRelease(cfd); | |
329 | break; | |
330 | default: | |
331 | /* should never happen, right? */ | |
427c49bc | 332 | MacOSError::throwMe(errSecInternalComponent); |
b1ab9ed8 A |
333 | } |
334 | } | |
335 | ItemImpl::freeAttributesAndData(attrList, NULL); | |
336 | } /* main loop fetching matching Extended Attr records */ | |
337 | ||
338 | if(ourRtn) { | |
339 | if(outNames) { | |
340 | CFRelease(outNames); | |
341 | } | |
342 | if(outValues) { | |
343 | CFRelease(outValues); | |
344 | } | |
345 | MacOSError::throwMe(ourRtn); | |
346 | } | |
347 | ||
348 | if(outNames == NULL) { | |
349 | /* no extended attributes found */ | |
350 | return errSecNoSuchAttr; | |
351 | } | |
352 | *attrNames = outNames; | |
353 | if(outValues) { | |
354 | *attrValues = outValues; | |
355 | } | |
356 | ||
357 | END_SECAPI | |
358 | } |