xref: /xnu-12377.1.9/osfmk/kern/arcade.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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_movable_send = true,
79     .iko_op_stable    = true,
80     .iko_op_permanent = true);
81 
82 static SECURITY_READ_ONLY_LATE(struct arcade_register) arcade_register_global;
83 
84 void
arcade_prepare(task_t task,thread_t thread)85 arcade_prepare(task_t task, thread_t thread)
86 {
87 	/* Platform binaries are exempt */
88 	if (task_get_platform_binary(task)) {
89 		return;
90 	}
91 
92 	/* Check to see if the task has the arcade entitlement */
93 	if (!IOTaskHasEntitlement(task, "com.apple.developer.arcade-operations")) {
94 		return;
95 	}
96 
97 	/* Others will stop in the AST to make an upcall */
98 	thread_ast_set(thread, AST_ARCADE);
99 }
100 
101 static LCK_GRP_DECLARE(arcade_upcall_lck_grp, "arcade_upcall");
102 static LCK_MTX_DECLARE(arcade_upcall_mutex, &arcade_upcall_lck_grp);
103 
104 static ipc_port_t arcade_upcall_port = IP_NULL;
105 static boolean_t arcade_upcall_refresh_in_progress = FALSE;
106 static boolean_t arcade_upcall_refresh_waiters = FALSE;
107 
108 void
arcade_init(void)109 arcade_init(void)
110 {
111 	ipc_port_t port;
112 
113 	/* Initialize the global arcade_register kobject and associated port */
114 	port = ipc_kobject_alloc_port((ipc_kobject_t)&arcade_register_global,
115 	    IKOT_ARCADE_REG, IPC_KOBJECT_ALLOC_MAKE_SEND);
116 	os_atomic_store(&arcade_register_global.ar_port, port, release);
117 }
118 
119 arcade_register_t
convert_port_to_arcade_register(ipc_port_t port)120 convert_port_to_arcade_register(
121 	ipc_port_t              port)
122 {
123 	arcade_register_t arcade_reg = ARCADE_REG_NULL;
124 
125 	if (IP_VALID(port)) {
126 		arcade_reg = ipc_kobject_get_stable(port, IKOT_ARCADE_REG);
127 		if (arcade_reg) {
128 			assert(arcade_reg == &arcade_register_global);
129 			assert(arcade_reg->ar_port == port);
130 		}
131 	}
132 	return arcade_reg;
133 }
134 
135 ipc_port_t
convert_arcade_register_to_port(arcade_register_t arcade_reg)136 convert_arcade_register_to_port(
137 	arcade_register_t arcade_reg)
138 {
139 	ipc_port_t port = IP_NULL;
140 
141 	if (arcade_reg == &arcade_register_global) {
142 		port = arcade_reg->ar_port;
143 	}
144 	return port;
145 }
146 
147 kern_return_t
arcade_register_new_upcall(arcade_register_t arcade_reg,mach_port_t port)148 arcade_register_new_upcall(
149 	arcade_register_t arcade_reg,
150 	mach_port_t port)
151 {
152 	os_log(OS_LOG_DEFAULT, "arcade: received register request");
153 	if (arcade_reg == ARCADE_REG_NULL) {
154 		return KERN_INVALID_ARGUMENT;
155 	}
156 	assert(arcade_reg == &arcade_register_global);
157 
158 	/* Check to see if this is the real arcade subscription service */
159 	if (!IOCurrentTaskHasEntitlement("com.apple.arcade.fpsd")) {
160 		return KERN_INVALID_VALUE;
161 	}
162 
163 	lck_mtx_lock(&arcade_upcall_mutex);
164 
165 	if (arcade_upcall_refresh_in_progress) {
166 		/* If we have an old arcade upcall port, discard it */
167 		if (IP_VALID(arcade_upcall_port)) {
168 			ipc_port_release_send(arcade_upcall_port);
169 			arcade_upcall_port = IP_NULL;
170 		}
171 		arcade_upcall_port = port; /* owns send right */
172 
173 		/* Wake up anyone waiting for the update */
174 		lck_mtx_unlock(&arcade_upcall_mutex);
175 		thread_wakeup(&arcade_upcall_port);
176 		return KERN_SUCCESS;
177 	}
178 	lck_mtx_unlock(&arcade_upcall_mutex);
179 	return KERN_FAILURE;
180 }
181 
182 
183 static kern_return_t
arcade_upcall_refresh(uint64_t deadline)184 arcade_upcall_refresh(uint64_t deadline)
185 {
186 	ipc_port_t fairplayd_port = IP_NULL;
187 	wait_result_t wr = THREAD_NOT_WAITING;
188 	kern_return_t kr;
189 
190 	LCK_MTX_ASSERT(&arcade_upcall_mutex, LCK_MTX_ASSERT_OWNED);
191 
192 	/* If someone else is doing the update, wait for them */
193 	if (arcade_upcall_refresh_in_progress) {
194 		arcade_upcall_refresh_waiters = TRUE;
195 		wr = lck_mtx_sleep(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
196 		    &arcade_upcall_refresh_in_progress, THREAD_INTERRUPTIBLE);
197 		goto out;
198 	}
199 
200 	arcade_upcall_refresh_in_progress = TRUE;
201 
202 	/* If we have an old arcade upcall port, discard it */
203 	if (IP_VALID(arcade_upcall_port)) {
204 		ipc_port_release_send(arcade_upcall_port);
205 		arcade_upcall_port = IP_NULL;
206 	}
207 
208 	if (host_get_fairplayd_port(host_priv_self(), &fairplayd_port) != KERN_SUCCESS) {
209 		panic("arcade_upcall_refresh(get fairplayd)");
210 	}
211 
212 	/* If no valid fairplayd port registered, we're done */
213 	if (!IP_VALID(fairplayd_port)) {
214 		goto finish_in_progress;
215 	}
216 
217 	/*
218 	 * Send a fairplayd notification to request a new arcade upcall port.
219 	 * Pass along a send right to the arcade_register kobject to complete
220 	 * the registration.
221 	 */
222 	ipc_port_t port = convert_arcade_register_to_port(&arcade_register_global);
223 	kr = fairplayd_arcade_request(fairplayd_port, port);
224 
225 	ipc_port_release_send(fairplayd_port);
226 
227 	switch (kr) {
228 	case MACH_MSG_SUCCESS:
229 		break;
230 	default:
231 		goto finish_in_progress;
232 	}
233 
234 	/*
235 	 * Wait on the arcade upcall port to get registered through the
236 	 * registration kobject waiting with a deadline here.
237 	 */
238 	wr = lck_mtx_sleep_deadline(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
239 	    &arcade_upcall_port, THREAD_INTERRUPTIBLE, deadline);
240 
241 finish_in_progress:
242 	arcade_upcall_refresh_in_progress = FALSE;
243 
244 	/* Wakeup any waiters */
245 	if (arcade_upcall_refresh_waiters) {
246 		arcade_upcall_refresh_waiters = FALSE;
247 		thread_wakeup_with_result(&arcade_upcall_refresh_in_progress, wr);
248 	}
249 
250 out:
251 	switch (wr) {
252 	case THREAD_AWAKENED:
253 		return KERN_SUCCESS;
254 	default:
255 		return KERN_FAILURE;
256 	}
257 }
258 
259 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)260 __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(mach_port_t port,
261     vm_map_copy_t path,
262     vm_size_t pathlen,
263     off_t offset,
264     boolean_t *should_killp)
265 {
266 	mach_msg_type_number_t len = (mach_msg_type_number_t)pathlen;
267 	return arcade_upcall(port, (vm_offset_t)path, len, offset, should_killp);
268 }
269 
270 void
arcade_ast(__unused thread_t thread)271 arcade_ast(__unused thread_t thread)
272 {
273 	ipc_port_t port;
274 	uint64_t deadline;
275 	kern_return_t kr;
276 	int retval;
277 
278 	/* Determine the deadline */
279 	clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
280 
281 restart:
282 	lck_mtx_lock(&arcade_upcall_mutex);
283 	port = ipc_port_copy_send_mqueue(arcade_upcall_port);
284 	/*
285 	 * if the arcade_upcall_port was inactive, "port" will be IP_DEAD.
286 	 * Otherwise, it holds a send right to the arcade_upcall_port.
287 	 */
288 
289 	while (!IP_VALID(port)) {
290 		/*
291 		 * Refresh the arcade upcall port. If that gives up,
292 		 * give up ourselves.
293 		 */
294 		kr = arcade_upcall_refresh(deadline);
295 		if (kr != KERN_SUCCESS) {
296 			lck_mtx_unlock(&arcade_upcall_mutex);
297 			goto fail;
298 		}
299 		port = ipc_port_copy_send_mqueue(arcade_upcall_port);
300 	}
301 	lck_mtx_unlock(&arcade_upcall_mutex);
302 
303 	/* We have an upcall port send right */
304 
305 	/* Gather the data we need to send in the upcall */
306 	off_t offset;
307 	struct proc *p = current_proc();
308 	char *path;
309 	vm_map_copy_t copy;
310 
311 	path = kalloc_data(MAXPATHLEN, Z_WAITOK | Z_ZERO);
312 	retval = proc_pidpathinfo_internal(p, 0, path, MAXPATHLEN, NULL);
313 	assert(!retval);
314 	kr = vm_map_copyin(kernel_map, (vm_map_address_t)path, MAXPATHLEN, FALSE, &copy);
315 	assert(kr == KERN_SUCCESS);
316 	kfree_data(path, MAXPATHLEN);
317 
318 	offset = proc_getexecutableoffset(p);
319 
320 	/* MAKE THE UPCALL */
321 	boolean_t should_kill = TRUE;
322 	kr = __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(port, copy, MAXPATHLEN, offset, &should_kill);
323 	os_log(OS_LOG_DEFAULT, "arcade: subscription validation upcall returned %#x", kr);
324 	ipc_port_release_send(port);
325 
326 	switch (kr) {
327 	case MACH_SEND_INVALID_DEST:
328 		vm_map_copy_discard(copy);
329 		OS_FALLTHROUGH;
330 	case MIG_SERVER_DIED:
331 		goto restart;
332 	case KERN_SUCCESS:
333 		if (should_kill == TRUE) {
334 			/*
335 			 * Invalid subscription. UI already presented as to why it did not
336 			 * launch.
337 			 */
338 			task_terminate_internal(current_task());
339 		}
340 		break;
341 	default:
342 fail:
343 		/*
344 		 * Failure of the subscription validation mechanism, not a rejection.
345 		 * for a missing subscription. There will be no indication WHY this
346 		 * process didn't launch. We might want this to be an exit_with_reason()
347 		 * in the future.
348 		 */
349 		os_log(OS_LOG_DEFAULT, "arcade: unable to make subscription upcall, error %#x", kr);
350 		task_terminate_internal(current_task());
351 		break;
352 	}
353 }
354