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 }