]> git.saurik.com Git - apple/libsecurity_codesigning.git/commitdiff
libsecurity_codesigning-55037.6.tar.gz mac-os-x-1074 v55037.6
authorApple <opensource@apple.com>
Mon, 26 Mar 2012 18:47:55 +0000 (18:47 +0000)
committerApple <opensource@apple.com>
Mon, 26 Mar 2012 18:47:55 +0000 (18:47 +0000)
23 files changed:
lib/CSCommon.h
lib/SecAssessment.cpp
lib/SecAssessment.h
lib/SecCode.cpp
lib/SecStaticCode.cpp
lib/StaticCode.cpp
lib/StaticCode.h
lib/bundlediskrep.cpp
lib/codedirectory.cpp
lib/codedirectory.h
lib/policydb.cpp
lib/policydb.h
lib/policyengine.cpp
lib/policyengine.h
lib/quarantine++.cpp [new file with mode: 0644]
lib/quarantine++.h [new file with mode: 0644]
lib/reqdumper.cpp
lib/security_codesigning.exp
lib/syspolicy.sql
lib/xpcengine.cpp
lib/xpcengine.h
libsecurity_codesigning.xcodeproj/project.pbxproj
requirements.grammar

index 4aae1709b703dae3fdb0b5a72f1a1d98f9083b10..3c87b6213cadc8b2cbecb1a0c4b4ad61b9ca44a3 100644 (file)
@@ -89,6 +89,7 @@ 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 */
 };
 
 
 };
 
 
@@ -176,6 +177,7 @@ enum {
        kSecCSDefaultFlags = 0,                                 /* no particular flags (default behavior) */
        
        kSecCSConsiderExpiration = 1 << 31,             /* consider expired certificates invalid */
        kSecCSDefaultFlags = 0,                                 /* no particular flags (default behavior) */
        
        kSecCSConsiderExpiration = 1 << 31,             /* consider expired certificates invalid */
+       kSecCSEnforceRevocationChecks = 1 << 30,        /* force revocation checks regardless of preference settings */
 };
 
 
 };
 
 
index d5a5a89a5e2edf9fb4608e70961491942ab77b2f..fbecc413a049cf2edc5adee7e6d01fd99abf600e 100644 (file)
@@ -30,6 +30,7 @@
 #include <security_utilities/globalizer.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
 #include <security_utilities/globalizer.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
+#include <notify.h>
 
 using namespace CodeSigning;
 
 
 using namespace CodeSigning;
 
@@ -114,42 +115,9 @@ ModuleNexus<ReadPolicy> gDatabase;
 ModuleNexus<PolicyEngine> gEngine;
 
 
 ModuleNexus<PolicyEngine> gEngine;
 
 
-//
-// Help mapping API-ish CFString keys to more convenient internal enumerations
-//
-typedef struct {
-       CFStringRef cstring;
-       uint enumeration;
-} StringMap;
-
-static uint mapEnum(CFDictionaryRef context, CFStringRef attr, const StringMap *map, uint value = 0)
-{
-       if (context)
-               if (CFTypeRef value = CFDictionaryGetValue(context, attr))
-                       for (const StringMap *mp = map; mp->cstring; ++mp)
-                               if (CFEqual(mp->cstring, value))
-                                       return mp->enumeration;
-       if (value)
-               return value;
-       MacOSError::throwMe(errSecCSInvalidAttributeValues);
-}
-
-static const StringMap mapType[] = {
-       { kSecAssessmentOperationTypeExecute, kAuthorityExecute },
-       { kSecAssessmentOperationTypeInstall, kAuthorityInstall },
-       { kSecAssessmentOperationTypeOpenDocument, kAuthorityOpenDoc },
-       { NULL }
-};
-
-static AuthorityType typeFor(CFDictionaryRef context, AuthorityType type = kAuthorityInvalid)
-{ return mapEnum(context, kSecAssessmentContextKeyOperation, mapType, type); }
-
-
 //
 // Policy evaluation ("assessment") operations
 //
 //
 // Policy evaluation ("assessment") operations
 //
-const CFStringRef kSecAssessmentContextKeyCertificates = CFSTR("context:certificates");
-
 const CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict");
 const CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator");
 const CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority");
 const CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict");
 const CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator");
 const CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority");
@@ -158,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 kSecAssessmentContextKeyCertificates = CFSTR("context:certificates");        // obsolete
+
 SecAssessmentRef SecAssessmentCreate(CFURLRef path,
        SecAssessmentFlags flags,
        CFDictionaryRef context,
 SecAssessmentRef SecAssessmentCreate(CFURLRef path,
        SecAssessmentFlags flags,
        CFDictionaryRef context,
@@ -189,10 +159,18 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path,
                        xpcEngineAssess(path, flags, context, result);
                }
        } catch (CommonError &error) {
                        xpcEngineAssess(path, flags, context, result);
                }
        } catch (CommonError &error) {
-               if (!overrideAssessment())
-                       throw;          // let it go as an error
+               switch (error.osStatus()) {
+               case CSSMERR_TP_CERT_REVOKED:
+                       throw;
+               default:
+                       if (!overrideAssessment())
+                               throw;          // let it go as an error
+                       break;
+               }
+               // record the error we would have returned
                cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
        } catch (...) {
                cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
        } catch (...) {
+               // catch stray errors not conforming to the CommonError scheme
                if (!overrideAssessment())
                        throw;          // let it go as an error
                cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
                if (!overrideAssessment())
                        throw;          // let it go as an error
                cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
@@ -289,16 +267,23 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef,
 
 
 //
 
 
 //
-// Policy editing operations
+// Policy editing operations.
+// These all make permanent changes to the system-wide authority records.
 //
 const CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update");
 //
 const CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update");
-const CFStringRef kSecAssessmentUpdateOperationAddFile = CFSTR("update:addfile");
-const CFStringRef kSecAssessmentUpdateOperationRemoveFile = CFSTR("update:removefile");
+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 kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization");
 const CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority");
 const CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label");
 const CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority");
 const CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label");
+const CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires");
+const CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow");
+const CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks");
 
 
-Boolean SecAssessmentUpdate(CFURLRef path,
+Boolean SecAssessmentUpdate(CFTypeRef target,
        SecAssessmentFlags flags,
        CFDictionaryRef context,
        CFErrorRef *errors)
        SecAssessmentFlags flags,
        CFDictionaryRef context,
        CFErrorRef *errors)
@@ -306,15 +291,14 @@ Boolean SecAssessmentUpdate(CFURLRef path,
        BEGIN_CSAPI
 
        CFDictionary ctx(context, errSecCSInvalidAttributeValues);
        BEGIN_CSAPI
 
        CFDictionary ctx(context, errSecCSInvalidAttributeValues);
-       CFStringRef edit = ctx.get<CFStringRef>(kSecAssessmentContextKeyUpdate);
 
 
-       AuthorityType type = typeFor(context);
-       if (edit == kSecAssessmentUpdateOperationAddFile)
-               return gEngine().add(path, type, flags, context);
-       else if (edit == kSecAssessmentUpdateOperationRemoveFile)
-               MacOSError::throwMe(errSecCSUnimplemented);
-       else
-               MacOSError::throwMe(errSecCSInvalidAttributeValues);
+       if (flags & kSecAssessmentFlagDirect) {
+               // ask the engine right here to do its thing
+               return gEngine().update(target, flags, ctx);
+       } else {
+               // relay the question to our daemon for consideration
+               return xpcEngineUpdate(target, flags, ctx);
+       }
 
        END_CSAPI_ERRORS1(false)
 }
 
        END_CSAPI_ERRORS1(false)
 }
@@ -329,12 +313,14 @@ 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);
+               { UnixPlusPlus::AutoFileDesc flagFile(visibleSecurityFlagFile, O_CREAT | O_WRONLY, 0644); }
+               notify_post(kNotifySecAssessmentMasterSwitch);
                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) {
                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;
                        MessageTrace trace("com.apple.security.assessment.state", "disable");
                        trace.send("disable assessment outcomes");
                        return true;
@@ -347,6 +333,19 @@ Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *e
                else
                        result = kCFBooleanTrue;
                return true;
                else
                        result = kCFBooleanTrue;
                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);
+       } else if (CFEqual(control, CFSTR("ui-disable-devid"))) {
+               CFTemp<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
+               return gEngine().disable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx);
+       } 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))
+                       result = kCFBooleanFalse;
+               else
+                       result = kCFBooleanTrue;
+               return true;
        } else
                MacOSError::throwMe(errSecCSInvalidAttributeValues);
 
        } else
                MacOSError::throwMe(errSecCSInvalidAttributeValues);
 
index 546b139b6047a6430f29a93d8fd9e817c73360fc..9cae77543d25b23ecee66f71da7d51b7d6afc69e 100644 (file)
@@ -42,17 +42,27 @@ typedef struct _SecAssessment *SecAssessmentRef;
 CFTypeID SecAssessmentGetTypeID();
 
 
 CFTypeID SecAssessmentGetTypeID();
 
 
+/*
+ * Notifications sent when the policy authority database changes.
+ * (Should move to /usr/include/notify_keys.h eventually.)
+ */
+#define kNotifySecAssessmentMasterSwitch "com.apple.security.assessment.masterswitch"
+#define kNotifySecAssessmentUpdate "com.apple.security.assessment.update"
+
+
 /*!
 /*!
- * Primary operation codes. These are operations the system policy can express
+ * Primary operation types. These are operations the system policy can express
  * opinions on. They are not operations *on* the system configuration itself.
  * (For those, see SecAssessmentUpdate below.)
  *
  * @constant kSecAssessmentContextKeyOperation Context key describing the type of operation
  * opinions on. They are not operations *on* the system configuration itself.
  * (For those, see SecAssessmentUpdate below.)
  *
  * @constant kSecAssessmentContextKeyOperation Context key describing the type of operation
- *     being contemplated.
- * @constant kSecAssessmentOperationTypeInstall Value denoting the operation of installing
- *     software into the system.
+ *     being contemplated. The default varies depending on the API call used.
  * @constant kSecAssessmentOperationTypeExecute Value denoting the operation of running or executing
  *     code on the system.
  * @constant kSecAssessmentOperationTypeExecute Value denoting the operation of running or executing
  *     code on the system.
+ * @constant kSecAssessmentOperationTypeInstall Value denoting the operation of installing
+ *     software into the system.
+ * @constant kSecAssessmentOperationTypeOpenDocument Value denoting the operation of opening
+ *     (in the LaunchServices sense) of documents.
  */
 extern const CFStringRef kSecAssessmentContextKeyOperation;    // proposed operation
 extern const CFStringRef kSecAssessmentOperationTypeExecute;   // .. execute code
  */
 extern const CFStringRef kSecAssessmentContextKeyOperation;    // proposed operation
 extern const CFStringRef kSecAssessmentOperationTypeExecute;   // .. execute code
@@ -71,6 +81,8 @@ extern const CFStringRef kSecAssessmentOperationTypeOpenDocument; // .. LaunchSe
                evaluation of system policy. This may be substantially slower.
        @constant kSecAssessmentFlagNoCache Do not save any evaluation outcome in the system caches.
                Any content already there is left undisturbed. Independent of kSecAssessmentFlagIgnoreCache.
                evaluation of system policy. This may be substantially slower.
        @constant kSecAssessmentFlagNoCache Do not save any evaluation outcome in the system caches.
                Any content already there is left undisturbed. Independent of kSecAssessmentFlagIgnoreCache.
+       @constant kSecAssessmentFlagEnforce Perform normal operations even if assessments have been
+               globally bypassed (which would usually approve anything).
        
        Flags common to multiple calls are assigned from high-bit down. Flags for particular calls
        are assigned low-bit up, and are documented with that call.
        
        Flags common to multiple calls are assigned from high-bit down. Flags for particular calls
        are assigned low-bit up, and are documented with that call.
@@ -98,7 +110,7 @@ enum {
        @param context Optional CFDictionaryRef containing additional information bearing
                on the requested assessment.
        @param errors Standard CFError argument for reporting errors. Note that declining to permit
        @param context Optional CFDictionaryRef containing additional information bearing
                on the requested assessment.
        @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.
+               the proposed operation is not an error. Inability to arrive at a judgment is.
        @result On success, a SecAssessment object that can be queried for its outcome.
                On error, NULL (with *errors set).
        
        @result On success, a SecAssessment object that can be queried for its outcome.
                On error, NULL (with *errors set).
        
@@ -111,10 +123,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.
-       @constant kSecAssessmentContextKeyEdit A CFArray of SecCertificateRefs describing the
-               certificate chain of a CMS-type signature as pulled from 'path' by the caller.
-               The first certificate is the signing certificate. The certificates provided must be
-               sufficient to construct a valid certificate chain.
  */
 extern const CFStringRef kSecAssessmentContextKeyCertificates; // certificate chain as provided by caller
 
  */
 extern const CFStringRef kSecAssessmentContextKeyCertificates; // certificate chain as provided by caller
 
@@ -141,8 +149,6 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path,
 
        Extract results from a completed assessment and return them as a CFDictionary.
 
 
        Extract results from a completed assessment and return them as a CFDictionary.
 
-       Assessment result keys (dictionary keys returned on success):
-
        @param assessment A SecAssessmentRef created with SecAssessmentCreate.
        @param flags Operation flags and options. Pass kSecAssessmentDefaultFlags for default
                behavior.
        @param assessment A SecAssessmentRef created with SecAssessmentCreate.
        @param flags Operation flags and options. Pass kSecAssessmentDefaultFlags for default
                behavior.
@@ -152,6 +158,8 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path,
                data as requested by flags. The caller owns this dictionary and should release it
                when done with it. On error, NULL (with *errors set).
 
                data as requested by flags. The caller owns this dictionary and should release it
                when done with it. On error, NULL (with *errors set).
 
+       Assessment result keys (dictionary keys returned on success):
+
        @constant kSecAssessmentAssessmentVerdict A CFBoolean value indicating whether the system policy
                allows (kCFBooleanTrue) or denies (kCFBooleanFalse) the proposed operation.
        @constant kSecAssessmentAssessmentAuthority A CFDictionary describing what sources of authority
        @constant kSecAssessmentAssessmentVerdict A CFBoolean value indicating whether the system policy
                allows (kCFBooleanTrue) or denies (kCFBooleanFalse) the proposed operation.
        @constant kSecAssessmentAssessmentAuthority A CFDictionary describing what sources of authority
@@ -169,9 +177,10 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment,
        @function SecAssessmentUpdate
        Make changes to the system policy configuration.
        
        @function SecAssessmentUpdate
        Make changes to the system policy configuration.
        
-       @param path CFURL describing the file central to an operation - the program
-               to be executed, archive to be installed, plugin to be loaded, etc.
-               Pass NULL if the requested operation has no file subject.
+       @param path CFTypeRef describing the subject of the operation. Depending on the operation,
+               this may be a CFURL denoting a (single) file or bundle; a SecRequirement describing
+               a group of files; a CFNumber denoting an existing rule by rule number, or NULL to perform
+               global changes.
        @param flags Operation flags and options. Pass kSecAssessmentDefaultFlags for default
                behavior.
        @param context Required CFDictionaryRef containing information bearing
        @param flags Operation flags and options. Pass kSecAssessmentDefaultFlags for default
                behavior.
        @param context Required CFDictionaryRef containing information bearing
@@ -184,19 +193,42 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessment,
 
        @constant kSecAssessmentContextKeyEdit Required context key describing the kind of change
                requested to the system policy configuration. Currently understood values:
 
        @constant kSecAssessmentContextKeyEdit Required context key describing the kind of change
                requested to the system policy configuration. Currently understood values:
-       @constant kSecAssessmentUpdateOperationAddFile Request to add rules governing operations on the 'path'
-               argument.
-       @constant kSecAssessmentUpdateOperationRemoveFile Request to remove rules governing operations on the
-               'path' argument.
+       @constant kSecAssessmentUpdateOperationAdd Add a new rule to the assessment 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 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
+               another client, an AuthorizationRef should be created there and passed along here.
+       @constant kSecAssessmentUpdateKeyPriority CFNumber denoting a (floating point) priority
+               for the rule(s) being processed.
+       @constant kSecAssessmentUpdateKeyLabel CFString denoting a label string applied to the rule(s)
+               being processed.
+       @constant kSecAssessmentUpdateKeyExpires CFDate denoting an (absolute, future) expiration date
+               for rule(s) being processed.
+       @constant kSecAssessmentUpdateKeyAllow CFBoolean denoting whether a new rule allows or denies
+               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.
  */
 extern const CFStringRef kSecAssessmentContextKeyUpdate;               // proposed operation
  */
 extern const CFStringRef kSecAssessmentContextKeyUpdate;               // proposed operation
-extern const CFStringRef kSecAssessmentUpdateOperationAddFile; // add to policy database
-extern const CFStringRef kSecAssessmentUpdateOperationRemoveFile; // remove from policy database
+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 kSecAssessmentUpdateKeyAuthorization; // [CFData] external form of governing authorization
 
 extern const CFStringRef kSecAssessmentUpdateKeyPriority;              // rule priority
 extern const CFStringRef kSecAssessmentUpdateKeyLabel;                 // rule label
 
 extern const CFStringRef kSecAssessmentUpdateKeyPriority;              // rule priority
 extern const CFStringRef kSecAssessmentUpdateKeyLabel;                 // rule label
+extern const CFStringRef kSecAssessmentUpdateKeyExpires;               // rule expiration
+extern const CFStringRef kSecAssessmentUpdateKeyAllow;                 // rule outcome (allow/deny)
+extern const CFStringRef kSecAssessmentUpdateKeyRemarks;               // rule remarks (human readable)
 
 
-Boolean SecAssessmentUpdate(CFURLRef path,
+extern const CFStringRef kSecAssessmentContextKeyCertificates; // obsolete
+
+Boolean SecAssessmentUpdate(CFTypeRef target,
        SecAssessmentFlags flags,
        CFDictionaryRef context,
        CFErrorRef *errors);
        SecAssessmentFlags flags,
        CFDictionaryRef context,
        CFErrorRef *errors);
@@ -204,7 +236,12 @@ Boolean SecAssessmentUpdate(CFURLRef path,
 
 /*!
        @function SecAssessmentControl
 
 /*!
        @function SecAssessmentControl
-       Miscellaneous system policy operations
+       Miscellaneous system policy operations.
+       
+       @param control A CFString indicating which operation is requested.
+       @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 fc64df445038f682626fb3c69ebbf177ee124932..779a7d309320ceb20046cbe9222a169a19c4e8b9 100644 (file)
@@ -197,7 +197,8 @@ OSStatus SecCodeCheckValidityWithErrors(SecCodeRef codeRef, SecCSFlags flags,
        BEGIN_CSAPI
        
        checkFlags(flags,
        BEGIN_CSAPI
        
        checkFlags(flags,
-               kSecCSConsiderExpiration);
+                 kSecCSConsiderExpiration
+               | kSecCSEnforceRevocationChecks);
        SecPointer<SecCode> code = SecCode::required(codeRef);
        code->checkValidity(flags);
        if (const SecRequirement *req = SecRequirement::optional(requirementRef))
        SecPointer<SecCode> code = SecCode::required(codeRef);
        code->checkValidity(flags);
        if (const SecRequirement *req = SecRequirement::optional(requirementRef))
index 3669928ecbf9364056f626be540395d5a0e1dd62..7cb5a17e6613030822852dc47fc6b098fdcf686e 100644 (file)
@@ -98,7 +98,7 @@ static void validateNested(string location, const SecRequirement *req, SecCSFlag
 static void validate(SecStaticCode *code, const SecRequirement *req, SecCSFlags flags)
 {
        try {
 static void validate(SecStaticCode *code, const SecRequirement *req, SecCSFlags flags)
 {
        try {
-               code->validateDirectory();
+               code->validateNonResourceComponents();  // also validates the CodeDirectory
                if (!(flags & kSecCSDoNotValidateExecutable))
                        code->validateExecutable();
                if (!(flags & kSecCSDoNotValidateResources))
                if (!(flags & kSecCSDoNotValidateExecutable))
                        code->validateExecutable();
                if (!(flags & kSecCSDoNotValidateResources))
@@ -187,6 +187,7 @@ OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCodeRef, Se
                | kSecCSDoNotValidateExecutable
                | kSecCSDoNotValidateResources
                | kSecCSConsiderExpiration
                | kSecCSDoNotValidateExecutable
                | kSecCSDoNotValidateResources
                | kSecCSConsiderExpiration
+               | kSecCSEnforceRevocationChecks
                | kSecCSCheckNestedCode);
 
        SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(staticCodeRef);
                | kSecCSCheckNestedCode);
 
        SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(staticCodeRef);
index cec0b2b58d5e416ef9c2645288bb19f4b9ad1daf..956c88f9d53a70872baee63c676a79a3bcc1d290 100644 (file)
@@ -42,6 +42,7 @@
 #include <Security/SecCmsContentInfo.h>
 #include <Security/SecCmsSignerInfo.h>
 #include <Security/SecCmsSignedData.h>
 #include <Security/SecCmsContentInfo.h>
 #include <Security/SecCmsSignerInfo.h>
 #include <Security/SecCmsSignedData.h>
+#include <Security/cssmapplePriv.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
 #include <Security/CMSDecoder.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
 #include <Security/CMSDecoder.h>
@@ -58,7 +59,7 @@ using namespace UnixPlusPlus;
 //
 SecStaticCode::SecStaticCode(DiskRep *rep)
        : mRep(rep),
 //
 SecStaticCode::SecStaticCode(DiskRep *rep)
        : mRep(rep),
-         mValidated(false), mExecutableValidated(false),
+         mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
          mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
 {
        CODESIGN_STATIC_CREATE(this, rep);
          mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
 {
        CODESIGN_STATIC_CREATE(this, rep);
@@ -72,6 +73,8 @@ SecStaticCode::SecStaticCode(DiskRep *rep)
 SecStaticCode::~SecStaticCode() throw()
 try {
        ::free(const_cast<Requirement *>(mDesignatedReq));
 SecStaticCode::~SecStaticCode() throw()
 try {
        ::free(const_cast<Requirement *>(mDesignatedReq));
+       if (mResourcesValidContext)
+               delete mResourcesValidContext;
 } catch (...) {
        return;
 }
 } catch (...) {
        return;
 }
@@ -186,6 +189,11 @@ void SecStaticCode::resetValidity()
        CODESIGN_EVAL_STATIC_RESET(this);
        mValidated = false;
        mExecutableValidated = false;
        CODESIGN_EVAL_STATIC_RESET(this);
        mValidated = false;
        mExecutableValidated = false;
+       mResourcesValidated = false;
+       if (mResourcesValidContext) {
+               delete mResourcesValidContext;
+               mResourcesValidContext = NULL;
+       }
        mDir = NULL;
        mSignature = NULL;
        for (unsigned n = 0; n < cdSlotCount; n++)
        mDir = NULL;
        mSignature = NULL;
        for (unsigned n = 0; n < cdSlotCount; n++)
@@ -303,10 +311,7 @@ void SecStaticCode::validateDirectory()
                        CODESIGN_EVAL_STATIC_DIRECTORY(this);
                        mValidationExpired = verifySignature();
                        component(cdInfoSlot, errSecCSInfoPlistFailed); // force load of Info Dictionary (if any)
                        CODESIGN_EVAL_STATIC_DIRECTORY(this);
                        mValidationExpired = verifySignature();
                        component(cdInfoSlot, errSecCSInfoPlistFailed); // force load of Info Dictionary (if any)
-                       CodeDirectory::SpecialSlot slot = codeDirectory()->nSpecialSlots;
-                       if (slot > cdSlotMax)   // might have more special slots than we know about...
-                               slot = cdSlotMax;       // ... but only process the ones we understand
-                       for ( ; slot >= 1; --slot)
+                       for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
                                if (mCache[slot])       // if we already loaded that resource...
                                        validateComponent(slot); // ... then check it now
                        mValidated = true;                      // we've done the deed...
                                if (mCache[slot])       // if we already loaded that resource...
                                        validateComponent(slot); // ... then check it now
                        mValidated = true;                      // we've done the deed...
@@ -332,6 +337,24 @@ void SecStaticCode::validateDirectory()
 }
 
 
 }
 
 
+//
+// Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
+// Those latter components are checked by validateResources().
+//
+void SecStaticCode::validateNonResourceComponents()
+{
+       this->validateDirectory();
+       for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
+               switch (slot) {
+               case cdResourceDirSlot:         // validated by validateResources
+                       break;
+               default:
+                       this->component(slot);          // loads and validates
+                       break;
+               }
+}
+
+
 //
 // Get the (signed) signing date from the code signature.
 // Sadly, we need to validate the signature to get the date (as a side benefit).
 //
 // Get the (signed) signing date from the code signature.
 // Sadly, we need to validate the signature to get the date (as a side benefit).
@@ -373,8 +396,9 @@ bool SecStaticCode::verifySignature()
        MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
        MacOSError::check(CMSDecoderFinalizeMessage(cms));
        MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
        MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
        MacOSError::check(CMSDecoderFinalizeMessage(cms));
        MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
+       CFRef<CFTypeRef> policy = verificationPolicy(apiFlags());
     CMSSignerStatus status;
     CMSSignerStatus status;
-    MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, verificationPolicy(),
+    MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, policy,
                false, &status, &mTrust.aref(), NULL));
        if (status != kCMSSignerValid)
                MacOSError::throwMe(errSecCSSignatureFailed);
                false, &status, &mTrust.aref(), NULL));
        if (status != kCMSSignerValid)
                MacOSError::throwMe(errSecCSSignatureFailed);
@@ -447,14 +471,47 @@ bool SecStaticCode::verifySignature()
 
 //
 // Return the TP policy used for signature verification.
 
 //
 // Return the TP policy used for signature verification.
-// This policy object is cached and reused.
+// This may be a simple SecPolicyRef or a CFArray of policies.
+// The caller owns the return value.
 //
 //
-SecPolicyRef SecStaticCode::verificationPolicy()
+static SecPolicyRef makeCRLPolicy()
+{
+       CFRef<SecPolicyRef> policy;
+       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
+       CSSM_APPLE_TP_CRL_OPTIONS options;
+       memset(&options, 0, sizeof(options));
+       options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
+       options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
+       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
+       MacOSError::check(SecPolicySetValue(policy, &optData));
+       return policy.yield();
+}
+
+static SecPolicyRef makeOCSPPolicy()
 {
 {
-       if (!mPolicy)
-               MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
-                       &CSSMOID_APPLE_TP_CODE_SIGNING, &mPolicy.aref()));
-       return mPolicy;
+       CFRef<SecPolicyRef> policy;
+       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
+       CSSM_APPLE_TP_OCSP_OPTIONS options;
+       memset(&options, 0, sizeof(options));
+       options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
+       options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
+       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
+       MacOSError::check(SecPolicySetValue(policy, &optData));
+       return policy.yield();
+}
+
+CFTypeRef SecStaticCode::verificationPolicy(SecCSFlags flags)
+{
+       CFRef<SecPolicyRef> core;
+       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
+                       &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
+       if (flags & kSecCSEnforceRevocationChecks) {
+               CFRef<SecPolicyRef> crl = makeCRLPolicy();
+               CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
+               return makeCFArray(3, core.get(), crl.get(), ocsp.get());
+       } else {
+               return core.yield();
+       }
 }
 
 
 }
 
 
@@ -487,29 +544,43 @@ void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus
 //
 void SecStaticCode::validateExecutable()
 {
 //
 void SecStaticCode::validateExecutable()
 {
-       DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
-               (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
-       const CodeDirectory *cd = this->codeDirectory();
-       if (!cd)
-               MacOSError::throwMe(errSecCSUnsigned);
-       AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
-       fd.fcntl(F_NOCACHE, true);              // turn off page caching (one-pass)
-       if (Universal *fat = mRep->mainExecutableImage())
-               fd.seek(fat->archOffset());
-       size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
-       size_t remaining = cd->codeLimit;
-       for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
-               size_t size = min(remaining, pageSize);
-               if (!cd->validateSlot(fd, size, slot)) {
-                       CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
-                       mExecutableValidated = true;    // we tried
-                       mExecutableValid = false;               // it failed
-                       MacOSError::throwMe(errSecCSSignatureFailed);
+       if (!validatedExecutable()) {
+               try {
+                       DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
+                               (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
+                       const CodeDirectory *cd = this->codeDirectory();
+                       if (!cd) 
+                               MacOSError::throwMe(errSecCSUnsigned);
+                       AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
+                       fd.fcntl(F_NOCACHE, true);              // turn off page caching (one-pass)
+                       if (Universal *fat = mRep->mainExecutableImage())
+                               fd.seek(fat->archOffset());
+                       size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
+                       size_t remaining = cd->codeLimit;
+                       for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
+                               size_t size = min(remaining, pageSize);
+                               if (!cd->validateSlot(fd, size, slot)) {
+                                       CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
+                                       MacOSError::throwMe(errSecCSSignatureFailed);
+                               }
+                               remaining -= size;
+                       }
+                       mExecutableValidated = true;
+                       mExecutableValidResult = noErr;
+               } catch (const CommonError &err) {
+                       mExecutableValidated = true;
+                       mExecutableValidResult = err.osStatus();
+                       throw;
+               } catch (...) {
+                       secdebug("staticCode", "%p executable validation threw non-common exception", this);
+                       mExecutableValidated = true;
+                       mExecutableValidResult = errSecCSInternalError;
+                       throw;
                }
                }
-               remaining -= size;
        }
        }
-       mExecutableValidated = true;    // we tried
-       mExecutableValid = true;                // it worked
+       assert(validatedExecutable());
+       if (mExecutableValidResult != noErr)
+               MacOSError::throwMe(mExecutableValidResult);
 }
 
 
 }
 
 
@@ -522,48 +593,68 @@ void SecStaticCode::validateExecutable()
 //
 void SecStaticCode::validateResources()
 {
 //
 void SecStaticCode::validateResources()
 {
-       // sanity first
-       CFDictionaryRef sealedResources = resourceDictionary();
-       if (this->resourceBase())               // disk has resources
-               if (sealedResources)
-                       /* go to work below */;
-               else
-                       MacOSError::throwMe(errSecCSResourcesNotFound);
-       else                                                    // disk has no resources
-               if (sealedResources)
-                       MacOSError::throwMe(errSecCSResourcesNotFound);
-               else
-                       return;                                 // no resources, not sealed - fine (no work)
-
-       // found resources, and they are sealed
-       CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
-       CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
-       DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
-               (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
-
-       // make a shallow copy of the ResourceDirectory so we can "check off" what we find
-       CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
-
-       // scan through the resources on disk, checking each against the resourceDirectory
-       CollectingContext ctx(*this);           // collect all failures in here
-       ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
-       mRep->adjustResources(resources);
-       string path;
-       ResourceBuilder::Rule *rule;
-
-       while (resources.next(path, rule)) {
-               validateResource(path, ctx);
-               CFDictionaryRemoveValue(resourceMap, CFTempString(path));
-       }
-       
-       if (CFDictionaryGetCount(resourceMap) > 0) {
-               secdebug("staticCode", "%p sealed resource(s) not found in code", this);
-               CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, &ctx);
+       if (!validatedResources()) {
+               try {
+                       // sanity first
+                       CFDictionaryRef sealedResources = resourceDictionary();
+                       if (this->resourceBase())               // disk has resources
+                               if (sealedResources)
+                                       /* go to work below */;
+                               else
+                                       MacOSError::throwMe(errSecCSResourcesNotFound);
+                       else                                                    // disk has no resources
+                               if (sealedResources)
+                                       MacOSError::throwMe(errSecCSResourcesNotFound);
+                               else
+                                       return;                                 // no resources, not sealed - fine (no work)
+               
+                       // found resources, and they are sealed
+                       CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
+                       CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
+                       DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
+                               (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
+               
+                       // make a shallow copy of the ResourceDirectory so we can "check off" what we find
+                       CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
+               
+                       // scan through the resources on disk, checking each against the resourceDirectory
+                       mResourcesValidContext = new CollectingContext(*this);          // collect all failures in here
+                       ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
+                       mRep->adjustResources(resources);
+                       string path;
+                       ResourceBuilder::Rule *rule;
+               
+                       while (resources.next(path, rule)) {
+                               validateResource(path, *mResourcesValidContext);
+                               CFDictionaryRemoveValue(resourceMap, CFTempString(path));
+                       }
+                       
+                       if (CFDictionaryGetCount(resourceMap) > 0) {
+                               secdebug("staticCode", "%p sealed resource(s) not found in code", this);
+                               CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
+                       }
+                       
+                       // now check for any errors found in the reporting context
+                       mResourcesValidated = true;
+                       if (mResourcesValidContext->osStatus() != noErr)
+                               mResourcesValidContext->throwMe();
+
+               } catch (const CommonError &err) {
+                       mResourcesValidated = true;
+                       mResourcesValidResult = err.osStatus();
+                       throw;
+               } catch (...) {
+                       secdebug("staticCode", "%p executable validation threw non-common exception", this);
+                       mResourcesValidated = true;
+                       mResourcesValidResult = errSecCSInternalError;
+                       throw;
+               }
        }
        }
-       
-       // now check for any errors found in the reporting context
-       if (ctx)
-               ctx.throwMe();
+       assert(!validatedResources());
+       if (mResourcesValidResult)
+               MacOSError::throwMe(mResourcesValidResult);
+       if (mResourcesValidContext->osStatus() != noErr)
+               mResourcesValidContext->throwMe();
 }
 
 
 }
 
 
@@ -1149,7 +1240,7 @@ void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef ty
 void SecStaticCode::CollectingContext::throwMe()
 {
        assert(mStatus != noErr);
 void SecStaticCode::CollectingContext::throwMe()
 {
        assert(mStatus != noErr);
-       throw CSError(mStatus, mCollection.yield());
+       throw CSError(mStatus, mCollection.retain());
 }
 
 
 }
 
 
index da616c82b797b6daf0e7d1c69e43c8b2ca65c75a..edb92a18b65dabdb919b088f201691fa6affce53 100644 (file)
@@ -80,6 +80,7 @@ protected:
                CollectingContext(SecStaticCode &c) : code(c), mStatus(noErr) { }
                void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value);
                
                CollectingContext(SecStaticCode &c) : code(c), mStatus(noErr) { }
                void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value);
                
+               OSStatus osStatus()             { return mStatus; }
                operator OSStatus () const              { return mStatus; }
                void throwMe() __attribute__((noreturn));
                
                operator OSStatus () const              { return mStatus; }
                void throwMe() __attribute__((noreturn));
                
@@ -135,9 +136,13 @@ public:
        bool validated() const  { return mValidated; }
        bool valid() const
                { assert(validated()); return mValidated && (mValidationResult == noErr); }
        bool validated() const  { return mValidated; }
        bool valid() const
                { assert(validated()); return mValidated && (mValidationResult == noErr); }
+       bool validatedExecutable() const        { return mExecutableValidated; }
+       bool validatedResources() const { return mResourcesValidated; }
+
        
        void validateDirectory();
        void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
        
        void validateDirectory();
        void validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail = errSecCSSignatureFailed);
+       void validateNonResourceComponents();
        void validateResources();
        void validateExecutable();
        
        void validateResources();
        void validateExecutable();
        
@@ -163,7 +168,7 @@ public:
 protected:
        CFDictionaryRef getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail); // component value as a dictionary
        bool verifySignature();
 protected:
        CFDictionaryRef getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail); // component value as a dictionary
        bool verifySignature();
-       SecPolicyRef verificationPolicy();
+       CFTypeRef verificationPolicy(SecCSFlags flags);
 
        void defaultDesignatedAppleAnchor(Requirement::Maker &maker);
        void defaultDesignatedNonAppleAnchor(Requirement::Maker &maker);
 
        void defaultDesignatedAppleAnchor(Requirement::Maker &maker);
        void defaultDesignatedNonAppleAnchor(Requirement::Maker &maker);
@@ -182,7 +187,12 @@ private:
        
        // static executable validation state (nested within mValidated/mValid)
        bool mExecutableValidated;                      // tried to validate executable file
        
        // static executable validation state (nested within mValidated/mValid)
        bool mExecutableValidated;                      // tried to validate executable file
-       bool mExecutableValid;                          // outcome if mExecutableValidated
+       OSStatus mExecutableValidResult;                // outcome if mExecutableValidated
+
+       // static resource validation state (nested within mValidated/mValid)
+       bool mResourcesValidated;                       // tried to validate resources
+       OSStatus mResourcesValidResult;                 // outcome if mResourceValidated or..
+       CollectingContext *mResourcesValidContext;      // other outcome
 
        // cached contents
        CFRef<CFDataRef> mDir;                          // code directory data
 
        // cached contents
        CFRef<CFDataRef> mDir;                          // code directory data
@@ -204,9 +214,6 @@ private:
        CFRef<SecTrustRef> mTrust;                      // outcome of crypto validation (valid or not)
        CFRef<CFArrayRef> mCertChain;
        CSSM_TP_APPLE_EVIDENCE_INFO *mEvalDetails;
        CFRef<SecTrustRef> mTrust;                      // outcome of crypto validation (valid or not)
        CFRef<CFArrayRef> mCertChain;
        CSSM_TP_APPLE_EVIDENCE_INFO *mEvalDetails;
-       
-       // cached verification policy
-       CFRef<SecPolicyRef> mPolicy;
 };
 
 
 };
 
 
index 757bbbc25d32237ec4a4f1bc72b425ef6776d173..09d5ca22fed9725e52892ea99b57b642849e81cb 100644 (file)
@@ -70,7 +70,7 @@ 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
        // conventional executable bundle: CFBundle identifies an executable for us
        if (mMainExecutableURL.take(CFBundleCopyExecutableURL(mBundle))) {
                // conventional executable bundle
@@ -100,6 +100,7 @@ void BundleDiskRep::setup(const Context *ctx)
        CFRef<CFURLRef> infoURL = _CFBundleCopyInfoPlistURL(mBundle);
        if (!infoURL)
                MacOSError::throwMe(errSecCSBadBundleFormat);
        CFRef<CFURLRef> infoURL = _CFBundleCopyInfoPlistURL(mBundle);
        if (!infoURL)
                MacOSError::throwMe(errSecCSBadBundleFormat);
+       
 
        // focus on the Info.plist (which we know exists) as the nominal "main executable" file
        if ((mMainExecutableURL = _CFBundleCopyInfoPlistURL(mBundle))) {
 
        // focus on the Info.plist (which we know exists) as the nominal "main executable" file
        if ((mMainExecutableURL = _CFBundleCopyInfoPlistURL(mBundle))) {
index 3eedb3c784ddc82b91fef39656844161f0fac4a6..9596e8f3f6578a296ce7747370e432d7c431d242 100644 (file)
@@ -35,6 +35,18 @@ namespace Security {
 namespace CodeSigning {
 
 
 namespace CodeSigning {
 
 
+//
+// Highest understood special slot in this CodeDirectory.
+//
+CodeDirectory::SpecialSlot CodeDirectory::maxSpecialSlot() const
+{
+       SpecialSlot slot = this->nSpecialSlots;
+       if (slot > cdSlotMax)
+               slot = cdSlotMax;
+       return slot;
+}
+
+
 //
 // Canonical filesystem names for select slot numbers.
 // These are variously used for filenames, extended attribute names, etc.
 //
 // Canonical filesystem names for select slot numbers.
 // These are variously used for filenames, extended attribute names, etc.
index b6f8b0ede4b584412fe97cc1a8835a799083936e..1028fd7f7d86f3300715888de5fd713353300619 100644 (file)
@@ -198,6 +198,8 @@ public:
        char *identifier() { return at<char>(identOffset); }
 
        // main hash array access
        char *identifier() { return at<char>(identOffset); }
 
        // main hash array access
+       SpecialSlot maxSpecialSlot() const;
+               
        unsigned char *operator [] (Slot slot)
        {
                assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
        unsigned char *operator [] (Slot slot)
        {
                assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
index 72018c3c91955d05d400910f43f85490e2abde76..b748dea1af5bb164fa485336a288589bb131efc4 100644 (file)
@@ -52,6 +52,37 @@ static const char *dbPath()
 }
 
 
 }
 
 
+//
+// Help mapping API-ish CFString keys to more convenient internal enumerations
+//
+typedef struct {
+       const CFStringRef &cstring;
+       uint enumeration;
+} StringMap;
+
+static uint mapEnum(CFDictionaryRef context, CFStringRef attr, const StringMap *map, uint value = 0)
+{
+       if (context)
+               if (CFTypeRef value = CFDictionaryGetValue(context, attr))
+                       for (const StringMap *mp = map; mp->cstring; ++mp)
+                               if (CFEqual(mp->cstring, value))
+                                       return mp->enumeration;
+       return value;
+}
+
+static const StringMap mapType[] = {
+       { kSecAssessmentOperationTypeExecute, kAuthorityExecute },
+       { kSecAssessmentOperationTypeInstall, kAuthorityInstall },
+       { kSecAssessmentOperationTypeOpenDocument, kAuthorityOpenDoc },
+       { NULL }
+};
+
+AuthorityType typeFor(CFDictionaryRef context, AuthorityType type /* = kAuthorityInvalid */)
+{
+       return mapEnum(context, kSecAssessmentContextKeyOperation, mapType, 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 (creating it if necessary and possible).
 // Note that this isn't creating the schema; we do that on first write.
@@ -75,48 +106,64 @@ bool PolicyDatabase::checkCache(CFURLRef path, AuthorityType type, CFMutableDict
        if (type != kAuthorityExecute)
                return false;
        
        if (type != kAuthorityExecute)
                return false;
        
-       SecCSFlags validationFlags = kSecCSDefaultFlags;
-       if (overrideAssessment())       // we'll force the verdict to 'pass' at the end, so don't sweat validating code
-               validationFlags = kSecCSBasicValidateOnly;
-
        CFRef<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
        CFRef<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
-       if (SecStaticCodeCheckValidity(code, validationFlags, NULL) != noErr)
+       if (SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, NULL) != noErr)
                return false;   // quick pass - any error is a cache miss
        CFRef<CFDictionaryRef> info;
        MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
        CFDataRef cdHash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
        
        // check the cache table for a fast match
                return false;   // quick pass - any error is a cache miss
        CFRef<CFDictionaryRef> info;
        MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
        CFDataRef cdHash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
        
        // check the cache table for a fast match
-       SQLite::Statement cached(*this, "SELECT allow, expires, label, authority FROM object WHERE type = ?1 and hash = ?2;");
-       cached.bind(1).integer(type);
-       cached.bind(2) = cdHash;
+       SQLite::Statement cached(*this, "SELECT object.allow, authority.label, authority FROM object, authority"
+               " WHERE object.authority = authority.id AND object.type = :type AND object.hash = :hash AND authority.disabled = 0"
+               " AND JULIANDAY('now') < object.expires;");
+       cached.bind(":type").integer(type);
+       cached.bind(":hash") = cdHash;
        if (cached.nextRow()) {
                bool allow = int(cached[0]);
        if (cached.nextRow()) {
                bool allow = int(cached[0]);
-               const char *label = cached[2];
-               SQLite::int64 auth = cached[3];
-               bool valid = true;
-               if (SQLite3::int64 expires = cached[1])
-                       valid = time(NULL) <= expires;
-               if (valid) {
-                       SYSPOLICY_ASSESS_CACHE_HIT();
-                       cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
-                       PolicyEngine::addAuthority(result, label, auth, kCFBooleanTrue);
-                       return true;
-               }
+               const char *label = cached[1];
+               SQLite::int64 auth = cached[2];
+               SYSPOLICY_ASSESS_CACHE_HIT();
+
+               // If its allowed, lets do a full validation unless if
+               // we are overriding the assessement, since that force
+               // the verdict to 'pass' at the end
+
+               if (allow && !overrideAssessment())
+                   MacOSError::check(SecStaticCodeCheckValidity(code, kSecCSDefaultFlags, NULL));
+
+               cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
+               PolicyEngine::addAuthority(result, label, auth, kCFBooleanTrue);
+               return true;
        }
        return false;
 }
 
 
 //
        }
        return false;
 }
 
 
 //
-// Purge the object cache of all expired entries
+// Purge the object cache of all expired entries.
+// These are meant to run within the caller's transaction.
 //
 //
-void PolicyDatabase::purge(const char *table)
+void PolicyDatabase::purgeAuthority()
+{
+       SQLite::Statement cleaner(*this,
+               "DELETE FROM authority WHERE expires <= JULIANDAY('now');");
+       cleaner.execute();
+}
+
+void PolicyDatabase::purgeObjects()
+{
+       SQLite::Statement cleaner(*this,
+               "DELETE FROM object WHERE expires <= JULIANDAY('now');");
+       cleaner.execute();
+}
+
+void PolicyDatabase::purgeObjects(double priority)
 {
        SQLite::Statement cleaner(*this,
 {
        SQLite::Statement cleaner(*this,
-               "DELETE FROM ?1 WHERE expires < DATE_TIME('now');");
-       cleaner.bind(1) = table;
+               "DELETE FROM object WHERE expires <= JULIANDAY('now') OR (SELECT priority FROM authority WHERE id = object.authority) <= :priority;");
+       cleaner.bind(":priority") = priority;
        cleaner.execute();
 }
 
        cleaner.execute();
 }
 
index 1b06b4a5e6ade3c07fde10f8186c47ace3ce9859..a112c16e3944c79d47b9c92af45911ae28db7f75 100644 (file)
@@ -38,6 +38,8 @@ namespace SQLite = SQLite3;
 static const char defaultDatabase[] = "/var/db/SystemPolicy";
 static const char visibleSecurityFlagFile[] = "/var/db/.sp_visible";
 
 static const char defaultDatabase[] = "/var/db/SystemPolicy";
 static const char visibleSecurityFlagFile[] = "/var/db/.sp_visible";
 
+static const double never = 5000000;   // expires never (i.e. in the year 8977)
+
 
 typedef SHA1::SDigest ObjectHash;
 
 
 typedef SHA1::SDigest ObjectHash;
 
@@ -51,6 +53,22 @@ enum {
 };
 
 
 };
 
 
+//
+// Defined flags for authority flags column
+//
+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
+};
+
+
+//
+// Mapping/translation to/from API space
+//
+AuthorityType typeFor(CFDictionaryRef context, AuthorityType type = kAuthorityInvalid);
+
+
 //
 // An open policy database.
 // Usually read-only, but can be opened for write by privileged callers.
 //
 // An open policy database.
 // Usually read-only, but can be opened for write by privileged callers.
@@ -66,7 +84,9 @@ public:
        bool checkCache(CFURLRef path, AuthorityType type, CFMutableDictionaryRef result);
 
 public:
        bool checkCache(CFURLRef path, AuthorityType type, CFMutableDictionaryRef result);
 
 public:
-       void purge(const char *table);
+       void purgeAuthority();
+       void purgeObjects();
+       void purgeObjects(double priority);
 };
 
 
 };
 
 
index 99e087b3862cc6c0071152dda828fcd999feb28a..4ebe2a029d131170d8f1e3ed17fdf843f56b5cd2 100644 (file)
 #include <Security/Security.h>
 #include <Security/SecRequirementPriv.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/Security.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 "quarantine++.h"
 
 #include <CoreServices/CoreServicesPriv.h>
 #undef check // Macro! Yech.
 
 #include <CoreServices/CoreServicesPriv.h>
 #undef check // Macro! Yech.
 namespace Security {
 namespace CodeSigning {
 
 namespace Security {
 namespace CodeSigning {
 
-static const time_t NEGATIVE_HOLD = 60;        // seconds for negative cache entries
+static const double NEGATIVE_HOLD = 60.0/86400;        // 60 seconds to cache negative outcomes
+
+
+static void authorizeUpdate(SecCSFlags flags, CFDictionaryRef context);
+static void normalizeTarget(CFRef<CFTypeRef> &target, CFDictionary &context, bool signUnsigned = false);
 
 
 //
 
 
 //
@@ -81,64 +91,88 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti
        CFRef<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
        
        CFRef<SecStaticCodeRef> code;
        MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
        
-       if (flags & kSecAssessmentFlagRequestOrigin) {
-               CFRef<CFDictionaryRef> info;
-               MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
-               if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates)))
-                       setOrigin(chain, result);
-       }
-       
-       SecCSFlags validationFlags = kSecCSDefaultFlags;
-       if (overrideAssessment())       // we'll force the verdict to 'pass' at the end, so don't sweat validating code
-               validationFlags = kSecCSBasicValidateOnly;
+       const SecCSFlags validationFlags = kSecCSEnforceRevocationChecks;
 
        SQLite::Statement query(*this,
 
        SQLite::Statement query(*this,
-               "SELECT allow, requirement, inhibit_cache, expires, id, label FROM authority WHERE type = ?1 ORDER BY priority DESC;");
-       query.bind(1).integer(type);
+               "SELECT allow, requirement, id, label, expires, flags, disabled FROM scan_authority"
+               " WHERE type = :type"
+               " ORDER BY priority DESC;");
+       query.bind(":type").integer(type);
+       SQLite3::int64 latentID = 0;            // first (highest priority) disabled matching ID
+       std::string latentLabel;                        // ... and associated label, if any
        while (query.nextRow()) {
                bool allow = int(query[0]);
                const char *reqString = query[1];
        while (query.nextRow()) {
                bool allow = int(query[0]);
                const char *reqString = query[1];
-               bool inhibit_cache = query[2];
-               time_t expires = SQLite::int64(query[3]);
-               SQLite3::int64 id = query[4];
-               const char *label = query[5];
-               
-               if (expires && expires < time(NULL))    // no longer active
-                       continue;
+               SQLite3::int64 id = query[2];
+               const char *label = query[3];
+               double expires = query[4];
+               sqlite3_int64 ruleFlags = query[5];
+               SQLite3::int64 disabled = query[6];
                
                CFRef<SecRequirementRef> requirement;
                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
                
                CFRef<SecRequirementRef> requirement;
                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
-               switch (OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags, requirement)) {
+
+               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);
+
+               switch (rc) {
                case noErr: // success
                        break;
                case errSecCSUnsigned:
                        cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
                        addAuthority(result, "no usable signature");
                        return;
                case noErr: // success
                        break;
                case errSecCSUnsigned:
                        cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
                        addAuthority(result, "no usable signature");
                        return;
-               case errSecCSSignatureFailed:
-               case errSecCSSignatureInvalid:
-               case errSecCSSignatureUnsupported:
-               case errSecCSSignatureNotVerifiable:
-                       cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
-                       addAuthority(result, "invalid signature");
-                       return;
                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);
                }
                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 (!inhibit_cache && !(flags & kSecAssessmentFlagNoCache))     // cache inhibit
-                               this->recordOutcome(code, allow, type, expires, id, label);
+               if (disabled) {
+                       if (latentID == 0) {
+                               latentID = id;
+                               if (label)
+                                       latentLabel = label;
+                       }
+                       continue;       // the loop
+               }
+       
+               CFRef<CFDictionaryRef> info;    // as needed
+               if (flags & kSecAssessmentFlagRequestOrigin) {
+                       if (!info)
+                               MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
+                       if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates)))
+                               setOrigin(chain, result);
+               }
+               if (!(ruleFlags & kAuthorityFlagInhibitCache) && !(flags & kSecAssessmentFlagNoCache)) {        // cache inhibit
+                       if (!info)
+                               MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
+                       if (SecTrustRef trust = SecTrustRef(CFDictionaryGetValue(info, kSecCodeInfoTrust))) {
+                               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);
+                               }
+                       }
+               }
                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
+       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 (!(flags & kSecAssessmentFlagNoCache))
        if (!(flags & kSecAssessmentFlagNoCache))
-               this->recordOutcome(code, false, type, time(NULL) + NEGATIVE_HOLD, 0, NULL);
+               this->recordOutcome(code, false, type, this->julianNow() + NEGATIVE_HOLD, latentID);
        cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
        cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
-       addAuthority(result, NULL);
+       addAuthority(result, latentLabel.c_str(), latentID);
 }
 
 
 }
 
 
@@ -147,10 +181,14 @@ void PolicyEngine::evaluateCode(CFURLRef path, SecAssessmentFlags flags, CFDicti
 // Certs passed from caller (untrusted), no policy engine yet, no caching (since untrusted).
 // The current "policy" is to trust any proper signature.
 //
 // Certs passed from caller (untrusted), no policy engine yet, no caching (since untrusted).
 // The current "policy" is to trust any proper signature.
 //
+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;
        
+       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()) {
        Xar xar(cfString(path).c_str());
        if (xar) {
                if (!xar.isSigned()) {
@@ -160,11 +198,11 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
                        return;
                }
                if (CFRef<CFArrayRef> certs = xar.copyCertChain()) {
                        return;
                }
                if (CFRef<CFArrayRef> certs = xar.copyCertChain()) {
-                       CFRef<SecPolicyRef> policy = SecPolicyCreateBasicX509();
+                       CFRef<CFTypeRef> policy = installerPolicy();
                        CFRef<SecTrustRef> trust;
                        MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref()));
 //                     MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors
                        CFRef<SecTrustRef> trust;
                        MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref()));
 //                     MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors
-                       MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionImplicitAnchors));
+                       MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors));
 
                        SecTrustResultType trustResult;
                        MacOSError::check(SecTrustEvaluate(trust, &trustResult));
 
                        SecTrustResultType trustResult;
                        MacOSError::check(SecTrustEvaluate(trust, &trustResult));
@@ -189,18 +227,17 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
                        }
 
                        SQLite::Statement query(*this,
                        }
 
                        SQLite::Statement query(*this,
-                               "SELECT allow, requirement, inhibit_cache, expires, id, label FROM authority WHERE type = ?1 ORDER BY priority DESC;");
-                       query.bind(1).integer(type);
+                               "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];
                        while (query.nextRow()) {
                                bool allow = int(query[0]);
                                const char *reqString = query[1];
-                               bool inhibit_cache = query[2];
-                               time_t expires = SQLite::int64(query[3]);
-                               SQLite3::int64 id = query[4];
-                               const char *label = query[5];
-                               
-                               if (expires && expires < time(NULL))    // no longer active
-                                       continue;
+                               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()));
                
                                CFRef<SecRequirementRef> requirement;
                                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
@@ -212,6 +249,14 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
                                default: // broken in some way; all tests will fail like this so bail out
                                        MacOSError::throwMe(rc);
                                }
                                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);
                                // 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);
@@ -222,7 +267,45 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
        
        // no applicable authority. Deny by default
        cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
        
        // no applicable authority. Deny by default
        cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
-       addAuthority(result, NULL);
+       addAuthority(result, latentLabel.c_str(), latentID);
+}
+
+
+//
+// Create a suitable policy array for verification of installer signatures.
+//
+static SecPolicyRef makeCRLPolicy()
+{
+       CFRef<SecPolicyRef> policy;
+       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
+       CSSM_APPLE_TP_CRL_OPTIONS options;
+       memset(&options, 0, sizeof(options));
+       options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
+       options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
+       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
+       MacOSError::check(SecPolicySetValue(policy, &optData));
+       return policy.yield();
+}
+
+static SecPolicyRef makeOCSPPolicy()
+{
+       CFRef<SecPolicyRef> policy;
+       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
+       CSSM_APPLE_TP_OCSP_OPTIONS options;
+       memset(&options, 0, sizeof(options));
+       options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
+       options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
+       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
+       MacOSError::check(SecPolicySetValue(policy, &optData));
+       return policy.yield();
+}
+
+static CFTypeRef installerPolicy()
+{
+       CFRef<SecPolicyRef> base = SecPolicyCreateBasicX509();
+       CFRef<SecPolicyRef> crl = makeCRLPolicy();
+       CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
+       return makeCFArray(3, base.get(), crl.get(), ocsp.get());
 }
 
 
 }
 
 
@@ -239,24 +322,31 @@ void PolicyEngine::evaluateDocOpen(CFURLRef path, SecAssessmentFlags flags, CFDi
                                || CFEqual(riskCategory, kLSRiskCategoryUnknown)
                                || CFEqual(riskCategory, kLSRiskCategoryMayContainUnsafeExecutable)) {
                                cfadd(result, "{%O=#T}", kSecAssessmentAssessmentVerdict);
                                || 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)) {
+                               cfadd(result, "{%O=#T}", kSecAssessmentAssessmentVerdict);
+                               addAuthority(result, "Prior Assessment");
                        } else {
                                cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
                        } else {
                                cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
+                               addAuthority(result, "_XProtect");
                        }
                        }
-                       addAuthority(result, "_XProtect");
                        addToAuthority(result, kLSDownloadRiskCategoryKey, riskCategory);
                        return;
                }
        }
        // insufficient information from LS - deny by default
                        addToAuthority(result, kLSDownloadRiskCategoryKey, riskCategory);
                        return;
                }
        }
        // insufficient information from LS - deny by default
-       cfadd(result, "{%O=%F}", kSecAssessmentAssessmentVerdict);
-       addAuthority(result, NULL);
+       cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
+       addAuthority(result, "Insufficient Context");
 }
 
 
 }
 
 
+//
+// Result-creation helpers
+//
 void PolicyEngine::addAuthority(CFMutableDictionaryRef parent, const char *label, SQLite::int64 row, CFTypeRef cacheInfo)
 {
        CFRef<CFMutableDictionaryRef> auth = makeCFMutableDictionary();
 void PolicyEngine::addAuthority(CFMutableDictionaryRef parent, const char *label, SQLite::int64 row, CFTypeRef cacheInfo)
 {
        CFRef<CFMutableDictionaryRef> auth = makeCFMutableDictionary();
-       if (label)
+       if (label && label[0])
                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));
@@ -276,47 +366,180 @@ void PolicyEngine::addToAuthority(CFMutableDictionaryRef parent, CFStringRef key
 //
 // Add a rule to the policy database
 //
 //
 // Add a rule to the policy database
 //
-bool PolicyEngine::add(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+bool PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
 {
 {
-       if (type != kAuthorityExecute)
-               MacOSError::throwMe(errSecCSUnimplemented);
+       // default type to execution
+       if (type == kAuthorityInvalid)
+               type = kAuthorityExecute;
 
 
-       double priority = 0;
-       string label;
-       if (context) {
-               if (CFTypeRef pri = CFDictionaryGetValue(context, kSecAssessmentUpdateKeyPriority)) {
-                       if (CFGetTypeID(pri) != CFNumberGetTypeID())
-                               MacOSError::throwMe(errSecCSBadDictionaryFormat);
-                       CFNumberGetValue(CFNumberRef(pri), kCFNumberDoubleType, &priority);
-               }
-               if (CFTypeRef lab = CFDictionaryGetValue(context, kSecAssessmentUpdateKeyLabel)) {
-                       if (CFGetTypeID(lab) != CFStringGetTypeID())
-                               MacOSError::throwMe(errSecCSBadDictionaryFormat);
-                       label = cfString(CFStringRef(lab));
-               }
+       authorizeUpdate(flags, context);
+       CFDictionary ctx(context, errSecCSInvalidAttributeValues);
+       CFCopyRef<CFTypeRef> target = inTarget;
+       
+       if (type == kAuthorityOpenDoc) {
+               // handle document-open differently: use quarantine flags for whitelisting
+               if (!target || CFGetTypeID(target) != CFURLGetTypeID())
+                       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"));
+       }
+       
+       // 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);
 
 
-       CFRef<SecStaticCodeRef> code;
-       MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
-       CFRef<CFDictionaryRef> info;
-       MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
-       CFRef<SecRequirementRef> dr;
-       MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, &dr.aref()));
+       double priority = 0;
+       string label;
+       bool allow = true;
+       double expires = never;
+       string remarks;
+       
+       if (CFNumberRef pri = ctx.get<CFNumberRef>(kSecAssessmentUpdateKeyPriority))
+               CFNumberGetValue(pri, kCFNumberDoubleType, &priority);
+       if (CFStringRef lab = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyLabel))
+               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;
+       if (CFBooleanRef allowing = ctx.get<CFBooleanRef>(kSecAssessmentUpdateKeyAllow))
+               allow = allowing == kCFBooleanTrue;
+       if (CFStringRef rem = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyRemarks))
+               remarks = cfString(rem);
 
        CFRef<CFStringRef> requirementText;
 
        CFRef<CFStringRef> requirementText;
-       MacOSError::check(SecRequirementCopyString(dr, kSecCSDefaultFlags, &requirementText.aref()));
+       MacOSError::check(SecRequirementCopyString(target.as<SecRequirementRef>(), kSecCSDefaultFlags, &requirementText.aref()));
+       SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "add_rule");
        SQLite::Statement insert(*this,
        SQLite::Statement insert(*this,
-               "INSERT INTO authority (type, allow, requirement, priority, label) VALUES (?1, ?2, ?3, ?4, ?5);");
-       insert.bind(1).integer(type);
-       insert.bind(2).integer(true);
-       insert.bind(3) = requirementText.get();
-       insert.bind(4) = priority;
-       insert.bind(5) = label.c_str();
+               "INSERT INTO authority (type, allow, requirement, priority, label, expires, remarks)"
+               "       VALUES (:type, :allow, :requirement, :priority, :label, :expires, :remarks);");
+       insert.bind(":type").integer(type);
+       insert.bind(":allow").integer(allow);
+       insert.bind(":requirement") = requirementText.get();
+       insert.bind(":priority") = priority;
+       if (!label.empty())
+               insert.bind(":label") = label.c_str();
+       insert.bind(":expires") = expires;
+       if (!remarks.empty())
+               insert.bind(":remarks") = remarks.c_str();
        insert.execute();
        insert.execute();
+       this->purgeObjects(priority);
+       xact.commit();
+       notify_post(kNotifySecAssessmentUpdate);
        return true;
 }
 
 
        return true;
 }
 
 
+//
+// Perform an action on existing authority rule(s)
+//
+bool PolicyEngine::manipulateRules(const std::string &stanza,
+       CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
+{
+       authorizeUpdate(flags, context);
+       CFDictionary ctx(context, errSecCSInvalidAttributeValues);
+       CFCopyRef<CFTypeRef> target = inTarget;
+       normalizeTarget(target, ctx);
+
+       string label;
+       
+       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 (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);
+               }
+               action.bind(":label") = label.c_str();
+       } else if (CFGetTypeID(target) == CFNumberGetTypeID()) {
+               action.query(stanza + " WHERE id = :id");
+               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.bind(":type").integer(type);
+               action.bind(":requirement") = requirementText.get();
+       } else
+               MacOSError::throwMe(errSecCSInvalidObjectRef);
+
+       action.execute();
+       unsigned int changes = this->changes(); // latch change count
+       // We MUST purge objects with priority <= MAX(priority of any changed rules);
+       // but for now we just get lazy and purge them ALL.
+       if (changes) {
+               this->purgeObjects(1.0E100);
+               xact.commit();
+               notify_post(kNotifySecAssessmentUpdate);
+               return true;
+       }
+       // 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
 //
@@ -333,30 +556,98 @@ void PolicyEngine::setOrigin(CFArrayRef chain, CFMutableDictionaryRef result)
 //
 // 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, time_t expires, int authority, const char *label)
+void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, int authority)
 {
        CFRef<CFDictionaryRef> info;
        MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
        CFDataRef cdHash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
 {
        CFRef<CFDictionaryRef> info;
        MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
        CFDataRef cdHash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
+       assert(cdHash);         // was signed
        CFRef<CFURLRef> path;
        MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
        CFRef<CFURLRef> path;
        MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
-       //@@@ should really be OR REPLACE IF EXPIRED... does it matter? @@@
+       assert(expires);
        SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "caching");
        SQLite::Statement insert(*this,
        SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "caching");
        SQLite::Statement insert(*this,
-               "INSERT OR REPLACE INTO object (type, allow, hash, expires, authority, label, path) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)");
-       insert.bind(1).integer(type);
-       insert.bind(2).integer(allow);
-       insert.bind(3) = cdHash;
-       if (expires)
-               insert.bind(4).integer(expires);
-       insert.bind(5).integer(authority);
-       if (label)
-               insert.bind(6) = label;
-       insert.bind(7) = cfString(path).c_str();
+               "INSERT OR REPLACE INTO object (type, allow, hash, expires, path, authority)"
+               "       VALUES (:type, :allow, :hash, :expires, :path,"
+               "       CASE :authority WHEN 0 THEN (SELECT id FROM authority WHERE label = 'No Matching Rule') ELSE :authority END"
+               "       );");
+       insert.bind(":type").integer(type);
+       insert.bind(":allow").integer(allow);
+       insert.bind(":hash") = cdHash;
+       insert.bind(":expires") = expires;
+       insert.bind(":path") = cfString(path).c_str();
+       insert.bind(":authority").integer(authority);
        insert.execute();
        xact.commit();
 }
 
 
        insert.execute();
        xact.commit();
 }
 
 
+//
+// Perform update authorization processing.
+// Throws an exception if authorization is denied.
+//
+static void authorizeUpdate(SecCSFlags flags, CFDictionaryRef context)
+{
+       AuthorizationRef authorization = NULL;
+       
+       if (context)
+               if (CFTypeRef authkey = CFDictionaryGetValue(context, kSecAssessmentUpdateKeyAuthorization))
+                       if (CFGetTypeID(authkey) == CFDataGetTypeID()) {
+                               CFDataRef authdata = CFDataRef(authkey);
+                               MacOSError::check(AuthorizationCreateFromExternalForm((AuthorizationExternalForm *)CFDataGetBytePtr(authdata), &authorization));
+                       }
+       if (authorization == NULL)
+               MacOSError::check(AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorization));
+       
+       AuthorizationItem right[] = {
+               { "com.apple.security.assessment.update", 0, NULL, 0 }
+       };
+       AuthorizationRights rights = { sizeof(right) / sizeof(right[0]), right };
+       MacOSError::check(AuthorizationCopyRights(authorization, &rights, NULL,
+               kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, NULL));
+       
+       MacOSError::check(AuthorizationFree(authorization, kAuthorizationFlagDefaults));
+}
+
+
+//
+// Perform common argument normalizations for update operations
+//
+static void normalizeTarget(CFRef<CFTypeRef> &target, CFDictionary &context, bool signUnsigned)
+{
+       // turn CFURLs into (designated) SecRequirements
+       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())) {
+               case noErr:
+                       break;
+               case errSecCSUnsigned:
+                       if (signUnsigned) {
+                               // Ad-hoc sign the code in the system database. This requires root privileges.
+                               CFRef<SecCodeSignerRef> signer;
+                               CFTemp<CFDictionaryRef> arguments("{%O=#N, %O=#N}", kSecCodeSignerDetached, kSecCodeSignerIdentity);
+                               MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref()));
+                               MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
+                               MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref()));
+                               break;
+                       }
+                       // fall through
+               default:
+                       MacOSError::check(rc);
+               }
+               if (context.get(kSecAssessmentUpdateKeyRemarks) == NULL)        {
+                       // no explicit remarks; add one with the path
+                       CFRef<CFURLRef> path;
+                       MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
+                       CFMutableDictionaryRef dict = makeCFMutableDictionary(context.get());
+                       CFDictionaryAddValue(dict, kSecAssessmentUpdateKeyRemarks, CFTempString(cfString(path)));
+                       context.take(dict);
+               }
+       }
+}
+
+
 } // end namespace CodeSigning
 } // end namespace Security
 } // end namespace CodeSigning
 } // end namespace Security
index 73b993a4e659b5b71bc61800b3c792a87a93c7df..e22f50d4edc029a37c72777ce355398b28de241e 100644 (file)
@@ -52,8 +52,14 @@ 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 add(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context);
 
 
+       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);
+
+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);
 
        static void addAuthority(CFMutableDictionaryRef parent, const char *label, SQLite::int64 row = 0, CFTypeRef cacheInfo = NULL);
        static void addToAuthority(CFMutableDictionaryRef parent, CFStringRef key, CFTypeRef value);
 
@@ -61,10 +67,13 @@ private:
        void evaluateCode(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);
        void evaluateCode(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,
+               CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context);
 
        void setOrigin(CFArrayRef chain, CFMutableDictionaryRef result);
 
 
        void setOrigin(CFArrayRef chain, CFMutableDictionaryRef result);
 
-       void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, time_t expires, int authority, const char *label);
+       void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, int authority);
 };
 
 
 };
 
 
diff --git a/lib/quarantine++.cpp b/lib/quarantine++.cpp
new file mode 100644 (file)
index 0000000..866627d
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2011 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@
+ */
+
+//
+// xar++ - interface to XAR-format archive files
+//
+#include "quarantine++.h"
+
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Check the int result of a qtn API call.
+// If the error is "not quarantined," note in the object (no error).
+// Other qtn-specific errors are arbitrarily mapped to ENOSYS (this isn't
+// important enough to subclass CommonError).
+//
+void FileQuarantine::check(int err)
+{
+       switch (err) {
+       case 0:
+               mQuarantined = true;
+               break;
+       case QTN_NOT_QUARANTINED:
+               mQuarantined = false;
+               return;
+       default:        // some flavor of quarantine-not-available
+               UnixError::throwMe(err);
+       }
+}
+
+
+FileQuarantine::~FileQuarantine()
+{
+       if (mQtn)
+               qtn_file_free(mQtn);
+}
+
+
+FileQuarantine::FileQuarantine(const char *path)
+{
+       if (!(mQtn = qtn_file_alloc()))
+               UnixError::throwMe();
+       check(qtn_file_init_with_path(mQtn, path));
+}
+
+FileQuarantine::FileQuarantine(int fd)
+{
+       if (!(mQtn = qtn_file_alloc()))
+               UnixError::throwMe();
+       check(qtn_file_init_with_fd(mQtn, fd));
+}
+
+
+void FileQuarantine::setFlags(uint32_t flags)
+{
+       if (mQuarantined)
+               check(qtn_file_set_flags(mQtn, flags));
+}
+
+void FileQuarantine::setFlag(uint32_t flag)
+{
+       if (mQuarantined)
+               setFlags(flags() | flag);
+}
+
+void FileQuarantine::clearFlag(uint32_t flag)
+{
+       if (mQuarantined)
+               setFlags(flags() & ~flag);
+}
+
+void FileQuarantine::applyTo(const char *path)
+{
+       check(qtn_file_apply_to_path(mQtn, path));
+}
+
+void FileQuarantine::applyTo(int fd)
+{
+       check(qtn_file_apply_to_fd(mQtn, fd));
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
diff --git a/lib/quarantine++.h b/lib/quarantine++.h
new file mode 100644 (file)
index 0000000..53c5609
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011 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@
+ */
+
+//
+// quarantine++ - interface to XAR-format archive files
+//
+#ifndef _H_QUARANTINEPLUSPLUS
+#define _H_QUARANTINEPLUSPLUS
+
+#include <security_utilities/utilities.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+extern "C" {
+#include <quarantine.h>
+}
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// A file quarantine object
+//
+class FileQuarantine {
+public:        
+       FileQuarantine(const char *path);
+       FileQuarantine(int fd);
+       virtual ~FileQuarantine();
+       
+       uint32_t flags() const
+               { return qtn_file_get_flags(mQtn); }
+       bool flag(uint32_t f) const
+               { return this->flags() & f; }
+       
+       void setFlags(uint32_t flags);
+       void setFlag(uint32_t flag);
+       void clearFlag(uint32_t flag);
+       
+       void applyTo(const char *path);
+       void applyTo(int fd);
+       
+       operator bool() const { return mQtn != 0; }
+       bool quarantined() const { return mQuarantined; }
+
+private:
+       void check(int err);
+       
+private:
+       qtn_file_t mQtn;                // qtn handle
+       bool mQuarantined;              // has quarantine information
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_QUARANTINEPLUSPLUS
index ec6cd64390f37071d5b5e0e316460c51d2700588..ceca15012fc2f3d6c01c06958aeae5b8d99f45a4 100644 (file)
@@ -109,7 +109,7 @@ string Dumper::dump(const Requirement *req, bool debug /* = false */)
        } catch (const CommonError &err) {
                if (debug) {
                        char errstr[80];
        } catch (const CommonError &err) {
                if (debug) {
                        char errstr[80];
-                       snprintf(errstr, sizeof(errstr), " !! error %ld !!", err.osStatus());
+                       snprintf(errstr, sizeof(errstr), " !! error %ld !!", (unsigned long)err.osStatus());
                        return dumper.value() + errstr;
                }
                throw;
                        return dumper.value() + errstr;
                }
                throw;
index 8558455da7d7001a7ad9ea23515a19c5b4dbc01d..a2ca3952dc6ceff626e183d54262d6cc155e3c65 100644 (file)
@@ -136,10 +136,16 @@ _kSecAssessmentOperationTypeExecute
 _kSecAssessmentOperationTypeInstall
 _kSecAssessmentOperationTypeOpenDocument
 _kSecAssessmentContextKeyUpdate
 _kSecAssessmentOperationTypeInstall
 _kSecAssessmentOperationTypeOpenDocument
 _kSecAssessmentContextKeyUpdate
-_kSecAssessmentUpdateOperationAddFile
-_kSecAssessmentUpdateOperationRemoveFile
+_kSecAssessmentUpdateOperationAdd
+_kSecAssessmentUpdateOperationRemove
+_kSecAssessmentUpdateOperationEnable
+_kSecAssessmentUpdateOperationDisable
+_kSecAssessmentUpdateKeyAuthorization
+_kSecAssessmentUpdateKeyAllow
+_kSecAssessmentUpdateKeyExpires
 _kSecAssessmentUpdateKeyPriority
 _kSecAssessmentUpdateKeyLabel
 _kSecAssessmentUpdateKeyPriority
 _kSecAssessmentUpdateKeyLabel
+_kSecAssessmentUpdateKeyRemarks
 _kSecAssessmentAssessmentAuthority
 _kSecAssessmentAssessmentAuthorityRow
 _kSecAssessmentAssessmentFromCache
 _kSecAssessmentAssessmentAuthority
 _kSecAssessmentAssessmentAuthorityRow
 _kSecAssessmentAssessmentFromCache
index 4e2eb8608dde091cfbc9dd4da160a8e6fbf8a08f..9448908fee75470c44047ee3ec13f6f2aa93c3f4 100644 (file)
 --
 -- This is currently for sqlite3
 --
 --
 -- This is currently for sqlite3
 --
+-- NOTES:
+-- Dates are uniformly in julian form. We use 5000000 as the canonical "never" expiration
+-- value; that's a day in the year 8977.
+--
+PRAGMA user_version = 1;
 PRAGMA foreign_keys = true;
 PRAGMA foreign_keys = true;
+PRAGMA legacy_file_format = false;
+PRAGMA recursive_triggers = true;
+
 
 
+--
+-- The feature table hold configuration features and options
+--
+CREATE TABLE feature (
+       id INTEGER PRIMARY KEY,                 -- canononical
+       name TEXT NOT NULL UNIQUE,                      -- name of option
+       value TEXT NULL,                                        -- value of option, if any
+       remarks TEXT NOT NULL                           -- optional remarks string
+);
 
 
 --
 -- The primary authority. This table is conceptually scanned
 
 
 --
 -- The primary authority. This table is conceptually scanned
--- in priority order, with the highest-priority matching record
+-- in priority order, with the highest-priority matching enabled record
 -- determining the outcome.
 -- 
 CREATE TABLE authority (
 -- determining the outcome.
 -- 
 CREATE TABLE authority (
-       id INTEGER PRIMARY KEY,
-       type INTEGER NOT NULL,
-       requirement TEXT NOT NULL,
-       allow INTEGER NOT NULL,
-       expires INTEGER NULL,
-       priority REAL NOT NULL DEFAULT (0),
-       label TEXT NULL,
-       inhibit_cache INTEGER NULL,
-       flags INTEGER NOT NULL DEFAULT (0),
+       id INTEGER PRIMARY KEY AUTOINCREMENT,                           -- canonical
+       version INTEGER NOT NULL DEFAULT (1)                            -- semantic version of this rule
+               CHECK (version > 0),
+       type INTEGER NOT NULL,                                                          -- operation type
+       requirement TEXT NULL                                                           -- code requirement
+               CHECK ((requirement IS NULL) = ((flags & 1) != 0)),
+       allow INTEGER NOT NULL DEFAULT (1)                                      -- allow (1) or deny (0)
+               CHECK (allow = 0 OR allow = 1),
+       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)
+       label TEXT NULL,                                                                        -- text label for authority rule
+       flags INTEGER NOT NULL DEFAULT (0),                             -- amalgamated binary flags
        -- following fields are for documentation only
        -- following fields are for documentation only
-       remarks TEXT NULL
+       ctime FLOAT NOT NULL DEFAULT (JULIANDAY('now')),        -- rule creation time (Julian)
+       mtime FLOAT NOT NULL DEFAULT (JULIANDAY('now')),        -- time rule was last changed (Julian)
+       user TEXT NULL,                                                                         -- user requesting this rule (NULL if unknown)
+       remarks TEXT NULL                                                                       -- optional remarks string
 );
 
 );
 
--- any Apple-signed installers of any kind
-insert into authority (type, allow, priority, label, requirement)
-       values (2, 1, -1, 'Apple Installer', 'anchor apple generic');
+-- index
+CREATE INDEX authority_type ON authority (type);
+CREATE INDEX authority_priority ON authority (priority);
+CREATE INDEX authority_expires ON authority (expires);
+
+-- update mtime if a record is changed
+CREATE TRIGGER authority_update AFTER UPDATE ON authority
+BEGIN
+       UPDATE authority SET mtime = JULIANDAY('now') WHERE id = old.id;
+END;
+
+-- rules that are actively considered
+CREATE VIEW active_authority AS
+SELECT * from authority
+WHERE disabled = 0 AND JULIANDAY('now') < expires AND (flags & 1) = 0;
+
+-- rules subject to priority scan: active_authority but including disabled rules
+CREATE VIEW scan_authority AS
+SELECT * from authority
+WHERE JULIANDAY('now') < expires AND (flags & 1) = 0;
+
+
+--
+-- Initial canonical contents of a fresh database
+--
+
+-- virtual rule anchoring negative cache entries (no rule found)
+insert into authority (type, allow, priority, flags, label)
+       values (1, 0, -1.0E100, 1, 'No Matching Rule');
+
+-- any Apple-signed installers except Developer ID
+insert into authority (type, allow, priority, flags, label, requirement)
+       values (2, 1, -1, 2, 'Apple Installer', 'anchor apple generic and ! certificate 1[field.1.2.840.113635.100.6.2.6]');
 
 -- Apple code signing
 
 -- Apple code signing
-insert into authority (type, allow, label, requirement)
-       values (1, 1, 'Apple', 'anchor apple');
+insert into authority (type, allow, flags, label, requirement)
+       values (1, 1, 2, 'Apple System', 'anchor apple');
 
 -- Mac App Store signing
 
 -- Mac App Store signing
-insert into authority (type, allow, label, requirement)
-       values (1, 1, 'Mac App Store', 'anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists');
+insert into authority (type, allow, flags, label, requirement)
+       values (1, 1, 2, 'Mac App Store', 'anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists');
 
 
-insert into authority (type, allow, label, requirement)
-       values (1, 1, 'Developer Seed', '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, label, requirement)
-       values (2, 1, 'Developer Seed', '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');
+-- Caspian code and archive signing
+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');
 
 
 --
 
 
 --
@@ -71,17 +127,50 @@ insert into authority (type, allow, label, requirement)
 -- for individual objects (by object hash). Entries come from
 -- full evaluations of authority records, or by explicitly inserting
 -- override rules that preempt the normal authority.
 -- for individual objects (by object hash). Entries come from
 -- full evaluations of authority records, or by explicitly inserting
 -- override rules that preempt the normal authority.
+-- EACH object record must have a parent authority record from which it is derived;
+-- this may be a normal authority rule or an override rule. If the parent rule is deleted,
+-- all objects created from it are automatically removed (by sqlite itself).
 --
 CREATE TABLE object (
 --
 CREATE TABLE object (
-       id INTEGER PRIMARY KEY,
-       type INTEGER NOT NULL,
-       hash CDHASH NOT NULL UNIQUE,
-       allow INTEGER NOT NULL,
-       expires INTEGER NULL,
-       label TEXT NULL,
-       authority INTEGER NULL REFERENCES authority(id),
+       id INTEGER PRIMARY KEY,                                                         -- canonical
+       type INTEGER NOT NULL,                                                                  -- operation type
+       hash CDHASH NOT NULL,                                                                   -- canonical hash of object
+       allow INTEGER NOT NULL,                                                         -- allow (1) or deny (0)
+       expires FLOAT NOT NULL DEFAULT (5000000),                               -- expiration of object entry
+       authority INTEGER NOT NULL                                                              -- governing authority rule
+               REFERENCES authority(id) ON DELETE CASCADE,
        -- following fields are for documentation only
        -- following fields are for documentation only
-       path TEXT NULL,
-       created INTEGER NOT NULL default (strftime('%s','now')),
-       remarks TEXT NULL
+       path TEXT NULL,                                                                                 -- path of object at record creation time
+       ctime FLOAT NOT NULL DEFAULT (JULIANDAY('now')),                -- record creation time
+       mtime FLOAT NOT NULL DEFAULT (JULIANDAY('now')),                -- record modification time
+       remarks TEXT NULL                                                                               -- optional remarks string
 );
 );
+
+-- index
+CREATE INDEX object_type ON object (type);
+CREATE INDEX object_expires ON object (expires);
+CREATE UNIQUE INDEX object_hash ON object (hash);
+
+-- update mtime if a record is changed
+CREATE TRIGGER object_update AFTER UPDATE ON object
+BEGIN
+       UPDATE object SET mtime = JULIANDAY('now') WHERE id = old.id;
+END;
+
+
+--
+-- Some useful views on objects. These are for administration; they are not used by the assessor.
+--
+CREATE VIEW object_state AS
+SELECT object.id, object.type, object.allow,
+       CASE object.expires WHEN 5000000 THEN NULL ELSE STRFTIME('%Y-%m-%d %H:%M:%f', object.expires, 'localtime') END AS expiration,
+       (object.expires - JULIANDAY('now')) * 86400 as remaining,
+       authority.label,
+       object.authority,
+       object.path,
+       object.ctime,
+       authority.requirement,
+       authority.disabled,
+       object.remarks
+FROM object, authority
+WHERE object.authority = authority.id;
index b9ace0fac176b9e2c932c18ac83fc48c0b6a3fde..64fbcf9d8b2fe898eb185339d519e5fcae5b59f5 100644 (file)
@@ -25,6 +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>
 
 
 namespace Security {
 
 
 namespace Security {
@@ -100,7 +101,7 @@ public:
 static void copyCFDictionary(const void *key, const void *value, void *ctx)
 {
        CFMutableDictionaryRef target = CFMutableDictionaryRef(ctx);
 static void copyCFDictionary(const void *key, const void *value, void *ctx)
 {
        CFMutableDictionaryRef target = CFMutableDictionaryRef(ctx);
-       if (CFEqual(key, kSecAssessmentContextKeyCertificates))         // legacy; drop it
+       if (CFEqual(key, kSecAssessmentContextKeyCertificates)) // obsolete
                return;
        if (CFGetTypeID(value) == CFURLGetTypeID()) {
                CFRef<CFStringRef> path = CFURLCopyFileSystemPath(CFURLRef(value), kCFURLPOSIXPathStyle);
                return;
        if (CFGetTypeID(value) == CFURLGetTypeID()) {
                CFRef<CFStringRef> path = CFURLCopyFileSystemPath(CFURLRef(value), kCFURLPOSIXPathStyle);
@@ -120,7 +121,12 @@ void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutab
                CFDictionaryApplyFunction(context, copyCFDictionary, ctx);
        CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx));
        xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData));
                CFDictionaryApplyFunction(context, copyCFDictionary, ctx);
        CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx));
        xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData));
+       
        msg.send();
        msg.send();
+       
+       if (int64_t error = xpc_dictionary_get_int64(msg, "error"))
+               MacOSError::throwMe(error);
+
        size_t resultLength;
        const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength);
        CFRef<CFDictionaryRef> resultDict = makeCFDictionaryFrom(resultData, resultLength);
        size_t resultLength;
        const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength);
        CFRef<CFDictionaryRef> resultDict = makeCFDictionaryFrom(resultData, resultLength);
@@ -129,6 +135,48 @@ void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutab
 }
 
 
 }
 
 
+bool xpcEngineUpdate(CFTypeRef target, uint flags, CFDictionaryRef context)
+{
+       Message msg("update");
+       // target can be NULL, a CFURLRef, a SecRequirementRef, or a CFNumberRef
+       if (target) {
+               if (CFGetTypeID(target) == CFNumberGetTypeID())
+                       xpc_dictionary_set_uint64(msg, "rule", cfNumber<int64_t>(CFNumberRef(target)));
+               else if (CFGetTypeID(target) == CFURLGetTypeID())
+                       xpc_dictionary_set_string(msg, "url", cfString(CFURLRef(target)).c_str());
+               else if (CFGetTypeID(target) == SecRequirementGetTypeID()) {
+                       CFRef<CFDataRef> data;
+                       MacOSError::check(SecRequirementCopyData(SecRequirementRef(target), kSecCSDefaultFlags, &data.aref()));
+                       xpc_dictionary_set_data(msg, "requirement", CFDataGetBytePtr(data), CFDataGetLength(data));
+               } else
+                       MacOSError::throwMe(errSecCSInvalidObjectRef);
+       }
+       xpc_dictionary_set_int64(msg, "flags", flags);
+       CFRef<CFMutableDictionaryRef> ctx = makeCFMutableDictionary();
+       if (context)
+               CFDictionaryApplyFunction(context, copyCFDictionary, ctx);
+       AuthorizationRef localAuthorization = NULL;
+       if (CFDictionaryGetValue(ctx, kSecAssessmentUpdateKeyAuthorization) == NULL) {  // no caller-provided authorization
+               MacOSError::check(AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &localAuthorization));
+               AuthorizationExternalForm extForm;
+               MacOSError::check(AuthorizationMakeExternalForm(localAuthorization, &extForm));
+               CFDictionaryAddValue(ctx, kSecAssessmentUpdateKeyAuthorization, CFTempData(&extForm, sizeof(extForm)));
+       }
+       CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx));
+       xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData));
+       
+       msg.send();
+
+       if (localAuthorization)
+               AuthorizationFree(localAuthorization, kAuthorizationFlagDefaults);
+       
+       if (int64_t error = xpc_dictionary_get_int64(msg, "error"))
+               MacOSError::throwMe(error);
+       
+       return true;
+}
+
+
 bool xpcEngineControl(const char *control)
 {
        Message msg("control");
 bool xpcEngineControl(const char *control)
 {
        Message msg("control");
index 92a6030924cf24345eb52cfb46749e6add19143b..8b2f115d4d9be37764737f3c397d03329f131f77 100644 (file)
@@ -33,6 +33,7 @@ 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);
 bool xpcEngineControl(const char *name);
 
 
 bool xpcEngineControl(const char *name);
 
 
index 111f93a3a7c59d5059dae925809a798ac0434c8f..8980156d867e2b66e572ef5eb1eb395b1395cc83 100644 (file)
@@ -13,6 +13,7 @@
                        buildPhases = (
                                C26AC0F0143BCF18001C98CE /* ShellScript */,
                                C26AC0F4143BD1C4001C98CE /* CopyFiles */,
                        buildPhases = (
                                C26AC0F0143BCF18001C98CE /* ShellScript */,
                                C26AC0F4143BD1C4001C98CE /* CopyFiles */,
+                               C2F24DFE14BCBBF200309FCD /* ShellScript */,
                        );
                        dependencies = (
                        );
                        );
                        dependencies = (
                        );
                C2E911E20ADEBE3200275CB2 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2E911E00ADEBE3200275CB2 /* resources.cpp */; };
                C2EF10100A49BD89005A44BB /* renum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2EF100E0A49BD89005A44BB /* renum.cpp */; };
                C2EF10130A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; };
                C2E911E20ADEBE3200275CB2 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2E911E00ADEBE3200275CB2 /* resources.cpp */; };
                C2EF10100A49BD89005A44BB /* renum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2EF100E0A49BD89005A44BB /* renum.cpp */; };
                C2EF10130A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2F4439A14C626D4000A01E6 /* quarantine++.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4439814C626D4000A01E6 /* quarantine++.cpp */; };
+               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, ); }; };
                FEB30C9310DAC89D00557BA2 /* SecTask.c in Sources */ = {isa = PBXBuildFile; fileRef = FEB30C9210DAC89D00557BA2 /* SecTask.c */; };
                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, ); }; };
                FEB30C9310DAC89D00557BA2 /* SecTask.c in Sources */ = {isa = PBXBuildFile; fileRef = FEB30C9210DAC89D00557BA2 /* SecTask.c */; };
                C2E911E10ADEBE3200275CB2 /* resources.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = resources.h; sourceTree = "<group>"; };
                C2EF100E0A49BD89005A44BB /* renum.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = renum.cpp; sourceTree = "<group>"; };
                C2EF100F0A49BD89005A44BB /* renum.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = renum.h; sourceTree = "<group>"; };
                C2E911E10ADEBE3200275CB2 /* resources.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = resources.h; sourceTree = "<group>"; };
                C2EF100E0A49BD89005A44BB /* renum.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = renum.cpp; sourceTree = "<group>"; };
                C2EF100F0A49BD89005A44BB /* renum.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = renum.h; sourceTree = "<group>"; };
+               C2F4439814C626D4000A01E6 /* quarantine++.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "quarantine++.cpp"; sourceTree = "<group>"; };
+               C2F4439914C626D4000A01E6 /* quarantine++.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "quarantine++.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>"; };
                                C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */,
                                C235340E145F1B050073F964 /* xar++.h */,
                                C2353410145F1B110073F964 /* xar++.cpp */,
                                C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */,
                                C235340E145F1B050073F964 /* xar++.h */,
                                C2353410145F1B110073F964 /* xar++.cpp */,
+                               C2F4439914C626D4000A01E6 /* quarantine++.h */,
+                               C2F4439814C626D4000A01E6 /* quarantine++.cpp */,
                        );
                        name = "Local Utilities";
                        sourceTree = "<group>";
                        );
                        name = "Local Utilities";
                        sourceTree = "<group>";
                                C273606F1433F09000A9A5FF /* SecAssessment.h in Headers */,
                                C2A436160F2133B2007A41A6 /* slcrep.h in Headers */,
                                C24EABAB1421432800C16AA9 /* policydb.h in Headers */,
                                C273606F1433F09000A9A5FF /* SecAssessment.h in Headers */,
                                C2A436160F2133B2007A41A6 /* slcrep.h in Headers */,
                                C24EABAB1421432800C16AA9 /* policydb.h in Headers */,
+                               C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "mkdir -p \"$(dirname \"$SCRIPT_OUTPUT_FILE_0\")\"\nsqlite3 \"$SCRIPT_OUTPUT_FILE_0\" <<END\n.read \"$SCRIPT_INPUT_FILE_0\"\nEND\n";
+                       shellScript = "if [ -f $(TEMPDIR)/SystemPolicy ]; then\n    rm $(TEMPDIR)/SystemPolicy\nfi\nmkdir -p \"$(dirname \"$SCRIPT_OUTPUT_FILE_0\")\"\nsqlite3 \"$SCRIPT_OUTPUT_FILE_0\" <<END\n.read \"$SCRIPT_INPUT_FILE_0\"\nEND\n";
                        showEnvVarsInLog = 0;
                };
                C26AC7080DAEB3A7005BFB40 /* ShellScript */ = {
                        showEnvVarsInLog = 0;
                };
                C26AC7080DAEB3A7005BFB40 /* ShellScript */ = {
                        shellPath = /bin/bash;
                        shellScript = "antlr=/usr/local/bin/antlr.jar\nDEBUG=\"\"\nmkdir -p $TEMPDIR\nrm -f $TEMPDIR/Requirement{Parser,Lexer}*\njava -cp \"$antlr\" antlr.Tool -o $TEMPDIR $DEBUG requirements.grammar\nsed -n 's/^.*=\\(\".*\"\\)=.*$/        \\1,/p' $TEMPDIR/RequirementParserTokenTypes.txt >$TEMPDIR/RequirementKeywords.h\n";
                };
                        shellPath = /bin/bash;
                        shellScript = "antlr=/usr/local/bin/antlr.jar\nDEBUG=\"\"\nmkdir -p $TEMPDIR\nrm -f $TEMPDIR/Requirement{Parser,Lexer}*\njava -cp \"$antlr\" antlr.Tool -o $TEMPDIR $DEBUG requirements.grammar\nsed -n 's/^.*=\\(\".*\"\\)=.*$/        \\1,/p' $TEMPDIR/RequirementParserTokenTypes.txt >$TEMPDIR/RequirementKeywords.h\n";
                };
+               C2F24DFE14BCBBF200309FCD /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                               "$(TEMPDIR)/SystemPolicy",
+                       );
+                       outputPaths = (
+                               "$(DSTROOT)/private/var/db/.SystemPolicy-default",
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/bash;
+                       shellScript = "cp \"$SCRIPT_INPUT_FILE_0\" \"$SCRIPT_OUTPUT_FILE_0\"\nchmod 444 \"$SCRIPT_OUTPUT_FILE_0\"";
+                       showEnvVarsInLog = 0;
+               };
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
                                C27360D51436866D00A9A5FF /* xpcengine.cpp in Sources */,
                                C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */,
                                C2DC2DCB145F5CD000AD2A3A /* policyengine.cpp in Sources */,
                                C27360D51436866D00A9A5FF /* xpcengine.cpp in Sources */,
                                C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */,
                                C2DC2DCB145F5CD000AD2A3A /* policyengine.cpp in Sources */,
+                               C2F4439A14C626D4000A01E6 /* quarantine++.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                BUILD_VARIANTS = debug;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                BUILD_VARIANTS = debug;
-                               CURRENT_PROJECT_VERSION = 55032;
+                               CURRENT_PROJECT_VERSION = 55037.4;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                        normal,
                                        debug,
                                );
                                        normal,
                                        debug,
                                );
-                               CURRENT_PROJECT_VERSION = 55032;
+                               CURRENT_PROJECT_VERSION = 55037.4;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                        normal,
                                        debug,
                                );
                                        normal,
                                        debug,
                                );
-                               CURRENT_PROJECT_VERSION = 55032;
+                               CURRENT_PROJECT_VERSION = 55037.4;
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
                                EXECUTABLE_PREFIX = "";
                                EXECUTABLE_SUFFIX = "";
                                FRAMEWORK_SEARCH_PATHS = (
index f6972e7accbf38e8fe395def575e9de5aeb2d3ed..6b83f053d36a63ec48d22bb81fbdb0139ca1a098 100644 (file)
@@ -266,7 +266,7 @@ appleanchor[Maker &maker]
                        { maker.put(opAppleAnchor); }
        |       "generic"
                        { maker.put(opAppleGenericAnchor); }
                        { maker.put(opAppleAnchor); }
        |       "generic"
                        { maker.put(opAppleGenericAnchor); }
-|      |       { string name; } name=identifierString
+       |       { string name; } name=identifierString
                        { maker.put(opNamedAnchor); maker.put(name); }
        ;
 
                        { maker.put(opNamedAnchor); maker.put(name); }
        ;