1//
2//  SecCFWrappers.h
3//
4//  Created by Mitch Adler on 1/27/11.
5//  Copyright 2011 Apple Inc. All rights reserved.
6//
7
8#ifndef _SECCFWRAPPERS_H_
9#define _SECCFWRAPPERS_H_
10
11#include <CoreFoundation/CFRuntime.h>
12#include <CoreFoundation/CFNumber.h>
13#include <CoreFoundation/CFData.h>
14#include <CoreFoundation/CFDate.h>
15#include <CoreFoundation/CFString.h>
16#include <CoreFoundation/CFPropertyList.h>
17
18#include <utilities/SecCFRelease.h>
19#include <utilities/debugging.h>
20
21#include <assert.h>
22#include <dispatch/dispatch.h>
23#include <stdlib.h>
24#include <string.h>
25#include <stdio.h>
26
27
28//
29// Convenience routines.
30//
31
32//
33// Macros for the pattern
34//
35// typedef struct _privateNewClass* NewClassRef;
36//
37// struct _privateNewClass {
38//      CFRuntimeBase _base;
39//      ... class additions
40// };
41//
42// kClassNameRegisterClass
43// kClassNameTypeID
44//
45// ClassNameGetTypeID()
46//
47// CFGiblisFor(NewClass);
48//
49// .. define NewClassDestroy
50// .. define NewClassCopyDescription
51//
52// .. use CFTypeAllocate(NewClass, _privateNewClass, allocator);
53//
54//
55
56#define CFGiblisWithFunctions(gibliClassName, describe_func, destroy_func, compare_func, hash_func) \
57    CFTypeID gibliClassName##GetTypeID(void); \
58    CFTypeID gibliClassName##GetTypeID(void) { \
59    static dispatch_once_t  k##gibliClassName##RegisterClass; \
60    static CFTypeID         k##gibliClassName##TypeID = _kCFRuntimeNotATypeID; \
61    \
62    dispatch_once(&k##gibliClassName##RegisterClass, ^{ \
63        static const CFRuntimeClass k##gibliClassName##Class = { \
64        .className = #gibliClassName, \
65        .finalize = destroy_func, \
66        .copyDebugDesc = describe_func, \
67        .equal = compare_func, \
68        .hash = hash_func, \
69        }; \
70        \
71        k##gibliClassName##TypeID = _CFRuntimeRegisterClass(&k##gibliClassName##Class); \
72    }); \
73    return k##gibliClassName##TypeID; \
74    }
75
76
77#define CFGiblisWithHashFor(gibliClassName) \
78    static CFStringRef  gibliClassName##CopyDescription(CFTypeRef cf); \
79    static void         gibliClassName##Destroy(CFTypeRef cf); \
80    static Boolean      gibliClassName##Compare(CFTypeRef lhs, CFTypeRef rhs); \
81    static CFHashCode   gibliClassName##Hash(CFTypeRef cf); \
82    \
83    CFGiblisWithFunctions(gibliClassName, gibliClassName##CopyDescription, gibliClassName##Destroy, gibliClassName##Compare, gibliClassName##Hash)
84
85#define CFGiblisWithCompareFor(gibliClassName) \
86    static CFStringRef  gibliClassName##CopyDescription(CFTypeRef cf); \
87    static void         gibliClassName##Destroy(CFTypeRef cf); \
88    static Boolean      gibliClassName##Compare(CFTypeRef lhs, CFTypeRef rhs); \
89    \
90    CFGiblisWithFunctions(gibliClassName, gibliClassName##CopyDescription, gibliClassName##Destroy, gibliClassName##Compare, NULL)
91
92
93#define CFGiblisFor(gibliClassName) \
94    static CFStringRef  gibliClassName##CopyDescription(CFTypeRef cf); \
95    static void         gibliClassName##Destroy(CFTypeRef cf); \
96    \
97    CFGiblisWithFunctions(gibliClassName, gibliClassName##CopyDescription, gibliClassName##Destroy, NULL, NULL)
98
99#define CFTypeAllocate(classType, internalType, allocator) \
100    (classType##Ref) _CFRuntimeCreateInstance(allocator, classType##GetTypeID(), \
101                                              sizeof(internalType) - sizeof(CFRuntimeBase), \
102                                              NULL)
103
104__BEGIN_DECLS
105
106//
107// Call block function
108//
109
110static void apply_block_1(const void *value, void *context)
111{
112    return ((void (^)(const void *value))context)(value);
113}
114
115static void apply_block_2(const void *key, const void *value, void *context)
116{
117    return ((void (^)(const void *key, const void *value))context)(key, value);
118}
119
120//
121// CFEqual Helpers
122//
123
124static inline bool CFEqualSafe(CFTypeRef left, CFTypeRef right)
125{
126    if (left == NULL || right == NULL)
127        return left == right;
128    else
129        return CFEqual(left, right);
130}
131
132//
133// CFNumber Helpers
134//
135
136static inline CFNumberRef CFNumberCreateWithCFIndex(CFAllocatorRef allocator, CFIndex value)
137{
138    return CFNumberCreate(allocator, kCFNumberCFIndexType, &value);
139}
140
141//
142// CFData Helpers
143//
144
145static inline CFMutableDataRef CFDataCreateMutableWithScratch(CFAllocatorRef allocator, CFIndex size) {
146    CFMutableDataRef result = CFDataCreateMutable(allocator, 0);
147    CFDataSetLength(result, size);
148
149    return result;
150}
151
152static inline void CFDataAppend(CFMutableDataRef appendTo, CFDataRef dataToAppend)
153{
154    CFDataAppendBytes(appendTo, CFDataGetBytePtr(dataToAppend), CFDataGetLength(dataToAppend));
155}
156
157static inline CFDataRef CFDataCreateReferenceFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
158{
159    return CFDataCreateWithBytesNoCopy(allocator,
160                                       CFDataGetBytePtr(sourceData) + range.location, range.length,
161                                       kCFAllocatorNull);
162}
163
164static inline CFDataRef CFDataCreateCopyFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
165{
166    return CFDataCreate(allocator, CFDataGetBytePtr(sourceData) + range.location, range.length);
167}
168
169static inline uint8_t* CFDataIncreaseLengthAndGetMutableBytes(CFMutableDataRef data, CFIndex extraLength)
170{
171    CFIndex startOffset = CFDataGetLength(data);
172
173    CFDataIncreaseLength(data, extraLength);
174
175    return CFDataGetMutableBytePtr(data) + startOffset;
176}
177
178static inline uint8_t* CFDataGetMutablePastEndPtr(CFMutableDataRef theData)
179{
180    return CFDataGetMutableBytePtr(theData) + CFDataGetLength(theData);
181}
182
183static inline const uint8_t* CFDataGetPastEndPtr(CFDataRef theData) {
184    return CFDataGetBytePtr(theData) + CFDataGetLength(theData);
185}
186
187static inline CFComparisonResult CFDataCompare(CFDataRef left, CFDataRef right)
188{
189    const size_t left_size = CFDataGetLength(left);
190    const size_t right_size = CFDataGetLength(right);
191    const size_t shortest = (left_size <= right_size) ? left_size : right_size;
192
193    int comparison = memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), shortest);
194
195    if (comparison > 0 || (comparison == 0 && left_size > right_size))
196        return kCFCompareGreaterThan;
197    else if (comparison < 0 || (comparison == 0 && left_size < right_size))
198        return kCFCompareLessThan;
199    else
200        return kCFCompareEqualTo;
201}
202
203
204//
205// CFString Helpers
206//
207
208//
209// Turn a CFString into an allocated UTF8-encoded C string.
210//
211static inline char *CFStringToCString(CFStringRef inStr)
212{
213    if (!inStr)
214        return (char *)strdup("");
215    CFRetain(inStr);        // compensate for release on exit
216
217    // need to extract into buffer
218    CFIndex length = CFStringGetLength(inStr);  // in 16-bit character units
219    size_t len = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
220    char *buffer = (char *)malloc(len);                 // pessimistic
221    if (!CFStringGetCString(inStr, buffer, len, kCFStringEncodingUTF8))
222        buffer[0] = 0;
223
224    CFRelease(inStr);
225    return buffer;
226}
227
228// runs operation with inStr as a zero terminated C string
229// in utf8 encoding passed to the operation block.
230void CFStringPerformWithCString(CFStringRef inStr, void(^operation)(const char *utf8Str));
231
232// runs operation with inStr as a zero terminated C string
233// in utf8 passed to the operation block, the length of
234// the string is also provided to the block.
235void CFStringPerformWithCStringAndLength(CFStringRef inStr, void(^operation)(const char *utf8Str, size_t utf8Length));
236
237#include <CommonNumerics/CommonCRC.h>
238
239static inline void CFStringAppendEncryptedData(CFMutableStringRef s, CFDataRef edata)
240{
241    const uint8_t *bytes = CFDataGetBytePtr(edata);
242    CFIndex len = CFDataGetLength(edata);
243    CFStringAppendFormat(s, 0, CFSTR("%04lx:"), len);
244    if(len<=8) {
245        for (CFIndex ix = 0; ix < len; ++ix) {
246            CFStringAppendFormat(s, 0, CFSTR("%02X"), bytes[ix]);
247        }
248    } else {
249        uint64_t crc = 0;
250        CNCRC(kCN_CRC_64_ECMA_182, bytes+8, len-8, &crc);
251        for (CFIndex ix = 0; ix < 8; ++ix) {
252            CFStringAppendFormat(s, 0, CFSTR("%02X"), bytes[ix]);
253        }
254        CFStringAppendFormat(s, 0, CFSTR("...|%08llx"), crc);
255    }
256}
257
258static inline void CFStringAppendHexData(CFMutableStringRef s, CFDataRef data) {
259    const uint8_t *bytes = CFDataGetBytePtr(data);
260    CFIndex len = CFDataGetLength(data);
261    for (CFIndex ix = 0; ix < len; ++ix) {
262        CFStringAppendFormat(s, 0, CFSTR("%02X"), bytes[ix]);
263    }
264}
265
266static inline CFStringRef CFDataCopyHexString(CFDataRef data) {
267    CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 2 * CFDataGetLength(data));
268    CFStringAppendHexData(hexString, data);
269    return hexString;
270}
271
272static inline void CFDataPerformWithHexString(CFDataRef data, void (^operation)(CFStringRef dataString)) {
273    CFStringRef hexString = CFDataCopyHexString(data);
274    operation(hexString);
275    CFRelease(hexString);
276}
277
278static inline void BufferPerformWithHexString(const UInt8 *bytes, CFIndex length, void (^operation)(CFStringRef dataString)) {
279    CFDataRef bufferAsData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, length, kCFAllocatorNull);
280
281    CFDataPerformWithHexString(bufferAsData, operation);
282
283    CFReleaseNull(bufferAsData);
284}
285
286
287
288static inline void CFStringWriteToFile(CFStringRef inStr, FILE* file)
289{
290    CFStringPerformWithCStringAndLength(inStr, ^(const char *utf8Str, size_t utf8Length) {
291        fwrite(utf8Str, 1, utf8Length, file);
292    });
293}
294
295static inline void CFStringWriteToFileWithNewline(CFStringRef inStr, FILE* file)
296{
297    CFStringWriteToFile(inStr, file);
298    fputc('\n', file);
299}
300
301//
302// MARK: CFArray Helpers
303//
304
305static inline CFIndex CFArrayRemoveAllValue(CFMutableArrayRef array, const void* value)
306{
307    CFIndex position = kCFNotFound;
308    CFIndex numberRemoved = 0;
309
310    position = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value);
311    while (position != kCFNotFound) {
312        CFArrayRemoveValueAtIndex(array, position);
313        ++numberRemoved;
314        position = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value);
315    }
316
317    return numberRemoved;
318}
319
320static inline void CFArrayForEach(CFArrayRef array, void (^operation)(const void *value)) {
321    CFArrayApplyFunction(array, CFRangeMake(0, CFArrayGetCount(array)), apply_block_1, operation);
322}
323
324static inline void CFArrayForEachReverse(CFArrayRef array, void (^operation)(const void *value)) {
325    for(CFIndex count = CFArrayGetCount(array); count > 0; --count) {
326        operation(CFArrayGetValueAtIndex(array, count - 1));
327    }
328}
329
330static inline const void *CFArrayGetValueMatching(CFArrayRef array, bool (^match)(const void *value)) {
331    CFIndex i, n = CFArrayGetCount(array);
332    for (i = 0; i < n; ++i) {
333        const void *value = CFArrayGetValueAtIndex(array, i);
334        if (match(value)) {
335            return value;
336        }
337    }
338    return NULL;
339}
340
341static inline bool CFArrayHasValueMatching(CFArrayRef array, bool (^match)(const void *value)) {
342    return CFArrayGetValueMatching(array, match) != NULL;
343}
344
345static inline void CFMutableArrayModifyValues(CFMutableArrayRef array, const void * (^process)(const void *value)) {
346    CFIndex i, n = CFArrayGetCount(array);
347    for (i = 0; i < n; ++i) {
348        const void *value = CFArrayGetValueAtIndex(array, i);
349        CFArraySetValueAtIndex(array, i, process(value));
350    }
351}
352
353//
354// MARK: CFArray creatino Var args helper functions.
355//
356static inline CFArrayRef CFArrayCreateCountedForVC(CFAllocatorRef allocator, const CFArrayCallBacks *cbs, CFIndex entries, va_list args)
357{
358    const void *values[entries];
359
360    for(CFIndex currentValue = 0; currentValue < entries; ++currentValue)
361    {
362        values[currentValue] = va_arg(args, void*);
363
364        if (values[currentValue] == NULL)
365            values[currentValue] = kCFNull;
366    }
367
368    return CFArrayCreate(allocator, values, entries, cbs);
369}
370
371static inline CFArrayRef CFArrayCreateForVC(CFAllocatorRef allocator, const CFArrayCallBacks *cbs, va_list args)
372{
373    va_list count;
374    va_copy(count, args);
375
376    CFIndex entries = 0;
377    while (NULL != va_arg(count, void*)) {
378        entries += 1;
379    }
380
381    return CFArrayCreateCountedForVC(allocator, cbs, entries, args);
382
383}
384
385//
386// MARK: CFArray of CFTypes support
387//
388
389static inline CFMutableArrayRef CFArrayCreateMutableForCFTypes(CFAllocatorRef allocator)
390{
391    return CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
392}
393
394static inline CFArrayRef CFArrayCreateForCFTypes(CFAllocatorRef allocator, ...)
395{
396    va_list args;
397    va_start(args, allocator);
398
399    return CFArrayCreateForVC(allocator, &kCFTypeArrayCallBacks, args);
400
401}
402
403static inline CFArrayRef CFArrayCreateCountedForCFTypes(CFAllocatorRef allocator, CFIndex entries, ...)
404{
405    va_list args;
406    va_start(args, entries);
407
408    return CFArrayCreateCountedForVC(allocator, &kCFTypeArrayCallBacks, entries, args);
409}
410
411static inline CFArrayRef CFArrayCreateCountedForCFTypesV(CFAllocatorRef allocator, CFIndex entries, va_list args)
412{
413    return CFArrayCreateCountedForVC(allocator, &kCFTypeArrayCallBacks, entries, args);
414}
415
416//
417// MARK: CFDictionary of CFTypes helpers
418//
419
420static inline CFDictionaryRef CFDictionaryCreateCountedForCFTypesV(CFAllocatorRef allocator, CFIndex entries, va_list args)
421{
422    const void *keys[entries];
423    const void *values[entries];
424
425    for(CFIndex currentValue = 0; currentValue < entries; ++currentValue)
426    {
427        keys[currentValue] = va_arg(args, void*);
428        values[currentValue] = va_arg(args, void*);
429
430        if (values[currentValue] == NULL)
431            values[currentValue] = kCFNull;
432    }
433
434    return CFDictionaryCreate(allocator, keys, values, entries,
435                              &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
436}
437
438static inline CFDictionaryRef CFDictionaryCreateForCFTypes(CFAllocatorRef allocator, ...)
439{
440    va_list args;
441    va_start(args, allocator);
442
443    CFIndex entries = 0;
444    while (NULL != va_arg(args, void*)) {
445        entries += 2;
446        (void) va_arg(args, void*);
447    }
448
449    entries /= 2;
450
451    va_start(args, allocator);
452
453    return CFDictionaryCreateCountedForCFTypesV(allocator, entries, args);
454
455}
456
457static inline CFDictionaryRef CFDictionaryCreateCountedForCFTypes(CFAllocatorRef allocator, CFIndex entries, ...)
458{
459    va_list args;
460    va_start(args, entries);
461
462    return CFDictionaryCreateCountedForCFTypesV(allocator, entries, args);
463}
464
465static inline CFMutableDictionaryRef CFDictionaryCreateMutableForCFTypes(CFAllocatorRef allocator)
466{
467    return CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
468}
469
470static inline CFMutableDictionaryRef CFDictionaryCreateMutableForCFTypesWith(CFAllocatorRef allocator, ...)
471{
472    CFMutableDictionaryRef result = CFDictionaryCreateMutableForCFTypes(allocator);
473
474    va_list args;
475    va_start(args, allocator);
476
477    void* key = va_arg(args, void*);
478
479    while (key != NULL) {
480        CFDictionarySetValue(result, key, va_arg(args, void*));
481        key = va_arg(args, void*);
482    };
483
484    return result;
485}
486
487//
488// MARK: CFDictionary Helpers
489//
490
491static inline void CFDictionaryForEach(CFDictionaryRef dictionary, void (^operation)(const void *key, const void *value)) {
492    CFDictionaryApplyFunction(dictionary, apply_block_2, operation);
493}
494
495//
496// Type checking
497//
498
499static inline bool isArray(CFTypeRef cfType) {
500    return cfType && CFGetTypeID(cfType) == CFArrayGetTypeID();
501}
502
503static inline bool isData(CFTypeRef cfType) {
504    return cfType && CFGetTypeID(cfType) == CFDataGetTypeID();
505}
506
507static inline bool isDate(CFTypeRef cfType) {
508    return cfType && CFGetTypeID(cfType) == CFDateGetTypeID();
509}
510
511static inline bool isDictionary(CFTypeRef cfType) {
512    return cfType && CFGetTypeID(cfType) == CFDictionaryGetTypeID();
513}
514
515static inline bool isNumber(CFTypeRef cfType) {
516    return cfType && CFGetTypeID(cfType) == CFNumberGetTypeID();
517}
518
519static inline bool isNumberOfType(CFTypeRef cfType, CFNumberType number) {
520    return isNumber(cfType) && CFNumberGetType((CFNumberRef)cfType) == number;
521}
522
523static inline bool isString(CFTypeRef cfType) {
524    return cfType && CFGetTypeID(cfType) == CFStringGetTypeID();
525}
526
527static inline bool isBoolean(CFTypeRef cfType) {
528    return cfType && CFGetTypeID(cfType) == CFBooleanGetTypeID();
529}
530
531static inline bool isNull(CFTypeRef cfType) {
532    return cfType && CFGetTypeID(cfType) == CFNullGetTypeID();
533}
534
535
536//
537// MARK: PropertyList Helpers
538//
539
540//
541// Crazy reading and writing stuff
542//
543
544static inline void CFPropertyListWriteToFile(CFPropertyListRef plist, CFURLRef file)
545{
546    CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, file);
547    CFErrorRef error = NULL;
548
549    CFWriteStreamOpen(writeStream);
550    CFPropertyListWrite(plist, writeStream, kCFPropertyListBinaryFormat_v1_0, 0, &error);
551    if (error)
552        secerror("Can't write plist: %@", error);
553
554    CFReleaseNull(error);
555    CFReleaseNull(writeStream);
556}
557
558static inline CF_RETURNS_RETAINED CFPropertyListRef CFPropertyListReadFromFile(CFURLRef file)
559{
560    CFPropertyListRef result = NULL;
561    CFErrorRef error = NULL;
562    CFBooleanRef isRegularFile;
563    if (!CFURLCopyResourcePropertyForKey(file, kCFURLIsRegularFileKey, &isRegularFile, &error)) {
564        secerror("file %@: %@", file, error);
565    } else if (CFBooleanGetValue(isRegularFile)) {
566        CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, file);
567        if (readStream) {
568            if (CFReadStreamOpen(readStream)) {
569                CFPropertyListFormat format;
570                result = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStream, 0, kCFPropertyListMutableContainers, &format, &error);
571                if (!result) {
572                    secerror("read plist from %@: %@", file, error);
573                }
574            }
575            CFRelease(readStream);
576        }
577    }
578    CFReleaseNull(error);
579
580    return result;
581}
582
583__END_DECLS
584
585#endif
586