]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/policyengine.cpp
Security-59306.120.7.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / policyengine.cpp
index fa2a971ae37fc2396e8da2c71aba52199d5726e1..a29937164d7d5bf1ea3c686c8278ac1bc6013e85 100644 (file)
 #include "diskrep.h"
 #include "codedirectory.h"
 #include "csutilities.h"
 #include "diskrep.h"
 #include "codedirectory.h"
 #include "csutilities.h"
+#include "notarization.h"
 #include "StaticCode.h"
 
 #include <CoreServices/CoreServicesPriv.h>
 #include "SecCodePriv.h"
 #undef check // Macro! Yech.
 
 #include "StaticCode.h"
 
 #include <CoreServices/CoreServicesPriv.h>
 #include "SecCodePriv.h"
 #undef check // Macro! Yech.
 
-extern "C" {
-#include <OpenScriptingUtilPriv.h>
-}
-
-
 namespace Security {
 namespace CodeSigning {
 
 namespace Security {
 namespace CodeSigning {
 
@@ -63,7 +59,6 @@ enum {
 
 
 static void authorizeUpdate(SecAssessmentFlags flags, CFDictionaryRef context);
 
 
 static void authorizeUpdate(SecAssessmentFlags flags, CFDictionaryRef context);
-static bool codeInvalidityExceptions(SecStaticCodeRef code, CFMutableDictionaryRef result);
 static CFTypeRef installerPolicy() CF_RETURNS_RETAINED;
 
 
 static CFTypeRef installerPolicy() CF_RETURNS_RETAINED;
 
 
@@ -182,8 +177,8 @@ void PolicyEngine::evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, Author
                SQLite3::int64 disabled = query[6];
 //             const char *filter = query[7];
 //             const char *remarks = query[8];
                SQLite3::int64 disabled = query[6];
 //             const char *filter = query[7];
 //             const char *remarks = query[8];
-               
-        secdebug("gk", "considering rule %d(%s) requirement %s", int(id), label ? label : "UNLABELED", reqString);
+
+               secdebug("gk", "considering rule %d(%s) requirement %s", int(id), label ? label : "UNLABELED", reqString);
                CFRef<SecRequirementRef> requirement;
                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
                switch (OSStatus rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly | kSecCSCheckGatekeeperArchitectures, requirement)) {
                CFRef<SecRequirementRef> requirement;
                MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
                switch (OSStatus rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly | kSecCSCheckGatekeeperArchitectures, requirement)) {
@@ -197,13 +192,17 @@ void PolicyEngine::evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, Author
                        MacOSError::throwMe(rc);        // general error; pass to caller
                }
                
                        MacOSError::throwMe(rc);        // general error; pass to caller
                }
                
-               // if this rule is disabled, skip it but record the first matching one for posterity
-               if (disabled && latentID == 0) {
-                       latentID = id;
-                       latentLabel = label ? label : "";
+               // If this rule is disabled, do not continue any further and just continue iterating
+               // until we find one that is enabled.
+               if (disabled) {
+                       // ...but always record the first matching rule for informational purposes.
+                       if (latentID == 0) {
+                               latentID = id;
+                               latentLabel = label ? label : "";
+                       }
                        continue;
                }
                        continue;
                }
-               
+
                // current rule is first rule (in priority order) that matched. Apply it
         secnotice("gk", "rule %d applies - allow=%d", int(id), allow);
                if (nested && allow)                    // success, nothing to record
                // current rule is first rule (in priority order) that matched. Apply it
         secnotice("gk", "rule %d applies - allow=%d", int(id), allow);
                if (nested && allow)                    // success, nothing to record
@@ -388,7 +387,7 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment
        }
        
        CFCopyRef<SecStaticCodeRef> code;
        }
        
        CFCopyRef<SecStaticCodeRef> code;
-       MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
+       MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags | kSecCSForceOnlineNotarizationCheck, &code.aref()));
        
        SecCSFlags validationFlags = kSecCSEnforceRevocationChecks | kSecCSCheckAllArchitectures;
        if (!(flags & kSecAssessmentFlagAllowWeak))
        
        SecCSFlags validationFlags = kSecCSEnforceRevocationChecks | kSecCSCheckAllArchitectures;
        if (!(flags & kSecAssessmentFlagAllowWeak))
@@ -398,15 +397,7 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment
        // deal with a very special case (broken 10.6/10.7 Applet bundles)
        OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSBasicValidateOnly, NULL);
        if (rc == errSecCSSignatureFailed) {
        // deal with a very special case (broken 10.6/10.7 Applet bundles)
        OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSBasicValidateOnly, NULL);
        if (rc == errSecCSSignatureFailed) {
-               if (!codeInvalidityExceptions(code, result)) {  // invalidly signed, no exceptions -> error
-                       if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED())
-                               SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, false);
-                       MacOSError::throwMe(rc);
-               }
-               // recognized exception - treat as unsigned
-               if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED())
-                       SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, true);
-               rc = errSecCSUnsigned;
+               MacOSError::throwMe(rc);
        }
 
        // ad-hoc sign unsigned code
        }
 
        // ad-hoc sign unsigned code
@@ -449,7 +440,7 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment
                }
                return NULL;
        }));
                }
                return NULL;
        }));
-       
+
        // go for it!
        SecCSFlags topFlags = validationFlags | kSecCSCheckNestedCode | kSecCSRestrictSymlinks | kSecCSReportProgress;
        if (type == kAuthorityExecute && !appOk)
        // go for it!
        SecCSFlags topFlags = validationFlags | kSecCSCheckNestedCode | kSecCSRestrictSymlinks | kSecCSReportProgress;
        if (type == kAuthorityExecute && !appOk)
@@ -506,6 +497,18 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment
        default:
                MacOSError::throwMe(rc);
        }
        default:
                MacOSError::throwMe(rc);
        }
+
+       // Copy notarization date, if present, from code signing information
+       CFRef<CFDictionaryRef> info;
+       OSStatus status = SecCodeCopySigningInformation(code, kSecCSInternalInformation, &info.aref());
+       if (status == 0 && info) {
+               CFDateRef date = (CFDateRef)CFDictionaryGetValue(info, kSecCodeInfoNotarizationDate);
+               if (date) {
+                       cfadd(result, "{%O=%O}", kSecAssessmentAssessmentNotarizationDate, date);
+               }
+       } else {
+               secerror("Unable to copy signing information: %d", (int)status);
+       }
        
        if (nestedFailure && CFEqual(CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict), kCFBooleanTrue)) {
                // structure intact, top level approved, nested code failed policy
        
        if (nestedFailure && CFEqual(CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict), kCFBooleanTrue)) {
                // structure intact, top level approved, nested code failed policy
@@ -568,6 +571,9 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
        if (CFRef<CFArrayRef> certs = xar.copyCertChain()) {
                CFRef<CFTypeRef> policy = installerPolicy();
                CFRef<SecTrustRef> trust;
        if (CFRef<CFArrayRef> certs = xar.copyCertChain()) {
                CFRef<CFTypeRef> policy = installerPolicy();
                CFRef<SecTrustRef> trust;
+               CFRef<CFDataRef> checksum;
+               CFRef<CFStringRef> teamID;
+               CFRef<CFMutableDictionaryRef> requirementContext = makeCFMutableDictionary();
                MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref()));
 //             MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors
                MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors));
                MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref()));
 //             MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors
                MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors));
@@ -593,6 +599,42 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
                        }
                }
 
                        }
                }
 
+               xar.registerStapledNotarization();
+               checksum.take(xar.createPackageChecksum());
+               if (checksum) {
+                       double notarizationDate = NAN;
+
+                       // Force a single online check for the checksum, which is always SHA1.
+                       bool is_revoked = checkNotarizationServiceForRevocation(checksum, kSecCodeSignatureHashSHA1, &notarizationDate);
+                       if (is_revoked) {
+                               MacOSError::throwMe(errSecCSRevokedNotarization);
+                       }
+
+                       // Extract a team identifier from the certificates.  This isn't validated and could be spoofed,
+                       // but since the 'legacy' keyword is only used in addition to the Developer ID requirement,
+                       // this is still stafe for now.
+                       SecCertificateRef leaf = SecCertificateRef(CFArrayGetValueAtIndex(certs, 0));
+                       CFRef<CFArrayRef> orgUnits = SecCertificateCopyOrganizationalUnit(leaf);
+                       if (orgUnits.get() && CFArrayGetCount(orgUnits) == 1) {
+                               teamID = (CFStringRef)CFArrayGetValueAtIndex(orgUnits, 0);
+                       }
+
+                       // Create the appropriate requirement context entry to allow notarized requirement check.
+                       CFRef<CFNumberRef> algorithm = makeCFNumber((uint32_t)xar.checksumDigestAlgorithm());
+                       cfadd(requirementContext, "{%O=%O}", kSecRequirementKeyPackageChecksum, checksum.get());
+                       cfadd(requirementContext, "{%O=%O}", kSecRequirementKeyChecksumAlgorithm, algorithm.get());
+                       if (teamID.get()) {
+                               cfadd(requirementContext, "{%O=%O}", kSecRequirementKeyTeamIdentifier, teamID.get());
+                       }
+
+                       if (!isnan(notarizationDate)) {
+                               CFRef<CFDateRef> date = CFDateCreate(NULL, notarizationDate);
+                               if (date) {
+                                       cfadd(result, "{%O=%O}", kSecAssessmentAssessmentNotarizationDate, date.get());
+                               }
+                       }
+               }
+
                SQLite::Statement query(*this,
                        "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority"
                        " WHERE type = :type"
                SQLite::Statement query(*this,
                        "SELECT allow, requirement, id, label, flags, disabled FROM scan_authority"
                        " WHERE type = :type"
@@ -605,10 +647,10 @@ void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDi
                        const char *label = query[3];
                        //sqlite_uint64 ruleFlags = query[4];
                        SQLite3::int64 disabled = query[5];
                        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()));
-                       switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) {
+                       switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, requirementContext.get(), kSecCSDefaultFlags)) {
                        case errSecSuccess: // success
                                break;
                        case errSecCSReqFailed: // requirement missed, but otherwise okay
                        case errSecSuccess: // success
                                break;
                        case errSecCSReqFailed: // requirement missed, but otherwise okay
@@ -1144,17 +1186,6 @@ void PolicyEngine::normalizeTarget(CFRef<CFTypeRef> &target, AuthorityType type,
                        }
                        MacOSError::check(rc);
                case errSecCSSignatureFailed:
                        }
                        MacOSError::check(rc);
                case errSecCSSignatureFailed:
-                       // recover certain cases of broken signatures (well, try)
-                       if (codeInvalidityExceptions(code, NULL)) {
-                               // Ad-hoc sign the code in place (requiring a writable subject). This requires root privileges.
-                               CFRef<SecCodeSignerRef> signer;
-                               CFTemp<CFDictionaryRef> arguments("{%O=#N}", kSecCodeSignerIdentity);
-                               MacOSError::check(SecCodeSignerCreate(arguments, kSecCSSignOpaque, &signer.aref()));
-                               MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
-                               MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref()));
-                               break;
-                       }
-                       MacOSError::check(rc);
                default:
                        MacOSError::check(rc);
                }
                default:
                        MacOSError::check(rc);
                }
@@ -1174,28 +1205,5 @@ void PolicyEngine::normalizeTarget(CFRef<CFTypeRef> &target, AuthorityType type,
        }
 }
 
        }
 }
 
-
-//
-// Process special overrides for invalidly signed code.
-// This is the (hopefully minimal) concessions we make to keep hurting our customers
-// for our own prior mistakes...
-//
-static bool codeInvalidityExceptions(SecStaticCodeRef code, CFMutableDictionaryRef result)
-{
-       CFRef<CFDictionaryRef> info;
-       MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
-       if (CFURLRef executable = CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable))) {
-               SInt32 error;
-               if (OSAIsRecognizedExecutableURL(executable, &error)) {
-                       if (result)
-                               CFDictionaryAddValue(result,
-                                       kSecAssessmentAssessmentAuthorityOverride, CFSTR("ignoring known invalid applet signature"));
-                       return true;
-               }
-       }
-       return false;
-}
-
-
 } // end namespace CodeSigning
 } // end namespace Security
 } // end namespace CodeSigning
 } // end namespace Security