1/* 2 * Copyright (c) 2006-2007,2011,2013-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// CoreFoundation building and parsing functions. 25// 26// These classes provide a printf/scanf-like interface to nested data structures 27// of the Property List Subset of CoreFoundation. 28// 29#include "cfmunge.h" 30#include <security_utilities/cfutilities.h> 31#include <security_utilities/errors.h> 32 33namespace Security { 34 35 36// 37// Format codes for consistency 38// 39#define F_ARRAY 'A' 40#define F_BOOLEAN 'B' 41#define F_DATA 'X' 42#define F_DICTIONARY 'D' 43#define F_OBJECT 'O' 44#define F_STRING 'S' 45#define F_NUMBER 'N' 46 47 48// 49// Initialize a CFMunge. We start out with the default CFAllocator, and 50// we do not throw errors. 51// 52CFMunge::CFMunge(const char *fmt, va_list arg) 53 : format(fmt), allocator(NULL), error(errSecSuccess) 54{ 55 va_copy(args, arg); 56} 57 58CFMunge::~CFMunge() 59{ 60 va_end(args); 61} 62 63 64// 65// Skip whitespace and other fluff and deliver the next significant character. 66// 67char CFMunge::next() 68{ 69 while (*format && (isspace(*format) || *format == ',')) ++format; 70 return *format; 71} 72 73 74// 75// Locate and consume an optional character 76// 77bool CFMunge::next(char c) 78{ 79 if (next() == c) { 80 ++format; 81 return true; 82 } else 83 return false; 84} 85 86 87// 88// Process @? parameter specifications. 89// The @ operator is used for side effects, and does not return a value. 90// 91bool CFMunge::parameter() 92{ 93 switch (*++format) { 94 case 'A': 95 ++format; 96 allocator = va_arg(args, CFAllocatorRef); 97 return true; 98 case 'E': 99 ++format; 100 error = va_arg(args, OSStatus); 101 return true; 102 default: 103 return false; 104 } 105} 106 107 108// 109// The top constructor. 110// 111CFTypeRef CFMake::make() 112{ 113 while (next() == '@') 114 parameter(); 115 switch (next()) { 116 case '\0': 117 return NULL; 118 case '{': 119 return makedictionary(); 120 case '[': 121 return makearray(); 122 case '\'': 123 return makestring(); 124 case '%': 125 return makeformat(); 126 case '#': 127 return makespecial(); 128 case ']': 129 case '}': 130 return NULL; // error 131 default: 132 if (isdigit(*format) || *format == '-') 133 return makenumber(); 134 else if (isalpha(*format)) 135 return makestring(); 136 else { 137 assert(false); 138 return NULL; 139 } 140 } 141} 142 143 144CFTypeRef CFMake::makeformat() 145{ 146 ++format; 147 switch (*format++) { 148 case 'b': // blob (pointer, length) 149 { 150 const void *data = va_arg(args, const void *); 151 size_t length = va_arg(args, size_t); 152 return CFDataCreate(allocator, (const UInt8 *)data, length); 153 } 154 case F_BOOLEAN: // boolean (with int promotion) 155 return va_arg(args, int) ? kCFBooleanTrue : kCFBooleanFalse; 156 case 'd': 157 return makeCFNumber(va_arg(args, int)); 158 case 's': 159 return CFStringCreateWithCString(allocator, va_arg(args, const char *), 160 kCFStringEncodingUTF8); 161 case F_OBJECT: 162 return CFRetain(va_arg(args, CFTypeRef)); 163 case 'u': 164 return makeCFNumber(va_arg(args, unsigned int)); 165 default: 166 assert(false); 167 return NULL; 168 } 169} 170 171 172CFTypeRef CFMake::makespecial() 173{ 174 ++format; 175 switch (*format++) { 176 case 'N': 177 return kCFNull; 178 case 't': 179 case 'T': 180 return kCFBooleanTrue; 181 case 'f': 182 case 'F': 183 return kCFBooleanFalse; 184 default: 185 assert(false); 186 return NULL; 187 } 188} 189 190 191CFTypeRef CFMake::makenumber() 192{ 193 double value = strtod(format, (char **)&format); 194 return CFNumberCreate(allocator, kCFNumberDoubleType, &value); 195} 196 197 198// 199// Embedded strings can either be alphanumeric (only), or delimited with single quotes ''. 200// No escapes are processed within such quotes. If you want arbitrary string values, use %s. 201// 202CFTypeRef CFMake::makestring() 203{ 204 const char *start, *end; 205 if (*format == '\'') { 206 start = ++format; // next quote 207 if (!(end = strchr(format, '\''))) { 208 assert(false); 209 return NULL; 210 } 211 format = end + 1; 212 } else { 213 start = format; 214 for (end = start + 1; isalnum(*end); ++end) ; 215 format = end; 216 } 217 return CFStringCreateWithBytes(allocator, 218 (const UInt8 *)start, end - start, 219 kCFStringEncodingUTF8, false); 220} 221 222 223// 224// Construct a CFDictionary 225// 226CFTypeRef CFMake::makedictionary() 227{ 228 ++format; // next '{' 229 next('!'); // indicates mutable (currently always true) 230 CFMutableDictionaryRef dict; 231 if (next('+')) { // {+%O, => copy dictionary argument, then proceed 232 if (next('%') && next('O')) { 233 CFDictionaryRef source = va_arg(args, CFDictionaryRef); 234 dict = CFDictionaryCreateMutableCopy(allocator, NULL, source); 235 if (next('}')) 236 return dict; 237 } else 238 return NULL; // bad syntax 239 } else 240 dict = CFDictionaryCreateMutable(allocator, 0, 241 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 242 if (add(dict)) 243 return dict; 244 else { 245 CFRelease(dict); 246 return NULL; 247 } 248} 249 250CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict) 251{ 252 while (next() != '}') { 253 CFTypeRef key = make(); 254 if (key == NULL) 255 return NULL; 256 if (!next('=')) { 257 CFRelease(key); 258 return NULL; 259 } 260 if (CFTypeRef value = make()) { 261 CFDictionaryAddValue(dict, key, value); 262 CFRelease(key); 263 CFRelease(value); 264 } else { 265 CFRelease(key); 266 return NULL; 267 } 268 } 269 ++format; 270 return dict; 271} 272 273 274CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict) 275{ 276 if (next('{')) 277 return add(dict); 278 else { 279 assert(false); 280 return NULL; 281 } 282} 283 284 285// 286// Construct a CFArray 287// 288CFTypeRef CFMake::makearray() 289{ 290 ++format; // next '[' 291 next('!'); // indicates mutable (currently always) 292 CFMutableArrayRef array = makeCFMutableArray(0); 293 while (next() != ']') { 294 CFTypeRef value = make(); 295 if (value == NULL) { 296 CFRelease(array); 297 return NULL; 298 } 299 CFArrayAppendValue(array, value); 300 CFRelease(value); 301 } 302 ++format; 303 return array; 304} 305 306 307// 308// A CFScan processes its format by parsing through an existing CF object 309// structure, matching and extracting values as directed. Note that CFScan 310// is a structure (tree) scanner rather than a linear parser, and will happily 311// parse out a subset of the input object graph. 312// 313class CFScan : public CFMake { 314public: 315 CFScan(const char *format, va_list args) 316 : CFMake(format, args), suppress(false) { } 317 318 bool scan(CFTypeRef obj); 319 CFTypeRef dictpath(CFTypeRef obj); 320 321protected: 322 bool scandictionary(CFDictionaryRef obj); 323 bool scanarray(CFArrayRef obj); 324 bool scanformat(CFTypeRef obj); 325 326 enum Typescan { fail = -1, more = 0, done = 1 }; 327 Typescan typescan(CFTypeRef obj, CFTypeID type); 328 329 template <class Value> 330 bool scannumber(CFTypeRef obj); 331 332 template <class Type> 333 void store(Type value); 334 335 bool suppress; // output suppression 336}; 337 338 339// 340// Master scan function 341// 342bool CFScan::scan(CFTypeRef obj) 343{ 344 while (next() == '@') 345 parameter(); 346 switch (next()) { 347 case '\0': 348 return true; // done, okay 349 case '{': 350 if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID()) 351 return false; 352 return scandictionary(CFDictionaryRef(obj)); 353 case '[': 354 if (obj && CFGetTypeID(obj) != CFArrayGetTypeID()) 355 return false; 356 return scanarray(CFArrayRef(obj)); 357 case '%': // return this value in some form 358 return scanformat(obj); 359 case '=': // match value 360 { 361 ++format; 362 CFTypeRef match = make(); 363 bool rc = CFEqual(obj, match); 364 CFRelease(match); 365 return rc; 366 } 367 case ']': 368 case '}': 369 assert(false); // unexpected 370 return false; 371 default: 372 assert(false); 373 return false; 374 } 375} 376 377 378// 379// Primitive type-match helper. 380// Ensures the object has the CF runtime type required, and processes 381// the %?o format (return CFTypeRef) and %?n format (ignore value). 382// 383CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type) 384{ 385 if (obj && CFGetTypeID(obj) != type) 386 return fail; 387 switch (*++format) { 388 case F_OBJECT: // return CFTypeRef 389 ++format; 390 store<CFTypeRef>(obj); 391 return done; 392 case 'n': // suppress assignment 393 ++format; 394 return done; 395 default: 396 return more; 397 } 398} 399 400 401// 402// Store a value into the next varargs slot, unless output suppression is on. 403// 404template <class Type> 405void CFScan::store(Type value) 406{ 407 if (!suppress) 408 *va_arg(args, Type *) = value; 409} 410 411 412// 413// Convert a CFNumber to an external numeric form 414// 415template <class Value> 416bool CFScan::scannumber(CFTypeRef obj) 417{ 418 ++format; // consume format code 419 if (!obj) 420 return true; // suppressed, okay 421 if (CFGetTypeID(obj) != CFNumberGetTypeID()) 422 return false; 423 store<Value>(cfNumber<Value>(CFNumberRef(obj))); 424 return true; 425} 426 427 428// 429// Process % scan forms. 430// This delivers the object value, scanf-style, somehow. 431// 432bool CFScan::scanformat(CFTypeRef obj) 433{ 434 switch (*++format) { 435 case F_OBJECT: 436 store<CFTypeRef>(obj); 437 return true; 438 case F_ARRAY: // %a* 439 return typescan(obj, CFArrayGetTypeID()) == done; 440 case F_BOOLEAN: 441 if (Typescan rc = typescan(obj, CFBooleanGetTypeID())) 442 return rc == done; 443 switch (*format) { 444 case 'f': // %Bf - two arguments (value, &variable) 445 { 446 unsigned flag = va_arg(args, unsigned); 447 unsigned *value = va_arg(args, unsigned *); 448 if (obj == kCFBooleanTrue && !suppress) 449 *value |= flag; 450 return true; 451 } 452 default: // %b - CFBoolean as int boolean 453 store<int>(obj == kCFBooleanTrue); 454 return true; 455 } 456 case F_DICTIONARY: 457 return typescan(obj, CFDictionaryGetTypeID()) == done; 458 case 'd': // %d - int 459 return scannumber<int>(obj); 460 case F_NUMBER: 461 return typescan(obj, CFNumberGetTypeID()) == done; 462 case F_STRING: 463 case 's': 464 if (Typescan rc = typescan(obj, CFStringGetTypeID())) 465 return rc == done; 466 // %s 467 store<std::string>(cfString(CFStringRef(obj))); 468 return true; 469 case 'u': 470 return scannumber<unsigned int>(obj); 471 case F_DATA: 472 return typescan(obj, CFDataGetTypeID()) == done; 473 default: 474 assert(false); 475 return false; 476 } 477} 478 479 480bool CFScan::scandictionary(CFDictionaryRef obj) 481{ 482 ++format; // skip '{' 483 while (next() != '}') { 484 bool optional = next('?'); 485 if (CFTypeRef key = make()) { 486 bool oldSuppress = suppress; 487 CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL; 488 if (elem || optional) { 489 suppress |= (elem == NULL); 490 if (next('=')) { 491 if (scan(elem)) { 492 suppress = oldSuppress; // restore 493 CFRelease(key); 494 continue; 495 } 496 } 497 } 498 CFRelease(key); 499 return false; 500 } else { 501 assert(false); // bad format 502 return false; 503 } 504 } 505 return true; 506} 507 508 509bool CFScan::scanarray(CFArrayRef obj) 510{ 511 ++format; // skip '[' 512 CFIndex length = CFArrayGetCount(obj); 513 for (int pos = 0; pos < length; ++pos) { 514 if (next() == ']') 515 return true; 516 if (!scan(CFArrayGetValueAtIndex(obj, pos))) 517 return false; 518 } 519 return false; // array length exceeded 520} 521 522 523// 524// Run down a "dictionary path", validating heavily. 525// 526CFTypeRef CFScan::dictpath(CFTypeRef obj) 527{ 528 while (next()) { // while we've got more text 529 next('.'); // optional 530 if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID()) 531 return NULL; 532 CFTypeRef key = make(); 533 obj = CFDictionaryGetValue(CFDictionaryRef(obj), key); 534 CFRelease(key); 535 } 536 return obj; 537} 538 539 540// 541// The public functions 542// 543CFTypeRef cfmake(const char *format, ...) 544{ 545 va_list args; 546 va_start(args, format); 547 CFTypeRef result = CFMake(format, args).make(); 548 va_end(args); 549 return result; 550} 551 552CFTypeRef vcfmake(const char *format, va_list args) 553{ 554 return CFMake(format, args).make(); 555} 556 557CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...) 558{ 559 va_list args; 560 va_start(args, format); 561 CFDictionaryRef result = CFMake(format, args).addto(dict); 562 va_end(args); 563 return result; 564} 565 566 567bool cfscan(CFTypeRef obj, const char *format, ...) 568{ 569 va_list args; 570 va_start(args, format); 571 bool result = vcfscan(obj, format, args); 572 va_end(args); 573 return result; 574} 575 576bool vcfscan(CFTypeRef obj, const char *format, va_list args) 577{ 578 return CFScan(format, args).scan(obj); 579} 580 581 582CFTypeRef cfget(CFTypeRef obj, const char *format, ...) 583{ 584 va_list args; 585 va_start(args, format); 586 CFTypeRef result = vcfget(obj, format, args); 587 va_end(args); 588 return result; 589} 590 591CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args) 592{ 593 return CFScan(format, args).dictpath(obj); 594} 595 596} // end namespace Security 597