]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_utilities/lib/cfutilities.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / cfutilities.cpp
CommitLineData
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
38namespace Security {
39
40
41ModuleNexus<CFEmptyArray> cfEmptyArray;
42
43CFEmptyArray::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//
52CFURLRef 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
62CFURLRef 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//
74CFMallocData::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//
87CFDictionaryRef 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
101CFMutableDictionaryRef makeCFMutableDictionary()
102{
103 if (CFMutableDictionaryRef r = CFDictionaryCreateMutable(NULL, 0,
104 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks))
105 return r;
106 CFError::throwMe();
107}
108
109CFMutableDictionaryRef 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
125CFMutableDictionaryRef makeCFMutableDictionary(CFDictionaryRef dict)
126{
127 if (CFMutableDictionaryRef r = CFDictionaryCreateMutableCopy(NULL, 0, dict))
128 return r;
129 CFError::throwMe();
130}
131
132CFDictionaryRef 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
145CFDictionaryRef makeCFDictionaryFrom(const void *data, size_t length)
146{
147 return makeCFDictionaryFrom(CFTempData(data, length).get());
148}
149
150
5c19dc3a
A
151
152static void cfarrayApplyBlock_func(const void *value, const void *ctx)
153{
154 CFArrayApplierBlock block = CFArrayApplierBlock(ctx);
155 block(value);
156}
157void cfArrayApplyBlock(CFArrayRef array, CFRange range, CFArrayApplierBlock block)
158{
159 CFArrayApplyFunction(array, range, (CFArrayApplierFunction)cfarrayApplyBlock_func, block);
160}
161void cfArrayApplyBlock(CFArrayRef array, CFArrayApplierBlock block)
162{
163 CFRange range = CFRangeMake(0, CFArrayGetCount(array));
164 cfArrayApplyBlock(array, range, block);
165}
166
167static void cfdictionaryApplyBlock_func(const void *key, const void *value, void *ctx)
168{
169 CFDictionaryApplierBlock block = CFDictionaryApplierBlock(ctx);
170 block(key, value);
171}
172void 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//
182string 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 201string cfStringRelease(CFStringRef CF_CONSUMED inStr)
b1ab9ed8
A
202{
203 CFRef<CFStringRef> str(inStr);
204 return cfString(str);
205}
206
207string 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 219string cfStringRelease(CFURLRef CF_CONSUMED inUrl)
b1ab9ed8
A
220{
221 CFRef<CFURLRef> bundle(inUrl);
222 return cfString(bundle);
223}
224
225string cfString(CFBundleRef inBundle)
226{
227 if (!inBundle)
228 CFError::throwMe();
229 return cfStringRelease(CFBundleCopyBundleURL(inBundle));
230}
231
6b200bc3 232string cfStringRelease(CFBundleRef CF_CONSUMED inBundle)
b1ab9ed8
A
233{
234 CFRef<CFBundleRef> bundle(inBundle);
235 return cfString(bundle);
236}
237
238
239string 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 259CFDataRef 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 273CFDataRef 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//
299CFArrayRef 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
310CFMutableArrayRef 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
321struct mmapAllocatorInfo {
322 size_t size;
323};
324
325static 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
331static 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
341static CFIndex mmapPreferredSize(CFIndex size, CFOptionFlags hint, void *info) {
342 return size + sizeof(struct mmapAllocatorInfo); // No need to be exact here.
343}
344
345CFDataRef 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
395CFDataRef 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
432CFDataRef 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
440CFDataRef 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