/* * Copyright (c) 2011-2013 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "xpcengine.h" #include #include #include #include #include namespace Security { namespace CodeSigning { static void doProgress(xpc_object_t msg); 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 void init() { dispatch_once(&dispatchInit, ^void(void) { const char *name = serviceName; if (const char *env = getenv("SYSPOLICYNAME")) name = env; queue = dispatch_queue_create("spd-client", 0); 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); } } }); xpc_connection_resume(service); }); } // // Your standard XPC client-side machinery // class Message { public: xpc_object_t obj; Message(const char *function) { init(); obj = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(obj, "function", function); } ~Message() { if (obj) xpc_release(obj); } operator xpc_object_t () { return obj; } void send() { xpc_object_t reply = xpc_connection_send_message_with_reply_sync(service, obj); xpc_release(obj); obj = NULL; xpc_type_t type = xpc_get_type(reply); if (type == XPC_TYPE_DICTIONARY) { obj = reply; if (int64_t error = xpc_dictionary_get_int64(obj, "error")) MacOSError::throwMe((int)error); } else if (type == XPC_TYPE_ERROR) { const char *s = xpc_copy_description(reply); printf("Error returned: %s\n", s); free((char*)s); MacOSError::throwMe(errSecCSInternalError); } else { const char *s = xpc_copy_description(reply); printf("Unexpected type of return object: %s\n", s); free((char*)s); } } }; static void copyCFDictionary(const void *key, const void *value, void *ctx) { CFMutableDictionaryRef target = CFMutableDictionaryRef(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 { CFDictionaryAddValue(target, key, value); } } void xpcEngineAssess(CFURLRef path, SecAssessmentFlags 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 ctx = makeCFMutableDictionary(); if (context) CFDictionaryApplyFunction(context, copyCFDictionary, ctx); CFRef 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((int)error); size_t resultLength; const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength); CFRef resultDict = makeCFDictionaryFrom(resultData, resultLength); CFDictionaryApplyFunction(resultDict, copyCFDictionary, result); CFDictionaryAddValue(result, CFSTR("assessment:remote"), kCFBooleanTrue); } static void doProgress(xpc_object_t msg) { uint64_t current = xpc_dictionary_get_uint64(msg, "current"); 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); CFTemp info("{current=%d,total=%d}", current, total); Boolean proceed = feedback(kSecAssessmentFeedbackProgress, info); if (!proceed) { xpc_connection_t connection = xpc_dictionary_get_remote_connection(msg); xpc_object_t cancelRequest = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(cancelRequest, "function", "cancel"); xpc_dictionary_set_string(cancelRequest, "token", token); xpc_connection_send_message(connection, cancelRequest); xpc_release(cancelRequest); } } CFDictionaryRef xpcEngineUpdate(CFTypeRef target, SecAssessmentFlags 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(CFNumberRef(target))); else if (CFGetTypeID(target) == CFURLGetTypeID()) xpc_dictionary_set_string(msg, "url", cfString(CFURLRef(target)).c_str()); else if (CFGetTypeID(target) == SecRequirementGetTypeID()) { CFRef 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 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 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((int)error); size_t resultLength; const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength); return makeCFDictionaryFrom(resultData, resultLength); } bool xpcEngineControl(const char *control) { Message msg("control"); xpc_dictionary_set_string(msg, "control", control); msg.send(); return true; } void xpcEngineRecord(CFDictionaryRef info) { Message msg("record"); CFRef infoData = makeCFData(CFDictionaryRef(info)); xpc_dictionary_set_data(msg, "info", CFDataGetBytePtr(infoData), CFDataGetLength(infoData)); msg.send(); } } // end namespace CodeSigning } // end namespace Security