2 * Copyright (c) 2011-2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 #include "xpcengine.h"
24 #include <xpc/connection.h>
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <security_utilities/cfutilities.h>
28 #include <security_utilities/logging.h>
29 #include <security_utilities/cfmunge.h>
34 namespace CodeSigning
{
37 static void doProgress(xpc_object_t msg
);
40 static const char serviceName
[] = "com.apple.security.syspolicy";
43 static dispatch_once_t dispatchInit
; // one-time init marker
44 static xpc_connection_t service
; // connection to spd
45 static dispatch_queue_t queue
; // dispatch queue for service
47 static map
<uint64_t, SecAssessmentFeedback
> *feedbackBlocks
;
51 dispatch_once(&dispatchInit
, ^void(void) {
52 feedbackBlocks
= new map
<uint64_t, SecAssessmentFeedback
>;
53 const char *name
= serviceName
;
54 if (const char *env
= getenv("SYSPOLICYNAME"))
56 queue
= dispatch_queue_create("spd-client", DISPATCH_QUEUE_SERIAL
);
57 service
= xpc_connection_create_mach_service(name
, queue
, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
58 xpc_connection_set_event_handler(service
, ^(xpc_object_t msg
) {
59 if (xpc_get_type(msg
) == XPC_TYPE_DICTIONARY
) {
60 const char *function
= xpc_dictionary_get_string(msg
, "function");
61 if (strcmp(function
, "progress") == 0) {
65 Syslog::error("Discarding progress handler exception");
70 xpc_connection_resume(service
);
76 // Your standard XPC client-side machinery
82 Message(const char *function
)
85 obj
= xpc_dictionary_create(NULL
, NULL
, 0);
86 xpc_dictionary_set_string(obj
, "function", function
);
93 operator xpc_object_t () { return obj
; }
97 xpc_object_t reply
= xpc_connection_send_message_with_reply_sync(service
, obj
);
100 xpc_type_t type
= xpc_get_type(reply
);
101 if (type
== XPC_TYPE_DICTIONARY
) {
103 if (int64_t error
= xpc_dictionary_get_int64(obj
, "error"))
104 MacOSError::throwMe((int)error
);
105 } else if (type
== XPC_TYPE_ERROR
) {
106 const char *s
= xpc_copy_description(reply
);
107 printf("Error returned: %s\n", s
);
108 Syslog::notice("code signing internal problem: unexpected error from xpc: %s", s
);
110 MacOSError::throwMe(errSecCSInternalError
);
112 const char *s
= xpc_copy_description(reply
);
113 printf("Unexpected type of return object: %s\n", s
);
121 static void copyCFDictionary(const void *key
, const void *value
, void *ctx
)
123 CFMutableDictionaryRef target
= CFMutableDictionaryRef(ctx
);
124 if (CFGetTypeID(value
) == CFURLGetTypeID()) {
125 CFRef
<CFStringRef
> path
= CFURLCopyFileSystemPath(CFURLRef(value
), kCFURLPOSIXPathStyle
);
126 CFDictionaryAddValue(target
, key
, path
);
127 } else if (!CFEqual(key
, kSecAssessmentContextKeyFeedback
)) {
128 CFDictionaryAddValue(target
, key
, value
);
133 static bool precheckAccess(CFURLRef path
, CFDictionaryRef context
)
135 CFTypeRef type
= CFDictionaryGetValue(context
, kSecAssessmentContextKeyOperation
);
136 if (type
== NULL
|| CFEqual(type
, kSecAssessmentOperationTypeExecute
)) {
137 CFRef
<SecStaticCodeRef
> code
;
138 OSStatus rc
= SecStaticCodeCreateWithPath(path
, kSecCSDefaultFlags
, &code
.aref());
139 if (rc
== errSecCSBadBundleFormat
) // work around <rdar://problem/26075034>
141 CFRef
<CFURLRef
> exec
;
142 MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &exec
.aref()));
143 UnixError::check(::access(cfString(exec
).c_str(), R_OK
));
145 UnixError::check(access(cfString(path
).c_str(), R_OK
));
151 void xpcEngineAssess(CFURLRef path
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
)
153 precheckAccess(path
, context
);
154 Message
msg("assess");
155 xpc_dictionary_set_string(msg
, "path", cfString(path
).c_str());
156 xpc_dictionary_set_uint64(msg
, "flags", flags
);
157 CFRef
<CFMutableDictionaryRef
> ctx
= makeCFMutableDictionary();
159 CFDictionaryApplyFunction(context
, copyCFDictionary
, ctx
);
162 SecAssessmentFeedback feedback
= (SecAssessmentFeedback
)CFDictionaryGetValue(context
, kSecAssessmentContextKeyFeedback
);
164 /* Map the feedback block to a random number for tracking, because we don't want
165 * to send over a pointer. */
166 uint64_t __block feedbackId
= 0;
168 dispatch_sync(queue
, ^{
171 /* Simple sequence number would probably be sufficient,
172 * but making the id unpredictable is also cheap enough here. */
173 arc4random_buf(&feedbackId
, sizeof(uint64_t));
174 if ((*feedbackBlocks
)[feedbackId
] == NULL
/* extremely certain */) {
175 (*feedbackBlocks
)[feedbackId
] = feedback
;
180 CFDictionaryAddValue(ctx
, kSecAssessmentContextKeyFeedback
, CFTempNumber(feedbackId
));
183 CFRef
<CFDataRef
> contextData
= makeCFData(CFDictionaryRef(ctx
));
184 xpc_dictionary_set_data(msg
, "context", CFDataGetBytePtr(contextData
), CFDataGetLength(contextData
));
188 /* Done, feedback block won't be called anymore,
189 * so remove the feedback mapping from the global map. */
191 dispatch_sync(queue
, ^{
192 feedbackBlocks
->erase(feedbackId
);
196 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error"))
197 MacOSError::throwMe((int)error
);
200 const void *resultData
= xpc_dictionary_get_data(msg
, "result", &resultLength
);
201 CFRef
<CFDictionaryRef
> resultDict
= makeCFDictionaryFrom(resultData
, resultLength
);
202 CFDictionaryApplyFunction(resultDict
, copyCFDictionary
, result
);
203 CFDictionaryAddValue(result
, CFSTR("assessment:remote"), kCFBooleanTrue
);
206 static void doProgress(xpc_object_t msg
)
208 uint64_t current
= xpc_dictionary_get_uint64(msg
, "current");
209 uint64_t total
= xpc_dictionary_get_uint64(msg
, "total");
210 uint64_t ref
= xpc_dictionary_get_uint64(msg
, "ref");
211 const char *token
= xpc_dictionary_get_string(msg
, "token");
213 SecAssessmentFeedback feedback
= NULL
;
215 // doProgress is called on the queue, so no dispatch_sync here.
217 feedback
= feedbackBlocks
->at(ref
);
218 } catch (std::out_of_range
) {
219 // Indicates that syspolicyd gave us something it shouldn't have.
220 Syslog::error("no feedback block registered with ID %lld", ref
);
221 MacOSError::throwMe(errSecCSInternalError
);
224 CFTemp
<CFDictionaryRef
> info("{current=%d,total=%d}", current
, total
);
225 Boolean proceed
= feedback(kSecAssessmentFeedbackProgress
, info
);
227 xpc_connection_t connection
= xpc_dictionary_get_remote_connection(msg
);
228 xpc_object_t cancelRequest
= xpc_dictionary_create(NULL
, NULL
, 0);
229 xpc_dictionary_set_string(cancelRequest
, "function", "cancel");
230 xpc_dictionary_set_string(cancelRequest
, "token", token
);
231 xpc_connection_send_message(connection
, cancelRequest
);
232 xpc_release(cancelRequest
);
237 CFDictionaryRef
xpcEngineUpdate(CFTypeRef target
, SecAssessmentFlags flags
, CFDictionaryRef context
)
239 Message
msg("update");
240 // target can be NULL, a CFURLRef, a SecRequirementRef, or a CFNumberRef
242 if (CFGetTypeID(target
) == CFNumberGetTypeID())
243 xpc_dictionary_set_uint64(msg
, "rule", cfNumber
<int64_t>(CFNumberRef(target
)));
244 else if (CFGetTypeID(target
) == CFURLGetTypeID()) {
245 bool good
= precheckAccess(CFURLRef(target
), context
);
246 if (!good
) // work around <rdar://problem/26075034>
247 return makeCFDictionary(0); // pretend this worked
248 xpc_dictionary_set_string(msg
, "url", cfString(CFURLRef(target
)).c_str());
249 } else if (CFGetTypeID(target
) == SecRequirementGetTypeID()) {
250 CFRef
<CFDataRef
> data
;
251 MacOSError::check(SecRequirementCopyData(SecRequirementRef(target
), kSecCSDefaultFlags
, &data
.aref()));
252 xpc_dictionary_set_data(msg
, "requirement", CFDataGetBytePtr(data
), CFDataGetLength(data
));
254 MacOSError::throwMe(errSecCSInvalidObjectRef
);
256 xpc_dictionary_set_uint64(msg
, "flags", flags
);
257 CFRef
<CFMutableDictionaryRef
> ctx
= makeCFMutableDictionary();
259 CFDictionaryApplyFunction(context
, copyCFDictionary
, ctx
);
260 AuthorizationRef localAuthorization
= NULL
;
261 if (CFDictionaryGetValue(ctx
, kSecAssessmentUpdateKeyAuthorization
) == NULL
) { // no caller-provided authorization
262 MacOSError::check(AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &localAuthorization
));
263 AuthorizationExternalForm extForm
;
264 MacOSError::check(AuthorizationMakeExternalForm(localAuthorization
, &extForm
));
265 CFDictionaryAddValue(ctx
, kSecAssessmentUpdateKeyAuthorization
, CFTempData(&extForm
, sizeof(extForm
)));
267 CFRef
<CFDataRef
> contextData
= makeCFData(CFDictionaryRef(ctx
));
268 xpc_dictionary_set_data(msg
, "context", CFDataGetBytePtr(contextData
), CFDataGetLength(contextData
));
272 if (localAuthorization
)
273 AuthorizationFree(localAuthorization
, kAuthorizationFlagDefaults
);
275 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error"))
276 MacOSError::throwMe((int)error
);
279 const void *resultData
= xpc_dictionary_get_data(msg
, "result", &resultLength
);
280 return makeCFDictionaryFrom(resultData
, resultLength
);
284 bool xpcEngineControl(const char *control
)
286 Message
msg("control");
287 xpc_dictionary_set_string(msg
, "control", control
);
293 void xpcEngineRecord(CFDictionaryRef info
)
295 Message
msg("record");
296 CFRef
<CFDataRef
> infoData
= makeCFData(CFDictionaryRef(info
));
297 xpc_dictionary_set_data(msg
, "info", CFDataGetBytePtr(infoData
), CFDataGetLength(infoData
));
302 void xpcEngineCheckDevID(CFBooleanRef
* result
)
304 Message
msg("check-dev-id");
308 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
309 MacOSError::throwMe((int)error
);
312 *result
= xpc_dictionary_get_bool(msg
,"result") ? kCFBooleanTrue
: kCFBooleanFalse
;
315 void xpcEngineCheckNotarized(CFBooleanRef
* result
)
317 Message
msg("check-notarized");
321 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
322 MacOSError::throwMe((int)error
);
325 *result
= xpc_dictionary_get_bool(msg
,"result") ? kCFBooleanTrue
: kCFBooleanFalse
;
328 void xpcEngineTicketRegister(CFDataRef ticketData
)
330 Message
msg("ticket-register");
331 xpc_dictionary_set_data(msg
, "ticketData", CFDataGetBytePtr(ticketData
), CFDataGetLength(ticketData
));
335 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
336 MacOSError::throwMe((int)error
);
340 void xpcEngineTicketLookup(CFDataRef hashData
, SecCSDigestAlgorithm hashType
, SecAssessmentTicketFlags flags
, double *date
)
342 Message
msg("ticket-lookup");
343 xpc_dictionary_set_data(msg
, "hashData", CFDataGetBytePtr(hashData
), CFDataGetLength(hashData
));
344 xpc_dictionary_set_uint64(msg
, "hashType", hashType
);
345 xpc_dictionary_set_uint64(msg
, "flags", flags
);
349 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
350 MacOSError::throwMe((int)error
);
353 double local_date
= xpc_dictionary_get_double(msg
, "date");
354 if (date
&& !isnan(local_date
)) {
359 void xpcEngineLegacyCheck(CFDataRef hashData
, SecCSDigestAlgorithm hashType
, CFStringRef teamID
)
361 Message
msg("legacy-check");
362 xpc_dictionary_set_data(msg
, "hashData", CFDataGetBytePtr(hashData
), CFDataGetLength(hashData
));
363 xpc_dictionary_set_uint64(msg
, "hashType", hashType
);
365 // There may not be a team id, so just leave it off if there isn't since xpc_dictionary_set_string
366 // will return a NULL if the value isn't provided.
368 xpc_dictionary_set_string(msg
, "teamID", CFStringGetCStringPtr(teamID
, kCFStringEncodingUTF8
));
373 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
374 MacOSError::throwMe((int)error
);
378 } // end namespace CodeSigning
379 } // end namespace Security