1/* 2 * Copyright (c) 2000-2006 Apple Computer, 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/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 29/*- 30 * Copyright (c) 1982, 1986, 1989, 1993 31 * The Regents of the University of California. All rights reserved. 32 * (c) UNIX System Laboratories, Inc. 33 * All or some portions of this file are derived from material licensed 34 * to the University of California by American Telephone and Telegraph 35 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 36 * the permission of UNIX System Laboratories, Inc. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. All advertising materials mentioning features or use of this software 47 * must display the following acknowledgement: 48 * This product includes software developed by the University of 49 * California, Berkeley and its contributors. 50 * 4. Neither the name of the University nor the names of its contributors 51 * may be used to endorse or promote products derived from this software 52 * without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 * SUCH DAMAGE. 65 * 66 * from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93 67 */ 68 69#include <machine/reg.h> 70#include <machine/psl.h> 71 72#include <sys/param.h> 73#include <sys/systm.h> 74#include <sys/proc_internal.h> 75#include <sys/kauth.h> 76#include <sys/errno.h> 77#include <sys/ptrace.h> 78#include <sys/uio.h> 79#include <sys/user.h> 80#include <sys/sysctl.h> 81#include <sys/wait.h> 82 83#include <sys/mount_internal.h> 84#include <sys/sysproto.h> 85#include <sys/kdebug.h> 86#include <sys/codesign.h> /* cs_allow_invalid() */ 87 88#include <security/audit/audit.h> 89 90#include <kern/task.h> 91#include <kern/thread.h> 92 93#include <mach/task.h> /* for task_resume() */ 94#include <kern/sched_prim.h> /* for thread_exception_return() */ 95 96#include <pexpert/pexpert.h> 97 98/* XXX ken/bsd_kern.c - prototype should be in common header */ 99int get_task_userstop(task_t); 100 101/* Macros to clear/set/test flags. */ 102#define SET(t, f) (t) |= (f) 103#define CLR(t, f) (t) &= ~(f) 104#define ISSET(t, f) ((t) & (f)) 105 106extern thread_t port_name_to_thread(mach_port_name_t port_name); 107extern thread_t get_firstthread(task_t); 108 109 110/* 111 * sys-trace system call. 112 */ 113 114int 115ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) 116{ 117 struct proc *t = current_proc(); /* target process */ 118 task_t task; 119 thread_t th_act; 120 struct uthread *ut; 121 int tr_sigexc = 0; 122 int error = 0; 123 int stopped = 0; 124 125 AUDIT_ARG(cmd, uap->req); 126 AUDIT_ARG(pid, uap->pid); 127 AUDIT_ARG(addr, uap->addr); 128 AUDIT_ARG(value32, uap->data); 129 130 if (uap->req == PT_DENY_ATTACH) { 131 proc_lock(p); 132 if (ISSET(p->p_lflag, P_LTRACED)) { 133 proc_unlock(p); 134 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_FRCEXIT) | DBG_FUNC_NONE, 135 p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0); 136 exit1(p, W_EXITCODE(ENOTSUP, 0), retval); 137 138 thread_exception_return(); 139 /* NOTREACHED */ 140 } 141 SET(p->p_lflag, P_LNOATTACH); 142 proc_unlock(p); 143 144 return(0); 145 } 146 147 if (uap->req == PT_FORCEQUOTA) { 148 if (kauth_cred_issuser(kauth_cred_get())) { 149 OSBitOrAtomic(P_FORCEQUOTA, &t->p_flag); 150 return (0); 151 } else 152 return (EPERM); 153 } 154 155 /* 156 * Intercept and deal with "please trace me" request. 157 */ 158 if (uap->req == PT_TRACE_ME) { 159 proc_lock(p); 160 SET(p->p_lflag, P_LTRACED); 161 /* Non-attached case, our tracer is our parent. */ 162 p->p_oppid = p->p_ppid; 163 /* Check whether child and parent are allowed to run modified 164 * code (they'll have to) */ 165 struct proc *pproc=proc_find(p->p_oppid); 166 proc_unlock(p); 167 cs_allow_invalid(p); 168 if(pproc) { 169 cs_allow_invalid(pproc); 170 proc_rele(pproc); 171 } 172 return(0); 173 } 174 if (uap->req == PT_SIGEXC) { 175 proc_lock(p); 176 if (ISSET(p->p_lflag, P_LTRACED)) { 177 SET(p->p_lflag, P_LSIGEXC); 178 proc_unlock(p); 179 return(0); 180 } else { 181 proc_unlock(p); 182 return(EINVAL); 183 } 184 } 185 186 /* 187 * We do not want ptrace to do anything with kernel or launchd 188 */ 189 if (uap->pid < 2) { 190 return(EPERM); 191 } 192 193 /* 194 * Locate victim, and make sure it is traceable. 195 */ 196 if ((t = proc_find(uap->pid)) == NULL) 197 return (ESRCH); 198 199 AUDIT_ARG(process, t); 200 201 task = t->task; 202 if (uap->req == PT_ATTACHEXC) { 203 uap->req = PT_ATTACH; 204 tr_sigexc = 1; 205 } 206 if (uap->req == PT_ATTACH) { 207 int err; 208 209 if ( kauth_authorize_process(proc_ucred(p), KAUTH_PROCESS_CANTRACE, 210 t, (uintptr_t)&err, 0, 0) == 0 ) { 211 /* it's OK to attach */ 212 proc_lock(t); 213 SET(t->p_lflag, P_LTRACED); 214 if (tr_sigexc) 215 SET(t->p_lflag, P_LSIGEXC); 216 217 t->p_oppid = t->p_ppid; 218 /* Check whether child and parent are allowed to run modified 219 * code (they'll have to) */ 220 proc_unlock(t); 221 cs_allow_invalid(t); 222 cs_allow_invalid(p); 223 if (t->p_pptr != p) 224 proc_reparentlocked(t, p, 1, 0); 225 226 proc_lock(t); 227 if (get_task_userstop(task) > 0 ) { 228 stopped = 1; 229 } 230 t->p_xstat = 0; 231 proc_unlock(t); 232 psignal(t, SIGSTOP); 233 /* 234 * If the process was stopped, wake up and run through 235 * issignal() again to properly connect to the tracing 236 * process. 237 */ 238 if (stopped) 239 task_resume(task); 240 error = 0; 241 goto out; 242 } 243 else { 244 /* not allowed to attach, proper error code returned by kauth_authorize_process */ 245 if (ISSET(t->p_lflag, P_LNOATTACH)) { 246 psignal(p, SIGSEGV); 247 } 248 249 error = err; 250 goto out; 251 } 252 } 253 254 /* 255 * You can't do what you want to the process if: 256 * (1) It's not being traced at all, 257 */ 258 proc_lock(t); 259 if (!ISSET(t->p_lflag, P_LTRACED)) { 260 proc_unlock(t); 261 error = EPERM; 262 goto out; 263 } 264 265 /* 266 * (2) it's not being traced by _you_, or 267 */ 268 if (t->p_pptr != p) { 269 proc_unlock(t); 270 error = EBUSY; 271 goto out; 272 } 273 274 /* 275 * (3) it's not currently stopped. 276 */ 277 if (t->p_stat != SSTOP) { 278 proc_unlock(t); 279 error = EBUSY; 280 goto out; 281 } 282 283 /* 284 * Mach version of ptrace executes request directly here, 285 * thus simplifying the interaction of ptrace and signals. 286 */ 287 /* proc lock is held here */ 288 switch (uap->req) { 289 290 case PT_DETACH: 291 if (t->p_oppid != t->p_ppid) { 292 struct proc *pp; 293 294 proc_unlock(t); 295 pp = proc_find(t->p_oppid); 296 if (pp != PROC_NULL) { 297 proc_reparentlocked(t, pp, 1, 0); 298 proc_rele(pp); 299 } else { 300 /* original parent exited while traced */ 301 proc_list_lock(); 302 t->p_listflag |= P_LIST_DEADPARENT; 303 proc_list_unlock(); 304 proc_reparentlocked(t, initproc, 1, 0); 305 } 306 proc_lock(t); 307 } 308 309 t->p_oppid = 0; 310 CLR(t->p_lflag, P_LTRACED); 311 CLR(t->p_lflag, P_LSIGEXC); 312 proc_unlock(t); 313 goto resume; 314 315 case PT_KILL: 316 /* 317 * Tell child process to kill itself after it 318 * is resumed by adding NSIG to p_cursig. [see issig] 319 */ 320 proc_unlock(t); 321#if CONFIG_MACF 322 error = mac_proc_check_signal(p, t, SIGKILL); 323 if (0 != error) 324 goto resume; 325#endif 326 psignal(t, SIGKILL); 327 goto resume; 328 329 case PT_STEP: /* single step the child */ 330 case PT_CONTINUE: /* continue the child */ 331 proc_unlock(t); 332 th_act = (thread_t)get_firstthread(task); 333 if (th_act == THREAD_NULL) { 334 error = EINVAL; 335 goto out; 336 } 337 338 /* force use of Mach SPIs (and task_for_pid security checks) to adjust PC */ 339 if (uap->addr != (user_addr_t)1) { 340 error = ENOTSUP; 341 goto out; 342 } 343 344 if ((unsigned)uap->data >= NSIG) { 345 error = EINVAL; 346 goto out; 347 } 348 349 if (uap->data != 0) { 350 psignal(t, uap->data); 351 } 352 353 if (uap->req == PT_STEP) { 354 /* 355 * set trace bit 356 * we use sending SIGSTOP as a comparable security check. 357 */ 358#if CONFIG_MACF 359 error = mac_proc_check_signal(p, t, SIGSTOP); 360 if (0 != error) { 361 goto out; 362 } 363#endif 364 if (thread_setsinglestep(th_act, 1) != KERN_SUCCESS) { 365 error = ENOTSUP; 366 goto out; 367 } 368 } else { 369 /* 370 * clear trace bit if on 371 * we use sending SIGCONT as a comparable security check. 372 */ 373#if CONFIG_MACF 374 error = mac_proc_check_signal(p, t, SIGCONT); 375 if (0 != error) { 376 goto out; 377 } 378#endif 379 if (thread_setsinglestep(th_act, 0) != KERN_SUCCESS) { 380 error = ENOTSUP; 381 goto out; 382 } 383 } 384 resume: 385 proc_lock(t); 386 t->p_xstat = uap->data; 387 t->p_stat = SRUN; 388 if (t->sigwait) { 389 wakeup((caddr_t)&(t->sigwait)); 390 proc_unlock(t); 391 if ((t->p_lflag & P_LSIGEXC) == 0) { 392 task_resume(task); 393 } 394 } else 395 proc_unlock(t); 396 397 break; 398 399 case PT_THUPDATE: { 400 proc_unlock(t); 401 if ((unsigned)uap->data >= NSIG) { 402 error = EINVAL; 403 goto out; 404 } 405 th_act = port_name_to_thread(CAST_MACH_PORT_TO_NAME(uap->addr)); 406 if (th_act == THREAD_NULL) 407 return (ESRCH); 408 ut = (uthread_t)get_bsdthread_info(th_act); 409 if (uap->data) 410 ut->uu_siglist |= sigmask(uap->data); 411 proc_lock(t); 412 t->p_xstat = uap->data; 413 t->p_stat = SRUN; 414 proc_unlock(t); 415 thread_deallocate(th_act); 416 error = 0; 417 } 418 break; 419 default: 420 proc_unlock(t); 421 error = EINVAL; 422 goto out; 423 } 424 425 error = 0; 426out: 427 proc_rele(t); 428 return(error); 429} 430 431 432/* 433 * determine if one process (cur_procp) can trace another process (traced_procp). 434 */ 435 436int 437cantrace(proc_t cur_procp, kauth_cred_t creds, proc_t traced_procp, int *errp) 438{ 439 int my_err; 440 /* 441 * You can't trace a process if: 442 * (1) it's the process that's doing the tracing, 443 */ 444 if (traced_procp->p_pid == cur_procp->p_pid) { 445 *errp = EINVAL; 446 return (0); 447 } 448 449 /* 450 * (2) it's already being traced, or 451 */ 452 if (ISSET(traced_procp->p_lflag, P_LTRACED)) { 453 *errp = EBUSY; 454 return (0); 455 } 456 457 /* 458 * (3) it's not owned by you, or is set-id on exec 459 * (unless you're root). 460 */ 461 if ((kauth_cred_getruid(creds) != kauth_cred_getruid(proc_ucred(traced_procp)) || 462 ISSET(traced_procp->p_flag, P_SUGID)) && 463 (my_err = suser(creds, &cur_procp->p_acflag)) != 0) { 464 *errp = my_err; 465 return (0); 466 } 467 468 if ((cur_procp->p_lflag & P_LTRACED) && isinferior(cur_procp, traced_procp)) { 469 *errp = EPERM; 470 return (0); 471 } 472 473 if (ISSET(traced_procp->p_lflag, P_LNOATTACH)) { 474 *errp = EBUSY; 475 return (0); 476 } 477 478#if CONFIG_MACF 479 if ((my_err = mac_proc_check_debug(cur_procp, traced_procp)) != 0) { 480 *errp = my_err; 481 return (0); 482 } 483#endif 484 485 return(1); 486} 487