]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/cfutilities.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / cfutilities.cpp
1 /*
2 * Copyright (c) 2000-2004,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
25 //
26 // CoreFoundation related utilities
27 //
28 #include <security_utilities/cfutilities.h>
29 #include <security_utilities/errors.h>
30 #include <security_utilities/debugging.h>
31 #include <security_utilities/unix++.h>
32 #include <utilities/SecCFRelease.h>
33 #include <cstdarg>
34 #include <vector>
35
36 #include <sys/mman.h>
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)
55 return CFURLCreateWithFileSystemPathRelativeToBase(NULL,
56 CFTempString(s), kCFURLPOSIXPathStyle, isDirectory, base);
57 else
58 return CFURLCreateWithFileSystemPath(NULL,
59 CFTempString(s), kCFURLPOSIXPathStyle, isDirectory);
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
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
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
201 string cfStringRelease(CFStringRef CF_CONSUMED inStr)
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
219 string cfStringRelease(CFURLRef CF_CONSUMED inUrl)
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
232 string cfStringRelease(CFBundleRef CF_CONSUMED inBundle)
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));
250 else {
251 return cfStringRelease(CFCopyDescription(it));
252 }
253 }
254
255
256 //
257 // CFURLAccess wrappers for specific purposes
258 //
259 CFDataRef cfReadFile(CFURLRef url)
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 {
268 secinfo("cfloadfile", "failed to fetch %s error=%d", cfString(url).c_str(), int(error));
269 return NULL;
270 }
271 }
272
273 CFDataRef cfReadFile(int fd, size_t bytes)
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 }
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
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){
433 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
434 return cfMapFile(url);
435 #else
436 return cfReadFile(url);
437 #endif
438 }
439
440 CFDataRef cfLoadFile(int fd, size_t bytes){
441 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
442 return cfMapFile(fd, bytes);
443 #else
444 return cfReadFile(fd, bytes);
445 #endif
446 }
447
448
449 } // end namespace Security