1/* 2 * Copyright (c) 2006-2010 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// cs_utils - shared utilities for CodeSigning tool commands 26// 27#include "codesign.h" 28#include <Security/CodeSigning.h> 29#include <Security/SecCertificatePriv.h> 30#include <Security/CSCommonPriv.h> 31#include <Security/SecIdentitySearchPriv.h> 32#include <Security/SecPolicyPriv.h> 33#include <security_utilities/cfutilities.h> 34#include <security_utilities/cfmunge.h> 35#include <security_codesigning/reqdumper.h> 36#include <security_codesigning/cdbuilder.h> 37#include <security_codesigning/reqparser.h> 38#include <Security/CMSEncoder.h> 39#include <cstdio> 40#include <cmath> 41#include <getopt.h> 42#include <sys/codesign.h> 43#include <sys/param.h> // MAXPATHLEN 44 45using namespace UnixPlusPlus; 46 47 48// 49// Shared command-line arguments and options 50// 51unsigned verbose = 0; // verbosity level 52bool force = false; // force overwrite flag 53bool continueOnError = false; // continue processing targets on error(s) 54bool numericErrors = false; // display errors as numbers (for mechanized callers) 55 56int exitcode = exitSuccess; // cumulative exit code 57 58 59// 60// Convert between hash code numbers and human-readable form. 61// We accept unambiguous prefix strings for conversion. 62// 63static const HashType hashTypes[] = { 64 { "sha1", kSecCodeSignatureHashSHA1, SHA1::digestLength }, 65 { "sha256", kSecCodeSignatureHashSHA256, 256 / 8 }, 66 { NULL } 67}; 68 69const HashType *findHashType(const char *hashName) 70{ 71 size_t length = strlen(hashName); 72 const HashType *match = NULL; 73 for (const HashType *h = hashTypes; h->name; h++) { 74 if (!strncmp(hashName, h->name, length)) { // prefix match 75 if (match) { 76 fail("%s: ambiguous hash specification (%s or %s)", 77 hashName, match->name, h->name); 78 } else 79 match = h; 80 } 81 } 82 if (match) 83 return match; 84 fail("%s: unknown hash specification", hashName); 85} 86 87const HashType *findHashType(uint32_t hashCode) 88{ 89 for (const HashType *h = hashTypes; h->name; h++) 90 if (h->code == hashCode) 91 return h; 92 return NULL; 93} 94 95 96// 97// Build requirements data from outside sources. 98// This automatically recognizes binary Requirement(s) blobs, on the 99// assumption that the high byte of their magic is not a valid 100// (first) character of a text Requirements syntax. The assumption is 101// that they all share the same first-byte prefix (0xfa, for the 0xfade0cxx 102// convention used for code signing magic numbers). 103// 104CFTypeRef readRequirement(const string &source, SecCSFlags flags) 105{ 106 CFTypeRef result; 107 ErrorCheck check; 108 if (source[0] == '=') { // =text 109 if (flags) 110 check(SecRequirementsCreateWithString(CFTempString(source.substr(1)), flags, &result, check)); 111 else 112 result = makeCFString(source.substr(1)); 113 return result; 114 } 115 FILE *f; 116 if (source == "-") { 117 f = stdin; 118 } else if (!(f = fopen(source.c_str(), "r"))) { 119 perror(source.c_str()); 120 fail("invalid requirement specification"); 121 } 122 int first = getc(f); 123 ungetc(first, f); 124 if (first == kSecCodeMagicByte) { // binary blob 125 BlobCore *blob = BlobCore::readBlob(f); 126 switch (blob->magic()) { 127 case kSecCodeMagicRequirement: 128 MacOSError::check(SecRequirementCreateWithData(CFTempData(*blob), kSecCSDefaultFlags, (SecRequirementRef *)&result)); 129 break; 130 case kSecCodeMagicRequirementSet: 131 result = makeCFData(*blob); 132 break; 133 default: 134 fail((source + ": not a recognized requirement file").c_str()); 135 } 136 ::free(blob); 137 } else { // presumably text 138 char buffer[10240]; // arbitrary size 139 size_t length = fread(buffer, 1, sizeof(buffer) - 1, f); 140 buffer[length] = '\0'; 141 if (flags) 142 check(SecRequirementsCreateWithString(CFTempString(buffer), flags, &result, check)); 143 else 144 result = makeCFString(buffer); 145 } 146 if (f != stdin) 147 fclose(f); 148 return result; 149} 150 151 152// 153// ErrorCheck 154// 155void ErrorCheck::throwError() 156{ 157 assert(mError); 158 throw Error(mError); 159} 160 161 162// 163// Given a keychain or type of keychain item, return the string form of the path 164// of the keychain containing it. 165// 166string keychainPath(CFTypeRef whatever) 167{ 168 CFRef<SecKeychainRef> keychain; 169 if (CFGetTypeID(whatever) == SecKeychainGetTypeID()) { 170 keychain = SecKeychainRef(whatever); 171 } else if (CFGetTypeID(whatever) == SecIdentityGetTypeID()) { 172 CFRef<SecCertificateRef> cert; 173 MacOSError::check(SecIdentityCopyCertificate(SecIdentityRef(whatever), &cert.aref())); 174 MacOSError::check(SecKeychainItemCopyKeychain(cert.as<SecKeychainItemRef>(), &keychain.aref())); 175 } else { // assume keychain item 176 switch (OSStatus rc = SecKeychainItemCopyKeychain(SecKeychainItemRef(whatever), &keychain.aref())) { 177 case noErr: 178 break; 179 case errSecNoSuchKeychain: 180 return "(nowhere)"; 181 default: 182 MacOSError::throwMe(rc); 183 } 184 } 185 char path[MAXPATHLEN]; 186 UInt32 length = sizeof(path); 187 MacOSError::check(SecKeychainGetPath(keychain, &length, path)); 188 return path; 189} 190 191 192// 193// Locate a signing identity from the keychain search list. 194// Exit with error diagnosed if we can't find exactly one match in the user's 195// keychain environment. 196// 197static SecIdentityRef findIdentity(SecKeychainRef keychain, const char *match, SecPolicyRef policy); 198 199SecIdentityRef findIdentity(SecKeychainRef keychain, const char *match) 200{ 201 // the special value "-" (dash) indicates ad-hoc signing 202 if (!strcmp(match, "-")) 203 return SecIdentityRef(kCFNull); 204 205 // interpret the match as a (full) identity preference name 206 CFRef<SecIdentityRef> preference; 207 switch (OSStatus rc = SecIdentityCopyPreference(CFTempString(match), 0, NULL, &preference.aref())) { 208 case noErr: 209 if (preference) 210 return preference.yield(); 211 break; 212 case errSecItemNotFound: 213 break; 214 default: 215 MacOSError::throwMe(rc); 216 } 217 218 // look for qualified identities 219 CFRef<SecPolicyRef> policy; 220 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, 221 &CSSMOID_APPLE_TP_CODE_SIGNING, &policy.aref())); 222 if (SecIdentityRef identity = findIdentity(keychain, match, policy)) 223 return identity; // good match 224 225 // now try again, using the generic policy 226 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, 227 &CSSMOID_APPLE_X509_BASIC, &policy.aref())); 228 if (SecIdentityRef identity = findIdentity(keychain, match, policy)) { 229 // found a match, but it's not good for Code Signing 230#if !defined(NDEBUG) 231 if (getenv("CODESIGN_ANYCERT")) { 232 note(1, "Using unqualified identity for test - signature will not verify"); 233 return identity; 234 } 235#endif //NDEBUG 236 CFRef<SecCertificateRef> cert; 237 MacOSError::check(SecIdentityCopyCertificate(identity, &cert.aref())); 238 CFRef<CFStringRef> name; 239 MacOSError::check(SecCertificateCopyCommonName(cert, &name.aref())); 240 fail("%s: this identity cannot be used for signing code", cfString(name).c_str()); 241 } 242 fail("%s: no identity found", match); 243} 244 245 246static SecIdentityRef findIdentity(SecKeychainRef keychain, const char *match, SecPolicyRef policy) 247{ 248 CFRef<SecIdentitySearchRef> search; 249 MacOSError::check(SecIdentitySearchCreateWithPolicy(policy, NULL, 250 CSSM_KEYUSE_SIGN, keychain, false, &search.aref())); 251 252 // recognize an exact hex expression of a SHA1 (no abbreviations allowed) 253 CFRef<CFDataRef> certHash; 254 const char hexDigits[] = "0123456789abcdefABCDEF"; 255 if (strlen(match) == 2 * SHA1::digestLength && strspn(match, hexDigits) == 2 * SHA1::digestLength) { 256 SHA1::Digest digest; 257 stringHash(match, digest); 258 certHash = CFDataCreate(NULL, digest, sizeof(digest)); 259 } 260 261 // filter all candidates against our match constraint 262 CFRef<CFStringRef> cfmatch = makeCFString(match); 263 CFRef<SecIdentityRef> bestMatch; 264 CFRef<CFStringRef> bestMatchName; 265 bool exactMatch = false; 266 CSSM_DATA bestMatchData; 267 for (;;) { 268 CFRef<SecIdentityRef> candidate; 269 switch (OSStatus rc = SecIdentitySearchCopyNext(search, &candidate.aref())) { 270 case noErr: 271 { 272 CFRef<SecCertificateRef> cert; 273 MacOSError::check(SecIdentityCopyCertificate(candidate, &cert.aref())); 274 275 // match on certificate hash if that was requested 276 if (certHash) { 277 CFRef<CFDataRef> hash = certificateHash(cert); 278 if (CFEqual(hash, certHash)) 279 return candidate.yield(); 280 } 281 282 // otherwise, match on best CN substring 283 CFRef<CFStringRef> name; 284 CSSM_DATA data; 285 MacOSError::check(SecCertificateCopyCommonName(cert, &name.aref())); 286 MacOSError::check(SecCertificateGetData(cert, &data)); 287 if (!strcmp(match, "*")) { // means "just take the first one" 288 note(1, "Using identity \"%s\"", cfString(name).c_str()); 289 return candidate.yield(); 290 } 291 if (!name) // certificate has no subject common name (can't find it by name) 292 continue; 293 CFRange find = CFStringFind(name, cfmatch, 294 kCFCompareCaseInsensitive | kCFCompareNonliteral); 295 if (find.location == kCFNotFound) 296 continue; // no match 297 bool isExact = find.location == 0 && find.length == CFStringGetLength(name); 298 if (bestMatch) { // got two matches 299 if (exactMatch && !isExact) // prior is better; ignore this one 300 continue; 301 if (exactMatch == isExact) { // same class of match 302 if (bestMatchData.Length == data.Length 303 && !memcmp(bestMatchData.Data, data.Data, data.Length)) { // same cert 304 note(2, "%s: found in both %s and %s (this is all right)", 305 match, keychainPath(bestMatch).c_str(), keychainPath(cert).c_str()); 306 continue; 307 } 308 // ambiguity - fail and try to explain it as well as we can 309 string firstKeychain = keychainPath(bestMatch); 310 string newKeychain = keychainPath(cert); 311 if (firstKeychain == newKeychain) 312 fail("%s: ambiguous (matches \"%s\" and \"%s\" in %s)", 313 match, cfString(name).c_str(), cfString(bestMatchName).c_str(), 314 newKeychain.c_str()); 315 else 316 fail("%s: ambiguous (matches \"%s\" in %s and \"%s\" in %s)", 317 match, cfString(name).c_str(), firstKeychain.c_str(), 318 cfString(bestMatchName).c_str(), newKeychain.c_str()); 319 } 320 } 321 bestMatch = candidate; 322 bestMatchName = name; 323 bestMatchData = data; 324 exactMatch = isExact; 325 break; 326 } 327 case errSecItemNotFound: 328 return bestMatch.yield(); 329 default: 330 MacOSError::check(rc); 331 } 332 } 333} 334 335 336// 337// Parse all forms of the -o/--options argument (CodeDirectory flags) 338// 339uint32_t parseOptionTable(const char *arg, const SecCodeDirectoryFlagTable *options) 340{ 341 uint32_t flags = 0; 342 std::string text = arg; 343 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) { 344 string word = (comma == string::npos) ? text : text.substr(0, comma); 345 const SecCodeDirectoryFlagTable *item; 346 for (item = options; item->name; item++) 347 if (item->signable && !strncmp(word.c_str(), item->name, word.size())) { 348 flags |= item->value; 349 break; 350 } 351 if (!item->name) // not found 352 MacOSError::throwMe(errSecCSInvalidFlags); 353 if (comma == string::npos) // last word 354 break; 355 } 356 return flags; 357} 358 359uint32_t parseCdFlags(const char *arg) 360{ 361 // if it's numeric, Just Do It 362 if (isdigit(arg[0])) { // numeric - any form of number 363 char *remain; 364 uint32_t flags = uint32_t(strtol(arg, &remain, 0)); 365 if (remain[0]) 366 fail("%s: invalid flag(s)", arg); 367 return flags; 368 } else { 369 return parseOptionTable(arg, kSecCodeDirectoryFlagTable); 370 } 371} 372 373 374// 375// Parse a date/time string into CFDateRef form. 376// The special value "none" is translated to cfNull. 377// 378CF_EXPORT CFStringRef const kCFDateFormatterIsLenientKey; 379 380CFDateRef parseDate(const char *string) 381{ 382 if (!string || !strcasecmp(string, "none")) 383 return CFDateRef(kCFNull); 384 CFRef<CFLocaleRef> userLocale = CFLocaleCopyCurrent(); 385 CFRef<CFDateFormatterRef> formatter = CFDateFormatterCreate(NULL, userLocale, 386 kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle); 387 CFDateFormatterSetProperty(formatter, kCFDateFormatterIsLenientKey, kCFBooleanTrue); 388 CFRef<CFDateRef> date = CFDateFormatterCreateDateFromString(NULL, formatter, CFTempString(string), NULL); 389 if (!date) 390 fail("%s: invalid date/time", string); 391 return date.yield(); 392} 393 394 395// 396// Clean up a pathname. 397// 398std::string cleanPath(const char *path) 399{ 400 char answer[PATH_MAX]; 401 if (const char *r = realpath(path, answer)) 402 return r; 403 UnixError::throwMe(); 404} 405 406 407// 408// Convert a SHA-1 hash into a string of hex digits (40 of them, of course) 409// 410std::string hashString(const SHA1::Byte *hash) 411{ 412 char s[2 * SHA1::digestLength + 1]; 413 for (unsigned n = 0; n < SHA1::digestLength; n++) 414 sprintf(&s[2*n], "%2.2x", hash[n]); 415 return s; 416} 417 418std::string hashString(SHA1 &hash) 419{ 420 SHA1::Digest digest; 421 hash.finish(digest); 422 return hashString(digest); 423} 424 425void stringHash(const char *string, SHA1::Digest digest) 426{ 427 for (unsigned n = 0; n < SHA1::digestLength; n++) 428 sscanf(string+2*n, "%2hhx", digest+n); 429} 430 431CFDataRef certificateHash(SecCertificateRef cert) 432{ 433 CFRef<CFDataRef> certData = SecCertificateCopyData(cert); 434 SHA1 hash; 435 hash(CFDataGetBytePtr(certData), CFDataGetLength(certData)); 436 SHA1::Digest digest; 437 hash.finish(digest); 438 return CFDataCreate(NULL, digest, sizeof(digest)); 439} 440 441 442// 443// Diagnostic and messaging support 444// 445void fail(const char *format, ...) 446{ 447 va_list args; 448 va_start(args, format); 449 vfprintf(stderr, format, args); 450 fprintf(stderr, "\n"); 451 va_end(args); 452 if (continueOnError) 453 throw Fail(exitFailure); 454 else 455 exit(exitFailure); 456} 457 458void note(unsigned level, const char *format, ...) 459{ 460 if (verbose >= level) { 461 va_list args; 462 va_start(args, format); 463 vfprintf(stderr, format, args); 464 fprintf(stderr, "\n"); 465 va_end(args); 466 } 467} 468 469 470// 471// Resolve an exception into a readable error message, then exit with error 472// 473static void diagnose1(const char *type, CFTypeRef value); 474static void diagnose1(const char *context, OSStatus rc); 475 476void diagnose(const char *context /* = NULL */, int stop /* = 0 */) 477{ 478 try { 479 throw; 480 } catch (const ErrorCheck::Error &err) { 481 diagnose(context, err.error); 482 } catch (const MacOSError &err) { 483 diagnose1(context, err.osStatus()); 484 } catch (const UnixError &err) { 485 errno = err.error; 486 perror(context); 487 } catch (const Fail &failure) { 488 // source has printed any error information 489 } catch (...) { 490 if (context) 491 fprintf(stderr, "%s: ", context); 492 fprintf(stderr, "unknown exception\n"); 493 } 494 if (stop) 495 exit(stop); 496} 497 498void diagnose(const char *context, CFErrorRef err) 499{ 500 diagnose(context, OSStatus(CFErrorGetCode(err)), CFErrorCopyUserInfo(err)); 501} 502 503static void diagnose1(const char *context, OSStatus rc) 504{ 505 if (numericErrors) 506 printf("%ld\n", long(rc)); 507 if (rc >= errSecErrnoBase && rc < errSecErrnoLimit) { 508 errno = rc - errSecErrnoBase; 509 perror(context); 510 } else 511 cssmPerror(context, rc); 512} 513 514void diagnose(const char *context, OSStatus rc, CFDictionaryRef info) 515{ 516 diagnose1(context, rc); 517 518 if (CFTypeRef path = CFDictionaryGetValue(info, kSecCFErrorPath)) 519 fprintf(stderr, "In subcomponent: %s\n", cfString(CFURLRef(path)).c_str()); 520 521 if (CFTypeRef detail = CFDictionaryGetValue(info, kSecCFErrorArchitecture)) 522 fprintf(stderr, "In architecture: %s\n", cfString(CFStringRef(detail)).c_str()); 523 524 if (CFTypeRef detail = CFDictionaryGetValue(info, kSecCFErrorRequirementSyntax)) 525 fprintf(stderr, "Requirement syntax error(s):\n%s", cfString(CFStringRef(detail)).c_str()); 526 527 if (verbose) { 528 if (CFTypeRef detail = CFDictionaryGetValue(info, kSecCFErrorResourceAdded)) 529 diagnose1("file added", detail); 530 if (CFTypeRef detail = CFDictionaryGetValue(info, kSecCFErrorResourceAltered)) 531 diagnose1("file modified", detail); 532 if (CFTypeRef detail = CFDictionaryGetValue(info, kSecCFErrorResourceMissing)) 533 diagnose1("file missing", detail); 534 } 535} 536 537static void diagnose1(const char *type, CFTypeRef value) 538{ 539 if (CFGetTypeID(value) == CFArrayGetTypeID()) { 540 CFArrayRef array = CFArrayRef(value); 541 CFIndex size = CFArrayGetCount(array); 542 for (CFIndex n = 0; n < size; n++) 543 diagnose1(type, CFArrayGetValueAtIndex(array, n)); 544 } else 545 printf("%s: %s\n", type, cfString(value, noErr).c_str()); 546} 547 548 549// 550// Take an array of CFURLRefs and write it to a file as a list of filesystem paths, 551// one path per line. 552// 553void writeFileList(CFArrayRef list, const char *destination, const char *mode) 554{ 555 FILE *out; 556 if (!strcmp(destination, "-")) { 557 out = stdout; 558 } else if (!(out = fopen(destination, mode))) { 559 perror(destination); 560 exit(1); 561 } 562 CFIndex count = CFArrayGetCount(list); 563 for (CFIndex n = 0; n < count; n++) 564 fprintf(out, "%s\n", cfString(CFURLRef(CFArrayGetValueAtIndex(list, n))).c_str()); 565 if (strcmp(destination, "-")) 566 fclose(out); 567} 568 569 570// 571// Take a CFDictionary and write it to a file as a property list. 572// 573void writeDictionary(CFDictionaryRef dict, const char *destination, const char *mode) 574{ 575 FILE *out; 576 if (!strcmp(destination, "-")) { 577 out = stdout; 578 } else if (!(out = fopen(destination, mode))) { 579 perror(destination); 580 exit(1); 581 } 582 if (dict) { 583 CFRef<CFDataRef> data = makeCFData(dict); 584 fwrite(CFDataGetBytePtr(data), 1, CFDataGetLength(data), out); 585 } 586 if (strcmp(destination, "-")) 587 fclose(out); 588} 589 590 591// 592// Take a CFDictionary and write it to a file as a property list. 593// 594void writeData(CFDataRef data, const char *destination, const char *mode) 595{ 596 FILE *out; 597 if (!strcmp(destination, "-")) { 598 out = stdout; 599 } else if (!(out = fopen(destination, mode))) { 600 perror(destination); 601 exit(1); 602 } 603 if (data) 604 fwrite(CFDataGetBytePtr(data), 1, CFDataGetLength(data), out); 605 if (strcmp(destination, "-")) 606 fclose(out); 607} 608 609 610// 611// Create a static code object, using command context as available 612// 613SecStaticCodeRef staticCodePath(const char *target, const Architecture &arch, const char *version) 614{ 615 CFRef<CFMutableDictionaryRef> attributes; 616 if (arch || version) { 617 attributes = makeCFMutableDictionary(); 618 if (arch) 619 cfadd(attributes, "{%O=%d,%O=%d}", 620 kSecCodeAttributeArchitecture, arch.cpuType(), 621 kSecCodeAttributeSubarchitecture, arch.cpuSubtype());; 622 if (version) 623 cfadd(attributes, "{%O=%s}", kSecCodeAttributeBundleVersion, version); 624 } 625 SecStaticCodeRef code; 626 MacOSError::check(SecStaticCodeCreateWithPathAndAttributes(CFTempURL(cleanPath(target)), kSecCSDefaultFlags, 627 attributes, &code)); 628 return code; 629} 630 631 632// 633// Accept various forms of dynamic target specifications 634// and return a SecCodeRef for the designated code. 635// Returns NULL if the syntax isn't recognized as a dynamic 636// host designation. Fails (calls fail which exits) if the 637// argument looks dynamic but has bad syntax. 638// 639static SecCodeRef descend(SecCodeRef host, CFRef<CFMutableDictionaryRef> attrs, string path); 640static void parsePath(CFMutableDictionaryRef attrs, string form); 641static void parseAttribute(CFMutableDictionaryRef attrs, string form); 642 643SecCodeRef dynamicCodePath(const char *target) 644{ 645 CFRef<CFMutableDictionaryRef> dict = NULL; 646 647 if (target[0] == '+') { 648 dict = cfmake<CFMutableDictionaryRef>("{%O=#T}", kSecGuestAttributeDynamicCode); 649 target++; 650 } else 651 dict = makeCFMutableDictionary(); 652 653 if (!isdigit(target[0])) 654 return NULL; // not a dynamic spec 655 656 char *path; 657 int pid = (int)strtol(target, &path, 10); 658 if (pid == 0) 659 return NULL; 660 661 return descend(NULL, 662 cfmake<CFMutableDictionaryRef>("{+%O,%O=%d}", dict.get(), kSecGuestAttributePid, pid), 663 path); 664} 665 666 667static SecCodeRef descend(SecCodeRef host, CFRef<CFMutableDictionaryRef> attrs, string path) 668{ 669 string::size_type colon = path.find(':'); 670 if (colon == string::npos) // last element 671 parsePath(attrs, path); 672 else 673 parsePath(attrs, path.substr(0, colon)); 674 CFRef<SecCodeRef> guest; 675 MacOSError::check(SecCodeCopyGuestWithAttributes(host, attrs, kSecCSDefaultFlags, &guest.aref())); 676 if (colon == string::npos) 677 return guest.yield(); 678 else 679 return descend(guest, makeCFMutableDictionary(), path.substr(colon + 1)); 680} 681 682 683// generate a guest selector dictionary for an attr=value specification 684static void parsePath(CFMutableDictionaryRef attrs, string form) 685{ 686 for (string::size_type comma = form.find(','); comma != string::npos; comma = form.find(',')) { 687 parseAttribute(attrs, form.substr(0, comma)); 688 form = form.substr(comma + 1); 689 } 690 parseAttribute(attrs, form); 691} 692 693static void parseAttribute(CFMutableDictionaryRef attrs, string form) 694{ 695 if (form.empty()) // nothing to add 696 return; 697 string::size_type eq = form.find('='); 698 CFRef<CFStringRef> key; 699 if (eq == string::npos) { 700 key = kSecGuestAttributeCanonical; 701 } else { 702 key.take(makeCFString(form.substr(0, eq))); 703 form = form.substr(eq + 1); 704 } 705 706 if (isdigit(form[0])) // number 707 CFDictionaryAddValue(attrs, key, CFTempNumber(strtol(form.c_str(), NULL, 0))); 708 else // string 709 CFDictionaryAddValue(attrs, key, CFTempString(form)); 710} 711