1 /*
2 * Copyright (c) 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 <os/refcnt.h>
30
31 #include <kern/ipc_kobject.h>
32 #include <kern/ipc_tt.h>
33 #include <kern/task_ident.h>
34
35 #include <mach/mach_types.h>
36 #include <mach/task.h>
37 #include <mach/notify.h>
38 #include <mach/kern_return.h>
39
40 #include <security/mac_mach_internal.h>
41 #include <kern/task_ident.h>
42 #include <corpses/task_corpse.h>
43
44 struct proc_ident {
45 uint64_t p_uniqueid;
46 pid_t p_pid;
47 int p_idversion;
48 };
49
50 extern void* proc_find_ident(struct proc_ident const *i);
51 extern int proc_rele(void* p);
52 extern task_t proc_task(void* p);
53 extern struct proc_ident proc_ident(void* p);
54 extern kern_return_t task_conversion_eval(task_t caller, task_t victim);
55
56 /* Exported to kexts */
57 extern typeof(task_id_token_port_name_to_task) task_id_token_port_name_to_task_external;
58
59 struct task_id_token {
60 struct proc_ident ident;
61 ipc_port_t port;
62 uint64_t task_uniqueid; /* for corpse task */
63 os_refcnt_t tidt_refs;
64 };
65
66 static ZONE_DEFINE_TYPE(task_id_token_zone, "task_id_token",
67 struct task_id_token, ZC_ZFREE_CLEARMEM);
68
69 void task_id_token_set_port(task_id_token_t token, ipc_port_t port);
70
71 static void
tidt_reference(task_id_token_t token)72 tidt_reference(task_id_token_t token)
73 {
74 if (token == TASK_ID_TOKEN_NULL) {
75 return;
76 }
77 os_ref_retain(&token->tidt_refs);
78 }
79
80 static void
tidt_release(task_id_token_t token)81 tidt_release(task_id_token_t token)
82 {
83 ipc_port_t port;
84
85 if (token == TASK_ID_TOKEN_NULL) {
86 return;
87 }
88
89 if (os_ref_release(&token->tidt_refs) > 0) {
90 return;
91 }
92
93 /* last ref */
94 port = token->port;
95
96 if (IP_VALID(port)) {
97 #if CONFIG_PROC_RESOURCE_LIMITS
98 /*
99 * Ports of type IKOT_TASK_FATAL use task_ident objects to avoid holding a task reference
100 * and are created to send resource limit notifications
101 */
102 int kotype = ip_kotype(port);
103 if (kotype == IKOT_TASK_ID_TOKEN || kotype == IKOT_TASK_FATAL) {
104 ipc_kobject_dealloc_port(port, 0, kotype);
105 } else {
106 panic("%s: unexpected kotype of port %p: got %d",
107 __func__, port, kotype);
108 }
109 #else /* CONFIG_PROC_RESOURCE_LIMITS */
110 ipc_kobject_dealloc_port(port, 0, IKOT_TASK_ID_TOKEN);
111 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
112 }
113
114 zfree(task_id_token_zone, token);
115 }
116
117 void
task_id_token_release(task_id_token_t token)118 task_id_token_release(task_id_token_t token)
119 {
120 tidt_release(token);
121 }
122
123 static void
task_id_token_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)124 task_id_token_no_senders(ipc_port_t port, __unused mach_port_mscount_t mscount)
125 {
126 task_id_token_t token;
127
128 token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
129 assert(token != NULL);
130 assert(port->ip_srights == 0);
131
132 tidt_release(token); /* consumes ref given by notification */
133 }
134
135 IPC_KOBJECT_DEFINE(IKOT_TASK_ID_TOKEN,
136 .iko_op_stable = true,
137 .iko_op_no_senders = task_id_token_no_senders);
138
139 kern_return_t
task_create_identity_token(task_t task,task_id_token_t * tokenp)140 task_create_identity_token(
141 task_t task,
142 task_id_token_t *tokenp)
143 {
144 task_id_token_t token;
145
146 if (task == TASK_NULL || task == kernel_task) {
147 return KERN_INVALID_ARGUMENT;
148 }
149
150 token = zalloc_flags(task_id_token_zone, Z_ZERO | Z_WAITOK | Z_NOFAIL);
151
152 task_lock(task);
153
154 if (task->bsd_info) {
155 token->ident = proc_ident(task->bsd_info);
156 } else if (is_corpsetask(task)) {
157 token->task_uniqueid = task->task_uniqueid;
158 } else {
159 task_unlock(task);
160 zfree(task_id_token_zone, token);
161 return KERN_INVALID_ARGUMENT;
162 }
163
164 token->port = IP_NULL;
165 /* this reference will be donated to no-senders notification */
166 os_ref_init_count(&token->tidt_refs, NULL, 1);
167
168 task_unlock(task);
169
170 *tokenp = token;
171
172 return KERN_SUCCESS;
173 }
174
175 /* Produces (corpse) task reference, does not consume token reference */
176 kern_return_t
task_identity_token_get_task_grp(task_id_token_t token,task_t * taskp,task_grp_t grp)177 task_identity_token_get_task_grp(
178 task_id_token_t token,
179 task_t *taskp,
180 task_grp_t grp)
181 {
182 kern_return_t kr;
183 task_t task;
184
185 if (token == TASK_ID_TOKEN_NULL) {
186 return KERN_INVALID_ARGUMENT;
187 }
188
189 if (token->task_uniqueid) {
190 kr = find_corpse_task_by_uniqueid_grp(token->task_uniqueid, &task, grp); /* produces ref */
191 if (kr) {
192 return KERN_NOT_FOUND;
193 }
194 assert(is_corpsetask(task));
195 } else {
196 void* p = proc_find_ident(&token->ident);
197 if (p == NULL) {
198 return KERN_NOT_FOUND;
199 }
200 task = proc_task(p);
201 task_reference_grp(task, grp); /* produces ref */
202 proc_rele(p);
203 }
204
205 *taskp = task;
206
207 return KERN_SUCCESS;
208 }
209
210 /* Produces task port send right, does not consume token reference */
211 kern_return_t
task_identity_token_get_task_port(task_id_token_t token,task_flavor_t flavor,mach_port_t * portp)212 task_identity_token_get_task_port(
213 task_id_token_t token,
214 task_flavor_t flavor,
215 mach_port_t *portp)
216 {
217 task_t task;
218 kern_return_t kr;
219
220 if (token == TASK_ID_TOKEN_NULL) {
221 return KERN_INVALID_ARGUMENT;
222 }
223
224 if (flavor > TASK_FLAVOR_MAX) {
225 return KERN_INVALID_ARGUMENT;
226 }
227
228 if (token->task_uniqueid) {
229 /*
230 * For corpses, the control port reference would hold the corpse,
231 * only allow conversion to control port for now.
232 */
233 if (flavor != TASK_FLAVOR_CONTROL) {
234 return KERN_INVALID_ARGUMENT;
235 }
236 }
237
238 if ((kr = task_identity_token_get_task_grp(token, &task, TASK_GRP_KERNEL)) != KERN_SUCCESS) {
239 return kr;
240 }
241
242 assert(task != TASK_NULL);
243 assert(token != TASK_ID_TOKEN_NULL);
244
245 /* holding a ref on (corpse) task */
246
247 if (flavor == TASK_FLAVOR_CONTROL && task == current_task()) {
248 *portp = convert_task_to_port_pinned(task); /* consumes task ref */
249 return KERN_SUCCESS;
250 }
251 if (flavor <= TASK_FLAVOR_INSPECT && task_conversion_eval(current_task(), task)) {
252 task_deallocate(task);
253 return KERN_INVALID_ARGUMENT;
254 }
255
256 #if CONFIG_MACF
257
258 if (task != current_task()) {
259 if (mac_task_check_task_id_token_get_task(task, flavor)) {
260 task_deallocate(task);
261 return KERN_DENIED;
262 }
263 }
264 #endif
265
266 *portp = convert_task_to_port_with_flavor(task, flavor, TASK_GRP_KERNEL);
267 /* task ref consumed */
268
269 return KERN_SUCCESS;
270 }
271
272 /* Produces task reference */
273 static kern_return_t
task_id_token_port_name_to_task_grp(mach_port_name_t name,task_t * task,task_grp_t grp)274 task_id_token_port_name_to_task_grp(
275 mach_port_name_t name,
276 task_t *task,
277 task_grp_t grp)
278 {
279 kern_return_t kr;
280 task_id_token_t token;
281
282 token = port_name_to_task_id_token(name); /* produces ref */
283 kr = task_identity_token_get_task_grp(token, task, grp);
284
285 tidt_release(token); /* consumes ref */
286
287 return kr;
288 }
289 /* Used by kexts only */
290 kern_return_t
task_id_token_port_name_to_task_external(mach_port_name_t name,task_t * task)291 task_id_token_port_name_to_task_external(
292 mach_port_name_t name,
293 task_t *task)
294 {
295 return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_EXTERNAL);
296 }
297 /* Used by kernel proper */
298 kern_return_t
task_id_token_port_name_to_task(mach_port_name_t name,task_t * task)299 task_id_token_port_name_to_task(
300 mach_port_name_t name,
301 task_t *task)
302 {
303 return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_KERNEL);
304 }
305
306 /* Produces token reference */
307 task_id_token_t
convert_port_to_task_id_token(ipc_port_t port)308 convert_port_to_task_id_token(
309 ipc_port_t port)
310 {
311 task_id_token_t token = TASK_ID_TOKEN_NULL;
312
313 if (IP_VALID(port)) {
314 token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
315 if (token != TASK_ID_TOKEN_NULL) {
316 zone_require(task_id_token_zone, token);
317 tidt_reference(token);
318 }
319 }
320 return token;
321 }
322
323 /* Consumes token reference */
324 ipc_port_t
convert_task_id_token_to_port(task_id_token_t token)325 convert_task_id_token_to_port(
326 task_id_token_t token)
327 {
328 boolean_t kr;
329
330 if (token == TASK_ID_TOKEN_NULL) {
331 return IP_NULL;
332 }
333
334 zone_require(task_id_token_zone, token);
335
336 kr = ipc_kobject_make_send_lazy_alloc_port(&token->port,
337 (ipc_kobject_t) token, IKOT_TASK_ID_TOKEN,
338 IPC_KOBJECT_ALLOC_NONE, 0);
339 assert(kr == TRUE); /* no-senders notification is armed, consumes token ref */
340
341 return token->port;
342 }
343
344 #if CONFIG_PROC_RESOURCE_LIMITS
345
346 /* Should be used only by ports of type IKOT_TASK_FATAL at allocation time */
347 void
task_id_token_set_port(task_id_token_t token,ipc_port_t port)348 task_id_token_set_port(
349 task_id_token_t token,
350 ipc_port_t port)
351 {
352 assert(token && port && (ip_kotype(port) == IKOT_TASK_FATAL));
353 token->port = port;
354 }
355 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
356