X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/bsd/kern/kern_prot.c?ds=sidebyside diff --git a/bsd/kern/kern_prot.c b/bsd/kern/kern_prot.c index 73806d055..d689b70d5 100644 --- a/bsd/kern/kern_prot.c +++ b/bsd/kern/kern_prot.c @@ -2,7 +2,7 @@ * Copyright (c) 2000-2008 Apple 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,12 +22,12 @@ * 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 - * + * * * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -66,7 +66,7 @@ * SUCH DAMAGE. * * @(#)kern_prot.c 8.9 (Berkeley) 2/14/95 - * + * * * NOTICE: This file was modified by McAfee Research in 2004 to introduce * support for mandatory and extensible security protections. This notice @@ -95,18 +95,12 @@ #include #include #include +#include #include -#if CONFIG_LCTX -#include -#endif - #if CONFIG_MACF #include -#if CONFIG_MACF_MACH -#include -#endif #endif #include @@ -115,7 +109,7 @@ #include #include -#include /* for current_task() */ +#include /* for current_task() */ #include @@ -130,14 +124,17 @@ * can be used if needed when debugging is active. */ #if DEBUG_CRED -#define DEBUG_CRED_ENTER printf -#define DEBUG_CRED_CHANGE printf +#define DEBUG_CRED_ENTER printf +#define DEBUG_CRED_CHANGE printf extern void kauth_cred_print(kauth_cred_t cred); -#else /* !DEBUG_CRED */ -#define DEBUG_CRED_ENTER(fmt, ...) do {} while (0) -#define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0) -#endif /* !DEBUG_CRED */ +#else /* !DEBUG_CRED */ +#define DEBUG_CRED_ENTER(fmt, ...) do {} while (0) +#define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0) +#endif /* !DEBUG_CRED */ +#if DEVELOPMENT || DEBUG +extern void task_importance_update_owner_info(task_t); +#endif /* @@ -158,7 +155,7 @@ setprivexec(proc_t p, struct setprivexec_args *uap, int32_t *retval) AUDIT_ARG(value32, uap->flag); *retval = p->p_debugger; p->p_debugger = (uap->flag != 0); - return(0); + return 0; } @@ -176,9 +173,8 @@ setprivexec(proc_t p, struct setprivexec_args *uap, int32_t *retval) int getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval) { - *retval = p->p_pid; - return (0); + return 0; } @@ -196,9 +192,8 @@ getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval) int getppid(proc_t p, __unused struct getppid_args *uap, int32_t *retval) { - *retval = p->p_ppid; - return (0); + return 0; } @@ -216,9 +211,8 @@ getppid(proc_t p, __unused struct getppid_args *uap, int32_t *retval) int getpgrp(proc_t p, __unused struct getpgrp_args *uap, int32_t *retval) { - *retval = p->p_pgrpid; - return (0); + return 0; } @@ -245,17 +239,20 @@ getpgid(proc_t p, struct getpgid_args *uap, int32_t *retval) int refheld = 0; pt = p; - if (uap->pid == 0) + if (uap->pid == 0) { goto found; + } - if ((pt = proc_find(uap->pid)) == 0) - return (ESRCH); + if ((pt = proc_find(uap->pid)) == 0) { + return ESRCH; + } refheld = 1; found: *retval = pt->p_pgrpid; - if (refheld != 0) + if (refheld != 0) { proc_rele(pt); - return (0); + } + return 0; } @@ -283,20 +280,23 @@ getsid(proc_t p, struct getsid_args *uap, int32_t *retval) struct session * sessp; pt = p; - if (uap->pid == 0) + if (uap->pid == 0) { goto found; + } - if ((pt = proc_find(uap->pid)) == 0) - return (ESRCH); + if ((pt = proc_find(uap->pid)) == 0) { + return ESRCH; + } refheld = 1; found: sessp = proc_session(pt); *retval = sessp->s_sid; session_rele(sessp); - if (refheld != 0) + if (refheld != 0) { proc_rele(pt); - return (0); + } + return 0; } @@ -312,9 +312,8 @@ found: int getuid(__unused proc_t p, __unused struct getuid_args *uap, int32_t *retval) { - - *retval = kauth_getruid(); - return (0); + *retval = kauth_getruid(); + return 0; } @@ -330,9 +329,8 @@ getuid(__unused proc_t p, __unused struct getuid_args *uap, int32_t *retval) int geteuid(__unused proc_t p, __unused struct geteuid_args *uap, int32_t *retval) { - - *retval = kauth_getuid(); - return (0); + *retval = kauth_getuid(); + return 0; } @@ -351,22 +349,25 @@ int gettid(__unused proc_t p, struct gettid_args *uap, int32_t *retval) { struct uthread *uthread = get_bsdthread_info(current_thread()); - int error; + int error; /* * If this thread is not running with an override identity, we can't * return one to the caller, so return an error instead. */ - if (!(uthread->uu_flag & UT_SETUID)) - return (ESRCH); + if (!(uthread->uu_flag & UT_SETUID)) { + return ESRCH; + } - if ((error = suword(uap->uidp, kauth_cred_getruid(uthread->uu_ucred)))) - return (error); - if ((error = suword(uap->gidp, kauth_cred_getrgid(uthread->uu_ucred)))) - return (error); + if ((error = suword(uap->uidp, kauth_cred_getruid(uthread->uu_ucred)))) { + return error; + } + if ((error = suword(uap->gidp, kauth_cred_getrgid(uthread->uu_ucred)))) { + return error; + } *retval = 0; - return (0); + return 0; } @@ -382,9 +383,8 @@ gettid(__unused proc_t p, struct gettid_args *uap, int32_t *retval) int getgid(__unused proc_t p, __unused struct getgid_args *uap, int32_t *retval) { - *retval = kauth_getrgid(); - return (0); + return 0; } @@ -406,9 +406,8 @@ getgid(__unused proc_t p, __unused struct getgid_args *uap, int32_t *retval) int getegid(__unused proc_t p, __unused struct getegid_args *uap, int32_t *retval) { - *retval = kauth_getgid(); - return (0); + return 0; } @@ -457,22 +456,22 @@ getgroups(__unused proc_t p, struct getgroups_args *uap, int32_t *retval) if ((ngrp = uap->gidsetsize) == 0) { *retval = pcred->cr_ngroups; kauth_cred_unref(&cred); - return (0); + return 0; } if (ngrp < pcred->cr_ngroups) { kauth_cred_unref(&cred); - return (EINVAL); + return EINVAL; } ngrp = pcred->cr_ngroups; if ((error = copyout((caddr_t)pcred->cr_groups, - uap->gidset, - ngrp * sizeof(gid_t)))) { + uap->gidset, + ngrp * sizeof(gid_t)))) { kauth_cred_unref(&cred); - return (error); + return error; } kauth_cred_unref(&cred); *retval = ngrp; - return (0); + return 0; } @@ -486,12 +485,12 @@ getgroups(__unused proc_t p, struct getgroups_args *uap, int32_t *retval) int getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused int32_t *retval) { - return(ENOTSUP); + return ENOTSUP; } /* * Return the per-thread/per-process whiteout groups list. - * + * * XXX implement getwgroups * */ @@ -499,9 +498,30 @@ getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused int int getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused int32_t *retval) { - return(ENOTSUP); + return ENOTSUP; } +/* + * setsid_internal + * + * Description: Core implementation of setsid(). + */ +int +setsid_internal(proc_t p) +{ + struct pgrp * pg = PGRP_NULL; + + if (p->p_pgrpid == p->p_pid || (pg = pgfind(p->p_pid)) || p->p_lflag & P_LINVFORK) { + if (pg != PGRP_NULL) { + pg_rele(pg); + } + return EPERM; + } else { + /* enter pgrp works with its own pgrp refcount */ + (void)enterpgrp(p, p->p_pid, 1); + return 0; + } +} /* * setsid @@ -530,18 +550,11 @@ getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused int int setsid(proc_t p, __unused struct setsid_args *uap, int32_t *retval) { - struct pgrp * pg = PGRP_NULL; - - if (p->p_pgrpid == p->p_pid || (pg = pgfind(p->p_pid)) || p->p_lflag & P_LINVFORK) { - if (pg != PGRP_NULL) - pg_rele(pg); - return (EPERM); - } else { - /* enter pgrp works with its own pgrp refcount */ - (void)enterpgrp(p, p->p_pid, 1); + int rc = setsid_internal(p); + if (rc == 0) { *retval = p->p_pid; - return (0); } + return rc; } @@ -578,15 +591,15 @@ setsid(proc_t p, __unused struct setsid_args *uap, int32_t *retval) * is used as the target process group ID. * * Legacy: This system call entry point is also used to implement the - * legacy library routine setpgrp(), which under POSIX + * legacy library routine setpgrp(), which under POSIX * * XXX: Belongs in kern_proc.c */ int -setpgid(proc_t curp, register struct setpgid_args *uap, __unused int32_t *retval) +setpgid(proc_t curp, struct setpgid_args *uap, __unused int32_t *retval) { - proc_t targp = PROC_NULL; /* target process */ - struct pgrp *pg = PGRP_NULL; /* target pgrp */ + proc_t targp = PROC_NULL; /* target process */ + struct pgrp *pg = PGRP_NULL; /* target pgrp */ int error = 0; int refheld = 0; int samesess = 0; @@ -597,8 +610,9 @@ setpgid(proc_t curp, register struct setpgid_args *uap, __unused int32_t *retval if (uap->pid != 0 && uap->pid != curp->p_pid) { if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) { - if (targp != PROC_NULL) + if (targp != PROC_NULL) { refheld = 1; + } error = ESRCH; goto out; } @@ -630,14 +644,14 @@ setpgid(proc_t curp, register struct setpgid_args *uap, __unused int32_t *retval error = EINVAL; goto out; } - if (uap->pgid == 0) + if (uap->pgid == 0) { uap->pgid = targp->p_pid; - else if (uap->pgid != targp->p_pid) { - if ((pg = pgfind(uap->pgid)) == 0){ + } else if (uap->pgid != targp->p_pid) { + if ((pg = pgfind(uap->pgid)) == 0) { error = EPERM; goto out; } - samesess = (pg->pg_session != curp_sessp); + samesess = (pg->pg_session != curp_sessp); pg_rele(pg); if (samesess != 0) { error = EPERM; @@ -646,13 +660,16 @@ setpgid(proc_t curp, register struct setpgid_args *uap, __unused int32_t *retval } error = enterpgrp(targp, uap->pgid, 0); out: - if (targp_sessp != SESSION_NULL) + if (targp_sessp != SESSION_NULL) { session_rele(targp_sessp); - if (curp_sessp != SESSION_NULL) + } + if (curp_sessp != SESSION_NULL) { session_rele(curp_sessp); - if (refheld != 0) + } + if (refheld != 0) { proc_rele(targp); - return(error); + } + return error; } @@ -672,6 +689,12 @@ out: * real, effective, or saved user or group IDs since beginning * execution. */ +int +proc_issetugid(proc_t p) +{ + return (p->p_flag & P_SUGID) ? 1 : 0; +} + int issetugid(proc_t p, __unused struct issetugid_args *uap, int32_t *retval) { @@ -684,8 +707,8 @@ issetugid(proc_t p, __unused struct issetugid_args *uap, int32_t *retval) * that libc *might* have put in their data segment. */ - *retval = (p->p_flag & P_SUGID) ? 1 : 0; - return (0); + *retval = proc_issetugid(p); + return 0; } @@ -720,42 +743,34 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; - uid = uap->uid; + /* get current credential and take a reference while we muck with it */ my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); DEBUG_CRED_ENTER("setuid (%d/%d): %p %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), my_cred, uap->uid); AUDIT_ARG(uid, uid); - if (uid != my_pcred->cr_ruid && /* allow setuid(getuid()) */ - uid != my_pcred->cr_svuid && /* allow setuid(saved uid) */ - (error = suser(my_cred, &p->p_acflag))) { - kauth_cred_unref(&my_cred); - return (error); - } - /* - * Everything's okay, do it. - */ + for (;;) { + if (uid != my_pcred->cr_ruid && /* allow setuid(getuid()) */ + uid != my_pcred->cr_svuid && /* allow setuid(saved uid) */ + (error = suser(my_cred, &p->p_acflag))) { + kauth_cred_unref(&my_cred); + return error; + } - /* - * If we are priviledged, then set the saved and real UID too; - * otherwise, just set the effective UID - */ - if (suser(my_cred, &p->p_acflag) == 0) { - svuid = uid; - ruid = uid; /* - * Transfer proc count to new user. - * chgproccnt uses list lock for protection + * If we are privileged, then set the saved and real UID too; + * otherwise, just set the effective UID */ - (void)chgproccnt(uid, 1); - (void)chgproccnt(my_pcred->cr_ruid, -1); - } - - /* get current credential and take a reference while we muck with it */ - for (;;) { + if (suser(my_cred, &p->p_acflag) == 0) { + svuid = uid; + ruid = uid; + } else { + svuid = KAUTH_UID_NONE; + ruid = KAUTH_UID_NONE; + } /* * Only set the gmuid if the current cred has not opt'ed out; * this normally only happens when calling setgroups() instead @@ -765,32 +780,54 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) * to something other than the default list for the user, as * in entering a group or leaving an exclusion group). */ - if (!(my_pcred->cr_flags & CRF_NOMEMBERD)) + if (!(my_pcred->cr_flags & CRF_NOMEMBERD)) { gmuid = uid; + } - /* + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ + */ my_new_cred = kauth_cred_setresuid(my_cred, ruid, uid, svuid, gmuid); if (my_cred != my_new_cred) { - DEBUG_CRED_CHANGE("setuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags); - proc_lock(p); + /* + * If we're changing the ruid from A to B, we might race with another thread that's setting ruid from B to A. + * The current locking mechanisms don't allow us to make the entire credential switch operation atomic, + * thus we may be able to change the process credentials from ruid A to B, but get preempted before incrementing the proc + * count of B. If a second thread sees the new process credentials and switches back to ruid A, that other thread + * may be able to decrement the proc count of B before we can increment it. This results in a panic. + * Incrementing the proc count of the target ruid, B, before setting the process credentials prevents this race. + */ + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + (void)chgproccnt(ruid, 1); + } + + proc_ucred_lock(p); /* * We need to protect for a race where another thread * also changed the credential after we took our * reference. If p_ucred has changed then we should * restart this again with the new cred. + * + * Note: the kauth_cred_setresuid has consumed a reference to my_cred, it p_ucred != my_cred, then my_cred must not be dereferenced! */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); + /* + * We didn't successfully switch to the new ruid, so decrement + * the procs/uid count that we incremented above. + */ + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + (void)chgproccnt(ruid, -1); + } kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); /* try again */ continue; } @@ -799,15 +836,22 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); - proc_unlock(p); + proc_ucred_unlock(p); + /* + * If we've updated the ruid, decrement the count of procs running + * under the previous ruid + */ + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + (void)chgproccnt(my_pcred->cr_ruid, -1); + } } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); - + set_security_token(p); - return (0); + return 0; } @@ -845,32 +889,26 @@ seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval) my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); - if (euid != my_pcred->cr_ruid && euid != my_pcred->cr_svuid && - (error = suser(my_cred, &p->p_acflag))) { - kauth_cred_unref(&my_cred); - return (error); - } - - /* - * Everything's okay, do it. Copy credentials so other references do - * not see our changes. get current credential and take a reference - * while we muck with it - */ for (;;) { - /* + if (euid != my_pcred->cr_ruid && euid != my_pcred->cr_svuid && + (error = suser(my_cred, &p->p_acflag))) { + kauth_cred_unref(&my_cred); + return error; + } + + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ + */ my_new_cred = kauth_cred_setresuid(my_cred, KAUTH_UID_NONE, euid, KAUTH_UID_NONE, my_pcred->cr_gmuid); - - if (my_cred != my_new_cred) { + if (my_cred != my_new_cred) { DEBUG_CRED_CHANGE("seteuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags); - proc_lock(p); + proc_ucred_lock(p); /* * We need to protect for a race where another thread * also changed the credential after we took our @@ -878,9 +916,10 @@ seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval) * should restart this again with the new cred. */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); /* try again */ continue; } @@ -888,7 +927,7 @@ seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval) /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); - proc_unlock(p); + proc_ucred_unlock(p); } break; } @@ -896,7 +935,7 @@ seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval) kauth_cred_unref(&my_cred); set_security_token(p); - return (0); + return 0; } @@ -943,62 +982,48 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) ruid = uap->ruid; euid = uap->euid; - if (ruid == (uid_t)-1) + if (ruid == (uid_t)-1) { ruid = KAUTH_UID_NONE; - if (euid == (uid_t)-1) + } + if (euid == (uid_t)-1) { euid = KAUTH_UID_NONE; + } AUDIT_ARG(euid, euid); AUDIT_ARG(ruid, ruid); my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); - if (((ruid != KAUTH_UID_NONE && /* allow no change of ruid */ - ruid != my_pcred->cr_ruid && /* allow ruid = ruid */ - ruid != my_pcred->cr_uid && /* allow ruid = euid */ - ruid != my_pcred->cr_svuid) || /* allow ruid = svuid */ - (euid != KAUTH_UID_NONE && /* allow no change of euid */ - euid != my_pcred->cr_uid && /* allow euid = euid */ - euid != my_pcred->cr_ruid && /* allow euid = ruid */ - euid != my_pcred->cr_svuid)) && /* allow euid = svui */ - (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ - kauth_cred_unref(&my_cred); - return (error); - } - - /* - * Everything's okay, do it. Copy credentials so other references do - * not see our changes. get current credential and take a reference - * while we muck with it - */ for (;;) { + if (((ruid != KAUTH_UID_NONE && /* allow no change of ruid */ + ruid != my_pcred->cr_ruid && /* allow ruid = ruid */ + ruid != my_pcred->cr_uid && /* allow ruid = euid */ + ruid != my_pcred->cr_svuid) || /* allow ruid = svuid */ + (euid != KAUTH_UID_NONE && /* allow no change of euid */ + euid != my_pcred->cr_uid && /* allow euid = euid */ + euid != my_pcred->cr_ruid && /* allow euid = ruid */ + euid != my_pcred->cr_svuid)) && /* allow euid = svuid */ + (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ + kauth_cred_unref(&my_cred); + return error; + } + uid_t new_euid; - uid_t new_ruid; uid_t svuid = KAUTH_UID_NONE; new_euid = my_pcred->cr_uid; - new_ruid = my_pcred->cr_ruid; - - /* + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ - if (euid == KAUTH_UID_NONE && my_pcred->cr_uid != euid) { + */ + if (euid != KAUTH_UID_NONE && my_pcred->cr_uid != euid) { /* changing the effective UID */ new_euid = euid; OSBitOrAtomic(P_SUGID, &p->p_flag); } - if (ruid != KAUTH_UID_NONE && my_pcred->cr_ruid != ruid) { - /* changing the real UID; must do user accounting */ - /* chgproccnt uses list lock for protection */ - (void)chgproccnt(ruid, 1); - (void)chgproccnt(my_pcred->cr_ruid, -1); - new_ruid = ruid; - OSBitOrAtomic(P_SUGID, &p->p_flag); - } /* * If the newly requested real uid or effective uid does * not match the saved uid, then set the saved uid to the @@ -1007,35 +1032,65 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) */ if (my_pcred->cr_svuid != uap->ruid && my_pcred->cr_svuid != uap->euid) { - svuid = new_euid; + svuid = new_euid; OSBitOrAtomic(P_SUGID, &p->p_flag); } my_new_cred = kauth_cred_setresuid(my_cred, ruid, euid, svuid, my_pcred->cr_gmuid); - - if (my_cred != my_new_cred) { + if (my_cred != my_new_cred) { DEBUG_CRED_CHANGE("setreuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags); - proc_lock(p); + /* + * If we're changing the ruid from A to B, we might race with another thread that's setting ruid from B to A. + * The current locking mechanisms don't allow us to make the entire credential switch operation atomic, + * thus we may be able to change the process credentials from ruid A to B, but get preempted before incrementing the proc + * count of B. If a second thread sees the new process credentials and switches back to ruid A, that other thread + * may be able to decrement the proc count of B before we can increment it. This results in a panic. + * Incrementing the proc count of the target ruid, B, before setting the process credentials prevents this race. + */ + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + (void)chgproccnt(ruid, 1); + } + + proc_ucred_lock(p); /* * We need to protect for a race where another thread * also changed the credential after we took our * reference. If p_ucred has changed then we should * restart this again with the new cred. + * + * Note: the kauth_cred_setresuid has consumed a reference to my_cred, it p_ucred != my_cred, then my_cred must not be dereferenced! */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + /* + * We didn't successfully switch to the new ruid, so decrement + * the procs/uid count that we incremented above. + */ + (void)chgproccnt(ruid, -1); + } kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); /* try again */ continue; } + p->p_ucred = my_new_cred; /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); - OSBitOrAtomic(P_SUGID, &p->p_flag); /* XXX redundant? */ - proc_unlock(p); + OSBitOrAtomic(P_SUGID, &p->p_flag); + proc_ucred_unlock(p); + + if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) { + /* + * We switched to a new ruid, so decrement the count of procs running + * under the previous ruid + */ + (void)chgproccnt(my_pcred->cr_ruid, -1); + } } break; } @@ -1043,7 +1098,7 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) kauth_cred_unref(&my_cred); set_security_token(p); - return (0); + return 0; } @@ -1087,41 +1142,42 @@ setgid(proc_t p, struct setgid_args *uap, __unused int32_t *retval) gid = uap->gid; AUDIT_ARG(gid, gid); + /* get current credential and take a reference while we muck with it */ my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); - if (gid != my_pcred->cr_rgid && /* allow setgid(getgid()) */ - gid != my_pcred->cr_svgid && /* allow setgid(saved gid) */ - (error = suser(my_cred, &p->p_acflag))) { - kauth_cred_unref(&my_cred); - return (error); - } + for (;;) { + if (gid != my_pcred->cr_rgid && /* allow setgid(getgid()) */ + gid != my_pcred->cr_svgid && /* allow setgid(saved gid) */ + (error = suser(my_cred, &p->p_acflag))) { + kauth_cred_unref(&my_cred); + return error; + } - /* - * If we are priviledged, then set the saved and real GID too; - * otherwise, just set the effective GID - */ - if (suser(my_cred, &p->p_acflag) == 0) { - svgid = gid; - rgid = gid; - } + /* + * If we are privileged, then set the saved and real GID too; + * otherwise, just set the effective GID + */ + if (suser(my_cred, &p->p_acflag) == 0) { + svgid = gid; + rgid = gid; + } else { + svgid = KAUTH_GID_NONE; + rgid = KAUTH_GID_NONE; + } - /* get current credential and take a reference while we muck with it */ - for (;;) { - - /* + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ + */ my_new_cred = kauth_cred_setresgid(my_cred, rgid, gid, svgid); if (my_cred != my_new_cred) { - DEBUG_CRED_CHANGE("setgid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); - proc_lock(p); + proc_ucred_lock(p); /* * We need to protect for a race where another thread * also changed the credential after we took our @@ -1129,25 +1185,26 @@ setgid(proc_t p, struct setgid_args *uap, __unused int32_t *retval) * should restart this again with the new cred. */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); continue; } p->p_ucred = my_new_cred; /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); - proc_unlock(p); + proc_ucred_unlock(p); } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); - + set_security_token(p); - return (0); + return 0; } @@ -1187,31 +1244,30 @@ setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval) egid = uap->egid; AUDIT_ARG(egid, egid); + /* get current credential and take a reference while we muck with it */ my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); - if (egid != my_pcred->cr_rgid && - egid != my_pcred->cr_svgid && - (error = suser(my_cred, &p->p_acflag))) { - kauth_cred_unref(&my_cred); - return (error); - } - /* get current credential and take a reference while we muck with it */ for (;;) { - /* + if (egid != my_pcred->cr_rgid && + egid != my_pcred->cr_svgid && + (error = suser(my_cred, &p->p_acflag))) { + kauth_cred_unref(&my_cred); + return error; + } + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ + */ my_new_cred = kauth_cred_setresgid(my_cred, KAUTH_GID_NONE, egid, KAUTH_GID_NONE); if (my_cred != my_new_cred) { - DEBUG_CRED_CHANGE("setegid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags); - proc_lock(p); + proc_ucred_lock(p); /* * We need to protect for a race where another thread * also changed the credential after we took our @@ -1219,17 +1275,18 @@ setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval) * should restart this again with the new cred. */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); continue; } p->p_ucred = my_new_cred; /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); - proc_unlock(p); + proc_ucred_unlock(p); } break; } @@ -1238,7 +1295,7 @@ setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval) kauth_cred_unref(&my_cred); set_security_token(p); - return (0); + return 0; } /* @@ -1291,45 +1348,47 @@ setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval) rgid = uap->rgid; egid = uap->egid; - if (rgid == (uid_t)-1) + if (rgid == (uid_t)-1) { rgid = KAUTH_GID_NONE; - if (egid == (uid_t)-1) + } + if (egid == (uid_t)-1) { egid = KAUTH_GID_NONE; + } AUDIT_ARG(egid, egid); AUDIT_ARG(rgid, rgid); + /* get current credential and take a reference while we muck with it */ my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); - if (((rgid != KAUTH_UID_NONE && /* allow no change of rgid */ - rgid != my_pcred->cr_rgid && /* allow rgid = rgid */ - rgid != my_pcred->cr_gid && /* allow rgid = egid */ - rgid != my_pcred->cr_svgid) || /* allow rgid = svgid */ - (egid != KAUTH_UID_NONE && /* allow no change of egid */ - egid != my_pcred->cr_groups[0] && /* allow no change of egid */ - egid != my_pcred->cr_gid && /* allow egid = egid */ - egid != my_pcred->cr_rgid && /* allow egid = rgid */ - egid != my_pcred->cr_svgid)) && /* allow egid = svgid */ - (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ - kauth_cred_unref(&my_cred); - return (error); - } - - /* get current credential and take a reference while we muck with it */ for (;;) { + if (((rgid != KAUTH_UID_NONE && /* allow no change of rgid */ + rgid != my_pcred->cr_rgid && /* allow rgid = rgid */ + rgid != my_pcred->cr_gid && /* allow rgid = egid */ + rgid != my_pcred->cr_svgid) || /* allow rgid = svgid */ + (egid != KAUTH_UID_NONE && /* allow no change of egid */ + egid != my_pcred->cr_groups[0] && /* allow no change of egid */ + egid != my_pcred->cr_gid && /* allow egid = egid */ + egid != my_pcred->cr_rgid && /* allow egid = rgid */ + egid != my_pcred->cr_svgid)) && /* allow egid = svgid */ + (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ + kauth_cred_unref(&my_cred); + return error; + } + uid_t new_egid = my_pcred->cr_gid; uid_t new_rgid = my_pcred->cr_rgid; uid_t svgid = KAUTH_UID_NONE; - - /* + + /* * Set the credential with new info. If there is no change, * we get back the same credential we passed in; if there is * a change, we drop the reference on the credential we * passed in. The subsequent compare is safe, because it is * a pointer compare rather than a contents compare. - */ - if (egid == KAUTH_UID_NONE && my_pcred->cr_gid != egid) { + */ + if (egid != KAUTH_UID_NONE && my_pcred->cr_gid != egid) { /* changing the effective GID */ new_egid = egid; OSBitOrAtomic(P_SUGID, &p->p_flag); @@ -1347,33 +1406,33 @@ setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval) */ if (my_pcred->cr_svgid != uap->rgid && my_pcred->cr_svgid != uap->egid) { - svgid = new_egid; + svgid = new_egid; OSBitOrAtomic(P_SUGID, &p->p_flag); } my_new_cred = kauth_cred_setresgid(my_cred, rgid, egid, svgid); if (my_cred != my_new_cred) { - DEBUG_CRED_CHANGE("setregid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags); - proc_lock(p); + proc_ucred_lock(p); /* need to protect for a race where another thread * also changed the credential after we took our * reference. If p_ucred has changed then we * should restart this again with the new cred. */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); + my_pcred = posix_cred_get(my_cred); continue; } p->p_ucred = my_new_cred; /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); /* XXX redundant? */ - proc_unlock(p); + proc_ucred_unlock(p); } break; } @@ -1381,13 +1440,13 @@ setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval) kauth_cred_unref(&my_cred); set_security_token(p); - return (0); + return 0; } /* * Set the per-thread override identity. The first parameter can be the - * current real UID, KAUTH_UID_NONE, or, if the caller is priviledged, it + * current real UID, KAUTH_UID_NONE, or, if the caller is privileged, it * can be any UID. If it is KAUTH_UID_NONE, then as a special case, this * means "revert to the per process credential"; otherwise, if permitted, * it changes the effective, real, and saved UIDs and GIDs for the current @@ -1406,14 +1465,15 @@ settid(proc_t p, struct settid_args *uap, __unused int32_t *retval) AUDIT_ARG(uid, uid); AUDIT_ARG(gid, gid); - if (proc_suser(p) != 0) - return (EPERM); - - if (uid == KAUTH_UID_NONE) { + if (proc_suser(p) != 0) { + return EPERM; + } + if (uid == KAUTH_UID_NONE) { /* must already be assuming another identity in order to revert back */ - if ((uthread->uu_flag & UT_SETUID) == 0) - return (EPERM); + if ((uthread->uu_flag & UT_SETUID) == 0) { + return EPERM; + } /* revert to delayed binding of process credential */ uc = kauth_cred_proc_ref(p); @@ -1425,7 +1485,7 @@ settid(proc_t p, struct settid_args *uap, __unused int32_t *retval) /* cannot already be assuming another identity */ if ((uthread->uu_flag & UT_SETUID) != 0) { - return (EPERM); + return EPERM; } /* @@ -1435,11 +1495,12 @@ settid(proc_t p, struct settid_args *uap, __unused int32_t *retval) * current credential while we muck with it, so we can do * the post-compare for changes by pointer. */ - kauth_cred_ref(uthread->uu_ucred); + kauth_cred_ref(uthread->uu_ucred); my_cred = uthread->uu_ucred; my_new_cred = kauth_cred_setuidgid(my_cred, uid, gid); - if (my_cred != my_new_cred) + if (my_cred != my_new_cred) { uthread->uu_ucred = my_new_cred; + } uthread->uu_flag |= UT_SETUID; /* Drop old uthread reference or our extra reference */ @@ -1451,7 +1512,7 @@ settid(proc_t p, struct settid_args *uap, __unused int32_t *retval) * XXX it is unclear whether P_SUGID should be st at this point; * XXX in theory, it is being deprecated. */ - return (0); + return 0; } @@ -1477,7 +1538,7 @@ settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *re AUDIT_ARG(value32, uap->assume); if (proc_suser(p) != 0) { - return (EPERM); + return EPERM; } /* @@ -1493,17 +1554,19 @@ settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *re */ if (uap->assume != 0) { /* can't do this if we have already assumed an identity */ - if ((uthread->uu_flag & UT_SETUID) != 0) - return (EPERM); - + if ((uthread->uu_flag & UT_SETUID) != 0) { + return EPERM; + } + target_proc = proc_find(uap->pid); /* can't assume the identity of the kernel process */ if (target_proc == NULL || target_proc == kernproc) { - if (target_proc!= NULL) + if (target_proc != NULL) { proc_rele(target_proc); - return (ESRCH); + } + return ESRCH; } - + /* * Take a reference on the credential used in our target * process then use it as the identity for our current @@ -1516,39 +1579,41 @@ settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *re * credential following our assumption of a per-thread one, * since the credential cache will maintain a unique instance. */ - kauth_cred_ref(uthread->uu_ucred); + kauth_cred_ref(uthread->uu_ucred); my_cred = uthread->uu_ucred; my_target_cred = kauth_cred_proc_ref(target_proc); my_target_pcred = posix_cred_get(my_target_cred); my_new_cred = kauth_cred_setuidgid(my_cred, my_target_pcred->cr_uid, my_target_pcred->cr_gid); - if (my_cred != my_new_cred) + if (my_cred != my_new_cred) { uthread->uu_ucred = my_new_cred; - + } + uthread->uu_flag |= UT_SETUID; - + /* Drop old uthread reference or our extra reference */ proc_rele(target_proc); kauth_cred_unref(&my_cred); kauth_cred_unref(&my_target_cred); - return (0); + return 0; } - + /* * Otherwise, we are reverting back to normal mode of operation where * delayed binding of the process credential sets the credential in * the thread (uu_ucred) */ - if ((uthread->uu_flag & UT_SETUID) == 0) - return (EPERM); + if ((uthread->uu_flag & UT_SETUID) == 0) { + return EPERM; + } /* revert to delayed binding of process credential */ my_new_cred = kauth_cred_proc_ref(p); kauth_cred_unref(&uthread->uu_ucred); uthread->uu_ucred = my_new_cred; uthread->uu_flag &= ~UT_SETUID; - - return (0); + + return 0; } @@ -1588,40 +1653,50 @@ settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *re * flag the process as having set privilege since the last exec. */ static int -setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused int32_t *retval) +setgroups1(proc_t p, u_int ngrp, user_addr_t gidset, uid_t gmuid, __unused int32_t *retval) { - u_int ngrp; - gid_t newgroups[NGROUPS] = { 0 }; - int error; - kauth_cred_t my_cred, my_new_cred; - struct uthread *uthread = get_bsdthread_info(current_thread()); + gid_t newgroups[NGROUPS] = { 0 }; + int error; - DEBUG_CRED_ENTER("setgroups1 (%d/%d): %d 0x%016x %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), gidsetsize, gidset, gmuid); + DEBUG_CRED_ENTER("setgroups1 (%d/%d): %d 0x%016x %d\n", p->p_pid, + (p->p_pptr ? p->p_pptr->p_pid : 0), ngrp, gidset, gmuid); - ngrp = gidsetsize; - if (ngrp > NGROUPS) - return (EINVAL); + if (ngrp > NGROUPS) { + return EINVAL; + } - if ( ngrp < 1 ) { - ngrp = 1; - } else { + if (ngrp >= 1) { error = copyin(gidset, - (caddr_t)newgroups, ngrp * sizeof(gid_t)); + (caddr_t)newgroups, ngrp * sizeof(gid_t)); if (error) { - return (error); + return error; } } + return setgroups_internal(p, ngrp, newgroups, gmuid); +} + +int +setgroups_internal(proc_t p, u_int ngrp, gid_t *newgroups, uid_t gmuid) +{ + struct uthread *uthread = get_bsdthread_info(current_thread()); + kauth_cred_t my_cred, my_new_cred; + int error; my_cred = kauth_cred_proc_ref(p); if ((error = suser(my_cred, &p->p_acflag))) { kauth_cred_unref(&my_cred); - return (error); + return error; + } + + if (ngrp < 1) { + ngrp = 1; + newgroups[0] = 0; } if ((uthread->uu_flag & UT_SETUID) != 0) { #if DEBUG_CRED int my_cred_flags = uthread->uu_ucred->cr_flags; -#endif /* DEBUG_CRED */ +#endif /* DEBUG_CRED */ kauth_cred_unref(&my_cred); /* @@ -1637,17 +1712,16 @@ setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused uthread->uu_ucred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid); #if DEBUG_CRED if (my_cred != uthread->uu_ucred) { - DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred_flags, uthread->uu_ucred , uthread->uu_ucred ->cr_flags); + DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred_flags, uthread->uu_ucred, uthread->uu_ucred->cr_flags); } -#endif /* DEBUG_CRED */ +#endif /* DEBUG_CRED */ } else { - /* * get current credential and take a reference while we muck * with it */ for (;;) { - /* + /* * Set the credential with new info. If there is no * change, we get back the same credential we passed * in; if there is a change, we drop the reference on @@ -1657,19 +1731,18 @@ setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused */ my_new_cred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid); if (my_cred != my_new_cred) { - DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); - proc_lock(p); + proc_ucred_lock(p); /* * We need to protect for a race where another * thread also changed the credential after we - * took our reference. If p_ucred has + * took our reference. If p_ucred has * changed then we should restart this again * with the new cred. */ if (p->p_ucred != my_cred) { - proc_unlock(p); + proc_ucred_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); /* try again */ @@ -1679,7 +1752,7 @@ setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused /* update cred on proc */ PROC_UPDATE_CREDS_ONPROC(p); OSBitOrAtomic(P_SUGID, &p->p_flag); - proc_unlock(p); + proc_ucred_unlock(p); } break; } @@ -1691,7 +1764,7 @@ setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused set_security_token(p); } - return (0); + return 0; } @@ -1729,7 +1802,7 @@ initgroups(proc_t p, struct initgroups_args *uap, __unused int32_t *retval) { DEBUG_CRED_ENTER("initgroups\n"); - return(setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval)); + return setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval); } @@ -1763,13 +1836,13 @@ setgroups(proc_t p, struct setgroups_args *uap, __unused int32_t *retval) { DEBUG_CRED_ENTER("setgroups\n"); - return(setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval)); + return setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval); } /* * Set the per-thread/per-process supplementary groups list. - * + * * XXX implement setsgroups * */ @@ -1777,12 +1850,12 @@ setgroups(proc_t p, struct setgroups_args *uap, __unused int32_t *retval) int setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused int32_t *retval) { - return(ENOTSUP); + return ENOTSUP; } /* * Set the per-thread/per-process whiteout groups list. - * + * * XXX implement setwgroups * */ @@ -1790,7 +1863,7 @@ setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused int int setwgroups(__unused proc_t p, __unused struct setwgroups_args *uap, __unused int32_t *retval) { - return(ENOTSUP); + return ENOTSUP; } @@ -1805,9 +1878,10 @@ groupmember(gid_t gid, kauth_cred_t cred) { int is_member; - if (kauth_cred_ismember_gid(cred, gid, &is_member) == 0 && is_member) - return (1); - return (0); + if (kauth_cred_ismember_gid(cred, gid, &is_member) == 0 && is_member) { + return 1; + } + return 0; } @@ -1829,15 +1903,17 @@ int suser(kauth_cred_t cred, u_short *acflag) { #if DIAGNOSTIC - if (!IS_VALID_CRED(cred)) + if (!IS_VALID_CRED(cred)) { panic("suser"); + } #endif if (kauth_cred_getuid(cred) == 0) { - if (acflag) + if (acflag) { *acflag |= ASU; - return (0); + } + return 0; } - return (EPERM); + return EPERM; } @@ -1867,26 +1943,39 @@ suser(kauth_cred_t cred, u_short *acflag) int getlogin(proc_t p, struct getlogin_args *uap, __unused int32_t *retval) { - char buffer[MAXLOGNAME+1]; + char buffer[MAXLOGNAME + 1]; struct session * sessp; - bzero(buffer, MAXLOGNAME+1); + bzero(buffer, MAXLOGNAME + 1); sessp = proc_session(p); - if (uap->namelen > MAXLOGNAME) + if (uap->namelen > MAXLOGNAME) { uap->namelen = MAXLOGNAME; + } - if(sessp != SESSION_NULL) { + if (sessp != SESSION_NULL) { session_lock(sessp); bcopy( sessp->s_login, buffer, uap->namelen); session_unlock(sessp); } session_rele(sessp); - return (copyout((caddr_t)buffer, uap->namebuf, uap->namelen)); + return copyout((caddr_t)buffer, uap->namebuf, uap->namelen); } +void +setlogin_internal(proc_t p, const char login[static MAXLOGNAME]) +{ + struct session *sessp = proc_session(p); + + if (sessp != SESSION_NULL) { + session_lock(sessp); + bcopy(login, sessp->s_login, MAXLOGNAME); + session_unlock(sessp); + session_rele(sessp); + } +} /* * setlogin @@ -1908,35 +1997,28 @@ int setlogin(proc_t p, struct setlogin_args *uap, __unused int32_t *retval) { int error; - size_t dummy=0; - char buffer[MAXLOGNAME+1]; - struct session * sessp; + size_t dummy = 0; + char buffer[MAXLOGNAME + 1]; - if ((error = proc_suser(p))) - return (error); + if ((error = proc_suser(p))) { + return error; + } - bzero(&buffer[0], MAXLOGNAME+1); + bzero(&buffer[0], MAXLOGNAME + 1); error = copyinstr(uap->namebuf, (caddr_t) &buffer[0], MAXLOGNAME - 1, (size_t *)&dummy); - sessp = proc_session(p); - - if (sessp != SESSION_NULL) { - session_lock(sessp); - bcopy(buffer, sessp->s_login, MAXLOGNAME); - session_unlock(sessp); - session_rele(sessp); - } - + setlogin_internal(p, buffer); if (!error) { AUDIT_ARG(text, buffer); - } else if (error == ENAMETOOLONG) + } else if (error == ENAMETOOLONG) { error = EINVAL; - return (error); + } + return error; } @@ -1948,11 +2030,47 @@ setlogin(proc_t p, struct setlogin_args *uap, __unused int32_t *retval) int set_security_token(proc_t p) { + return set_security_token_task_internal(p, p->task); +} + +static void +proc_calc_audit_token(proc_t p, kauth_cred_t my_cred, audit_token_t *audit_token) +{ + posix_cred_t my_pcred = posix_cred_get(my_cred); + + /* + * The current layout of the Mach audit token explicitly + * adds these fields. But nobody should rely on such + * a literal representation. Instead, the BSM library + * provides a function to convert an audit token into + * a BSM subject. Use of that mechanism will isolate + * the user of the trailer from future representation + * changes. + */ + audit_token->val[0] = my_cred->cr_audit.as_aia_p->ai_auid; + audit_token->val[1] = my_pcred->cr_uid; + audit_token->val[2] = my_pcred->cr_gid; + audit_token->val[3] = my_pcred->cr_ruid; + audit_token->val[4] = my_pcred->cr_rgid; + audit_token->val[5] = p->p_pid; + audit_token->val[6] = my_cred->cr_audit.as_aia_p->ai_asid; + audit_token->val[7] = p->p_idversion; +} + +/* + * Set the secrity token of the task with current euid and eguid + * The function takes a proc and a task, where proc->task might point to a + * different task if called from exec. + */ + +int +set_security_token_task_internal(proc_t p, void *t) +{ + kauth_cred_t my_cred; security_token_t sec_token; audit_token_t audit_token; - kauth_cred_t my_cred; - posix_cred_t my_pcred; host_priv_t host_priv; + task_t task = t; /* * Don't allow a vfork child to override the parent's token settings @@ -1960,15 +2078,17 @@ set_security_token(proc_t p) * suffer along using the parent's token until the exec(). It's all * undefined behavior anyway, right? */ - if (p->task == current_task()) { - uthread_t uthread; + if (task == current_task()) { + uthread_t uthread; uthread = (uthread_t)get_bsdthread_info(current_thread()); - if (uthread->uu_flag & UT_VFORK) - return (1); + if (uthread->uu_flag & UT_VFORK) { + return 1; + } } - + my_cred = kauth_cred_proc_ref(p); - my_pcred = posix_cred_get(my_cred); + + proc_calc_audit_token(p, my_cred, &audit_token); /* XXX mach_init doesn't have a p_ucred when it calls this function */ if (IS_VALID_CRED(my_cred)) { @@ -1979,222 +2099,70 @@ set_security_token(proc_t p) sec_token.val[1] = 0; } - /* - * The current layout of the Mach audit token explicitly - * adds these fields. But nobody should rely on such - * a literal representation. Instead, the BSM library - * provides a function to convert an audit token into - * a BSM subject. Use of that mechanism will isolate - * the user of the trailer from future representation - * changes. - */ - audit_token.val[0] = my_cred->cr_audit.as_aia_p->ai_auid; - audit_token.val[1] = my_pcred->cr_uid; - audit_token.val[2] = my_pcred->cr_gid; - audit_token.val[3] = my_pcred->cr_ruid; - audit_token.val[4] = my_pcred->cr_rgid; - audit_token.val[5] = p->p_pid; - audit_token.val[6] = my_cred->cr_audit.as_aia_p->ai_asid; - audit_token.val[7] = p->p_idversion; - -#if CONFIG_MACF_MACH - mac_task_label_update_cred(my_cred, p->task); -#endif - host_priv = (sec_token.val[0]) ? HOST_PRIV_NULL : host_priv_self(); #if CONFIG_MACF - if (host_priv != HOST_PRIV_NULL && mac_system_check_host_priv(my_cred)) + if (host_priv != HOST_PRIV_NULL && mac_system_check_host_priv(my_cred)) { host_priv = HOST_PRIV_NULL; + } #endif kauth_cred_unref(&my_cred); - return (host_security_set_task_token(host_security_self(), - p->task, - sec_token, - audit_token, - host_priv) != KERN_SUCCESS); -} - - -/* - * Fill in a struct xucred based on a kauth_cred_t. - */ -__private_extern__ -void -cru2x(kauth_cred_t cr, struct xucred *xcr) -{ - posix_cred_t pcr = posix_cred_get(cr); +#if DEVELOPMENT || DEBUG + /* + * Update the pid an proc name for importance base if any + */ + task_importance_update_owner_info(task); +#endif - bzero(xcr, sizeof(*xcr)); - xcr->cr_version = XUCRED_VERSION; - xcr->cr_uid = kauth_cred_getuid(cr); - xcr->cr_ngroups = pcr->cr_ngroups; - bcopy(pcr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups)); + return host_security_set_task_token(host_security_self(), + task, + sec_token, + audit_token, + host_priv) != KERN_SUCCESS; } -#if CONFIG_LCTX - -/* - * Set Login Context ID - */ -/* - * MPSAFE - assignment of (visible) process to context protected by ALLLCTX_LOCK, - * LCTX by its own locks. - */ -int -setlcid(proc_t p0, struct setlcid_args *uap, __unused int32_t *retval) +void +proc_parent_audit_token(proc_t p, audit_token_t *token_out) { - proc_t p; - struct lctx *l; - int error = 0; - int refheld = 0; - - AUDIT_ARG(pid, uap->pid); - AUDIT_ARG(value32, uap->lcid); - if (uap->pid == LCID_PROC_SELF) { /* Create/Join/Leave */ - p = p0; - } else { /* Adopt/Orphan */ - p = proc_find(uap->pid); - if (p == NULL) - return (ESRCH); - refheld = 1; - } - -#if CONFIG_MACF - error = mac_proc_check_setlcid(p0, p, uap->pid, uap->lcid); - if (error) - goto out; -#endif - - switch (uap->lcid) { - /* Leave/Orphan */ - case LCID_REMOVE: - - /* Only root may Leave/Orphan. */ - if (!kauth_cred_issuser(kauth_cred_get())) { - error = EPERM; - goto out; - } - - /* Process not in login context. */ - if (p->p_lctx == NULL) { - error = ENOATTR; - goto out; - } - - l = NULL; - - break; - - /* Create */ - case LCID_CREATE: - - /* Create only valid for self! */ - if (uap->pid != LCID_PROC_SELF) { - error = EPERM; - goto out; - } - - /* Already in a login context. */ - if (p->p_lctx != NULL) { - error = EPERM; - goto out; - } - - l = lccreate(); - if (l == NULL) { - error = ENOMEM; - goto out; - } - - LCTX_LOCK(l); - - break; + proc_t parent; + kauth_cred_t my_cred; - /* Join/Adopt */ - default: + proc_list_lock(); - /* Only root may Join/Adopt. */ - if (!kauth_cred_issuser(kauth_cred_get())) { - error = EPERM; - goto out; - } + parent = p->p_pptr; + my_cred = kauth_cred_proc_ref(parent); + proc_calc_audit_token(parent, my_cred, token_out); + kauth_cred_unref(&my_cred); - l = lcfind(uap->lcid); - if (l == NULL) { - error = ENOATTR; - goto out; - } + proc_list_unlock(); +} - break; - } - ALLLCTX_LOCK; - leavelctx(p); - enterlctx(p, l, (uap->lcid == LCID_CREATE) ? 1 : 0); - ALLLCTX_UNLOCK; +int get_audit_token_pid(audit_token_t *audit_token); -out: - if (refheld != 0) - proc_rele(p); - return (error); -} - -/* - * Get Login Context ID - */ -/* - * MPSAFE - membership of (visible) process in a login context - * protected by the all-context lock. - */ int -getlcid(proc_t p0, struct getlcid_args *uap, int32_t *retval) +get_audit_token_pid(audit_token_t *audit_token) { - proc_t p; - int error = 0; - int refheld = 0; - - AUDIT_ARG(pid, uap->pid); - if (uap->pid == LCID_PROC_SELF) { - p = p0; - } else { - p = proc_find(uap->pid); - if (p == NULL) - return (ESRCH); - refheld = 1; + /* keep in-sync with set_security_token (above) */ + if (audit_token) { + return (int)audit_token->val[5]; } - -#if CONFIG_MACF - error = mac_proc_check_getlcid(p0, p, uap->pid); - if (error) - goto out; -#endif - ALLLCTX_LOCK; - if (p->p_lctx == NULL) { - error = ENOATTR; - ALLLCTX_UNLOCK; - goto out; - } - *retval = p->p_lctx->lc_id; - ALLLCTX_UNLOCK; - out: - if (refheld != 0) - proc_rele(p); - - return (error); + return -1; } -#else /* LCTX */ -int -setlcid(proc_t p0, struct setlcid_args *uap, int32_t *retval) -{ - return (ENOSYS); -} -int -getlcid(proc_t p0, struct getlcid_args *uap, int32_t *retval) +/* + * Fill in a struct xucred based on a kauth_cred_t. + */ +__private_extern__ +void +cru2x(kauth_cred_t cr, struct xucred *xcr) { + posix_cred_t pcr = posix_cred_get(cr); - return (ENOSYS); + bzero(xcr, sizeof(*xcr)); + xcr->cr_version = XUCRED_VERSION; + xcr->cr_uid = kauth_cred_getuid(cr); + xcr->cr_ngroups = pcr->cr_ngroups; + bcopy(pcr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups)); } -#endif /* !LCTX */