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
56extern struct proc *current_proc(void);
57extern int proc_pidpathinfo_internal(struct proc *p, uint64_t arg,
58 char *buffer, uint32_t buffersize,
59 int32_t *retval);
60extern 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 */
72struct arcade_register {
73 ipc_port_t ar_port;
74};
75typedef struct arcade_register *arcade_register_t;
76
77IPC_KOBJECT_DEFINE(IKOT_ARCADE_REG,
78 .iko_op_stable = true,
79 .iko_op_permanent = true);
80
81static SECURITY_READ_ONLY_LATE(struct arcade_register) arcade_register_global;
82
83void
84arcade_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, entitlement: "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
100static LCK_GRP_DECLARE(arcade_upcall_lck_grp, "arcade_upcall");
101static LCK_MTX_DECLARE(arcade_upcall_mutex, &arcade_upcall_lck_grp);
102
103static ipc_port_t arcade_upcall_port = IP_NULL;
104static boolean_t arcade_upcall_refresh_in_progress = FALSE;
105static boolean_t arcade_upcall_refresh_waiters = FALSE;
106
107void
108arcade_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(kobject: (ipc_kobject_t)&arcade_register_global,
114 type: IKOT_ARCADE_REG, options: IPC_KOBJECT_ALLOC_MAKE_SEND);
115 os_atomic_store(&arcade_register_global.ar_port, port, release);
116}
117
118arcade_register_t
119convert_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, type: 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
134ipc_port_t
135convert_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
146kern_return_t
147arcade_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(entitlement: "com.apple.arcade.fpsd")) {
159 return KERN_INVALID_VALUE;
160 }
161
162 lck_mtx_lock(lck: &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(port: 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(lck: &arcade_upcall_mutex);
174 thread_wakeup(&arcade_upcall_port);
175 return KERN_SUCCESS;
176 }
177 lck_mtx_unlock(lck: &arcade_upcall_mutex);
178 return KERN_FAILURE;
179}
180
181
182static kern_return_t
183arcade_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(lck: &arcade_upcall_mutex, lck_sleep_action: LCK_SLEEP_DEFAULT,
195 event: &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(port: 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_reg: &arcade_register_global);
222 kr = fairplayd_arcade_request(fairplayd_port, arcade_reg_port: port);
223
224 ipc_port_release_send(port: 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(lck: &arcade_upcall_mutex, lck_sleep_action: LCK_SLEEP_DEFAULT,
238 event: &arcade_upcall_port, THREAD_INTERRUPTIBLE, deadline);
239
240finish_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
249out:
250 switch (wr) {
251 case THREAD_AWAKENED:
252 return KERN_SUCCESS;
253 default:
254 return KERN_FAILURE;
255 }
256}
257
258static kern_return_t
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(arcade_upcall: port, path: (vm_offset_t)path, pathCnt: len, offset, should_kill: should_killp);
267}
268
269void
270arcade_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(interval: 10, NSEC_PER_SEC, result: &deadline);
279
280restart:
281 lck_mtx_lock(lck: &arcade_upcall_mutex);
282 port = ipc_port_copy_send_mqueue(port: 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(lck: &arcade_upcall_mutex);
296 goto fail;
297 }
298 port = ipc_port_copy_send_mqueue(port: arcade_upcall_port);
299 }
300 lck_mtx_unlock(lck: &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, arg: 0, buffer: path, MAXPATHLEN, NULL);
312 assert(!retval);
313 kr = vm_map_copyin(src_map: kernel_map, src_addr: (vm_map_address_t)path, MAXPATHLEN, FALSE, copy_result: &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, path: copy, MAXPATHLEN, offset, should_killp: &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(task: current_task());
338 }
339 break;
340 default:
341fail:
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(task: current_task());
350 break;
351 }
352}
353