X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..c6bf4f310a33a9262d455ea4d3f0630b1255e3fe:/bsd/kern/mach_process.c diff --git a/bsd/kern/mach_process.c b/bsd/kern/mach_process.c index 1a5cac88f..915a8cb45 100644 --- a/bsd/kern/mach_process.c +++ b/bsd/kern/mach_process.c @@ -2,7 +2,7 @@ * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ @@ -83,28 +83,30 @@ #include #include #include +#include /* cs_allow_invalid() */ #include #include #include -#include /* for task_resume() */ -#include /* for thread_exception_return() */ - -#include /* cs_allow_invalid() */ +#include /* for task_resume() */ +#include /* for thread_exception_return() */ #include +#if CONFIG_MACF +#include +#endif + /* XXX ken/bsd_kern.c - prototype should be in common header */ int get_task_userstop(task_t); /* Macros to clear/set/test flags. */ -#define SET(t, f) (t) |= (f) -#define CLR(t, f) (t) &= ~(f) -#define ISSET(t, f) ((t) & (f)) +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) -extern thread_t port_name_to_thread(mach_port_name_t port_name); extern thread_t get_firstthread(task_t); @@ -115,10 +117,10 @@ extern thread_t get_firstthread(task_t); int ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) { - struct proc *t = current_proc(); /* target process */ - task_t task; - thread_t th_act; - struct uthread *ut; + struct proc *t = current_proc(); /* target process */ + task_t task; + thread_t th_act; + struct uthread *ut; int tr_sigexc = 0; int error = 0; int stopped = 0; @@ -129,103 +131,141 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) AUDIT_ARG(value32, uap->data); if (uap->req == PT_DENY_ATTACH) { +#if (DEVELOPMENT || DEBUG) && CONFIG_EMBEDDED + if (PE_i_can_has_debugger(NULL)) { + return 0; + } +#endif proc_lock(p); if (ISSET(p->p_lflag, P_LTRACED)) { proc_unlock(p); KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_FRCEXIT) | DBG_FUNC_NONE, - p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0); + p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0); exit1(p, W_EXITCODE(ENOTSUP, 0), retval); - /* drop funnel before we return */ + thread_exception_return(); /* NOTREACHED */ } SET(p->p_lflag, P_LNOATTACH); proc_unlock(p); - return(0); + return 0; } if (uap->req == PT_FORCEQUOTA) { - if (is_suser()) { + if (kauth_cred_issuser(kauth_cred_get())) { OSBitOrAtomic(P_FORCEQUOTA, &t->p_flag); - return (0); - } else - return (EPERM); + return 0; + } else { + return EPERM; + } } /* * Intercept and deal with "please trace me" request. - */ + */ if (uap->req == PT_TRACE_ME) { +retry_trace_me: ; + proc_t pproc = proc_parent(p); + if (pproc == NULL) { + return EINVAL; + } +#if CONFIG_MACF + /* + * NB: Cannot call kauth_authorize_process(..., KAUTH_PROCESS_CANTRACE, ...) + * since that assumes the process being checked is the current process + * when, in this case, it is the current process's parent. + * Most of the other checks in cantrace() don't apply either. + */ + if ((error = mac_proc_check_debug(pproc, p)) == 0) { +#endif proc_lock(p); + /* Make sure the process wasn't re-parented. */ + if (p->p_ppid != pproc->p_pid) { + proc_unlock(p); + proc_rele(pproc); + goto retry_trace_me; + } SET(p->p_lflag, P_LTRACED); /* Non-attached case, our tracer is our parent. */ p->p_oppid = p->p_ppid; - /* Check whether child and parent are allowed to run modified - * code (they'll have to) */ - struct proc *pproc=proc_find(p->p_oppid); proc_unlock(p); + /* Child and parent will have to be able to run modified code. */ cs_allow_invalid(p); - if(pproc) { - cs_allow_invalid(pproc); - proc_rele(pproc); - } - return(0); + cs_allow_invalid(pproc); +#if CONFIG_MACF + } +#endif + proc_rele(pproc); + return error; } if (uap->req == PT_SIGEXC) { proc_lock(p); if (ISSET(p->p_lflag, P_LTRACED)) { SET(p->p_lflag, P_LSIGEXC); proc_unlock(p); - return(0); + return 0; } else { proc_unlock(p); - return(EINVAL); + return EINVAL; } } - /* - * We do not want ptrace to do anything with kernel or launchd + /* + * We do not want ptrace to do anything with kernel or launchd */ if (uap->pid < 2) { - return(EPERM); + return EPERM; } /* * Locate victim, and make sure it is traceable. */ - if ((t = proc_find(uap->pid)) == NULL) - return (ESRCH); + if ((t = proc_find(uap->pid)) == NULL) { + return ESRCH; + } AUDIT_ARG(process, t); task = t->task; if (uap->req == PT_ATTACHEXC) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" uap->req = PT_ATTACH; tr_sigexc = 1; } if (uap->req == PT_ATTACH) { - int err; - - if ( kauth_authorize_process(proc_ucred(p), KAUTH_PROCESS_CANTRACE, - t, (uintptr_t)&err, 0, 0) == 0 ) { +#pragma clang diagnostic pop + int err; + +#if CONFIG_EMBEDDED + if (tr_sigexc == 0) { + error = ENOTSUP; + goto out; + } +#endif + + if (kauth_authorize_process(proc_ucred(p), KAUTH_PROCESS_CANTRACE, + t, (uintptr_t)&err, 0, 0) == 0) { /* it's OK to attach */ proc_lock(t); SET(t->p_lflag, P_LTRACED); - if (tr_sigexc) + if (tr_sigexc) { SET(t->p_lflag, P_LSIGEXC); - + } + t->p_oppid = t->p_ppid; /* Check whether child and parent are allowed to run modified * code (they'll have to) */ proc_unlock(t); cs_allow_invalid(t); cs_allow_invalid(p); - if (t->p_pptr != p) + if (t->p_pptr != p) { proc_reparentlocked(t, p, 1, 0); - + } + proc_lock(t); - if (get_task_userstop(task) > 0 ) { + if (get_task_userstop(task) > 0) { stopped = 1; } t->p_xstat = 0; @@ -236,17 +276,17 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) * issignal() again to properly connect to the tracing * process. */ - if (stopped) - task_resume(task); + if (stopped) { + task_resume(task); + } error = 0; goto out; - } - else { + } else { /* not allowed to attach, proper error code returned by kauth_authorize_process */ if (ISSET(t->p_lflag, P_LNOATTACH)) { psignal(p, SIGSEGV); } - + error = err; goto out; } @@ -287,18 +327,23 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) */ /* proc lock is held here */ switch (uap->req) { - case PT_DETACH: if (t->p_oppid != t->p_ppid) { struct proc *pp; proc_unlock(t); pp = proc_find(t->p_oppid); - proc_reparentlocked(t, pp ? pp : initproc, 1, 0); - if (pp != PROC_NULL) + if (pp != PROC_NULL) { + proc_reparentlocked(t, pp, 1, 0); proc_rele(pp); + } else { + /* original parent exited while traced */ + proc_list_lock(); + t->p_listflag |= P_LIST_DEADPARENT; + proc_list_unlock(); + proc_reparentlocked(t, initproc, 1, 0); + } proc_lock(t); - } t->p_oppid = 0; @@ -306,23 +351,24 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) CLR(t->p_lflag, P_LSIGEXC); proc_unlock(t); goto resume; - + case PT_KILL: /* * Tell child process to kill itself after it * is resumed by adding NSIG to p_cursig. [see issig] */ proc_unlock(t); -#if NOTYET +#if CONFIG_MACF error = mac_proc_check_signal(p, t, SIGKILL); - if (0 != error) + if (0 != error) { goto resume; + } #endif psignal(t, SIGKILL); goto resume; - case PT_STEP: /* single step the child */ - case PT_CONTINUE: /* continue the child */ + case PT_STEP: /* single step the child */ + case PT_CONTINUE: /* continue the child */ proc_unlock(t); th_act = (thread_t)get_firstthread(task); if (th_act == THREAD_NULL) { @@ -342,15 +388,21 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) } if (uap->data != 0) { +#if CONFIG_MACF + error = mac_proc_check_signal(p, t, uap->data); + if (0 != error) { + goto out; + } +#endif psignal(t, uap->data); - } + } if (uap->req == PT_STEP) { - /* - * set trace bit + /* + * set trace bit * we use sending SIGSTOP as a comparable security check. */ -#if NOTYET +#if CONFIG_MACF error = mac_proc_check_signal(p, t, SIGSTOP); if (0 != error) { goto out; @@ -361,11 +413,11 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) goto out; } } else { - /* + /* * clear trace bit if on * we use sending SIGCONT as a comparable security check. */ -#if NOTYET +#if CONFIG_MACF error = mac_proc_check_signal(p, t, SIGCONT); if (0 != error) { goto out; @@ -375,8 +427,8 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) error = ENOTSUP; goto out; } - } - resume: + } +resume: proc_lock(t); t->p_xstat = uap->data; t->p_stat = SRUN; @@ -386,31 +438,36 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) if ((t->p_lflag & P_LSIGEXC) == 0) { task_resume(task); } - } else + } else { proc_unlock(t); - + } + break; - + case PT_THUPDATE: { proc_unlock(t); if ((unsigned)uap->data >= NSIG) { error = EINVAL; goto out; } - th_act = port_name_to_thread(CAST_MACH_PORT_TO_NAME(uap->addr)); - if (th_act == THREAD_NULL) - return (ESRCH); + th_act = port_name_to_thread(CAST_MACH_PORT_TO_NAME(uap->addr), + PORT_TO_THREAD_NONE); + if (th_act == THREAD_NULL) { + error = ESRCH; + goto out; + } ut = (uthread_t)get_bsdthread_info(th_act); - if (uap->data) + if (uap->data) { ut->uu_siglist |= sigmask(uap->data); + } proc_lock(t); t->p_xstat = uap->data; t->p_stat = SRUN; proc_unlock(t); thread_deallocate(th_act); error = 0; - } - break; + } + break; default: proc_unlock(t); error = EINVAL; @@ -420,7 +477,7 @@ ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval) error = 0; out: proc_rele(t); - return(error); + return error; } @@ -431,14 +488,14 @@ out: int cantrace(proc_t cur_procp, kauth_cred_t creds, proc_t traced_procp, int *errp) { - int my_err; + int my_err; /* * You can't trace a process if: * (1) it's the process that's doing the tracing, */ if (traced_procp->p_pid == cur_procp->p_pid) { *errp = EINVAL; - return (0); + return 0; } /* @@ -446,7 +503,7 @@ cantrace(proc_t cur_procp, kauth_cred_t creds, proc_t traced_procp, int *errp) */ if (ISSET(traced_procp->p_lflag, P_LTRACED)) { *errp = EBUSY; - return (0); + return 0; } /* @@ -454,20 +511,28 @@ cantrace(proc_t cur_procp, kauth_cred_t creds, proc_t traced_procp, int *errp) * (unless you're root). */ if ((kauth_cred_getruid(creds) != kauth_cred_getruid(proc_ucred(traced_procp)) || - ISSET(traced_procp->p_flag, P_SUGID)) && - (my_err = suser(creds, &cur_procp->p_acflag)) != 0) { + ISSET(traced_procp->p_flag, P_SUGID)) && + (my_err = suser(creds, &cur_procp->p_acflag)) != 0) { *errp = my_err; - return (0); + return 0; } if ((cur_procp->p_lflag & P_LTRACED) && isinferior(cur_procp, traced_procp)) { *errp = EPERM; - return (0); + return 0; } if (ISSET(traced_procp->p_lflag, P_LNOATTACH)) { *errp = EBUSY; - return (0); + return 0; + } + +#if CONFIG_MACF + if ((my_err = mac_proc_check_debug(cur_procp, traced_procp)) != 0) { + *errp = my_err; + return 0; } - return(1); +#endif + + return 1; }