// // Generate and attach an ad-hoc opaque signature // Use SHA-1 digests because that's what the whitelist is made with // static void attachOpaque(SecStaticCodeRef code, SecAssessmentFeedback feedback) { CFTemp<CFDictionaryRef> rules("{" // same resource rules as used for collection "rules={" "'^.*' = #T" "'^Info\\.plist$' = {omit=#T,weight=10}" "},rules2={" "'^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/' = {nested=#T, weight=0}" "'^.*' = #T" "'^Info\\.plist$' = {omit=#T,weight=10}" "'^[^/]+$' = {top=#T, weight=0}" "}" "}"); CFRef<CFDataRef> signature = CFDataCreateMutable(NULL, 0); CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N, %O=%d, %O=%O}", kSecCodeSignerDetached, signature.get(), kSecCodeSignerIdentity, /* kCFNull, */ kSecCodeSignerDigestAlgorithm, kSecCodeSignatureHashSHA1, kSecCodeSignerResourceRules, rules.get()); CFRef<SecCodeSignerRef> signer; SecCSFlags creationFlags = kSecCSSignOpaque | kSecCSSignNoV1 | kSecCSSignBundleRoot; SecCSFlags operationFlags = 0; if (feedback) operationFlags |= kSecCSReportProgress; MacOSError::check(SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, ^CFTypeRef(SecStaticCodeRef code, CFStringRef stage, CFDictionaryRef info) { if (CFEqual(stage, CFSTR("progress"))) { bool proceed = feedback(kSecAssessmentFeedbackProgress, info); if (!proceed) SecStaticCodeCancelValidation(code, kSecCSDefaultFlags); } return NULL; }));
// // Executable code. // Read from disk, evaluate properly, cache as indicated. // void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result, bool handleUnsigned) { // not really a Gatekeeper function... but reject all "hard quarantined" files because they were made from sandboxed sources without download privilege FileQuarantine qtn(cfString(path).c_str()); if (qtn.flag(QTN_FLAG_HARD)) MacOSError::throwMe(errSecCSFileHardQuarantined); CFCopyRef<SecStaticCodeRef> code; MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref())); SecCSFlags validationFlags = kSecCSEnforceRevocationChecks | kSecCSCheckAllArchitectures; if (!(flags & kSecAssessmentFlagAllowWeak)) validationFlags |= kSecCSStrictValidate; adjustValidation(code); // 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; } // ad-hoc sign unsigned code if (rc == errSecCSUnsigned && handleUnsigned && (!overrideAssessment(flags) || SYSPOLICY_RECORDER_MODE_ENABLED())) { if (temporarySigning(code, type, path, 0)) { rc = errSecSuccess; // clear unsigned; we are now well-signed validationFlags |= kSecCSBasicValidateOnly; // no need to re-validate deep contents } } // prepare for deep traversal of (hopefully) good signatures SecAssessmentFeedback feedback = SecAssessmentFeedback(CFDictionaryGetValue(context, kSecAssessmentContextKeyFeedback)); MacOSError::check(SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, ^CFTypeRef (SecStaticCodeRef item, CFStringRef cfStage, CFDictionaryRef info) { string stage = cfString(cfStage); if (stage == "prepared") { if (!CFEqual(item, code)) // genuine nested (not top) code adjustValidation(item); } else if (stage == "progress") { if (feedback && CFEqual(item, code)) { // top level progress bool proceed = feedback(kSecAssessmentFeedbackProgress, info); if (!proceed) SecStaticCodeCancelValidation(code, kSecCSDefaultFlags); } } else if (stage == "validated") { SecStaticCodeSetCallback(item, kSecCSDefaultFlags, NULL, NULL); // clear callback to avoid unwanted recursion evaluateCodeItem(item, path, type, flags, item != code, result); if (CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict)) if (CFEqual(verdict, kCFBooleanFalse)) return makeCFNumber(OSStatus(errSecCSVetoed)); // (signal nested-code policy failure, picked up below) } return NULL; }));