// // Given a bag of attribute values, automagically come up with a SecCode // without any other information. // This is meant to be the "just do what makes sense" generic call, for callers // who don't want to engage in the fascinating dance of manual guest enumeration. // // Note that we expect the logic embedded here to change over time (in backward // compatible fashion, one hopes), and that it's all right to use heuristics here // as long as it's done sensibly. // // Be warned that the present logic is quite a bit ad-hoc, and will likely not // handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated // hosting all that well. // SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) { // special case: with no attributes at all, return the root of trust if (CFDictionaryGetCount(attributes) == 0) return KernelCode::active()->retain(); // main logic: we need a pid, and we'll take a canonical guest id as an option int pid = 0; if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid)) CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); if (SecCode *process = KernelCode::active()->locateGuest(attributes)) { SecPointer<SecCode> code; code.take(process); // locateGuest gave us a retained object if (code->staticCode()->flag(kSecCodeSignatureHost)) { // might be a code host. Let's find out CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes); CFDictionaryRemoveValue(rest, kSecGuestAttributePid); if (SecCode *guest = code->locateGuest(rest)) return guest; } if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { // only "soft" attributes, and no hosting is happening. Return the (non-)host itself return code.yield(); } } MacOSError::throwMe(errSecCSNoSuchCode); }
// // 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); } } }
// // Result-creation helpers // void PolicyEngine::addAuthority(CFMutableDictionaryRef parent, const char *label, SQLite::int64 row, CFTypeRef cacheInfo) { CFRef<CFMutableDictionaryRef> auth = makeCFMutableDictionary(); if (label && label[0]) cfadd(auth, "{%O=%s}", kSecAssessmentAssessmentSource, label); if (row) CFDictionaryAddValue(auth, kSecAssessmentAssessmentAuthorityRow, CFTempNumber(row)); if (overrideAssessment()) CFDictionaryAddValue(auth, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); if (cacheInfo) CFDictionaryAddValue(auth, kSecAssessmentAssessmentFromCache, cacheInfo); CFDictionaryAddValue(parent, kSecAssessmentAssessmentAuthority, auth); }
CFMutableDictionaryRef makeCFMutableDictionary(unsigned count, ...) { CFMutableDictionaryRef dict = makeCFMutableDictionary(); if (count > 0) { va_list args; va_start(args, count); for (unsigned n = 0; n < count; n++) { CFTypeRef key = va_arg(args, CFTypeRef); CFTypeRef value = va_arg(args, CFTypeRef); CFDictionaryAddValue(dict, key, value); } va_end(args); } return dict; }
CFDictionaryRef 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); size_t resultLength; const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength); return makeCFDictionaryFrom(resultData, resultLength); }
CFDictionaryRef PolicyEngine::find(CFTypeRef target, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context) { SQLite::Statement query(*this); selectRules(query, "SELECT scan_authority.id, scan_authority.type, scan_authority.requirement, scan_authority.allow, scan_authority.label, scan_authority.priority, scan_authority.remarks, scan_authority.expires, scan_authority.disabled, bookmarkhints.bookmark FROM scan_authority LEFT OUTER JOIN bookmarkhints ON scan_authority.id = bookmarkhints.authority", "scan_authority", target, type, flags, context, " ORDER BY priority DESC"); CFRef<CFMutableArrayRef> found = makeCFMutableArray(0); while (query.nextRow()) { SQLite::int64 id = query[0]; int type = int(query[1]); const char *requirement = query[2]; int allow = int(query[3]); const char *label = query[4]; double priority = query[5]; const char *remarks = query[6]; double expires = query[7]; int disabled = int(query[8]); CFRef<CFDataRef> bookmark = query[9].data(); CFRef<CFMutableDictionaryRef> rule = makeCFMutableDictionary(5, kSecAssessmentRuleKeyID, CFTempNumber(id).get(), kSecAssessmentRuleKeyType, CFRef<CFStringRef>(typeNameFor(type)).get(), kSecAssessmentRuleKeyRequirement, CFTempString(requirement).get(), kSecAssessmentRuleKeyAllow, allow ? kCFBooleanTrue : kCFBooleanFalse, kSecAssessmentRuleKeyPriority, CFTempNumber(priority).get() ); if (label) CFDictionaryAddValue(rule, kSecAssessmentRuleKeyLabel, CFTempString(label)); if (remarks) CFDictionaryAddValue(rule, kSecAssessmentRuleKeyRemarks, CFTempString(remarks)); if (expires != never) CFDictionaryAddValue(rule, kSecAssessmentRuleKeyExpires, CFRef<CFDateRef>(julianToDate(expires))); if (disabled) CFDictionaryAddValue(rule, kSecAssessmentRuleKeyDisabled, CFTempNumber(disabled)); if (bookmark) CFDictionaryAddValue(rule, kSecAssessmentRuleKeyBookmark, bookmark); CFArrayAppendValue(found, rule); } if (CFArrayGetCount(found) == 0) MacOSError::throwMe(errSecCSNoMatches); return cfmake<CFDictionaryRef>("{%O=%O}", kSecAssessmentUpdateKeyFound, found.get()); }
void xpcEngineAssess(CFURLRef path, uint flags, CFDictionaryRef context, CFMutableDictionaryRef result) { Message msg("assess"); xpc_dictionary_set_string(msg, "path", cfString(path).c_str()); xpc_dictionary_set_int64(msg, "flags", flags); CFRef<CFMutableDictionaryRef> ctx = makeCFMutableDictionary(); if (context) CFDictionaryApplyFunction(context, copyCFDictionary, ctx); CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx)); xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData)); 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); CFDictionaryApplyFunction(resultDict, copyCFDictionary, result); CFDictionaryAddValue(result, CFSTR("assessment:remote"), kCFBooleanTrue); }