From: Apple Date: Wed, 25 Jul 2012 22:22:25 +0000 (+0000) Subject: libsecurity_codesigning-55037.15.tar.gz X-Git-Tag: mac-os-x-1075^0 X-Git-Url: https://git.saurik.com/apple/libsecurity_codesigning.git/commitdiff_plain libsecurity_codesigning-55037.15.tar.gz --- diff --git a/dtrace/sp-watch.d b/dtrace/sp-watch.d new file mode 100755 index 0000000..194d572 --- /dev/null +++ b/dtrace/sp-watch.d @@ -0,0 +1,52 @@ +#!/usr/sbin/dtrace -qs +# +# +# +self unsigned char *cdhash; + +syspolicy*:::assess-* +{ + self->cdhash = 0; +} + +self string type; +syspolicy*:::assess-outcome-* / arg1 == 1 / { type = "execute"; } +syspolicy*:::assess-outcome-* / arg1 == 2 / { type = "install"; } + +syspolicy*:::assess-outcome-accept +{ + printf("accept %s %s;%s", self->type, copyinstr(arg0), copyinstr(arg1)); + self->cdhash = copyin(arg2, 20); +} + +syspolicy*:::assess-outcome-deny +{ + printf("deny %s %s;%s", self->type, copyinstr(arg0), copyinstr(arg1)); + self->cdhash = copyin(arg2, 20); +} + +syspolicy*:::assess-outcome-default +{ + printf("default %s %s;%s", self->type, copyinstr(arg0), copyinstr(arg1)); + self->cdhash = copyin(arg2, 20); +} + +syspolicy*:::assess-outcome-unsigned +{ + printf("unsigned %s %s;", self->type, copyinstr(arg0)); +} + +syspolicy*:::assess-* +/ self->cdhash / +{ + printf(";%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x", + self->cdhash[0], self->cdhash[1], self->cdhash[2], self->cdhash[3], self->cdhash[4], + self->cdhash[5], self->cdhash[6], self->cdhash[7], self->cdhash[8], self->cdhash[9], + self->cdhash[10], self->cdhash[11], self->cdhash[12], self->cdhash[13], self->cdhash[14], + self->cdhash[15], self->cdhash[16], self->cdhash[17], self->cdhash[18], self->cdhash[19]); +} + +syspolicy*:::assess-* +{ + printf("\n"); +} diff --git a/gke/gkclear b/gke/gkclear new file mode 100755 index 0000000..e7fab61 --- /dev/null +++ b/gke/gkclear @@ -0,0 +1,84 @@ +#!/usr/bin/python +# +# gkclear - clear system state for Gatekeeper recording sessions +# +# This removes DetachedSignatures, resets SystemPolicy, and removes existing gke files. +# +import sys +import os +import signal +import errno +import subprocess +import shutil + + +# +# Usage and fail +# +def usage(): + print >>sys.stderr, "Usage: %s" % sys.argv[0] + sys.exit(2) + +def fail(whatever): + print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever) + sys.exit(1) + + +# +# Argument processing +# +if len(sys.argv) != 1: + usage() + + +# +# Places and things +# +db = "/var/db/" +detachedsignatures = db + "DetachedSignatures" +gkeauth = db + "gke.auth" +gkesigs = db + "gke.sigs" +policydb = db + "SystemPolicy" +policydb_default = db + ".SystemPolicy-default" + + +# must be root +if os.getuid() != 0: + fail("Must have root privileges") + + +# +# Make sure Gatekeeper is disabled +# +subprocess.check_call(["/usr/sbin/spctl", "--master-disable"]) + + +# +# Clear detached signatures database +# +for file in [detachedsignatures, gkeauth, gkesigs]: + try: + os.remove(file) + except OSError, e: + if e[0] != errno.ENOENT: + raise + + +# +# Reset system policy to default values +# +shutil.copyfile(policydb_default, policydb) + + +# +# Kill any extant syspolicyd to flush state +# +null = open("/dev/null", "w") +subprocess.call(["/usr/bin/killall", "syspolicyd"], stderr=null) + + +# +# Done +# +print "System state has been reset." +sys.exit(0) diff --git a/gke/gkgenerate b/gke/gkgenerate new file mode 100755 index 0000000..b62383f --- /dev/null +++ b/gke/gkgenerate @@ -0,0 +1,101 @@ +#!/usr/bin/python +# +# gkgenerate - produce Gatekeeper explicit allow data +# +# gkgenerate [--output name] files... +# will collect GKE data from all files and write two output files (name.auth and name.sigs) +# that are ready to drop into a /var/db for pickup. +# +import sys +import os +import signal +import errno +import subprocess +import argparse +import plistlib +import uuid + + +# +# Parameters and constants +# +authfile = "gke.auth" +sigfile = "gke.dsig" + + +# +# Usage and fail +# +def usage(): + print >>sys.stderr, "Usage: %s sourcedir" % sys.argv[0] + sys.exit(2) + +def fail(whatever): + print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever) + sys.exit(1) + + +# +# Argument processing +# +parser = argparse.ArgumentParser() +parser.add_argument("--output", default="./gke", help="name of output files") +parser.add_argument("--uuid", default=uuid.uuid4(), help="explicitly specify the uuid stamp") +parser.add_argument("--empty", action='store_true', help="allow empty output sets") +parser.add_argument('source', nargs='+', help='files generated by the gkrecord command') +args = parser.parse_args() + +authfile = args.output + ".auth" +sigsfile = args.output + ".sigs" + + +# +# Start by collecting authority evidence from the authority records +# +auth = { } +sigs = { } +for source in args.source: + if source[0] == '+': + data = plistlib.readPlist(source[1:]) + auth.update(data["authority"]) + sigs.update(data["signatures"]) + else: + data = plistlib.readPlist(source) + auth.update(data["authority"]) + +if not auth and not args.empty: + fail("No authority records (nothing to do)") + + +# +# Scrub the authority records to remove incriminating evidence +# +new_auth = { } +for rec in auth.values(): + u = uuid.uuid4() + rec["path"] = "(gke)" + del rec["status"] + new_auth[str(u)] = rec +auth = new_auth + + +# +# The authority file is written as-is, as a plist +# +wrap = dict( + authority=auth, + uuid=str(args.uuid) +) +plistlib.writePlist(wrap, authfile) +print "Wrote %d authority record(s) to %s" % (len(auth), authfile) + + +# +# The signatures are written as tightly packed signature blobs +# +sigblobs = open(sigsfile, "w") +for sig in sigs: + sigdata = sigs[sig] + sigblobs.write(sigdata["signature"].data) +sigblobs.close() +print "Wrote %d signature record(s) to %s" % (len(sigs), sigsfile) diff --git a/gke/gkhandmake b/gke/gkhandmake new file mode 100755 index 0000000..35f63fd --- /dev/null +++ b/gke/gkhandmake @@ -0,0 +1,109 @@ +#!/usr/bin/python +# +# gkhandmake - manually create a recorded snippet +# +# gkhandmake path type source outputfile +# +import sys +import os +import signal +import errno +import subprocess +import plistlib + + +# +# Usage and fail +# +def usage(): + print >>sys.stderr, "Usage: %s program specfile outputfile" % sys.argv[0] + sys.exit(2) + +def fail(whatever): + print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever) + sys.exit(1) + + +# +# Argument processing +# +if len(sys.argv) != 4: + usage() +path=os.path.abspath(sys.argv[1]) +specfile=sys.argv[2] +outputfile = sys.argv[3] +type=1 # always execution + + +# +# If the output file already exists, bail +# +if os.path.exists(outputfile): + fail("already exists: %s" % outputfile) + + +# +# We'll let the detached signature live in case we need to inspect it +# +sigpath = "/tmp/%s.dsig" % os.path.basename(path.strip('/')) + + +# +# Generate an adhoc detached signature with the given resource specification +# + +display = subprocess.check_call(["/usr/bin/codesign", + "--sign", "-", + "--detached", sigpath, + "--resource-rules", specfile, + path +]) + + +# +# Now verify it so we can extract the cdhash +# +display = subprocess.Popen(["/usr/bin/codesign", + "--display", "--verbose=3", + "--detached", sigpath, + path +], stderr=subprocess.PIPE) +(stdout, stderr) = display.communicate() + +cdhash = None +for line in stderr.split('\n'): + if line.startswith("CDHash="): + cdhash = line[7:] + break +if cdhash is None: + fail("no cdhash in generated signature?!") + + +# +# Pack up a single (detached) signature as a snippet +# under the given path +# +with open(sigpath, "r") as sigfile: + sigdata = sigfile.read() +auth = { } +sigs = { } + +auth[path] = dict( + type=type, + path=path, + status=9, + cdhash=cdhash +) + +sigs[path] = dict( + type=type, + path=path, + signature=plistlib.Data(sigdata) +) +gkedict = dict( + authority = auth, + signatures = sigs +) +plistlib.writePlist(gkedict, outputfile) + +sys.exit(0) diff --git a/gke/gklist b/gke/gklist new file mode 100755 index 0000000..ed00e55 --- /dev/null +++ b/gke/gklist @@ -0,0 +1,18 @@ +#!/usr/bin/python +# +# gklist - report Gatekeeper MessageTracer data since last reset +# +# mtdebug --reset +# ... exercise Gatekeeper ... +# gklist +# +import os +import plistlib + + +data = os.popen("mtdebug --plist", "r") +for mt in plistlib.readPlistFromString(data.read()): + if mt["com.apple.message.domain"] == "com.apple.security.assessment.outcome": + outcome = mt["com.apple.message.signature"] + id = mt["com.apple.message.signature2"] + print outcome, "--", id diff --git a/gke/gkmerge b/gke/gkmerge new file mode 100755 index 0000000..bef6c2a --- /dev/null +++ b/gke/gkmerge @@ -0,0 +1,50 @@ +#!/usr/bin/python +# +# gkmerge - merge Gatekeeper whitelist snippets +# +# gkmerge [--output name] files... +# Takes GKE data from all files, merges it together, and writes it to a new snippet file 'output'. +# +import sys +import os +import signal +import errno +import subprocess +import argparse +import plistlib +import uuid + + +# +# Usage and fail +# +def fail(whatever): + print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever) + sys.exit(1) + + +# +# Argument processing +# +parser = argparse.ArgumentParser() +parser.add_argument("--output", default="./snippet.gke", help="name of output file") +parser.add_argument('source', nargs='+', help='files generated by the gkrecord command') +args = parser.parse_args() + + +# +# Merge all the plist data from the input files, overriding with later files +# +gkedict = { } +for source in args.source: + data = plistlib.readPlist(source) + gkedict.update(data) + + +# +# Write it back out as a snippet file +# +plistlib.writePlist(gkedict, args.output) +print "Wrote %d authority records + %d signatures to %s" % ( + len(gkedict["authority"]), len(gkedict["signatures"]), args.output +) diff --git a/gke/gkrecord b/gke/gkrecord new file mode 100755 index 0000000..5261688 --- /dev/null +++ b/gke/gkrecord @@ -0,0 +1,198 @@ +#!/usr/bin/python +# +# gkrecord - record Gatekeeper rejection activity +# +# gkrecord filename +# +import sys +import os +import signal +import errno +import subprocess +import tempfile +import plistlib + + +# +# Usage and fail +# +def usage(): + print >>sys.stderr, "Usage: %s outputfile" % sys.argv[0] + sys.exit(2) + +def fail(whatever): + print >>sys.stderr, "%s: %s" % (sys.argv[0], whatever) + sys.exit(1) + + +# +# Argument processing +# +if len(sys.argv) != 2: + usage() +outputfile = sys.argv[1] + + +# +# If the output file already exists, bail +# +if os.path.exists(outputfile): + fail("already exists: %s" % outputfile) + + +# +# Places and things +# +collect = "/tmp/gke/" + + +# must be root +if os.getuid() != 0: + fail("Must have root privileges") + + +# +# Make sure Gatekeeper is disabled +# +subprocess.check_call(["/usr/sbin/spctl", "--master-disable"]) + + +# +# make sure we have a fresh syspolicyd and get its pid +# +subprocess.check_call(["/usr/sbin/spctl", "--assess", "--ignore-cache", "/bin/ls"]) +try: + psax = subprocess.check_output("ps ax|grep syspolicyd|grep -v grep", shell=True).split("\n") + if len(psax) != 2: # [ found_syspolicyd, '' ] + fail("Cannot find syspolicyd") + spd_pid = int(psax[0].split()[0]) +except subprocess.CalledProcessError: + fail("Cannot find syspolicyd") + + +# +# run collector dtrace script until dtrace dies. +# recorder_mode arguments are (path, type, label, cdhash, flags) +# +DSCRIPT = ''' +syspolicy$1:::recorder_mode { printf("RECORD;%d;%d", arg1, arg4); } + +self unsigned char *cdhash; + +syspolicy$1:::recorder_mode +{ + self->cdhash = copyin(arg3, 20); + printf(";%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x%02.2x", + self->cdhash[0], self->cdhash[1], self->cdhash[2], self->cdhash[3], self->cdhash[4], + self->cdhash[5], self->cdhash[6], self->cdhash[7], self->cdhash[8], self->cdhash[9], + self->cdhash[10], self->cdhash[11], self->cdhash[12], self->cdhash[13], self->cdhash[14], + self->cdhash[15], self->cdhash[16], self->cdhash[17], self->cdhash[18], self->cdhash[19]); + printf(";%s\\n", copyinstr(arg0)); +} + +syspolicy$1:::recorder_mode_adhoc_path +{ + printf("SIGNATURE;%d;%s;%s\\n", arg1, copyinstr(arg2), copyinstr(arg0)); +} + +syspolicy$1:::assess-outcome-unsigned +{ + printf("UNSIGNED;%d;%s\\n", arg1, copyinstr(arg0)); +} + +syspolicy$1:::assess-outcome-broken +{ + printf("BROKEN;%d;%d;%s\\n", arg1, arg2, copyinstr(arg0)); +} +''' + +def sigint(sig, ctx): + os.kill(spd_pid, signal.SIGINT) +signal.signal(signal.SIGINT, sigint) + +(authfd, authfile) = tempfile.mkstemp() +dtrace = subprocess.Popen(["dtrace", "-qs", "/dev/stdin", str(spd_pid)], stdin=subprocess.PIPE, stdout=authfd, stderr=subprocess.PIPE) +print "Exercise the programs to be whitelisted now. Interrupt this script (^C) when you are done." +(stdout, stderr) = dtrace.communicate(input=DSCRIPT) +signal.signal(signal.SIGINT, signal.SIG_DFL) +if stderr: + fail("dtrace failed: %s" % stderr) +os.lseek(authfd, os.SEEK_SET, 0) # rewind + + +# +# Collect all the data into dicts +# +auth = { } +sigs = { } +unsigned = { } +badsigned = { } +errors = { } + +file = os.fdopen(authfd, "r") +for line in file: + (cmd, s, args) = line.strip().partition(";") + if s != ";": + continue # spurious +# print cmd, "--->", args + if cmd == "RECORD": + (type, status, cdhash, path) = args.split(";", 3) + auth[path] = dict( + path=path, + type=type, + status=status, + cdhash=cdhash + ) + elif cmd == "SIGNATURE": + (type, sigpath, path) = args.split(";", 2) + with open(sigpath, "r") as sigfile: + sigdata = sigfile.read() + sigs[path] = dict( + path=path, + type=type, + signature=plistlib.Data(sigdata) + ) + elif cmd == "UNSIGNED": + (type, path) = args.split(";", 1) + unsigned[path] = dict( + path=path, + type=type + ) + elif cmd == "BROKEN": + (type, exception, path) = args.split(";", 2) + badsigned[path] = dict( + path=path, + type=type, + exception=exception + ) + +# unsigned code that had a good detached signature recorded is okay +for rec in sigs: + if rec in unsigned: + del unsigned[rec] + + +# +# Pack them up as a single output (plist) file +# +gkedict = dict( + authority = auth, + signatures = sigs +) +plistlib.writePlist(gkedict, outputfile) + + +# +# Report on any problems found +# +for rec in unsigned.values(): + print >>sys.stderr, "PROBLEM: unsigned type %d object not whitelisted: %s" % (rec["type"], rec["path"]) +for rec in badsigned.values(): + print >>sys.stderr, "PROBLEM: broken code signature; object not whitelisted: %s" % rec["path"] + + +# +# Done +# +print "Recorded %d authorization(s), %d signature(s) in %s" % (len(auth), len(sigs), outputfile) +sys.exit(0) diff --git a/lib/CSCommon.h b/lib/CSCommon.h index 3c87b62..883d57b 100644 --- a/lib/CSCommon.h +++ b/lib/CSCommon.h @@ -89,7 +89,10 @@ enum { errSecCSInfoPlistFailed = -67030, /* invalid Info.plist (plist or signature have been modified) */ errSecCSNoMainExecutable = -67029, /* the code has no main executable file */ errSecCSBadBundleFormat = -67028, /* bundle format unrecognized, invalid, or unsuitable */ - errSecCSNoMatches = -67027, /* no matches for search or update operation */ + errSecCSNoMatches = -67027, /* no matches for search or update operation */ + errSecCSFileHardQuarantined = -67026, /* File created by an AppSandbox, exec/open not allowed */ + errSecCSOutdated = -67025, /* presented data is out of date */ + errSecCSDbCorrupt = -67024, /* a system database of file is corrupt */ }; diff --git a/lib/CSCommonPriv.h b/lib/CSCommonPriv.h index e341362..f87f454 100644 --- a/lib/CSCommonPriv.h +++ b/lib/CSCommonPriv.h @@ -75,7 +75,6 @@ extern const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[]; numbers. This is not a valid ASCII character; test for this to distinguish between text and binary data if you expect a code signing-related binary blob. */ -typedef uint32_t SecCodeSignatureFlags; enum { kSecCodeMagicRequirement = 0xfade0c00, /* single requirement */ diff --git a/lib/CodeSigner.cpp b/lib/CodeSigner.cpp index 361d8fc..e40620b 100644 --- a/lib/CodeSigner.cpp +++ b/lib/CodeSigner.cpp @@ -29,8 +29,11 @@ #include "reqparser.h" #include "renum.h" #include "csdatabase.h" +#include "drmaker.h" +#include "csutilities.h" #include #include +#include #include namespace Security { @@ -171,10 +174,9 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) : CFDictionary(parameters, errSecCSBadDictionaryFormat) { // the signer may be an identity or null - if (CFTypeRef signer = get(kSecCodeSignerIdentity)) - if (CFGetTypeID(signer) == SecIdentityGetTypeID() || signer == kCFNull) - state.mSigner = SecIdentityRef(signer); - else + state.mSigner = SecIdentityRef(get(kSecCodeSignerIdentity)); + if (state.mSigner) + if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull)) MacOSError::throwMe(errSecCSInvalidObjectRef); // the flags need some augmentation @@ -186,19 +188,20 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) // digest algorithms are specified as a numeric code if (CFNumberRef digestAlgorithm = get(kSecCodeSignerDigestAlgorithm)) - state.mDigestAlgorithm = cfNumber(digestAlgorithm); + state.mDigestAlgorithm = cfNumber(digestAlgorithm); if (CFNumberRef cmsSize = get(CFSTR("cmssize"))) state.mCMSSize = cfNumber(cmsSize); else - state.mCMSSize = 5000; // likely big enough + state.mCMSSize = 9000; // likely big enough // signing time can be a CFDateRef or null - if (CFTypeRef time = get(kSecCodeSignerSigningTime)) + if (CFTypeRef time = get(kSecCodeSignerSigningTime)) { if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull) state.mSigningTime = CFDateRef(time); else MacOSError::throwMe(errSecCSInvalidObjectRef); + } if (CFStringRef ident = get(kSecCodeSignerIdentifier)) state.mIdentifier = cfString(ident); @@ -223,7 +226,7 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) state.mPageSize = get(kSecCodeSignerPageSize); // detached can be (destination) file URL or (mutable) Data to be appended-to - if (state.mDetached = get(kSecCodeSignerDetached)) { + if ((state.mDetached = get(kSecCodeSignerDetached))) { CFTypeID type = CFGetTypeID(state.mDetached); if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID()) MacOSError::throwMe(errSecCSInvalidObjectRef); @@ -237,6 +240,21 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) state.mEntitlementData = get(kSecCodeSignerEntitlements); state.mSDKRoot = get(kSecCodeSignerSDKRoot); + + if (CFBooleanRef timestampRequest = get(kSecCodeSignerRequireTimestamp)) { + state.mWantTimeStamp = timestampRequest == kCFBooleanTrue; + } else { // pick default + state.mWantTimeStamp = false; + if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) { + CFRef signerCert; + MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref())); + if (certificateHasField(signerCert, devIdLeafMarkerOID)) + state.mWantTimeStamp = true; + } + } + state.mTimestampAuthentication = get(kSecCodeSignerTimestampAuthentication); + state.mTimestampService = get(kSecCodeSignerTimestampServer); + state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates); } diff --git a/lib/CodeSigner.h b/lib/CodeSigner.h index f3e8b8f..71f9cc3 100644 --- a/lib/CodeSigner.h +++ b/lib/CodeSigner.h @@ -85,6 +85,10 @@ private: bool mNoMachO; // override to perform non-Mach-O signing bool mDryRun; // dry run (do not change target) CFRef mPageSize; // main executable page size + CFRef mTimestampAuthentication; // identity for client-side authentication to the Timestamp server + CFRef mTimestampService; // URL for Timestamp server + bool mWantTimeStamp; // use a Timestamp server + bool mNoTimeStampCerts; // don't request certificates with timestamping request }; diff --git a/lib/RequirementKeywords.h b/lib/RequirementKeywords.h new file mode 100644 index 0000000..bd0438e --- /dev/null +++ b/lib/RequirementKeywords.h @@ -0,0 +1,24 @@ + "guest", + "host", + "designated", + "library", + "plugin", + "or", + "and", + "always", + "true", + "never", + "false", + "identifier", + "cdhash", + "anchor", + "apple", + "generic", + "certificate", + "cert", + "trusted", + "info", + "entitlement", + "exists", + "leaf", + "root", diff --git a/lib/RequirementLexer.cpp b/lib/RequirementLexer.cpp new file mode 100644 index 0000000..ac8b2bd --- /dev/null +++ b/lib/RequirementLexer.cpp @@ -0,0 +1,1268 @@ +/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementLexer.cpp"$ */ +#include "RequirementLexer.hpp" +#include +#include +#include +#include +#include +#include +#include + + +#include "requirement.h" +#include "reqmaker.h" +#include "csutilities.h" +#include +#include +#include // OID coding +using namespace CodeSigning; +typedef Requirement::Maker Maker; + +ANTLR_BEGIN_NAMESPACE(Security_CodeSigning) +RequirementLexer::RequirementLexer(std::istream& in) + : antlr::CharScanner(new antlr::CharBuffer(in),true) +{ + initLiterals(); +} + +RequirementLexer::RequirementLexer(antlr::InputBuffer& ib) + : antlr::CharScanner(ib,true) +{ + initLiterals(); +} + +RequirementLexer::RequirementLexer(const antlr::LexerSharedInputState& state) + : antlr::CharScanner(state,true) +{ + initLiterals(); +} + +void RequirementLexer::initLiterals() +{ + literals["certificate"] = 25; + literals["always"] = 16; + literals["host"] = 6; + literals["guest"] = 5; + literals["cdhash"] = 21; + literals["entitlement"] = 29; + literals["library"] = 8; + literals["never"] = 18; + literals["cert"] = 26; + literals["plugin"] = 9; + literals["or"] = 11; + literals["leaf"] = 42; + literals["info"] = 28; + literals["designated"] = 7; + literals["apple"] = 23; + literals["trusted"] = 27; + literals["true"] = 17; + literals["and"] = 12; + literals["root"] = 43; + literals["anchor"] = 22; + literals["false"] = 19; + literals["generic"] = 24; + literals["identifier"] = 20; + literals["exists"] = 30; +} + +antlr::RefToken RequirementLexer::nextToken() +{ + antlr::RefToken theRetToken; + for (;;) { + antlr::RefToken theRetToken; + int _ttype = antlr::Token::INVALID_TYPE; + resetText(); + try { // for lexical and char stream error handling + switch ( LA(1)) { + case 0x22 /* '\"' */ : + { + mSTRING(true); + theRetToken=_returnToken; + break; + } + case 0x3b /* ';' */ : + { + mSEMI(true); + theRetToken=_returnToken; + break; + } + case 0x28 /* '(' */ : + { + mLPAREN(true); + theRetToken=_returnToken; + break; + } + case 0x29 /* ')' */ : + { + mRPAREN(true); + theRetToken=_returnToken; + break; + } + case 0x5b /* '[' */ : + { + mLBRACK(true); + theRetToken=_returnToken; + break; + } + case 0x5d /* ']' */ : + { + mRBRACK(true); + theRetToken=_returnToken; + break; + } + case 0x2c /* ',' */ : + { + mCOMMA(true); + theRetToken=_returnToken; + break; + } + case 0x7e /* '~' */ : + { + mSUBS(true); + theRetToken=_returnToken; + break; + } + case 0x2d /* '-' */ : + { + mNEG(true); + theRetToken=_returnToken; + break; + } + case 0x21 /* '!' */ : + { + mNOT(true); + theRetToken=_returnToken; + break; + } + case 0x2a /* '*' */ : + { + mSTAR(true); + theRetToken=_returnToken; + break; + } + case 0x9 /* '\t' */ : + case 0xa /* '\n' */ : + case 0x20 /* ' ' */ : + { + mWS(true); + theRetToken=_returnToken; + break; + } + case 0x23 /* '#' */ : + { + mSHELLCOMMENT(true); + theRetToken=_returnToken; + break; + } + default: + if ((LA(1) == 0x2f /* '/' */ ) && (_tokenSet_0.member(LA(2)))) { + mPATHNAME(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x48 /* 'H' */ ) && (LA(2) == 0x22 /* '\"' */ )) { + mHASHCONSTANT(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x30 /* '0' */ ) && (LA(2) == 0x78 /* 'x' */ )) { + mHEXCONSTANT(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3d /* '=' */ ) && (LA(2) == 0x3e /* '>' */ )) { + mARROW(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3c /* '<' */ ) && (LA(2) == 0x3d /* '=' */ )) { + mLE(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3e /* '>' */ ) && (LA(2) == 0x3d /* '=' */ )) { + mGE(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3d /* '=' */ ) && (LA(2) == 0x3d /* '=' */ )) { + mEQQL(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x2f /* '/' */ ) && (LA(2) == 0x2a /* '*' */ )) { + mC_COMMENT(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x2f /* '/' */ ) && (LA(2) == 0x2f /* '/' */ )) { + mCPP_COMMENT(true); + theRetToken=_returnToken; + } + else if ((_tokenSet_0.member(LA(1))) && (true)) { + mDOTKEY(true); + theRetToken=_returnToken; + } + else if (((LA(1) >= 0x30 /* '0' */ && LA(1) <= 0x39 /* '9' */ )) && (true)) { + mINTEGER(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3c /* '<' */ ) && (true)) { + mLESS(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3e /* '>' */ ) && (true)) { + mGT(true); + theRetToken=_returnToken; + } + else if ((LA(1) == 0x3d /* '=' */ ) && (true)) { + mEQL(true); + theRetToken=_returnToken; + } + else { + if (LA(1)==EOF_CHAR) + { + uponEOF(); + _returnToken = makeToken(antlr::Token::EOF_TYPE); + } + else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + } + if ( !_returnToken ) + goto tryAgain; // found SKIP token + + _ttype = _returnToken->getType(); + _returnToken->setType(_ttype); + return _returnToken; + } + catch (antlr::RecognitionException& e) { + throw antlr::TokenStreamRecognitionException(e); + } + catch (antlr::CharStreamIOException& csie) { + throw antlr::TokenStreamIOException(csie.io); + } + catch (antlr::CharStreamException& cse) { + throw antlr::TokenStreamException(cse.getMessage()); + } +tryAgain:; + } +} + +void RequirementLexer::mIDENT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = IDENT; + std::string::size_type _saveIndex; + + { + switch ( LA(1)) { + case 0x41 /* 'A' */ : + case 0x42 /* 'B' */ : + case 0x43 /* 'C' */ : + case 0x44 /* 'D' */ : + case 0x45 /* 'E' */ : + case 0x46 /* 'F' */ : + case 0x47 /* 'G' */ : + case 0x48 /* 'H' */ : + case 0x49 /* 'I' */ : + case 0x4a /* 'J' */ : + case 0x4b /* 'K' */ : + case 0x4c /* 'L' */ : + case 0x4d /* 'M' */ : + case 0x4e /* 'N' */ : + case 0x4f /* 'O' */ : + case 0x50 /* 'P' */ : + case 0x51 /* 'Q' */ : + case 0x52 /* 'R' */ : + case 0x53 /* 'S' */ : + case 0x54 /* 'T' */ : + case 0x55 /* 'U' */ : + case 0x56 /* 'V' */ : + case 0x57 /* 'W' */ : + case 0x58 /* 'X' */ : + case 0x59 /* 'Y' */ : + case 0x5a /* 'Z' */ : + { + matchRange('A','Z'); + break; + } + case 0x61 /* 'a' */ : + case 0x62 /* 'b' */ : + case 0x63 /* 'c' */ : + case 0x64 /* 'd' */ : + case 0x65 /* 'e' */ : + case 0x66 /* 'f' */ : + case 0x67 /* 'g' */ : + case 0x68 /* 'h' */ : + case 0x69 /* 'i' */ : + case 0x6a /* 'j' */ : + case 0x6b /* 'k' */ : + case 0x6c /* 'l' */ : + case 0x6d /* 'm' */ : + case 0x6e /* 'n' */ : + case 0x6f /* 'o' */ : + case 0x70 /* 'p' */ : + case 0x71 /* 'q' */ : + case 0x72 /* 'r' */ : + case 0x73 /* 's' */ : + case 0x74 /* 't' */ : + case 0x75 /* 'u' */ : + case 0x76 /* 'v' */ : + case 0x77 /* 'w' */ : + case 0x78 /* 'x' */ : + case 0x79 /* 'y' */ : + case 0x7a /* 'z' */ : + { + matchRange('a','z'); + break; + } + default: + { + throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { // ( ... )* + for (;;) { + switch ( LA(1)) { + case 0x41 /* 'A' */ : + case 0x42 /* 'B' */ : + case 0x43 /* 'C' */ : + case 0x44 /* 'D' */ : + case 0x45 /* 'E' */ : + case 0x46 /* 'F' */ : + case 0x47 /* 'G' */ : + case 0x48 /* 'H' */ : + case 0x49 /* 'I' */ : + case 0x4a /* 'J' */ : + case 0x4b /* 'K' */ : + case 0x4c /* 'L' */ : + case 0x4d /* 'M' */ : + case 0x4e /* 'N' */ : + case 0x4f /* 'O' */ : + case 0x50 /* 'P' */ : + case 0x51 /* 'Q' */ : + case 0x52 /* 'R' */ : + case 0x53 /* 'S' */ : + case 0x54 /* 'T' */ : + case 0x55 /* 'U' */ : + case 0x56 /* 'V' */ : + case 0x57 /* 'W' */ : + case 0x58 /* 'X' */ : + case 0x59 /* 'Y' */ : + case 0x5a /* 'Z' */ : + { + matchRange('A','Z'); + break; + } + case 0x61 /* 'a' */ : + case 0x62 /* 'b' */ : + case 0x63 /* 'c' */ : + case 0x64 /* 'd' */ : + case 0x65 /* 'e' */ : + case 0x66 /* 'f' */ : + case 0x67 /* 'g' */ : + case 0x68 /* 'h' */ : + case 0x69 /* 'i' */ : + case 0x6a /* 'j' */ : + case 0x6b /* 'k' */ : + case 0x6c /* 'l' */ : + case 0x6d /* 'm' */ : + case 0x6e /* 'n' */ : + case 0x6f /* 'o' */ : + case 0x70 /* 'p' */ : + case 0x71 /* 'q' */ : + case 0x72 /* 'r' */ : + case 0x73 /* 's' */ : + case 0x74 /* 't' */ : + case 0x75 /* 'u' */ : + case 0x76 /* 'v' */ : + case 0x77 /* 'w' */ : + case 0x78 /* 'x' */ : + case 0x79 /* 'y' */ : + case 0x7a /* 'z' */ : + { + matchRange('a','z'); + break; + } + case 0x30 /* '0' */ : + case 0x31 /* '1' */ : + case 0x32 /* '2' */ : + case 0x33 /* '3' */ : + case 0x34 /* '4' */ : + case 0x35 /* '5' */ : + case 0x36 /* '6' */ : + case 0x37 /* '7' */ : + case 0x38 /* '8' */ : + case 0x39 /* '9' */ : + { + matchRange('0','9'); + break; + } + default: + { + goto _loop46; + } + } + } + _loop46:; + } // ( ... )* + _ttype = testLiteralsTable(text.substr(_begin, text.length()-_begin),_ttype); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mDOTKEY(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = DOTKEY; + std::string::size_type _saveIndex; + + mIDENT(false); + { // ( ... )* + for (;;) { + if ((LA(1) == 0x2e /* '.' */ )) { + match("."); + { + switch ( LA(1)) { + case 0x41 /* 'A' */ : + case 0x42 /* 'B' */ : + case 0x43 /* 'C' */ : + case 0x44 /* 'D' */ : + case 0x45 /* 'E' */ : + case 0x46 /* 'F' */ : + case 0x47 /* 'G' */ : + case 0x48 /* 'H' */ : + case 0x49 /* 'I' */ : + case 0x4a /* 'J' */ : + case 0x4b /* 'K' */ : + case 0x4c /* 'L' */ : + case 0x4d /* 'M' */ : + case 0x4e /* 'N' */ : + case 0x4f /* 'O' */ : + case 0x50 /* 'P' */ : + case 0x51 /* 'Q' */ : + case 0x52 /* 'R' */ : + case 0x53 /* 'S' */ : + case 0x54 /* 'T' */ : + case 0x55 /* 'U' */ : + case 0x56 /* 'V' */ : + case 0x57 /* 'W' */ : + case 0x58 /* 'X' */ : + case 0x59 /* 'Y' */ : + case 0x5a /* 'Z' */ : + case 0x61 /* 'a' */ : + case 0x62 /* 'b' */ : + case 0x63 /* 'c' */ : + case 0x64 /* 'd' */ : + case 0x65 /* 'e' */ : + case 0x66 /* 'f' */ : + case 0x67 /* 'g' */ : + case 0x68 /* 'h' */ : + case 0x69 /* 'i' */ : + case 0x6a /* 'j' */ : + case 0x6b /* 'k' */ : + case 0x6c /* 'l' */ : + case 0x6d /* 'm' */ : + case 0x6e /* 'n' */ : + case 0x6f /* 'o' */ : + case 0x70 /* 'p' */ : + case 0x71 /* 'q' */ : + case 0x72 /* 'r' */ : + case 0x73 /* 's' */ : + case 0x74 /* 't' */ : + case 0x75 /* 'u' */ : + case 0x76 /* 'v' */ : + case 0x77 /* 'w' */ : + case 0x78 /* 'x' */ : + case 0x79 /* 'y' */ : + case 0x7a /* 'z' */ : + { + mIDENT(false); + break; + } + case 0x30 /* '0' */ : + case 0x31 /* '1' */ : + case 0x32 /* '2' */ : + case 0x33 /* '3' */ : + case 0x34 /* '4' */ : + case 0x35 /* '5' */ : + case 0x36 /* '6' */ : + case 0x37 /* '7' */ : + case 0x38 /* '8' */ : + case 0x39 /* '9' */ : + { + mINTEGER(false); + break; + } + default: + { + throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn()); + } + } + } + } + else { + goto _loop50; + } + + } + _loop50:; + } // ( ... )* + _ttype = testLiteralsTable(_ttype); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mINTEGER(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = INTEGER; + std::string::size_type _saveIndex; + + { // ( ... )+ + int _cnt68=0; + for (;;) { + if (((LA(1) >= 0x30 /* '0' */ && LA(1) <= 0x39 /* '9' */ ))) { + matchRange('0','9'); + } + else { + if ( _cnt68>=1 ) { goto _loop68; } else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt68++; + } + _loop68:; + } // ( ... )+ + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mPATHNAME(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = PATHNAME; + std::string::size_type _saveIndex; + + match("/"); + mIDENT(false); + { // ( ... )+ + int _cnt53=0; + for (;;) { + if ((LA(1) == 0x2f /* '/' */ )) { + match("/"); + mIDENT(false); + } + else { + if ( _cnt53>=1 ) { goto _loop53; } else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt53++; + } + _loop53:; + } // ( ... )+ + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mHASHCONSTANT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = HASHCONSTANT; + std::string::size_type _saveIndex; + + _saveIndex = text.length(); + match('H' /* charlit */ ); + text.erase(_saveIndex); + _saveIndex = text.length(); + match('\"' /* charlit */ ); + text.erase(_saveIndex); + { // ( ... )+ + int _cnt56=0; + for (;;) { + if ((_tokenSet_1.member(LA(1)))) { + mHEX(false); + } + else { + if ( _cnt56>=1 ) { goto _loop56; } else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt56++; + } + _loop56:; + } // ( ... )+ + _saveIndex = text.length(); + match('\"' /* charlit */ ); + text.erase(_saveIndex); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mHEX(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = HEX; + std::string::size_type _saveIndex; + + switch ( LA(1)) { + case 0x30 /* '0' */ : + case 0x31 /* '1' */ : + case 0x32 /* '2' */ : + case 0x33 /* '3' */ : + case 0x34 /* '4' */ : + case 0x35 /* '5' */ : + case 0x36 /* '6' */ : + case 0x37 /* '7' */ : + case 0x38 /* '8' */ : + case 0x39 /* '9' */ : + { + matchRange('0','9'); + break; + } + case 0x61 /* 'a' */ : + case 0x62 /* 'b' */ : + case 0x63 /* 'c' */ : + case 0x64 /* 'd' */ : + case 0x65 /* 'e' */ : + case 0x66 /* 'f' */ : + { + matchRange('a','f'); + break; + } + case 0x41 /* 'A' */ : + case 0x42 /* 'B' */ : + case 0x43 /* 'C' */ : + case 0x44 /* 'D' */ : + case 0x45 /* 'E' */ : + case 0x46 /* 'F' */ : + { + matchRange('A','F'); + break; + } + default: + { + throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mHEXCONSTANT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = HEXCONSTANT; + std::string::size_type _saveIndex; + + _saveIndex = text.length(); + match('0' /* charlit */ ); + text.erase(_saveIndex); + _saveIndex = text.length(); + match('x' /* charlit */ ); + text.erase(_saveIndex); + { // ( ... )+ + int _cnt59=0; + for (;;) { + if ((_tokenSet_1.member(LA(1)))) { + mHEX(false); + } + else { + if ( _cnt59>=1 ) { goto _loop59; } else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt59++; + } + _loop59:; + } // ( ... )+ + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mSTRING(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = STRING; + std::string::size_type _saveIndex; + + _saveIndex = text.length(); + match('\"' /* charlit */ ); + text.erase(_saveIndex); + { // ( ... )* + for (;;) { + switch ( LA(1)) { + case 0x5c /* '\\' */ : + { + { + _saveIndex = text.length(); + match('\\' /* charlit */ ); + text.erase(_saveIndex); + match('\"' /* charlit */ ); + } + break; + } + case 0x0 /* '\0' */ : + case 0x1 /* '\1' */ : + case 0x2 /* '\2' */ : + case 0x3 /* '\3' */ : + case 0x4 /* '\4' */ : + case 0x5 /* '\5' */ : + case 0x6 /* '\6' */ : + case 0x7 /* '\7' */ : + case 0x8 /* '\10' */ : + case 0x9 /* '\t' */ : + case 0xa /* '\n' */ : + case 0xb /* '\13' */ : + case 0xc /* '\14' */ : + case 0xd /* '\r' */ : + case 0xe /* '\16' */ : + case 0xf /* '\17' */ : + case 0x10 /* '\20' */ : + case 0x11 /* '\21' */ : + case 0x12 /* '\22' */ : + case 0x13 /* '\23' */ : + case 0x14 /* '\24' */ : + case 0x15 /* '\25' */ : + case 0x16 /* '\26' */ : + case 0x17 /* '\27' */ : + case 0x18 /* '\30' */ : + case 0x19 /* '\31' */ : + case 0x1a /* '\32' */ : + case 0x1b /* '\33' */ : + case 0x1c /* '\34' */ : + case 0x1d /* '\35' */ : + case 0x1e /* '\36' */ : + case 0x1f /* '\37' */ : + case 0x20 /* ' ' */ : + case 0x21 /* '!' */ : + case 0x23 /* '#' */ : + case 0x24 /* '$' */ : + case 0x25 /* '%' */ : + case 0x26 /* '&' */ : + case 0x27 /* '\'' */ : + case 0x28 /* '(' */ : + case 0x29 /* ')' */ : + case 0x2a /* '*' */ : + case 0x2b /* '+' */ : + case 0x2c /* ',' */ : + case 0x2d /* '-' */ : + case 0x2e /* '.' */ : + case 0x2f /* '/' */ : + case 0x30 /* '0' */ : + case 0x31 /* '1' */ : + case 0x32 /* '2' */ : + case 0x33 /* '3' */ : + case 0x34 /* '4' */ : + case 0x35 /* '5' */ : + case 0x36 /* '6' */ : + case 0x37 /* '7' */ : + case 0x38 /* '8' */ : + case 0x39 /* '9' */ : + case 0x3a /* ':' */ : + case 0x3b /* ';' */ : + case 0x3c /* '<' */ : + case 0x3d /* '=' */ : + case 0x3e /* '>' */ : + case 0x3f /* '?' */ : + case 0x40 /* '@' */ : + case 0x41 /* 'A' */ : + case 0x42 /* 'B' */ : + case 0x43 /* 'C' */ : + case 0x44 /* 'D' */ : + case 0x45 /* 'E' */ : + case 0x46 /* 'F' */ : + case 0x47 /* 'G' */ : + case 0x48 /* 'H' */ : + case 0x49 /* 'I' */ : + case 0x4a /* 'J' */ : + case 0x4b /* 'K' */ : + case 0x4c /* 'L' */ : + case 0x4d /* 'M' */ : + case 0x4e /* 'N' */ : + case 0x4f /* 'O' */ : + case 0x50 /* 'P' */ : + case 0x51 /* 'Q' */ : + case 0x52 /* 'R' */ : + case 0x53 /* 'S' */ : + case 0x54 /* 'T' */ : + case 0x55 /* 'U' */ : + case 0x56 /* 'V' */ : + case 0x57 /* 'W' */ : + case 0x58 /* 'X' */ : + case 0x59 /* 'Y' */ : + case 0x5a /* 'Z' */ : + case 0x5b /* '[' */ : + case 0x5d /* ']' */ : + case 0x5e /* '^' */ : + case 0x5f /* '_' */ : + case 0x60 /* '`' */ : + case 0x61 /* 'a' */ : + case 0x62 /* 'b' */ : + case 0x63 /* 'c' */ : + case 0x64 /* 'd' */ : + case 0x65 /* 'e' */ : + case 0x66 /* 'f' */ : + case 0x67 /* 'g' */ : + case 0x68 /* 'h' */ : + case 0x69 /* 'i' */ : + case 0x6a /* 'j' */ : + case 0x6b /* 'k' */ : + case 0x6c /* 'l' */ : + case 0x6d /* 'm' */ : + case 0x6e /* 'n' */ : + case 0x6f /* 'o' */ : + case 0x70 /* 'p' */ : + case 0x71 /* 'q' */ : + case 0x72 /* 'r' */ : + case 0x73 /* 's' */ : + case 0x74 /* 't' */ : + case 0x75 /* 'u' */ : + case 0x76 /* 'v' */ : + case 0x77 /* 'w' */ : + case 0x78 /* 'x' */ : + case 0x79 /* 'y' */ : + case 0x7a /* 'z' */ : + case 0x7b /* '{' */ : + case 0x7c /* '|' */ : + case 0x7d /* '}' */ : + case 0x7e /* '~' */ : + case 0x7f: + { + { + { + match(_tokenSet_2); + } + } + break; + } + default: + { + goto _loop65; + } + } + } + _loop65:; + } // ( ... )* + _saveIndex = text.length(); + match('\"' /* charlit */ ); + text.erase(_saveIndex); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mARROW(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = ARROW; + std::string::size_type _saveIndex; + + match("=>"); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mSEMI(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = SEMI; + std::string::size_type _saveIndex; + + match(';' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mLPAREN(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = LPAREN; + std::string::size_type _saveIndex; + + match('(' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mRPAREN(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = RPAREN; + std::string::size_type _saveIndex; + + match(')' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mLBRACK(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = LBRACK; + std::string::size_type _saveIndex; + + match('[' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mRBRACK(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = RBRACK; + std::string::size_type _saveIndex; + + match(']' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mLESS(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = LESS; + std::string::size_type _saveIndex; + + match('<' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mGT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = GT; + std::string::size_type _saveIndex; + + match('>' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mLE(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = LE; + std::string::size_type _saveIndex; + + match("<="); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mGE(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = GE; + std::string::size_type _saveIndex; + + match(">="); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mCOMMA(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = COMMA; + std::string::size_type _saveIndex; + + match(',' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mEQL(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = EQL; + std::string::size_type _saveIndex; + + match('=' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mEQQL(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = EQQL; + std::string::size_type _saveIndex; + + match("=="); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mSUBS(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = SUBS; + std::string::size_type _saveIndex; + + match('~' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mNEG(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = NEG; + std::string::size_type _saveIndex; + + match('-' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mNOT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = NOT; + std::string::size_type _saveIndex; + + match('!' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mSTAR(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = STAR; + std::string::size_type _saveIndex; + + match('*' /* charlit */ ); + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mWS(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = WS; + std::string::size_type _saveIndex; + + { // ( ... )+ + int _cnt89=0; + for (;;) { + switch ( LA(1)) { + case 0x20 /* ' ' */ : + { + match(' ' /* charlit */ ); + break; + } + case 0xa /* '\n' */ : + { + match('\n' /* charlit */ ); + newline(); + break; + } + case 0x9 /* '\t' */ : + { + match('\t' /* charlit */ ); + break; + } + default: + { + if ( _cnt89>=1 ) { goto _loop89; } else {throw antlr::NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());} + } + } + _cnt89++; + } + _loop89:; + } // ( ... )+ + _ttype = antlr::Token::SKIP; + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mSHELLCOMMENT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = SHELLCOMMENT; + std::string::size_type _saveIndex; + + match('#' /* charlit */ ); + { // ( ... )* + for (;;) { + if ((_tokenSet_3.member(LA(1)))) { + matchNot('\n' /* charlit */ ); + } + else { + goto _loop92; + } + + } + _loop92:; + } // ( ... )* + _ttype = antlr::Token::SKIP; + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mC_COMMENT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = C_COMMENT; + std::string::size_type _saveIndex; + + match("/*"); + { // ( ... )* + for (;;) { + if ((LA(1) == 0x2a /* '*' */ ) && (_tokenSet_4.member(LA(2)))) { + { + match('*' /* charlit */ ); + { + matchNot('/' /* charlit */ ); + } + } + } + else if ((_tokenSet_5.member(LA(1)))) { + { + matchNot('*' /* charlit */ ); + } + } + else { + goto _loop98; + } + + } + _loop98:; + } // ( ... )* + match("*/"); + _ttype = antlr::Token::SKIP; + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + +void RequirementLexer::mCPP_COMMENT(bool _createToken) { + int _ttype; antlr::RefToken _token; std::string::size_type _begin = text.length(); + _ttype = CPP_COMMENT; + std::string::size_type _saveIndex; + + match("//"); + { // ( ... )* + for (;;) { + if ((_tokenSet_3.member(LA(1)))) { + matchNot('\n' /* charlit */ ); + } + else { + goto _loop101; + } + + } + _loop101:; + } // ( ... )* + _ttype = antlr::Token::SKIP; + if ( _createToken && _token==antlr::nullToken && _ttype!=antlr::Token::SKIP ) { + _token = makeToken(_ttype); + _token->setText(text.substr(_begin, text.length()-_begin)); + } + _returnToken = _token; + _saveIndex=0; +} + + +const unsigned long RequirementLexer::_tokenSet_0_data_[] = { 0UL, 0UL, 134217726UL, 134217726UL, 0UL, 0UL, 0UL, 0UL }; +const antlr::BitSet RequirementLexer::_tokenSet_0(_tokenSet_0_data_,8); +const unsigned long RequirementLexer::_tokenSet_1_data_[] = { 0UL, 67043328UL, 126UL, 126UL, 0UL, 0UL, 0UL, 0UL }; +// 0 1 2 3 4 5 6 7 8 +const antlr::BitSet RequirementLexer::_tokenSet_1(_tokenSet_1_data_,8); +const unsigned long RequirementLexer::_tokenSet_2_data_[] = { 4294967295UL, 4294967291UL, 4026531839UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL }; +// 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 +// 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e +// 0x1f ! # $ % & \' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 +const antlr::BitSet RequirementLexer::_tokenSet_2(_tokenSet_2_data_,8); +const unsigned long RequirementLexer::_tokenSet_3_data_[] = { 4294966271UL, 4294967295UL, 4294967295UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL }; +// 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xb 0xc 0xd 0xe 0xf 0x10 0x11 +// 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f +// ! \" # $ % & \' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 +const antlr::BitSet RequirementLexer::_tokenSet_3(_tokenSet_3_data_,8); +const unsigned long RequirementLexer::_tokenSet_4_data_[] = { 4294967295UL, 4294934527UL, 4294967295UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL }; +// 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 +// 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e +// 0x1f ! \" # $ % & \' ( ) * + , - . 0 1 2 3 4 5 6 7 8 +const antlr::BitSet RequirementLexer::_tokenSet_4(_tokenSet_4_data_,8); +const unsigned long RequirementLexer::_tokenSet_5_data_[] = { 4294967295UL, 4294966271UL, 4294967295UL, 4294967295UL, 0UL, 0UL, 0UL, 0UL }; +// 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 +// 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e +// 0x1f ! \" # $ % & \' ( ) + , - . / 0 1 2 3 4 5 6 7 8 +const antlr::BitSet RequirementLexer::_tokenSet_5(_tokenSet_5_data_,8); + +ANTLR_END_NAMESPACE diff --git a/lib/RequirementLexer.hpp b/lib/RequirementLexer.hpp new file mode 100644 index 0000000..ed5758f --- /dev/null +++ b/lib/RequirementLexer.hpp @@ -0,0 +1,77 @@ +#ifndef INC_RequirementLexer_hpp_ +#define INC_RequirementLexer_hpp_ + +#include +/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementLexer.hpp"$ */ +#include +#include +#include +#include "RequirementParserTokenTypes.hpp" +#include + +#include "requirement.h" +using namespace CodeSigning; +typedef Requirement::Maker Maker; + +ANTLR_BEGIN_NAMESPACE(Security_CodeSigning) +class CUSTOM_API RequirementLexer : public antlr::CharScanner, public RequirementParserTokenTypes +{ +private: + void initLiterals(); +public: + bool getCaseSensitiveLiterals() const + { + return true; + } +public: + RequirementLexer(std::istream& in); + RequirementLexer(antlr::InputBuffer& ib); + RequirementLexer(const antlr::LexerSharedInputState& state); + antlr::RefToken nextToken(); + protected: void mIDENT(bool _createToken); + public: void mDOTKEY(bool _createToken); + public: void mINTEGER(bool _createToken); + public: void mPATHNAME(bool _createToken); + public: void mHASHCONSTANT(bool _createToken); + protected: void mHEX(bool _createToken); + public: void mHEXCONSTANT(bool _createToken); + public: void mSTRING(bool _createToken); + public: void mARROW(bool _createToken); + public: void mSEMI(bool _createToken); + public: void mLPAREN(bool _createToken); + public: void mRPAREN(bool _createToken); + public: void mLBRACK(bool _createToken); + public: void mRBRACK(bool _createToken); + public: void mLESS(bool _createToken); + public: void mGT(bool _createToken); + public: void mLE(bool _createToken); + public: void mGE(bool _createToken); + public: void mCOMMA(bool _createToken); + public: void mEQL(bool _createToken); + public: void mEQQL(bool _createToken); + public: void mSUBS(bool _createToken); + public: void mNEG(bool _createToken); + public: void mNOT(bool _createToken); + public: void mSTAR(bool _createToken); + public: void mWS(bool _createToken); + public: void mSHELLCOMMENT(bool _createToken); + public: void mC_COMMENT(bool _createToken); + public: void mCPP_COMMENT(bool _createToken); +private: + + static const unsigned long _tokenSet_0_data_[]; + static const antlr::BitSet _tokenSet_0; + static const unsigned long _tokenSet_1_data_[]; + static const antlr::BitSet _tokenSet_1; + static const unsigned long _tokenSet_2_data_[]; + static const antlr::BitSet _tokenSet_2; + static const unsigned long _tokenSet_3_data_[]; + static const antlr::BitSet _tokenSet_3; + static const unsigned long _tokenSet_4_data_[]; + static const antlr::BitSet _tokenSet_4; + static const unsigned long _tokenSet_5_data_[]; + static const antlr::BitSet _tokenSet_5; +}; + +ANTLR_END_NAMESPACE +#endif /*INC_RequirementLexer_hpp_*/ diff --git a/lib/RequirementParser.cpp b/lib/RequirementParser.cpp new file mode 100644 index 0000000..90995c9 --- /dev/null +++ b/lib/RequirementParser.cpp @@ -0,0 +1,1303 @@ +/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementParser.cpp"$ */ +#include "RequirementParser.hpp" +#include +#include +#include + +#include "requirement.h" +#include "reqmaker.h" +#include "csutilities.h" +#include +#include +#include // OID coding +using namespace CodeSigning; +typedef Requirement::Maker Maker; + +ANTLR_BEGIN_NAMESPACE(Security_CodeSigning) + + // + // Collect error messages. + // Note that the immediate caller takes the absence of collected error messages + // to indicate compilation success. + // + void RequirementParser::reportError(const antlr::RecognitionException &ex) + { + errors += ex.toString() + "\n"; + } + + void RequirementParser::reportError(const std::string &s) + { + errors += s + "\n"; + } + + + // + // Parser helper functions + // + string RequirementParser::hexString(const string &s) + { + if (s.size() % 2) + throw antlr::SemanticException("odd number of digits"); + const char *p = s.data(); + string result; + for (unsigned n = 0; n < s.length(); n += 2) { + char c; + sscanf(p+n, "%2hhx", &c); + result.push_back(c); + } + return result; + } + + void RequirementParser::hashString(const string &s, SHA1::Digest hash) + { + if (s.size() != 2 * SHA1::digestLength) + throw antlr::SemanticException("invalid hash length"); + memcpy(hash, hexString(s).data(), SHA1::digestLength); + } + + static const char *matchPrefix(const string &key, const char *prefix) + { + unsigned pLength = strlen(prefix); + if (!key.compare(0, pLength, prefix, 0, pLength)) + return key.c_str() + pLength; + else + return NULL; + } + + void RequirementParser::certMatchOperation(Maker &maker, int32_t slot, string key) + { + if (matchPrefix(key, "subject.")) { + maker.put(opCertField); + maker.put(slot); + maker.put(key); + } else if (const char *oids = matchPrefix(key, "field.")) { + maker.put(opCertGeneric); + maker.put(slot); + CssmAutoData oid(Allocator::standard()); oid.fromOid(oids); + maker.putData(oid.data(), oid.length()); + } else if (const char *oids = matchPrefix(key, "extension.")) { + maker.put(opCertGeneric); + maker.put(slot); + CssmAutoData oid(Allocator::standard()); oid.fromOid(oids); + maker.putData(oid.data(), oid.length()); + } else if (const char *oids = matchPrefix(key, "policy.")) { + maker.put(opCertPolicy); + maker.put(slot); + CssmAutoData oid(Allocator::standard()); oid.fromOid(oids); + maker.putData(oid.data(), oid.length()); + } else { + throw antlr::SemanticException(key + ": unrecognized certificate field"); + } + } + +RequirementParser::RequirementParser(antlr::TokenBuffer& tokenBuf, int k) +: antlr::LLkParser(tokenBuf,k) +{ +} + +RequirementParser::RequirementParser(antlr::TokenBuffer& tokenBuf) +: antlr::LLkParser(tokenBuf,2) +{ +} + +RequirementParser::RequirementParser(antlr::TokenStream& lexer, int k) +: antlr::LLkParser(lexer,k) +{ +} + +RequirementParser::RequirementParser(antlr::TokenStream& lexer) +: antlr::LLkParser(lexer,2) +{ +} + +RequirementParser::RequirementParser(const antlr::ParserSharedInputState& state) +: antlr::LLkParser(state,2) +{ +} + +BlobCore * RequirementParser::autosense() { + BlobCore *result = NULL; + + try { // for error handling + switch ( LA(1)) { + case LPAREN: + case NOT: + case LITERAL_always: + case LITERAL_true: + case LITERAL_never: + case LITERAL_false: + case LITERAL_identifier: + case LITERAL_cdhash: + case LITERAL_anchor: + case LITERAL_certificate: + case LITERAL_cert: + case LITERAL_info: + case LITERAL_entitlement: + { + result=requirement(); + break; + } + case LITERAL_guest: + case LITERAL_host: + case LITERAL_designated: + case LITERAL_library: + case LITERAL_plugin: + case INTEGER: + { + result=requirementSet(); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_0); + } + return result; +} + +Requirement * RequirementParser::requirement() { + Requirement *result = NULL; + + try { // for error handling + result=requirementElement(); + match(antlr::Token::EOF_TYPE); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_0); + } + return result; +} + +Requirements * RequirementParser::requirementSet() { + Requirements *result = NULL; + Requirements::Maker maker; + + try { // for error handling + { // ( ... )+ + int _cnt4=0; + for (;;) { + if (((LA(1) >= LITERAL_guest && LA(1) <= INTEGER))) { + uint32_t t; Requirement *req; + t=requirementType(); + match(ARROW); + req=requirementElement(); + maker.add(t, req); + } + else { + if ( _cnt4>=1 ) { goto _loop4; } else {throw antlr::NoViableAltException(LT(1), getFilename());} + } + + _cnt4++; + } + _loop4:; + } // ( ... )+ + result = errors.empty() ? maker() : NULL; + match(antlr::Token::EOF_TYPE); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_0); + } + return result; +} + +uint32_t RequirementParser::requirementType() { + uint32_t type = kSecInvalidRequirementType; + antlr::RefToken stype = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case LITERAL_guest: + { + match(LITERAL_guest); + type = kSecGuestRequirementType; + break; + } + case LITERAL_host: + { + match(LITERAL_host); + type = kSecHostRequirementType; + break; + } + case LITERAL_designated: + { + match(LITERAL_designated); + type = kSecDesignatedRequirementType; + break; + } + case LITERAL_library: + { + match(LITERAL_library); + type = kSecLibraryRequirementType; + break; + } + case LITERAL_plugin: + { + match(LITERAL_plugin); + type = kSecPluginRequirementType; + break; + } + case INTEGER: + { + stype = LT(1); + match(INTEGER); + type = atol(stype->getText().c_str()); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_1); + } + return type; +} + +Requirement * RequirementParser::requirementElement() { + Requirement *result = NULL; + Requirement::Maker maker; + + try { // for error handling + expr(maker); + result = maker(); + { // ( ... )* + for (;;) { + if ((LA(1) == SEMI)) { + fluff(); + } + else { + goto _loop9; + } + + } + _loop9:; + } // ( ... )* + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_2); + } + return result; +} + +void RequirementParser::expr( + Maker &maker +) { + Maker::Label label(maker); + + try { // for error handling + term(maker); + { // ( ... )* + for (;;) { + if ((LA(1) == LITERAL_or)) { + match(LITERAL_or); + maker.insert(label) = opOr; + term(maker); + } + else { + goto _loop12; + } + + } + _loop12:; + } // ( ... )* + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_3); + } +} + +void RequirementParser::fluff() { + + try { // for error handling + match(SEMI); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_4); + } +} + +void RequirementParser::term( + Maker &maker +) { + Maker::Label label(maker); + + try { // for error handling + primary(maker); + { // ( ... )* + for (;;) { + if ((LA(1) == LITERAL_and)) { + match(LITERAL_and); + maker.insert(label) = opAnd; + primary(maker); + } + else { + goto _loop15; + } + + } + _loop15:; + } // ( ... )* + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_5); + } +} + +void RequirementParser::primary( + Maker &maker +) { + + try { // for error handling + switch ( LA(1)) { + case NOT: + { + match(NOT); + maker.put(opNot); + primary(maker); + break; + } + case LITERAL_always: + case LITERAL_true: + { + { + switch ( LA(1)) { + case LITERAL_always: + { + match(LITERAL_always); + break; + } + case LITERAL_true: + { + match(LITERAL_true); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + maker.put(opTrue); + break; + } + case LITERAL_never: + case LITERAL_false: + { + { + switch ( LA(1)) { + case LITERAL_never: + { + match(LITERAL_never); + break; + } + case LITERAL_false: + { + match(LITERAL_false); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + maker.put(opFalse); + break; + } + case LITERAL_anchor: + case LITERAL_certificate: + case LITERAL_cert: + { + certspec(maker); + break; + } + case LITERAL_info: + { + infospec(maker); + break; + } + case LITERAL_entitlement: + { + entitlementspec(maker); + break; + } + case LITERAL_identifier: + { + match(LITERAL_identifier); + string code; + eql(); + code=identifierString(); + maker.ident(code); + break; + } + case LITERAL_cdhash: + { + match(LITERAL_cdhash); + SHA1::Digest digest; + eql(); + hash(digest); + maker.cdhash(digest); + break; + } + default: + if ((LA(1) == LPAREN) && (_tokenSet_6.member(LA(2)))) { + match(LPAREN); + expr(maker); + match(RPAREN); + } + else if ((LA(1) == LPAREN) && (LA(2) == DOTKEY || LA(2) == STRING)) { + match(LPAREN); + string name; + name=identifierString(); + match(RPAREN); + maker.put(opNamedCode); maker.put(name); + } + else { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::certspec( + Maker &maker +) { + + try { // for error handling + if ((LA(1) == LITERAL_anchor) && (LA(2) == LITERAL_apple)) { + match(LITERAL_anchor); + match(LITERAL_apple); + appleanchor(maker); + } + else if ((LA(1) == LITERAL_anchor) && (LA(2) == LITERAL_generic)) { + match(LITERAL_anchor); + match(LITERAL_generic); + match(LITERAL_apple); + maker.put(opAppleGenericAnchor); + } + else if ((LA(1) == LITERAL_anchor || LA(1) == LITERAL_certificate || LA(1) == LITERAL_cert) && (LA(2) == LITERAL_trusted)) { + { + switch ( LA(1)) { + case LITERAL_certificate: + { + match(LITERAL_certificate); + break; + } + case LITERAL_cert: + { + match(LITERAL_cert); + break; + } + case LITERAL_anchor: + { + match(LITERAL_anchor); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + match(LITERAL_trusted); + maker.trustedAnchor(); + } + else if ((LA(1) == LITERAL_certificate || LA(1) == LITERAL_cert) && (_tokenSet_8.member(LA(2)))) { + { + switch ( LA(1)) { + case LITERAL_certificate: + { + match(LITERAL_certificate); + break; + } + case LITERAL_cert: + { + match(LITERAL_cert); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + int32_t slot; + slot=certSlot(); + { + switch ( LA(1)) { + case EQL: + case EQQL: + case LBRACK: + case HASHCONSTANT: + case DOTKEY: + case STRING: + case PATHNAME: + { + certslotspec(maker, slot); + break; + } + case LITERAL_trusted: + { + match(LITERAL_trusted); + maker.trustedAnchor(slot); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + } + else if ((LA(1) == LITERAL_anchor) && (_tokenSet_9.member(LA(2)))) { + match(LITERAL_anchor); + certslotspec(maker, Requirement::anchorCert); + } + else { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::infospec( + Maker &maker +) { + string key; + + try { // for error handling + match(LITERAL_info); + key=bracketKey(); + maker.put(opInfoKeyField); maker.put(key); + match_suffix(maker); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::entitlementspec( + Maker &maker +) { + string key; + + try { // for error handling + match(LITERAL_entitlement); + key=bracketKey(); + maker.put(opEntitlementField); maker.put(key); + match_suffix(maker); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::eql() { + + try { // for error handling + switch ( LA(1)) { + case EQL: + { + match(EQL); + break; + } + case EQQL: + { + match(EQQL); + break; + } + case HASHCONSTANT: + case DOTKEY: + case STRING: + case PATHNAME: + { + empty(); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_10); + } +} + +string RequirementParser::identifierString() { + string result; + antlr::RefToken dk = antlr::nullToken; + antlr::RefToken s = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case DOTKEY: + { + dk = LT(1); + match(DOTKEY); + result = dk->getText(); + break; + } + case STRING: + { + s = LT(1); + match(STRING); + result = s->getText(); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } + return result; +} + +void RequirementParser::hash( + SHA1::Digest digest +) { + antlr::RefToken hash = antlr::nullToken; + + try { // for error handling + hash = LT(1); + match(HASHCONSTANT); + hashString(hash->getText(), digest); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::appleanchor( + Maker &maker +) { + + try { // for error handling + switch ( LA(1)) { + case antlr::Token::EOF_TYPE: + case LITERAL_guest: + case LITERAL_host: + case LITERAL_designated: + case LITERAL_library: + case LITERAL_plugin: + case INTEGER: + case LITERAL_or: + case LITERAL_and: + case RPAREN: + case SEMI: + { + empty(); + maker.put(opAppleAnchor); + break; + } + case LITERAL_generic: + { + match(LITERAL_generic); + maker.put(opAppleGenericAnchor); + break; + } + case DOTKEY: + case STRING: + { + string name; + name=identifierString(); + maker.put(opNamedAnchor); maker.put(name); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +int32_t RequirementParser::certSlot() { + int32_t slot = 0; + antlr::RefToken s = antlr::nullToken; + antlr::RefToken ss = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case INTEGER: + { + s = LT(1); + match(INTEGER); + slot = atol(s->getText().c_str()); + break; + } + case NEG: + { + match(NEG); + ss = LT(1); + match(INTEGER); + slot = -atol(ss->getText().c_str()); + break; + } + case LITERAL_leaf: + { + match(LITERAL_leaf); + slot = Requirement::leafCert; + break; + } + case LITERAL_root: + { + match(LITERAL_root); + slot = Requirement::anchorCert; + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_11); + } + return slot; +} + +void RequirementParser::certslotspec( + Maker &maker, int32_t slot +) { + string key; + + try { // for error handling + switch ( LA(1)) { + case EQL: + case EQQL: + case HASHCONSTANT: + case DOTKEY: + case STRING: + case PATHNAME: + { + eql(); + SHA1::Digest digest; + certificateDigest(digest); + maker.anchor(slot, digest); + break; + } + case LBRACK: + { + key=bracketKey(); + certMatchOperation(maker, slot, key); + match_suffix(maker); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +void RequirementParser::empty() { + + try { // for error handling + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_12); + } +} + +void RequirementParser::certificateDigest( + SHA1::Digest digest +) { + + try { // for error handling + switch ( LA(1)) { + case HASHCONSTANT: + { + hash(digest); + break; + } + case DOTKEY: + case STRING: + case PATHNAME: + { + string path; + path=pathstring(); + if (CFRef certData = cfLoadFile(path)) + hashOfCertificate(CFDataGetBytePtr(certData), CFDataGetLength(certData), digest); + else + throw antlr::SemanticException(path + ": not found"); + + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +string RequirementParser::bracketKey() { + string key; + + try { // for error handling + match(LBRACK); + key=stringvalue(); + match(RBRACK); + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_13); + } + return key; +} + +void RequirementParser::match_suffix( + Maker &maker +) { + + try { // for error handling + switch ( LA(1)) { + case antlr::Token::EOF_TYPE: + case LITERAL_guest: + case LITERAL_host: + case LITERAL_designated: + case LITERAL_library: + case LITERAL_plugin: + case INTEGER: + case LITERAL_or: + case LITERAL_and: + case RPAREN: + case LITERAL_exists: + case SEMI: + { + empty(); + { + switch ( LA(1)) { + case LITERAL_exists: + { + match(LITERAL_exists); + break; + } + case antlr::Token::EOF_TYPE: + case LITERAL_guest: + case LITERAL_host: + case LITERAL_designated: + case LITERAL_library: + case LITERAL_plugin: + case INTEGER: + case LITERAL_or: + case LITERAL_and: + case RPAREN: + case SEMI: + { + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + maker.put(matchExists); + break; + } + case EQL: + case EQQL: + { + { + switch ( LA(1)) { + case EQL: + { + match(EQL); + break; + } + case EQQL: + { + match(EQQL); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + MatchOperation mop = matchEqual; string value; + { + switch ( LA(1)) { + case STAR: + { + match(STAR); + mop = matchEndsWith; + break; + } + case HEXCONSTANT: + case DOTKEY: + case STRING: + { + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + value=datavalue(); + { + switch ( LA(1)) { + case STAR: + { + match(STAR); + mop = (mop == matchEndsWith) ? matchContains : matchBeginsWith; + break; + } + case antlr::Token::EOF_TYPE: + case LITERAL_guest: + case LITERAL_host: + case LITERAL_designated: + case LITERAL_library: + case LITERAL_plugin: + case INTEGER: + case LITERAL_or: + case LITERAL_and: + case RPAREN: + case SEMI: + { + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + maker.put(mop); maker.put(value); + break; + } + case SUBS: + { + match(SUBS); + string value; + value=datavalue(); + maker.put(matchContains); maker.put(value); + break; + } + case LESS: + { + match(LESS); + string value; + value=datavalue(); + maker.put(matchLessThan); maker.put(value); + break; + } + case GT: + { + match(GT); + string value; + value=datavalue(); + maker.put(matchGreaterThan); maker.put(value); + break; + } + case LE: + { + match(LE); + string value; + value=datavalue(); + maker.put(matchLessEqual); maker.put(value); + break; + } + case GE: + { + match(GE); + string value; + value=datavalue(); + maker.put(matchGreaterEqual); maker.put(value); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } +} + +string RequirementParser::datavalue() { + string result; + antlr::RefToken hex = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case DOTKEY: + case STRING: + { + result=stringvalue(); + break; + } + case HEXCONSTANT: + { + hex = LT(1); + match(HEXCONSTANT); + result = hexString(hex->getText()); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_14); + } + return result; +} + +string RequirementParser::stringvalue() { + string result; + antlr::RefToken dk = antlr::nullToken; + antlr::RefToken s = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case DOTKEY: + { + dk = LT(1); + match(DOTKEY); + result = dk->getText(); + break; + } + case STRING: + { + s = LT(1); + match(STRING); + result = s->getText(); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_15); + } + return result; +} + +string RequirementParser::pathstring() { + string result; + antlr::RefToken dk = antlr::nullToken; + antlr::RefToken s = antlr::nullToken; + antlr::RefToken pn = antlr::nullToken; + + try { // for error handling + switch ( LA(1)) { + case DOTKEY: + { + dk = LT(1); + match(DOTKEY); + result = dk->getText(); + break; + } + case STRING: + { + s = LT(1); + match(STRING); + result = s->getText(); + break; + } + case PATHNAME: + { + pn = LT(1); + match(PATHNAME); + result = pn->getText(); + break; + } + default: + { + throw antlr::NoViableAltException(LT(1), getFilename()); + } + } + } + catch (antlr::RecognitionException& ex) { + reportError(ex); + recover(ex,_tokenSet_7); + } + return result; +} + +void RequirementParser::initializeASTFactory( antlr::ASTFactory& ) +{ +} +const char* RequirementParser::tokenNames[] = { + "<0>", + "EOF", + "<2>", + "NULL_TREE_LOOKAHEAD", + "ARROW", + "\"guest\"", + "\"host\"", + "\"designated\"", + "\"library\"", + "\"plugin\"", + "INTEGER", + "\"or\"", + "\"and\"", + "LPAREN", + "RPAREN", + "NOT", + "\"always\"", + "\"true\"", + "\"never\"", + "\"false\"", + "\"identifier\"", + "\"cdhash\"", + "\"anchor\"", + "\"apple\"", + "\"generic\"", + "\"certificate\"", + "\"cert\"", + "\"trusted\"", + "\"info\"", + "\"entitlement\"", + "\"exists\"", + "EQL", + "EQQL", + "STAR", + "SUBS", + "LESS", + "GT", + "LE", + "GE", + "LBRACK", + "RBRACK", + "NEG", + "\"leaf\"", + "\"root\"", + "HASHCONSTANT", + "HEXCONSTANT", + "DOTKEY", + "STRING", + "PATHNAME", + "SEMI", + "IDENT", + "HEX", + "COMMA", + "WS", + "SHELLCOMMENT", + "C_COMMENT", + "CPP_COMMENT", + 0 +}; + +const unsigned long RequirementParser::_tokenSet_0_data_[] = { 2UL, 0UL, 0UL, 0UL }; +// EOF +const antlr::BitSet RequirementParser::_tokenSet_0(_tokenSet_0_data_,4); +const unsigned long RequirementParser::_tokenSet_1_data_[] = { 16UL, 0UL, 0UL, 0UL }; +// ARROW +const antlr::BitSet RequirementParser::_tokenSet_1(_tokenSet_1_data_,4); +const unsigned long RequirementParser::_tokenSet_2_data_[] = { 2018UL, 0UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER +const antlr::BitSet RequirementParser::_tokenSet_2(_tokenSet_2_data_,4); +const unsigned long RequirementParser::_tokenSet_3_data_[] = { 18402UL, 131072UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER RPAREN SEMI +const antlr::BitSet RequirementParser::_tokenSet_3(_tokenSet_3_data_,4); +const unsigned long RequirementParser::_tokenSet_4_data_[] = { 2018UL, 131072UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER SEMI +const antlr::BitSet RequirementParser::_tokenSet_4(_tokenSet_4_data_,4); +const unsigned long RequirementParser::_tokenSet_5_data_[] = { 20450UL, 131072UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" RPAREN +// SEMI +const antlr::BitSet RequirementParser::_tokenSet_5(_tokenSet_5_data_,4); +const unsigned long RequirementParser::_tokenSet_6_data_[] = { 914333696UL, 0UL, 0UL, 0UL }; +// LPAREN NOT "always" "true" "never" "false" "identifier" "cdhash" "anchor" +// "certificate" "cert" "info" "entitlement" +const antlr::BitSet RequirementParser::_tokenSet_6(_tokenSet_6_data_,4); +const unsigned long RequirementParser::_tokenSet_7_data_[] = { 24546UL, 131072UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" "and" +// RPAREN SEMI +const antlr::BitSet RequirementParser::_tokenSet_7(_tokenSet_7_data_,4); +const unsigned long RequirementParser::_tokenSet_8_data_[] = { 1024UL, 3584UL, 0UL, 0UL }; +// INTEGER NEG "leaf" "root" +const antlr::BitSet RequirementParser::_tokenSet_8(_tokenSet_8_data_,4); +const unsigned long RequirementParser::_tokenSet_9_data_[] = { 2147483648UL, 118913UL, 0UL, 0UL }; +// EQL EQQL LBRACK HASHCONSTANT DOTKEY STRING PATHNAME +const antlr::BitSet RequirementParser::_tokenSet_9(_tokenSet_9_data_,4); +const unsigned long RequirementParser::_tokenSet_10_data_[] = { 0UL, 118784UL, 0UL, 0UL }; +// HASHCONSTANT DOTKEY STRING PATHNAME +const antlr::BitSet RequirementParser::_tokenSet_10(_tokenSet_10_data_,4); +const unsigned long RequirementParser::_tokenSet_11_data_[] = { 2281701376UL, 118913UL, 0UL, 0UL }; +// "trusted" EQL EQQL LBRACK HASHCONSTANT DOTKEY STRING PATHNAME +const antlr::BitSet RequirementParser::_tokenSet_11(_tokenSet_11_data_,4); +const unsigned long RequirementParser::_tokenSet_12_data_[] = { 1073766370UL, 249856UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" "and" +// RPAREN "exists" HASHCONSTANT DOTKEY STRING PATHNAME SEMI +const antlr::BitSet RequirementParser::_tokenSet_12(_tokenSet_12_data_,4); +const unsigned long RequirementParser::_tokenSet_13_data_[] = { 3221250018UL, 131197UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" "and" +// RPAREN "exists" EQL EQQL SUBS LESS GT LE GE SEMI +const antlr::BitSet RequirementParser::_tokenSet_13(_tokenSet_13_data_,4); +const unsigned long RequirementParser::_tokenSet_14_data_[] = { 24546UL, 131074UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" "and" +// RPAREN STAR SEMI +const antlr::BitSet RequirementParser::_tokenSet_14(_tokenSet_14_data_,4); +const unsigned long RequirementParser::_tokenSet_15_data_[] = { 24546UL, 131330UL, 0UL, 0UL }; +// EOF "guest" "host" "designated" "library" "plugin" INTEGER "or" "and" +// RPAREN STAR RBRACK SEMI +const antlr::BitSet RequirementParser::_tokenSet_15(_tokenSet_15_data_,4); + + +ANTLR_END_NAMESPACE diff --git a/lib/RequirementParser.hpp b/lib/RequirementParser.hpp new file mode 100644 index 0000000..c93719e --- /dev/null +++ b/lib/RequirementParser.hpp @@ -0,0 +1,153 @@ +#ifndef INC_RequirementParser_hpp_ +#define INC_RequirementParser_hpp_ + +#include +/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementParser.hpp"$ */ +#include +#include +#include "RequirementParserTokenTypes.hpp" +#include + + +#include "requirement.h" +using namespace CodeSigning; +typedef Requirement::Maker Maker; + +ANTLR_BEGIN_NAMESPACE(Security_CodeSigning) +class CUSTOM_API RequirementParser : public antlr::LLkParser, public RequirementParserTokenTypes +{ + +public: + std::string errors; + void reportError(const antlr::RecognitionException &ex); + void reportError(const std::string &s); + +private: + static string hexString(const string &s); + static void hashString(const string &s, SHA1::Digest hash); + void certMatchOperation(Maker &maker, int32_t slot, string key); +public: + void initializeASTFactory( antlr::ASTFactory& factory ); +protected: + RequirementParser(antlr::TokenBuffer& tokenBuf, int k); +public: + RequirementParser(antlr::TokenBuffer& tokenBuf); +protected: + RequirementParser(antlr::TokenStream& lexer, int k); +public: + RequirementParser(antlr::TokenStream& lexer); + RequirementParser(const antlr::ParserSharedInputState& state); + int getNumTokens() const + { + return RequirementParser::NUM_TOKENS; + } + const char* getTokenName( int type ) const + { + if( type > getNumTokens() ) return 0; + return RequirementParser::tokenNames[type]; + } + const char* const* getTokenNames() const + { + return RequirementParser::tokenNames; + } + public: BlobCore * autosense(); + public: Requirement * requirement(); + public: Requirements * requirementSet(); + public: uint32_t requirementType(); + public: Requirement * requirementElement(); + public: void expr( + Maker &maker + ); + public: void fluff(); + public: void term( + Maker &maker + ); + public: void primary( + Maker &maker + ); + public: void certspec( + Maker &maker + ); + public: void infospec( + Maker &maker + ); + public: void entitlementspec( + Maker &maker + ); + public: void eql(); + public: string identifierString(); + public: void hash( + SHA1::Digest digest + ); + public: void appleanchor( + Maker &maker + ); + public: int32_t certSlot(); + public: void certslotspec( + Maker &maker, int32_t slot + ); + public: void empty(); + public: void certificateDigest( + SHA1::Digest digest + ); + public: string bracketKey(); + public: void match_suffix( + Maker &maker + ); + public: string datavalue(); + public: string stringvalue(); + public: string pathstring(); +public: + antlr::RefAST getAST() + { + return returnAST; + } + +protected: + antlr::RefAST returnAST; +private: + static const char* tokenNames[]; +#ifndef NO_STATIC_CONSTS + static const int NUM_TOKENS = 57; +#else + enum { + NUM_TOKENS = 57 + }; +#endif + + static const unsigned long _tokenSet_0_data_[]; + static const antlr::BitSet _tokenSet_0; + static const unsigned long _tokenSet_1_data_[]; + static const antlr::BitSet _tokenSet_1; + static const unsigned long _tokenSet_2_data_[]; + static const antlr::BitSet _tokenSet_2; + static const unsigned long _tokenSet_3_data_[]; + static const antlr::BitSet _tokenSet_3; + static const unsigned long _tokenSet_4_data_[]; + static const antlr::BitSet _tokenSet_4; + static const unsigned long _tokenSet_5_data_[]; + static const antlr::BitSet _tokenSet_5; + static const unsigned long _tokenSet_6_data_[]; + static const antlr::BitSet _tokenSet_6; + static const unsigned long _tokenSet_7_data_[]; + static const antlr::BitSet _tokenSet_7; + static const unsigned long _tokenSet_8_data_[]; + static const antlr::BitSet _tokenSet_8; + static const unsigned long _tokenSet_9_data_[]; + static const antlr::BitSet _tokenSet_9; + static const unsigned long _tokenSet_10_data_[]; + static const antlr::BitSet _tokenSet_10; + static const unsigned long _tokenSet_11_data_[]; + static const antlr::BitSet _tokenSet_11; + static const unsigned long _tokenSet_12_data_[]; + static const antlr::BitSet _tokenSet_12; + static const unsigned long _tokenSet_13_data_[]; + static const antlr::BitSet _tokenSet_13; + static const unsigned long _tokenSet_14_data_[]; + static const antlr::BitSet _tokenSet_14; + static const unsigned long _tokenSet_15_data_[]; + static const antlr::BitSet _tokenSet_15; +}; + +ANTLR_END_NAMESPACE +#endif /*INC_RequirementParser_hpp_*/ diff --git a/lib/RequirementParserTokenTypes.hpp b/lib/RequirementParserTokenTypes.hpp new file mode 100644 index 0000000..388c378 --- /dev/null +++ b/lib/RequirementParserTokenTypes.hpp @@ -0,0 +1,75 @@ +#ifndef INC_RequirementParserTokenTypes_hpp_ +#define INC_RequirementParserTokenTypes_hpp_ + +ANTLR_BEGIN_NAMESPACE(Security_CodeSigning) +/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementParserTokenTypes.hpp"$ */ + +#ifndef CUSTOM_API +# define CUSTOM_API +#endif + +#ifdef __cplusplus +struct CUSTOM_API RequirementParserTokenTypes { +#endif + enum { + EOF_ = 1, + ARROW = 4, + LITERAL_guest = 5, + LITERAL_host = 6, + LITERAL_designated = 7, + LITERAL_library = 8, + LITERAL_plugin = 9, + INTEGER = 10, + LITERAL_or = 11, + LITERAL_and = 12, + LPAREN = 13, + RPAREN = 14, + NOT = 15, + LITERAL_always = 16, + LITERAL_true = 17, + LITERAL_never = 18, + LITERAL_false = 19, + LITERAL_identifier = 20, + LITERAL_cdhash = 21, + LITERAL_anchor = 22, + LITERAL_apple = 23, + LITERAL_generic = 24, + LITERAL_certificate = 25, + LITERAL_cert = 26, + LITERAL_trusted = 27, + LITERAL_info = 28, + LITERAL_entitlement = 29, + LITERAL_exists = 30, + EQL = 31, + EQQL = 32, + STAR = 33, + SUBS = 34, + LESS = 35, + GT = 36, + LE = 37, + GE = 38, + LBRACK = 39, + RBRACK = 40, + NEG = 41, + LITERAL_leaf = 42, + LITERAL_root = 43, + HASHCONSTANT = 44, + HEXCONSTANT = 45, + DOTKEY = 46, + STRING = 47, + PATHNAME = 48, + SEMI = 49, + IDENT = 50, + HEX = 51, + COMMA = 52, + WS = 53, + SHELLCOMMENT = 54, + C_COMMENT = 55, + CPP_COMMENT = 56, + NULL_TREE_LOOKAHEAD = 3 + }; +#ifdef __cplusplus +}; +#endif +ANTLR_END_NAMESPACE +#endif /*INC_RequirementParserTokenTypes_hpp_*/ diff --git a/lib/RequirementParserTokenTypes.txt b/lib/RequirementParserTokenTypes.txt new file mode 100644 index 0000000..5bb5975 --- /dev/null +++ b/lib/RequirementParserTokenTypes.txt @@ -0,0 +1,55 @@ +// $ANTLR 2.7.7 (20120228): requirements.grammar -> RequirementParserTokenTypes.txt$ +RequirementParser // output token vocab name +ARROW=4 +LITERAL_guest="guest"=5 +LITERAL_host="host"=6 +LITERAL_designated="designated"=7 +LITERAL_library="library"=8 +LITERAL_plugin="plugin"=9 +INTEGER=10 +LITERAL_or="or"=11 +LITERAL_and="and"=12 +LPAREN=13 +RPAREN=14 +NOT=15 +LITERAL_always="always"=16 +LITERAL_true="true"=17 +LITERAL_never="never"=18 +LITERAL_false="false"=19 +LITERAL_identifier="identifier"=20 +LITERAL_cdhash="cdhash"=21 +LITERAL_anchor="anchor"=22 +LITERAL_apple="apple"=23 +LITERAL_generic="generic"=24 +LITERAL_certificate="certificate"=25 +LITERAL_cert="cert"=26 +LITERAL_trusted="trusted"=27 +LITERAL_info="info"=28 +LITERAL_entitlement="entitlement"=29 +LITERAL_exists="exists"=30 +EQL=31 +EQQL=32 +STAR=33 +SUBS=34 +LESS=35 +GT=36 +LE=37 +GE=38 +LBRACK=39 +RBRACK=40 +NEG=41 +LITERAL_leaf="leaf"=42 +LITERAL_root="root"=43 +HASHCONSTANT=44 +HEXCONSTANT=45 +DOTKEY=46 +STRING=47 +PATHNAME=48 +SEMI=49 +IDENT=50 +HEX=51 +COMMA=52 +WS=53 +SHELLCOMMENT=54 +C_COMMENT=55 +CPP_COMMENT=56 diff --git a/lib/SecAssessment.cpp b/lib/SecAssessment.cpp index fbecc41..714bba8 100644 --- a/lib/SecAssessment.cpp +++ b/lib/SecAssessment.cpp @@ -126,6 +126,8 @@ const CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:autho const CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override"); const CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached"); +const CFStringRef kDisabledOverride = CFSTR("security disabled"); + const CFStringRef kSecAssessmentContextKeyCertificates = CFSTR("context:certificates"); // obsolete SecAssessmentRef SecAssessmentCreate(CFURLRef path, @@ -134,7 +136,6 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path, CFErrorRef *errors) { BEGIN_CSAPI - SYSPOLICY_ASSESS_API(cfString(path).c_str(), flags); if (flags & kSecAssessmentFlagAsynchronous) MacOSError::throwMe(errSecCSUnimplemented); @@ -142,6 +143,8 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path, AuthorityType type = typeFor(context, kAuthorityExecute); CFRef result = makeCFMutableDictionary(); + SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags); + try { // check the object cache first unless caller denied that or we need extended processing if (!(flags & (kSecAssessmentFlagRequestOrigin | kSecAssessmentFlagIgnoreCache))) { @@ -209,8 +212,9 @@ static void traceResult(SecAssessment &assessment, CFDictionaryRef result) authority = cfString(auth); else authority = "no authority"; - if (CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride)) - overridden = true; + if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride)) + if (CFEqual(override, kDisabledOverride)) + overridden = true; } MessageTrace trace("com.apple.security.assessment.outcome", NULL); @@ -250,11 +254,11 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef, CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue); if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) { CFRef authority2 = makeCFMutableDictionary(authority); - CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, CFSTR("security disabled")); + CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2); } else { - cfadd(adulterated, "{%O={%O='security disabled'}}", - kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride); + cfadd(adulterated, "{%O={%O=%O}}", + kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); } result = adulterated.get(); } @@ -275,6 +279,7 @@ const CFStringRef kSecAssessmentUpdateOperationAdd = CFSTR("update:add"); const CFStringRef kSecAssessmentUpdateOperationRemove = CFSTR("update:remove"); const CFStringRef kSecAssessmentUpdateOperationEnable = CFSTR("update:enable"); const CFStringRef kSecAssessmentUpdateOperationDisable = CFSTR("update:disable"); +const CFStringRef kSecAssessmentUpdateOperationFind = CFSTR("update:find"); const CFStringRef kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization"); const CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority"); @@ -283,10 +288,39 @@ const CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires"); const CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow"); const CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks"); +const CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row"); +const CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count"); +const CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found"); + +const CFStringRef kSecAssessmentRuleKeyID = CFSTR("rule:id"); +const CFStringRef kSecAssessmentRuleKeyPriority = CFSTR("rule:priority"); +const CFStringRef kSecAssessmentRuleKeyAllow = CFSTR("rule:allow"); +const CFStringRef kSecAssessmentRuleKeyLabel = CFSTR("rule:label"); +const CFStringRef kSecAssessmentRuleKeyRemarks = CFSTR("rule:remarks"); +const CFStringRef kSecAssessmentRuleKeyRequirement = CFSTR("rule:requirement"); +const CFStringRef kSecAssessmentRuleKeyType = CFSTR("rule:type"); +const CFStringRef kSecAssessmentRuleKeyExpires = CFSTR("rule:expires"); +const CFStringRef kSecAssessmentRuleKeyDisabled = CFSTR("rule:disabled"); +const CFStringRef kSecAssessmentRuleKeyBookmark = CFSTR("rule:bookmark"); + + Boolean SecAssessmentUpdate(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context, CFErrorRef *errors) +{ + if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) { + CFRelease(outcome); + return true; + } else { + return false; + } +} + +CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target, + SecAssessmentFlags flags, + CFDictionaryRef context, + CFErrorRef *errors) { BEGIN_CSAPI @@ -313,19 +347,15 @@ Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *e BEGIN_CSAPI if (CFEqual(control, CFSTR("ui-enable"))) { - { UnixPlusPlus::AutoFileDesc flagFile(visibleSecurityFlagFile, O_CREAT | O_WRONLY, 0644); } - notify_post(kNotifySecAssessmentMasterSwitch); + setAssessment(true); MessageTrace trace("com.apple.security.assessment.state", "enable"); trace.send("enable assessment outcomes"); return true; } else if (CFEqual(control, CFSTR("ui-disable"))) { - if (::remove(visibleSecurityFlagFile) == 0 || errno == ENOENT) { - notify_post(kNotifySecAssessmentMasterSwitch); - MessageTrace trace("com.apple.security.assessment.state", "disable"); - trace.send("disable assessment outcomes"); - return true; - } - UnixError::throwMe(); + setAssessment(false); + MessageTrace trace("com.apple.security.assessment.state", "disable"); + trace.send("disable assessment outcomes"); + return true; } else if (CFEqual(control, CFSTR("ui-status"))) { CFBooleanRef &result = *(CFBooleanRef*)(arguments); if (overrideAssessment()) @@ -335,10 +365,14 @@ Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *e return true; } else if (CFEqual(control, CFSTR("ui-enable-devid"))) { CFTemp ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID"); - return gEngine().enable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx); + if (CFDictionaryRef result = gEngine().enable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx)) + CFRelease(result); + return true; } else if (CFEqual(control, CFSTR("ui-disable-devid"))) { CFTemp ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID"); - return gEngine().disable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx); + if (CFDictionaryRef result = gEngine().disable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx)) + CFRelease(result); + return true; } else if (CFEqual(control, CFSTR("ui-get-devid"))) { CFBooleanRef &result = *(CFBooleanRef*)(arguments); if (gEngine().value("SELECT disabled FROM authority WHERE label = 'Developer ID';", true)) diff --git a/lib/SecAssessment.h b/lib/SecAssessment.h index 9cae775..b8883a6 100644 --- a/lib/SecAssessment.h +++ b/lib/SecAssessment.h @@ -124,8 +124,6 @@ enum { @constant kSecAssessmentContextKeyOperation Type of operation (see overview above). This defaults to the kSecAssessmentOperationTypeExecute. */ -extern const CFStringRef kSecAssessmentContextKeyCertificates; // certificate chain as provided by caller - extern const CFStringRef kSecAssessmentAssessmentVerdict; // CFBooleanRef: master result - allow or deny extern const CFStringRef kSecAssessmentAssessmentOriginator; // CFStringRef: describing the signature originator extern const CFStringRef kSecAssessmentAssessmentAuthority; // CFDictionaryRef: authority used to arrive at result @@ -134,6 +132,8 @@ extern const CFStringRef kSecAssessmentAssessmentFromCache; // present if result extern const CFStringRef kSecAssessmentAssessmentAuthorityRow; // (internal) extern const CFStringRef kSecAssessmentAssessmentAuthorityOverride; // (internal) +extern const CFStringRef kDisabledOverride; // AuthorityOverride value for "Gatekeeper is disabled" + enum { kSecAssessmentFlagRequestOrigin = 1 << 0, // request origin information (slower) }; @@ -174,7 +174,7 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment, /*! - @function SecAssessmentUpdate + @function SecAssessmentCopyUpdate Make changes to the system policy configuration. @param path CFTypeRef describing the subject of the operation. Depending on the operation, @@ -187,7 +187,10 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment, on the requested assessment. Must at least contain the kSecAssessmentContextKeyEdit key. @param errors Standard CFError argument for reporting errors. Note that declining to permit the proposed operation is not an error. Inability to form a judgment is. - @result Returns True on success. Returns False on failure (and sets *error). + @result Returns On success, a CFDictionary containing information pertaining to the completed operation. + Caller must CFRelease it when done. On failure, NULL, with *errors set if provided. + + Note: The SecAssessmentUpdate variant does not return data. It returns True on success, or False on error. Context keys and values: @@ -197,6 +200,9 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment, @constant kSecAssessmentUpdateOperationRemove Remove rules from the rule database. @constant kSecAssessmentUpdateOperationEnable (Re)enable rules in the rule database. @constant kSecAssessmentUpdateOperationDisable Disable rules in the rule database. + @constant kSecAssessmentUpdateOperationFind Locate and return rules from the rule database. + This operation does not change the database, and does not require authorization or privileges. + @constant kSecAssessmentUpdateKeyAuthorization A CFData containing the external form of a system AuthorizationRef used to authorize the change. The call will automatically generate a suitable authorization if this is missing; however, if the request is on behalf of @@ -211,13 +217,32 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment, assessment. The default is to allow; set to kCFBooleanFalse to create a negative (denial) rule. @constant kSecAssessmentUpdateKeyRemarks CFString containing a colloquial description or comment about a newly created rule. This is mean to be human readable and is not used when evaluating rules. + + Keys returned as the result of a successful kSecAssessmentUpdateOperationFind operation: + + @constant kSecAssessmentRuleKeyID A CFNumber uniquely identifying a rule. + @constant kSecAssessmentRuleKeyPriority A CFNumber indicating the rule's priority. + This is a floating point number. Higher values indicate higher priority. + @constant kSecAssessmentRuleKeyAllow A CFBoolean indicating whether the rule allows (true) or denies (false) the operation. + @constant kSecAssessmentRuleKeyLabel An optional CFString labeling the rule. Multiple rules may have the same label; + this can be used to group rules. Labels are not presented to the user. The label has no effect on evaluation. + @constant kSecAssessmentRuleKeyRemarks An optional CFString containing user-readable text characterizing the rule's meaning. + The remark has no effect on the evaluation. + @constant kSecAssessmentRuleKeyRequirement A CFString containing the (text form of) the code requirement governing the rule's match. + @constant kSecAssessmentRuleKeyType A CFString denoting the type of operation governed by the rule. + One of the kSecAssessmentOperationType* constants. + @constant kSecAssessmentRuleKeyExpires A CFDate indicating when the rule expires. Absent if the rule does not expire. Expired rules are never returned. + @constant kSecAssessmentRuleKeyDisabled A CFNumber; non zero if temporarily disabled. Optional. + @constant kSecAssessmentRuleKeyBookmark A CFData with the bookmark to the rule. Optional. */ extern const CFStringRef kSecAssessmentContextKeyUpdate; // proposed operation extern const CFStringRef kSecAssessmentUpdateOperationAdd; // add rule to policy database extern const CFStringRef kSecAssessmentUpdateOperationRemove; // remove rule from policy database extern const CFStringRef kSecAssessmentUpdateOperationEnable; // enable rule(s) in policy database extern const CFStringRef kSecAssessmentUpdateOperationDisable; // disable rule(s) in policy database +extern const CFStringRef kSecAssessmentUpdateOperationFind; // extract rule(s) from the policy database +extern const CFStringRef kSecAssessmentContextKeyCertificates; // obsolete extern const CFStringRef kSecAssessmentUpdateKeyAuthorization; // [CFData] external form of governing authorization extern const CFStringRef kSecAssessmentUpdateKeyPriority; // rule priority @@ -226,7 +251,25 @@ extern const CFStringRef kSecAssessmentUpdateKeyExpires; // rule expiration extern const CFStringRef kSecAssessmentUpdateKeyAllow; // rule outcome (allow/deny) extern const CFStringRef kSecAssessmentUpdateKeyRemarks; // rule remarks (human readable) -extern const CFStringRef kSecAssessmentContextKeyCertificates; // obsolete +extern const CFStringRef kSecAssessmentUpdateKeyRow; // rule identifier (CFNumber; add only) +extern const CFStringRef kSecAssessmentUpdateKeyCount; // count of changed rules (CFNumber) +extern const CFStringRef kSecAssessmentUpdateKeyFound; // set of found rules (CFArray of CFDictionaries) + +extern const CFStringRef kSecAssessmentRuleKeyID; // rule content returned: rule ID +extern const CFStringRef kSecAssessmentRuleKeyPriority; // rule content returned: rule priority (floating point) +extern const CFStringRef kSecAssessmentRuleKeyAllow; // rule content returned: rule allows (boolean) +extern const CFStringRef kSecAssessmentRuleKeyLabel; // rule content returned: rule label (string; optional) +extern const CFStringRef kSecAssessmentRuleKeyRemarks; // rule content returned: rule remarks (string; optional) +extern const CFStringRef kSecAssessmentRuleKeyRequirement; // rule content returned: rule code requirement (string) +extern const CFStringRef kSecAssessmentRuleKeyType; // rule content returned: rule type (string) +extern const CFStringRef kSecAssessmentRuleKeyExpires; // rule content returned: rule expiration (CFDate; optional) +extern const CFStringRef kSecAssessmentRuleKeyDisabled; // rule content returned: rule disabled (CFNumber; nonzero means temporarily disabled) +extern const CFStringRef kSecAssessmentRuleKeyBookmark; // rule content returned: bookmark data (CFBookmark; optional) + +CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target, + SecAssessmentFlags flags, + CFDictionaryRef context, + CFErrorRef *errors); Boolean SecAssessmentUpdate(CFTypeRef target, SecAssessmentFlags flags, @@ -242,7 +285,6 @@ Boolean SecAssessmentUpdate(CFTypeRef target, @param arguments Arguments to the operation as documented for control. @param errors Standard CFErrorRef * argument to report errors. @result Returns True on success. Returns False on failure (and sets *errors). - */ Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors); diff --git a/lib/SecCode.cpp b/lib/SecCode.cpp index 779a7d3..f5b300f 100644 --- a/lib/SecCode.cpp +++ b/lib/SecCode.cpp @@ -221,6 +221,7 @@ const CFStringRef kSecCodeInfoChangedFiles = CFSTR("changed-files"); const CFStringRef kSecCodeInfoCMS = CFSTR("cms"); const CFStringRef kSecCodeInfoDesignatedRequirement = CFSTR("designated-requirement"); const CFStringRef kSecCodeInfoEntitlements = CFSTR("entitlements"); +const CFStringRef kSecCodeInfoEntitlementsDict = CFSTR("entitlements-dict"); const CFStringRef kSecCodeInfoFormat = CFSTR("format"); const CFStringRef kSecCodeInfoDigestAlgorithm = CFSTR("digest-algorithm"); const CFStringRef kSecCodeInfoIdentifier = CFSTR("identifier"); @@ -232,6 +233,7 @@ const CFStringRef kSecCodeInfoRequirementData = CFSTR("requirement-data"); const CFStringRef kSecCodeInfoSource = CFSTR("source"); const CFStringRef kSecCodeInfoStatus = CFSTR("status"); const CFStringRef kSecCodeInfoTime = CFSTR("signing-time"); +const CFStringRef kSecCodeInfoTimestamp = CFSTR("signing-timestamp"); const CFStringRef kSecCodeInfoTrust = CFSTR("trust"); const CFStringRef kSecCodeInfoUnique = CFSTR("unique"); @@ -257,8 +259,7 @@ OSStatus SecCodeCopySigningInformation(SecStaticCodeRef codeRef, SecCSFlags flag if (flags & kSecCSDynamicInformation) if (SecPointer dcode = SecStaticCode::optionalDynamic(codeRef)) - info = cfmake("{+%O,%O=%u}", info.get(), - kSecCodeInfoStatus, dcode->status()); + info.take(cfmake("{+%O,%O=%u}", info.get(), kSecCodeInfoStatus, dcode->status())); CodeSigning::Required(infoRef) = info.yield(); diff --git a/lib/SecCode.h b/lib/SecCode.h index 0e5ca7c..4ae85d9 100644 --- a/lib/SecCode.h +++ b/lib/SecCode.h @@ -318,6 +318,10 @@ OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef code, SecCSFlags flag actual Designated Requirement of the code. @constant kSecCodeInfoEntitlements A CFData containing the embedded entitlement blob of the code, if any. + @constant kSecCodeInfoEntitlementsDict A CFDictionary containing the embedded entitlements + of the code if it has entitlements and they are in standard dictionary form. + Absent if the code has no entitlements, or they are in a different format (in which + case, see kSecCodeInfoEntitlements). @constant kSecCodeInfoFormat A CFString characterizing the type and format of the code. Suitable for display to a (knowledeable) user. @constant kSecCodeInfoDigestAlgorithm A CFNumber indicating the kind of cryptographic @@ -352,6 +356,10 @@ OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef code, SecCSFlags flag it. Nobody certifies that this was really the date the code was signed; however, you do know that this is the date the signer wanted you to see. Ad-hoc signatures have no CMS and thus never have secured signing dates. + @constant kSecCodeInfoTimestamp A CFDate describing the signing date as (securely) + certified by a timestamp authority service. This time cannot be falsified by the + signer; you trust the timestamp authority's word on this. + Ad-hoc signatures have no CMS and thus never have secured signing dates. @constant kSecCodeInfoTrust The (retained) SecTrust object the system uses to evaluate the validity of the code's signature. You may use the SecTrust API to extract detailed information, particularly for reasons why certificate @@ -364,6 +372,8 @@ OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef code, SecCSFlags flag This is currently the SHA-1 hash of the code's CodeDirectory. However, future versions of the system may use a different algorithm for newly signed code. Already-signed code not change the reported value in this case. + @constant kSecCodeSignerFlags A CFNumber with the dynamic state of the object. + Contants are defined by the type SecCodeSignatureFlags. */ enum { kSecCSInternalInformation = 1 << 0, @@ -379,6 +389,7 @@ extern const CFStringRef kSecCodeInfoChangedFiles; /* Content */ extern const CFStringRef kSecCodeInfoCMS; /* Signing */ extern const CFStringRef kSecCodeInfoDesignatedRequirement; /* Requirement */ extern const CFStringRef kSecCodeInfoEntitlements; /* Requirement */ +extern const CFStringRef kSecCodeInfoEntitlementsDict; /* Requirement */ extern const CFStringRef kSecCodeInfoFormat; /* generic */ extern const CFStringRef kSecCodeInfoDigestAlgorithm; /* generic */ extern const CFStringRef kSecCodeInfoIdentifier; /* generic */ @@ -390,8 +401,10 @@ extern const CFStringRef kSecCodeInfoRequirementData; /* Requirement */ extern const CFStringRef kSecCodeInfoSource; /* generic */ extern const CFStringRef kSecCodeInfoStatus; /* Dynamic */ extern const CFStringRef kSecCodeInfoTime; /* Signing */ +extern const CFStringRef kSecCodeInfoTimestamp; /* Signing */ extern const CFStringRef kSecCodeInfoTrust; /* Signing */ extern const CFStringRef kSecCodeInfoUnique; /* generic */ +extern const CFStringRef kSecCodeSignerFlags; /* dynamic */ OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef *information); diff --git a/lib/SecCodeSigner.cpp b/lib/SecCodeSigner.cpp index adb8346..d5e7438 100644 --- a/lib/SecCodeSigner.cpp +++ b/lib/SecCodeSigner.cpp @@ -52,6 +52,16 @@ const CFStringRef kSecCodeSignerRequirements = CFSTR("requirements"); const CFStringRef kSecCodeSignerResourceRules = CFSTR("resource-rules"); const CFStringRef kSecCodeSignerSDKRoot = CFSTR("sdkroot"); const CFStringRef kSecCodeSignerSigningTime = CFSTR("signing-time"); +const CFStringRef kSecCodeSignerRequireTimestamp = CFSTR("timestamp-required"); +const CFStringRef kSecCodeSignerTimestampServer = CFSTR("timestamp-url"); +const CFStringRef kSecCodeSignerTimestampAuthentication = CFSTR("timestamp-authentication"); +const CFStringRef kSecCodeSignerTimestampOmitCertificates = CFSTR("timestamp-omit-certificates"); + +// temporary add-back to bridge B&I build dependencies -- remove soon +const CFStringRef kSecCodeSignerTSAUse = CFSTR("timestamp-required"); +const CFStringRef kSecCodeSignerTSAURL = CFSTR("timestamp-url"); +const CFStringRef kSecCodeSignerTSAClientAuth = CFSTR("timestamp-authentication"); +const CFStringRef kSecCodeSignerTSANoCerts = CFSTR("timestamp-omit-certificates"); // diff --git a/lib/SecCodeSigner.h b/lib/SecCodeSigner.h index 184dc66..42ad089 100644 --- a/lib/SecCodeSigner.h +++ b/lib/SecCodeSigner.h @@ -109,6 +109,32 @@ CFTypeID SecCodeSignerGetTypeID(void); If not specified, the current date is chosen and sealed. Since an ad-hoc signature has no CMS data, this argument is ineffective for ad-hoc signing operations. + @constant kSecCodeSignerRequireTimestamp A CFBoolean indicating (if kCFBooleanTrue) that + the code signature should be certified by a timestamp authority service. This option + requires access to a timestamp server (usually over the Internet). If requested and + the timestamp server cannot be contacted or refuses service, the signing operation fails. + The timestamp value is not under the caller's control. + If the value is kCFBooleanFalse, no timestamp service is contacted and the resulting signature + has no certified timestamp. + If this key is omitted, a default is used that may vary from release to release. + Note that when signing multi-architectural ("fat") programs, each architecture will + be signed separately, and thus each architecture will have a slightly different timestamp. + @constant kSecCodeSignerTimestampServer A CFURL specifying which timestamp authority service + to contact for timestamping if requested by the kSecCodeSignerRequireTimestamp argument. + If omitted (and timestamping is performed), a system-defined default value is used, referring + to an Apple-operated timestamp service. Note that this service may not freely serve all requests. + @constant kSecCodeSignerTimestampAuthentication A SecIdentityRef describing the identity + used to authenticate to the timestamp authority server, if the server requires client-side + (SSL/TLS) authentication. This will not generally be the identity used to sign the actual + code, depending on the requirements of the timestamp authority service used. + If omitted, the timestamp server is contacted using unauthenticated HTTP requests. + @constant kSecCodeSignerTimestampOmitCertificates A CFBoolean indicating (if kCFBooleanTrue) + that the timestamp embedded in the signature, if requested, not contain the full certificate chain + of the timestamp service used. This will make for a marginally smaller signature, but may not + verify correctly unless all such certificates are available (through the keychain system) + on the verifying system. + The default is to embed enough certificates to ensure proper verification of Apple-generated + timestamp signatures. */ extern const CFStringRef kSecCodeSignerApplicationData; extern const CFStringRef kSecCodeSignerDetached; @@ -124,6 +150,16 @@ extern const CFStringRef kSecCodeSignerRequirements; extern const CFStringRef kSecCodeSignerResourceRules; extern const CFStringRef kSecCodeSignerSDKRoot; extern const CFStringRef kSecCodeSignerSigningTime; +extern const CFStringRef kSecCodeSignerTimestampAuthentication; +extern const CFStringRef kSecCodeSignerRequireTimestamp; +extern const CFStringRef kSecCodeSignerTimestampServer; +extern const CFStringRef kSecCodeSignerTimestampOmitCertificates; + +// temporary add-back to bridge B&I build dependencies -- remove soon +extern const CFStringRef kSecCodeSignerTSAUse; +extern const CFStringRef kSecCodeSignerTSAURL; +extern const CFStringRef kSecCodeSignerTSAClientAuth; +extern const CFStringRef kSecCodeSignerTSANoCerts; /*! diff --git a/lib/SecRequirement.cpp b/lib/SecRequirement.cpp index cee8310..68d95e4 100644 --- a/lib/SecRequirement.cpp +++ b/lib/SecRequirement.cpp @@ -162,6 +162,7 @@ OSStatus SecRequirementCopyString(SecRequirementRef requirementRef, SecCSFlags f // CFStringRef kSecRequirementKeyInfoPlist = CFSTR("requirement:eval:info"); CFStringRef kSecRequirementKeyEntitlements = CFSTR("requirement:eval:entitlements"); +CFStringRef kSecRequirementKeyIdentifier = CFSTR("requirement:eval:identifier"); OSStatus SecRequirementEvaluate(SecRequirementRef requirementRef, CFArrayRef certificateChain, CFDictionaryRef context, @@ -176,6 +177,8 @@ OSStatus SecRequirementEvaluate(SecRequirementRef requirementRef, Requirement::Context ctx(certificateChain, // mandatory context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyInfoPlist)) : NULL, context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyEntitlements)) : NULL, + (context && CFDictionaryGetValue(context, kSecRequirementKeyIdentifier)) ? + cfString(CFStringRef(CFDictionaryGetValue(context, kSecRequirementKeyIdentifier))) : "", NULL // can't specify a CodeDirectory here ); req->validate(ctx); diff --git a/lib/SecRequirementPriv.h b/lib/SecRequirementPriv.h index b7e6062..4405576 100644 --- a/lib/SecRequirementPriv.h +++ b/lib/SecRequirementPriv.h @@ -179,9 +179,11 @@ OSStatus SecRequirementCreateGroup(CFStringRef groupName, SecCertificateRef anch an Info.plist. If this key is missing, all references to Info.plist contents will fail. @constant kSecRequirementKeyEntitlements A context key providing an CFDictionary describing an entitlement dictionary. If this key is missing, all references to entitlements will fail. + @constant kSecRequirementKeyIdentifier A context key providing the signing identifier as a CFString. */ extern CFStringRef kSecRequirementKeyInfoPlist; extern CFStringRef kSecRequirementKeyEntitlements; +extern CFStringRef kSecRequirementKeyIdentifier; OSStatus SecRequirementEvaluate(SecRequirementRef requirement, CFArrayRef certificateChain, CFDictionaryRef context, diff --git a/lib/StaticCode.cpp b/lib/StaticCode.cpp index 956c88f..d86a30b 100644 --- a/lib/StaticCode.cpp +++ b/lib/StaticCode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved. + * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,6 +27,7 @@ #include "StaticCode.h" #include "Code.h" #include "reqmaker.h" +#include "drmaker.h" #include "reqdumper.h" #include "sigblob.h" #include "resources.h" @@ -34,6 +35,7 @@ #include "detachedrep.h" #include "csdatabase.h" #include "csutilities.h" +#include "SecCode.h" #include #include #include @@ -369,6 +371,12 @@ CFAbsoluteTime SecStaticCode::signingTime() return mSigningTime; } +CFAbsoluteTime SecStaticCode::signingTimestamp() +{ + validateDirectory(); + return mSigningTimestamp; +} + // // Verify the CMS signature on the CodeDirectory. @@ -403,28 +411,29 @@ bool SecStaticCode::verifySignature() if (status != kCMSSignerValid) MacOSError::throwMe(errSecCSSignatureFailed); - // get signing date (we've got the decoder handle right here) - mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-) - SecCmsMessageRef cmsg; - MacOSError::check(CMSDecoderGetCmsMessage(cms, &cmsg)); - SecCmsSignedDataRef signedData = NULL; - int numContentInfos = SecCmsMessageContentLevelCount(cmsg); - for(int dex = 0; !signedData && dex < numContentInfos; dex++) { - SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); - SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); - switch(tag) { - case SEC_OID_PKCS7_SIGNED_DATA: - if (SecCmsSignedDataRef signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))) - if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, 0)) - SecCmsSignerInfoGetSigningTime(signerInfo, &mSigningTime); - break; - default: - break; - } - } + // internal signing time (as specified by the signer; optional) + mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-) + switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) { + case noErr: + case errSecSigningTimeMissing: + break; + default: + MacOSError::throwMe(rc); + } + // certified signing time (as specified by a TSA; optional) + mSigningTimestamp = 0; + switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) { + case noErr: + case errSecTimestampMissing: + break; + default: + MacOSError::throwMe(rc); + } + // set up the environment for SecTrust MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors + MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains CSSM_APPLE_TP_ACTION_DATA actionData = { CSSM_APPLE_TP_ACTION_VERSION, // version of data structure CSSM_TP_ACTION_IMPLICIT_ANCHORS // action flags @@ -441,7 +450,6 @@ bool SecStaticCode::verifySignature() CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0); switch (trustResult) { case kSecTrustResultProceed: - case kSecTrustResultConfirm: case kSecTrustResultUnspecified: break; // success case kSecTrustResultDeny: @@ -449,18 +457,19 @@ bool SecStaticCode::verifySignature() case kSecTrustResultInvalid: assert(false); // should never happen MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED); - case kSecTrustResultRecoverableTrustFailure: - case kSecTrustResultFatalTrustFailure: - case kSecTrustResultOtherError: + default: { OSStatus result; MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result)); - if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET)) - && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) { - CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this); - actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs) - continue; // retry validation - } + // if we have a valid timestamp, CMS validates against (that) signing time and all is well. + // If we don't have one, may validate against *now*, and must be able to tolerate expiration. + if (mSigningTimestamp == 0) // no timestamp available + if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET)) + && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) { + CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this); + actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs) + continue; // retry validation while tolerating expiration + } MacOSError::throwMe(result); } } @@ -650,7 +659,7 @@ void SecStaticCode::validateResources() throw; } } - assert(!validatedResources()); + assert(validatedResources()); if (mResourcesValidResult) MacOSError::throwMe(mResourcesValidResult); if (mResourcesValidContext->osStatus() != noErr) @@ -662,12 +671,14 @@ void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void * { CollectingContext *ctx = static_cast(context); ResourceSeal seal(value); - if (!seal.optional()) + if (!seal.optional()) { if (key && CFGetTypeID(key) == CFStringGetTypeID()) { ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, CFTempURL(CFStringRef(key), false, ctx->code.resourceBase())); - } else + } else { ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key); + } + } } @@ -869,8 +880,10 @@ const Requirement *SecStaticCode::internalRequirement(SecRequirementType type) // -// Return the Designated Requirement. This can be either explicit in the -// Internal Requirements resource, or implicitly generated. +// Return the Designated Requirement (DR). This can be either explicit in the +// Internal Requirements component, or implicitly generated on demand here. +// Note that an explicit DR may have been implicitly generated at signing time; +// we don't distinguish this case. // const Requirement *SecStaticCode::designatedRequirement() { @@ -885,163 +898,31 @@ const Requirement *SecStaticCode::designatedRequirement() // -// Generate the default (implicit) Designated Requirement for this StaticCode. -// This is a heuristic of sorts, and may change over time (for the better, we hope). -// -// The current logic is this: -// * If the code is ad-hoc signed, use the CodeDirectory hash directory. -// * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier). -// ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise, -// we default to the leaf (directly signing) certificate. +// Generate the default Designated Requirement (DR) for this StaticCode. +// Ignore any explicit DR it may contain. // const Requirement *SecStaticCode::defaultDesignatedRequirement() { - validateDirectory(); // need the cert chain - Requirement::Maker maker; - - // if this is an ad-hoc (unsigned) object, return a cdhash requirement if (flag(kSecCodeSignatureAdhoc)) { + // adhoc signature: return a plain cdhash requirement + Requirement::Maker maker; SHA1 hash; hash(codeDirectory(), codeDirectory()->length()); SHA1::Digest digest; hash.finish(digest); maker.cdhash(digest); + return maker.make(); } else { - // always require the identifier - maker.put(opAnd); - maker.ident(codeDirectory()->identifier()); - - SHA1::Digest anchorHash; - hashOfCertificate(cert(Requirement::anchorCert), anchorHash); - if (!memcmp(anchorHash, Requirement::appleAnchorHash(), SHA1::digestLength) -#if defined(TEST_APPLE_ANCHOR) - || !memcmp(anchorHash, Requirement::testAppleAnchorHash(), SHA1::digestLength) -#endif - ) - defaultDesignatedAppleAnchor(maker); - else - defaultDesignatedNonAppleAnchor(maker); + // full signature: Gin up full context and let DRMaker do its thing + validateDirectory(); // need the cert chain + Requirement::Context context(this->certificates(), + this->infoDictionary(), + this->entitlements(), + this->identifier(), + this->codeDirectory() + ); + return DRMaker(context).make(); } - - return maker(); -} - -static const uint8_t adcSdkMarker[] = { APPLE_EXTENSION_OID, 2, 1 }; // iOS intermediate marker -static const CSSM_DATA adcSdkMarkerOID = { sizeof(adcSdkMarker), (uint8_t *)adcSdkMarker }; - -static const uint8_t caspianSdkMarker[] = { APPLE_EXTENSION_OID, 2, 6 }; // Caspian intermediate marker -static const CSSM_DATA caspianSdkMarkerOID = { sizeof(caspianSdkMarker), (uint8_t *)caspianSdkMarker }; -static const uint8_t caspianLeafMarker[] = { APPLE_EXTENSION_OID, 1, 13 }; // Caspian leaf certificate marker -static const CSSM_DATA caspianLeafMarkerOID = { sizeof(caspianLeafMarker), (uint8_t *)caspianLeafMarker }; - -void SecStaticCode::defaultDesignatedAppleAnchor(Requirement::Maker &maker) -{ - if (isAppleSDKSignature()) { - // get the Common Name DN element for the leaf - CFRef leafCN; - MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert), - &CSSMOID_CommonName, &leafCN.aref())); - - // apple anchor generic and ... - maker.put(opAnd); - maker.anchorGeneric(); // apple generic anchor and... - // ... leaf[subject.CN] = and ... - maker.put(opAnd); - maker.put(opCertField); // certificate - maker.put(0); // leaf - maker.put("subject.CN"); // [subject.CN] - maker.put(matchEqual); // = - maker.putData(leafCN); // - // ... cert 1[field.] exists - maker.put(opCertGeneric); // certificate - maker.put(1); // 1 - maker.putData(adcSdkMarkerOID.Data, adcSdkMarkerOID.Length); // [field.] - maker.put(matchExists); // exists - return; - } - - if (isAppleCaspianSignature()) { - // get the Organizational Unit DN element for the leaf (it contains the TEAMID) - CFRef teamID; - MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert), - &CSSMOID_OrganizationalUnitName, &teamID.aref())); - - // apple anchor generic and ... - maker.put(opAnd); - maker.anchorGeneric(); // apple generic anchor and... - - // ... certificate 1[intermediate marker oid] exists and ... - maker.put(opAnd); - maker.put(opCertGeneric); // certificate - maker.put(1); // 1 - maker.putData(caspianSdkMarker, sizeof(caspianSdkMarker)); - maker.put(matchExists); // exists - - // ... certificate leaf[Caspian cert oid] exists and ... - maker.put(opAnd); - maker.put(opCertGeneric); // certificate - maker.put(0); // leaf - maker.putData(caspianLeafMarker, sizeof(caspianLeafMarker)); - maker.put(matchExists); // exists - - // ... leaf[subject.OU] = - maker.put(opCertField); // certificate - maker.put(0); // leaf - maker.put("subject.OU"); // [subject.OU] - maker.put(matchEqual); // = - maker.putData(teamID); // TEAMID - return; - } - - // otherwise, claim this program for Apple Proper - maker.anchor(); -} - -bool SecStaticCode::isAppleSDKSignature() -{ - if (CFArrayRef certChain = certificates()) // got cert chain - if (CFArrayGetCount(certChain) == 3) // leaf, one intermediate, anchor - if (SecCertificateRef intermediate = cert(1)) // get intermediate - if (certificateHasField(intermediate, CssmOid::overlay(adcSdkMarkerOID))) - return true; - return false; -} - -bool SecStaticCode::isAppleCaspianSignature() -{ - if (CFArrayRef certChain = certificates()) // got cert chain - if (CFArrayGetCount(certChain) == 3) // leaf, one intermediate, anchor - if (SecCertificateRef intermediate = cert(1)) // get intermediate - if (certificateHasField(intermediate, CssmOid::overlay(caspianSdkMarkerOID))) - return true; - return false; -} - -void SecStaticCode::defaultDesignatedNonAppleAnchor(Requirement::Maker &maker) -{ - // get the Organization DN element for the leaf - CFRef leafOrganization; - MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert), - &CSSMOID_OrganizationName, &leafOrganization.aref())); - - // now step up the cert chain looking for the first cert with a different one - int slot = Requirement::leafCert; // start at leaf - if (leafOrganization) { - while (SecCertificateRef ca = cert(slot+1)) { // NULL if you over-run the anchor slot - CFRef caOrganization; - MacOSError::check(SecCertificateCopySubjectComponent(ca, &CSSMOID_OrganizationName, &caOrganization.aref())); - if (!caOrganization || CFStringCompare(leafOrganization, caOrganization, 0) != kCFCompareEqualTo) - break; - slot++; - } - if (slot == CFArrayGetCount(mCertChain) - 1) // went all the way to the anchor... - slot = Requirement::anchorCert; // ... so say that - } - - // nail the last cert with the leaf's Organization value - SHA1::Digest authorityHash; - hashOfCertificate(cert(slot), authorityHash); - maker.anchor(slot, authorityHash); } @@ -1068,7 +949,7 @@ bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failur { assert(req); validateDirectory(); - return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()), failure); + return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure); } void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure) @@ -1134,11 +1015,17 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags) CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier())); CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format())); CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource())); - if (CFDictionaryRef info = this->infoDictionary()) - CFDictionaryAddValue(dict, kSecCodeInfoPList, info); CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash()); CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(this->codeDirectory(false)->hashType)); + // + // Deliver any Info.plist only if it looks intact + // + try { + if (CFDictionaryRef info = this->infoDictionary()) + CFDictionaryAddValue(dict, kSecCodeInfoPList, info); + } catch (...) { } // don't deliver Info.plist if questionable + // // kSecCSSigningInformation adds information about signing certificates and chains // @@ -1152,6 +1039,9 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags) if (CFAbsoluteTime time = this->signingTime()) if (CFRef date = CFDateCreate(NULL, time)) CFDictionaryAddValue(dict, kSecCodeInfoTime, date); + if (CFAbsoluteTime time = this->signingTimestamp()) + if (CFRef date = CFDateCreate(NULL, time)) + CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date); } // @@ -1174,8 +1064,11 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags) CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef); } - if (CFDataRef ent = this->component(cdEntitlementSlot)) + if (CFDataRef ent = this->component(cdEntitlementSlot)) { CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent); + if (CFDictionaryRef entdict = this->entitlements()) + CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict); + } } // diff --git a/lib/StaticCode.h b/lib/StaticCode.h index edb92a1..af255ba 100644 --- a/lib/StaticCode.h +++ b/lib/StaticCode.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. + * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -112,6 +112,7 @@ public: CFDataRef cdHash(); CFDataRef signature(); CFAbsoluteTime signingTime(); + CFAbsoluteTime signingTimestamp(); bool isSigned() { return codeDirectory(false) != NULL; } DiskRep *diskRep() { return mRep; } std::string mainExecutablePath() { return mRep->mainExecutablePath(); } @@ -139,7 +140,6 @@ public: bool validatedExecutable() const { return mExecutableValidated; } bool validatedResources() const { return mResourcesValidated; } - void validateDirectory(); void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed); void validateNonResourceComponents(); @@ -170,11 +170,6 @@ protected: bool verifySignature(); CFTypeRef verificationPolicy(SecCSFlags flags); - void defaultDesignatedAppleAnchor(Requirement::Maker &maker); - void defaultDesignatedNonAppleAnchor(Requirement::Maker &maker); - bool isAppleSDKSignature(); - bool isAppleCaspianSignature(); - static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context); private: @@ -198,6 +193,7 @@ private: CFRef mDir; // code directory data CFRef mSignature; // CMS signature data CFAbsoluteTime mSigningTime; // (signed) signing time + CFAbsoluteTime mSigningTimestamp; // Timestamp time (from timestamping authority) CFRef mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present // alternative cache forms (storage may depend on cached contents above) diff --git a/lib/bundlediskrep.cpp b/lib/bundlediskrep.cpp index 09d5ca2..fa5a857 100644 --- a/lib/bundlediskrep.cpp +++ b/lib/bundlediskrep.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace Security { @@ -34,6 +35,12 @@ namespace CodeSigning { using namespace UnixPlusPlus; +// +// Local helpers +// +static std::string findDistFile(const std::string &directory); + + // // We make a CFBundleRef immediately, but everything else is lazy // @@ -56,6 +63,8 @@ BundleDiskRep::BundleDiskRep(CFBundleRef ref, const Context *ctx) // common construction code void BundleDiskRep::setup(const Context *ctx) { + mInstallerPackage = false; // default + // deal with versioned bundles (aka Frameworks) string version = resourcesRootPath() + "/Versions/" @@ -70,23 +79,27 @@ void BundleDiskRep::setup(const Context *ctx) if (ctx && ctx->version) // explicitly specified MacOSError::throwMe(errSecCSStaticCodeNotFound); } - - // conventional executable bundle: CFBundle identifies an executable for us - if (mMainExecutableURL.take(CFBundleCopyExecutableURL(mBundle))) { - // conventional executable bundle - mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx); - mFormat = string("bundle with ") + mExecRep->format(); - return; - } CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle); assert(infoDict); // CFBundle will always make one up for us + CFTypeRef mainHTML = CFDictionaryGetValue(infoDict, CFSTR("MainHTML")); + CFTypeRef packageVersion = CFDictionaryGetValue(infoDict, CFSTR("IFMajorVersion")); + + // conventional executable bundle: CFBundle identifies an executable for us + if (CFRef mainExec = CFBundleCopyExecutableURL(mBundle)) // if CFBundle claims an executable... + if (mainHTML == NULL) { // ... and it's not a widget + mMainExecutableURL = mainExec; + mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx); + mFormat = "bundle with " + mExecRep->format(); + return; + } - if (CFTypeRef main = CFDictionaryGetValue(infoDict, CFSTR("MainHTML"))) { - // widget - if (CFGetTypeID(main) != CFStringGetTypeID()) + // widget + if (mainHTML) { + if (CFGetTypeID(mainHTML) != CFStringGetTypeID()) MacOSError::throwMe(errSecCSBadBundleFormat); - mMainExecutableURL = makeCFURL(cfString(CFStringRef(main)), false, CFRef(CFBundleCopySupportFilesDirectoryURL(mBundle))); + mMainExecutableURL.take(makeCFURL(cfString(CFStringRef(mainHTML)), false, + CFRef(CFBundleCopySupportFilesDirectoryURL(mBundle)))); if (!mMainExecutableURL) MacOSError::throwMe(errSecCSBadBundleFormat); mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); @@ -94,18 +107,28 @@ void BundleDiskRep::setup(const Context *ctx) return; } - // generic bundle case - impose our own "minimal signable bundle" standard - - // we MUST have an actual Info.plist in here - CFRef infoURL = _CFBundleCopyInfoPlistURL(mBundle); - if (!infoURL) - MacOSError::throwMe(errSecCSBadBundleFormat); - + // do we have a real Info.plist here? + if (CFRef infoURL = _CFBundleCopyInfoPlistURL(mBundle)) { + // focus on the Info.plist (which we know exists) as the nominal "main executable" file + if ((mMainExecutableURL = _CFBundleCopyInfoPlistURL(mBundle))) { + mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); + if (packageVersion) { + mInstallerPackage = true; + mFormat = "installer package bundle"; + } else { + mFormat = "bundle"; + } + return; + } + } - // focus on the Info.plist (which we know exists) as the nominal "main executable" file - if ((mMainExecutableURL = _CFBundleCopyInfoPlistURL(mBundle))) { + // we're getting desperate here. Perhaps an oldish-style installer package? Look for a *.dist file + std::string distFile = findDistFile(this->resourcesRootPath()); + if (!distFile.empty()) { + mMainExecutableURL = makeCFURL(distFile); mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); - mFormat = "bundle"; + mInstallerPackage = true; + mFormat = "installer package bundle"; return; } @@ -114,6 +137,41 @@ void BundleDiskRep::setup(const Context *ctx) } +// +// Return the full path to the one-and-only file named something.dist in a directory. +// Return empty string if none; throw an exception if multiple. Do not descend into subdirectories. +// +static std::string findDistFile(const std::string &directory) +{ + std::string found; + char *paths[] = {(char *)directory.c_str(), NULL}; + FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL); + bool root = true; + while (FTSENT *ent = fts_read(fts)) { + switch (ent->fts_info) { + case FTS_F: + case FTS_NSOK: + if (!strcmp(ent->fts_path + ent->fts_pathlen - 5, ".dist")) { // found plain file foo.dist + if (found.empty()) // first found + found = ent->fts_path; + else // multiple *.dist files (bad) + MacOSError::throwMe(errSecCSBadBundleFormat); + } + break; + case FTS_D: + if (!root) + fts_set(fts, ent, FTS_SKIP); // don't descend + root = false; + break; + default: + break; + } + } + fts_close(fts); + return found; +} + + // // Create a path to a bundle signing resource, by name. // If the BUNDLEDISKREP_DIRECTORY directory exists in the bundle's support directory, files @@ -122,7 +180,7 @@ void BundleDiskRep::setup(const Context *ctx) string BundleDiskRep::metaPath(const char *name) { if (mMetaPath.empty()) { - string support = cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true); + string support = cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle)); mMetaPath = support + "/" BUNDLEDISKREP_DIRECTORY; if (::access(mMetaPath.c_str(), F_OK) == 0) { mMetaExists = true; @@ -209,7 +267,7 @@ string BundleDiskRep::mainExecutablePath() string BundleDiskRep::resourcesRootPath() { - return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true); + return cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle)); } void BundleDiskRep::adjustResources(ResourceBuilder &builder) @@ -303,19 +361,30 @@ CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &) string rbase = this->resourcesRootPath(); if (rbase.substr(rbase.length()-2, 2) == "/.") // produced by versioned bundle implicit "Current" case rbase = rbase.substr(0, rbase.length()-2); // ... so take it off for this - string resources = cfString(CFBundleCopyResourcesDirectoryURL(mBundle), true); + string resources = cfStringRelease(CFBundleCopyResourcesDirectoryURL(mBundle)); if (resources == rbase) resources = ""; else if (resources.compare(0, rbase.length(), rbase, 0, rbase.length()) != 0) // Resources not in resource root MacOSError::throwMe(errSecCSBadBundleFormat); else resources = resources.substr(rbase.length() + 1) + "/"; // differential path segment + + // installer package rules + if (mInstallerPackage) + return cfmake("{rules={" + "'^.*' = #T" // include everything, but... + "%s = {optional=#T, weight=1000}" // make localizations optional + "'^.*/.*\\.pkg/' = {omit=#T, weight=10000}" // and exclude all nested packages (by name) + "}}", + (string("^") + resources + ".*\\.lproj/").c_str() + ); + // executable bundle rules return cfmake("{rules={" - "'^version.plist$' = #T" - "%s = #T" - "%s = {optional=#T, weight=1000}" - "%s = {omit=#T, weight=1100}" + "'^version.plist$' = #T" // include version.plist + "%s = #T" // include Resources + "%s = {optional=#T, weight=1000}" // make localizations optional + "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files "}}", (string("^") + resources).c_str(), (string("^") + resources + ".*\\.lproj/").c_str(), diff --git a/lib/bundlediskrep.h b/lib/bundlediskrep.h index e961993..bef1861 100644 --- a/lib/bundlediskrep.h +++ b/lib/bundlediskrep.h @@ -90,7 +90,8 @@ private: CFRef mBundle; std::string mMetaPath; // path to directory containing signing files bool mMetaExists; // separate meta-file directory exists - CFRef mMainExecutableURL; // chosen main executable URL + CFRef mMainExecutableURL; // chosen main executable URL + bool mInstallerPackage; // is an installer (not executable) bundle string mFormat; // format description string RefPointer mExecRep; // DiskRep for main executable file }; diff --git a/lib/cfmdiskrep.cpp b/lib/cfmdiskrep.cpp index 41607e1..6ba10f2 100644 --- a/lib/cfmdiskrep.cpp +++ b/lib/cfmdiskrep.cpp @@ -163,7 +163,7 @@ void CFMDiskRep::readSigningData() if (fd.read(&sentinel, sizeof(sentinel), fd.fileSize() - sizeof(Sentinel)) == sizeof(Sentinel)) if (sentinel.magic == EmbeddedSignatureBlob::typeMagic) { mSigningOffset = sentinel.offset; - if (mSigningData = EmbeddedSignatureBlob::readBlob(fd, mSigningOffset)) + if ((mSigningData = EmbeddedSignatureBlob::readBlob(fd, mSigningOffset))) secdebug("cfmrep", "%zd signing bytes in %d blob(s) from %s(CFM)", mSigningData->length(), mSigningData->count(), mainExecutablePath().c_str()); diff --git a/lib/codedirectory.cpp b/lib/codedirectory.cpp index 9596e8f..0bcb980 100644 --- a/lib/codedirectory.cpp +++ b/lib/codedirectory.cpp @@ -143,7 +143,7 @@ void CodeDirectory::checkIntegrity() const // now check interior offsets for validity if (!stringAt(identOffset)) MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range - if (!contains(hashOffset - hashSize * nSpecialSlots, hashSize * (nSpecialSlots + nCodeSlots))) + if (!contains(hashOffset - uint64_t(hashSize) * nSpecialSlots, hashSize * (uint64_t(nSpecialSlots) + nCodeSlots))) MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range if (const Scatter *scatter = this->scatterVector()) { // the optional scatter vector is terminated with an element having (count == 0) diff --git a/lib/csutilities.cpp b/lib/csutilities.cpp index ed5a0a8..6ac5db8 100644 --- a/lib/csutilities.cpp +++ b/lib/csutilities.cpp @@ -61,7 +61,7 @@ void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest) // Check to see if a certificate contains a particular field, by OID. This works for extensions, // even ones not recognized by the local CL. It does not return any value, only presence. // -bool certificateHasField(SecCertificateRef cert, const CssmOid &oid) +bool certificateHasField(SecCertificateRef cert, const CSSM_OID &oid) { assert(cert); CSSM_DATA *value; @@ -97,7 +97,7 @@ bool certificateHasField(SecCertificateRef cert, const CssmOid &oid) // Retrieve X.509 policy extension OIDs, if any. // This currently ignores policy qualifiers. // -bool certificateHasPolicy(SecCertificateRef cert, const CssmOid &policyOid) +bool certificateHasPolicy(SecCertificateRef cert, const CSSM_OID &policyOid) { bool matched = false; assert(cert); diff --git a/lib/csutilities.h b/lib/csutilities.h index 4565113..10b33f8 100644 --- a/lib/csutilities.h +++ b/lib/csutilities.h @@ -88,8 +88,8 @@ size_t hashFileData(UnixPlusPlus::FileDesc fd, _Hash *hasher, size_t limit = 0) // Check to see if a certificate contains a particular field, by OID. This works for extensions, // even ones not recognized by the local CL. It does not return any value, only presence. // -bool certificateHasField(SecCertificateRef cert, const CssmOid &oid); -bool certificateHasPolicy(SecCertificateRef cert, const CssmOid &policyOid); +bool certificateHasField(SecCertificateRef cert, const CSSM_OID &oid); +bool certificateHasPolicy(SecCertificateRef cert, const CSSM_OID &policyOid); // diff --git a/lib/detachedrep.cpp b/lib/detachedrep.cpp index ed59e58..348a8ed 100644 --- a/lib/detachedrep.cpp +++ b/lib/detachedrep.cpp @@ -43,7 +43,7 @@ DetachedRep::DetachedRep(CFDataRef sig, DiskRep *orig, const std::string &source { const BlobCore *sigBlob = reinterpret_cast(CFDataGetBytePtr(sig)); if (sigBlob->is()) { // architecture-less - if (mArch = EmbeddedSignatureBlob::specific(sigBlob)) { + if ((mArch = EmbeddedSignatureBlob::specific(sigBlob))) { mGlobal = NULL; CODESIGN_DISKREP_CREATE_DETACHED(this, orig, (char*)source.c_str(), NULL); return; @@ -52,8 +52,8 @@ DetachedRep::DetachedRep(CFDataRef sig, DiskRep *orig, const std::string &source if (const DetachedSignatureBlob *dsblob = DetachedSignatureBlob::specific(sigBlob)) if (Universal *fat = orig->mainExecutableImage()) if (const BlobCore *blob = dsblob->find(fat->bestNativeArch().cpuType())) - if (mArch = EmbeddedSignatureBlob::specific(blob)) - if (mGlobal = EmbeddedSignatureBlob::specific(dsblob->find(0))) { + if ((mArch = EmbeddedSignatureBlob::specific(blob))) + if ((mGlobal = EmbeddedSignatureBlob::specific(dsblob->find(0)))) { CODESIGN_DISKREP_CREATE_DETACHED(this, orig, (char*)source.c_str(), (void*)mGlobal); return; } diff --git a/lib/diskrep.h b/lib/diskrep.h index 5d9c2d3..4a6beb5 100644 --- a/lib/diskrep.h +++ b/lib/diskrep.h @@ -194,10 +194,12 @@ public: std::string mainExecutablePath() { return mOriginal->mainExecutablePath(); } CFURLRef canonicalPath() { return mOriginal->canonicalPath(); } std::string resourcesRootPath() { return mOriginal->resourcesRootPath(); } + void adjustResources(ResourceBuilder &builder) { return mOriginal->adjustResources(builder); } Universal *mainExecutableImage() { return mOriginal->mainExecutableImage(); } size_t signingBase() { return mOriginal->signingBase(); } size_t signingLimit() { return mOriginal->signingLimit(); } std::string format() { return mOriginal->format(); } + CFArrayRef modifiedFiles() { return mOriginal->modifiedFiles(); } UnixPlusPlus::FileDesc &fd() { return mOriginal->fd(); } void flush() { return mOriginal->flush(); } diff --git a/lib/drmaker.cpp b/lib/drmaker.cpp new file mode 100644 index 0000000..98f887d --- /dev/null +++ b/lib/drmaker.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// drmaker - create automatic Designated Requirements +// +#include "drmaker.h" +#include "csutilities.h" +#include +#include +//#include + +namespace Security { +namespace CodeSigning { + + +static const uint8_t adcSdkMarker[] = { APPLE_EXTENSION_OID, 2, 1 }; // iOS intermediate marker +const CSSM_DATA adcSdkMarkerOID = { sizeof(adcSdkMarker), (uint8_t *)adcSdkMarker }; + +static const uint8_t caspianSdkMarker[] = { APPLE_EXTENSION_OID, 2, 6 }; // Caspian intermediate marker +const CSSM_DATA devIdSdkMarkerOID = { sizeof(caspianSdkMarker), (uint8_t *)caspianSdkMarker }; +static const uint8_t caspianLeafMarker[] = { APPLE_EXTENSION_OID, 1, 13 }; // Caspian leaf certificate marker +const CSSM_DATA devIdLeafMarkerOID = { sizeof(caspianLeafMarker), (uint8_t *)caspianLeafMarker }; + + + +DRMaker::DRMaker(const Requirement::Context &context) + : ctx(context) +{ +} + +DRMaker::~DRMaker() +{ +} + + +// +// Generate the default (implicit) Designated Requirement for this StaticCode. +// This is a heuristic of sorts, and may change over time (for the better, we hope). +// +Requirement *DRMaker::make() +{ + // we can't make an explicit DR for a (proposed) ad-hoc signing because that requires the CodeDirectory (which we ain't got yet) + if (ctx.certCount() == 0) + return NULL; + + // always require the identifier + this->put(opAnd); + this->ident(ctx.identifier); + + SHA1::Digest anchorHash; + hashOfCertificate(ctx.cert(Requirement::anchorCert), anchorHash); + if (!memcmp(anchorHash, Requirement::appleAnchorHash(), SHA1::digestLength) +#if defined(TEST_APPLE_ANCHOR) + || !memcmp(anchorHash, Requirement::testAppleAnchorHash(), SHA1::digestLength) +#endif + ) + appleAnchor(); + else + nonAppleAnchor(); + + return Maker::make(); +} + + +void DRMaker::nonAppleAnchor() +{ + // get the Organization DN element for the leaf + CFRef leafOrganization; + MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert), + &CSSMOID_OrganizationName, &leafOrganization.aref())); + + // now step up the cert chain looking for the first cert with a different one + int slot = Requirement::leafCert; // start at leaf + if (leafOrganization) { + while (SecCertificateRef ca = ctx.cert(slot+1)) { // NULL if you over-run the anchor slot + CFRef caOrganization; + MacOSError::check(SecCertificateCopySubjectComponent(ca, &CSSMOID_OrganizationName, &caOrganization.aref())); + if (!caOrganization || CFStringCompare(leafOrganization, caOrganization, 0) != kCFCompareEqualTo) + break; + slot++; + } + if (slot == ctx.certCount() - 1) // went all the way to the anchor... + slot = Requirement::anchorCert; // ... so say that + } + + // nail the last cert with the leaf's Organization value + SHA1::Digest authorityHash; + hashOfCertificate(ctx.cert(slot), authorityHash); + this->anchor(slot, authorityHash); +} + + +void DRMaker::appleAnchor() +{ + if (isIOSSignature()) { + // get the Common Name DN element for the leaf + CFRef leafCN; + MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert), + &CSSMOID_CommonName, &leafCN.aref())); + + // apple anchor generic and ... + this->put(opAnd); + this->anchorGeneric(); // apple generic anchor and... + // ... leaf[subject.CN] = and ... + this->put(opAnd); + this->put(opCertField); // certificate + this->put(0); // leaf + this->put("subject.CN"); // [subject.CN] + this->put(matchEqual); // = + this->putData(leafCN); // + // ... cert 1[field.] exists + this->put(opCertGeneric); // certificate + this->put(1); // 1 + this->putData(adcSdkMarkerOID.Data, adcSdkMarkerOID.Length); // [field.] + this->put(matchExists); // exists + return; + } + + if (isDeveloperIDSignature()) { + // get the Organizational Unit DN element for the leaf (it contains the TEAMID) + CFRef teamID; + MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert), + &CSSMOID_OrganizationalUnitName, &teamID.aref())); + + // apple anchor generic and ... + this->put(opAnd); + this->anchorGeneric(); // apple generic anchor and... + + // ... certificate 1[intermediate marker oid] exists and ... + this->put(opAnd); + this->put(opCertGeneric); // certificate + this->put(1); // 1 + this->putData(caspianSdkMarker, sizeof(caspianSdkMarker)); + this->put(matchExists); // exists + + // ... certificate leaf[Caspian cert oid] exists and ... + this->put(opAnd); + this->put(opCertGeneric); // certificate + this->put(0); // leaf + this->putData(caspianLeafMarker, sizeof(caspianLeafMarker)); + this->put(matchExists); // exists + + // ... leaf[subject.OU] = + this->put(opCertField); // certificate + this->put(0); // leaf + this->put("subject.OU"); // [subject.OU] + this->put(matchEqual); // = + this->putData(teamID); // TEAMID + return; + } + + // otherwise, claim this program for Apple Proper + this->anchor(); +} + +bool DRMaker::isIOSSignature() +{ + if (ctx.certCount() == 3) // leaf, one intermediate, anchor + if (SecCertificateRef intermediate = ctx.cert(1)) // get intermediate + if (certificateHasField(intermediate, CssmOid::overlay(adcSdkMarkerOID))) + return true; + return false; +} + +bool DRMaker::isDeveloperIDSignature() +{ + if (ctx.certCount() == 3) // leaf, one intermediate, anchor + if (SecCertificateRef intermediate = ctx.cert(1)) // get intermediate + if (certificateHasField(intermediate, CssmOid::overlay(devIdSdkMarkerOID))) + return true; + return false; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/drmaker.h b/lib/drmaker.h new file mode 100644 index 0000000..704c361 --- /dev/null +++ b/lib/drmaker.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// drmaker - create Designated Requirements +// +#ifndef _H_DRMAKER +#define _H_DRMAKER + +#include "reqmaker.h" + +namespace Security { +namespace CodeSigning { + + +// +// Some useful certificate OID markers +// +extern const CSSM_DATA adcSdkMarkerOID; +extern const CSSM_DATA devIdSdkMarkerOID; +extern const CSSM_DATA devIdLeafMarkerOID; + + + +// +// A Maker of Designated Requirements +// +class DRMaker : public Requirement::Maker { +public: + DRMaker(const Requirement::Context &context); + virtual ~DRMaker(); + + const Requirement::Context &ctx; + +public: + Requirement *make(); + +private: + void appleAnchor(); + void nonAppleAnchor(); + bool isIOSSignature(); + bool isDeveloperIDSignature(); +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_DRMAKER diff --git a/lib/machorep.cpp b/lib/machorep.cpp index 8753ca6..180a400 100644 --- a/lib/machorep.cpp +++ b/lib/machorep.cpp @@ -172,7 +172,7 @@ CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot) if (const linkedit_data_command *cs = macho->findCodeSignature()) { size_t offset = macho->flip(cs->dataoff); size_t length = macho->flip(cs->datasize); - if (mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length)) { + if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) { secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)", mSigningData->length(), mSigningData->count(), mainExecutablePath().c_str(), macho->architecture().name()); @@ -263,7 +263,7 @@ void MachORep::flush() string MachORep::recommendedIdentifier(const SigningContext &ctx) { if (CFDataRef info = infoPlist()) { - if (CFDictionaryRef dict = makeCFDictionaryFrom(info)) { + if (CFRef dict = makeCFDictionaryFrom(info)) { CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey)); if (code && CFGetTypeID(code) != CFStringGetTypeID()) MacOSError::throwMe(errSecCSBadDictionaryFormat); @@ -280,23 +280,12 @@ string MachORep::recommendedIdentifier(const SigningContext &ctx) // // The default suggested requirements for Mach-O binaries are as follows: -// Hosting requirement: Rosetta if it's PPC, none otherwise. // Library requirement: Composed from dynamic load commands. // -static const uint8_t ppc_host_ireq[] = { // anchor apple and identifier com.apple.translate - 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x63, 0x6f, 0x6d, 0x2e, - 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x00, -}; - const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx) { assert(arch); // enforced by signing infrastructure Requirements::Maker maker; - - // if ppc architecture, add hosting requirement for Rosetta's translate tool - if (arch->cpuType() == CPU_TYPE_POWERPC) - maker.add(kSecHostRequirementType, ((const Requirement *)ppc_host_ireq)->clone()); // add library requirements from DYLIB commands (if any) if (Requirement *libreq = libraryRequirements(arch, ctx)) @@ -311,31 +300,47 @@ Requirement *MachORep::libraryRequirements(const Architecture *arch, const Signi auto_ptr macho(mainExecutableImage()->architecture(*arch)); Requirement::Maker maker; Requirement::Maker::Chain chain(maker, opOr); - if (macho.get()) { - for (const load_command *command = macho->loadCommands(); command; command = macho->nextCommand(command)) { - if (macho->flip(command->cmd) == LC_LOAD_DYLIB) { - const dylib_command *dycmd = (const dylib_command *)command; - if (const char *name = macho->string(command, dycmd->dylib.name)) - try { - string path = ctx.sdkPath(name); - secdebug("machorep", "examining DYLIB %s", path.c_str()); - // find path on disk, get designated requirement (if signed) - if (RefPointer rep = DiskRep::bestGuess(path)) - if (SecPointer code = new SecStaticCode(rep)) - if (const Requirement *req = code->designatedRequirement()) { - CODESIGN_SIGN_DEP_MACHO(this, (char*)path.c_str(), (void*)req); - chain.add(); - chain.maker.copy(req); - } - } catch (...) { - CODESIGN_SIGN_DEP_MACHO(this, (char*)name, NULL); - secdebug("machorep", "exception getting library requirement (ignored)"); + + if (macho.get()) + if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) { + size_t offset = macho->flip(ldep->dataoff); + size_t length = macho->flip(ldep->datasize); + if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) { + try { + secdebug("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)", + deplist->length(), deplist->count(), + mainExecutablePath().c_str(), macho->architecture().name()); + unsigned count = deplist->count(); + // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far + for (unsigned n = 0; n < count; n++) { + const Requirement *req = NULL; + if (const BlobCore *dep = deplist->blob(n)) { + if ((req = Requirement::specific(dep))) { + // binary code requirement; good to go + } else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) { + // blob-wrapped text form - convert to binary requirement + std::string reqString = std::string((const char *)wrap->data(), wrap->length()); + CFRef areq; + MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref())); + CFRef reqData; + MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref())); + req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData)); + } else { + secdebug("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n); + continue; + } + chain.add(); + maker.copy(req); + } else + secdebug("machorep", "missing DR info for library index %d", n); } - else - CODESIGN_SIGN_DEP_MACHO(this, NULL, NULL); + ::free(deplist); + } catch (...) { + ::free(deplist); + throw; + } } } - } if (chain.empty()) return NULL; else diff --git a/lib/policydb.cpp b/lib/policydb.cpp index b748dea..072d8ec 100644 --- a/lib/policydb.cpp +++ b/lib/policydb.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Apple Inc. All Rights Reserved. + * Copyright (c) 2011-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,6 +26,16 @@ #include #include #include +#include +#include +#include +#include +#include "csdatabase.h" + +#include +#include +#include +#include namespace Security { namespace CodeSigning { @@ -34,13 +44,6 @@ namespace CodeSigning { using namespace SQLite; -// -// The one and only PolicyDatabase object. -// It auto-adapts to readonly vs. writable use. -// -ModuleNexus PolicyDatabase; - - // // Determine the database path // @@ -82,14 +85,35 @@ AuthorityType typeFor(CFDictionaryRef context, AuthorityType type /* = kAuthorit return mapEnum(context, kSecAssessmentContextKeyOperation, mapType, type); } +CFStringRef typeNameFor(AuthorityType type) +{ + for (const StringMap *mp = mapType; mp->cstring; ++mp) + if (type == mp->enumeration) + return mp->cstring; + return CFStringCreateWithFormat(NULL, NULL, CFSTR("type %d"), type); +} + // -// Open the database (creating it if necessary and possible). -// Note that this isn't creating the schema; we do that on first write. +// Open the database // PolicyDatabase::PolicyDatabase(const char *path, int flags) - : SQLite::Database(path ? path : dbPath(), flags) + : SQLite::Database(path ? path : dbPath(), flags), + mLastExplicitCheck(0) { + // sqlite3 doesn't do foreign key support by default, have to turn this on per connection + SQLite::Statement foreign(*this, "PRAGMA foreign_keys = true"); + foreign.execute(); + + // Try upgrade processing if we may be open for write. + // Ignore any errors (we may have been downgraded to read-only) + // and try again later. + if (openFlags() & SQLITE_OPEN_READWRITE) + try { + upgradeDatabase(); + installExplicitSet(gkeAuthFile, gkeSigsFile); + } catch(...) { + } } PolicyDatabase::~PolicyDatabase() @@ -167,18 +191,223 @@ void PolicyDatabase::purgeObjects(double priority) cleaner.execute(); } + +// +// Database migration +// +std::string PolicyDatabase::featureLevel(const char *name) +{ + SQLite::Statement feature(*this, "SELECT value FROM feature WHERE name=:name"); + feature.bind(":name") = name; + if (feature.nextRow()) + return feature[0].string(); + else + return ""; // new feature (no level) +} + +void PolicyDatabase::addFeature(const char *name, const char *value, const char *remarks) +{ + SQLite::Statement feature(*this, "INSERT OR REPLACE INTO feature (name,value,remarks) VALUES(:name, :value, :remarks)"); + feature.bind(":name") = name; + feature.bind(":value") = value; + feature.bind(":remarks") = remarks; + feature.execute(); +} + +void PolicyDatabase::simpleFeature(const char *feature, void (^perform)()) +{ + if (!hasFeature(feature)) { + SQLite::Transaction update(*this); + addFeature(feature, "upgraded", "upgraded"); + perform(); + update.commit(); + } +} + +void PolicyDatabase::simpleFeature(const char *feature, const char *sql) +{ + if (!hasFeature(feature)) { + SQLite::Transaction update(*this); + addFeature(feature, "upgraded", "upgraded"); + SQLite::Statement perform(*this, sql); + perform.execute(); + update.commit(); + } +} + + +void PolicyDatabase::upgradeDatabase() +{ + simpleFeature("bookmarkhints", + "CREATE TABLE bookmarkhints (" + " id INTEGER PRIMARY KEY AUTOINCREMENT, " + " bookmark BLOB," + " authority INTEGER NOT NULL" + " REFERENCES authority(id) ON DELETE CASCADE" + ")"); + + if (!hasFeature("codesignedpackages")) { + SQLite::Transaction update(*this); + addFeature("codesignedpackages", "upgraded", "upgraded"); + SQLite::Statement updates(*this, + "UPDATE authority" + " SET requirement = 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and " + "(certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13])'" + " WHERE type = 2 and label = 'Developer ID' and flags & :flag"); + updates.bind(":flag") = kAuthorityFlagDefault; + updates.execute(); + update.commit(); + } +} + + +// +// Install Gatekeeper override (GKE) data. +// The arguments are paths to the authority and signature files. +// +void PolicyDatabase::installExplicitSet(const char *authfile, const char *sigfile) +{ + // only try this every gkeCheckInterval seconds + time_t now = time(NULL); + if (mLastExplicitCheck + gkeCheckInterval > now) + return; + mLastExplicitCheck = now; + + try { + if (CFRef authData = cfLoadFile(authfile)) { + CFDictionary auth(CFRef(makeCFDictionaryFrom(authData)), errSecCSDbCorrupt); + CFDictionaryRef content = auth.get(CFSTR("authority")); + std::string authUUID = cfString(auth.get(CFSTR("uuid"))); + if (authUUID.empty()) { + secdebug("gkupgrade", "no uuid in auth file; ignoring gke.auth"); + return; + } + std::string dbUUID; + SQLite::Statement uuidQuery(*this, "SELECT value FROM feature WHERE name='gke'"); + if (uuidQuery.nextRow()) + dbUUID = (const char *)uuidQuery[0]; + if (dbUUID == authUUID) { + secdebug("gkupgrade", "gke.auth already present, ignoring"); + return; + } + Syslog::notice("loading GKE %s (replacing %s)", authUUID.c_str(), dbUUID.empty() ? "nothing" : dbUUID.c_str()); + + // first, load code signatures. This is pretty much idempotent + if (sigfile) + if (FILE *sigs = fopen(sigfile, "r")) { + unsigned count = 0; + while (const BlobCore *blob = BlobCore::readBlob(sigs)) { + signatureDatabaseWriter().storeCode(blob, ""); + count++; + } + secdebug("gkupgrade", "%d detached signature(s) loaded from override data", count); + fclose(sigs); + } + + // start transaction (atomic from here on out) + SQLite::Transaction loadAuth(*this, SQLite::Transaction::exclusive, "GKE_Upgrade"); + + // purge prior authority data + SQLite::Statement purge(*this, "DELETE FROM authority WHERE flags & :flag"); + purge.bind(":flag") = kAuthorityFlagWhitelist; + purge(); + + // load new data + CFIndex count = CFDictionaryGetCount(content); + CFStringRef keys[count]; + CFDictionaryRef values[count]; + CFDictionaryGetKeysAndValues(content, (const void **)keys, (const void **)values); + + SQLite::Statement insert(*this, "INSERT INTO authority (type, allow, requirement, label, flags, remarks)" + " VALUES (:type, 1, :requirement, 'GKE', :flags, :path)"); + for (CFIndex n = 0; n < count; n++) { + CFDictionary info(values[n], errSecCSDbCorrupt); + insert.reset(); + insert.bind(":type") = cfString(info.get(CFSTR("type"))); + insert.bind(":path") = cfString(info.get(CFSTR("path"))); + insert.bind(":requirement") = "cdhash H\"" + cfString(info.get(CFSTR("cdhash"))) + "\""; + insert.bind(":flags") = kAuthorityFlagWhitelist; + insert(); + } + + // update version and commit + addFeature("gke", authUUID.c_str(), "gke loaded"); + loadAuth.commit(); + } + } catch (...) { + secdebug("gkupgrade", "exception during GKE upgrade"); + } +} + // // Check the override-enable master flag // +#define SP_ENABLE_KEY CFSTR("enabled") +#define SP_ENABLED CFSTR("yes") +#define SP_DISABLED CFSTR("no") + bool overrideAssessment() { - if (::access(visibleSecurityFlagFile, F_OK) == 0) { - return false; - } else if (errno == ENOENT) { - return true; - } else - UnixError::throwMe(); + static bool enabled = false; + static dispatch_once_t once; + static int token = -1; + static int have_token = 0; + static dispatch_queue_t queue; + int check; + + if (have_token && notify_check(token, &check) == NOTIFY_STATUS_OK && !check) + return !enabled; + + dispatch_once(&once, ^{ + if (notify_register_check(kNotifySecAssessmentMasterSwitch, &token) == NOTIFY_STATUS_OK) + have_token = 1; + queue = dispatch_queue_create("com.apple.SecAssessment.assessment", NULL); + }); + + dispatch_sync(queue, ^{ + /* upgrade configuration from emir, ignore all error since we might not be able to write to */ + if (::access(visibleSecurityFlagFile, F_OK) == 0) { + try { + setAssessment(true); + ::unlink(visibleSecurityFlagFile); + } catch (...) { + } + enabled = true; + return; + } + + try { + Dictionary * prefsDict = Dictionary::CreateDictionary(prefsFile); + if (prefsDict == NULL) + return; + + CFStringRef value = prefsDict->getStringValue(SP_ENABLE_KEY); + if (value && CFStringCompare(value, SP_DISABLED, 0) == 0) + enabled = false; + else + enabled = true; + delete prefsDict; + } catch(...) { + } + }); + + return !enabled; +} + +void setAssessment(bool masterSwitch) +{ + MutableDictionary *prefsDict = MutableDictionary::CreateMutableDictionary(prefsFile); + if (prefsDict == NULL) + prefsDict = new MutableDictionary::MutableDictionary(); + prefsDict->setValue(SP_ENABLE_KEY, masterSwitch ? SP_ENABLED : SP_DISABLED); + prefsDict->writePlistToFile(prefsFile); + delete prefsDict; + + /* make sure permissions is right */ + ::chmod(prefsFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + notify_post(kNotifySecAssessmentMasterSwitch); } diff --git a/lib/policydb.h b/lib/policydb.h index a112c16..643b4fe 100644 --- a/lib/policydb.h +++ b/lib/policydb.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Apple Inc. All Rights Reserved. + * Copyright (c) 2011-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -36,9 +36,25 @@ namespace SQLite = SQLite3; static const char defaultDatabase[] = "/var/db/SystemPolicy"; -static const char visibleSecurityFlagFile[] = "/var/db/.sp_visible"; +static const char visibleSecurityFlagFile[] = "/var/db/.sp_visible"; /* old duchess/emir style configration */ +static const char prefsFile[] = "/var/db/SystemPolicy-prefs.plist"; -static const double never = 5000000; // expires never (i.e. in the year 8977) +static const char gkeAuthFile[] = "/var/db/gke.auth"; +static const char gkeSigsFile[] = "/var/db/gke.sigs"; +static const unsigned int gkeCheckInterval = 60; // seconds + + +// +// We use Julian dates in the database, because SQLite understands them well and they convert easily to/from CFAbsoluteTime +// +static const double never = 5000000; // canonical "never" julian date (an arbitrary point in the year 8977) +static const double julianBase = 2451910.5; // julian date of CFAbsoluteTime epoch + +static inline double dateToJulian(CFDateRef time) +{ return CFDateGetAbsoluteTime(time) / 86400.0 + julianBase; } + +static inline CFDateRef julianToDate(double julian) +{ return CFDateCreate(NULL, (julian - julianBase) * 86400); } typedef SHA1::SDigest ObjectHash; @@ -60,6 +76,7 @@ enum { kAuthorityFlagVirtual = 0x0001, // virtual rule (anchoring object records) kAuthorityFlagDefault = 0x0002, // rule is part of the original default set kAuthorityFlagInhibitCache = 0x0004, // never cache outcome of this rule + kAuthorityFlagWhitelist = 0x1000, // whitelist override }; @@ -67,6 +84,8 @@ enum { // Mapping/translation to/from API space // AuthorityType typeFor(CFDictionaryRef context, AuthorityType type = kAuthorityInvalid); +CFStringRef typeNameFor(AuthorityType type) + CF_RETURNS_RETAINED; // @@ -86,7 +105,19 @@ public: public: void purgeAuthority(); void purgeObjects(); - void purgeObjects(double priority); + void purgeObjects(double priority);// + + void upgradeDatabase(); + std::string featureLevel(const char *feature); + bool hasFeature(const char *feature) { return !featureLevel(feature).empty(); } + void addFeature(const char *feature, const char *value, const char *remarks); + void simpleFeature(const char *feature, const char *sql); + void simpleFeature(const char *feature, void (^perform)()); + + void installExplicitSet(const char *auth, const char *sigs); + +private: + time_t mLastExplicitCheck; }; @@ -94,7 +125,7 @@ public: // Check the system-wide overriding flag file // bool overrideAssessment(); - +void setAssessment(bool masterSwitch); } // end namespace CodeSigning } // end namespace Security diff --git a/lib/policyengine.cpp b/lib/policyengine.cpp index 4ebe2a0..3eb43d3 100644 --- a/lib/policyengine.cpp +++ b/lib/policyengine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Apple Inc. All Rights Reserved. + * Copyright (c) 2011-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,28 +22,45 @@ */ #include "policyengine.h" #include "xar++.h" +#include "quarantine++.h" +#include "codesigning_dtrace.h" #include #include +#include #include #include #include #include #include -#include #include -#include "quarantine++.h" +#include #include +#include "SecCodePriv.h" #undef check // Macro! Yech. +extern "C" { +#include +} + + namespace Security { namespace CodeSigning { static const double NEGATIVE_HOLD = 60.0/86400; // 60 seconds to cache negative outcomes +static const char RECORDER_DIR[] = "/tmp/gke-"; // recorder mode destination for detached signatures +enum { + recorder_code_untrusted = 0, // signed but untrusted + recorder_code_adhoc = 1, // unsigned; signature recorded + recorder_code_unable = 2, // unsigned; unable to record signature +}; + -static void authorizeUpdate(SecCSFlags flags, CFDictionaryRef context); +static void authorizeUpdate(SecAssessmentFlags flags, CFDictionaryRef context); static void normalizeTarget(CFRef &target, CFDictionary &context, bool signUnsigned = false); +static bool codeInvalidityExceptions(SecStaticCodeRef code, CFMutableDictionaryRef result); +static CFTypeRef installerPolicy() CF_RETURNS_RETAINED; // @@ -65,7 +82,7 @@ void PolicyEngine::evaluate(CFURLRef path, AuthorityType type, SecAssessmentFlag { switch (type) { case kAuthorityExecute: - evaluateCode(path, flags, context, result); + evaluateCode(path, kAuthorityExecute, flags, context, result); break; case kAuthorityInstall: evaluateInstall(path, flags, context, result); @@ -84,9 +101,11 @@ void PolicyEngine::evaluate(CFURLRef path, AuthorityType type, SecAssessmentFlag // Executable code. // Read from disk, evaluate properly, cache as indicated. The whole thing, so far. // -void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result) +void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result) { - const AuthorityType type = kAuthorityExecute; + FileQuarantine qtn(cfString(path).c_str()); + if (qtn.flag(QTN_FLAG_HARD)) + MacOSError::throwMe(errSecCSFileHardQuarantined); CFRef code; MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref())); @@ -111,15 +130,57 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti CFRef requirement; MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref())); + OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags, requirement); + + if (rc == errSecCSUnsigned && !overrideAssessment()) { + try { + // ad-hoc sign the code and attach the signature + CFRef signature = CFDataCreateMutable(NULL, 0); + CFTemp arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached, signature.get(), kSecCodeSignerIdentity); + CFRef signer; + MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref())); + MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags)); + MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags)); + + // if we're in GKE recording mode, save that signature and report its location + if (SYSPOLICY_RECORDER_MODE_ENABLED()) { + int status = recorder_code_unable; // ephemeral signature (not recorded) + if (geteuid() == 0) { + CFRef uuid = CFUUIDCreate(NULL); + std::string sigfile = RECORDER_DIR + cfStringRelease(CFUUIDCreateString(NULL, uuid)) + ".tsig"; + try { + UnixPlusPlus::AutoFileDesc fd(sigfile, O_WRONLY | O_CREAT); + fd.write(CFDataGetBytePtr(signature), CFDataGetLength(signature)); + status = recorder_code_adhoc; // recorded signature + SYSPOLICY_RECORDER_MODE_ADHOC_PATH(cfString(path).c_str(), type, sigfile.c_str()); + } catch (...) { } + } - OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSBasicValidateOnly, requirement); - // ok, so this rule matches lets do a full validation if not overriding assessments - if (rc == noErr && !overrideAssessment()) - rc = SecStaticCodeCheckValidity(code, validationFlags, requirement); + // now report the D probe itself + CFRef info; + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref())); + CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); + SYSPOLICY_RECORDER_MODE(cfString(path).c_str(), type, "", + cdhash ? CFDataGetBytePtr(cdhash) : NULL, status); + } + + // rerun the validation to update state + rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSBasicValidateOnly, requirement); + } catch (...) { } + } switch (rc) { - case noErr: // success - break; + case noErr: // well signed and satisfies requirement... + break; // ... continue below + case errSecCSSignatureFailed: + if (!codeInvalidityExceptions(code, result)) { + if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED()) + SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, false); + MacOSError::throwMe(rc); + } + if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED()) + SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, true); + // treat as unsigned to fix problems in the field case errSecCSUnsigned: cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict); addAuthority(result, "no usable signature"); @@ -152,23 +213,47 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti CFRef xinfo; MacOSError::check(SecTrustCopyExtendedResult(trust, &xinfo.aref())); if (CFDateRef limit = CFDateRef(CFDictionaryGetValue(xinfo, kSecTrustExpirationDate))) { - double julianLimit = CFDateGetAbsoluteTime(limit) / 86400.0 + 2451910.5; - this->recordOutcome(code, allow, type, min(expires, julianLimit), id); + this->recordOutcome(code, allow, type, min(expires, dateToJulian(limit)), id); } } } + if (allow) { + if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED()) { + if (!info) + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())); + CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); + SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, cdhash ? CFDataGetBytePtr(cdhash) : NULL); + } + } else { + if (SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) { + if (!info) + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())); + CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); + std::string cpath = cfString(path); + const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL; + SYSPOLICY_ASSESS_OUTCOME_DENY(cpath.c_str(), type, label, hashp); + SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, label, hashp, recorder_code_untrusted); + } + } cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow); addAuthority(result, label, id); return; } // no applicable authority. Deny by default + CFRef info; + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())); if (flags & kSecAssessmentFlagRequestOrigin) { - CFRef info; // as needed - MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())); if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates))) setOrigin(chain, result); } + if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) { + CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique)); + const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL; + std::string cpath = cfString(path); + SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cpath.c_str(), type, latentLabel.c_str(), hashp); + SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, latentLabel.c_str(), hashp, 0); + } if (!(flags & kSecAssessmentFlagNoCache)) this->recordOutcome(code, false, type, this->julianNow() + NEGATIVE_HOLD, latentID); cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false); @@ -178,92 +263,105 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti // // Installer archive. -// Certs passed from caller (untrusted), no policy engine yet, no caching (since untrusted). -// The current "policy" is to trust any proper signature. +// Hybrid policy: If we detect an installer signature, use and validate that. +// If we don't, check for a code signature instead. // -static CFTypeRef installerPolicy(); - void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result) { const AuthorityType type = kAuthorityInstall; + + Xar xar(cfString(path).c_str()); + if (!xar) { + // follow the code signing path + evaluateCode(path, type, flags, context, result); + return; + } SQLite3::int64 latentID = 0; // first (highest priority) disabled matching ID std::string latentLabel; // ... and associated label, if any - Xar xar(cfString(path).c_str()); - if (xar) { - if (!xar.isSigned()) { - // unsigned xar - cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false); - addAuthority(result, "no usable signature"); - return; - } - if (CFRef certs = xar.copyCertChain()) { - CFRef policy = installerPolicy(); - CFRef trust; - MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref())); -// MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors - MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors)); - - SecTrustResultType trustResult; - MacOSError::check(SecTrustEvaluate(trust, &trustResult)); - CFRef chain; - CSSM_TP_APPLE_EVIDENCE_INFO *info; - MacOSError::check(SecTrustGetResult(trust, &trustResult, &chain.aref(), &info)); - - if (flags & kSecAssessmentFlagRequestOrigin) - setOrigin(chain, result); + if (!xar.isSigned()) { + // unsigned xar + if (SYSPOLICY_ASSESS_OUTCOME_UNSIGNED_ENABLED()) + SYSPOLICY_ASSESS_OUTCOME_UNSIGNED(cfString(path).c_str(), type); + cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false); + addAuthority(result, "no usable signature"); + return; + } + if (CFRef certs = xar.copyCertChain()) { + CFRef policy = installerPolicy(); + CFRef trust; + MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref())); +// MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors + MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors)); + + SecTrustResultType trustResult; + MacOSError::check(SecTrustEvaluate(trust, &trustResult)); + CFRef chain; + CSSM_TP_APPLE_EVIDENCE_INFO *info; + MacOSError::check(SecTrustGetResult(trust, &trustResult, &chain.aref(), &info)); - switch (trustResult) { - case kSecTrustResultProceed: - case kSecTrustResultConfirm: - case kSecTrustResultUnspecified: + if (flags & kSecAssessmentFlagRequestOrigin) + setOrigin(chain, result); + + switch (trustResult) { + case kSecTrustResultProceed: + case kSecTrustResultUnspecified: + break; + default: + { + OSStatus rc; + MacOSError::check(SecTrustGetCssmResultCode(trust, &rc)); + MacOSError::throwMe(rc); + } + } + + SQLite::Statement query(*this, + "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority" + " WHERE type = :type" + " ORDER BY priority DESC;"); + query.bind(":type").integer(type); + while (query.nextRow()) { + bool allow = int(query[0]); + const char *reqString = query[1]; + SQLite3::int64 id = query[2]; + const char *label = query[3]; + //sqlite_uint64 ruleFlags = query[4]; + SQLite3::int64 disabled = query[5]; + + CFRef requirement; + MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref())); + switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) { + case noErr: // success break; - default: - { - OSStatus rc; - MacOSError::check(SecTrustGetCssmResultCode(trust, &rc)); - MacOSError::throwMe(rc); + case errSecCSReqFailed: // requirement missed, but otherwise okay + continue; + default: // broken in some way; all tests will fail like this so bail out + MacOSError::throwMe(rc); + } + if (disabled) { + if (latentID == 0) { + latentID = id; + if (label) + latentLabel = label; } + continue; // the loop } - SQLite::Statement query(*this, - "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority" - " WHERE type = :type" - " ORDER BY priority DESC;"); - query.bind(":type").integer(type); - while (query.nextRow()) { - bool allow = int(query[0]); - const char *reqString = query[1]; - SQLite3::int64 id = query[2]; - const char *label = query[3]; - //sqlite_uint64 ruleFlags = query[4]; - SQLite3::int64 disabled = query[5]; - - CFRef requirement; - MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref())); - switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) { - case noErr: // success - break; - case errSecCSReqFailed: // requirement missed, but otherwise okay - continue; - default: // broken in some way; all tests will fail like this so bail out - MacOSError::throwMe(rc); - } - if (disabled) { - if (latentID == 0) { - latentID = id; - if (label) - latentLabel = label; - } - continue; // the loop - } - // not adding to the object cache - we could, but it's not likely to be worth it - cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow); - addAuthority(result, label, id); - return; + if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED() || SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED()) { + if (allow) + SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, NULL); + else + SYSPOLICY_ASSESS_OUTCOME_DENY(cfString(path).c_str(), type, label, NULL); } + + // not adding to the object cache - we could, but it's not likely to be worth it + cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow); + addAuthority(result, label, id); + return; } } + if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED()) + SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cfString(path).c_str(), type, latentLabel.c_str(), NULL); // no applicable authority. Deny by default cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict); @@ -317,13 +415,17 @@ void PolicyEngine::evaluateDocOpen(CFURLRef path, SecAssessmentFlags flags, CFDi { if (context) { if (CFStringRef riskCategory = CFStringRef(CFDictionaryGetValue(context, kLSDownloadRiskCategoryKey))) { + FileQuarantine qtn(cfString(path).c_str()); + if (CFEqual(riskCategory, kLSRiskCategorySafe) || CFEqual(riskCategory, kLSRiskCategoryNeutral) || CFEqual(riskCategory, kLSRiskCategoryUnknown) || CFEqual(riskCategory, kLSRiskCategoryMayContainUnsafeExecutable)) { cfadd(result, "{%O=#T}", kSecAssessmentAssessmentVerdict); addAuthority(result, "_XProtect"); - } else if (FileQuarantine(cfString(path).c_str()).flag(QTN_FLAG_ASSESSMENT_OK)) { + } else if (qtn.flag(QTN_FLAG_HARD)) { + MacOSError::throwMe(errSecCSFileHardQuarantined); + } else if (qtn.flag(QTN_FLAG_ASSESSMENT_OK)) { cfadd(result, "{%O=#T}", kSecAssessmentAssessmentVerdict); addAuthority(result, "Prior Assessment"); } else { @@ -350,6 +452,8 @@ void PolicyEngine::addAuthority(CFMutableDictionaryRef parent, const char *label cfadd(auth, "{%O=%s}", kSecAssessmentAssessmentSource, label); if (row) CFDictionaryAddValue(auth, kSecAssessmentAssessmentAuthorityRow, CFTempNumber(row)); + if (overrideAssessment()) + CFDictionaryAddValue(auth, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); if (cacheInfo) CFDictionaryAddValue(auth, kSecAssessmentAssessmentFromCache, cacheInfo); CFDictionaryAddValue(parent, kSecAssessmentAssessmentAuthority, auth); @@ -366,7 +470,7 @@ void PolicyEngine::addToAuthority(CFMutableDictionaryRef parent, CFStringRef key // // Add a rule to the policy database // -bool PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +CFDictionaryRef PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) { // default type to execution if (type == kAuthorityInvalid) @@ -375,25 +479,39 @@ bool PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlag authorizeUpdate(flags, context); CFDictionary ctx(context, errSecCSInvalidAttributeValues); CFCopyRef target = inTarget; - - if (type == kAuthorityOpenDoc) { + CFRef bookmark = NULL; + + switch (type) { + case kAuthorityExecute: + normalizeTarget(target, ctx, true); + // bookmarks are untrusted and just a hint to callers + bookmark = ctx.get(kSecAssessmentRuleKeyBookmark); + break; + case kAuthorityInstall: + if (inTarget && CFGetTypeID(inTarget) == CFURLGetTypeID()) { + // no good way to turn an installer file into a requirement. Pretend to succeeed so caller proceeds + return cfmake("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("virtual install")); + } + break; + case kAuthorityOpenDoc: // handle document-open differently: use quarantine flags for whitelisting - if (!target || CFGetTypeID(target) != CFURLGetTypeID()) + if (!target || CFGetTypeID(target) != CFURLGetTypeID()) // can only "add" file paths MacOSError::throwMe(errSecCSInvalidObjectRef); - std::string spath = cfString(target.as()).c_str(); - FileQuarantine qtn(spath.c_str()); - qtn.setFlag(QTN_FLAG_ASSESSMENT_OK); - qtn.applyTo(spath.c_str()); - return true; - } - - if (type == kAuthorityInstall) { - return cfmake("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("virtual install")); + try { + std::string spath = cfString(target.as()); + FileQuarantine qtn(spath.c_str()); + qtn.setFlag(QTN_FLAG_ASSESSMENT_OK); + qtn.applyTo(spath.c_str()); + } catch (const CommonError &error) { + // could not set quarantine flag - report qualified success + return cfmake("{%O=%O,'assessment:error'=%d}", + kSecAssessmentAssessmentAuthorityOverride, CFSTR("error setting quarantine"), error.osStatus()); + } catch (...) { + return cfmake("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("unable to set quarantine")); + } + return NULL; } - // resolve URLs to Requirements - normalizeTarget(target, ctx, true); - // if we now have anything else, we're busted if (!target || CFGetTypeID(target) != SecRequirementGetTypeID()) MacOSError::throwMe(errSecCSInvalidObjectRef); @@ -410,7 +528,7 @@ bool PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlag label = cfString(lab); if (CFDateRef time = ctx.get(kSecAssessmentUpdateKeyExpires)) // we're using Julian dates here; convert from CFDate - expires = CFDateGetAbsoluteTime(time) / 86400.0 + 2451910.5; + expires = dateToJulian(time); if (CFBooleanRef allowing = ctx.get(kSecAssessmentUpdateKeyAllow)) allow = allowing == kCFBooleanTrue; if (CFStringRef rem = ctx.get(kSecAssessmentUpdateKeyRemarks)) @@ -427,60 +545,175 @@ bool PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlag insert.bind(":requirement") = requirementText.get(); insert.bind(":priority") = priority; if (!label.empty()) - insert.bind(":label") = label.c_str(); + insert.bind(":label") = label; insert.bind(":expires") = expires; if (!remarks.empty()) - insert.bind(":remarks") = remarks.c_str(); + insert.bind(":remarks") = remarks; insert.execute(); + SQLite::int64 newRow = this->lastInsert(); + if (bookmark) { + SQLite::Statement bi(*this, "INSERT INTO bookmarkhints (bookmark, authority) VALUES (:bookmark, :authority)"); + bi.bind(":bookmark") = CFDataRef(bookmark); + bi.bind(":authority").integer(newRow); + bi.execute(); + } this->purgeObjects(priority); xact.commit(); notify_post(kNotifySecAssessmentUpdate); - return true; + return cfmake("{%O=%d}", kSecAssessmentUpdateKeyRow, newRow); +} + + +CFDictionaryRef PolicyEngine::remove(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +{ + if (type == kAuthorityOpenDoc) { + // handle document-open differently: use quarantine flags for whitelisting + authorizeUpdate(flags, context); + if (!target || CFGetTypeID(target) != CFURLGetTypeID()) + MacOSError::throwMe(errSecCSInvalidObjectRef); + std::string spath = cfString(CFURLRef(target)).c_str(); + FileQuarantine qtn(spath.c_str()); + qtn.clearFlag(QTN_FLAG_ASSESSMENT_OK); + qtn.applyTo(spath.c_str()); + return NULL; + } + return manipulateRules("DELETE FROM authority", target, type, flags, context); +} + +CFDictionaryRef PolicyEngine::enable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +{ + return manipulateRules("UPDATE authority SET disabled = 0", target, type, flags, context); +} + +CFDictionaryRef PolicyEngine::disable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +{ + return manipulateRules("UPDATE authority SET disabled = 1", target, type, flags, context); +} + +CFDictionaryRef PolicyEngine::find(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +{ + SQLite::Statement query(*this); + selectRules(query, "SELECT scan_authority.id, scan_authority.type, scan_authority.requirement, scan_authority.allow, scan_authority.label, scan_authority.priority, scan_authority.remarks, scan_authority.expires, scan_authority.disabled, bookmarkhints.bookmark FROM scan_authority LEFT OUTER JOIN bookmarkhints ON scan_authority.id = bookmarkhints.authority", + "scan_authority", target, type, flags, context, + " ORDER BY priority DESC"); + CFRef found = makeCFMutableArray(0); + while (query.nextRow()) { + SQLite::int64 id = query[0]; + int type = int(query[1]); + const char *requirement = query[2]; + int allow = int(query[3]); + const char *label = query[4]; + double priority = query[5]; + const char *remarks = query[6]; + double expires = query[7]; + int disabled = int(query[8]); + CFRef bookmark = query[9].data(); + CFRef rule = makeCFMutableDictionary(5, + kSecAssessmentRuleKeyID, CFTempNumber(id).get(), + kSecAssessmentRuleKeyType, CFRef(typeNameFor(type)).get(), + kSecAssessmentRuleKeyRequirement, CFTempString(requirement).get(), + kSecAssessmentRuleKeyAllow, allow ? kCFBooleanTrue : kCFBooleanFalse, + kSecAssessmentRuleKeyPriority, CFTempNumber(priority).get() + ); + if (label) + CFDictionaryAddValue(rule, kSecAssessmentRuleKeyLabel, CFTempString(label)); + if (remarks) + CFDictionaryAddValue(rule, kSecAssessmentRuleKeyRemarks, CFTempString(remarks)); + if (expires != never) + CFDictionaryAddValue(rule, kSecAssessmentRuleKeyExpires, CFRef(julianToDate(expires))); + if (disabled) + CFDictionaryAddValue(rule, kSecAssessmentRuleKeyDisabled, CFTempNumber(disabled)); + if (bookmark) + CFDictionaryAddValue(rule, kSecAssessmentRuleKeyBookmark, bookmark); + CFArrayAppendValue(found, rule); + } + if (CFArrayGetCount(found) == 0) + MacOSError::throwMe(errSecCSNoMatches); + return cfmake("{%O=%O}", kSecAssessmentUpdateKeyFound, found.get()); +} + + +CFDictionaryRef PolicyEngine::update(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context) +{ + AuthorityType type = typeFor(context, kAuthorityInvalid); + CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate)); + CFDictionaryRef result; + if (CFEqual(edit, kSecAssessmentUpdateOperationAdd)) + result = this->add(target, type, flags, context); + else if (CFEqual(edit, kSecAssessmentUpdateOperationRemove)) + result = this->remove(target, type, flags, context); + else if (CFEqual(edit, kSecAssessmentUpdateOperationEnable)) + result = this->enable(target, type, flags, context); + else if (CFEqual(edit, kSecAssessmentUpdateOperationDisable)) + result = this->disable(target, type, flags, context); + else if (CFEqual(edit, kSecAssessmentUpdateOperationFind)) + result = this->find(target, type, flags, context); + else + MacOSError::throwMe(errSecCSInvalidAttributeValues); + if (result == NULL) + result = makeCFDictionary(0); // success, no details + return result; } // -// Perform an action on existing authority rule(s) +// Construct and prepare an SQL query on the authority table, operating on some set of existing authority records. +// In essence, this appends a suitable WHERE clause to the stanza passed and prepares it on the statement given. // -bool PolicyEngine::manipulateRules(const std::string &stanza, - CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +void PolicyEngine::selectRules(SQLite::Statement &action, std::string phrase, std::string table, + CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, std::string suffix /* = "" */) { - authorizeUpdate(flags, context); CFDictionary ctx(context, errSecCSInvalidAttributeValues); CFCopyRef target = inTarget; normalizeTarget(target, ctx); string label; - if (CFStringRef lab = ctx.get(kSecAssessmentUpdateKeyLabel)) label = cfString(CFStringRef(lab)); - SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "rule_change"); - SQLite::Statement action(*this); if (!target) { - if (label.empty()) // underspecified - MacOSError::throwMe(errSecCSInvalidObjectRef); - if (type == kAuthorityInvalid) { - action.query(stanza + " WHERE label = :label"); - } else { - action.query(stanza + " WHERE type = :type AND label = :label"); - action.bind(":type").integer(type); + if (label.empty()) { + if (type == kAuthorityInvalid) { + action.query(phrase + suffix); + } else { + action.query(phrase + " WHERE " + table + ".type = :type" + suffix); + action.bind(":type").integer(type); + } + } else { // have label + if (type == kAuthorityInvalid) { + action.query(phrase + " WHERE " + table + ".label = :label" + suffix); + } else { + action.query(phrase + " WHERE " + table + ".type = :type AND " + table + ".label = :label" + suffix); + action.bind(":type").integer(type); + } + action.bind(":label") = label; } - action.bind(":label") = label.c_str(); } else if (CFGetTypeID(target) == CFNumberGetTypeID()) { - action.query(stanza + " WHERE id = :id"); + action.query(phrase + " WHERE " + table + ".id = :id" + suffix); action.bind(":id").integer(cfNumber(target.as())); } else if (CFGetTypeID(target) == SecRequirementGetTypeID()) { if (type == kAuthorityInvalid) type = kAuthorityExecute; CFRef requirementText; MacOSError::check(SecRequirementCopyString(target.as(), kSecCSDefaultFlags, &requirementText.aref())); - action.query(stanza + " WHERE type = :type AND requirement = :requirement"); + action.query(phrase + " WHERE " + table + ".type = :type AND " + table + ".requirement = :requirement" + suffix); action.bind(":type").integer(type); action.bind(":requirement") = requirementText.get(); } else MacOSError::throwMe(errSecCSInvalidObjectRef); +} + +// +// Execute an atomic change to existing records in the authority table. +// +CFDictionaryRef PolicyEngine::manipulateRules(const std::string &stanza, + CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) +{ + SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "rule_change"); + SQLite::Statement action(*this); + authorizeUpdate(flags, context); + selectRules(action, stanza, "authority", inTarget, type, flags, context); action.execute(); unsigned int changes = this->changes(); // latch change count // We MUST purge objects with priority <= MAX(priority of any changed rules); @@ -489,57 +722,13 @@ bool PolicyEngine::manipulateRules(const std::string &stanza, this->purgeObjects(1.0E100); xact.commit(); notify_post(kNotifySecAssessmentUpdate); - return true; + return cfmake("{%O=%d}", kSecAssessmentUpdateKeyCount, changes); } // no change; return an error MacOSError::throwMe(errSecCSNoMatches); } -bool PolicyEngine::remove(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) -{ - if (type == kAuthorityOpenDoc) { - // handle document-open differently: use quarantine flags for whitelisting - authorizeUpdate(flags, context); - if (!target || CFGetTypeID(target) != CFURLGetTypeID()) - MacOSError::throwMe(errSecCSInvalidObjectRef); - std::string spath = cfString(CFURLRef(target)).c_str(); - FileQuarantine qtn(spath.c_str()); - qtn.clearFlag(QTN_FLAG_ASSESSMENT_OK); - qtn.applyTo(spath.c_str()); - return true; - } - return manipulateRules("DELETE FROM authority", target, type, flags, context); -} - -bool PolicyEngine::enable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) -{ - return manipulateRules("UPDATE authority SET disabled = 0", target, type, flags, context); -} - -bool PolicyEngine::disable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) -{ - return manipulateRules("UPDATE authority SET disabled = 1", target, type, flags, context); -} - - -bool PolicyEngine::update(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context) -{ - AuthorityType type = typeFor(context, kAuthorityInvalid); - CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate)); - if (CFEqual(edit, kSecAssessmentUpdateOperationAdd)) - return this->add(target, type, flags, context); - else if (CFEqual(edit, kSecAssessmentUpdateOperationRemove)) - return this->remove(target, type, flags, context); - else if (CFEqual(edit, kSecAssessmentUpdateOperationEnable)) - return this->enable(target, type, flags, context); - else if (CFEqual(edit, kSecAssessmentUpdateOperationDisable)) - return this->disable(target, type, flags, context); - else - MacOSError::throwMe(errSecCSInvalidAttributeValues); -} - - // // Fill in extra information about the originator of cryptographic credentials found - if any // @@ -548,15 +737,17 @@ void PolicyEngine::setOrigin(CFArrayRef chain, CFMutableDictionaryRef result) if (chain) if (CFArrayGetCount(chain) > 0) if (SecCertificateRef leaf = SecCertificateRef(CFArrayGetValueAtIndex(chain, 0))) - if (CFStringRef summary = SecCertificateCopyLongDescription(NULL, leaf, NULL)) + if (CFStringRef summary = SecCertificateCopyLongDescription(NULL, leaf, NULL)) { CFDictionarySetValue(result, kSecAssessmentAssessmentOriginator, summary); + CFRelease(summary); + } } // // Take an assessment outcome and record it in the object cache // -void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, int authority) +void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, SQLite::int64 authority) { CFRef info; MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref())); @@ -575,7 +766,7 @@ void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityTyp insert.bind(":allow").integer(allow); insert.bind(":hash") = cdHash; insert.bind(":expires") = expires; - insert.bind(":path") = cfString(path).c_str(); + insert.bind(":path") = cfString(path); insert.bind(":authority").integer(authority); insert.execute(); xact.commit(); @@ -586,7 +777,7 @@ void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityTyp // Perform update authorization processing. // Throws an exception if authorization is denied. // -static void authorizeUpdate(SecCSFlags flags, CFDictionaryRef context) +static void authorizeUpdate(SecAssessmentFlags flags, CFDictionaryRef context) { AuthorizationRef authorization = NULL; @@ -619,9 +810,13 @@ static void normalizeTarget(CFRef &target, CFDictionary &context, boo if (target && CFGetTypeID(target) == CFURLGetTypeID()) { CFRef code; MacOSError::check(SecStaticCodeCreateWithPath(target.as(), kSecCSDefaultFlags, &code.aref())); - CFRef requirement; switch (OSStatus rc = SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref())) { - case noErr: + case noErr: { + // use the *default* DR to avoid unreasonably wide DRs opening up Gatekeeper to attack + CFRef info; + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSRequirementInformation, &info.aref())); + target = CFDictionaryGetValue(info, kSecCodeInfoImplicitDesignatedRequirement); + } break; case errSecCSUnsigned: if (signUnsigned) { @@ -633,7 +828,19 @@ static void normalizeTarget(CFRef &target, CFDictionary &context, boo MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref())); break; } - // fall through + MacOSError::check(rc); + case errSecCSSignatureFailed: + // recover certain cases of broken signatures (well, try) + if (codeInvalidityExceptions(code, NULL)) { + // Ad-hoc sign the code in place (requiring a writable subject). This requires root privileges. + CFRef signer; + CFTemp arguments("{%O=#N}", kSecCodeSignerIdentity); + MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref())); + MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags)); + MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref())); + break; + } + MacOSError::check(rc); default: MacOSError::check(rc); } @@ -649,5 +856,29 @@ static void normalizeTarget(CFRef &target, CFDictionary &context, boo } +// +// Process special overrides for invalidly signed code. +// This is the (hopefully minimal) concessions we make to keep hurting our customers +// for our own prior mistakes... +// +static bool codeInvalidityExceptions(SecStaticCodeRef code, CFMutableDictionaryRef result) +{ + if (OSAIsRecognizedExecutableURL) { + CFRef info; + MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref())); + if (CFURLRef executable = CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable))) { + SInt32 error; + if (OSAIsRecognizedExecutableURL(executable, &error)) { + if (result) + CFDictionaryAddValue(result, + kSecAssessmentAssessmentAuthorityOverride, CFSTR("ignoring known invalid applet signature")); + return true; + } + } + } + return false; +} + + } // end namespace CodeSigning } // end namespace Security diff --git a/lib/policyengine.h b/lib/policyengine.h index e22f50d..7ae9bfc 100644 --- a/lib/policyengine.h +++ b/lib/policyengine.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Apple Inc. All Rights Reserved. + * Copyright (c) 2011-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -53,27 +53,30 @@ public: public: void evaluate(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); - bool update(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context); - bool add(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); - bool remove(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); - bool enable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); - bool disable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef update(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef add(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef remove(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef enable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef disable(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); + CFDictionaryRef find(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); public: static void addAuthority(CFMutableDictionaryRef parent, const char *label, SQLite::int64 row = 0, CFTypeRef cacheInfo = NULL); static void addToAuthority(CFMutableDictionaryRef parent, CFStringRef key, CFTypeRef value); private: - void evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); + void evaluateCode(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); void evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); void evaluateDocOpen(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); - bool manipulateRules(const std::string &stanza, + void selectRules(SQLite::Statement &action, std::string stanza, std::string table, + CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, std::string suffix = ""); + CFDictionaryRef manipulateRules(const std::string &stanza, CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context); void setOrigin(CFArrayRef chain, CFMutableDictionaryRef result); - void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, int authority); + void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, SQLite::int64 authority); }; diff --git a/lib/reqinterp.cpp b/lib/reqinterp.cpp index 5e1567d..e3e8233 100644 --- a/lib/reqinterp.cpp +++ b/lib/reqinterp.cpp @@ -239,7 +239,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma if (cf->name == key) { CFRef value; if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { - secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), rc); + secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc); return false; } return match(value); @@ -249,7 +249,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma if (key == "email") { CFRef value; if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) { - secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, rc); + secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc); return false; } return match(value); diff --git a/lib/requirement.cpp b/lib/requirement.cpp index 714e6c5..cc52729 100644 --- a/lib/requirement.cpp +++ b/lib/requirement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. + * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -102,6 +102,8 @@ SecCertificateRef Requirement::Context::cert(int ix) const if (certs) { if (ix < 0) ix += certCount(); + if (ix >= CFArrayGetCount(certs)) + return NULL; if (CFTypeRef element = CFArrayGetValueAtIndex(certs, ix)) return SecCertificateRef(element); } @@ -154,21 +156,6 @@ const SHA1::Digest &Requirement::testAppleAnchorHash() #endif //TEST_APPLE_ANCHOR -// -// InternalRequirements -// -void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted) -{ - if (defaulted) { - this->add(defaulted); - ::free((void *)defaulted); // was malloc(3)ed by DiskRep - } - if (given) - this->add(given); - mReqs = make(); -} - - // // Debug dump support // diff --git a/lib/requirement.h b/lib/requirement.h index 4588a02..dc59fa1 100644 --- a/lib/requirement.h +++ b/lib/requirement.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. + * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -99,17 +99,23 @@ private: // An interpretation context // class Requirement::Context { -public: - Context(CFArrayRef certChain, CFDictionaryRef infoDict, CFDictionaryRef entitlementDict, const CodeDirectory *dir) - : certs(certChain), info(infoDict), entitlements(entitlementDict), directory(dir) { } - - const CFArrayRef certs; // certificate chain - const CFDictionaryRef info; // Info.plist - const CFDictionaryRef entitlements; // entitlement plist - const CodeDirectory * const directory; // CodeDirectory +protected: + Context() + : certs(NULL), info(NULL), entitlements(NULL), identifier(""), directory(NULL) { } - SecCertificateRef cert(int ix) const; // get a cert from the cert chain - unsigned int certCount() const; // length of cert chain +public: + Context(CFArrayRef certChain, CFDictionaryRef infoDict, CFDictionaryRef entitlementDict, + const std::string &ident, const CodeDirectory *dir) + : certs(certChain), info(infoDict), entitlements(entitlementDict), identifier(ident), directory(dir) { } + + CFArrayRef certs; // certificate chain + CFDictionaryRef info; // Info.plist + CFDictionaryRef entitlements; // entitlement plist + std::string identifier; // signing identifier + const CodeDirectory *directory; // CodeDirectory + + SecCertificateRef cert(int ix) const; // get a cert from the cert chain (NULL if not found) + unsigned int certCount() const; // length of cert chain (including root) }; @@ -178,21 +184,6 @@ enum MatchOperation { typedef SuperBlob<0xfade0c01> Requirements; -// -// A helper to deal with the magic merger logic of internal requirements -// -class InternalRequirements : public Requirements::Maker { -public: - InternalRequirements() : mReqs(NULL) { } - ~InternalRequirements() { ::free((void *)mReqs); } - void operator () (const Requirements *given, const Requirements *defaulted); - operator const Requirements * () const { return mReqs; } - -private: - const Requirements *mReqs; -}; - - // // Byte order flippers // diff --git a/lib/security_codesigning.d b/lib/security_codesigning.d index 4dbcb67..c4849d9 100644 --- a/lib/security_codesigning.d +++ b/lib/security_codesigning.d @@ -79,8 +79,18 @@ provider codesign { provider syspolicy { - probe assess_api(const char *path, uint32_t flags); + probe assess_api(const char *path, int type, uint64_t flags); + + probe assess__outcome__accept(const char *path, int type, const char *label, const void *cdhash); + probe assess__outcome__deny(const char *path, int type, const char *label, const void *cdhash); + probe assess__outcome__default(const char *path, int type, const char *label, const void *cdhash); + probe assess__outcome__unsigned(const char *path, int type); + probe assess__outcome__broken(const char *path, int type, bool exception_made); + + probe recorder_mode(const char *path, int type, const char *label, const void *cdhash, int flags); + probe recorder_mode_adhoc_path(const char *path, int type, const char *sig_path); // path containing adhoc signature recorded + probe assess_cache_hit(); probe assess_local(); probe assess_remote(); -}; \ No newline at end of file +}; diff --git a/lib/security_codesigning.exp b/lib/security_codesigning.exp index a2ca395..de23f60 100644 --- a/lib/security_codesigning.exp +++ b/lib/security_codesigning.exp @@ -77,6 +77,10 @@ _kSecCodeSignerFlags _kSecCodeSignerIdentifier _kSecCodeSignerIdentifierPrefix _kSecCodeSignerIdentity +_kSecCodeSignerTimestampAuthentication +_kSecCodeSignerRequireTimestamp +_kSecCodeSignerTimestampServer +_kSecCodeSignerTimestampOmitCertificates _kSecCodeSignerPageSize _kSecCodeSignerRequirements _kSecCodeSignerResourceRules @@ -86,8 +90,10 @@ _kSecCodeInfoCertificates _kSecCodeInfoChangedFiles _kSecCodeInfoCMS _kSecCodeInfoTime +_kSecCodeInfoTimestamp _kSecCodeInfoDesignatedRequirement _kSecCodeInfoEntitlements +_kSecCodeInfoEntitlementsDict _kSecCodeInfoFormat _kSecCodeInfoDigestAlgorithm _kSecCodeInfoIdentifier @@ -109,6 +115,7 @@ _kSecGuestAttributeMachPort _kSecGuestAttributePid _kSecRequirementKeyInfoPlist _kSecRequirementKeyEntitlements +_kSecRequirementKeyIdentifier _kSecCFErrorArchitecture _kSecCFErrorPattern _kSecCFErrorResourceSeal @@ -129,6 +136,7 @@ _SecTaskCopyValuesForEntitlements # Assessments _SecAssessmentCreate _SecAssessmentCopyResult +_SecAssessmentCopyUpdate _SecAssessmentUpdate _SecAssessmentControl _kSecAssessmentContextKeyOperation @@ -140,12 +148,25 @@ _kSecAssessmentUpdateOperationAdd _kSecAssessmentUpdateOperationRemove _kSecAssessmentUpdateOperationEnable _kSecAssessmentUpdateOperationDisable -_kSecAssessmentUpdateKeyAuthorization +_kSecAssessmentUpdateOperationFind _kSecAssessmentUpdateKeyAllow +_kSecAssessmentUpdateKeyLabel _kSecAssessmentUpdateKeyExpires _kSecAssessmentUpdateKeyPriority -_kSecAssessmentUpdateKeyLabel _kSecAssessmentUpdateKeyRemarks +_kSecAssessmentUpdateKeyRow +_kSecAssessmentUpdateKeyCount +_kSecAssessmentUpdateKeyFound +_kSecAssessmentRuleKeyID +_kSecAssessmentRuleKeyPriority +_kSecAssessmentRuleKeyAllow +_kSecAssessmentRuleKeyLabel +_kSecAssessmentRuleKeyRemarks +_kSecAssessmentRuleKeyRequirement +_kSecAssessmentRuleKeyType +_kSecAssessmentRuleKeyExpires +_kSecAssessmentRuleKeyDisabled +_kSecAssessmentRuleKeyBookmark _kSecAssessmentAssessmentAuthority _kSecAssessmentAssessmentAuthorityRow _kSecAssessmentAssessmentFromCache @@ -154,3 +175,4 @@ _kSecAssessmentAssessmentAuthorityOverride _kSecAssessmentAssessmentSource _kSecAssessmentAssessmentVerdict _kSecAssessmentContextKeyCertificates +_kSecAssessmentUpdateKeyAuthorization diff --git a/lib/sigblob.h b/lib/sigblob.h index 969b728..c90f5e8 100644 --- a/lib/sigblob.h +++ b/lib/sigblob.h @@ -60,6 +60,12 @@ public: typedef SuperBlob<0xfade0cc1> DetachedSignatureBlob; // indexed by main architecture +// +// The linkers produces a superblob of dependency records from its dylib inputs +// +typedef SuperBlob<0xfade0c05> LibraryDependencyBlob; // indexed sequentially from 0 + + // // An entitlement blob is used for embedding entitlement configuration data // diff --git a/lib/signer.cpp b/lib/signer.cpp index 23cbfd6..92fce92 100644 --- a/lib/signer.cpp +++ b/lib/signer.cpp @@ -51,10 +51,11 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags) { rep = code->diskRep()->base(); this->prepare(flags); + PreSigningContext context(*this); if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { - signMachO(fat); + signMachO(fat, context); } else { - signArchitectureAgnostic(); + signArchitectureAgnostic(context); } } @@ -112,7 +113,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) } else { cdFlags = 0; if (infoDict) - if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) + if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) { if (CFGetTypeID(csflags) == CFNumberGetTypeID()) { cdFlags = cfNumber(CFNumberRef(csflags)); secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags); @@ -121,6 +122,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags); } else MacOSError::throwMe(errSecCSBadDictionaryFormat); + } } if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested... cdFlags |= kSecCodeSignatureAdhoc; // ... so note that @@ -167,6 +169,9 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) } pagesize = state.mPageSize ? cfNumber(state.mPageSize) : rep->pageSize(state); + + // Timestamping setup + CFRef mTSAuth; // identity for client-side authentication to the Timestamp server } @@ -176,7 +181,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) // treat them as architectural binaries containing (only) one architecture - that // interpretation is courtesy of the Universal/MachO support classes. // -void SecCodeSigner::Signer::signMachO(Universal *fat) +void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context) { // Mach-O executable at the core - perform multi-architecture signing auto_ptr editor(state.mDetached @@ -190,7 +195,7 @@ void SecCodeSigner::Signer::signMachO(Universal *fat) for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { MachOEditor::Arch &arch = *it->second; arch.source.reset(fat->architecture(it->first)); - arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture, state)); + arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture, state), context); if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch populate(arch); populate(arch.cdbuilder, arch, arch.ireqs, @@ -239,14 +244,14 @@ void SecCodeSigner::Signer::signMachO(Universal *fat) // Sign a binary that has no notion of architecture. // That currently means anything that isn't Mach-O format. // -void SecCodeSigner::Signer::signArchitectureAgnostic() +void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context) { // non-Mach-O executable - single-instance signing RefPointer writer = state.mDetached ? (new DetachedBlobWriter(*this)) : rep->writer(); CodeDirectory::Builder builder(state.mDigestAlgorithm); InternalRequirements ireqs; - ireqs(state.mRequirements, rep->defaultRequirements(NULL, state)); + ireqs(state.mRequirements, rep->defaultRequirements(NULL, state), context); populate(*writer); populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit()); @@ -291,7 +296,7 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W builder.flags(cdFlags); builder.identifier(identifier); - if (CFDataRef data = rep->component(cdInfoSlot)) + if (CFRef data = rep->component(cdInfoSlot)) builder.specialSlot(cdInfoSlot, data); if (ireqs) { CFRef data = makeCFData(*ireqs); @@ -312,6 +317,7 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W writer.addDiscretionary(builder); } +#include // // Generate the CMS signature for a (finished) CodeDirectory. @@ -319,7 +325,8 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) { assert(state.mSigner); - + CFRef defaultTSContext = NULL; + // a null signer generates a null signature blob if (state.mSigner == SecIdentityRef(kCFNull)) return CFDataCreate(NULL, NULL, 0); @@ -337,8 +344,29 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) } MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length())); + + // Set up to call Timestamp server if requested + + if (state.mWantTimeStamp) + { + CFRef error = NULL; + defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref()); + if (error) + MacOSError::throwMe(errSecDataNotAvailable); + + if (state.mNoTimeStampCerts || state.mTimestampService) { + if (state.mTimestampService) + CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService); + if (state.mNoTimeStampCerts) + CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue); + } + + CmsMessageSetTSAContext(cms, defaultTSContext); + } + CFDataRef signature; MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature)); + return signature; } diff --git a/lib/signer.h b/lib/signer.h index 9bef127..101f222 100644 --- a/lib/signer.h +++ b/lib/signer.h @@ -55,11 +55,13 @@ public: CodeDirectory::HashAlgorithm digestAlgorithm() const { return state.mDigestAlgorithm; } std::string path() const { return cfString(rep->canonicalPath()); } + SecIdentityRef signingIdentity() const { return state.mSigner; } + std::string signingIdentifier() const { return identifier; } protected: void prepare(SecCSFlags flags); // set up signing parameters - void signMachO(Universal *fat); // sign a Mach-O binary - void signArchitectureAgnostic(); // sign anything else + void signMachO(Universal *fat, const Requirement::Context &context); // sign a Mach-O binary + void signArchitectureAgnostic(const Requirement::Context &context); // sign anything else void populate(DiskRep::Writer &writer); // global void populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer, diff --git a/lib/signerutils.cpp b/lib/signerutils.cpp index f1264f5..d03d884 100644 --- a/lib/signerutils.cpp +++ b/lib/signerutils.cpp @@ -31,6 +31,7 @@ #include #include "renum.h" #include "csutilities.h" +#include "drmaker.h" #include #include #include @@ -306,5 +307,57 @@ void MachOEditor::commit() } +// +// InternalRequirements +// +void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context) +{ + // first add the default internal requirements + if (defaulted) { + this->add(defaulted); + ::free((void *)defaulted); // was malloc(3)ed by DiskRep + } + + // now override them with any requirements explicitly given by the signer + if (given) + this->add(given); + + // now add the Designated Requirement, if we can make it and it's not been provided + if (!this->contains(kSecDesignatedRequirementType)) { + DRMaker maker(context); + if (Requirement *dr = maker.make()) { + this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr + } + } + + // return the result + mReqs = this->make(); +} + + +// +// Pre-Signing contexts +// +PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer) +{ + // construct a cert chain + if (signer.signingIdentity() != SecIdentityRef(kCFNull)) { + CFRef signingCert; + MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref())); + CFRef policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning); + CFRef trust; + MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref())); + SecTrustResultType result; + MacOSError::check(SecTrustEvaluate(trust, &result)); + CSSM_TP_APPLE_EVIDENCE_INFO *info; + MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info)); + this->certs = mCerts; + } + + // other stuff + this->identifier = signer.signingIdentifier(); +} + + } // end namespace CodeSigning } // end namespace Security diff --git a/lib/signerutils.h b/lib/signerutils.h index 92d48f3..3fdb081 100644 --- a/lib/signerutils.h +++ b/lib/signerutils.h @@ -39,6 +39,21 @@ namespace Security { namespace CodeSigning { +// +// A helper to deal with the magic merger logic of internal requirements +// +class InternalRequirements : public Requirements::Maker { +public: + InternalRequirements() : mReqs(NULL) { } + ~InternalRequirements() { ::free((void *)mReqs); } + void operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context); + operator const Requirements * () const { return mReqs; } + +private: + const Requirements *mReqs; +}; + + // // A DiskRep::Writer that assembles data in a SuperBlob (in memory) // @@ -165,6 +180,21 @@ private: }; +// +// A Requirement::Context populated from a signing request. +// We use this to help generate the explicit Designated Requirement +// during signing ops, and thus this must be constructed BEFORE we +// actually have a signed object. +// +class PreSigningContext : public Requirement::Context { +public: + PreSigningContext(const SecCodeSigner::Signer &signer); + +private: + CFRef mCerts; // hold cert chain +}; + + } // end namespace CodeSigning } // end namespace Security diff --git a/lib/syspolicy.sql b/lib/syspolicy.sql index 9448908..9e3eece 100644 --- a/lib/syspolicy.sql +++ b/lib/syspolicy.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2011 Apple Inc. All Rights Reserved. +-- Copyright (c) 2011-2012 Apple Inc. All Rights Reserved. -- -- @APPLE_LICENSE_HEADER_START@ -- @@ -39,10 +39,10 @@ PRAGMA recursive_triggers = true; -- The feature table hold configuration features and options -- CREATE TABLE feature ( - id INTEGER PRIMARY KEY, -- canononical + id INTEGER PRIMARY KEY, -- canononical name TEXT NOT NULL UNIQUE, -- name of option value TEXT NULL, -- value of option, if any - remarks TEXT NOT NULL -- optional remarks string + remarks TEXT NULL -- optional remarks string ); @@ -63,9 +63,9 @@ CREATE TABLE authority ( disabled INTEGER NOT NULL DEFAULT (0) -- disable count (stacks; enabled if zero) CHECK (disabled >= 0), expires FLOAT NOT NULL DEFAULT (5000000), -- expiration of rule authority (Julian date) - priority REAL NOT NULL DEFAULT (0), -- rule priority (full float) + priority REAL NOT NULL DEFAULT (0), -- rule priority (full float) label TEXT NULL, -- text label for authority rule - flags INTEGER NOT NULL DEFAULT (0), -- amalgamated binary flags + flags INTEGER NOT NULL DEFAULT (0), -- amalgamated binary flags -- following fields are for documentation only ctime FLOAT NOT NULL DEFAULT (JULIANDAY('now')), -- rule creation time (Julian) mtime FLOAT NOT NULL DEFAULT (JULIANDAY('now')), -- time rule was last changed (Julian) @@ -95,6 +95,27 @@ SELECT * from authority WHERE JULIANDAY('now') < expires AND (flags & 1) = 0; +-- +-- A table to carry (potentially large-ish) filesystem data stored as a bookmark blob. +-- +CREATE TABLE bookmarkhints ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + bookmark BLOB NOT NULL, + authority INTEGER NOT NULL + REFERENCES authority(id) ON DELETE CASCADE +); + + +-- +-- Upgradable features already contained in this baseline. +-- See policydatabase.cpp for upgrade code. +-- +INSERT INTO feature (name, value, remarks) + VALUES ('bookmarkhints', 'value', 'builtin'); +INSERT INTO feature (name, value, remarks) + VALUES ('codesignedpackages', 'value', 'builtin'); + + -- -- Initial canonical contents of a fresh database -- @@ -119,7 +140,7 @@ insert into authority (type, allow, flags, label, requirement) insert into authority (type, allow, flags, label, requirement) values (1, 1, 2, 'Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists'); insert into authority (type, allow, flags, label, requirement) - values (2, 1, 2, 'Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.14] exists'); + values (2, 1, 2, 'Developer ID', 'anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and (certificate leaf[field.1.2.840.113635.100.6.1.14] or certificate leaf[field.1.2.840.113635.100.6.1.13])'); -- diff --git a/lib/xar++.cpp b/lib/xar++.cpp index 42b49a4..30e9665 100644 --- a/lib/xar++.cpp +++ b/lib/xar++.cpp @@ -36,15 +36,28 @@ namespace CodeSigning { Xar::Xar(const char *path) { mXar = 0; - mSig = 0; + mSigCMS = 0; + mSigClassic = 0; if (path) open(path); } void Xar::open(const char *path) { - if ((mXar = ::xar_open(path, READ))) - mSig = xar_signature_first(mXar); + if ((mXar = ::xar_open(path, READ)) == NULL) + return; + + xar_signature_t sig = ::xar_signature_first(mXar); + // read signatures until we find a CMS signature + while (sig && mSigCMS == NULL) { + const char *type = ::xar_signature_type(sig); + if (strcmp(type, "CMS") == 0) { + mSigCMS = sig; + } else if (strcmp(type, "RSA") == 0) { + mSigClassic = sig; + } + sig = ::xar_signature_next(sig); + } } Xar::~Xar() @@ -53,17 +66,14 @@ Xar::~Xar() ::xar_close(mXar); } - -CFArrayRef Xar::copyCertChain() +static CFArrayRef copyCertChainFromSignature(xar_signature_t sig) { - if (!mSig) - return NULL; - unsigned count = xar_signature_get_x509certificate_count(mSig); + unsigned count = xar_signature_get_x509certificate_count(sig); CFRef certs = makeCFMutableArray(0); for (unsigned ix = 0; ix < count; ix++) { const uint8_t *data; uint32_t length; - if (xar_signature_get_x509certificate_data(mSig, ix, &data, &length) == 0) { + if (xar_signature_get_x509certificate_data(sig, ix, &data, &length) == 0) { CFTempData cdata(data, length); CFRef cert = SecCertificateCreateWithData(NULL, cdata); CFArrayAppendValue(certs, cert.get()); @@ -72,6 +82,15 @@ CFArrayRef Xar::copyCertChain() return certs.yield(); } +CFArrayRef Xar::copyCertChain() +{ + if (mSigCMS) + return copyCertChainFromSignature(mSigCMS); + else if (mSigClassic) + return copyCertChainFromSignature(mSigClassic); + return NULL; +} + } // end namespace CodeSigning } // end namespace Security diff --git a/lib/xar++.h b/lib/xar++.h index fa28777..11f4f7e 100644 --- a/lib/xar++.h +++ b/lib/xar++.h @@ -48,13 +48,14 @@ public: void open(const char *path); operator bool() const { return mXar != 0; } - bool isSigned() const { return mSig != 0; } + bool isSigned() const { return mSigClassic != 0 || mSigCMS != 0; } CFArrayRef copyCertChain(); private: xar_t mXar; - xar_signature_t mSig; + xar_signature_t mSigClassic; + xar_signature_t mSigCMS; }; diff --git a/lib/xpcengine.cpp b/lib/xpcengine.cpp index 64fbcf9..30a25a1 100644 --- a/lib/xpcengine.cpp +++ b/lib/xpcengine.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace Security { @@ -135,7 +135,7 @@ void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutab } -bool xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context) +CFDictionaryRef xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context) { Message msg("update"); // target can be NULL, a CFURLRef, a SecRequirementRef, or a CFNumberRef @@ -173,7 +173,9 @@ bool xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context) if (int64_t error = xpc_dictionary_get_int64(msg, "error")) MacOSError::throwMe(error); - return true; + size_t resultLength; + const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength); + return makeCFDictionaryFrom(resultData, resultLength); } diff --git a/lib/xpcengine.h b/lib/xpcengine.h index 8b2f115..e2edd01 100644 --- a/lib/xpcengine.h +++ b/lib/xpcengine.h @@ -33,11 +33,11 @@ namespace CodeSigning { void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutableDictionaryRef result); -bool xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context); +CFDictionaryRef xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context) + CF_RETURNS_RETAINED; bool xpcEngineControl(const char *name); - } // end namespace CodeSigning } // end namespace Security diff --git a/libsecurity_codesigning.xcodeproj/project.pbxproj b/libsecurity_codesigning.xcodeproj/project.pbxproj index 8980156..84f108c 100644 --- a/libsecurity_codesigning.xcodeproj/project.pbxproj +++ b/libsecurity_codesigning.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ C26AC0F0143BCF18001C98CE /* ShellScript */, C26AC0F4143BD1C4001C98CE /* CopyFiles */, C2F24DFE14BCBBF200309FCD /* ShellScript */, + C24826BD159911A20082B10B /* CopyFiles */, ); dependencies = ( ); @@ -71,6 +72,12 @@ C22463610B86210100626F1B /* antlrplugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2CC31130B85254F005FA59D /* antlrplugin.cpp */; }; C236E3D70AD59446000F5140 /* signer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D50AD59446000F5140 /* signer.cpp */; }; C236E3DB0AD595C2000F5140 /* signerutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D90AD595C2000F5140 /* signerutils.cpp */; }; + C24826C6159911F40082B10B /* gkclear in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826BF159911CF0082B10B /* gkclear */; }; + C24826C7159911F40082B10B /* gkgenerate in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826C0159911CF0082B10B /* gkgenerate */; }; + C24826C8159911F40082B10B /* gkhandmake in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826C1159911CF0082B10B /* gkhandmake */; }; + C24826C9159911F40082B10B /* gklist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826C2159911CF0082B10B /* gklist */; }; + C24826CA159911F40082B10B /* gkmerge in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826C3159911CF0082B10B /* gkmerge */; }; + C24826CB159911F40082B10B /* gkrecord in CopyFiles */ = {isa = PBXBuildFile; fileRef = C24826C4159911CF0082B10B /* gkrecord */; }; C24EABAB1421432800C16AA9 /* policydb.h in Headers */ = {isa = PBXBuildFile; fileRef = C24EABAA1421432800C16AA9 /* policydb.h */; }; C24EABAD1421433700C16AA9 /* policydb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C24EABAC1421433700C16AA9 /* policydb.cpp */; }; C250F6C30B5EF1910076098F /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; }; @@ -189,6 +196,8 @@ C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4439914C626D4000A01E6 /* quarantine++.h */; }; C2F6566E0BCBFB250078779E /* cserror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F6566C0BCBFB250078779E /* cserror.cpp */; }; C2F656930BCBFFF40078779E /* cserror.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F6566D0BCBFB250078779E /* cserror.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB5B6856156E4FEE0067635E /* drmaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EB5B684E156E492D0067635E /* drmaker.cpp */; }; + EB5B6857156E4FEE0067635E /* drmaker.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5B684F156E492D0067635E /* drmaker.h */; }; FEB30C9310DAC89D00557BA2 /* SecTask.c in Sources */ = {isa = PBXBuildFile; fileRef = FEB30C9210DAC89D00557BA2 /* SecTask.c */; }; FEB30C9E10DAC8FD00557BA2 /* SecTask.h in Headers */ = {isa = PBXBuildFile; fileRef = FEB30C9410DAC8A500557BA2 /* SecTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; FEB30CA310DAC91800557BA2 /* SecTask.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FEB30C9410DAC8A500557BA2 /* SecTask.h */; }; @@ -284,6 +293,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C24826BD159911A20082B10B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/bin; + dstSubfolderSpec = 0; + files = ( + C24826C6159911F40082B10B /* gkclear in CopyFiles */, + C24826C7159911F40082B10B /* gkgenerate in CopyFiles */, + C24826C8159911F40082B10B /* gkhandmake in CopyFiles */, + C24826C9159911F40082B10B /* gklist in CopyFiles */, + C24826CA159911F40082B10B /* gkmerge in CopyFiles */, + C24826CB159911F40082B10B /* gkrecord in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; C26AC0F4143BD1C4001C98CE /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -331,6 +355,12 @@ C236E3D60AD59446000F5140 /* signer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signer.h; sourceTree = ""; }; C236E3D90AD595C2000F5140 /* signerutils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signerutils.cpp; sourceTree = ""; }; C236E3DA0AD595C2000F5140 /* signerutils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signerutils.h; sourceTree = ""; }; + C24826BF159911CF0082B10B /* gkclear */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gkclear; path = gke/gkclear; sourceTree = SOURCE_ROOT; }; + C24826C0159911CF0082B10B /* gkgenerate */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gkgenerate; path = gke/gkgenerate; sourceTree = SOURCE_ROOT; }; + C24826C1159911CF0082B10B /* gkhandmake */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gkhandmake; path = gke/gkhandmake; sourceTree = SOURCE_ROOT; }; + C24826C2159911CF0082B10B /* gklist */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gklist; path = gke/gklist; sourceTree = SOURCE_ROOT; }; + C24826C3159911CF0082B10B /* gkmerge */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gkmerge; path = gke/gkmerge; sourceTree = SOURCE_ROOT; }; + C24826C4159911CF0082B10B /* gkrecord */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = gkrecord; path = gke/gkrecord; sourceTree = SOURCE_ROOT; }; C24EABAA1421432800C16AA9 /* policydb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = policydb.h; sourceTree = ""; }; C24EABAC1421433700C16AA9 /* policydb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = policydb.cpp; sourceTree = ""; }; C250F6C20B5EF1910076098F /* SecIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrity.h; sourceTree = ""; }; @@ -437,6 +467,8 @@ C2F6071B107D575700A83618 /* codesign-watch.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "codesign-watch.d"; path = "dtrace/codesign-watch.d"; sourceTree = SOURCE_ROOT; }; C2F6566C0BCBFB250078779E /* cserror.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cserror.cpp; sourceTree = ""; }; C2F6566D0BCBFB250078779E /* cserror.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cserror.h; sourceTree = ""; }; + EB5B684E156E492D0067635E /* drmaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drmaker.cpp; sourceTree = ""; }; + EB5B684F156E492D0067635E /* drmaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = drmaker.h; sourceTree = ""; }; FEB30C9210DAC89D00557BA2 /* SecTask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SecTask.c; sourceTree = ""; }; FEB30C9410DAC8A500557BA2 /* SecTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecTask.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -483,6 +515,7 @@ C2CC31160B852554005FA59D /* Security Plugins */, FEB30C9110DAC6C400557BA2 /* Entitlements */, C24EABA914213FAF00C16AA9 /* System Policy */, + C24826C5159911D70082B10B /* GKE */, ); path = lib; sourceTree = ""; @@ -520,6 +553,19 @@ name = "Signing Operations"; sourceTree = ""; }; + C24826C5159911D70082B10B /* GKE */ = { + isa = PBXGroup; + children = ( + C24826BF159911CF0082B10B /* gkclear */, + C24826C0159911CF0082B10B /* gkgenerate */, + C24826C1159911CF0082B10B /* gkhandmake */, + C24826C2159911CF0082B10B /* gklist */, + C24826C3159911CF0082B10B /* gkmerge */, + C24826C4159911CF0082B10B /* gkrecord */, + ); + name = GKE; + sourceTree = ""; + }; C24EABA914213FAF00C16AA9 /* System Policy */ = { isa = PBXGroup; children = ( @@ -634,6 +680,8 @@ C2C1DF620A2E45B600D1B02B /* Requirements */ = { isa = PBXGroup; children = ( + EB5B684E156E492D0067635E /* drmaker.cpp */, + EB5B684F156E492D0067635E /* drmaker.h */, C2C1DF8F0A2E4A2700D1B02B /* requirements.grammar */, C2D383360A237F47005C63A2 /* requirement.h */, C2D383350A237F47005C63A2 /* requirement.cpp */, @@ -819,6 +867,7 @@ C2A436160F2133B2007A41A6 /* slcrep.h in Headers */, C24EABAB1421432800C16AA9 /* policydb.h in Headers */, C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */, + EB5B6857156E4FEE0067635E /* drmaker.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1116,6 +1165,7 @@ C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */, C2DC2DCB145F5CD000AD2A3A /* policyengine.cpp in Sources */, C2F4439A14C626D4000A01E6 /* quarantine++.cpp in Sources */, + EB5B6856156E4FEE0067635E /* drmaker.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1224,7 +1274,7 @@ isa = XCBuildConfiguration; buildSettings = { BUILD_VARIANTS = debug; - CURRENT_PROJECT_VERSION = 55037.4; + CURRENT_PROJECT_VERSION = 55037.15; EXECUTABLE_PREFIX = ""; EXECUTABLE_SUFFIX = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -1239,6 +1289,8 @@ "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", "$(TEMPDIR)", "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + /usr/local/SecurityPieces/Headers, + /usr/local/SecurityPieces/PrivateHeaders, ); LIBRARY_STYLE = "\U0001STATIC"; OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)"; @@ -1277,7 +1329,7 @@ normal, debug, ); - CURRENT_PROJECT_VERSION = 55037.4; + CURRENT_PROJECT_VERSION = 55037.15; EXECUTABLE_PREFIX = ""; EXECUTABLE_SUFFIX = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -1290,6 +1342,8 @@ "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", "$(TEMPDIR)", "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + /usr/local/SecurityPieces/Headers, + /usr/local/SecurityPieces/PrivateHeaders, ); LIBRARY_STYLE = STATIC; OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)"; @@ -1328,7 +1382,7 @@ normal, debug, ); - CURRENT_PROJECT_VERSION = 55037.4; + CURRENT_PROJECT_VERSION = 55037.15; EXECUTABLE_PREFIX = ""; EXECUTABLE_SUFFIX = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -1341,6 +1395,8 @@ "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", "$(TEMPDIR)", "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + /usr/local/SecurityPieces/Headers, + /usr/local/SecurityPieces/PrivateHeaders, ); LIBRARY_STYLE = STATIC; OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)";