/* * 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 * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * 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, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * 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. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * 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 * is included in support of clause 2.2 (b) of the Apple Public License, * Version 2.0. * * * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce * support for mandatory and extensible security protections. This notice * is included in support of clause 2.2 (b) of the Apple Public License, * Version 2.0. * */ /* * System calls related to processes and protection */ #include #include #include #include #include #include #include #include #include #include #include #if CONFIG_LCTX #include #endif #if CONFIG_MACF #include #if CONFIG_MACF_MACH #include #endif #endif #include #include #include #include #include #include /* for current_task() */ #include /* * Credential debugging; we can track entry into a function that might * change a credential, and we can track actual credential changes that * result. * * Note: Does *NOT* currently include per-thread credential changes * * We don't use kauth_cred_print() in current debugging, but it * can be used if needed when debugging is active. */ #if DEBUG_CRED #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 */ /* * setprivexec * * Description: (dis)allow this process to hold task, thread, or execption * ports of processes about to exec. * * Parameters: uap->flag New value for flag * * Returns: int Previous value of flag * * XXX: Belongs in kern_proc.c */ int 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); } /* * getpid * * Description: get the process ID * * Parameters: (void) * * Returns: pid_t Current process ID * * XXX: Belongs in kern_proc.c */ int getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval) { *retval = p->p_pid; return (0); } /* * getppid * * Description: get the parent process ID * * Parameters: (void) * * Returns: pid_t Parent process ID * * XXX: Belongs in kern_proc.c */ int getppid(proc_t p, __unused struct getppid_args *uap, int32_t *retval) { *retval = p->p_ppid; return (0); } /* * getpgrp * * Description: get the process group ID of the calling process * * Parameters: (void) * * Returns: pid_t Process group ID * * XXX: Belongs in kern_proc.c */ int getpgrp(proc_t p, __unused struct getpgrp_args *uap, int32_t *retval) { *retval = p->p_pgrpid; return (0); } /* * getpgid * * Description: Get an arbitary pid's process group id * * Parameters: uap->pid The target pid * * Returns: 0 Success * ESRCH No such process * * Notes: We are permitted to return EPERM in the case that the target * process is not in the same session as the calling process, * which could be a security consideration * * XXX: Belongs in kern_proc.c */ int getpgid(proc_t p, struct getpgid_args *uap, int32_t *retval) { proc_t pt; int refheld = 0; pt = p; if (uap->pid == 0) goto found; if ((pt = proc_find(uap->pid)) == 0) return (ESRCH); refheld = 1; found: *retval = pt->p_pgrpid; if (refheld != 0) proc_rele(pt); return (0); } /* * getsid * * Description: Get an arbitary pid's session leaders process group ID * * Parameters: uap->pid The target pid * * Returns: 0 Success * ESRCH No such process * * Notes: We are permitted to return EPERM in the case that the target * process is not in the same session as the calling process, * which could be a security consideration * * XXX: Belongs in kern_proc.c */ int getsid(proc_t p, struct getsid_args *uap, int32_t *retval) { proc_t pt; int refheld = 0; struct session * sessp; pt = p; if (uap->pid == 0) goto found; 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) proc_rele(pt); return (0); } /* * getuid * * Description: get real user ID for caller * * Parameters: (void) * * Returns: uid_t The real uid of the caller */ int getuid(__unused proc_t p, __unused struct getuid_args *uap, int32_t *retval) { *retval = kauth_getruid(); return (0); } /* * geteuid * * Description: get effective user ID for caller * * Parameters: (void) * * Returns: uid_t The effective uid of the caller */ int geteuid(__unused proc_t p, __unused struct geteuid_args *uap, int32_t *retval) { *retval = kauth_getuid(); return (0); } /* * gettid * * Description: Return the per-thread override identity. * * Parameters: uap->uidp Address of uid_t to get uid * uap->gidp Address of gid_t to get gid * * Returns: 0 Success * ESRCH No per thread identity active */ int gettid(__unused proc_t p, struct gettid_args *uap, int32_t *retval) { struct uthread *uthread = get_bsdthread_info(current_thread()); 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 ((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); } /* * getgid * * Description: get the real group ID for the calling process * * Parameters: (void) * * Returns: gid_t The real gid of the caller */ int getgid(__unused proc_t p, __unused struct getgid_args *uap, int32_t *retval) { *retval = kauth_getrgid(); return (0); } /* * getegid * * Description: get the effective group ID for the calling process * * Parameters: (void) * * Returns: gid_t The effective gid of the caller * * Notes: As an implementation detail, the effective gid is stored as * the first element of the supplementary group list. * * This could be implemented in Libc instead because of the above * detail. */ int getegid(__unused proc_t p, __unused struct getegid_args *uap, int32_t *retval) { *retval = kauth_getgid(); return (0); } /* * getgroups * * Description: get the list of supplementary groups for the calling process * * Parameters: uap->gidsetsize # of gid_t's in user buffer * uap->gidset Pointer to user buffer * * Returns: 0 Success * EINVAL User buffer too small * copyout:EFAULT User buffer invalid * * Retval: -1 Error * !0 # of groups * * Notes: The caller may specify a 0 value for gidsetsize, and we will * then return how large a buffer is required (in gid_t's) to * contain the answer at the time of the call. Otherwise, we * return the number of gid_t's catually copied to user space. * * When called with a 0 gidsetsize from a multithreaded program, * there is no guarantee that another thread may not change the * number of supplementary groups, and therefore a subsequent * call could still fail, unless the maximum possible buffer * size is supplied by the user. * * As an implementation detail, the effective gid is stored as * the first element of the supplementary group list, and will * be returned by this call. */ int getgroups(__unused proc_t p, struct getgroups_args *uap, int32_t *retval) { int ngrp; int error; kauth_cred_t cred; posix_cred_t pcred; /* grab reference while we muck around with the credential */ cred = kauth_cred_get_with_ref(); pcred = posix_cred_get(cred); if ((ngrp = uap->gidsetsize) == 0) { *retval = pcred->cr_ngroups; kauth_cred_unref(&cred); return (0); } if (ngrp < pcred->cr_ngroups) { kauth_cred_unref(&cred); return (EINVAL); } ngrp = pcred->cr_ngroups; if ((error = copyout((caddr_t)pcred->cr_groups, uap->gidset, ngrp * sizeof(gid_t)))) { kauth_cred_unref(&cred); return (error); } kauth_cred_unref(&cred); *retval = ngrp; return (0); } /* * Return the per-thread/per-process supplementary groups list. * * XXX implement getsgroups * */ int getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused int32_t *retval) { return(ENOTSUP); } /* * Return the per-thread/per-process whiteout groups list. * * XXX implement getwgroups * */ int getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused int32_t *retval) { return(ENOTSUP); } /* * setsid * * Description: Create a new session and set the process group ID to the * session ID * * Parameters: (void) * * Returns: 0 Success * EPERM Permission denied * * Notes: If the calling process is not the process group leader; there * is no existing process group with its ID, and we are not * currently in vfork, then this function will create a new * session, a new process group, and put the caller in the * process group (as the sole member) and make it the session * leader (as the sole process in the session). * * The existing controlling tty (if any) will be dissociated * from the process, and the next non-O_NOCTTY open of a tty * will establish a new controlling tty. * * XXX: Belongs in kern_proc.c */ 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); *retval = p->p_pid; return (0); } } /* * setpgid * * Description: set process group ID for job control * * Parameters: uap->pid Process to change * uap->pgid Process group to join or create * * Returns: 0 Success * ESRCH pid is not the caller or a child of * the caller * enterpgrp:ESRCH No such process * EACCES Permission denied due to exec * EINVAL Invalid argument * EPERM The target process is not in the same * session as the calling process * EPERM The target process is a session leader * EPERM pid and pgid are not the same, and * there is no process in the calling * process whose process group ID matches * pgid * * Notes: This function will cause the target process to either join * an existing process process group, or create a new process * group in the session of the calling process. It cannot be * used to change the process group ID of a process which is * already a session leader. * * If the target pid is 0, the pid of the calling process is * substituted as the new target; if pgid is 0, the target pid * 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 * * XXX: Belongs in kern_proc.c */ int setpgid(proc_t curp, register struct setpgid_args *uap, __unused int32_t *retval) { proc_t targp = PROC_NULL; /* target process */ struct pgrp *pg = PGRP_NULL; /* target pgrp */ int error = 0; int refheld = 0; int samesess = 0; struct session * curp_sessp = SESSION_NULL; struct session * targp_sessp = SESSION_NULL; curp_sessp = proc_session(curp); if (uap->pid != 0 && uap->pid != curp->p_pid) { if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) { if (targp != PROC_NULL) refheld = 1; error = ESRCH; goto out; } refheld = 1; targp_sessp = proc_session(targp); if (targp_sessp != curp_sessp) { error = EPERM; goto out; } if (targp->p_flag & P_EXEC) { error = EACCES; goto out; } } else { targp = curp; targp_sessp = proc_session(targp); } if (SESS_LEADER(targp, targp_sessp)) { error = EPERM; goto out; } if (targp_sessp != SESSION_NULL) { session_rele(targp_sessp); targp_sessp = SESSION_NULL; } if (uap->pgid < 0) { error = EINVAL; goto out; } if (uap->pgid == 0) uap->pgid = targp->p_pid; else if (uap->pgid != targp->p_pid) { if ((pg = pgfind(uap->pgid)) == 0){ error = EPERM; goto out; } samesess = (pg->pg_session != curp_sessp); pg_rele(pg); if (samesess != 0) { error = EPERM; goto out; } } error = enterpgrp(targp, uap->pgid, 0); out: if (targp_sessp != SESSION_NULL) session_rele(targp_sessp); if (curp_sessp != SESSION_NULL) session_rele(curp_sessp); if (refheld != 0) proc_rele(targp); return(error); } /* * issetugid * * Description: Is current process tainted by uid or gid changes system call * * Parameters: (void) * * Returns: 0 Not tainted * 1 Tainted * * Notes: A process is considered tainted if it was created as a retult * of an execve call from an imnage that had either the SUID or * SGID bit set on the executable, or if it has changed any of its * real, effective, or saved user or group IDs since beginning * execution. */ int issetugid(proc_t p, __unused struct issetugid_args *uap, int32_t *retval) { /* * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, * we use P_SUGID because we consider changing the owners as * "tainting" as well. * This is significant for procs that start as root and "become" * a user without an exec - programs cannot know *everything* * that libc *might* have put in their data segment. */ *retval = (p->p_flag & P_SUGID) ? 1 : 0; return (0); } /* * setuid * * Description: Set user ID system call * * Parameters: uap->uid uid to set * * Returns: 0 Success * suser:EPERM Permission denied * * Notes: If called by a privileged process, this function will set the * real, effective, and saved uid to the requested value. * * If called from an unprivileged process, but uid is equal to the * real or saved uid, then the effective uid will be set to the * requested value, but the real and saved uid will not change. * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. */ int setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) { uid_t uid; uid_t svuid = KAUTH_UID_NONE; uid_t ruid = KAUTH_UID_NONE; uid_t gmuid = KAUTH_UID_NONE; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; uid = uap->uid; 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. */ /* * 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 */ (void)chgproccnt(uid, 1); (void)chgproccnt(my_pcred->cr_ruid, -1); } /* get current credential and take a reference while we muck with it */ for (;;) { /* * Only set the gmuid if the current cred has not opt'ed out; * this normally only happens when calling setgroups() instead * of initgroups() to set an explicit group list, or one of the * other group manipulation functions is invoked and results in * a dislocation (i.e. the credential group membership changes * 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)) 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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); /* try again */ 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); } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); return (0); } /* * seteuid * * Description: Set effective user ID system call * * Parameters: uap->euid effective uid to set * * Returns: 0 Success * suser:EPERM Permission denied * * Notes: If called by a privileged process, or called from an * unprivileged process but euid is equal to the real or saved * uid, then the effective uid will be set to the requested * value, but the real and saved uid will not change. * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. */ int seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval) { uid_t euid; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; DEBUG_CRED_ENTER("seteuid: %d\n", uap->euid); euid = uap->euid; AUDIT_ARG(euid, euid); 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 (;;) { /* * 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) { 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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); /* try again */ 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); } break; } /* drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); return (0); } /* * setreuid * * Description: Set real and effective user ID system call * * Parameters: uap->ruid real uid to set * uap->euid effective uid to set * * Returns: 0 Success * suser:EPERM Permission denied * * Notes: A value of -1 is a special case indicating that the uid for * which that value is specified not be changed. If both values * are specified as -1, no action is taken. * * If called by a privileged process, the real and effective uid * will be set to the new value(s) specified. * * If called from an unprivileged process, the real uid may be * set to the current value of the real uid, or to the current * value of the saved uid. The effective uid may be set to the * current value of any of the effective, real, or saved uid. * * If the newly requested real uid or effective uid does not * match the saved uid, then set the saved uid to the new * effective uid (potentially unrecoverably dropping saved * privilege). * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. */ int setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) { uid_t ruid, euid; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; DEBUG_CRED_ENTER("setreuid %d %d\n", uap->ruid, uap->euid); ruid = uap->ruid; euid = uap->euid; if (ruid == (uid_t)-1) ruid = KAUTH_UID_NONE; 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 (;;) { 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) { /* 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 * new effective uid. We are protected from escalation * by the prechecking. */ if (my_pcred->cr_svuid != uap->ruid && my_pcred->cr_svuid != uap->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) { 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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); /* 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); } break; } /* drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); return (0); } /* * setgid * * Description: Set group ID system call * * Parameters: uap->gid gid to set * * Returns: 0 Success * suser:EPERM Permission denied * * Notes: If called by a privileged process, this function will set the * real, effective, and saved gid to the requested value. * * If called from an unprivileged process, but gid is equal to the * real or saved gid, then the effective gid will be set to the * requested value, but the real and saved gid will not change. * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. * * As an implementation detail, the effective gid is stored as * the first element of the supplementary group list, and * therefore the effective group list may be reordered to keep * the supplementary group list unchanged. */ int setgid(proc_t p, struct setgid_args *uap, __unused int32_t *retval) { gid_t gid; gid_t rgid = KAUTH_GID_NONE; gid_t svgid = KAUTH_GID_NONE; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; DEBUG_CRED_ENTER("setgid(%d/%d): %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), uap->gid); gid = uap->gid; AUDIT_ARG(gid, gid); 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); } /* * 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; } /* 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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); 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); } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); return (0); } /* * setegid * * Description: Set effective group ID system call * * Parameters: uap->egid effective gid to set * * Returns: 0 Success * suser:EPERM * * Notes: If called by a privileged process, or called from an * unprivileged process but egid is equal to the real or saved * gid, then the effective gid will be set to the requested * value, but the real and saved gid will not change. * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. * * As an implementation detail, the effective gid is stored as * the first element of the supplementary group list, and * therefore the effective group list may be reordered to keep * the supplementary group list unchanged. */ int setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval) { gid_t egid; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; DEBUG_CRED_ENTER("setegid %d\n", uap->egid); egid = uap->egid; AUDIT_ARG(egid, egid); 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 (;;) { /* * 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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); 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); } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); return (0); } /* * setregid * * Description: Set real and effective group ID system call * * Parameters: uap->rgid real gid to set * uap->egid effective gid to set * * Returns: 0 Success * suser:EPERM Permission denied * * Notes: A value of -1 is a special case indicating that the gid for * which that value is specified not be changed. If both values * are specified as -1, no action is taken. * * If called by a privileged process, the real and effective gid * will be set to the new value(s) specified. * * If called from an unprivileged process, the real gid may be * set to the current value of the real gid, or to the current * value of the saved gid. The effective gid may be set to the * current value of any of the effective, real, or saved gid. * * If the new real and effective gid will not be equal, or the * new real or effective gid is not the same as the saved gid, * then the saved gid will be updated to reflect the new * effective gid (potentially unrecoverably dropping saved * privilege). * * If the credential is changed as a result of this call, then we * flag the process as having set privilege since the last exec. * * As an implementation detail, the effective gid is stored as * the first element of the supplementary group list, and * therefore the effective group list may be reordered to keep * the supplementary group list unchanged. */ int setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval) { gid_t rgid, egid; int error; kauth_cred_t my_cred, my_new_cred; posix_cred_t my_pcred; DEBUG_CRED_ENTER("setregid %d %d\n", uap->rgid, uap->egid); rgid = uap->rgid; egid = uap->egid; if (rgid == (uid_t)-1) rgid = KAUTH_GID_NONE; if (egid == (uid_t)-1) egid = KAUTH_GID_NONE; AUDIT_ARG(egid, egid); AUDIT_ARG(rgid, rgid); 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 (;;) { 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) { /* changing the effective GID */ new_egid = egid; OSBitOrAtomic(P_SUGID, &p->p_flag); } if (rgid != KAUTH_UID_NONE && my_pcred->cr_rgid != rgid) { /* changing the real GID */ new_rgid = rgid; OSBitOrAtomic(P_SUGID, &p->p_flag); } /* * If the newly requested real gid or effective gid does * not match the saved gid, then set the saved gid to the * new effective gid. We are protected from escalation * by the prechecking. */ if (my_pcred->cr_svgid != uap->rgid && my_pcred->cr_svgid != uap->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); /* 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); kauth_cred_unref(&my_new_cred); /* try again */ my_cred = kauth_cred_proc_ref(p); 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); } break; } /* Drop old proc reference or our extra reference */ kauth_cred_unref(&my_cred); set_security_token(p); 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 * 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 * thread to the requested UID and single GID, and clears all other GIDs. */ int settid(proc_t p, struct settid_args *uap, __unused int32_t *retval) { kauth_cred_t uc; struct uthread *uthread = get_bsdthread_info(current_thread()); uid_t uid; gid_t gid; uid = uap->uid; gid = uap->gid; AUDIT_ARG(uid, uid); AUDIT_ARG(gid, gid); 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); /* revert to delayed binding of process credential */ uc = kauth_cred_proc_ref(p); kauth_cred_unref(&uthread->uu_ucred); uthread->uu_ucred = uc; uthread->uu_flag &= ~UT_SETUID; } else { kauth_cred_t my_cred, my_new_cred; /* cannot already be assuming another identity */ if ((uthread->uu_flag & UT_SETUID) != 0) { return (EPERM); } /* * Get a new credential instance from the old if this one * changes; otherwise kauth_cred_setuidgid() returns the * same credential. We take an extra reference on the * current credential while we muck with it, so we can do * the post-compare for changes by pointer. */ 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) uthread->uu_ucred = my_new_cred; uthread->uu_flag |= UT_SETUID; /* Drop old uthread reference or our extra reference */ kauth_cred_unref(&my_cred); } /* * XXX should potentially set per thread security token (there is * XXX none). * XXX it is unclear whether P_SUGID should be st at this point; * XXX in theory, it is being deprecated. */ return (0); } /* * Set the per-thread override identity. Use this system call for a thread to * assume the identity of another process or to revert back to normal identity * of the current process. * * When the "assume" argument is non zero the current thread will assume the * identity of the process represented by the pid argument. * * When the assume argument is zero we revert back to our normal identity. */ int settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *retval) { proc_t target_proc; struct uthread *uthread = get_bsdthread_info(current_thread()); kauth_cred_t my_cred, my_target_cred, my_new_cred; posix_cred_t my_target_pcred; AUDIT_ARG(pid, uap->pid); AUDIT_ARG(value32, uap->assume); if (proc_suser(p) != 0) { return (EPERM); } /* * XXX should potentially set per thread security token (there is * XXX none). * XXX it is unclear whether P_SUGID should be st at this point; * XXX in theory, it is being deprecated. */ /* * assume argument tells us to assume the identity of the process with the * id passed in the pid argument. */ if (uap->assume != 0) { /* can't do this if we have already assumed an identity */ 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) proc_rele(target_proc); return (ESRCH); } /* * Take a reference on the credential used in our target * process then use it as the identity for our current * thread. We take an extra reference on the current * credential while we muck with it, so we can do the * post-compare for changes by pointer. * * The post-compare is needed for the case that our process * credential has been changed to be identical to our thread * credential following our assumption of a per-thread one, * since the credential cache will maintain a unique instance. */ 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) 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); } /* * 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); /* 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); } /* * setgroups1 * * Description: Internal implementation for both the setgroups and initgroups * system calls * * Parameters: gidsetsize Number of groups in set * gidset Pointer to group list * gmuid Base gid (initgroups only!) * * Returns: 0 Success * suser:EPERM Permision denied * EINVAL Invalid gidsetsize value * copyin:EFAULT Bad gidset or gidsetsize is * too large * * Notes: When called from a thread running under an assumed per-thread * identity, this function will operate against the per-thread * credential, rather than against the process credential. In * this specific case, the process credential is verified to * still be privileged at the time of the call, rather than the * per-thread credential for this operation to be permitted. * * This effectively means that setgroups/initigroups calls in * a thread running a per-thread credential should occur *after* * the settid call that created it, not before (unlike setuid, * which must be called after, since it will result in privilege * being dropped). * * When called normally (i.e. no per-thread assumed identity), * the per process credential is updated per POSIX. * * If the credential is changed as a result of this call, then we * 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) { 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()); 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); ngrp = gidsetsize; if (ngrp > NGROUPS) return (EINVAL); if ( ngrp < 1 ) { ngrp = 1; } else { error = copyin(gidset, (caddr_t)newgroups, ngrp * sizeof(gid_t)); if (error) { return (error); } } my_cred = kauth_cred_proc_ref(p); if ((error = suser(my_cred, &p->p_acflag))) { kauth_cred_unref(&my_cred); return (error); } if ((uthread->uu_flag & UT_SETUID) != 0) { #if DEBUG_CRED int my_cred_flags = uthread->uu_ucred->cr_flags; #endif /* DEBUG_CRED */ kauth_cred_unref(&my_cred); /* * If this thread is under an assumed identity, set the * supplementary grouplist on the thread credential instead * of the process one. If we were the only reference holder, * the credential is updated in place, otherwise, our reference * is dropped and we get back a different cred with a reference * already held on it. Because this is per-thread, we don't * need the referencing/locking/retry required for per-process. */ my_cred = uthread->uu_ucred; 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); } #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 * 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_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); /* * 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. */ if (p->p_ucred != my_cred) { proc_unlock(p); kauth_cred_unref(&my_new_cred); my_cred = kauth_cred_proc_ref(p); /* try again */ 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); } break; } /* Drop old proc reference or our extra reference */ AUDIT_ARG(groupset, posix_cred_get(my_cred)->cr_groups, ngrp); kauth_cred_unref(&my_cred); set_security_token(p); } return (0); } /* * initgroups * * Description: Initialize the default supplementary groups list and set the * gmuid for use by the external group resolver (if any) * * Parameters: uap->gidsetsize Number of groups in set * uap->gidset Pointer to group list * uap->gmuid Base gid * * Returns: 0 Success * setgroups1:EPERM Permision denied * setgroups1:EINVAL Invalid gidsetsize value * setgroups1:EFAULT Bad gidset or gidsetsize is * * Notes: This function opts *IN* to memberd participation * * The normal purpose of this function is for a privileged * process to indicate supplementary groups and identity for * participation in extended group membership resolution prior * to dropping privilege by assuming a specific user identity. * * It is the first half of the primary mechanism whereby user * identity is established to the system by programs such as * /usr/bin/login. The second half is the drop of uid privilege * for a specific uid corresponding to the user. * * See also: setgroups1() */ int 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)); } /* * setgroups * * Description: Initialize the default supplementary groups list * * Parameters: gidsetsize Number of groups in set * gidset Pointer to group list * * Returns: 0 Success * setgroups1:EPERM Permision denied * setgroups1:EINVAL Invalid gidsetsize value * setgroups1:EFAULT Bad gidset or gidsetsize is * * Notes: This functions opts *OUT* of memberd participation. * * This function exists for compatibility with POSIX. Most user * programs should use initgroups() instead to ensure correct * participation in group membership resolution when utilizing * a directory service for authentication. * * It is identical to an initgroups() call with a gmuid argument * of KAUTH_UID_NONE. * * See also: setgroups1() */ int 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)); } /* * Set the per-thread/per-process supplementary groups list. * * XXX implement setsgroups * */ int setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused int32_t *retval) { return(ENOTSUP); } /* * Set the per-thread/per-process whiteout groups list. * * XXX implement setwgroups * */ int setwgroups(__unused proc_t p, __unused struct setwgroups_args *uap, __unused int32_t *retval) { return(ENOTSUP); } /* * Check if gid is a member of the group set. * * XXX This interface is going away; use kauth_cred_ismember_gid() directly * XXX instead. */ int 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); } /* * Test whether the specified credentials imply "super-user" * privilege; if so, and we have accounting info, set the flag * indicating use of super-powers. * Returns 0 or error. * * XXX This interface is going away; use kauth_cred_issuser() directly * XXX instead. * * Note: This interface exists to implement the "has used privilege" * bit (ASU) in the p_acflags field of the process, which is * only externalized via private sysctl and in process accounting * records. The flag is technically not required in either case. */ int suser(kauth_cred_t cred, u_short *acflag) { #if DIAGNOSTIC if (!IS_VALID_CRED(cred)) panic("suser"); #endif if (kauth_cred_getuid(cred) == 0) { if (acflag) *acflag |= ASU; return (0); } return (EPERM); } /* * getlogin * * Description: Get login name, if available. * * Parameters: uap->namebuf User buffer for return * uap->namelen User buffer length * * Returns: 0 Success * copyout:EFAULT * * Notes: Intended to obtain a string containing the user name of the * user associated with the controlling terminal for the calling * process. * * Not very useful on modern systems, due to inherent length * limitations for the static array in the session structure * which is used to store the login name. * * Permitted to return NULL * * XXX: Belongs in kern_proc.c */ int getlogin(proc_t p, struct getlogin_args *uap, __unused int32_t *retval) { char buffer[MAXLOGNAME+1]; struct session * sessp; bzero(buffer, MAXLOGNAME+1); sessp = proc_session(p); if (uap->namelen > MAXLOGNAME) uap->namelen = MAXLOGNAME; 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)); } /* * setlogin * * Description: Set login name. * * Parameters: uap->namebuf User buffer containing name * * Returns: 0 Success * suser:EPERM Permission denied * copyinstr:EFAULT User buffer invalid * copyinstr:EINVAL Supplied name was too long * * Notes: This is a utility system call to support getlogin(). * * XXX: Belongs in kern_proc.c */ 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; if ((error = proc_suser(p))) return (error); 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); } if (!error) { AUDIT_ARG(text, buffer); } else if (error == ENAMETOOLONG) error = EINVAL; return (error); } /* Set the secrity token of the task with current euid and eguid */ /* * XXX This needs to change to give the task a reference and/or an opaque * XXX identifier. */ int set_security_token(proc_t p) { 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; /* * Don't allow a vfork child to override the parent's token settings * (since they share a task). Instead, the child will just have to * 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; uthread = (uthread_t)get_bsdthread_info(current_thread()); if (uthread->uu_flag & UT_VFORK) return (1); } my_cred = kauth_cred_proc_ref(p); my_pcred = posix_cred_get(my_cred); /* XXX mach_init doesn't have a p_ucred when it calls this function */ if (IS_VALID_CRED(my_cred)) { sec_token.val[0] = kauth_cred_getuid(my_cred); sec_token.val[1] = kauth_cred_getgid(my_cred); } else { sec_token.val[0] = 0; 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)) 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); 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)); } #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) { 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; /* Join/Adopt */ default: /* Only root may Join/Adopt. */ if (!kauth_cred_issuser(kauth_cred_get())) { error = EPERM; goto out; } l = lcfind(uap->lcid); if (l == NULL) { error = ENOATTR; goto out; } break; } ALLLCTX_LOCK; leavelctx(p); enterlctx(p, l, (uap->lcid == LCID_CREATE) ? 1 : 0); ALLLCTX_UNLOCK; 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) { 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; } #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); } #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) { return (ENOSYS); } #endif /* !LCTX */