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