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_dump - codesign dump/display operation 26// 27#include "codesign.h" 28#include <Security/SecKeychain.h> 29#include "cs_utils.h" 30#include <cmath> 31#include <getopt.h> 32 33using namespace UnixPlusPlus; 34 35 36// 37// Operational mode 38// 39enum Operation { 40 doNothing, // none given (print usage) 41 doSign, // sign code 42 doVerify, // verify code 43 doDump, // dump/display signature 44 doHostingInfo, // build and display hosting chain 45 doProcInfo, // process state information 46 doProcAction // process state manipulation 47}; 48Operation operation = doNothing; 49 50 51// 52// Command-line arguments and options 53// 54size_t pagesize = pagesizeUnspecified; // signing page size (-1 => not specified) 55SecIdentityRef signer = NULL; // signer identity 56SecKeychainRef keychain = NULL; // source keychain for signer identity 57const char *internalReq = NULL; // internal requirement (raw optarg) 58const char *testReq = NULL; // external requirement (raw optarg) 59const char *detached = NULL; // detached signature path (to explicit file) 60const char *detachedDb = NULL; // reference to detached signature database 61const char *entitlements = NULL; // path to entitlement configuration input 62const char *resourceRules = NULL; // explicit resource rules template 63const char *uniqueIdentifier = NULL; // unique ident hash 64const char *identifierPrefix = NULL; // prefix for un-dotted default identifiers 65const char *teamID = NULL; // TeamID 66const char *modifiedFiles = NULL; // file to receive list of modified files 67const char *extractCerts = NULL; // location for extracting signing chain certificates 68const char *sdkRoot = NULL; // alternate root for looking up sub-components 69const char *featureCheck = NULL; // feature support check 70SecCSFlags staticVerifyOptions = kSecCSCheckAllArchitectures | kSecCSStrictValidate; // option flags to static verifications 71SecCSFlags dynamicVerifyOptions = kSecCSDefaultFlags; // option flags to dynamic verifications 72SecCSFlags signOptions = kSecCSSignStrictPreflight; // option flags to signing operations 73uint32_t digestAlgorithm = 0; // digest algorithm to be used when signing 74CFDateRef signingTime; // explicit signing time option 75size_t signatureSize = 0; // override CMS blob estimate 76uint32_t cdFlags = 0; // CodeDirectory flags requested 77const char *procAction = NULL; // action-on-process(es) requested 78Architecture architecture; // specific binary architecture to process (from a universal file) 79const char *bundleVersion; // specific version string requested (from a versioned bundle) 80bool noMachO = false; // force non-MachO operation 81bool dryrun = false; // do not actually change anything 82bool allArchitectures = false; // process all architectures in a universal (aka fat) code file 83bool nested = false; // nested code processing (--deep) 84uint32_t preserveMetadata = 0; // what metadata to keep from previous signature 85CFBooleanRef timestampRequest = NULL; // timestamp service request 86bool noTSAcerts = false; // Don't request certificates with ts request 87const char *tsaURL = NULL; // TimeStamping Authority URL 88const char *dumpBinary = NULL; // dump a binary image of the CodeDirectory to disk 89 90 91// 92// Feature set 93// 94static const char *features[] = { 95 "hash-identities", // supports -s hash-of-certificate 96 "identity-preferences", // supports -s identity-preference-name 97 "deep-verify", // supports --deep-verify 98 "numeric-errors", // supports --numeric-errors 99 "deep-signing", // supports "deep" (recursive) signing 100 NULL // sentinel 101}; 102 103 104// 105// Local functions 106// 107static void usage(); 108static OSStatus keychain_open(const char *name, SecKeychainRef &keychain); 109static void chooseArchitecture(const char *arg); 110static uint32_t parseMetadataFlags(const char *arg); 111static void checkFeatures(const char *arg); 112static bool checkTeamId(const char *teamID); 113 114static const int TEAM_ID_MAX = 100; 115 116 117// 118// Command-line options 119// 120enum { 121 optNone = 0, // null (no, absent) option 122 optAllArchitectures, 123 optBundleVersion, 124 optCheckExpiration, 125 optCheckRevocation, 126 optCodeDirectory, 127 optContinue, 128 optDeep, 129 optDetachedDatabase, 130 optDigestAlgorithm, 131 optDryRun, 132 optExtractCerts, 133 optEntitlements, 134 optFeatures, 135 optFMJ, 136 optFileList, 137 optIdentifierPrefix, 138 optIgnoreResources, 139 optKeychain, 140 optLegacy, 141 optNoLegacy, 142 optNoMachO, 143 optNumeric, 144 optPreserveMetadata, 145 optProcInfo, 146 optProcAction, 147 optRemoveSignature, 148 optResourceRules, 149 optSDKRoot, 150 optSigningTime, 151 optSignatureSize, 152 optTimestamp, 153 optTSANoCerts, 154 optTeamID, 155 optNoStrict, 156 optSealRoot, 157}; 158 159const struct option options[] = { 160 { "architecture", required_argument, NULL, 'a' }, 161 { "dump", no_argument, NULL, 'd' }, 162 { "display", no_argument, NULL, 'd' }, 163 { "detached", required_argument, NULL, 'D' }, 164 { "force", no_argument, NULL, 'f' }, 165 { "help", no_argument, NULL, '?' }, 166 { "hosting", no_argument, NULL, 'h' }, 167 { "identifier", required_argument, NULL, 'i' }, 168 { "options", required_argument, NULL, 'o' }, 169 { "pagesize", required_argument, NULL, 'P' }, 170 { "requirements", required_argument, NULL, 'r' }, 171 { "test-requirement", required_argument,NULL, 'R' }, 172 { "sign", required_argument, NULL, 's' }, 173 { "verbose", optional_argument, NULL, 'v' }, 174 { "verify", no_argument, NULL, 'v' }, 175 176 { "all-architectures", no_argument, NULL, optAllArchitectures }, 177 { "bundle-version", required_argument, NULL, optBundleVersion }, 178 { "check-expiration", no_argument, NULL, optCheckExpiration }, 179 { "check-revocation", no_argument, NULL, optCheckRevocation }, 180 { "codedirectory", required_argument, NULL, optCodeDirectory }, 181 { "continue", no_argument, NULL, optContinue }, 182 { "deep", no_argument, NULL, optDeep }, 183 { "deep-verify", no_argument, NULL, optDeep }, // legacy 184 { "detached-database", optional_argument, NULL, optDetachedDatabase }, 185 { "digest-algorithm", required_argument, NULL, optDigestAlgorithm }, 186 { "dryrun", no_argument, NULL, optDryRun }, 187 { "entitlements", required_argument, NULL, optEntitlements }, 188 { "expired", no_argument, NULL, optCheckExpiration }, 189 { "extract-certificates", optional_argument, NULL, optExtractCerts }, 190 { "features", optional_argument, NULL, optFeatures }, 191 { "file-list", required_argument, NULL, optFileList }, 192 { "full-metal-jacket", no_argument, NULL, optFMJ }, 193 { "ignore-resources", no_argument, NULL, optIgnoreResources }, 194 { "keychain", required_argument, NULL, optKeychain }, 195 { "legacy-signing", no_argument, NULL, optLegacy }, 196 { "no-legacy-signing", no_argument, NULL, optNoLegacy }, 197 { "no-macho", no_argument, NULL, optNoMachO }, 198 { "numeric-errors", no_argument, NULL, optNumeric }, 199 { "team-identifier", required_argument, NULL, optTeamID }, 200 { "prefix", required_argument, NULL, optIdentifierPrefix }, 201 { "preserve-metadata", optional_argument, NULL, optPreserveMetadata }, 202 { "procaction", required_argument, NULL, optProcAction }, 203 { "procinfo", no_argument, NULL, optProcInfo }, 204 { "remove-signature", no_argument, NULL, optRemoveSignature }, 205 { "resource-rules", required_argument, NULL, optResourceRules }, 206 { "revoked", no_argument, NULL, optCheckRevocation }, 207 { "sdkroot", required_argument, NULL, optSDKRoot }, 208 { "signature-size", required_argument, NULL, optSignatureSize }, 209 { "signing-time", required_argument, NULL, optSigningTime }, 210 { "timestamp", optional_argument, NULL, optTimestamp }, 211 { "no-tsa-certs", no_argument, NULL, optTSANoCerts }, 212 { "no-strict", no_argument, NULL, optNoStrict }, 213 { "seal-root", no_argument, NULL, optSealRoot }, 214 { } 215}; 216 217 218// 219// codesign [options] bundle-path 220// 221int main(int argc, char *argv[]) 222{ 223 try { 224 const char *signerName = NULL; 225 int arg, argslot; 226 while (argslot = -1, 227 (arg = getopt_long(argc, argv, "a:dD:fhi:o:P:r:R:s:v", options, &argslot)) != -1) 228 switch (arg) { 229 case 'a': 230 chooseArchitecture(optarg); 231 staticVerifyOptions &= ~kSecCSCheckAllArchitectures; 232 break; 233 case 'd': 234 operation = doDump; 235 break; 236 case 'D': 237 detached = optarg; 238 break; 239 case 'f': 240 force = true; 241 break; 242 case 'h': 243 operation = doHostingInfo; 244 break; 245 case 'i': 246 uniqueIdentifier = optarg; 247 break; 248 case 'o': 249 cdFlags = parseCdFlags(optarg); 250 break; 251 case 'P': 252 { 253 if ((pagesize = atol(optarg))) { 254 int pslog; 255 if (frexp(pagesize, &pslog) != 0.5) 256 fail("page size must be a power of two"); 257 } 258 break; 259 } 260 case 'r': 261 internalReq = optarg; 262 break; 263 case 'R': 264 testReq = optarg; 265 break; 266 case 's': 267 signerName = optarg; 268 operation = doSign; 269 break; 270 case 'v': 271 if (argslot < 0) // -v 272 verbose++; 273 else if (options[argslot].has_arg == no_argument) 274 operation = doVerify; // --verify 275 else if (optarg) 276 verbose = atoi(optarg); // --verbose=level 277 else 278 verbose++; // --verbose 279 break; 280 281 case optAllArchitectures: 282 allArchitectures = true; 283 staticVerifyOptions |= kSecCSCheckAllArchitectures; 284 break; 285 case optBundleVersion: 286 bundleVersion = optarg; 287 break; 288 case optCheckExpiration: 289 staticVerifyOptions |= kSecCSConsiderExpiration; 290 dynamicVerifyOptions |= kSecCSConsiderExpiration; 291 break; 292 case optCheckRevocation: 293 staticVerifyOptions |= kSecCSEnforceRevocationChecks; 294 dynamicVerifyOptions |= kSecCSEnforceRevocationChecks; 295 break; 296 case optContinue: 297 continueOnError = true; 298 break; 299 case optDeep: 300 nested = true; 301 signOptions |= kSecCSSignNestedCode; 302 staticVerifyOptions |= kSecCSCheckNestedCode; 303 break; 304 case optDetachedDatabase: 305 if (optarg) 306 detachedDb = optarg; 307 else 308 detachedDb = "system"; 309 break; 310 case optDigestAlgorithm: 311 digestAlgorithm = findHashType(optarg)->code; 312 break; 313 case optDryRun: 314 dryrun = true; 315 break; 316 case optCodeDirectory: 317 dumpBinary = optarg; 318 break; 319 case optEntitlements: 320 entitlements = optarg; 321 break; 322 case optExtractCerts: 323 if (optarg) 324 extractCerts = optarg; 325 else 326 extractCerts = "./codesign"; 327 break; 328 case optFeatures: 329 if (optarg) 330 featureCheck = optarg; 331 else { 332 for (const char **p = features; *p; p++) 333 printf("%s\n", *p); 334 exit(0); 335 } 336 break; 337 case optFileList: 338 modifiedFiles = optarg; 339 break; 340 case optTeamID: 341 if (checkTeamId(optarg)) 342 teamID = optarg; 343 else 344 fail("TeamIdentifier must be at least 1 and no more than %d alphanumeric characters", TEAM_ID_MAX); 345 break; 346 case optIdentifierPrefix: 347 identifierPrefix = optarg; 348 break; 349 case optIgnoreResources: 350 staticVerifyOptions |= kSecCSDoNotValidateResources; 351 break; 352 case optKeychain: 353 MacOSError::check(keychain_open(optarg, keychain)); 354 break; 355 case optLegacy: 356 signOptions |= kSecCSSignV1; 357 break; 358 case optFMJ: 359 signOptions |= kSecCSSignOpaque; // no need for V2 signature for FMJ 360 break; 361 case optNoLegacy: 362 signOptions |= kSecCSSignNoV1; 363 break; 364 case optNoMachO: 365 noMachO = true; 366 break; 367 case optNumeric: 368 numericErrors = true; 369 break; 370 case optTimestamp: 371 if (optarg && !strcmp(optarg, "none")) { // explicit defeat 372 timestampRequest = kCFBooleanFalse; 373 } else { 374 timestampRequest = kCFBooleanTrue; 375 tsaURL = optarg; 376 } 377 break; 378 case optTSANoCerts: 379 noTSAcerts = true; 380 break; 381 case optPreserveMetadata: 382 preserveMetadata = parseMetadataFlags(optarg); 383 break; 384 case optProcAction: 385 operation = doProcAction; 386 procAction = optarg; 387 break; 388 case optProcInfo: 389 operation = doProcInfo; 390 break; 391 case optRemoveSignature: 392 signerName = NULL; 393 operation = doSign; // well, un-sign 394 break; 395 case optResourceRules: 396 resourceRules = optarg; 397 note(0, "Warning: --resource-rules has been deprecated in Mac OS X >= 10.10!"); 398 break; 399 case optSDKRoot: 400 sdkRoot = optarg; 401 break; 402 case optSignatureSize: 403 signatureSize = atol(optarg); 404 break; 405 case optSigningTime: 406 signingTime = parseDate(optarg); 407 break; 408 case optNoStrict: 409 signOptions &= ~kSecCSSignStrictPreflight; 410 staticVerifyOptions &= ~kSecCSStrictValidate; 411 break; 412 case optSealRoot: 413 signOptions |= kSecCSSignBundleRoot; 414 break; 415 416 case '?': 417 usage(); 418 } 419 420 if (signerName) 421 signer = findIdentity(keychain, signerName); 422 } catch (...) { 423 diagnose(NULL, exitFailure); 424 } 425 426 // -v does double duty as -v(erbose) and -v(erify) 427 if (operation == doNothing && verbose) { 428 operation = doVerify; 429 verbose--; 430 } 431 if (featureCheck) { 432 checkFeatures(featureCheck); 433 if (operation == doNothing) 434 exit(0); 435 } 436 if (operation == doNothing || optind == argc) 437 usage(); 438 439 // masticate the more interesting arguments 440 try { 441 switch (operation) { 442 case doSign: 443 prepareToSign(); 444 break; 445 case doVerify: 446 prepareToVerify(); 447 break; 448 default: 449 break; 450 } 451 } catch (...) { 452 diagnose(NULL, exitFailure); 453 } 454 455 // operate on paths given after options 456 for ( ; optind < argc; optind++) { 457 const char *target = argv[optind]; 458 try { 459 switch (operation) { 460 case doSign: 461 sign(target); 462 break; 463 case doVerify: 464 verify(target); 465 break; 466 case doDump: 467 dump(target); 468 break; 469 case doHostingInfo: 470 hostinginfo(target); 471 break; 472 case doProcInfo: 473 procinfo(target); 474 break; 475 case doProcAction: 476 procaction(target); 477 break; 478 default: 479 break; 480 } 481 } catch (...) { 482 diagnose(target); 483 if (!exitcode) 484 exitcode = exitFailure; 485 if (!continueOnError) 486 exit(exitFailure); 487 } 488 } 489 490 exit(exitcode); 491} 492 493void usage() 494{ 495 fprintf(stderr, "Usage: codesign -s identity [-fv*] [-o flags] [-r reqs] [-i ident] path ... # sign\n" 496 " codesign -v [-v*] [-R testreq] path|[+]pid ... # verify\n" 497 " codesign -d [options] path ... # display contents\n" 498 " codesign -h pid ... # display hosting paths\n" 499 ); 500 exit(exitUsage); 501} 502 503OSStatus 504keychain_open(const char *name, SecKeychainRef &keychain) 505{ 506 OSStatus result; 507 508 if (name && name[0] != '/') 509 { 510 CFArrayRef dynamic = NULL; 511 result = SecKeychainCopyDomainSearchList( 512 kSecPreferencesDomainDynamic, &dynamic); 513 if (result) 514 return result; 515 else 516 { 517 uint32_t i; 518 uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0; 519 520 for (i = 0; i < count; ++i) 521 { 522 char pathName[PATH_MAX]; 523 UInt32 ioPathLength = sizeof(pathName); 524 bzero(pathName, ioPathLength); 525 keychain = (SecKeychainRef)CFArrayGetValueAtIndex(dynamic, i); 526 result = SecKeychainGetPath(keychain, &ioPathLength, pathName); 527 if (result) 528 return result; 529 530 if (!strncmp(pathName, name, ioPathLength)) 531 { 532 CFRetain(keychain); 533 CFRelease(dynamic); 534 return noErr; 535 } 536 } 537 CFRelease(dynamic); 538 } 539 } 540 541 return SecKeychainOpen(name, &keychain); 542} 543 544static bool checkTeamId(const char *teamID) 545{ 546 if (!teamID) 547 return false; 548 549 size_t id_len = strnlen(teamID, TEAM_ID_MAX+1); 550 if (id_len > TEAM_ID_MAX || id_len < 1) 551 return false; 552 553 for (size_t i = 0; i < id_len; i++) { 554 if (!isalnum(teamID[i])) 555 return false; 556 } 557 return true; 558} 559 560void chooseArchitecture(const char *arg) 561{ 562 int arch, subarch; 563 switch (sscanf(arg, "%d,%d", &arch, &subarch)) { 564 case 0: // not a number 565 if (!(architecture = Architecture(arg))) 566 fail("%s: unknown architecture name", arg); 567 break; 568 case 1: 569 architecture = Architecture(arch); 570 break; 571 case 2: 572 architecture = Architecture(arch, subarch); 573 break; 574 } 575} 576 577 578static uint32_t parseMetadataFlags(const char *arg) 579{ 580 static const SecCodeDirectoryFlagTable metadataFlags[] = { 581 { "identifier", kSecCodeSignerPreserveIdentifier, true }, 582 { "requirements", kSecCodeSignerPreserveRequirements, true }, 583 { "entitlements", kSecCodeSignerPreserveEntitlements, true }, 584 { "resource-rules", kSecCodeSignerPreserveResourceRules,true }, 585 { "flags", kSecCodeSignerPreserveFlags, true }, 586 { "team-identifier", kSecCodeSignerPreserveTeamIdentifier, true}, 587 { NULL } 588 }; 589 uint32_t flags; 590 if (arg == NULL) { // --preserve-metadata compatibility default 591 flags = kSecCodeSignerPreserveRequirements | kSecCodeSignerPreserveEntitlements | kSecCodeSignerPreserveResourceRules | kSecCodeSignerPreserveFlags; 592 if (!getenv("RC_XBS") || getenv("RC_BUILDIT")) // if we're NOT in real B&I... 593 flags |= kSecCodeSignerPreserveIdentifier; // ... then preserve identifier too 594 note(0, "Warning: default usage of --preserve-metadata implies \"resource-rules\" (deprecated in Mac OS X >= 10.10)!"); 595 } else { 596 flags = parseOptionTable(arg, metadataFlags); 597 if (flags & kSecCodeSignerPreserveResourceRules) { 598 note(0, "Warning: usage of --preserve-metadata with option \"resource-rules\" (deprecated in Mac OS X >= 10.10)!", 599 options[optPreserveMetadata].name, options[optResourceRules]); 600 } 601 } 602 603 return flags; 604} 605 606 607// 608// Exit unless each of the comma-separated feature names is supported 609// by this version of codesign(1). 610// 611void checkFeatures(const char *arg) 612{ 613 while (true) { 614 const char *comma = strchr(arg, ','); 615 string feature = comma ? string(arg, comma-arg) : arg; 616 if (feature.empty()) 617 fail("Invalid feature name"); 618 const char **p; 619 for (p = features; *p && feature != *p; p++) ; 620 if (!*p) 621 fail("%s: not supported in this version", feature.c_str()); 622 if (comma) { 623 arg = comma + 1; 624 if (!*arg) // tolerate trailing comma 625 break; 626 } else { 627 break; 628 } 629 } 630} 631