]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2004,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 | ||
24 | ||
25 | // | |
26 | // CoreFoundation related utilities | |
27 | // | |
28 | #include <security_utilities/cfutilities.h> | |
29 | #include <security_utilities/errors.h> | |
30 | #include <security_utilities/debugging.h> | |
866f8763 | 31 | #include <security_utilities/unix++.h> |
6b200bc3 | 32 | #include <utilities/SecCFRelease.h> |
b1ab9ed8 A |
33 | #include <cstdarg> |
34 | #include <vector> | |
35 | ||
866f8763 | 36 | #include <sys/mman.h> |
b1ab9ed8 A |
37 | |
38 | namespace Security { | |
39 | ||
40 | ||
41 | ModuleNexus<CFEmptyArray> cfEmptyArray; | |
42 | ||
43 | CFEmptyArray::CFEmptyArray() | |
44 | { | |
45 | mArray = CFArrayCreate(NULL, NULL, 0, NULL); | |
46 | } | |
47 | ||
48 | ||
49 | // | |
50 | // Turn a C(++) string into a CFURLRef indicating a file: path | |
51 | // | |
52 | CFURLRef makeCFURL(const char *s, bool isDirectory, CFURLRef base) | |
53 | { | |
54 | if (base) | |
d8f41ccd A |
55 | return CFURLCreateWithFileSystemPathRelativeToBase(NULL, |
56 | CFTempString(s), kCFURLPOSIXPathStyle, isDirectory, base); | |
b1ab9ed8 | 57 | else |
d8f41ccd A |
58 | return CFURLCreateWithFileSystemPath(NULL, |
59 | CFTempString(s), kCFURLPOSIXPathStyle, isDirectory); | |
b1ab9ed8 A |
60 | } |
61 | ||
62 | CFURLRef makeCFURL(CFStringRef s, bool isDirectory, CFURLRef base) | |
63 | { | |
64 | if (base) | |
65 | return CFURLCreateWithFileSystemPathRelativeToBase(NULL, s, kCFURLPOSIXPathStyle, isDirectory, base); | |
66 | else | |
67 | return CFURLCreateWithFileSystemPath(NULL, s, kCFURLPOSIXPathStyle, isDirectory); | |
68 | } | |
69 | ||
70 | ||
71 | // | |
72 | // CFMallocData objects | |
73 | // | |
74 | CFMallocData::operator CFDataRef () | |
75 | { | |
76 | CFDataRef result = makeCFDataMalloc(mData, mSize); | |
77 | if (!result) | |
78 | CFError::throwMe(); | |
79 | mData = NULL; // release ownership | |
80 | return result; | |
81 | } | |
82 | ||
83 | ||
84 | // | |
85 | // Make CFDictionaries from stuff | |
86 | // | |
87 | CFDictionaryRef makeCFDictionary(unsigned count, ...) | |
88 | { | |
89 | CFTypeRef keys[count], values[count]; | |
90 | va_list args; | |
91 | va_start(args, count); | |
92 | for (unsigned n = 0; n < count; n++) { | |
93 | keys[n] = va_arg(args, CFTypeRef); | |
94 | values[n] = va_arg(args, CFTypeRef); | |
95 | } | |
96 | va_end(args); | |
97 | return CFDictionaryCreate(NULL, (const void **)keys, (const void **)values, count, | |
98 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
99 | } | |
100 | ||
101 | CFMutableDictionaryRef makeCFMutableDictionary() | |
102 | { | |
103 | if (CFMutableDictionaryRef r = CFDictionaryCreateMutable(NULL, 0, | |
104 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) | |
105 | return r; | |
106 | CFError::throwMe(); | |
107 | } | |
108 | ||
109 | CFMutableDictionaryRef makeCFMutableDictionary(unsigned count, ...) | |
110 | { | |
111 | CFMutableDictionaryRef dict = makeCFMutableDictionary(); | |
112 | if (count > 0) { | |
113 | va_list args; | |
114 | va_start(args, count); | |
115 | for (unsigned n = 0; n < count; n++) { | |
116 | CFTypeRef key = va_arg(args, CFTypeRef); | |
117 | CFTypeRef value = va_arg(args, CFTypeRef); | |
118 | CFDictionaryAddValue(dict, key, value); | |
119 | } | |
120 | va_end(args); | |
121 | } | |
122 | return dict; | |
123 | } | |
124 | ||
125 | CFMutableDictionaryRef makeCFMutableDictionary(CFDictionaryRef dict) | |
126 | { | |
127 | if (CFMutableDictionaryRef r = CFDictionaryCreateMutableCopy(NULL, 0, dict)) | |
128 | return r; | |
129 | CFError::throwMe(); | |
130 | } | |
131 | ||
132 | CFDictionaryRef makeCFDictionaryFrom(CFDataRef data) | |
133 | { | |
134 | if (data) { | |
135 | CFPropertyListRef plist = CFPropertyListCreateFromXMLData(NULL, data, | |
136 | kCFPropertyListImmutable, NULL); | |
137 | if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID()) | |
138 | CFError::throwMe(); | |
139 | return CFDictionaryRef(plist); | |
140 | } else | |
141 | return NULL; | |
142 | ||
143 | } | |
144 | ||
145 | CFDictionaryRef makeCFDictionaryFrom(const void *data, size_t length) | |
146 | { | |
147 | return makeCFDictionaryFrom(CFTempData(data, length).get()); | |
148 | } | |
149 | ||
150 | ||
5c19dc3a A |
151 | |
152 | static void cfarrayApplyBlock_func(const void *value, const void *ctx) | |
153 | { | |
154 | CFArrayApplierBlock block = CFArrayApplierBlock(ctx); | |
155 | block(value); | |
156 | } | |
157 | void cfArrayApplyBlock(CFArrayRef array, CFRange range, CFArrayApplierBlock block) | |
158 | { | |
159 | CFArrayApplyFunction(array, range, (CFArrayApplierFunction)cfarrayApplyBlock_func, block); | |
160 | } | |
161 | void cfArrayApplyBlock(CFArrayRef array, CFArrayApplierBlock block) | |
162 | { | |
163 | CFRange range = CFRangeMake(0, CFArrayGetCount(array)); | |
164 | cfArrayApplyBlock(array, range, block); | |
165 | } | |
166 | ||
167 | static void cfdictionaryApplyBlock_func(const void *key, const void *value, void *ctx) | |
168 | { | |
169 | CFDictionaryApplierBlock block = CFDictionaryApplierBlock(ctx); | |
170 | block(key, value); | |
171 | } | |
172 | void cfDictionaryApplyBlock(CFDictionaryRef dict, CFDictionaryApplierBlock block) | |
173 | { | |
174 | CFDictionaryApplyFunction(dict, cfdictionaryApplyBlock_func, block); | |
175 | } | |
176 | ||
177 | ||
b1ab9ed8 A |
178 | // |
179 | // Turn a CFString into a UTF8-encoded C++ string. | |
180 | // If release==true, the argument will be CFReleased even in case of error. | |
181 | // | |
182 | string cfString(CFStringRef str) | |
183 | { | |
184 | if (!str) | |
185 | return ""; | |
186 | // quick path first | |
187 | if (const char *s = CFStringGetCStringPtr(str, kCFStringEncodingUTF8)) { | |
188 | return s; | |
189 | } | |
190 | ||
191 | // need to extract into buffer | |
192 | string ret; | |
193 | CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); | |
194 | std::vector<char> buffer; | |
195 | buffer.resize(length + 1); | |
196 | if (CFStringGetCString(str, &buffer[0], length + 1, kCFStringEncodingUTF8)) | |
197 | ret = &buffer[0]; | |
198 | return ret; | |
199 | } | |
200 | ||
6b200bc3 | 201 | string cfStringRelease(CFStringRef CF_CONSUMED inStr) |
b1ab9ed8 A |
202 | { |
203 | CFRef<CFStringRef> str(inStr); | |
204 | return cfString(str); | |
205 | } | |
206 | ||
207 | string cfString(CFURLRef inUrl) | |
208 | { | |
209 | if (!inUrl) | |
210 | CFError::throwMe(); | |
211 | ||
212 | UInt8 buffer[PATH_MAX+1]; | |
213 | if (CFURLGetFileSystemRepresentation(inUrl, true, buffer, sizeof(buffer))) | |
214 | return string(reinterpret_cast<char *>(buffer)); | |
215 | else | |
216 | CFError::throwMe(); | |
217 | } | |
218 | ||
6b200bc3 | 219 | string cfStringRelease(CFURLRef CF_CONSUMED inUrl) |
b1ab9ed8 A |
220 | { |
221 | CFRef<CFURLRef> bundle(inUrl); | |
222 | return cfString(bundle); | |
223 | } | |
224 | ||
225 | string cfString(CFBundleRef inBundle) | |
226 | { | |
227 | if (!inBundle) | |
228 | CFError::throwMe(); | |
229 | return cfStringRelease(CFBundleCopyBundleURL(inBundle)); | |
230 | } | |
231 | ||
6b200bc3 | 232 | string cfStringRelease(CFBundleRef CF_CONSUMED inBundle) |
b1ab9ed8 A |
233 | { |
234 | CFRef<CFBundleRef> bundle(inBundle); | |
235 | return cfString(bundle); | |
236 | } | |
237 | ||
238 | ||
239 | string cfString(CFTypeRef it, OSStatus err) | |
240 | { | |
241 | if (it == NULL) | |
242 | MacOSError::throwMe(err); | |
243 | CFTypeID id = CFGetTypeID(it); | |
244 | if (id == CFStringGetTypeID()) | |
245 | return cfString(CFStringRef(it)); | |
246 | else if (id == CFURLGetTypeID()) | |
247 | return cfString(CFURLRef(it)); | |
248 | else if (id == CFBundleGetTypeID()) | |
249 | return cfString(CFBundleRef(it)); | |
6b200bc3 A |
250 | else { |
251 | return cfStringRelease(CFCopyDescription(it)); | |
252 | } | |
b1ab9ed8 A |
253 | } |
254 | ||
255 | ||
256 | // | |
257 | // CFURLAccess wrappers for specific purposes | |
258 | // | |
866f8763 | 259 | CFDataRef cfReadFile(CFURLRef url) |
b1ab9ed8 A |
260 | { |
261 | assert(url); | |
262 | CFDataRef data; | |
263 | SInt32 error; | |
264 | if (CFURLCreateDataAndPropertiesFromResource(NULL, url, | |
265 | &data, NULL, NULL, &error)) { | |
266 | return data; | |
267 | } else { | |
fa7225c8 | 268 | secinfo("cfloadfile", "failed to fetch %s error=%d", cfString(url).c_str(), int(error)); |
b1ab9ed8 A |
269 | return NULL; |
270 | } | |
271 | } | |
272 | ||
866f8763 | 273 | CFDataRef cfReadFile(int fd, size_t bytes) |
80e23899 A |
274 | { |
275 | uint8_t *buffer = (uint8_t *) malloc(bytes); | |
276 | ||
277 | if (buffer == NULL) | |
278 | return NULL; | |
279 | ||
280 | if (read(fd, buffer, bytes) != bytes) { | |
281 | free(buffer); | |
282 | return NULL; | |
283 | } | |
284 | ||
285 | CFDataRef result = CFDataCreateWithBytesNoCopy(kCFAllocatorMalloc, buffer, bytes, kCFAllocatorMalloc); | |
286 | ||
287 | // If CFDataCreateWithBytesNoCopy fails, the buffer is not free()-ed | |
288 | if (result == NULL) { | |
289 | free(buffer); | |
290 | return NULL; | |
291 | } | |
292 | ||
293 | return result; | |
294 | } | |
b1ab9ed8 A |
295 | |
296 | // | |
297 | // CFArray creators | |
298 | // | |
299 | CFArrayRef makeCFArray(CFIndex count, ...) | |
300 | { | |
301 | CFTypeRef elements[count]; | |
302 | va_list args; | |
303 | va_start(args, count); | |
304 | for (CFIndex n = 0; n < count; n++) | |
305 | elements[n] = va_arg(args, CFTypeRef); | |
306 | va_end(args); | |
307 | return CFArrayCreate(NULL, elements, count, &kCFTypeArrayCallBacks); | |
308 | } | |
309 | ||
310 | CFMutableArrayRef makeCFMutableArray(CFIndex count, ...) | |
311 | { | |
312 | CFMutableArrayRef array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); | |
313 | va_list args; | |
314 | va_start(args, count); | |
315 | for (CFIndex n = 0; n < count; n++) | |
316 | CFArrayAppendValue(array, va_arg(args, CFTypeRef)); | |
317 | va_end(args); | |
318 | return array; | |
319 | } | |
320 | ||
866f8763 A |
321 | struct mmapAllocatorInfo { |
322 | size_t size; | |
323 | }; | |
324 | ||
325 | static void *mmapDeallocatorAllocate(CFIndex allocSize, CFOptionFlags hint, void *info) { | |
326 | /* We do nothing here. makeMappedData already did everything, the only thing we want | |
327 | * this allocator for is to deallocate. */ | |
328 | return NULL; | |
329 | } | |
330 | ||
331 | static void mmapDeallocatorDeallocate(void *ptr, void *info) { | |
332 | struct mmapAllocatorInfo const *mmapInfo = | |
333 | reinterpret_cast<struct mmapAllocatorInfo const *> | |
334 | (CFDataGetBytePtr(reinterpret_cast<CFDataRef>(info))); | |
335 | ||
336 | if (munmap(ptr, mmapInfo->size) != 0) { | |
337 | secdebug("mmapdeallocatordeallocate", "could not unmap: errno %d", errno); | |
338 | } | |
339 | } | |
340 | ||
341 | static CFIndex mmapPreferredSize(CFIndex size, CFOptionFlags hint, void *info) { | |
342 | return size + sizeof(struct mmapAllocatorInfo); // No need to be exact here. | |
343 | } | |
344 | ||
345 | CFDataRef cfMapFile(int fd, size_t bytes) | |
346 | { | |
347 | off_t offset = lseek(fd, 0, SEEK_CUR); | |
348 | ||
349 | if (offset == -1) { | |
350 | secdebug("cfmapfile", "cannot get file offset, errno %d", errno); | |
351 | } | |
352 | ||
353 | uint8_t *buf = (uint8_t*)mmap(NULL, bytes, PROT_READ, MAP_PRIVATE, fd, offset); | |
354 | ||
355 | if (buf == MAP_FAILED) { | |
356 | secdebug("cfmapfile", "cannot mmap file, errno %d", errno); | |
357 | return NULL; | |
358 | } | |
359 | ||
360 | /* We're finally set up. */ | |
361 | ||
362 | struct mmapAllocatorInfo info = { | |
363 | .size = bytes | |
364 | }; | |
365 | ||
366 | CFRef<CFDataRef> infoData = makeCFData(&info, sizeof(info)); | |
367 | ||
368 | CFAllocatorContext context = { | |
369 | .version = 0, | |
370 | .info = NULL, | |
371 | .retain = CFRetain, | |
372 | .release = CFRelease, | |
373 | .copyDescription = NULL, | |
374 | .allocate = mmapDeallocatorAllocate, | |
375 | .reallocate = NULL, | |
376 | .deallocate = mmapDeallocatorDeallocate, | |
377 | .preferredSize = mmapPreferredSize | |
378 | }; | |
379 | ||
380 | context.info = (void*)infoData.get(); | |
381 | ||
382 | CFRef<CFAllocatorRef> deallocator = CFAllocatorCreate(NULL, &context); | |
383 | ||
384 | CFDataRef result = CFDataCreateWithBytesNoCopy(NULL, buf, info.size, deallocator.get()); | |
385 | ||
386 | // If CFDataCreateWithBytesNoCopy fails, the buffer is not unallocated | |
387 | if (result == NULL) { | |
388 | munmap(buf, bytes); | |
389 | return NULL; | |
390 | } | |
391 | ||
392 | return result; | |
393 | } | |
394 | ||
395 | CFDataRef cfMapFile(CFURLRef url) { | |
396 | string path; | |
397 | ||
398 | /* This is contrived, | |
399 | * but we want this as compatible to cfLoadFile as possible, which also means | |
400 | * not throwing the exceptions that cfString might, as cfLoadFile does not call | |
401 | * cfString. */ | |
402 | ||
403 | try { | |
404 | path = cfString(url); | |
405 | } catch (...) { | |
406 | secdebug("cfmapfile", "Exception while forming path from URL, giving up."); | |
407 | return NULL; | |
408 | } | |
409 | ||
410 | UnixPlusPlus::AutoFileDesc fd(path.c_str(), O_RDONLY, 0666 | UnixPlusPlus::AutoFileDesc::modeMissingOk); | |
411 | ||
412 | struct stat st; | |
413 | ||
414 | if (!fd.isOpen()) { | |
415 | secdebug("cfmapfile", "cannot open file '%s', errno %d", path.c_str(), errno); | |
416 | return NULL; | |
417 | } | |
418 | ||
419 | if (fstat(fd.fd(), &st) != 0) { | |
420 | secdebug("cfmapfile", "cannot stat '%s', errno %d", path.c_str(), errno); | |
421 | return NULL; | |
422 | } | |
423 | ||
424 | if (st.st_size < 0) { | |
425 | secdebug("cfmapfile", "size for '%s' is negative", path.c_str()); | |
426 | return NULL; | |
427 | } | |
428 | ||
429 | return cfMapFile(fd.fd(), fd.fileSize()); | |
430 | } | |
431 | ||
432 | CFDataRef cfLoadFile(CFURLRef url){ | |
b54c578e | 433 | #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR |
866f8763 A |
434 | return cfMapFile(url); |
435 | #else | |
436 | return cfReadFile(url); | |
437 | #endif | |
438 | } | |
439 | ||
440 | CFDataRef cfLoadFile(int fd, size_t bytes){ | |
b54c578e | 441 | #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR |
866f8763 A |
442 | return cfMapFile(fd, bytes); |
443 | #else | |
444 | return cfReadFile(fd, bytes); | |
445 | #endif | |
446 | } | |
447 | ||
b1ab9ed8 A |
448 | |
449 | } // end namespace Security |