#include "codesign.h"
#include <Security/SecKeychain.h>
#include <cmath>
#include <getopt.h>
using namespace CodeSigning;
using namespace UnixPlusPlus;
enum Operation {
doNothing, doSign, doVerify, doDump, doHostingInfo, doProcInfo, doProcAction };
Operation operation = doNothing;
int pagesize = pagesizeUnspecified; SecIdentityRef signer = NULL; SecKeychainRef keychain = NULL; const char *internalReq = NULL; const char *testReq = NULL; const char *detached = NULL; const char *entitlements = NULL; const char *resourceRules = NULL; const char *uniqueIdentifier = NULL; const char *identifierPrefix = NULL; const char *modifiedFiles = NULL; const char *extractCerts = NULL; SecCSFlags verifyOptions = kSecCSDefaultFlags; CFDateRef signingTime; size_t signatureSize = 0; uint32_t cdFlags = 0; const char *procAction = NULL; bool noMachO = false; bool dryrun = false; bool preserveMetadata = false;
static void usage();
enum {
optCheckExpiration = 1,
optContinue,
optDryRun,
optExtractCerts,
optEntitlements,
optFileList,
optIdentifierPrefix,
optIgnoreResources,
optKeychain,
optNoMachO,
optPreserveMetadata,
optProcInfo,
optProcAction,
optRemoveSignature,
optResourceRules,
optSigningTime,
optSignatureSize,
};
const struct option options[] = {
{ "dump", no_argument, NULL, 'd' },
{ "display", no_argument, NULL, 'd' },
{ "detached", required_argument, NULL, 'D' },
{ "force", no_argument, NULL, 'f' },
{ "help", no_argument, NULL, '?' },
{ "hosting", no_argument, NULL, 'h' },
{ "identifier", required_argument, NULL, 'i' },
{ "options", required_argument, NULL, 'o' },
{ "pagesize", required_argument, NULL, 'P' },
{ "requirements", required_argument, NULL, 'r' },
{ "test-requirement", required_argument,NULL, 'R' },
{ "sign", required_argument, NULL, 's' },
{ "verbose", optional_argument, NULL, 'v' },
{ "verify", no_argument, NULL, 'v' },
{ "check-expiration", no_argument, NULL, optCheckExpiration },
{ "continue", no_argument, NULL, optContinue },
{ "dryrun", no_argument, NULL, optDryRun },
{ "entitlements", required_argument, NULL, optEntitlements },
{ "expired", no_argument, NULL, optCheckExpiration },
{ "extract-certificates", optional_argument, NULL, optExtractCerts },
{ "file-list", required_argument, NULL, optFileList },
{ "ignore-resources", no_argument, NULL, optIgnoreResources },
{ "keychain", required_argument, NULL, optKeychain },
{ "no-macho", no_argument, NULL, optNoMachO },
{ "prefix", required_argument, NULL, optIdentifierPrefix },
{ "preserve-metadata", no_argument, NULL, optPreserveMetadata },
{ "procaction", required_argument, NULL, optProcAction },
{ "procinfo", no_argument, NULL, optProcInfo },
{ "resource-rules", required_argument, NULL, optResourceRules },
{ "signature-size", required_argument, NULL, optSignatureSize },
{ "signing-time", required_argument, NULL, optSigningTime },
{ }
};
int main(int argc, char *argv[])
{
try {
const char *signerName = NULL;
int arg, argslot;
while (argslot = -1,
(arg = getopt_long(argc, argv, "dD:fhi:o:P:r:R:s:v", options, &argslot)) != -1)
switch (arg) {
case 'd':
operation = doDump;
break;
case 'D':
detached = optarg;
break;
case 'f':
force = true;
break;
case 'h':
operation = doHostingInfo;
break;
case 'i':
uniqueIdentifier = optarg;
break;
case 'o':
cdFlags = parseCdFlags(optarg);
break;
case 'P':
{
if (pagesize = atol(optarg)) {
int pslog;
if (frexp(pagesize, &pslog) != 0.5)
fail("page size must be a power of two");
}
break;
}
case 'r':
internalReq = optarg;
break;
case 'R':
testReq = optarg;
break;
case 's':
signerName = optarg;
operation = doSign;
break;
case 'v':
if (argslot < 0) verbose++;
else if (options[argslot].has_arg == no_argument)
operation = doVerify; else if (optarg)
verbose = atoi(optarg); else
verbose++; break;
case optCheckExpiration:
verifyOptions |= kSecCSConsiderExpiration;
break;
case optContinue:
continueOnError = true;
break;
case optDryRun:
dryrun = true;
break;
case optEntitlements:
entitlements = optarg;
break;
case optExtractCerts:
if (optarg)
extractCerts = optarg;
else
extractCerts = "./codesign";
break;
case optFileList:
modifiedFiles = optarg;
break;
case optIdentifierPrefix:
identifierPrefix = optarg;
break;
case optIgnoreResources:
verifyOptions |= kSecCSDoNotValidateResources;
break;
case optKeychain:
MacOSError::check(SecKeychainOpen(optarg, &keychain));
break;
case optNoMachO:
noMachO = true;
break;
case optPreserveMetadata:
preserveMetadata = true;
break;
case optProcAction:
operation = doProcAction;
procAction = optarg;
break;
case optProcInfo:
operation = doProcInfo;
break;
case optResourceRules:
resourceRules = optarg;
break;
case optSignatureSize:
signatureSize = atol(optarg);
break;
case optSigningTime:
signingTime = parseDate(optarg);
break;
case '?':
usage();
}
if (signerName)
signer = findIdentity(keychain, signerName);
} catch (...) {
diagnose(NULL, exitFailure);
}
if (operation == doNothing && verbose) {
operation = doVerify;
verbose--;
}
if (optind == argc || operation == doNothing)
usage();
try {
switch (operation) {
case doSign:
prepareToSign();
break;
case doVerify:
prepareToVerify();
break;
}
} catch (...) {
diagnose(NULL, exitFailure);
}
for ( ; optind < argc; optind++) {
const char *target = argv[optind];
try {
switch (operation) {
case doSign:
sign(target);
break;
case doVerify:
try {
verify(target);
} catch (...) {
diagnose(target);
if (!exitcode)
exitcode = exitFailure;
}
break;
case doDump:
dump(target);
break;
case doHostingInfo:
hostinginfo(target);
break;
case doProcInfo:
procinfo(target);
break;
case doProcAction:
procaction(target);
break;
}
} catch (...) {
diagnose(target, continueOnError ? 0 : exitFailure);
}
}
exit(exitcode);
}
void usage()
{
fprintf(stderr, "Usage: codesign -s identity [-fv*] [-o flags] [-r reqs] [-i ident] path ... # sign\n"
" codesign -v [-v*] [-R testreq] path|pid ... # verify\n"
" codesign -d [options] path ... # display contents\n"
" codesign -h pid ... # display hosting paths\n"
);
exit(exitUsage);
}