Пример #1
0
//
// Perform common argument normalizations for update operations
//
static void normalizeTarget(CFRef<CFTypeRef> &target, CFDictionary &context, std::string *signUnsigned)
{
	// turn CFURLs into (designated) SecRequirements
	if (target && CFGetTypeID(target) == CFURLGetTypeID()) {
		CFRef<SecStaticCodeRef> code;
		MacOSError::check(SecStaticCodeCreateWithPath(target.as<CFURLRef>(), kSecCSDefaultFlags, &code.aref()));
		switch (OSStatus rc = SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref())) {
		case noErr: {
			// use the *default* DR to avoid unreasonably wide DRs opening up Gatekeeper to attack
			CFRef<CFDictionaryRef> info;
			MacOSError::check(SecCodeCopySigningInformation(code, kSecCSRequirementInformation, &info.aref()));
			target = CFDictionaryGetValue(info, kSecCodeInfoImplicitDesignatedRequirement);
			}
			break;
		case errSecCSUnsigned:
			if (signUnsigned) {
				// Ad-hoc sign the code temporarily so we can get its code requirement
				CFRef<CFDataRef> signature = CFDataCreateMutable(NULL, 0);
				CFRef<SecCodeSignerRef> signer;
				CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N}", kSecCodeSignerDetached, signature.get(), kSecCodeSignerIdentity);
				MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref()));
				MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
				MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags));
				MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref()));
				CFRef<CFDictionaryRef> info;
				MacOSError::check(SecCodeCopySigningInformation(code, kSecCSInternalInformation, &info.aref()));
				if (CFDataRef cdData = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoCodeDirectory)))
					*signUnsigned = ((const CodeDirectory *)CFDataGetBytePtr(cdData))->screeningCode();
				break;
			}
			MacOSError::check(rc);
		case errSecCSSignatureFailed:
			// recover certain cases of broken signatures (well, try)
			if (codeInvalidityExceptions(code, NULL)) {
				// Ad-hoc sign the code in place (requiring a writable subject). This requires root privileges.
				CFRef<SecCodeSignerRef> signer;
				CFTemp<CFDictionaryRef> arguments("{%O=#N}", kSecCodeSignerIdentity);
				MacOSError::check(SecCodeSignerCreate(arguments, kSecCSDefaultFlags, &signer.aref()));
				MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
				MacOSError::check(SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, (SecRequirementRef *)&target.aref()));
				break;
			}
			MacOSError::check(rc);
		default:
			MacOSError::check(rc);
		}
		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);
		}
	}
}
Пример #2
0
//
// Perform update authorization processing.
// Throws an exception if authorization is denied.
//
static void authorizeUpdate(SecAssessmentFlags 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));
}
Пример #3
0
//
// Take an assessment outcome and record it in the object cache
//
void PolicyEngine::recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, SQLite::int64 authority)
{
	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()));
	assert(expires);
	SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "caching");
	SQLite::Statement insert(*this,
		"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);
	insert.bind(":authority").integer(authority);
	insert.execute();
	xact.commit();
}
Пример #4
0
//
// Add a rule to the policy database
//
CFDictionaryRef PolicyEngine::add(CFTypeRef inTarget, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context)
{
	// default type to execution
	if (type == kAuthorityInvalid)
		type = kAuthorityExecute;

	authorizeUpdate(flags, context);
	CFDictionary ctx(context, errSecCSInvalidAttributeValues);
	CFCopyRef<CFTypeRef> target = inTarget;
	CFRef<CFDataRef> bookmark = NULL;
	std::string filter_unsigned;

	switch (type) {
	case kAuthorityExecute:
		normalizeTarget(target, ctx, &filter_unsigned);
		// bookmarks are untrusted and just a hint to callers
		bookmark = ctx.get<CFDataRef>(kSecAssessmentRuleKeyBookmark);
		break;
	case kAuthorityInstall:
		if (inTarget && CFGetTypeID(inTarget) == CFURLGetTypeID()) {
			// no good way to turn an installer file into a requirement. Pretend to succeeed so caller proceeds
			return cfmake<CFDictionaryRef>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("virtual install"));
		}
		break;
	case kAuthorityOpenDoc:
		// handle document-open differently: use quarantine flags for whitelisting
		if (!target || CFGetTypeID(target) != CFURLGetTypeID())	// can only "add" file paths
			MacOSError::throwMe(errSecCSInvalidObjectRef);
		try {
			std::string spath = cfString(target.as<CFURLRef>());
			FileQuarantine qtn(spath.c_str());
			qtn.setFlag(QTN_FLAG_ASSESSMENT_OK);
			qtn.applyTo(spath.c_str());
		} catch (const CommonError &error) {
			// could not set quarantine flag - report qualified success
			return cfmake<CFDictionaryRef>("{%O=%O,'assessment:error'=%d}",
				kSecAssessmentAssessmentAuthorityOverride, CFSTR("error setting quarantine"), error.osStatus());
		} catch (...) {
			return cfmake<CFDictionaryRef>("{%O=%O}", kSecAssessmentAssessmentAuthorityOverride, CFSTR("unable to set quarantine"));
		}
		return NULL;
	}
	
	// if we now have anything else, we're busted
	if (!target || CFGetTypeID(target) != SecRequirementGetTypeID())
		MacOSError::throwMe(errSecCSInvalidObjectRef);

	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 = dateToJulian(time);
	if (CFBooleanRef allowing = ctx.get<CFBooleanRef>(kSecAssessmentUpdateKeyAllow))
		allow = allowing == kCFBooleanTrue;
	if (CFStringRef rem = ctx.get<CFStringRef>(kSecAssessmentUpdateKeyRemarks))
		remarks = cfString(rem);

	CFRef<CFStringRef> requirementText;
	MacOSError::check(SecRequirementCopyString(target.as<SecRequirementRef>(), kSecCSDefaultFlags, &requirementText.aref()));
	SQLite::Transaction xact(*this, SQLite3::Transaction::deferred, "add_rule");
	SQLite::Statement insert(*this,
		"INSERT INTO authority (type, allow, requirement, priority, label, expires, filter_unsigned, remarks)"
		"	VALUES (:type, :allow, :requirement, :priority, :label, :expires, :filter_unsigned, :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;
	insert.bind(":expires") = expires;
	insert.bind(":filter_unsigned") = filter_unsigned.empty() ? NULL : filter_unsigned.c_str();
	if (!remarks.empty())
		insert.bind(":remarks") = remarks;
	insert.execute();
	SQLite::int64 newRow = this->lastInsert();
	if (bookmark) {
		SQLite::Statement bi(*this, "INSERT INTO bookmarkhints (bookmark, authority) VALUES (:bookmark, :authority)");
		bi.bind(":bookmark") = CFDataRef(bookmark);
		bi.bind(":authority").integer(newRow);
		bi.execute();
	}
	this->purgeObjects(priority);
	xact.commit();
	notify_post(kNotifySecAssessmentUpdate);
	return cfmake<CFDictionaryRef>("{%O=%d}", kSecAssessmentUpdateKeyRow, newRow);
}
Пример #5
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);
}
bool PolicyEngine::temporarySigning(SecStaticCodeRef code, AuthorityType type, CFURLRef path, SecAssessmentFlags matchFlags)
{
	if (matchFlags == 0) {	// playback; consult authority table for matches
		DiskRep *rep = SecStaticCode::requiredStatic(code)->diskRep();
		std::string screen;
		if (CFRef<CFDataRef> info = rep->component(cdInfoSlot)) {
			SHA1 hash;
			hash.update(CFDataGetBytePtr(info), CFDataGetLength(info));
			screen = createWhitelistScreen('I', hash);
		} else if (rep->mainExecutableImage()) {
			screen = "N";
		} else {
			SHA1 hash;
			hashFileData(rep->mainExecutablePath().c_str(), &hash);
			screen = createWhitelistScreen('M', hash);
		}
		SQLite::Statement query(*this,
			"SELECT flags FROM authority "
			"WHERE type = :type"
			" AND NOT flags & :flag"
			" AND CASE WHEN filter_unsigned IS NULL THEN remarks = :remarks ELSE filter_unsigned = :screen END");
		query.bind(":type").integer(type);
		query.bind(":flag").integer(kAuthorityFlagDefault);
		query.bind(":screen") = screen;
		query.bind(":remarks") = cfString(path);
		if (!query.nextRow())	// guaranteed no matching rule
			return false;
		matchFlags = SQLite3::int64(query[0]);
	}

	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, (matchFlags & kAuthorityFlagWhitelistV2) ? kSecCSSignOpaque : kSecCSSignV1, &signer.aref()));
		MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags));
		MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags));
		
		SecRequirementRef dr = NULL;
		SecCodeCopyDesignatedRequirement(code, kSecCSDefaultFlags, &dr);
		CFStringRef drs = NULL;
		SecRequirementCopyString(dr, kSecCSDefaultFlags, &drs);

		// 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);
		}
		
		return true;	// it worked; we're now (well) signed
	} catch (...) { }
	
	return false;
}
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);
}