1/*
2 * Copyright (c) 2011-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include "xpcengine.h"
24#include <xpc/connection.h>
25#include <syslog.h>
26#include <CoreFoundation/CoreFoundation.h>
27#include <security_utilities/cfutilities.h>
28#include <security_utilities/cfmunge.h>
29
30
31namespace Security {
32namespace CodeSigning {
33
34
35static void doProgress(xpc_object_t msg);
36
37
38static const char serviceName[] = "com.apple.security.syspolicy";
39
40
41static dispatch_once_t dispatchInit;		// one-time init marker
42static xpc_connection_t service;			// connection to spd
43static dispatch_queue_t queue;				// dispatch queue for service
44
45static void init()
46{
47	dispatch_once(&dispatchInit, ^void(void) {
48		const char *name = serviceName;
49		if (const char *env = getenv("SYSPOLICYNAME"))
50			name = env;
51		queue = dispatch_queue_create("spd-client", 0);
52		service = xpc_connection_create_mach_service(name, queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
53		xpc_connection_set_event_handler(service, ^(xpc_object_t msg) {
54			if (xpc_get_type(msg) == XPC_TYPE_DICTIONARY) {
55				const char *function = xpc_dictionary_get_string(msg, "function");
56				if (!strcmp(function, "progress")) {
57					doProgress(msg);
58				}
59			}
60		});
61		xpc_connection_resume(service);
62	});
63}
64
65
66//
67// Your standard XPC client-side machinery
68//
69class Message {
70public:
71	xpc_object_t obj;
72
73	Message(const char *function)
74	{
75		init();
76		obj = xpc_dictionary_create(NULL, NULL, 0);
77		xpc_dictionary_set_string(obj, "function", function);
78	}
79	~Message()
80	{
81		if (obj)
82			xpc_release(obj);
83	}
84	operator xpc_object_t () { return obj; }
85
86	void send()
87	{
88		xpc_object_t reply = xpc_connection_send_message_with_reply_sync(service, obj);
89		xpc_release(obj);
90		obj = NULL;
91		xpc_type_t type = xpc_get_type(reply);
92		if (type == XPC_TYPE_DICTIONARY) {
93			obj = reply;
94			if (int64_t error = xpc_dictionary_get_int64(obj, "error"))
95				MacOSError::throwMe((int)error);
96		} else if (type == XPC_TYPE_ERROR) {
97			const char *s = xpc_copy_description(reply);
98			printf("Error returned: %s\n", s);
99			free((char*)s);
100			MacOSError::throwMe(errSecCSInternalError);
101		} else {
102			const char *s = xpc_copy_description(reply);
103			printf("Unexpected type of return object: %s\n", s);
104			free((char*)s);
105		}
106	}
107};
108
109
110
111static void copyCFDictionary(const void *key, const void *value, void *ctx)
112{
113	CFMutableDictionaryRef target = CFMutableDictionaryRef(ctx);
114	if (CFGetTypeID(value) == CFURLGetTypeID()) {
115		CFRef<CFStringRef> path = CFURLCopyFileSystemPath(CFURLRef(value), kCFURLPOSIXPathStyle);
116		CFDictionaryAddValue(target, key, path);
117	} else if (CFEqual(key, kSecAssessmentContextKeyFeedback)) {
118		CFDictionaryAddValue(target, key, CFTempNumber(uint64_t(value)));
119	} else {
120		CFDictionaryAddValue(target, key, value);
121	}
122}
123
124void xpcEngineAssess(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result)
125{
126	Message msg("assess");
127	xpc_dictionary_set_string(msg, "path", cfString(path).c_str());
128	xpc_dictionary_set_int64(msg, "flags", flags);
129	CFRef<CFMutableDictionaryRef> ctx = makeCFMutableDictionary();
130	if (context)
131		CFDictionaryApplyFunction(context, copyCFDictionary, ctx);
132	CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx));
133	xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData));
134
135	msg.send();
136
137	if (int64_t error = xpc_dictionary_get_int64(msg, "error"))
138		MacOSError::throwMe((int)error);
139
140	size_t resultLength;
141	const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength);
142	CFRef<CFDictionaryRef> resultDict = makeCFDictionaryFrom(resultData, resultLength);
143	CFDictionaryApplyFunction(resultDict, copyCFDictionary, result);
144	CFDictionaryAddValue(result, CFSTR("assessment:remote"), kCFBooleanTrue);
145}
146
147static void doProgress(xpc_object_t msg)
148{
149	uint64_t current = xpc_dictionary_get_uint64(msg, "current");
150	uint64_t total = xpc_dictionary_get_uint64(msg, "total");
151	uint64_t ref = xpc_dictionary_get_uint64(msg, "ref");
152	const char *token = xpc_dictionary_get_string(msg, "token");
153	SecAssessmentFeedback feedback = SecAssessmentFeedback(ref);
154	CFTemp<CFDictionaryRef> info("{current=%d,total=%d}", current, total);
155	Boolean proceed = feedback(kSecAssessmentFeedbackProgress, info);
156	if (!proceed) {
157		xpc_connection_t connection = xpc_dictionary_get_remote_connection(msg);
158		xpc_object_t cancelRequest = xpc_dictionary_create(NULL, NULL, 0);
159		xpc_dictionary_set_string(cancelRequest, "function", "cancel");
160		xpc_dictionary_set_string(cancelRequest, "token", token);
161		xpc_connection_send_message(connection, cancelRequest);
162		xpc_release(cancelRequest);
163	}
164}
165
166
167CFDictionaryRef xpcEngineUpdate(CFTypeRef target, SecAssessmentFlags flags, CFDictionaryRef context)
168{
169	Message msg("update");
170	// target can be NULL, a CFURLRef, a SecRequirementRef, or a CFNumberRef
171	if (target) {
172		if (CFGetTypeID(target) == CFNumberGetTypeID())
173			xpc_dictionary_set_uint64(msg, "rule", cfNumber<int64_t>(CFNumberRef(target)));
174		else if (CFGetTypeID(target) == CFURLGetTypeID())
175			xpc_dictionary_set_string(msg, "url", cfString(CFURLRef(target)).c_str());
176		else if (CFGetTypeID(target) == SecRequirementGetTypeID()) {
177			CFRef<CFDataRef> data;
178			MacOSError::check(SecRequirementCopyData(SecRequirementRef(target), kSecCSDefaultFlags, &data.aref()));
179			xpc_dictionary_set_data(msg, "requirement", CFDataGetBytePtr(data), CFDataGetLength(data));
180		} else
181			MacOSError::throwMe(errSecCSInvalidObjectRef);
182	}
183	xpc_dictionary_set_int64(msg, "flags", flags);
184	CFRef<CFMutableDictionaryRef> ctx = makeCFMutableDictionary();
185	if (context)
186		CFDictionaryApplyFunction(context, copyCFDictionary, ctx);
187	AuthorizationRef localAuthorization = NULL;
188	if (CFDictionaryGetValue(ctx, kSecAssessmentUpdateKeyAuthorization) == NULL) {	// no caller-provided authorization
189		MacOSError::check(AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &localAuthorization));
190		AuthorizationExternalForm extForm;
191		MacOSError::check(AuthorizationMakeExternalForm(localAuthorization, &extForm));
192		CFDictionaryAddValue(ctx, kSecAssessmentUpdateKeyAuthorization, CFTempData(&extForm, sizeof(extForm)));
193	}
194	CFRef<CFDataRef> contextData = makeCFData(CFDictionaryRef(ctx));
195	xpc_dictionary_set_data(msg, "context", CFDataGetBytePtr(contextData), CFDataGetLength(contextData));
196
197	msg.send();
198
199	if (localAuthorization)
200		AuthorizationFree(localAuthorization, kAuthorizationFlagDefaults);
201
202	if (int64_t error = xpc_dictionary_get_int64(msg, "error"))
203		MacOSError::throwMe((int)error);
204
205	size_t resultLength;
206	const void *resultData = xpc_dictionary_get_data(msg, "result", &resultLength);
207	return makeCFDictionaryFrom(resultData, resultLength);
208}
209
210
211bool xpcEngineControl(const char *control)
212{
213	Message msg("control");
214	xpc_dictionary_set_string(msg, "control", control);
215	msg.send();
216	return true;
217}
218
219
220void xpcEngineRecord(CFDictionaryRef info)
221{
222	Message msg("record");
223	CFRef<CFDataRef> infoData = makeCFData(CFDictionaryRef(info));
224	xpc_dictionary_set_data(msg, "info", CFDataGetBytePtr(infoData), CFDataGetLength(infoData));
225
226	msg.send();
227}
228
229
230} // end namespace CodeSigning
231} // end namespace Security
232