]> git.saurik.com Git - apple/libsecurity_codesigning.git/commitdiff
libsecurity_codesigning-55037.15.tar.gz master mac-os-x-1075 v55037.15
authorApple <opensource@apple.com>
Wed, 25 Jul 2012 22:22:25 +0000 (22:22 +0000)
committerApple <opensource@apple.com>
Wed, 25 Jul 2012 22:22:25 +0000 (22:22 +0000)
59 files changed:
dtrace/sp-watch.d [new file with mode: 0755]
gke/gkclear [new file with mode: 0755]
gke/gkgenerate [new file with mode: 0755]
gke/gkhandmake [new file with mode: 0755]
gke/gklist [new file with mode: 0755]
gke/gkmerge [new file with mode: 0755]
gke/gkrecord [new file with mode: 0755]
lib/CSCommon.h
lib/CSCommonPriv.h
lib/CodeSigner.cpp
lib/CodeSigner.h
lib/RequirementKeywords.h [new file with mode: 0644]
lib/RequirementLexer.cpp [new file with mode: 0644]
lib/RequirementLexer.hpp [new file with mode: 0644]
lib/RequirementParser.cpp [new file with mode: 0644]
lib/RequirementParser.hpp [new file with mode: 0644]
lib/RequirementParserTokenTypes.hpp [new file with mode: 0644]
lib/RequirementParserTokenTypes.txt [new file with mode: 0644]
lib/SecAssessment.cpp
lib/SecAssessment.h
lib/SecCode.cpp
lib/SecCode.h
lib/SecCodeSigner.cpp
lib/SecCodeSigner.h
lib/SecRequirement.cpp
lib/SecRequirementPriv.h
lib/StaticCode.cpp
lib/StaticCode.h
lib/bundlediskrep.cpp
lib/bundlediskrep.h
lib/cfmdiskrep.cpp
lib/codedirectory.cpp
lib/csutilities.cpp
lib/csutilities.h
lib/detachedrep.cpp
lib/diskrep.h
lib/drmaker.cpp [new file with mode: 0644]
lib/drmaker.h [new file with mode: 0644]
lib/machorep.cpp
lib/policydb.cpp
lib/policydb.h
lib/policyengine.cpp
lib/policyengine.h
lib/reqinterp.cpp
lib/requirement.cpp
lib/requirement.h
lib/security_codesigning.d
lib/security_codesigning.exp
lib/sigblob.h
lib/signer.cpp
lib/signer.h
lib/signerutils.cpp
lib/signerutils.h
lib/syspolicy.sql
lib/xar++.cpp
lib/xar++.h
lib/xpcengine.cpp
lib/xpcengine.h
libsecurity_codesigning.xcodeproj/project.pbxproj

diff --git a/dtrace/sp-watch.d b/dtrace/sp-watch.d
new file mode 100755 (executable)
index 0000000..194d572
--- /dev/null
@@ -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 (executable)
index 0000000..e7fab61
--- /dev/null
@@ -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 (executable)
index 0000000..b62383f
--- /dev/null
@@ -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 (executable)
index 0000000..35f63fd
--- /dev/null
@@ -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 (executable)
index 0000000..ed00e55
--- /dev/null
@@ -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 (executable)
index 0000000..bef6c2a
--- /dev/null
@@ -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 (executable)
index 0000000..5261688
--- /dev/null
@@ -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)
index 3c87b6213cadc8b2cbecb1a0c4b4ad61b9ca44a3..883d57bc5d1f9b2ca3f13f6b6e96e561325298fe 100644 (file)
@@ -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 */
        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 */
 };
 
 
 };
 
 
index e3413624d3696b80fee92a7204b91e0eb2870475..f87f4541d9d746120b81f9284133a18f44d83d60 100644 (file)
@@ -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.
  */
        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 */
 
 enum {
        kSecCodeMagicRequirement = 0xfade0c00,          /* single requirement */
index 361d8fc342243ec21bfdf6a3834308316b2d7077..e40620b921d5f5c10e45f45ab8809f57159e4e17 100644 (file)
 #include "reqparser.h"
 #include "renum.h"
 #include "csdatabase.h"
 #include "reqparser.h"
 #include "renum.h"
 #include "csdatabase.h"
+#include "drmaker.h"
+#include "csutilities.h"
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
+#include <Security/SecCertificate.h>
 #include <vector>
 
 namespace Security {
 #include <vector>
 
 namespace Security {
@@ -171,10 +174,9 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
        : CFDictionary(parameters, errSecCSBadDictionaryFormat)
 {
        // the signer may be an identity or null
        : CFDictionary(parameters, errSecCSBadDictionaryFormat)
 {
        // the signer may be an identity or null
-       if (CFTypeRef signer = get<CFTypeRef>(kSecCodeSignerIdentity))
-               if (CFGetTypeID(signer) == SecIdentityGetTypeID() || signer == kCFNull)
-                       state.mSigner = SecIdentityRef(signer);
-               else
+       state.mSigner = SecIdentityRef(get<CFTypeRef>(kSecCodeSignerIdentity));
+       if (state.mSigner)
+               if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull))
                        MacOSError::throwMe(errSecCSInvalidObjectRef);
 
        // the flags need some augmentation
                        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<CFNumberRef>(kSecCodeSignerDigestAlgorithm))
        
        // digest algorithms are specified as a numeric code
        if (CFNumberRef digestAlgorithm = get<CFNumberRef>(kSecCodeSignerDigestAlgorithm))
-               state.mDigestAlgorithm = cfNumber<long>(digestAlgorithm);
+               state.mDigestAlgorithm = cfNumber<unsigned int>(digestAlgorithm);
 
        if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
                state.mCMSSize = cfNumber<size_t>(cmsSize);
        else
 
        if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
                state.mCMSSize = cfNumber<size_t>(cmsSize);
        else
-               state.mCMSSize = 5000;  // likely big enough
+               state.mCMSSize = 9000;  // likely big enough
 
        // signing time can be a CFDateRef or null
 
        // signing time can be a CFDateRef or null
-       if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime))
+       if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime)) {
                if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
                        state.mSigningTime = CFDateRef(time);
                else
                        MacOSError::throwMe(errSecCSInvalidObjectRef);
                if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
                        state.mSigningTime = CFDateRef(time);
                else
                        MacOSError::throwMe(errSecCSInvalidObjectRef);
+       }
        
        if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
                state.mIdentifier = cfString(ident);
        
        if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
                state.mIdentifier = cfString(ident);
@@ -223,7 +226,7 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
        state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize);
        
        // detached can be (destination) file URL or (mutable) Data to be appended-to
        state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize);
        
        // detached can be (destination) file URL or (mutable) Data to be appended-to
-       if (state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached)) {
+       if ((state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached))) {
                CFTypeID type = CFGetTypeID(state.mDetached);
                if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID())
                        MacOSError::throwMe(errSecCSInvalidObjectRef);
                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<CFDataRef>(kSecCodeSignerEntitlements);
        
        state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot);
        state.mEntitlementData = get<CFDataRef>(kSecCodeSignerEntitlements);
        
        state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot);
+    
+       if (CFBooleanRef timestampRequest = get<CFBooleanRef>(kSecCodeSignerRequireTimestamp)) {
+               state.mWantTimeStamp = timestampRequest == kCFBooleanTrue;
+       } else {        // pick default
+               state.mWantTimeStamp = false;
+               if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) {
+                       CFRef<SecCertificateRef> signerCert;
+                       MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref()));
+                       if (certificateHasField(signerCert, devIdLeafMarkerOID))
+                               state.mWantTimeStamp = true;
+               }
+       }
+       state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication);
+       state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer);
+       state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates);
 }
 
 
 }
 
 
index f3e8b8f60ec93e62a0fce12f8ae3f63c55ca19ee..71f9cc3baf33a1a4b60cab2f4c5d652f2bb11186 100644 (file)
@@ -85,6 +85,10 @@ private:
        bool mNoMachO;                                  // override to perform non-Mach-O signing
        bool mDryRun;                                   // dry run (do not change target)
        CFRef<CFNumberRef> mPageSize;   // main executable page size
        bool mNoMachO;                                  // override to perform non-Mach-O signing
        bool mDryRun;                                   // dry run (do not change target)
        CFRef<CFNumberRef> mPageSize;   // main executable page size
+       CFRef<SecIdentityRef> mTimestampAuthentication; // identity for client-side authentication to the Timestamp server
+       CFRef<CFURLRef> 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 (file)
index 0000000..bd0438e
--- /dev/null
@@ -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 (file)
index 0000000..ac8b2bd
--- /dev/null
@@ -0,0 +1,1268 @@
+/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementLexer.cpp"$ */
+#include "RequirementLexer.hpp"
+#include <antlr/CharBuffer.hpp>
+#include <antlr/TokenStreamException.hpp>
+#include <antlr/TokenStreamIOException.hpp>
+#include <antlr/TokenStreamRecognitionException.hpp>
+#include <antlr/CharStreamException.hpp>
+#include <antlr/CharStreamIOException.hpp>
+#include <antlr/NoViableAltForCharException.hpp>
+
+
+#include "requirement.h"
+#include "reqmaker.h"
+#include "csutilities.h"
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+#include <security_cdsa_utilities/cssmdata.h>  // 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 (file)
index 0000000..ed5758f
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef INC_RequirementLexer_hpp_
+#define INC_RequirementLexer_hpp_
+
+#include <antlr/config.hpp>
+/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementLexer.hpp"$ */
+#include <antlr/CommonToken.hpp>
+#include <antlr/InputBuffer.hpp>
+#include <antlr/BitSet.hpp>
+#include "RequirementParserTokenTypes.hpp"
+#include <antlr/CharScanner.hpp>
+
+#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 (file)
index 0000000..90995c9
--- /dev/null
@@ -0,0 +1,1303 @@
+/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementParser.cpp"$ */
+#include "RequirementParser.hpp"
+#include <antlr/NoViableAltException.hpp>
+#include <antlr/SemanticException.hpp>
+#include <antlr/ASTFactory.hpp>
+
+#include "requirement.h"
+#include "reqmaker.h"
+#include "csutilities.h"
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+#include <security_cdsa_utilities/cssmdata.h>  // 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<ExprOp>(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<ExprOp>(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<CFDataRef> 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 (file)
index 0000000..c93719e
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef INC_RequirementParser_hpp_
+#define INC_RequirementParser_hpp_
+
+#include <antlr/config.hpp>
+/* $ANTLR 2.7.7 (20120228): "requirements.grammar" -> "RequirementParser.hpp"$ */
+#include <antlr/TokenStream.hpp>
+#include <antlr/TokenBuffer.hpp>
+#include "RequirementParserTokenTypes.hpp"
+#include <antlr/LLkParser.hpp>
+
+
+#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 (file)
index 0000000..388c378
--- /dev/null
@@ -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 (file)
index 0000000..5bb5975
--- /dev/null
@@ -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
index fbecc413a049cf2edc5adee7e6d01fd99abf600e..714bba832c279b17c44a708246bfb6176fb4a5fe 100644 (file)
@@ -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 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,
 const CFStringRef kSecAssessmentContextKeyCertificates = CFSTR("context:certificates");        // obsolete
 
 SecAssessmentRef SecAssessmentCreate(CFURLRef path,
@@ -134,7 +136,6 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path,
        CFErrorRef *errors)
 {
        BEGIN_CSAPI
        CFErrorRef *errors)
 {
        BEGIN_CSAPI
-       SYSPOLICY_ASSESS_API(cfString(path).c_str(), flags);
        
        if (flags & kSecAssessmentFlagAsynchronous)
                MacOSError::throwMe(errSecCSUnimplemented);
        
        if (flags & kSecAssessmentFlagAsynchronous)
                MacOSError::throwMe(errSecCSUnimplemented);
@@ -142,6 +143,8 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path,
        AuthorityType type = typeFor(context, kAuthorityExecute);
        CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
 
        AuthorityType type = typeFor(context, kAuthorityExecute);
        CFRef<CFMutableDictionaryRef> 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))) {
        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";
                        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);
        }
        
        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<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority);
                        CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue);
                        if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) {
                                CFRef<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority);
-                               CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, CFSTR("security disabled"));
+                               CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
                                CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2);
                        } else {
                                CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2);
                        } else {
-                               cfadd(adulterated, "{%O={%O='security disabled'}}",
-                                       kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride);
+                               cfadd(adulterated, "{%O={%O=%O}}",
+                                       kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
                        }
                        result = adulterated.get();
                }
                        }
                        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 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");
 
 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 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)
 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
 
 {
        BEGIN_CSAPI
 
@@ -313,19 +347,15 @@ Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *e
        BEGIN_CSAPI
        
        if (CFEqual(control, CFSTR("ui-enable"))) {
        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"))) {
                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())
        } 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<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
                return true;
        } else if (CFEqual(control, CFSTR("ui-enable-devid"))) {
                CFTemp<CFDictionaryRef> 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<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
        } else if (CFEqual(control, CFSTR("ui-disable-devid"))) {
                CFTemp<CFDictionaryRef> 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<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true))
        } else if (CFEqual(control, CFSTR("ui-get-devid"))) {
                CFBooleanRef &result = *(CFBooleanRef*)(arguments);
                if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true))
index 9cae77543d25b23ecee66f71da7d51b7d6afc69e..b8883a6aa88a5dfa42a1fa8cec658a8e24224f72 100644 (file)
@@ -124,8 +124,6 @@ enum {
        @constant kSecAssessmentContextKeyOperation Type of operation (see overview above). This defaults
                to the kSecAssessmentOperationTypeExecute.
  */
        @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
 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 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)
 };
 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,
        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.
                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:
 
        
        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 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
        @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.
                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 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
 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 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,
 
 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).
        @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);
 
  */
 Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors);
 
index 779a7d309320ceb20046cbe9222a169a19c4e8b9..f5b300f6dc9ae15e815f5c125375ecfab6de198c 100644 (file)
@@ -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 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");
 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 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");
 
 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<SecCode> dcode = SecStaticCode::optionalDynamic(codeRef))
        
        if (flags & kSecCSDynamicInformation)
                if (SecPointer<SecCode> dcode = SecStaticCode::optionalDynamic(codeRef))
-                       info = cfmake<CFDictionaryRef>("{+%O,%O=%u}", info.get(),
-                               kSecCodeInfoStatus, dcode->status());
+                       info.take(cfmake<CFDictionaryRef>("{+%O,%O=%u}", info.get(), kSecCodeInfoStatus, dcode->status()));
        
        CodeSigning::Required(infoRef) = info.yield();
        
        
        CodeSigning::Required(infoRef) = info.yield();
        
index 0e5ca7c409410169d34edabcb62fc4aa09c35db0..4ae85d901082de00a678626205ef43c9b170f6e1 100644 (file)
@@ -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.
                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
        @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.
                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
        @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.
                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,
  */
 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 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 */
 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 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 kSecCodeInfoTrust;                    /* Signing */
 extern const CFStringRef kSecCodeInfoUnique;           /* generic */
+extern const CFStringRef kSecCodeSignerFlags;          /* dynamic */
 
 OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags,
        CFDictionaryRef *information);
 
 OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags,
        CFDictionaryRef *information);
index adb8346c73d2ef5086bb49dac6944587ab3493d5..d5e7438fb7657fcea710a0a326cbfc03151b9381 100644 (file)
@@ -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 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");
 
 
 //
 
 
 //
index 184dc663bec89e09b47ce20afbce14bcd240a13e..42ad08993d2bacf9c56fdb7adcfd532c15ce89f4 100644 (file)
@@ -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.
                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;
  */
 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 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;
 
 
 /*!
 
 
 /*!
index cee83106df7f26a773ee5e4baa5ec1ee33c606b9..68d95e45e8ab96b4be7777326cb08209d8e3f5ae 100644 (file)
@@ -162,6 +162,7 @@ OSStatus SecRequirementCopyString(SecRequirementRef requirementRef, SecCSFlags f
 //
 CFStringRef kSecRequirementKeyInfoPlist = CFSTR("requirement:eval:info");
 CFStringRef kSecRequirementKeyEntitlements = CFSTR("requirement:eval:entitlements");
 //
 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,
 
 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,
        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);
                NULL    // can't specify a CodeDirectory here
        );
        req->validate(ctx);
index b7e6062ee269ea089009c94bd99f8e1d222e5465..44055769cdd548eff8916d603218caefa8997240 100644 (file)
@@ -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.
        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 kSecRequirementKeyInfoPlist;
 extern CFStringRef kSecRequirementKeyEntitlements;
+extern CFStringRef kSecRequirementKeyIdentifier;
 
 OSStatus SecRequirementEvaluate(SecRequirementRef requirement,
        CFArrayRef certificateChain, CFDictionaryRef context,
 
 OSStatus SecRequirementEvaluate(SecRequirementRef requirement,
        CFArrayRef certificateChain, CFDictionaryRef context,
index 956c88f9d53a70872baee63c676a79a3bcc1d290..d86a30beaf0cc8013ff55586ca561bcb7db109f6 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -27,6 +27,7 @@
 #include "StaticCode.h"
 #include "Code.h"
 #include "reqmaker.h"
 #include "StaticCode.h"
 #include "Code.h"
 #include "reqmaker.h"
+#include "drmaker.h"
 #include "reqdumper.h"
 #include "sigblob.h"
 #include "resources.h"
 #include "reqdumper.h"
 #include "sigblob.h"
 #include "resources.h"
@@ -34,6 +35,7 @@
 #include "detachedrep.h"
 #include "csdatabase.h"
 #include "csutilities.h"
 #include "detachedrep.h"
 #include "csdatabase.h"
 #include "csutilities.h"
+#include "SecCode.h"
 #include <CoreFoundation/CFURLAccess.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
 #include <CoreFoundation/CFURLAccess.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
@@ -369,6 +371,12 @@ CFAbsoluteTime SecStaticCode::signingTime()
        return mSigningTime;
 }
 
        return mSigningTime;
 }
 
+CFAbsoluteTime SecStaticCode::signingTimestamp()
+{
+       validateDirectory();
+       return mSigningTimestamp;
+}
+
 
 //
 // Verify the CMS signature on the CodeDirectory.
 
 //
 // Verify the CMS signature on the CodeDirectory.
@@ -403,28 +411,29 @@ bool SecStaticCode::verifySignature()
        if (status != kCMSSignerValid)
                MacOSError::throwMe(errSecCSSignatureFailed);
        
        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
        // 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
        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:
                CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0);
                switch (trustResult) {
                case kSecTrustResultProceed:
-               case kSecTrustResultConfirm:
                case kSecTrustResultUnspecified:
                        break;                          // success
                case kSecTrustResultDeny:
                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 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));
                        {
                                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);
                        }
                }
                                MacOSError::throwMe(result);
                        }
                }
@@ -650,7 +659,7 @@ void SecStaticCode::validateResources()
                        throw;
                }
        }
                        throw;
                }
        }
-       assert(!validatedResources());
+       assert(validatedResources());
        if (mResourcesValidResult)
                MacOSError::throwMe(mResourcesValidResult);
        if (mResourcesValidContext->osStatus() != noErr)
        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<CollectingContext *>(context);
        ResourceSeal seal(value);
 {
        CollectingContext *ctx = static_cast<CollectingContext *>(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()));
                if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
                        ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing,
                                CFTempURL(CFStringRef(key), false, ctx->code.resourceBase()));
-               } else
+               } else {
                        ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
                        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()
 {
 //
 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()
 {
 //
 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)) {
        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);
                SHA1 hash;
                hash(codeDirectory(), codeDirectory()->length());
                SHA1::Digest digest;
                hash.finish(digest);
                maker.cdhash(digest);
+               return maker.make();
        } else {
        } 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<CFStringRef> 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] = <leaf's subject> and ...
-               maker.put(opAnd);
-               maker.put(opCertField);                 // certificate
-               maker.put(0);                                   // leaf
-               maker.put("subject.CN");                // [subject.CN]
-               maker.put(matchEqual);                  // =
-               maker.putData(leafCN);                  // <leaf CN>
-               // ... cert 1[field.<marker>] exists
-               maker.put(opCertGeneric);               // certificate
-               maker.put(1);                                   // 1
-               maker.putData(adcSdkMarkerOID.Data, adcSdkMarkerOID.Length); // [field.<marker>]
-               maker.put(matchExists);                 // exists
-               return;
-       }
-       
-       if (isAppleCaspianSignature()) {
-               // get the Organizational Unit DN element for the leaf (it contains the TEAMID)
-               CFRef<CFStringRef> 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] = <leaf's subject>
-               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<CFStringRef> 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<CFStringRef> 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();
 {
        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)
 }
 
 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()));
        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));
 
        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
        //
        //
        // kSecCSSigningInformation adds information about signing certificates and chains
        //
@@ -1152,6 +1039,9 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
                if (CFAbsoluteTime time = this->signingTime())
                        if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
                if (CFAbsoluteTime time = this->signingTime())
                        if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
+               if (CFAbsoluteTime time = this->signingTimestamp())
+                       if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
+                               CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
        }
        
        //
        }
        
        //
@@ -1174,8 +1064,11 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
                        CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
                }
                
                        CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
                }
                
-          if (CFDataRef ent = this->component(cdEntitlementSlot))
+          if (CFDataRef ent = this->component(cdEntitlementSlot)) {
                   CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
                   CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
+                  if (CFDictionaryRef entdict = this->entitlements())
+                               CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
+               }
        }
        
        //
        }
        
        //
index edb92a18b65dabdb919b088f201691fa6affce53..af255bad718a2231e3f2651188e3e49c325041dc 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -112,6 +112,7 @@ public:
        CFDataRef cdHash();
        CFDataRef signature();
        CFAbsoluteTime signingTime();
        CFDataRef cdHash();
        CFDataRef signature();
        CFAbsoluteTime signingTime();
+       CFAbsoluteTime signingTimestamp();
        bool isSigned() { return codeDirectory(false) != NULL; }
        DiskRep *diskRep() { return mRep; }
        std::string mainExecutablePath() { return mRep->mainExecutablePath(); }
        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; }
 
        bool validatedExecutable() const        { return mExecutableValidated; }
        bool validatedResources() const { return mResourcesValidated; }
 
-       
        void validateDirectory();
        void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
        void validateNonResourceComponents();
        void validateDirectory();
        void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
        void validateNonResourceComponents();
@@ -170,11 +170,6 @@ protected:
        bool verifySignature();
        CFTypeRef verificationPolicy(SecCSFlags flags);
 
        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:
        static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context);
 
 private:
@@ -198,6 +193,7 @@ private:
        CFRef<CFDataRef> mDir;                          // code directory data
        CFRef<CFDataRef> mSignature;            // CMS signature data
        CFAbsoluteTime mSigningTime;            // (signed) signing time
        CFRef<CFDataRef> mDir;                          // code directory data
        CFRef<CFDataRef> mSignature;            // CMS signature data
        CFAbsoluteTime mSigningTime;            // (signed) signing time
+       CFAbsoluteTime mSigningTimestamp;               // Timestamp time (from timestamping authority)
        CFRef<CFDataRef> mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present
        
        // alternative cache forms (storage may depend on cached contents above)
        CFRef<CFDataRef> mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present
        
        // alternative cache forms (storage may depend on cached contents above)
index 09d5ca22fed9725e52892ea99b57b642849e81cb..fa5a8574e58d47a8efe0ff32d93ff52d5d412c93 100644 (file)
@@ -26,6 +26,7 @@
 #include <CoreFoundation/CFBundlePriv.h>
 #include <security_utilities/cfmunge.h>
 #include <copyfile.h>
 #include <CoreFoundation/CFBundlePriv.h>
 #include <security_utilities/cfmunge.h>
 #include <copyfile.h>
+#include <fts.h>
 
 
 namespace Security {
 
 
 namespace Security {
@@ -34,6 +35,12 @@ namespace CodeSigning {
 using namespace UnixPlusPlus;
 
 
 using namespace UnixPlusPlus;
 
 
+//
+// Local helpers
+//
+static std::string findDistFile(const std::string &directory);
+
+
 //
 // We make a CFBundleRef immediately, but everything else is lazy
 //
 //
 // 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)
 {
 // common construction code
 void BundleDiskRep::setup(const Context *ctx)
 {
+       mInstallerPackage = false;      // default
+       
        // deal with versioned bundles (aka Frameworks)
        string version = resourcesRootPath()
                + "/Versions/"
        // 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);
        }
                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
        
        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<CFURLRef> 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);
                        MacOSError::throwMe(errSecCSBadBundleFormat);
-               mMainExecutableURL = makeCFURL(cfString(CFStringRef(main)), false, CFRef<CFURLRef>(CFBundleCopySupportFilesDirectoryURL(mBundle)));
+               mMainExecutableURL.take(makeCFURL(cfString(CFStringRef(mainHTML)), false,
+                       CFRef<CFURLRef>(CFBundleCopySupportFilesDirectoryURL(mBundle))));
                if (!mMainExecutableURL)
                        MacOSError::throwMe(errSecCSBadBundleFormat);
                mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
                if (!mMainExecutableURL)
                        MacOSError::throwMe(errSecCSBadBundleFormat);
                mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
@@ -94,18 +107,28 @@ void BundleDiskRep::setup(const Context *ctx)
                return;
        }
        
                return;
        }
        
-       // generic bundle case - impose our own "minimal signable bundle" standard
-
-       // we MUST have an actual Info.plist in here
-       CFRef<CFURLRef> infoURL = _CFBundleCopyInfoPlistURL(mBundle);
-       if (!infoURL)
-               MacOSError::throwMe(errSecCSBadBundleFormat);
-       
+       // do we have a real Info.plist here?
+       if (CFRef<CFURLRef> 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());
                mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
-               mFormat = "bundle";
+               mInstallerPackage = true;
+               mFormat = "installer package bundle";
                return;
        }
        
                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
 //
 // 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 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;
                mMetaPath = support + "/" BUNDLEDISKREP_DIRECTORY;
                if (::access(mMetaPath.c_str(), F_OK) == 0) {
                        mMetaExists = true;
@@ -209,7 +267,7 @@ string BundleDiskRep::mainExecutablePath()
 
 string BundleDiskRep::resourcesRootPath()
 {
 
 string BundleDiskRep::resourcesRootPath()
 {
-       return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true);
+       return cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle));
 }
 
 void BundleDiskRep::adjustResources(ResourceBuilder &builder)
 }
 
 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 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
        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<CFDictionaryRef>("{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<CFDictionaryRef>("{rules={"
        return cfmake<CFDictionaryRef>("{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(),
                "}}",
                (string("^") + resources).c_str(),
                (string("^") + resources + ".*\\.lproj/").c_str(),
index e96199326a51aac9c03867c57398a8a75809cad3..bef1861b0a2ac667c6ddde0d22593207bd39cf90 100644 (file)
@@ -90,7 +90,8 @@ private:
        CFRef<CFBundleRef> mBundle;
        std::string mMetaPath;                                  // path to directory containing signing files
        bool mMetaExists;                                               // separate meta-file directory exists
        CFRef<CFBundleRef> mBundle;
        std::string mMetaPath;                                  // path to directory containing signing files
        bool mMetaExists;                                               // separate meta-file directory exists
-       CFRef<CFURLRef> mMainExecutableURL;     // chosen main executable URL
+       CFRef<CFURLRef> mMainExecutableURL;             // chosen main executable URL
+       bool mInstallerPackage;                                 // is an installer (not executable) bundle
        string mFormat;                                                 // format description string
        RefPointer<DiskRep> mExecRep;                   // DiskRep for main executable file
 };
        string mFormat;                                                 // format description string
        RefPointer<DiskRep> mExecRep;                   // DiskRep for main executable file
 };
index 41607e12a425b67ee3d0d47c94542ba5a8a21da4..6ba10f26786556e5774af11ac337b3740d032fce 100644 (file)
@@ -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 (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());
                                        secdebug("cfmrep", "%zd signing bytes in %d blob(s) from %s(CFM)",
                                                mSigningData->length(), mSigningData->count(),
                                                mainExecutablePath().c_str());
index 9596e8f3f6578a296ce7747370e432d7c431d242..0bcb980192b6cc4a746533cdddd8c8094a1b220a 100644 (file)
@@ -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
        // 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)
                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)
index ed5a0a88ca4541f9763dfb03d4393edf03c24a8b..6ac5db8fe0a65c6bf642788ef66880fe05ab87a8 100644 (file)
@@ -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.
 //
 // 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;
 {
        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.
 //
 // 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);
 {
        bool matched = false;
        assert(cert);
index 456511316899038b8363e9b02ea1e8cd996f3e2f..10b33f8091175ef047c2ca3652273d501cfda184 100644 (file)
@@ -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.
 //
 // 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);
 
 
 //
 
 
 //
index ed59e5855ad51a7792949598c8487eacb3612c65..348a8ed055b02decc905d6274fd52413013b3658 100644 (file)
@@ -43,7 +43,7 @@ DetachedRep::DetachedRep(CFDataRef sig, DiskRep *orig, const std::string &source
 {
        const BlobCore *sigBlob = reinterpret_cast<const BlobCore *>(CFDataGetBytePtr(sig));
        if (sigBlob->is<EmbeddedSignatureBlob>()) {             // architecture-less
 {
        const BlobCore *sigBlob = reinterpret_cast<const BlobCore *>(CFDataGetBytePtr(sig));
        if (sigBlob->is<EmbeddedSignatureBlob>()) {             // 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;
                        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 (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;
                                                }
                                                        CODESIGN_DISKREP_CREATE_DETACHED(this, orig, (char*)source.c_str(), (void*)mGlobal);
                                                        return;
                                                }
index 5d9c2d302769b2f535e2fe5da4f10d09e1c2baa3..4a6beb5df13b9bce467fa27cde131ac8a1e6f95d 100644 (file)
@@ -194,10 +194,12 @@ public:
        std::string mainExecutablePath()                { return mOriginal->mainExecutablePath(); }
        CFURLRef canonicalPath()                                { return mOriginal->canonicalPath(); }
        std::string resourcesRootPath()                 { return mOriginal->resourcesRootPath(); }
        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(); }
        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(); }
        
        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 (file)
index 0000000..98f887d
--- /dev/null
@@ -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 <Security/oidsbase.h>
+#include <Security/SecCertificatePriv.h>
+//#include <Security/cssmapplePriv.h>
+
+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<CFStringRef> 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<CFStringRef> 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<CFStringRef> 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] = <leaf's subject> and ...
+               this->put(opAnd);
+               this->put(opCertField);                 // certificate
+               this->put(0);                                   // leaf
+               this->put("subject.CN");                // [subject.CN]
+               this->put(matchEqual);                  // =
+               this->putData(leafCN);                  // <leaf CN>
+               // ... cert 1[field.<marker>] exists
+               this->put(opCertGeneric);               // certificate
+               this->put(1);                                   // 1
+               this->putData(adcSdkMarkerOID.Data, adcSdkMarkerOID.Length); // [field.<marker>]
+               this->put(matchExists);                 // exists
+               return;
+       }
+       
+       if (isDeveloperIDSignature()) {
+               // get the Organizational Unit DN element for the leaf (it contains the TEAMID)
+               CFRef<CFStringRef> 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] = <leaf's subject>
+               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 (file)
index 0000000..704c361
--- /dev/null
@@ -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
index 8753ca609bfa2cc9db04201b0b3de66ea6598d28..180a400e8909635f17ce7256e326ce3fa3d09d6a 100644 (file)
@@ -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 (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());
                                        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()) {
 string MachORep::recommendedIdentifier(const SigningContext &ctx)
 {
        if (CFDataRef info = infoPlist()) {
-               if (CFDictionaryRef dict = makeCFDictionaryFrom(info)) {
+               if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) {
                        CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey));
                        if (code && CFGetTypeID(code) != CFStringGetTypeID())
                                MacOSError::throwMe(errSecCSBadDictionaryFormat);
                        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:
 
 //
 // 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.
 //
 // 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;
 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))
                
        // 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> macho(mainExecutableImage()->architecture(*arch));
        Requirement::Maker maker;
        Requirement::Maker::Chain chain(maker, opOr);
        auto_ptr<MachO> 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<DiskRep> rep = DiskRep::bestGuess(path))
-                                                       if (SecPointer<SecStaticCode> 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<SecRequirementRef> areq;
+                                                               MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref()));
+                                                               CFRef<CFDataRef> 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
        if (chain.empty())
                return NULL;
        else
index b748dea1af5bb164fa485336a288589bb131efc4..072d8ec7d40efd978c7a0bc48c77074e7004f76b 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include <Security/CodeSigning.h>
 #include <security_utilities/cfutilities.h>
 #include <security_utilities/cfmunge.h>
 #include <Security/CodeSigning.h>
 #include <security_utilities/cfutilities.h>
 #include <security_utilities/cfmunge.h>
+#include <security_utilities/blob.h>
+#include <security_utilities/logging.h>
+#include <security_utilities/simpleprefs.h>
+#include <security_utilities/logging.h>
+#include "csdatabase.h"
+
+#include <dispatch/dispatch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <notify.h>
 
 namespace Security {
 namespace CodeSigning {
 
 namespace Security {
 namespace CodeSigning {
@@ -34,13 +44,6 @@ namespace CodeSigning {
 using namespace SQLite;
 
 
 using namespace SQLite;
 
 
-//
-// The one and only PolicyDatabase object.
-// It auto-adapts to readonly vs. writable use.
-//
-ModuleNexus<PolicyDatabase> PolicyDatabase;
-
-
 //
 // Determine the database path
 //
 //
 // Determine the database path
 //
@@ -82,14 +85,35 @@ AuthorityType typeFor(CFDictionaryRef context, AuthorityType type /* = kAuthorit
        return mapEnum(context, kSecAssessmentContextKeyOperation, mapType, type);
 }
 
        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)
 //
 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()
 }
 
 PolicyDatabase::~PolicyDatabase()
@@ -167,18 +191,223 @@ void PolicyDatabase::purgeObjects(double priority)
        cleaner.execute();
 }
 
        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<CFDataRef> authData = cfLoadFile(authfile)) {
+                       CFDictionary auth(CFRef<CFDictionaryRef>(makeCFDictionaryFrom(authData)), errSecCSDbCorrupt);
+                       CFDictionaryRef content = auth.get<CFDictionaryRef>(CFSTR("authority"));
+                       std::string authUUID = cfString(auth.get<CFStringRef>(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, "<remote>");
+                                               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<CFStringRef>(CFSTR("type")));
+                               insert.bind(":path") = cfString(info.get<CFStringRef>(CFSTR("path")));
+                               insert.bind(":requirement") = "cdhash H\"" + cfString(info.get<CFStringRef>(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
 //
 
 //
 // Check the override-enable master flag
 //
+#define SP_ENABLE_KEY CFSTR("enabled")
+#define SP_ENABLED CFSTR("yes")
+#define SP_DISABLED CFSTR("no")
+
 bool overrideAssessment()
 {
 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);
 }
 
 
 }
 
 
index a112c16e3944c79d47b9c92af45911ae28db7f75..643b4fe4f57ef2546d703ba8ed537a825a893c5b 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -36,9 +36,25 @@ namespace SQLite = SQLite3;
 
 
 static const char defaultDatabase[] = "/var/db/SystemPolicy";
 
 
 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;
 
 
 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
        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);
 // 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();
 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();
 // Check the system-wide overriding flag file
 //
 bool overrideAssessment();
-
+void setAssessment(bool masterSwitch);
 
 } // end namespace CodeSigning
 } // end namespace Security
 
 } // end namespace CodeSigning
 } // end namespace Security
index 4ebe2a029d131170d8f1e3ed17fdf843f56b5cd2..3eb43d3bfbfbd48bdcd44f5f36300b922db39d4a 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
  */
 #include "policyengine.h"
 #include "xar++.h"
  */
 #include "policyengine.h"
 #include "xar++.h"
+#include "quarantine++.h"
+#include "codesigning_dtrace.h"
 #include <security_utilities/cfmunge.h>
 #include <Security/Security.h>
 #include <security_utilities/cfmunge.h>
 #include <Security/Security.h>
+#include <Security/SecCodePriv.h>
 #include <Security/SecRequirementPriv.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
 #include <Security/SecCodeSigner.h>
 #include <Security/cssmapplePriv.h>
 #include <Security/SecRequirementPriv.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
 #include <Security/SecCodeSigner.h>
 #include <Security/cssmapplePriv.h>
-#include <notify.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/unix++.h>
-#include "quarantine++.h"
+#include <notify.h>
 
 #include <CoreServices/CoreServicesPriv.h>
 
 #include <CoreServices/CoreServicesPriv.h>
+#include "SecCodePriv.h"
 #undef check // Macro! Yech.
 
 #undef check // Macro! Yech.
 
+extern "C" {
+#include <OpenScriptingUtilPriv.h>
+}
+
+
 namespace Security {
 namespace CodeSigning {
 
 static const double NEGATIVE_HOLD = 60.0/86400;        // 60 seconds to cache negative outcomes
 
 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<CFTypeRef> &target, CFDictionary &context, bool signUnsigned = false);
 static void normalizeTarget(CFRef<CFTypeRef> &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:
 {
        switch (type) {
        case kAuthorityExecute:
-               evaluateCode(path, flags, context, result);
+               evaluateCode(path, kAuthorityExecute, flags, context, result);
                break;
        case kAuthorityInstall:
                evaluateInstall(path, 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.
 //
 // 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<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
        
        CFRef<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
@@ -111,15 +130,57 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti
                
                CFRef<SecRequirementRef> requirement;
                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
                
                CFRef<SecRequirementRef> 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<CFDataRef> signature = CFDataCreateMutable(NULL, 0);
+                               CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached, signature.get(), kSecCodeSignerIdentity);
+                               CFRef<SecCodeSignerRef> 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<CFUUIDRef> 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<CFDictionaryRef> 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) {
 
                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");
                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<CFDictionaryRef> xinfo;
                                MacOSError::check(SecTrustCopyExtendedResult(trust, &xinfo.aref()));
                                if (CFDateRef limit = CFDateRef(CFDictionaryGetValue(xinfo, kSecTrustExpirationDate))) {
                                CFRef<CFDictionaryRef> 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
                cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
                addAuthority(result, label, id);
                return;
        }
        
        // no applicable authority. Deny by default
+       CFRef<CFDictionaryRef> info;
+       MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
        if (flags & kSecAssessmentFlagRequestOrigin) {
        if (flags & kSecAssessmentFlagRequestOrigin) {
-               CFRef<CFDictionaryRef> info;    // as needed
-               MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
                if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates)))
                        setOrigin(chain, result);
        }
                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);
        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.
 
 //
 // 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;
 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
        
        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<CFArrayRef> certs = xar.copyCertChain()) {
-                       CFRef<CFTypeRef> policy = installerPolicy();
-                       CFRef<SecTrustRef> 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<CFArrayRef> 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<CFArrayRef> certs = xar.copyCertChain()) {
+               CFRef<CFTypeRef> policy = installerPolicy();
+               CFRef<SecTrustRef> 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<CFArrayRef> 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<SecRequirementRef> requirement;
+                       MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
+                       switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) {
+                       case noErr: // success
                                break;
                                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<SecRequirementRef> 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);
        
        // 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))) {
 {
        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");
                        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 {
                                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));
                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);
        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
 //
 //
 // 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)
 {
        // 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<CFTypeRef> target = inTarget;
        authorizeUpdate(flags, context);
        CFDictionary ctx(context, errSecCSInvalidAttributeValues);
        CFCopyRef<CFTypeRef> target = inTarget;
-       
-       if (type == kAuthorityOpenDoc) {
+       CFRef<CFDataRef> bookmark = NULL;
+
+       switch (type) {
+       case kAuthorityExecute:
+               normalizeTarget(target, ctx, true);
+               // bookmarks are untrusted and just a hint to callers
+               bookmark = ctx.get<CFDataRef>(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<CFDictionaryRef>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("virtual install"));
+               }
+               break;
+       case kAuthorityOpenDoc:
                // handle document-open differently: use quarantine flags for whitelisting
                // 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);
                        MacOSError::throwMe(errSecCSInvalidObjectRef);
-               std::string spath = cfString(target.as<CFURLRef>()).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<CFDictionaryRef>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("virtual install"));
+               try {
+                       std::string spath = cfString(target.as<CFURLRef>());
+                       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<CFDictionaryRef>("{%O=%O,'assessment:error'=%d}",
+                               kSecAssessmentAssessmentAuthorityOverride, CFSTR("error setting quarantine"), error.osStatus());
+               } catch (...) {
+                       return cfmake<CFDictionaryRef>("{%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);
        // 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<CFDateRef>(kSecAssessmentUpdateKeyExpires))
                // we're using Julian dates here; convert from CFDate
                label = cfString(lab);
        if (CFDateRef time = ctx.get<CFDateRef>(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<CFBooleanRef>(kSecAssessmentUpdateKeyAllow))
                allow = allowing == kCFBooleanTrue;
        if (CFStringRef rem = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyRemarks))
        if (CFBooleanRef allowing = ctx.get<CFBooleanRef>(kSecAssessmentUpdateKeyAllow))
                allow = allowing == kCFBooleanTrue;
        if (CFStringRef rem = ctx.get<CFStringRef>(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(":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(":expires") = expires;
        if (!remarks.empty())
-               insert.bind(":remarks") = remarks.c_str();
+               insert.bind(":remarks") = remarks;
        insert.execute();
        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);
        this->purgeObjects(priority);
        xact.commit();
        notify_post(kNotifySecAssessmentUpdate);
-       return true;
+       return cfmake<CFDictionaryRef>("{%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<CFMutableArrayRef> 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<CFDataRef> bookmark = query[9].data();
+               CFRef<CFMutableDictionaryRef> rule = makeCFMutableDictionary(5,
+                       kSecAssessmentRuleKeyID, CFTempNumber(id).get(),
+                       kSecAssessmentRuleKeyType, CFRef<CFStringRef>(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<CFDateRef>(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<CFDictionaryRef>("{%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<CFTypeRef> target = inTarget;
        normalizeTarget(target, ctx);
 
        string label;
        CFDictionary ctx(context, errSecCSInvalidAttributeValues);
        CFCopyRef<CFTypeRef> target = inTarget;
        normalizeTarget(target, ctx);
 
        string label;
-       
        if (CFStringRef lab = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyLabel))
                label = cfString(CFStringRef(lab));
 
        if (CFStringRef lab = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyLabel))
                label = cfString(CFStringRef(lab));
 
-       SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "rule_change");
-       SQLite::Statement action(*this);
        if (!target) {
        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()) {
        } else if (CFGetTypeID(target) == CFNumberGetTypeID()) {
-               action.query(stanza + " WHERE id = :id");
+               action.query(phrase + " WHERE " + table + ".id = :id" + suffix);
                action.bind(":id").integer(cfNumber<uint64_t>(target.as<CFNumberRef>()));
        } else if (CFGetTypeID(target) == SecRequirementGetTypeID()) {
                if (type == kAuthorityInvalid)
                        type = kAuthorityExecute;
                CFRef<CFStringRef> requirementText;
                MacOSError::check(SecRequirementCopyString(target.as<SecRequirementRef>(), kSecCSDefaultFlags, &requirementText.aref()));
                action.bind(":id").integer(cfNumber<uint64_t>(target.as<CFNumberRef>()));
        } else if (CFGetTypeID(target) == SecRequirementGetTypeID()) {
                if (type == kAuthorityInvalid)
                        type = kAuthorityExecute;
                CFRef<CFStringRef> requirementText;
                MacOSError::check(SecRequirementCopyString(target.as<SecRequirementRef>(), 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);
                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);
        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);
                this->purgeObjects(1.0E100);
                xact.commit();
                notify_post(kNotifySecAssessmentUpdate);
-               return true;
+               return cfmake<CFDictionaryRef>("{%O=%d}", kSecAssessmentUpdateKeyCount, changes);
        }
        // no change; return an error
        MacOSError::throwMe(errSecCSNoMatches);
 }
 
 
        }
        // 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
 //
 //
 // 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 (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);
                                        CFDictionarySetValue(result, kSecAssessmentAssessmentOriginator, summary);
+                                       CFRelease(summary);
+                               }
 }
 
 
 //
 // Take an assessment outcome and record it in the object cache
 //
 }
 
 
 //
 // 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<CFDictionaryRef> info;
        MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
 {
        CFRef<CFDictionaryRef> 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(":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();
        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.
 //
 // 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;
        
 {
        AuthorizationRef authorization = NULL;
        
@@ -619,9 +810,13 @@ static void normalizeTarget(CFRef<CFTypeRef> &target, CFDictionary &context, boo
        if (target && CFGetTypeID(target) == CFURLGetTypeID()) {
                CFRef<SecStaticCodeRef> code;
                MacOSError::check(SecStaticCodeCreateWithPath(target.as<CFURLRef>(), kSecCSDefaultFlags, &code.aref()));
        if (target && CFGetTypeID(target) == CFURLGetTypeID()) {
                CFRef<SecStaticCodeRef> code;
                MacOSError::check(SecStaticCodeCreateWithPath(target.as<CFURLRef>(), kSecCSDefaultFlags, &code.aref()));
-               CFRef<SecRequirementRef> requirement;
                switch (OSStatus rc = SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref())) {
                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<CFDictionaryRef> info;
+                       MacOSError::check(SecCodeCopySigningInformation(code, kSecCSRequirementInformation, &info.aref()));
+                       target = CFDictionaryGetValue(info, kSecCodeInfoImplicitDesignatedRequirement);
+                       }
                        break;
                case errSecCSUnsigned:
                        if (signUnsigned) {
                        break;
                case errSecCSUnsigned:
                        if (signUnsigned) {
@@ -633,7 +828,19 @@ static void normalizeTarget(CFRef<CFTypeRef> &target, CFDictionary &context, boo
                                MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref()));
                                break;
                        }
                                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<SecCodeSignerRef> signer;
+                               CFTemp<CFDictionaryRef> 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);
                }
                default:
                        MacOSError::check(rc);
                }
@@ -649,5 +856,29 @@ static void normalizeTarget(CFRef<CFTypeRef> &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<CFDictionaryRef> 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
 } // end namespace CodeSigning
 } // end namespace Security
index e22f50d4edc029a37c72777ce355398b28de241e..7ae9bfc7c3a997649df712d11e17a41ffe2ff318 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -53,27 +53,30 @@ public:
 public:
        void evaluate(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result);
 
 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:
 
 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);
        
        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);
 
                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);
 };
 
 
 };
 
 
index 5e1567d58961fdcda7646f4a6094f907e198a644..e3e823380fb79d56836a75b96e6605454978e886 100644 (file)
@@ -239,7 +239,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma
                if (cf->name == key) {
                        CFRef<CFStringRef> value;
                        if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) {
                if (cf->name == key) {
                        CFRef<CFStringRef> 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);
                                return false;
                        }
                        return match(value);
@@ -249,7 +249,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma
        if (key == "email") {
                CFRef<CFArrayRef> value;
                if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) {
        if (key == "email") {
                CFRef<CFArrayRef> 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);
                        return false;
                }
                return match(value);
index 714e6c57ef831c4ea0421293b8840ff46c5922ad..cc52729181b67dc20c76b627f0024092ab2ec602 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -102,6 +102,8 @@ SecCertificateRef Requirement::Context::cert(int ix) const
        if (certs) {
                if (ix < 0)
                        ix += certCount();
        if (certs) {
                if (ix < 0)
                        ix += certCount();
+               if (ix >= CFArrayGetCount(certs))
+                   return NULL;
                if (CFTypeRef element = CFArrayGetValueAtIndex(certs, ix))
                        return SecCertificateRef(element);
        }
                if (CFTypeRef element = CFArrayGetValueAtIndex(certs, ix))
                        return SecCertificateRef(element);
        }
@@ -154,21 +156,6 @@ const SHA1::Digest &Requirement::testAppleAnchorHash()
 #endif //TEST_APPLE_ANCHOR
 
 
 #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
 //
 //
 // Debug dump support
 //
index 4588a0249825363d0532fdd46f857962fe1f81f9..dc59fa1026a055012ea5941bef849de34d5b5d19 100644 (file)
@@ -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@
  * 
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -99,17 +99,23 @@ private:
 // An interpretation context
 //
 class Requirement::Context {
 // 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;
 
 
 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
 //
 //
 // Byte order flippers
 //
index 4dbcb67279889866dcf0486d350c6c4378e46dc3..c4849d97c5293a64ea136ed6aef64bee91e6951a 100644 (file)
@@ -79,8 +79,18 @@ provider codesign {
 
 
 provider syspolicy {
 
 
 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();
        probe assess_cache_hit();
        probe assess_local();
        probe assess_remote();
-};
\ No newline at end of file
+};
index a2ca3952dc6ceff626e183d54262d6cc155e3c65..de23f60e6ab0baeb2174754f341720c687f9d9b6 100644 (file)
@@ -77,6 +77,10 @@ _kSecCodeSignerFlags
 _kSecCodeSignerIdentifier
 _kSecCodeSignerIdentifierPrefix
 _kSecCodeSignerIdentity
 _kSecCodeSignerIdentifier
 _kSecCodeSignerIdentifierPrefix
 _kSecCodeSignerIdentity
+_kSecCodeSignerTimestampAuthentication
+_kSecCodeSignerRequireTimestamp
+_kSecCodeSignerTimestampServer
+_kSecCodeSignerTimestampOmitCertificates
 _kSecCodeSignerPageSize
 _kSecCodeSignerRequirements
 _kSecCodeSignerResourceRules
 _kSecCodeSignerPageSize
 _kSecCodeSignerRequirements
 _kSecCodeSignerResourceRules
@@ -86,8 +90,10 @@ _kSecCodeInfoCertificates
 _kSecCodeInfoChangedFiles
 _kSecCodeInfoCMS
 _kSecCodeInfoTime
 _kSecCodeInfoChangedFiles
 _kSecCodeInfoCMS
 _kSecCodeInfoTime
+_kSecCodeInfoTimestamp
 _kSecCodeInfoDesignatedRequirement
 _kSecCodeInfoEntitlements
 _kSecCodeInfoDesignatedRequirement
 _kSecCodeInfoEntitlements
+_kSecCodeInfoEntitlementsDict
 _kSecCodeInfoFormat
 _kSecCodeInfoDigestAlgorithm
 _kSecCodeInfoIdentifier
 _kSecCodeInfoFormat
 _kSecCodeInfoDigestAlgorithm
 _kSecCodeInfoIdentifier
@@ -109,6 +115,7 @@ _kSecGuestAttributeMachPort
 _kSecGuestAttributePid
 _kSecRequirementKeyInfoPlist
 _kSecRequirementKeyEntitlements
 _kSecGuestAttributePid
 _kSecRequirementKeyInfoPlist
 _kSecRequirementKeyEntitlements
+_kSecRequirementKeyIdentifier
 _kSecCFErrorArchitecture
 _kSecCFErrorPattern
 _kSecCFErrorResourceSeal
 _kSecCFErrorArchitecture
 _kSecCFErrorPattern
 _kSecCFErrorResourceSeal
@@ -129,6 +136,7 @@ _SecTaskCopyValuesForEntitlements
 # Assessments
 _SecAssessmentCreate
 _SecAssessmentCopyResult
 # Assessments
 _SecAssessmentCreate
 _SecAssessmentCopyResult
+_SecAssessmentCopyUpdate
 _SecAssessmentUpdate
 _SecAssessmentControl
 _kSecAssessmentContextKeyOperation
 _SecAssessmentUpdate
 _SecAssessmentControl
 _kSecAssessmentContextKeyOperation
@@ -140,12 +148,25 @@ _kSecAssessmentUpdateOperationAdd
 _kSecAssessmentUpdateOperationRemove
 _kSecAssessmentUpdateOperationEnable
 _kSecAssessmentUpdateOperationDisable
 _kSecAssessmentUpdateOperationRemove
 _kSecAssessmentUpdateOperationEnable
 _kSecAssessmentUpdateOperationDisable
-_kSecAssessmentUpdateKeyAuthorization
+_kSecAssessmentUpdateOperationFind
 _kSecAssessmentUpdateKeyAllow
 _kSecAssessmentUpdateKeyAllow
+_kSecAssessmentUpdateKeyLabel
 _kSecAssessmentUpdateKeyExpires
 _kSecAssessmentUpdateKeyPriority
 _kSecAssessmentUpdateKeyExpires
 _kSecAssessmentUpdateKeyPriority
-_kSecAssessmentUpdateKeyLabel
 _kSecAssessmentUpdateKeyRemarks
 _kSecAssessmentUpdateKeyRemarks
+_kSecAssessmentUpdateKeyRow
+_kSecAssessmentUpdateKeyCount
+_kSecAssessmentUpdateKeyFound
+_kSecAssessmentRuleKeyID
+_kSecAssessmentRuleKeyPriority
+_kSecAssessmentRuleKeyAllow
+_kSecAssessmentRuleKeyLabel
+_kSecAssessmentRuleKeyRemarks
+_kSecAssessmentRuleKeyRequirement
+_kSecAssessmentRuleKeyType
+_kSecAssessmentRuleKeyExpires
+_kSecAssessmentRuleKeyDisabled
+_kSecAssessmentRuleKeyBookmark
 _kSecAssessmentAssessmentAuthority
 _kSecAssessmentAssessmentAuthorityRow
 _kSecAssessmentAssessmentFromCache
 _kSecAssessmentAssessmentAuthority
 _kSecAssessmentAssessmentAuthorityRow
 _kSecAssessmentAssessmentFromCache
@@ -154,3 +175,4 @@ _kSecAssessmentAssessmentAuthorityOverride
 _kSecAssessmentAssessmentSource
 _kSecAssessmentAssessmentVerdict
 _kSecAssessmentContextKeyCertificates
 _kSecAssessmentAssessmentSource
 _kSecAssessmentAssessmentVerdict
 _kSecAssessmentContextKeyCertificates
+_kSecAssessmentUpdateKeyAuthorization
index 969b728cb8506fb8b92c6aa720bee00a3b15c123..c90f5e8e75fd4f614e2aec5255c8e79a41cfb060 100644 (file)
@@ -60,6 +60,12 @@ public:
 typedef SuperBlob<0xfade0cc1> DetachedSignatureBlob;   // indexed by main architecture
 
 
 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
 //
 //
 // An entitlement blob is used for embedding entitlement configuration data
 //
index 23cbfd64006e9a3b1fff52ea03a0281bd0be9ab9..92fce92b03d724efed9ea0b480d4ce8ab5c98f10 100644 (file)
@@ -51,10 +51,11 @@ void SecCodeSigner::Signer::sign(SecCSFlags flags)
 {
        rep = code->diskRep()->base();
        this->prepare(flags);
 {
        rep = code->diskRep()->base();
        this->prepare(flags);
+       PreSigningContext context(*this);
        if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
        if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
-               signMachO(fat);
+               signMachO(fat, context);
        } else {
        } else {
-               signArchitectureAgnostic();
+               signArchitectureAgnostic(context);
        }
 }
 
        }
 }
 
@@ -112,7 +113,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        } else {
                cdFlags = 0;
                if (infoDict)
        } 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<uint32_t>(CFNumberRef(csflags));
                                        secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
                                if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
                                        cdFlags = cfNumber<uint32_t>(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);
                                        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
        }
        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<size_t>(state.mPageSize) : rep->pageSize(state);
        }
        
        pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(state);
+    
+    // Timestamping setup
+    CFRef<SecIdentityRef> 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.
 //
 // 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<ArchEditor> editor(state.mDetached
 {
        // Mach-O executable at the core - perform multi-architecture signing
        auto_ptr<ArchEditor> 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));
        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,
                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.
 //
 // 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<DiskRep::Writer> writer = state.mDetached ?
                (new DetachedBlobWriter(*this)) : rep->writer();
        CodeDirectory::Builder builder(state.mDigestAlgorithm);
        InternalRequirements ireqs;
 {
        // non-Mach-O executable - single-instance signing
        RefPointer<DiskRep::Writer> 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());
        
        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);
        
        builder.flags(cdFlags);
        builder.identifier(identifier);
        
-       if (CFDataRef data = rep->component(cdInfoSlot))
+       if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
                builder.specialSlot(cdInfoSlot, data);
        if (ireqs) {
                CFRef<CFDataRef> data = makeCFData(*ireqs);
                builder.specialSlot(cdInfoSlot, data);
        if (ireqs) {
                CFRef<CFDataRef> data = makeCFData(*ireqs);
@@ -312,6 +317,7 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
        writer.addDiscretionary(builder);
 }
 
        writer.addDiscretionary(builder);
 }
 
+#include <Security/tsaSupport.h>
 
 //
 // Generate the CMS signature for a (finished) CodeDirectory.
 
 //
 // 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);
 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
 {
        assert(state.mSigner);
-       
+       CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
+    
        // a null signer generates a null signature blob
        if (state.mSigner == SecIdentityRef(kCFNull))
                return CFDataCreate(NULL, NULL, 0);
        // 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()));
        }
        
        MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
+    
+    // Set up to call Timestamp server if requested
+    
+    if (state.mWantTimeStamp)
+    {
+        CFRef<CFErrorRef> 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));
        CFDataRef signature;
        MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
+
        return signature;
 }
 
        return signature;
 }
 
index 9bef12705651f52e39cd115dd94eb4d1117ccd43..101f222fba49ea824671a5c041a2acbad6334b93 100644 (file)
@@ -55,11 +55,13 @@ public:
        CodeDirectory::HashAlgorithm digestAlgorithm() const { return state.mDigestAlgorithm; }
        
        std::string path() const { return cfString(rep->canonicalPath()); }
        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
        
 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,
 
        void populate(DiskRep::Writer &writer);         // global
        void populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
index f1264f5db344764fb15e40b80b22c19708799a17..d03d884e5ede3411717bbbd525c894416da899fe 100644 (file)
@@ -31,6 +31,7 @@
 #include <Security/CMSEncoder.h>
 #include "renum.h"
 #include "csutilities.h"
 #include <Security/CMSEncoder.h>
 #include "renum.h"
 #include "csutilities.h"
+#include "drmaker.h"
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
 #include <vector>
 #include <security_utilities/unix++.h>
 #include <security_utilities/unixchild.h>
 #include <vector>
@@ -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<SecCertificateRef> signingCert;
+               MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
+               CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
+               CFRef<SecTrustRef> 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
 } // end namespace CodeSigning
 } // end namespace Security
index 92d48f37256cbeaa6b552ce4eceb02e3c1de2eef..3fdb081565ec78eddf78687ebdfbdb7d97af32e4 100644 (file)
@@ -39,6 +39,21 @@ namespace Security {
 namespace CodeSigning {
 
 
 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)
 //
 //
 // 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<CFArrayRef> mCerts;               // hold cert chain
+};
+
+
 } // end namespace CodeSigning
 } // end namespace Security
 
 } // end namespace CodeSigning
 } // end namespace Security
 
index 9448908fee75470c44047ee3ec13f6f2aa93c3f4..9e3eecee18a4a6f69509f0b7a3f770333ad546d8 100644 (file)
@@ -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@
 -- 
 -- 
 -- @APPLE_LICENSE_HEADER_START@
 -- 
@@ -39,10 +39,10 @@ PRAGMA recursive_triggers = true;
 -- The feature table hold configuration features and options
 --
 CREATE TABLE feature (
 -- 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
        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)
        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
        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)
        -- 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;
 
 
 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
 --
 --
 -- 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)
 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])');
 
 
 --
 
 
 --
index 42b49a413472fbab04b12092e85272ef19cb5072..30e9665e919dde67cb1803cf0112a51571414319 100644 (file)
@@ -36,15 +36,28 @@ namespace CodeSigning {
 Xar::Xar(const char *path)
 {
        mXar = 0;
 Xar::Xar(const char *path)
 {
        mXar = 0;
-       mSig = 0;
+       mSigCMS = 0;
+       mSigClassic = 0;
        if (path)
                open(path);
 }
 
 void Xar::open(const char *path)
 {
        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()
 }
 
 Xar::~Xar()
@@ -53,17 +66,14 @@ Xar::~Xar()
                ::xar_close(mXar);
 }
 
                ::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<CFMutableArrayRef> certs = makeCFMutableArray(0);
        for (unsigned ix = 0; ix < count; ix++) {
                const uint8_t *data;
                uint32_t length;
        CFRef<CFMutableArrayRef> 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<SecCertificateRef> cert = SecCertificateCreateWithData(NULL, cdata);
                        CFArrayAppendValue(certs, cert.get());
                        CFTempData cdata(data, length);
                        CFRef<SecCertificateRef> cert = SecCertificateCreateWithData(NULL, cdata);
                        CFArrayAppendValue(certs, cert.get());
@@ -72,6 +82,15 @@ CFArrayRef Xar::copyCertChain()
        return certs.yield();
 }
 
        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
 
 } // end namespace CodeSigning
 } // end namespace Security
index fa287775180cfed45826b022037e34b43e543eae..11f4f7e4147b31beb8f494348f25fbf394dac476 100644 (file)
@@ -48,13 +48,14 @@ public:
        void open(const char *path);
        
        operator bool() const { return mXar != 0; }
        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;
        
        CFArrayRef copyCertChain();
 
 private:
        xar_t mXar;
-       xar_signature_t mSig;
+       xar_signature_t mSigClassic;
+       xar_signature_t mSigCMS;
 };
 
 
 };
 
 
index 64fbcf9d8b2fe898eb185339d519e5fcae5b59f5..30a25a1db67fd3a7767654be8f302bf959f22ca2 100644 (file)
@@ -25,7 +25,7 @@
 #include <syslog.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <security_utilities/cfutilities.h>
 #include <syslog.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <security_utilities/cfutilities.h>
-#include <Security/CodeSigning.h>
+#include <Security/SecRequirement.h>
 
 
 namespace Security {
 
 
 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
 {
        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);
        
        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);
 }
 
 
 }
 
 
index 8b2f115d4d9be37764737f3c397d03329f131f77..e2edd01bb27f898ad2ccfe4bd7970e54c94daed0 100644 (file)
@@ -33,11 +33,11 @@ namespace CodeSigning {
 
 
 void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutableDictionaryRef result);
 
 
 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);
 
 
 bool xpcEngineControl(const char *name);
 
 
-
 } // end namespace CodeSigning
 } // end namespace Security
 
 } // end namespace CodeSigning
 } // end namespace Security
 
index 8980156d867e2b66e572ef5eb1eb395b1395cc83..84f108cfb22ebc166e28efba9ae472f01d39b0ff 100644 (file)
@@ -14,6 +14,7 @@
                                C26AC0F0143BCF18001C98CE /* ShellScript */,
                                C26AC0F4143BD1C4001C98CE /* CopyFiles */,
                                C2F24DFE14BCBBF200309FCD /* ShellScript */,
                                C26AC0F0143BCF18001C98CE /* ShellScript */,
                                C26AC0F4143BD1C4001C98CE /* CopyFiles */,
                                C2F24DFE14BCBBF200309FCD /* ShellScript */,
+                               C24826BD159911A20082B10B /* CopyFiles */,
                        );
                        dependencies = (
                        );
                        );
                        dependencies = (
                        );
                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 */; };
                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 */; };
                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 */; };
                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, ); }; };
                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 */; };
                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 */; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        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;
                C26AC0F4143BD1C4001C98CE /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                C236E3D60AD59446000F5140 /* signer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signer.h; sourceTree = "<group>"; };
                C236E3D90AD595C2000F5140 /* signerutils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signerutils.cpp; sourceTree = "<group>"; };
                C236E3DA0AD595C2000F5140 /* signerutils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signerutils.h; sourceTree = "<group>"; };
                C236E3D60AD59446000F5140 /* signer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signer.h; sourceTree = "<group>"; };
                C236E3D90AD595C2000F5140 /* signerutils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signerutils.cpp; sourceTree = "<group>"; };
                C236E3DA0AD595C2000F5140 /* signerutils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signerutils.h; sourceTree = "<group>"; };
+               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 = "<group>"; };
                C24EABAC1421433700C16AA9 /* policydb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = policydb.cpp; sourceTree = "<group>"; };
                C250F6C20B5EF1910076098F /* SecIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrity.h; sourceTree = "<group>"; };
                C24EABAA1421432800C16AA9 /* policydb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = policydb.h; sourceTree = "<group>"; };
                C24EABAC1421433700C16AA9 /* policydb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = policydb.cpp; sourceTree = "<group>"; };
                C250F6C20B5EF1910076098F /* SecIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrity.h; sourceTree = "<group>"; };
                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 = "<group>"; };
                C2F6566D0BCBFB250078779E /* cserror.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cserror.h; sourceTree = "<group>"; };
                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 = "<group>"; };
                C2F6566D0BCBFB250078779E /* cserror.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cserror.h; sourceTree = "<group>"; };
+               EB5B684E156E492D0067635E /* drmaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drmaker.cpp; sourceTree = "<group>"; };
+               EB5B684F156E492D0067635E /* drmaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = drmaker.h; sourceTree = "<group>"; };
                FEB30C9210DAC89D00557BA2 /* SecTask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SecTask.c; sourceTree = "<group>"; };
                FEB30C9410DAC8A500557BA2 /* SecTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecTask.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
                FEB30C9210DAC89D00557BA2 /* SecTask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SecTask.c; sourceTree = "<group>"; };
                FEB30C9410DAC8A500557BA2 /* SecTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecTask.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
                                C2CC31160B852554005FA59D /* Security Plugins */,
                                FEB30C9110DAC6C400557BA2 /* Entitlements */,
                                C24EABA914213FAF00C16AA9 /* System Policy */,
                                C2CC31160B852554005FA59D /* Security Plugins */,
                                FEB30C9110DAC6C400557BA2 /* Entitlements */,
                                C24EABA914213FAF00C16AA9 /* System Policy */,
+                               C24826C5159911D70082B10B /* GKE */,
                        );
                        path = lib;
                        sourceTree = "<group>";
                        );
                        path = lib;
                        sourceTree = "<group>";
                        name = "Signing Operations";
                        sourceTree = "<group>";
                };
                        name = "Signing Operations";
                        sourceTree = "<group>";
                };
+               C24826C5159911D70082B10B /* GKE */ = {
+                       isa = PBXGroup;
+                       children = (
+                               C24826BF159911CF0082B10B /* gkclear */,
+                               C24826C0159911CF0082B10B /* gkgenerate */,
+                               C24826C1159911CF0082B10B /* gkhandmake */,
+                               C24826C2159911CF0082B10B /* gklist */,
+                               C24826C3159911CF0082B10B /* gkmerge */,
+                               C24826C4159911CF0082B10B /* gkrecord */,
+                       );
+                       name = GKE;
+                       sourceTree = "<group>";
+               };
                C24EABA914213FAF00C16AA9 /* System Policy */ = {
                        isa = PBXGroup;
                        children = (
                C24EABA914213FAF00C16AA9 /* System Policy */ = {
                        isa = PBXGroup;
                        children = (
                C2C1DF620A2E45B600D1B02B /* Requirements */ = {
                        isa = PBXGroup;
                        children = (
                C2C1DF620A2E45B600D1B02B /* Requirements */ = {
                        isa = PBXGroup;
                        children = (
+                               EB5B684E156E492D0067635E /* drmaker.cpp */,
+                               EB5B684F156E492D0067635E /* drmaker.h */,
                                C2C1DF8F0A2E4A2700D1B02B /* requirements.grammar */,
                                C2D383360A237F47005C63A2 /* requirement.h */,
                                C2D383350A237F47005C63A2 /* requirement.cpp */,
                                C2C1DF8F0A2E4A2700D1B02B /* requirements.grammar */,
                                C2D383360A237F47005C63A2 /* requirement.h */,
                                C2D383350A237F47005C63A2 /* requirement.cpp */,
                                C2A436160F2133B2007A41A6 /* slcrep.h in Headers */,
                                C24EABAB1421432800C16AA9 /* policydb.h in Headers */,
                                C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */,
                                C2A436160F2133B2007A41A6 /* slcrep.h in Headers */,
                                C24EABAB1421432800C16AA9 /* policydb.h in Headers */,
                                C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */,
+                               EB5B6857156E4FEE0067635E /* drmaker.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */,
                                C2DC2DCB145F5CD000AD2A3A /* policyengine.cpp in Sources */,
                                C2F4439A14C626D4000A01E6 /* quarantine++.cpp in Sources */,
                                C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */,
                                C2DC2DCB145F5CD000AD2A3A /* policyengine.cpp in Sources */,
                                C2F4439A14C626D4000A01E6 /* quarantine++.cpp in Sources */,
+                               EB5B6856156E4FEE0067635E /* drmaker.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                BUILD_VARIANTS = debug;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                BUILD_VARIANTS = debug;
-                               CURRENT_PROJECT_VERSION = 55037.4;
+                               CURRENT_PROJECT_VERSION = 55037.15;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders",
                                        "$(TEMPDIR)",
                                        "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders",
                                        "$(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)";
                                );
                                LIBRARY_STYLE = "\U0001STATIC";
                                OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)";
                                        normal,
                                        debug,
                                );
                                        normal,
                                        debug,
                                );
-                               CURRENT_PROJECT_VERSION = 55037.4;
+                               CURRENT_PROJECT_VERSION = 55037.15;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders",
                                        "$(TEMPDIR)",
                                        "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders",
                                        "$(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)";
                                );
                                LIBRARY_STYLE = STATIC;
                                OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)";
                                        normal,
                                        debug,
                                );
                                        normal,
                                        debug,
                                );
-                               CURRENT_PROJECT_VERSION = 55037.4;
+                               CURRENT_PROJECT_VERSION = 55037.15;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders",
                                        "$(TEMPDIR)",
                                        "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders",
                                        "$(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)";
                                );
                                LIBRARY_STYLE = STATIC;
                                OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)";