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>
33 namespace CodeSigning
{
36 static void doProgress(xpc_object_t msg
);
39 static const char serviceName
[] = "com.apple.security.syspolicy";
42 static dispatch_once_t dispatchInit
; // one-time init marker
43 static xpc_connection_t service
; // connection to spd
44 static dispatch_queue_t queue
; // dispatch queue for service
48 dispatch_once(&dispatchInit
, ^void(void) {
49 const char *name
= serviceName
;
50 if (const char *env
= getenv("SYSPOLICYNAME"))
52 queue
= dispatch_queue_create("spd-client", 0);
53 service
= xpc_connection_create_mach_service(name
, queue
, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
54 xpc_connection_set_event_handler(service
, ^(xpc_object_t msg
) {
55 if (xpc_get_type(msg
) == XPC_TYPE_DICTIONARY
) {
56 const char *function
= xpc_dictionary_get_string(msg
, "function");
57 if (!strcmp(function
, "progress")) {
62 xpc_connection_resume(service
);
68 // Your standard XPC client-side machinery
74 Message(const char *function
)
77 obj
= xpc_dictionary_create(NULL
, NULL
, 0);
78 xpc_dictionary_set_string(obj
, "function", function
);
85 operator xpc_object_t () { return obj
; }
89 xpc_object_t reply
= xpc_connection_send_message_with_reply_sync(service
, obj
);
92 xpc_type_t type
= xpc_get_type(reply
);
93 if (type
== XPC_TYPE_DICTIONARY
) {
95 if (int64_t error
= xpc_dictionary_get_int64(obj
, "error"))
96 MacOSError::throwMe((int)error
);
97 } else if (type
== XPC_TYPE_ERROR
) {
98 const char *s
= xpc_copy_description(reply
);
99 printf("Error returned: %s\n", s
);
100 Syslog::notice("code signing internal problem: unexpected error from xpc: %s", s
);
102 MacOSError::throwMe(errSecCSInternalError
);
104 const char *s
= xpc_copy_description(reply
);
105 printf("Unexpected type of return object: %s\n", s
);
113 static void copyCFDictionary(const void *key
, const void *value
, void *ctx
)
115 CFMutableDictionaryRef target
= CFMutableDictionaryRef(ctx
);
116 if (CFGetTypeID(value
) == CFURLGetTypeID()) {
117 CFRef
<CFStringRef
> path
= CFURLCopyFileSystemPath(CFURLRef(value
), kCFURLPOSIXPathStyle
);
118 CFDictionaryAddValue(target
, key
, path
);
119 } else if (CFEqual(key
, kSecAssessmentContextKeyFeedback
)) {
120 CFDictionaryAddValue(target
, key
, CFTempNumber(uint64_t(value
)));
122 CFDictionaryAddValue(target
, key
, value
);
127 static bool precheckAccess(CFURLRef path
, CFDictionaryRef context
)
129 CFTypeRef type
= CFDictionaryGetValue(context
, kSecAssessmentContextKeyOperation
);
130 if (type
== NULL
|| CFEqual(type
, kSecAssessmentOperationTypeExecute
)) {
131 CFRef
<SecStaticCodeRef
> code
;
132 OSStatus rc
= SecStaticCodeCreateWithPath(path
, kSecCSDefaultFlags
, &code
.aref());
133 if (rc
== errSecCSBadBundleFormat
) // work around <rdar://problem/26075034>
135 CFRef
<CFURLRef
> exec
;
136 MacOSError::check(SecCodeCopyPath(code
, kSecCSDefaultFlags
, &exec
.aref()));
137 UnixError::check(::access(cfString(exec
).c_str(), R_OK
));
139 UnixError::check(access(cfString(path
).c_str(), R_OK
));
145 void xpcEngineAssess(CFURLRef path
, SecAssessmentFlags flags
, CFDictionaryRef context
, CFMutableDictionaryRef result
)
147 precheckAccess(path
, context
);
148 Message
msg("assess");
149 xpc_dictionary_set_string(msg
, "path", cfString(path
).c_str());
150 xpc_dictionary_set_int64(msg
, "flags", flags
);
151 CFRef
<CFMutableDictionaryRef
> ctx
= makeCFMutableDictionary();
153 CFDictionaryApplyFunction(context
, copyCFDictionary
, ctx
);
154 CFRef
<CFDataRef
> contextData
= makeCFData(CFDictionaryRef(ctx
));
155 xpc_dictionary_set_data(msg
, "context", CFDataGetBytePtr(contextData
), CFDataGetLength(contextData
));
159 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error"))
160 MacOSError::throwMe((int)error
);
163 const void *resultData
= xpc_dictionary_get_data(msg
, "result", &resultLength
);
164 CFRef
<CFDictionaryRef
> resultDict
= makeCFDictionaryFrom(resultData
, resultLength
);
165 CFDictionaryApplyFunction(resultDict
, copyCFDictionary
, result
);
166 CFDictionaryAddValue(result
, CFSTR("assessment:remote"), kCFBooleanTrue
);
169 static void doProgress(xpc_object_t msg
)
171 uint64_t current
= xpc_dictionary_get_uint64(msg
, "current");
172 uint64_t total
= xpc_dictionary_get_uint64(msg
, "total");
173 uint64_t ref
= xpc_dictionary_get_uint64(msg
, "ref");
174 const char *token
= xpc_dictionary_get_string(msg
, "token");
175 SecAssessmentFeedback feedback
= SecAssessmentFeedback(ref
);
176 CFTemp
<CFDictionaryRef
> info("{current=%d,total=%d}", current
, total
);
177 Boolean proceed
= feedback(kSecAssessmentFeedbackProgress
, info
);
179 xpc_connection_t connection
= xpc_dictionary_get_remote_connection(msg
);
180 xpc_object_t cancelRequest
= xpc_dictionary_create(NULL
, NULL
, 0);
181 xpc_dictionary_set_string(cancelRequest
, "function", "cancel");
182 xpc_dictionary_set_string(cancelRequest
, "token", token
);
183 xpc_connection_send_message(connection
, cancelRequest
);
184 xpc_release(cancelRequest
);
189 CFDictionaryRef
xpcEngineUpdate(CFTypeRef target
, SecAssessmentFlags flags
, CFDictionaryRef context
)
191 Message
msg("update");
192 // target can be NULL, a CFURLRef, a SecRequirementRef, or a CFNumberRef
194 if (CFGetTypeID(target
) == CFNumberGetTypeID())
195 xpc_dictionary_set_uint64(msg
, "rule", cfNumber
<int64_t>(CFNumberRef(target
)));
196 else if (CFGetTypeID(target
) == CFURLGetTypeID()) {
197 bool good
= precheckAccess(CFURLRef(target
), context
);
198 if (!good
) // work around <rdar://problem/26075034>
199 return makeCFDictionary(0); // pretend this worked
200 xpc_dictionary_set_string(msg
, "url", cfString(CFURLRef(target
)).c_str());
201 } else if (CFGetTypeID(target
) == SecRequirementGetTypeID()) {
202 CFRef
<CFDataRef
> data
;
203 MacOSError::check(SecRequirementCopyData(SecRequirementRef(target
), kSecCSDefaultFlags
, &data
.aref()));
204 xpc_dictionary_set_data(msg
, "requirement", CFDataGetBytePtr(data
), CFDataGetLength(data
));
206 MacOSError::throwMe(errSecCSInvalidObjectRef
);
208 xpc_dictionary_set_int64(msg
, "flags", flags
);
209 CFRef
<CFMutableDictionaryRef
> ctx
= makeCFMutableDictionary();
211 CFDictionaryApplyFunction(context
, copyCFDictionary
, ctx
);
212 AuthorizationRef localAuthorization
= NULL
;
213 if (CFDictionaryGetValue(ctx
, kSecAssessmentUpdateKeyAuthorization
) == NULL
) { // no caller-provided authorization
214 MacOSError::check(AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &localAuthorization
));
215 AuthorizationExternalForm extForm
;
216 MacOSError::check(AuthorizationMakeExternalForm(localAuthorization
, &extForm
));
217 CFDictionaryAddValue(ctx
, kSecAssessmentUpdateKeyAuthorization
, CFTempData(&extForm
, sizeof(extForm
)));
219 CFRef
<CFDataRef
> contextData
= makeCFData(CFDictionaryRef(ctx
));
220 xpc_dictionary_set_data(msg
, "context", CFDataGetBytePtr(contextData
), CFDataGetLength(contextData
));
224 if (localAuthorization
)
225 AuthorizationFree(localAuthorization
, kAuthorizationFlagDefaults
);
227 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error"))
228 MacOSError::throwMe((int)error
);
231 const void *resultData
= xpc_dictionary_get_data(msg
, "result", &resultLength
);
232 return makeCFDictionaryFrom(resultData
, resultLength
);
236 bool xpcEngineControl(const char *control
)
238 Message
msg("control");
239 xpc_dictionary_set_string(msg
, "control", control
);
245 void xpcEngineRecord(CFDictionaryRef info
)
247 Message
msg("record");
248 CFRef
<CFDataRef
> infoData
= makeCFData(CFDictionaryRef(info
));
249 xpc_dictionary_set_data(msg
, "info", CFDataGetBytePtr(infoData
), CFDataGetLength(infoData
));
254 void xpcEngineCheckDevID(CFBooleanRef
* result
)
256 Message
msg("check-dev-id");
260 if (int64_t error
= xpc_dictionary_get_int64(msg
, "error")) {
261 MacOSError::throwMe((int)error
);
264 *result
= xpc_dictionary_get_bool(msg
,"result") ? kCFBooleanTrue
: kCFBooleanFalse
;
268 } // end namespace CodeSigning
269 } // end namespace Security