/ duct-tape / xnu / osfmk / kern / task_ident.c
task_ident.c
  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  #include <kern/ipc_kobject.h>
 31  #include <mach/mach_types.h>
 32  #include <mach/task.h>
 33  #include <mach/notify.h>
 34  #include <mach/kern_return.h>
 35  #include <security/mac_mach_internal.h>
 36  #include <kern/task_ident.h>
 37  
 38  #ifdef __DARLING__
 39  #include <darlingserver/duct-tape/task.h>
 40  
 41  kern_return_t
 42  task_get_special_port(
 43  	task_t          task,
 44  	int             which,
 45  	ipc_port_t      *portp);
 46  #else
 47  struct proc_ident {
 48  	uint64_t        p_uniqueid;
 49  	pid_t           p_pid;
 50  	int             p_idversion;
 51  };
 52  #endif // __DARLING__
 53  
 54  extern void* proc_find_ident(struct proc_ident const *i);
 55  extern int proc_rele(void* p);
 56  extern task_t proc_task(void* p);
 57  extern struct proc_ident proc_ident(void* p);
 58  extern kern_return_t task_conversion_eval(task_t caller, task_t victim);
 59  
 60  struct task_id_token {
 61  	struct proc_ident ident;
 62  	ipc_port_t        port;
 63  	os_refcnt_t       tidt_refs;
 64  };
 65  
 66  #ifdef __DARLING__
 67  zone_t task_id_token_zone;
 68  #else
 69  static ZONE_DECLARE(task_id_token_zone, "task_id_token",
 70      sizeof(struct task_id_token), ZC_ZFREE_CLEARMEM);
 71  #endif // __DARLING__
 72  
 73  static void
 74  tidt_reference(task_id_token_t token)
 75  {
 76  	if (token == TASK_ID_TOKEN_NULL) {
 77  		return;
 78  	}
 79  	os_ref_retain(&token->tidt_refs);
 80  }
 81  
 82  static void
 83  tidt_release(task_id_token_t token)
 84  {
 85  	ipc_port_t port;
 86  
 87  	if (token == TASK_ID_TOKEN_NULL) {
 88  		return;
 89  	}
 90  
 91  	if (os_ref_release(&token->tidt_refs) > 0) {
 92  		return;
 93  	}
 94  
 95  	/* last ref */
 96  	port = token->port;
 97  
 98  	require_ip_active(port);
 99  	assert(!port->ip_srights);
100  	ipc_port_dealloc_kernel(port);
101  
102  	zfree(task_id_token_zone, token);
103  }
104  
105  void
106  task_id_token_release(task_id_token_t token)
107  {
108  	tidt_release(token);
109  }
110  
111  void
112  task_id_token_notify(mach_msg_header_t *msg)
113  {
114  	assert(msg->msgh_id == MACH_NOTIFY_NO_SENDERS);
115  
116  	mach_no_senders_notification_t *not = (mach_no_senders_notification_t *)msg;
117  	ipc_port_t port = not->not_header.msgh_remote_port;
118  	task_id_token_t token = ip_get_kobject(port);
119  
120  	require_ip_active(port);
121  	assert(IKOT_TASK_ID_TOKEN == ip_kotype(port));
122  	assert(port->ip_srights == 0);
123  
124  	tidt_release(token); /* consumes ref given by notification */
125  }
126  
127  kern_return_t
128  task_create_identity_token(
129  	task_t task,
130  	task_id_token_t *tokenp)
131  {
132  	task_id_token_t token;
133  
134  	if (task == TASK_NULL || task == kernel_task) {
135  		return KERN_INVALID_ARGUMENT;
136  	}
137  
138  	token = zalloc_flags(task_id_token_zone, Z_ZERO | Z_WAITOK | Z_NOFAIL);
139  
140  	task_lock(task);
141  #ifdef __DARLING__
142  	dtape_task_t* dtape_task = dtape_task_for_xnu_task(task);
143  	if (dtape_task) {
144  		token->port = IP_NULL;
145  		token->ident = proc_ident(dtape_task);
146  #else
147  	if (task->bsd_info) {
148  		token->port = IP_NULL;
149  		token->ident = proc_ident(task->bsd_info);
150  #endif // __DARLING__
151  		/* this reference will be donated to no-senders notification */
152  		os_ref_init_count(&token->tidt_refs, NULL, 1);
153  	} else {
154  		task_unlock(task);
155  		zfree(task_id_token_zone, token);
156  		return KERN_INVALID_ARGUMENT;
157  	}
158  	task_unlock(task);
159  
160  	*tokenp = token;
161  
162  	return KERN_SUCCESS;
163  }
164  
165  kern_return_t
166  task_identity_token_get_task_port(
167  	task_id_token_t token,
168  	task_flavor_t  flavor,
169  	ipc_port_t    *portp)
170  {
171  	int which;
172  	task_t task;
173  	kern_return_t kr;
174  
175  	if (token == TASK_ID_TOKEN_NULL) {
176  		return KERN_INVALID_ARGUMENT;
177  	}
178  
179  	switch (flavor) {
180  	case TASK_FLAVOR_NAME:
181  		which = TASK_NAME_PORT;
182  		break;
183  	case TASK_FLAVOR_INSPECT:
184  		which = TASK_INSPECT_PORT;
185  		break;
186  	case TASK_FLAVOR_READ:
187  		which = TASK_READ_PORT;
188  		break;
189  	case TASK_FLAVOR_CONTROL:
190  		which = TASK_KERNEL_PORT;
191  		break;
192  	default:
193  		return KERN_INVALID_ARGUMENT;
194  	}
195  
196  	void* p = proc_find_ident(&token->ident);
197  	if (p == NULL) {
198  		return KERN_INVALID_ARGUMENT;
199  	}
200  	task = proc_task(p);
201  	task_reference(task);
202  	proc_rele(p);
203  
204  	if (task == TASK_NULL) {
205  		return KERN_INVALID_ARGUMENT;
206  	}
207  
208  	if (flavor == TASK_FLAVOR_CONTROL && task == current_task()) {
209  		*portp = convert_task_to_port_pinned(task); /* consumes task ref */
210  		return KERN_SUCCESS;
211  	}
212  	if (flavor <= TASK_FLAVOR_INSPECT && task_conversion_eval(current_task(), task)) {
213  		task_deallocate(task);
214  		return KERN_INVALID_ARGUMENT;
215  	}
216  
217  #if CONFIG_MACF
218  	if (task != current_task()) {
219  		if (mac_task_check_task_id_token_get_task(task, flavor)) {
220  			task_deallocate(task);
221  			return KERN_DENIED;
222  		}
223  	}
224  #endif
225  
226  	kr = task_get_special_port(task, which, portp);
227  	task_deallocate(task);
228  	return kr;
229  }
230  
231  /* Produces token ref */
232  task_id_token_t
233  convert_port_to_task_id_token(
234  	ipc_port_t              port)
235  {
236  	task_id_token_t token = TASK_ID_TOKEN_NULL;
237  
238  	if (IP_VALID(port)) {
239  		ip_lock(port);
240  		if (ip_active(port)) {
241  			if (ip_kotype(port) == IKOT_TASK_ID_TOKEN) {
242  				token = (task_id_token_t)ip_get_kobject(port);
243  
244  				zone_require(task_id_token_zone, token);
245  				tidt_reference(token);
246  			}
247  		}
248  		ip_unlock(port);
249  	}
250  	return token;
251  }
252  
253  /* Consumes token ref */
254  ipc_port_t
255  convert_task_id_token_to_port(
256  	task_id_token_t token)
257  {
258  	boolean_t kr;
259  
260  	if (token == TASK_ID_TOKEN_NULL) {
261  		return IP_NULL;
262  	}
263  
264  	zone_require(task_id_token_zone, token);
265  
266  	kr = ipc_kobject_make_send_lazy_alloc_port(&token->port,
267  	    (ipc_kobject_t) token, IKOT_TASK_ID_TOKEN, IPC_KOBJECT_ALLOC_NONE, false, 0);
268  	assert(kr == TRUE); /* no-senders notification is armed, consumes token ref */
269  
270  	return token->port;
271  }