X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/e3d460c9de4426da6c630c3ae3f46173a99f82d8..0d4552ce43ff8bf2e8666a9c5c44c3590eb117a8:/OSX/libsecurity_codesigning/lib/xpcengine.cpp?ds=sidebyside diff --git a/OSX/libsecurity_codesigning/lib/xpcengine.cpp b/OSX/libsecurity_codesigning/lib/xpcengine.cpp index 6e415298..d6e37ed9 100644 --- a/OSX/libsecurity_codesigning/lib/xpcengine.cpp +++ b/OSX/libsecurity_codesigning/lib/xpcengine.cpp @@ -25,8 +25,10 @@ #include #include #include +#include #include +#include namespace Security { namespace CodeSigning { @@ -41,20 +43,27 @@ static const char serviceName[] = "com.apple.security.syspolicy"; static dispatch_once_t dispatchInit; // one-time init marker static xpc_connection_t service; // connection to spd static dispatch_queue_t queue; // dispatch queue for service - + +static map *feedbackBlocks; + static void init() { dispatch_once(&dispatchInit, ^void(void) { + feedbackBlocks = new map; const char *name = serviceName; if (const char *env = getenv("SYSPOLICYNAME")) name = env; - queue = dispatch_queue_create("spd-client", 0); + queue = dispatch_queue_create("spd-client", DISPATCH_QUEUE_SERIAL); service = xpc_connection_create_mach_service(name, queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); xpc_connection_set_event_handler(service, ^(xpc_object_t msg) { if (xpc_get_type(msg) == XPC_TYPE_DICTIONARY) { const char *function = xpc_dictionary_get_string(msg, "function"); - if (!strcmp(function, "progress")) { - doProgress(msg); + if (strcmp(function, "progress") == 0) { + try { + doProgress(msg); + } catch (...) { + Syslog::error("Discarding progress handler exception"); + } } } }); @@ -96,8 +105,9 @@ public: } else if (type == XPC_TYPE_ERROR) { const char *s = xpc_copy_description(reply); printf("Error returned: %s\n", s); + Syslog::notice("code signing internal problem: unexpected error from xpc: %s", s); free((char*)s); - MacOSError::throwMe(errSecCSInternalError); + MacOSError::throwMe(errSecCSInternalError); } else { const char *s = xpc_copy_description(reply); printf("Unexpected type of return object: %s\n", s); @@ -114,26 +124,27 @@ static void copyCFDictionary(const void *key, const void *value, void *ctx) if (CFGetTypeID(value) == CFURLGetTypeID()) { CFRef path = CFURLCopyFileSystemPath(CFURLRef(value), kCFURLPOSIXPathStyle); CFDictionaryAddValue(target, key, path); - } else if (CFEqual(key, kSecAssessmentContextKeyFeedback)) { - CFDictionaryAddValue(target, key, CFTempNumber(uint64_t(value))); - } else { + } else if (!CFEqual(key, kSecAssessmentContextKeyFeedback)) { CFDictionaryAddValue(target, key, value); } } -static void precheckAccess(CFURLRef path, CFDictionaryRef context) +static bool precheckAccess(CFURLRef path, CFDictionaryRef context) { CFTypeRef type = CFDictionaryGetValue(context, kSecAssessmentContextKeyOperation); if (type == NULL || CFEqual(type, kSecAssessmentOperationTypeExecute)) { CFRef code; - MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref())); + OSStatus rc = SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref()); + if (rc == errSecCSBadBundleFormat) // work around + return false; CFRef exec; MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &exec.aref())); UnixError::check(::access(cfString(exec).c_str(), R_OK)); } else { UnixError::check(access(cfString(path).c_str(), R_OK)); } + return true; } @@ -142,15 +153,46 @@ void xpcEngineAssess(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef co precheckAccess(path, context); Message msg("assess"); xpc_dictionary_set_string(msg, "path", cfString(path).c_str()); - xpc_dictionary_set_int64(msg, "flags", flags); + xpc_dictionary_set_uint64(msg, "flags", flags); CFRef ctx = makeCFMutableDictionary(); - if (context) + if (context) { CFDictionaryApplyFunction(context, copyCFDictionary, ctx); + } + + SecAssessmentFeedback feedback = (SecAssessmentFeedback)CFDictionaryGetValue(context, kSecAssessmentContextKeyFeedback); + + /* Map the feedback block to a random number for tracking, because we don't want + * to send over a pointer. */ + uint64_t __block feedbackId = 0; + if (feedback) { + dispatch_sync(queue, ^{ + bool added = false; + while (!added) { + /* Simple sequence number would probably be sufficient, + * but making the id unpredictable is also cheap enough here. */ + arc4random_buf(&feedbackId, sizeof(uint64_t)); + if ((*feedbackBlocks)[feedbackId] == NULL /* extremely certain */) { + (*feedbackBlocks)[feedbackId] = feedback; + added = true; + } + } + }); + CFDictionaryAddValue(ctx, kSecAssessmentContextKeyFeedback, CFTempNumber(feedbackId)); + } + CFRef contextData = makeCFData(CFDictionaryRef(ctx)); xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData)); msg.send(); + /* Done, feedback block won't be called anymore, + * so remove the feedback mapping from the global map. */ + if (feedback) { + dispatch_sync(queue, ^{ + feedbackBlocks->erase(feedbackId); + }); + } + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) MacOSError::throwMe((int)error); @@ -167,7 +209,18 @@ static void doProgress(xpc_object_t msg) uint64_t total = xpc_dictionary_get_uint64(msg, "total"); uint64_t ref = xpc_dictionary_get_uint64(msg, "ref"); const char *token = xpc_dictionary_get_string(msg, "token"); - SecAssessmentFeedback feedback = SecAssessmentFeedback(ref); + + SecAssessmentFeedback feedback = NULL; + + // doProgress is called on the queue, so no dispatch_sync here. + try { + feedback = feedbackBlocks->at(ref); + } catch (std::out_of_range) { + // Indicates that syspolicyd gave us something it shouldn't have. + Syslog::error("no feedback block registered with ID %lld", ref); + MacOSError::throwMe(errSecCSInternalError); + } + CFTemp info("{current=%d,total=%d}", current, total); Boolean proceed = feedback(kSecAssessmentFeedbackProgress, info); if (!proceed) { @@ -189,7 +242,9 @@ CFDictionaryRef xpcEngineUpdate(CFTypeRef target, SecAssessmentFlags flags, CFDi if (CFGetTypeID(target) == CFNumberGetTypeID()) xpc_dictionary_set_uint64(msg, "rule", cfNumber(CFNumberRef(target))); else if (CFGetTypeID(target) == CFURLGetTypeID()) { - precheckAccess(CFURLRef(target), context); + bool good = precheckAccess(CFURLRef(target), context); + if (!good) // work around + return makeCFDictionary(0); // pretend this worked xpc_dictionary_set_string(msg, "url", cfString(CFURLRef(target)).c_str()); } else if (CFGetTypeID(target) == SecRequirementGetTypeID()) { CFRef data; @@ -198,7 +253,7 @@ CFDictionaryRef xpcEngineUpdate(CFTypeRef target, SecAssessmentFlags flags, CFDi } else MacOSError::throwMe(errSecCSInvalidObjectRef); } - xpc_dictionary_set_int64(msg, "flags", flags); + xpc_dictionary_set_uint64(msg, "flags", flags); CFRef ctx = makeCFMutableDictionary(); if (context) CFDictionaryApplyFunction(context, copyCFDictionary, ctx); @@ -217,8 +272,8 @@ CFDictionaryRef xpcEngineUpdate(CFTypeRef target, SecAssessmentFlags flags, CFDi if (localAuthorization) AuthorizationFree(localAuthorization, kAuthorizationFlagDefaults); - if (int64_t error = xpc_dictionary_get_int64(msg, "error")) - MacOSError::throwMe((int)error); + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) + MacOSError::throwMe((int)error); size_t resultLength; const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength); @@ -244,6 +299,81 @@ void xpcEngineRecord(CFDictionaryRef info) msg.send(); } +void xpcEngineCheckDevID(CFBooleanRef* result) +{ + Message msg("check-dev-id"); + + msg.send(); + + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) { + MacOSError::throwMe((int)error); + } + + *result = xpc_dictionary_get_bool(msg,"result") ? kCFBooleanTrue : kCFBooleanFalse; +} + +void xpcEngineCheckNotarized(CFBooleanRef* result) +{ + Message msg("check-notarized"); + + msg.send(); + + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) { + MacOSError::throwMe((int)error); + } + + *result = xpc_dictionary_get_bool(msg,"result") ? kCFBooleanTrue : kCFBooleanFalse; +} + +void xpcEngineTicketRegister(CFDataRef ticketData) +{ + Message msg("ticket-register"); + xpc_dictionary_set_data(msg, "ticketData", CFDataGetBytePtr(ticketData), CFDataGetLength(ticketData)); + + msg.send(); + + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) { + MacOSError::throwMe((int)error); + } +} + +void xpcEngineTicketLookup(CFDataRef hashData, SecCSDigestAlgorithm hashType, SecAssessmentTicketFlags flags, double *date) +{ + Message msg("ticket-lookup"); + xpc_dictionary_set_data(msg, "hashData", CFDataGetBytePtr(hashData), CFDataGetLength(hashData)); + xpc_dictionary_set_uint64(msg, "hashType", hashType); + xpc_dictionary_set_uint64(msg, "flags", flags); + + msg.send(); + + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) { + MacOSError::throwMe((int)error); + } + + double local_date = xpc_dictionary_get_double(msg, "date"); + if (date && !isnan(local_date)) { + *date = local_date; + } +} + +void xpcEngineLegacyCheck(CFDataRef hashData, SecCSDigestAlgorithm hashType, CFStringRef teamID) +{ + Message msg("legacy-check"); + xpc_dictionary_set_data(msg, "hashData", CFDataGetBytePtr(hashData), CFDataGetLength(hashData)); + xpc_dictionary_set_uint64(msg, "hashType", hashType); + + // There may not be a team id, so just leave it off if there isn't since xpc_dictionary_set_string + // will return a NULL if the value isn't provided. + if (teamID) { + xpc_dictionary_set_string(msg, "teamID", CFStringGetCStringPtr(teamID, kCFStringEncodingUTF8)); + } + + msg.send(); + + if (int64_t error = xpc_dictionary_get_int64(msg, "error")) { + MacOSError::throwMe((int)error); + } +} } // end namespace CodeSigning } // end namespace Security