Exemplo n.º 1
0
// static
void Sandbox::initialize(const QString& permissionsFile) {
    QMutexLocker locker(&s_mutex);
    s_pSandboxPermissions = new ConfigObject<ConfigValue>(permissionsFile);

#ifdef Q_OS_MAC
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
    // If we are running on at least 10.7.0 and have the com.apple.security.app-sandbox
    // entitlement, we are in a sandbox
    SInt32 version = 0;
    Gestalt(gestaltSystemVersion, &version);
    SecCodeRef secCodeSelf;
    if (version >= 0x1070 && SecCodeCopySelf(kSecCSDefaultFlags, &secCodeSelf) == errSecSuccess) {
        SecRequirementRef sandboxReq;
        CFStringRef entitlement = CFSTR("entitlement [\"com.apple.security.app-sandbox\"]");
        if (SecRequirementCreateWithString(entitlement, kSecCSDefaultFlags,
                                           &sandboxReq) == errSecSuccess) {
            if (SecCodeCheckValidity(secCodeSelf, kSecCSDefaultFlags,
                                     sandboxReq) == errSecSuccess) {
                s_bInSandbox = true;
            }
            CFRelease(sandboxReq);
        }
        CFRelease(secCodeSelf);
    }
#endif
#endif
}
Exemplo n.º 2
0
/*
 * Determine if the given task meets a specified requirement.
 */
OSStatus
SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
{
    OSStatus status;
    SecCodeRef code = NULL;
    SecRequirementRef req = NULL;
    pid_t pid = task->pid;
    if (pid <= 0) {
        return errSecParam;
    }
    status = SecCodeCreateWithPID(pid, kSecCSDefaultFlags, &code);
    //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCreateWithPID=%d", status);
    if (!status) {
        status = SecRequirementCreateWithString(requirement,
                                                kSecCSDefaultFlags, &req);
        //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecRequirementCreateWithString=%d", status);
    }
    if (!status) {
        status = SecCodeCheckValidity(code, kSecCSDefaultFlags, req);
        //syslog(LOG_NOTICE, "SecTaskValidateForRequirement: SecCodeCheckValidity=%d", status);
    }
    if (req)
        CFRelease(req);
    if (code)
        CFRelease(code);

    return status;
}
Exemplo n.º 3
0
const bool ClientIdentification::checkAppleSigned() const
{
	if (GuestState *guest = current()) {
		if (!guest->checkedSignature) {
            // This is the clownfish supported way to check for a Mac App Store or B&I signed build
            CFStringRef requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])");
            SecRequirementRef  secRequirementRef = NULL;
            OSStatus status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef);
            if (status == errSecSuccess) {
                OSStatus status = SecCodeCheckValidity(guest->code, kSecCSDefaultFlags, secRequirementRef);
                if (status != errSecSuccess) {
                    secdebug("SecurityAgentXPCQuery", "code requirement check failed (%d)", (int32_t)status);
                } else {
                    guest->appleSigned = true;
                }
                guest->checkedSignature = true;
            }
            CFRelease(secRequirementRef);
		}
		return guest->appleSigned;
	} else
		return false;
}
Exemplo n.º 4
0
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,
                                           QSettings::Scope scope,
                                           const QString &organization,
                                           const QString &application)
{
#ifndef QT_BOOTSTRAPPED
    static bool useAppLocalStorage = false;
    static bool initialized = false;

    if (!initialized) {
        bool inSandbox = false;

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
        // If we are running on at least 10.7.0 and have the com.apple.security.app-sandbox
        // entitlement, we are in a sandbox
        SInt32 version = 0;
        Gestalt(gestaltSystemVersion, &version);
        SecCodeRef secCodeSelf;
        if (version >= 0x1070 && SecCodeCopySelf(kSecCSDefaultFlags, &secCodeSelf) == errSecSuccess) {
            SecRequirementRef sandboxReq;
            CFStringRef entitlement = CFSTR("entitlement [\"com.apple.security.app-sandbox\"]");
            if (SecRequirementCreateWithString(entitlement, kSecCSDefaultFlags, &sandboxReq) == errSecSuccess) {
                if (SecCodeCheckValidity(secCodeSelf, kSecCSDefaultFlags, sandboxReq) == errSecSuccess)
                    inSandbox = true;
                CFRelease(sandboxReq);
            }
            CFRelease(secCodeSelf);
        }
#endif

        bool forAppStore = false;
        if (!inSandbox) {
            CFTypeRef val = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("ForAppStore"));
            forAppStore = (val &&
                           CFGetTypeID(val) == CFStringGetTypeID() &&
                           CFStringCompare(CFStringRef(val), CFSTR("yes"), kCFCompareCaseInsensitive) == 0);
        }

        useAppLocalStorage = inSandbox || forAppStore;
        initialized = true;
    }

    if (useAppLocalStorage) {
        // Ensure that the global and app-local settings go to the same file, since that's
        // what we really want
        if (organization == QLatin1String("Trolltech") ||
                organization.isEmpty() ||
                (organization == qApp->organizationDomain() && application == qApp->applicationName()) ||
                (organization == qApp->organizationName()) && application == qApp->applicationName())
        {
            CFStringRef bundleIdentifier = CFBundleGetIdentifier(CFBundleGetMainBundle());
            if (!bundleIdentifier) {
                qWarning("QSettingsPrivate::create: You must set the bundle identifier when using ForAppStore");
            } else {
                QSettingsPrivate* settings = new QMacSettingsPrivate(bundleIdentifier);
                if (organization == QLatin1String("Trolltech"))
                    settings->beginGroupOrArray(QSettingsGroup("QtLibrarySettings"));
                return settings;
            }
        }
   }
#endif

    if (format == QSettings::NativeFormat) {
        return new QMacSettingsPrivate(scope, organization, application);
    } else {
        return new QConfFileSettingsPrivate(format, scope, organization, application);
    }
}
OSStatus SecAccessCreateWithTrustedApplications(CFStringRef trustedApplicationsPListPath, CFStringRef accessLabel, Boolean allowAny, SecAccessRef* returnedAccess)
{
	OSStatus err = errSecSuccess;
	SecAccessRef accessToReturn=nil;
	CFMutableArrayRef trustedApplications=nil;

	if (!allowAny) // use default access ("confirm access")
	{
		// make an exception list of applications you want to trust,
		// which are allowed to access the item without requiring user confirmation
		SecTrustedApplicationRef myself=NULL, someOther=NULL;
        CFArrayRef trustedAppListFromBundle=NULL;

        trustedApplications=CFArrayCreateMutable(kCFAllocatorDefault,0,&kCFTypeArrayCallBacks);
        err = SecTrustedApplicationCreateFromPath(NULL, &myself);
        if (!err)
            CFArrayAppendValue(trustedApplications,myself);

		CFURLRef url = CFURLCreateWithFileSystemPath(NULL, trustedApplicationsPListPath, kCFURLPOSIXPathStyle, 0);
		CFStringRef leafStr = NULL;
		leafStr = CFURLCopyLastPathComponent(url);

		CFURLRef bndlPathURL = NULL;
		bndlPathURL = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
		CFStringRef bndlPath = NULL;
		bndlPath = CFURLCopyFileSystemPath(bndlPathURL, kCFURLPOSIXPathStyle);
        trustedAppListFromBundle=copyTrustedAppListFromBundle(bndlPath, leafStr);
		if ( leafStr )
			CFRelease(leafStr);
		if ( bndlPath )
			CFRelease(bndlPath);
		if ( url )
			CFRelease(url);
		if ( bndlPathURL )
			CFRelease(bndlPathURL);
        if (trustedAppListFromBundle)
        {
            CFIndex ix,top;
            char buffer[MAXPATHLEN];
            top = CFArrayGetCount(trustedAppListFromBundle);
            for (ix=0;ix<top;ix++)
            {
                CFStringRef filename = (CFStringRef)CFArrayGetValueAtIndex(trustedAppListFromBundle,ix);
                CFIndex stringLength = CFStringGetLength(filename);
                CFIndex usedBufLen;

                if (stringLength != CFStringGetBytes(filename,CFRangeMake(0,stringLength),kCFStringEncodingUTF8,0,
                    false,(UInt8 *)&buffer,MAXPATHLEN, &usedBufLen))
                    break;
                buffer[usedBufLen] = 0;
				//
				// Support specification of trusted applications by either
				// a full pathname or a code requirement string.
				//
				if (buffer[0]=='/')
				{
					err = SecTrustedApplicationCreateFromPath(buffer,&someOther);
				}
				else
				{
					char *buf = NULL;
					CFStringRef reqStr = filename;
					CFArrayRef descArray = CFStringCreateArrayBySeparatingStrings(NULL, reqStr, CFSTR("\""));
					if (descArray && (CFArrayGetCount(descArray) > 1))
					{
						CFStringRef descStr = (CFStringRef) CFArrayGetValueAtIndex(descArray, 1);
						if (descStr)
							buf = CFStringToCString(descStr);
					}
					SecRequirementRef reqRef = NULL;
					err = SecRequirementCreateWithString(reqStr, kSecCSDefaultFlags, &reqRef);
					if (!err)
						err = SecTrustedApplicationCreateFromRequirement((const char *)buf, reqRef, &someOther);
					if (buf)
						free(buf);
					CFReleaseSafe(reqRef);
					CFReleaseSafe(descArray);
				}
                if (!err)
                    CFArrayAppendValue(trustedApplications,someOther);

				if (someOther)
					CFReleaseNull(someOther);
            }
            CFRelease(trustedAppListFromBundle);
        }
	}

	err = SecAccessCreate((CFStringRef)accessLabel, (CFArrayRef)trustedApplications, &accessToReturn);
    if (!err)
	{
		if (allowAny) // change access to be wide-open for decryption ("always allow access")
		{
			// get the access control list for decryption operations
			CFArrayRef aclList=nil;
			err = SecAccessCopySelectedACLList(accessToReturn, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
			if (!err)
			{
				// get the first entry in the access control list
				SecACLRef aclRef=(SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
				CFArrayRef appList=nil;
				CFStringRef promptDescription=nil;
				CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
				err = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector);

				// modify the default ACL to not require the passphrase, and have a nil application list
				promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE;
				err = SecACLSetSimpleContents(aclRef, NULL, promptDescription, &promptSelector);

				if (appList) CFRelease(appList);
				if (promptDescription) CFRelease(promptDescription);
			}
		}
	}
	*returnedAccess = accessToReturn;
	return err;
}
Exemplo n.º 6
0
//
// Installer archive.
// Hybrid policy: If we detect an installer signature, use and validate that.
// If we don't, check for a code signature instead.
//
void PolicyEngine::evaluateInstall(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result)
{
	const AuthorityType type = kAuthorityInstall;

	Xar xar(cfString(path).c_str());
	if (!xar) {
		// follow the code signing path
		evaluateCode(path, type, flags, context, result);
		return;
	}
	
	SQLite3::int64 latentID = 0;		// first (highest priority) disabled matching ID
	std::string latentLabel;			// ... and associated label, if any
	if (!xar.isSigned()) {
		// unsigned xar
		if (SYSPOLICY_ASSESS_OUTCOME_UNSIGNED_ENABLED())
			SYSPOLICY_ASSESS_OUTCOME_UNSIGNED(cfString(path).c_str(), type);
		cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
		addAuthority(result, "no usable signature");
		return;
	}
	if (CFRef<CFArrayRef> certs = xar.copyCertChain()) {
		CFRef<CFTypeRef> policy = installerPolicy();
		CFRef<SecTrustRef> trust;
		MacOSError::check(SecTrustCreateWithCertificates(certs, policy, &trust.aref()));
//		MacOSError::check(SecTrustSetAnchorCertificates(trust, cfEmptyArray())); // no anchors
		MacOSError::check(SecTrustSetOptions(trust, kSecTrustOptionAllowExpired | kSecTrustOptionImplicitAnchors));

		SecTrustResultType trustResult;
		MacOSError::check(SecTrustEvaluate(trust, &trustResult));
		CFRef<CFArrayRef> chain;
		CSSM_TP_APPLE_EVIDENCE_INFO *info;
		MacOSError::check(SecTrustGetResult(trust, &trustResult, &chain.aref(), &info));
			
		if (flags & kSecAssessmentFlagRequestOrigin)
			setOrigin(chain, result);
		
		switch (trustResult) {
		case kSecTrustResultProceed:
		case kSecTrustResultUnspecified:
			break;
		default:
			{
				OSStatus rc;
				MacOSError::check(SecTrustGetCssmResultCode(trust, &rc));
				MacOSError::throwMe(rc);
			}
		}

		SQLite::Statement query(*this,
			"SELECT allow, requirement, id, label, flags, disabled FROM scan_authority"
			" WHERE type = :type"
			" ORDER BY priority DESC;");
		query.bind(":type").integer(type);
		while (query.nextRow()) {
			bool allow = int(query[0]);
			const char *reqString = query[1];
			SQLite3::int64 id = query[2];
			const char *label = query[3];
			//sqlite_uint64 ruleFlags = query[4];
			SQLite3::int64 disabled = query[5];
	
			CFRef<SecRequirementRef> requirement;
			MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
			switch (OSStatus rc = SecRequirementEvaluate(requirement, chain, NULL, kSecCSDefaultFlags)) {
			case noErr: // success
				break;
			case errSecCSReqFailed: // requirement missed, but otherwise okay
				continue;
			default: // broken in some way; all tests will fail like this so bail out
				MacOSError::throwMe(rc);
			}
			if (disabled) {
				if (latentID == 0) {
					latentID = id;
					if (label)
						latentLabel = label;
				}
				continue;	// the loop
			}

			if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED() || SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED()) {
				if (allow)
					SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, NULL);
				else
					SYSPOLICY_ASSESS_OUTCOME_DENY(cfString(path).c_str(), type, label, NULL);
			}

			// not adding to the object cache - we could, but it's not likely to be worth it
			cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
			addAuthority(result, label, id);
			return;
		}
	}
	if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED())
		SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cfString(path).c_str(), type, latentLabel.c_str(), NULL);
	
	// no applicable authority. Deny by default
	cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
	addAuthority(result, latentLabel.c_str(), latentID);
}
Exemplo n.º 7
0
//
// Executable code.
// Read from disk, evaluate properly, cache as indicated. The whole thing, so far.
//
void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result,
	bool handleUnsignedCode /* = true */)
{
	FileQuarantine qtn(cfString(path).c_str());
	if (qtn.flag(QTN_FLAG_HARD))
		MacOSError::throwMe(errSecCSFileHardQuarantined);
	
	CFRef<SecStaticCodeRef> code;
	MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()));
	OSStatus rc = noErr;	// last validation error
	
	const SecCSFlags validationFlags = kSecCSEnforceRevocationChecks;
	
	WhitelistPrescreen whitelistScreen(code); // pre-screening filter for whitelist pre-screening (only)

	SQLite::Statement query(*this,
		"SELECT allow, requirement, id, label, expires, flags, disabled, filter_unsigned, remarks 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];
		SQLite3::int64 id = query[2];
		const char *label = query[3];
		double expires = query[4];
		sqlite3_int64 ruleFlags = query[5];
		SQLite3::int64 disabled = query[6];
		const char *filter = query[7];
		const char *remarks = query[8];
		
		CFRef<SecRequirementRef> requirement;
		MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
		rc = SecStaticCodeCheckValidity(code, validationFlags, requirement);
		
		// ad-hoc sign unsigned code, skip of Gatekeeper is off or the rule is disabled; but always do it for whitelist recording
		if (rc == errSecCSUnsigned && handleUnsignedCode && (!(disabled || overrideAssessment()) || SYSPOLICY_RECORDER_MODE_ENABLED())) {
			if (!SYSPOLICY_RECORDER_MODE_ENABLED()) {
				// apply whitelist pre-screening to speed things up for non-matches
				if (ruleFlags & kAuthorityFlagDefault)	// can't ever match standard rules with unsigned code
					continue;
				if (whitelistScreen.reject(filter, remarks))	// apply whitelist pre-filter
					continue;
			}
			try {
				// ad-hoc sign the code and attach the signature
				CFRef<CFDataRef> signature = CFDataCreateMutable(NULL, 0);
				CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached, signature.get(), kSecCodeSignerIdentity);
				CFRef<SecCodeSignerRef> signer;
				MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref()));
				MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
				MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags));
								
				// if we're in GKE recording mode, save that signature and report its location
				if (SYSPOLICY_RECORDER_MODE_ENABLED()) {
					int status = recorder_code_unable;	// ephemeral signature (not recorded)
					if (geteuid() == 0) {
						CFRef<CFUUIDRef> uuid = CFUUIDCreate(NULL);
						std::string sigfile = RECORDER_DIR + cfStringRelease(CFUUIDCreateString(NULL, uuid)) + ".tsig";
						try {
							UnixPlusPlus::AutoFileDesc fd(sigfile, O_WRONLY | O_CREAT);
							fd.write(CFDataGetBytePtr(signature), CFDataGetLength(signature));
							status = recorder_code_adhoc;	// recorded signature
							SYSPOLICY_RECORDER_MODE_ADHOC_PATH(cfString(path).c_str(), type, sigfile.c_str());
						} catch (...) { }
					}

					// now report the D probe itself
					CFRef<CFDictionaryRef> info;
					MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
					CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
					SYSPOLICY_RECORDER_MODE(cfString(path).c_str(), type, "",
						cdhash ? CFDataGetBytePtr(cdhash) : NULL, status);
				}
				
				// rerun the validation to update state
				rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSBasicValidateOnly, requirement);
			} catch (...) { }
		}

		switch (rc) {
		case noErr: // well signed and satisfies requirement...
			break;	// ... continue below
		case errSecCSSignatureFailed:
			if (!codeInvalidityExceptions(code, result)) {
				if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED())
					SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, false);
				MacOSError::throwMe(rc);
			}
			if (SYSPOLICY_ASSESS_OUTCOME_BROKEN_ENABLED())
				SYSPOLICY_ASSESS_OUTCOME_BROKEN(cfString(path).c_str(), type, true);
			// treat as unsigned to fix problems in the field
		case errSecCSUnsigned:
			if (handleUnsignedCode) {
				cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
				addAuthority(result, "no usable 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);
		}
		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))) {
					this->recordOutcome(code, allow, type, min(expires, dateToJulian(limit)), id);
				}
			}
		}
		if (allow) {
			if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED()) {
				if (!info)
					MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
				CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
				SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, cdhash ? CFDataGetBytePtr(cdhash) : NULL);
			}
		} else {
			if (SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) {
				if (!info)
					MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
				CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
				std::string cpath = cfString(path);
				const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL;
				SYSPOLICY_ASSESS_OUTCOME_DENY(cpath.c_str(), type, label, hashp);
				SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, label, hashp, recorder_code_untrusted);
			}
		}
		cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
		addAuthority(result, label, id);
		return;
	}

	if (rc == errSecCSUnsigned) {	// skipped all applicable rules due to pre-screening
		cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
		addAuthority(result, "no usable signature");
		return;
	}
	
	// no applicable authority (but signed, perhaps temporarily). Deny by default
	CFRef<CFDictionaryRef> info;
	MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
	if (flags & kSecAssessmentFlagRequestOrigin) {
		if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates)))
			setOrigin(chain, result);
	}
	if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) {
		CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
		const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL;
		std::string cpath = cfString(path);
		SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cpath.c_str(), type, latentLabel.c_str(), hashp);
		SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, latentLabel.c_str(), hashp, 0);
	}
	if (!(flags & kSecAssessmentFlagNoCache))
		this->recordOutcome(code, false, type, this->julianNow() + NEGATIVE_HOLD, latentID);
	cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
	addAuthority(result, latentLabel.c_str(), latentID);
}
void PolicyEngine::evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, AuthorityType type, SecAssessmentFlags flags, bool nested, CFMutableDictionaryRef result)
{
	
	SQLite::Statement query(*this,
		"SELECT allow, requirement, id, label, expires, flags, disabled, filter_unsigned, remarks 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];
		SQLite3::int64 id = query[2];
		const char *label = query[3];
		double expires = query[4];
		sqlite3_int64 ruleFlags = query[5];
		SQLite3::int64 disabled = query[6];
//		const char *filter = query[7];
//		const char *remarks = query[8];
		
		CFRef<SecRequirementRef> requirement;
		MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref()));
		switch (OSStatus rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, requirement)) {
		case errSecSuccess:
			break;						// rule match; process below
		case errSecCSReqFailed:
			continue;					// rule does not apply
		case errSecCSVetoed:
			return;						// nested code has failed to pass
		default:
			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 : "";
			continue;
		}
		
		// current rule is first rule (in priority order) that matched. Apply it
		if (nested)	// success, nothing to record
			return;

		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))) {
					this->recordOutcome(code, allow, type, min(expires, dateToJulian(limit)), id);
				}
			}
		}
		if (allow) {
			if (SYSPOLICY_ASSESS_OUTCOME_ACCEPT_ENABLED()) {
				if (!info)
					MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
				CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
				SYSPOLICY_ASSESS_OUTCOME_ACCEPT(cfString(path).c_str(), type, label, cdhash ? CFDataGetBytePtr(cdhash) : NULL);
			}
		} else {
			if (SYSPOLICY_ASSESS_OUTCOME_DENY_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) {
				if (!info)
					MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
				CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
				std::string cpath = cfString(path);
				const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL;
				SYSPOLICY_ASSESS_OUTCOME_DENY(cpath.c_str(), type, label, hashp);
				SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, label, hashp, recorder_code_untrusted);
			}
		}
		cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, allow);
		addAuthority(flags, result, label, id);
		return;
	}
	
	// no applicable authority (but signed, perhaps temporarily). Deny by default
	CFRef<CFDictionaryRef> info;
	MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
	if (flags & kSecAssessmentFlagRequestOrigin) {
		if (CFArrayRef chain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates)))
			setOrigin(chain, result);
	}
	if (SYSPOLICY_ASSESS_OUTCOME_DEFAULT_ENABLED() || SYSPOLICY_RECORDER_MODE_ENABLED()) {
		CFDataRef cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
		const void *hashp = cdhash ? CFDataGetBytePtr(cdhash) : NULL;
		std::string cpath = cfString(path);
		SYSPOLICY_ASSESS_OUTCOME_DEFAULT(cpath.c_str(), type, latentLabel.c_str(), hashp);
		SYSPOLICY_RECORDER_MODE(cpath.c_str(), type, latentLabel.c_str(), hashp, 0);
	}
	if (!(flags & kSecAssessmentFlagNoCache))
		this->recordOutcome(code, false, type, this->julianNow() + NEGATIVE_HOLD, latentID);
	cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
	addAuthority(flags, result, latentLabel.c_str(), latentID);
}