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