xref: /xnu-11215.41.3/osfmk/kern/arcade.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2019-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <mach/mach_types.h>
30 #include <kern/kern_types.h>
31 #include <mach/notify.h>
32 #include <mach/resource_monitors.h>
33 #include <os/log.h>
34 
35 #include <mach/host_special_ports.h>
36 #include <mach/mach_host_server.h>
37 #include <mach/host_priv_server.h>
38 #include <mach/fairplayd_notification.h>
39 #include <mach/arcade_upcall.h>
40 
41 #include <kern/kern_types.h>
42 #include <kern/assert.h>
43 #include <kern/host.h>
44 #include <kern/ast.h>
45 #include <kern/task.h>
46 
47 #include <kern/arcade.h>
48 #include <mach/arcade_register_server.h>
49 
50 #include <IOKit/IOBSD.h>
51 
52 #if !defined(MAXPATHLEN)
53 #define MAXPATHLEN 4096
54 #endif
55 
56 extern struct proc *current_proc(void);
57 extern int proc_pidpathinfo_internal(struct proc *p, uint64_t arg,
58     char *buffer, uint32_t buffersize,
59     int32_t *retval);
60 extern off_t proc_getexecutableoffset(struct proc *p);
61 
62 /*
63  * Simple structure to represent a handle for the Arcade registration.
64  *
65  * This registration is done with an independent kobject callback, rather
66  * than a reply, so that we execute it in the context of the user-space
67  * server replying (in order to do an entitlement check on the reply).
68  *
69  * We cache the resulting upcall port until it fails, and then we go
70  * get another one.
71  */
72 struct arcade_register {
73 	ipc_port_t ar_port;
74 };
75 typedef struct arcade_register *arcade_register_t;
76 
77 IPC_KOBJECT_DEFINE(IKOT_ARCADE_REG,
78     .iko_op_stable    = true,
79     .iko_op_permanent = true);
80 
81 static SECURITY_READ_ONLY_LATE(struct arcade_register) arcade_register_global;
82 
83 void
arcade_prepare(task_t task,thread_t thread)84 arcade_prepare(task_t task, thread_t thread)
85 {
86 	/* Platform binaries are exempt */
87 	if (task_get_platform_binary(task)) {
88 		return;
89 	}
90 
91 	/* Check to see if the task has the arcade entitlement */
92 	if (!IOTaskHasEntitlement(task, "com.apple.developer.arcade-operations")) {
93 		return;
94 	}
95 
96 	/* Others will stop in the AST to make an upcall */
97 	thread_ast_set(thread, AST_ARCADE);
98 }
99 
100 static LCK_GRP_DECLARE(arcade_upcall_lck_grp, "arcade_upcall");
101 static LCK_MTX_DECLARE(arcade_upcall_mutex, &arcade_upcall_lck_grp);
102 
103 static ipc_port_t arcade_upcall_port = IP_NULL;
104 static boolean_t arcade_upcall_refresh_in_progress = FALSE;
105 static boolean_t arcade_upcall_refresh_waiters = FALSE;
106 
107 void
arcade_init(void)108 arcade_init(void)
109 {
110 	ipc_port_t port;
111 
112 	/* Initialize the global arcade_register kobject and associated port */
113 	port = ipc_kobject_alloc_port((ipc_kobject_t)&arcade_register_global,
114 	    IKOT_ARCADE_REG, IPC_KOBJECT_ALLOC_MAKE_SEND);
115 	os_atomic_store(&arcade_register_global.ar_port, port, release);
116 }
117 
118 arcade_register_t
convert_port_to_arcade_register(ipc_port_t port)119 convert_port_to_arcade_register(
120 	ipc_port_t              port)
121 {
122 	arcade_register_t arcade_reg = ARCADE_REG_NULL;
123 
124 	if (IP_VALID(port)) {
125 		arcade_reg = ipc_kobject_get_stable(port, IKOT_ARCADE_REG);
126 		if (arcade_reg) {
127 			assert(arcade_reg == &arcade_register_global);
128 			assert(arcade_reg->ar_port == port);
129 		}
130 	}
131 	return arcade_reg;
132 }
133 
134 ipc_port_t
convert_arcade_register_to_port(arcade_register_t arcade_reg)135 convert_arcade_register_to_port(
136 	arcade_register_t arcade_reg)
137 {
138 	ipc_port_t port = IP_NULL;
139 
140 	if (arcade_reg == &arcade_register_global) {
141 		port = arcade_reg->ar_port;
142 	}
143 	return port;
144 }
145 
146 kern_return_t
arcade_register_new_upcall(arcade_register_t arcade_reg,mach_port_t port)147 arcade_register_new_upcall(
148 	arcade_register_t arcade_reg,
149 	mach_port_t port)
150 {
151 	os_log(OS_LOG_DEFAULT, "arcade: received register request");
152 	if (arcade_reg == ARCADE_REG_NULL) {
153 		return KERN_INVALID_ARGUMENT;
154 	}
155 	assert(arcade_reg == &arcade_register_global);
156 
157 	/* Check to see if this is the real arcade subscription service */
158 	if (!IOCurrentTaskHasEntitlement("com.apple.arcade.fpsd")) {
159 		return KERN_INVALID_VALUE;
160 	}
161 
162 	lck_mtx_lock(&arcade_upcall_mutex);
163 
164 	if (arcade_upcall_refresh_in_progress) {
165 		/* If we have an old arcade upcall port, discard it */
166 		if (IP_VALID(arcade_upcall_port)) {
167 			ipc_port_release_send(arcade_upcall_port);
168 			arcade_upcall_port = IP_NULL;
169 		}
170 		arcade_upcall_port = port; /* owns send right */
171 
172 		/* Wake up anyone waiting for the update */
173 		lck_mtx_unlock(&arcade_upcall_mutex);
174 		thread_wakeup(&arcade_upcall_port);
175 		return KERN_SUCCESS;
176 	}
177 	lck_mtx_unlock(&arcade_upcall_mutex);
178 	return KERN_FAILURE;
179 }
180 
181 
182 static kern_return_t
arcade_upcall_refresh(uint64_t deadline)183 arcade_upcall_refresh(uint64_t deadline)
184 {
185 	ipc_port_t fairplayd_port = IP_NULL;
186 	wait_result_t wr = THREAD_NOT_WAITING;
187 	kern_return_t kr;
188 
189 	LCK_MTX_ASSERT(&arcade_upcall_mutex, LCK_MTX_ASSERT_OWNED);
190 
191 	/* If someone else is doing the update, wait for them */
192 	if (arcade_upcall_refresh_in_progress) {
193 		arcade_upcall_refresh_waiters = TRUE;
194 		wr = lck_mtx_sleep(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
195 		    &arcade_upcall_refresh_in_progress, THREAD_INTERRUPTIBLE);
196 		goto out;
197 	}
198 
199 	arcade_upcall_refresh_in_progress = TRUE;
200 
201 	/* If we have an old arcade upcall port, discard it */
202 	if (IP_VALID(arcade_upcall_port)) {
203 		ipc_port_release_send(arcade_upcall_port);
204 		arcade_upcall_port = IP_NULL;
205 	}
206 
207 	if (host_get_fairplayd_port(host_priv_self(), &fairplayd_port) != KERN_SUCCESS) {
208 		panic("arcade_upcall_refresh(get fairplayd)");
209 	}
210 
211 	/* If no valid fairplayd port registered, we're done */
212 	if (!IP_VALID(fairplayd_port)) {
213 		goto finish_in_progress;
214 	}
215 
216 	/*
217 	 * Send a fairplayd notification to request a new arcade upcall port.
218 	 * Pass along a send right to the arcade_register kobject to complete
219 	 * the registration.
220 	 */
221 	ipc_port_t port = convert_arcade_register_to_port(&arcade_register_global);
222 	kr = fairplayd_arcade_request(fairplayd_port, port);
223 
224 	ipc_port_release_send(fairplayd_port);
225 
226 	switch (kr) {
227 	case MACH_MSG_SUCCESS:
228 		break;
229 	default:
230 		goto finish_in_progress;
231 	}
232 
233 	/*
234 	 * Wait on the arcade upcall port to get registered through the
235 	 * registration kobject waiting with a deadline here.
236 	 */
237 	wr = lck_mtx_sleep_deadline(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
238 	    &arcade_upcall_port, THREAD_INTERRUPTIBLE, deadline);
239 
240 finish_in_progress:
241 	arcade_upcall_refresh_in_progress = FALSE;
242 
243 	/* Wakeup any waiters */
244 	if (arcade_upcall_refresh_waiters) {
245 		arcade_upcall_refresh_waiters = FALSE;
246 		thread_wakeup_with_result(&arcade_upcall_refresh_in_progress, wr);
247 	}
248 
249 out:
250 	switch (wr) {
251 	case THREAD_AWAKENED:
252 		return KERN_SUCCESS;
253 	default:
254 		return KERN_FAILURE;
255 	}
256 }
257 
258 static kern_return_t
__MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(mach_port_t port,vm_map_copy_t path,vm_size_t pathlen,off_t offset,boolean_t * should_killp)259 __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(mach_port_t port,
260     vm_map_copy_t path,
261     vm_size_t pathlen,
262     off_t offset,
263     boolean_t *should_killp)
264 {
265 	mach_msg_type_number_t len = (mach_msg_type_number_t)pathlen;
266 	return arcade_upcall(port, (vm_offset_t)path, len, offset, should_killp);
267 }
268 
269 void
arcade_ast(__unused thread_t thread)270 arcade_ast(__unused thread_t thread)
271 {
272 	ipc_port_t port;
273 	uint64_t deadline;
274 	kern_return_t kr;
275 	int retval;
276 
277 	/* Determine the deadline */
278 	clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
279 
280 restart:
281 	lck_mtx_lock(&arcade_upcall_mutex);
282 	port = ipc_port_copy_send_mqueue(arcade_upcall_port);
283 	/*
284 	 * if the arcade_upcall_port was inactive, "port" will be IP_DEAD.
285 	 * Otherwise, it holds a send right to the arcade_upcall_port.
286 	 */
287 
288 	while (!IP_VALID(port)) {
289 		/*
290 		 * Refresh the arcade upcall port. If that gives up,
291 		 * give up ourselves.
292 		 */
293 		kr = arcade_upcall_refresh(deadline);
294 		if (kr != KERN_SUCCESS) {
295 			lck_mtx_unlock(&arcade_upcall_mutex);
296 			goto fail;
297 		}
298 		port = ipc_port_copy_send_mqueue(arcade_upcall_port);
299 	}
300 	lck_mtx_unlock(&arcade_upcall_mutex);
301 
302 	/* We have an upcall port send right */
303 
304 	/* Gather the data we need to send in the upcall */
305 	off_t offset;
306 	struct proc *p = current_proc();
307 	char *path;
308 	vm_map_copy_t copy;
309 
310 	path = kalloc_data(MAXPATHLEN, Z_WAITOK | Z_ZERO);
311 	retval = proc_pidpathinfo_internal(p, 0, path, MAXPATHLEN, NULL);
312 	assert(!retval);
313 	kr = vm_map_copyin(kernel_map, (vm_map_address_t)path, MAXPATHLEN, FALSE, &copy);
314 	assert(kr == KERN_SUCCESS);
315 	kfree_data(path, MAXPATHLEN);
316 
317 	offset = proc_getexecutableoffset(p);
318 
319 	/* MAKE THE UPCALL */
320 	boolean_t should_kill = TRUE;
321 	kr = __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(port, copy, MAXPATHLEN, offset, &should_kill);
322 	os_log(OS_LOG_DEFAULT, "arcade: subscription validation upcall returned %#x", kr);
323 	ipc_port_release_send(port);
324 
325 	switch (kr) {
326 	case MACH_SEND_INVALID_DEST:
327 		vm_map_copy_discard(copy);
328 		OS_FALLTHROUGH;
329 	case MIG_SERVER_DIED:
330 		goto restart;
331 	case KERN_SUCCESS:
332 		if (should_kill == TRUE) {
333 			/*
334 			 * Invalid subscription. UI already presented as to why it did not
335 			 * launch.
336 			 */
337 			task_terminate_internal(current_task());
338 		}
339 		break;
340 	default:
341 fail:
342 		/*
343 		 * Failure of the subscription validation mechanism, not a rejection.
344 		 * for a missing subscription. There will be no indication WHY this
345 		 * process didn't launch. We might want this to be an exit_with_reason()
346 		 * in the future.
347 		 */
348 		os_log(OS_LOG_DEFAULT, "arcade: unable to make subscription upcall, error %#x", kr);
349 		task_terminate_internal(current_task());
350 		break;
351 	}
352 }
353