1753 lines
45 KiB
C
1753 lines
45 KiB
C
|
/*
|
||
|
* 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 <sys/param.h>
|
||
|
#include <sys/acct.h>
|
||
|
#include <sys/systm.h>
|
||
|
#include <sys/ucred.h>
|
||
|
#include <sys/proc_internal.h>
|
||
|
#include <sys/user.h>
|
||
|
#include <sys/kauth.h>
|
||
|
#include <sys/timeb.h>
|
||
|
#include <sys/times.h>
|
||
|
#include <sys/malloc.h>
|
||
|
#include <sys/persona.h>
|
||
|
|
||
|
#include <security/audit/audit.h>
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
#include <security/mac_framework.h>
|
||
|
#endif
|
||
|
|
||
|
#include <sys/mount_internal.h>
|
||
|
#include <sys/sysproto.h>
|
||
|
#include <mach/message.h>
|
||
|
|
||
|
#include <kern/host.h>
|
||
|
#include <kern/task.h> /* for current_task() */
|
||
|
#include <kern/assert.h>
|
||
|
|
||
|
#if DEVELOPMENT || DEBUG
|
||
|
extern void task_importance_update_owner_info(task_t);
|
||
|
#endif
|
||
|
|
||
|
/* Used by pmap.c to copy kauth_cred_t structs */
|
||
|
void kauth_cred_copy(const uintptr_t kv, const uintptr_t new_data);
|
||
|
|
||
|
/*
|
||
|
* 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 = proc_getpid(p);
|
||
|
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;
|
||
|
|
||
|
if (uap->pid == 0) {
|
||
|
*retval = proc_sessionid(p);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ((pt = proc_find(uap->pid)) != PROC_NULL) {
|
||
|
*retval = proc_sessionid(pt);
|
||
|
proc_rele(pt);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return ESRCH;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
thread_ro_t tro = current_thread_ro();
|
||
|
kauth_cred_t tro_cred = tro->tro_cred;
|
||
|
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 (tro->tro_realcred == tro->tro_cred) {
|
||
|
return ESRCH;
|
||
|
}
|
||
|
|
||
|
if ((error = suword(uap->uidp, kauth_cred_getruid(tro_cred)))) {
|
||
|
return error;
|
||
|
}
|
||
|
if ((error = suword(uap->gidp, kauth_cred_getrgid(tro_cred)))) {
|
||
|
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_internal
|
||
|
*
|
||
|
* Description: Core implementation of setsid().
|
||
|
*/
|
||
|
int
|
||
|
setsid_internal(proc_t p)
|
||
|
{
|
||
|
struct pgrp * pg = PGRP_NULL;
|
||
|
|
||
|
if (p->p_pgrpid == proc_getpid(p) ||
|
||
|
(pg = pgrp_find(proc_getpid(p)))) {
|
||
|
pgrp_rele(pg);
|
||
|
return EPERM;
|
||
|
}
|
||
|
|
||
|
/* enter pgrp works with its own pgrp refcount */
|
||
|
(void)enterpgrp(p, proc_getpid(p), 1);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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, 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)
|
||
|
{
|
||
|
int rc = setsid_internal(p);
|
||
|
if (rc == 0) {
|
||
|
*retval = proc_getpid(p);
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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, struct setpgid_args *uap, __unused int32_t *retval)
|
||
|
{
|
||
|
proc_t targp = PROC_NULL; /* target process */
|
||
|
struct pgrp *curp_pg = PGRP_NULL;
|
||
|
struct pgrp *targp_pg = PGRP_NULL;
|
||
|
int error = 0;
|
||
|
int refheld = 0;
|
||
|
int samesess = 0;
|
||
|
|
||
|
curp_pg = proc_pgrp(curp, NULL);
|
||
|
|
||
|
if (uap->pid != 0 && uap->pid != proc_getpid(curp)) {
|
||
|
if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) {
|
||
|
if (targp != PROC_NULL) {
|
||
|
refheld = 1;
|
||
|
}
|
||
|
error = ESRCH;
|
||
|
goto out;
|
||
|
}
|
||
|
refheld = 1;
|
||
|
targp_pg = proc_pgrp(targp, NULL);
|
||
|
if (targp_pg->pg_session != curp_pg->pg_session) {
|
||
|
error = EPERM;
|
||
|
goto out;
|
||
|
}
|
||
|
if (targp->p_flag & P_EXEC) {
|
||
|
error = EACCES;
|
||
|
goto out;
|
||
|
}
|
||
|
} else {
|
||
|
targp = curp;
|
||
|
targp_pg = proc_pgrp(targp, NULL);
|
||
|
}
|
||
|
|
||
|
if (SESS_LEADER(targp, targp_pg->pg_session)) {
|
||
|
error = EPERM;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
if (uap->pgid < 0) {
|
||
|
error = EINVAL;
|
||
|
goto out;
|
||
|
}
|
||
|
if (uap->pgid == 0) {
|
||
|
uap->pgid = proc_getpid(targp);
|
||
|
} else if (uap->pgid != proc_getpid(targp)) {
|
||
|
struct pgrp *pg = PGRP_NULL;
|
||
|
|
||
|
if ((pg = pgrp_find(uap->pgid)) == PGRP_NULL) {
|
||
|
error = EPERM;
|
||
|
goto out;
|
||
|
}
|
||
|
samesess = (pg->pg_session != curp_pg->pg_session);
|
||
|
pgrp_rele(pg);
|
||
|
if (samesess != 0) {
|
||
|
error = EPERM;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
error = enterpgrp(targp, uap->pgid, 0);
|
||
|
out:
|
||
|
pgrp_rele(curp_pg);
|
||
|
pgrp_rele(targp_pg);
|
||
|
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
|
||
|
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)
|
||
|
{
|
||
|
/*
|
||
|
* 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 = proc_issetugid(p);
|
||
|
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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
__block uid_t old_ruid;
|
||
|
__block uid_t ruid;
|
||
|
uid_t want_uid;
|
||
|
bool changed;
|
||
|
|
||
|
want_uid = uap->uid;
|
||
|
AUDIT_ARG(uid, want_uid);
|
||
|
|
||
|
changed = kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
uid_t svuid = KAUTH_UID_NONE;
|
||
|
uid_t gmuid = KAUTH_UID_NONE;
|
||
|
|
||
|
ruid = KAUTH_UID_NONE;
|
||
|
old_ruid = cur_pcred->cr_ruid;
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_setuid(p, parent, want_uid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (want_uid != cur_pcred->cr_ruid && /* allow setuid(getuid()) */
|
||
|
want_uid != cur_pcred->cr_svuid && /* allow setuid(saved uid) */
|
||
|
(error = suser(parent, &p->p_acflag))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we are privileged, then set the saved and real UID too;
|
||
|
* otherwise, just set the effective UID
|
||
|
*/
|
||
|
if (suser(parent, &p->p_acflag) == 0) {
|
||
|
svuid = want_uid;
|
||
|
ruid = want_uid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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 (!(cur_pcred->cr_flags & CRF_NOMEMBERD)) {
|
||
|
gmuid = want_uid;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresuid(model,
|
||
|
ruid, want_uid, svuid, gmuid);
|
||
|
});
|
||
|
|
||
|
if (changed && ruid != KAUTH_UID_NONE && old_ruid != ruid &&
|
||
|
!proc_has_persona(p)) {
|
||
|
(void)chgproccnt(ruid, 1);
|
||
|
(void)chgproccnt(old_ruid, -1);
|
||
|
}
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
uid_t want_euid;
|
||
|
|
||
|
want_euid = uap->euid;
|
||
|
AUDIT_ARG(euid, want_euid);
|
||
|
|
||
|
kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_seteuid(p, parent, want_euid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (want_euid != cur_pcred->cr_ruid && want_euid != cur_pcred->cr_svuid &&
|
||
|
(error = suser(parent, &p->p_acflag))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresuid(model,
|
||
|
KAUTH_UID_NONE, want_euid,
|
||
|
KAUTH_UID_NONE, cur_pcred->cr_gmuid);
|
||
|
});
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
__block uid_t old_ruid;
|
||
|
uid_t want_ruid, want_euid;
|
||
|
bool changed;
|
||
|
|
||
|
want_ruid = uap->ruid;
|
||
|
want_euid = uap->euid;
|
||
|
|
||
|
if (want_ruid == (uid_t)-1) {
|
||
|
want_ruid = KAUTH_UID_NONE;
|
||
|
}
|
||
|
|
||
|
if (want_euid == (uid_t)-1) {
|
||
|
want_euid = KAUTH_UID_NONE;
|
||
|
}
|
||
|
|
||
|
AUDIT_ARG(euid, want_euid);
|
||
|
AUDIT_ARG(ruid, want_ruid);
|
||
|
|
||
|
changed = kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
uid_t svuid = KAUTH_UID_NONE;
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_setreuid(p, parent, want_ruid, want_euid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (((want_ruid != KAUTH_UID_NONE && /* allow no change of ruid */
|
||
|
want_ruid != cur_pcred->cr_ruid && /* allow ruid = ruid */
|
||
|
want_ruid != cur_pcred->cr_uid && /* allow ruid = euid */
|
||
|
want_ruid != cur_pcred->cr_svuid) || /* allow ruid = svuid */
|
||
|
(want_euid != KAUTH_UID_NONE && /* allow no change of euid */
|
||
|
want_euid != cur_pcred->cr_uid && /* allow euid = euid */
|
||
|
want_euid != cur_pcred->cr_ruid && /* allow euid = ruid */
|
||
|
want_euid != cur_pcred->cr_svuid)) && /* allow euid = svuid */
|
||
|
(error = suser(parent, &p->p_acflag))) { /* allow root user any */
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uid_t new_euid = cur_pcred->cr_uid;
|
||
|
|
||
|
if (want_euid != KAUTH_UID_NONE && cur_pcred->cr_uid != want_euid) {
|
||
|
new_euid = want_euid;
|
||
|
}
|
||
|
|
||
|
old_ruid = cur_pcred->cr_ruid;
|
||
|
|
||
|
/*
|
||
|
* 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 (cur_pcred->cr_svuid != uap->ruid &&
|
||
|
cur_pcred->cr_svuid != uap->euid) {
|
||
|
svuid = new_euid;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresuid(model, want_ruid, want_euid,
|
||
|
svuid, cur_pcred->cr_gmuid);
|
||
|
});
|
||
|
|
||
|
if (changed && want_ruid != KAUTH_UID_NONE && want_ruid != old_ruid &&
|
||
|
!proc_has_persona(p)) {
|
||
|
(void)chgproccnt(want_ruid, 1);
|
||
|
(void)chgproccnt(old_ruid, -1);
|
||
|
}
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
gid_t want_gid;
|
||
|
|
||
|
want_gid = uap->gid;
|
||
|
AUDIT_ARG(gid, want_gid);
|
||
|
|
||
|
kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
gid_t rgid = KAUTH_GID_NONE;
|
||
|
gid_t svgid = KAUTH_GID_NONE;
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_setgid(p, parent, want_gid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (want_gid != cur_pcred->cr_rgid && /* allow setgid(getgid()) */
|
||
|
want_gid != cur_pcred->cr_svgid && /* allow setgid(saved gid) */
|
||
|
(error = suser(parent, &p->p_acflag))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we are privileged, then set the saved and real GID too;
|
||
|
* otherwise, just set the effective GID
|
||
|
*/
|
||
|
if (suser(parent, &p->p_acflag) == 0) {
|
||
|
svgid = want_gid;
|
||
|
rgid = want_gid;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresgid(model, rgid, want_gid, svgid);
|
||
|
});
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
gid_t want_egid;
|
||
|
|
||
|
want_egid = uap->egid;
|
||
|
AUDIT_ARG(egid, want_egid);
|
||
|
|
||
|
kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_setegid(p, parent, want_egid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (want_egid != cur_pcred->cr_rgid &&
|
||
|
want_egid != cur_pcred->cr_svgid &&
|
||
|
(error = suser(parent, &p->p_acflag))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresgid(model, KAUTH_GID_NONE,
|
||
|
want_egid, KAUTH_GID_NONE);
|
||
|
});
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
__block int error = 0;
|
||
|
gid_t want_rgid;
|
||
|
gid_t want_egid;
|
||
|
|
||
|
want_rgid = uap->rgid;
|
||
|
want_egid = uap->egid;
|
||
|
|
||
|
if (want_rgid == (gid_t)-1) {
|
||
|
want_rgid = KAUTH_GID_NONE;
|
||
|
}
|
||
|
|
||
|
if (want_egid == (gid_t)-1) {
|
||
|
want_egid = KAUTH_GID_NONE;
|
||
|
}
|
||
|
|
||
|
AUDIT_ARG(egid, want_egid);
|
||
|
AUDIT_ARG(rgid, want_rgid);
|
||
|
|
||
|
kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID,
|
||
|
^bool (kauth_cred_t parent, kauth_cred_t model) {
|
||
|
posix_cred_t cur_pcred = posix_cred_get(parent);
|
||
|
uid_t svgid = KAUTH_UID_NONE;
|
||
|
|
||
|
#if CONFIG_MACF
|
||
|
if ((error = mac_proc_check_setregid(p, parent, want_rgid,
|
||
|
want_egid)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (((want_rgid != KAUTH_UID_NONE && /* allow no change of rgid */
|
||
|
want_rgid != cur_pcred->cr_rgid && /* allow rgid = rgid */
|
||
|
want_rgid != cur_pcred->cr_gid && /* allow rgid = egid */
|
||
|
want_rgid != cur_pcred->cr_svgid) || /* allow rgid = svgid */
|
||
|
(want_egid != KAUTH_UID_NONE && /* allow no change of egid */
|
||
|
want_egid != cur_pcred->cr_groups[0] && /* allow no change of egid */
|
||
|
want_egid != cur_pcred->cr_gid && /* allow egid = egid */
|
||
|
want_egid != cur_pcred->cr_rgid && /* allow egid = rgid */
|
||
|
want_egid != cur_pcred->cr_svgid)) && /* allow egid = svgid */
|
||
|
(error = suser(parent, &p->p_acflag))) { /* allow root user any */
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uid_t new_egid = cur_pcred->cr_gid;
|
||
|
if (want_egid != KAUTH_UID_NONE && cur_pcred->cr_gid != want_egid) {
|
||
|
/* changing the effective GID */
|
||
|
new_egid = want_egid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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 (cur_pcred->cr_svgid != want_rgid &&
|
||
|
cur_pcred->cr_svgid != want_egid) {
|
||
|
svgid = new_egid;
|
||
|
}
|
||
|
|
||
|
return kauth_cred_model_setresgid(model, want_rgid, want_egid, svgid);
|
||
|
});
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
kern_settid_assume_cred(thread_ro_t tro, kauth_cred_t tmp)
|
||
|
{
|
||
|
kauth_cred_t cred = NOCRED;
|
||
|
|
||
|
kauth_cred_set(&cred, tmp);
|
||
|
zalloc_ro_update_field(ZONE_ID_THREAD_RO, tro, tro_cred, &cred);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set the per-thread override identity. The first parameter can be the
|
||
|
* 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
|
||
|
* thread to the requested UID and single GID, and clears all other GIDs.
|
||
|
*/
|
||
|
static int
|
||
|
kern_settid(proc_t p, uid_t uid, gid_t gid)
|
||
|
{
|
||
|
kauth_cred_t cred;
|
||
|
struct thread_ro *tro = current_thread_ro();
|
||
|
#if CONFIG_MACF
|
||
|
int error;
|
||
|
|
||
|
if ((error = mac_proc_check_settid(p, uid, gid)) != 0) {
|
||
|
return error;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (proc_suser(p) != 0) {
|
||
|
return EPERM;
|
||
|
}
|
||
|
|
||
|
if (uid == KAUTH_UID_NONE) {
|
||
|
/* must already be assuming another identity in order to revert back */
|
||
|
if (tro->tro_realcred == tro->tro_cred) {
|
||
|
return EPERM;
|
||
|
}
|
||
|
|
||
|
/* revert to delayed binding of process credential */
|
||
|
kern_settid_assume_cred(tro, tro->tro_realcred);
|
||
|
} else {
|
||
|
/* cannot already be assuming another identity */
|
||
|
if (tro->tro_realcred != tro->tro_cred) {
|
||
|
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.
|
||
|
*/
|
||
|
cred = kauth_cred_derive(tro->tro_cred,
|
||
|
^bool (kauth_cred_t parent __unused, kauth_cred_t model) {
|
||
|
return kauth_cred_model_setuidgid(model, uid, gid);
|
||
|
});
|
||
|
kern_settid_assume_cred(tro, cred);
|
||
|
kauth_cred_unref(&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;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
sys_settid(proc_t p, struct settid_args *uap, __unused int32_t *retval)
|
||
|
{
|
||
|
AUDIT_ARG(uid, uap->uid);
|
||
|
AUDIT_ARG(gid, uap->gid);
|
||
|
|
||
|
return kern_settid(p, uap->uid, uap->gid);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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
|
||
|
sys_settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *retval)
|
||
|
{
|
||
|
uid_t uid;
|
||
|
gid_t gid;
|
||
|
|
||
|
AUDIT_ARG(pid, uap->pid);
|
||
|
AUDIT_ARG(value32, uap->assume);
|
||
|
|
||
|
/*
|
||
|
* 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) {
|
||
|
kauth_cred_t cred;
|
||
|
|
||
|
if (uap->pid == 0) {
|
||
|
return ESRCH;
|
||
|
}
|
||
|
|
||
|
cred = kauth_cred_proc_ref_for_pid(uap->pid);
|
||
|
if (cred == NOCRED) {
|
||
|
return ESRCH;
|
||
|
}
|
||
|
|
||
|
uid = kauth_cred_getuid(cred);
|
||
|
gid = kauth_cred_getgid(cred);
|
||
|
kauth_cred_unref(&cred);
|
||
|
} else {
|
||
|
/*
|
||
|
* Otherwise, we are reverting back to normal mode of operation
|
||
|
* where delayed binding of the process credential sets the
|
||
|
* credential in the thread_ro (tro_cred)
|
||
|
*/
|
||
|
uid = KAUTH_UID_NONE;
|
||
|
gid = KAUTH_GID_NONE;
|
||
|
}
|
||
|
|
||
|
return kern_settid(p, uid, gid);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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 ngrp, user_addr_t gidset, uid_t gmuid, __unused int32_t *retval)
|
||
|
{
|
||
|
gid_t newgroups[NGROUPS] = { 0 };
|
||
|
int error;
|
||
|
|
||
|
if (ngrp > NGROUPS) {
|
||
|
return EINVAL;
|
||
|
}
|
||
|
|
||
|
if (ngrp >= 1) {
|
||
|
error = copyin(gidset,
|
||
|
(caddr_t)newgroups, ngrp * sizeof(gid_t));
|
||
|
if (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)
|
||
|
{
|
||
|
thread_ro_t tro = current_thread_ro();
|
||
|
kauth_cred_t cred;
|
||
|
int error;
|
||
|
|
||
|
error = proc_suser(p);
|
||
|
if (error) {
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
if (ngrp < 1) {
|
||
|
ngrp = 1;
|
||
|
newgroups[0] = 0;
|
||
|
}
|
||
|
|
||
|
kauth_cred_derive_t fn = ^bool (kauth_cred_t parent __unused, kauth_cred_t model) {
|
||
|
return kauth_cred_model_setgroups(model, newgroups, ngrp, gmuid);
|
||
|
};
|
||
|
|
||
|
if (tro->tro_realcred != tro->tro_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.
|
||
|
*/
|
||
|
cred = kauth_cred_derive(tro->tro_cred, fn);
|
||
|
kern_settid_assume_cred(tro, cred);
|
||
|
kauth_cred_unref(&cred);
|
||
|
} else {
|
||
|
kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID, fn);
|
||
|
AUDIT_ARG(groupset, &newgroups[0], ngrp);
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
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 (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];
|
||
|
struct session *sessp;
|
||
|
struct pgrp *pg;
|
||
|
|
||
|
if (uap->namelen > MAXLOGNAME) {
|
||
|
uap->namelen = MAXLOGNAME;
|
||
|
}
|
||
|
|
||
|
if ((pg = proc_pgrp(p, &sessp)) != PGRP_NULL) {
|
||
|
session_lock(sessp);
|
||
|
bcopy(sessp->s_login, buffer, uap->namelen);
|
||
|
session_unlock(sessp);
|
||
|
pgrp_rele(pg);
|
||
|
} else {
|
||
|
bzero(buffer, 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;
|
||
|
struct pgrp *pg;
|
||
|
|
||
|
if ((pg = proc_pgrp(p, &sessp)) != PGRP_NULL) {
|
||
|
session_lock(sessp);
|
||
|
bcopy(login, sessp->s_login, MAXLOGNAME);
|
||
|
session_unlock(sessp);
|
||
|
pgrp_rele(pg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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];
|
||
|
|
||
|
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);
|
||
|
|
||
|
setlogin_internal(p, buffer);
|
||
|
|
||
|
if (!error) {
|
||
|
AUDIT_ARG(text, buffer);
|
||
|
} else if (error == ENAMETOOLONG) {
|
||
|
error = EINVAL;
|
||
|
}
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
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] = proc_getpid(p);
|
||
|
audit_token->val[6] = my_cred->cr_audit.as_aia_p->ai_asid;
|
||
|
audit_token->val[7] = proc_pidversion(p);
|
||
|
}
|
||
|
|
||
|
/* Set the secrity token of the task with current euid and eguid */
|
||
|
int
|
||
|
set_security_token(proc_t p, struct ucred *my_cred)
|
||
|
{
|
||
|
security_token_t sec_token;
|
||
|
audit_token_t audit_token;
|
||
|
host_priv_t host_priv;
|
||
|
task_t task = proc_task(p);
|
||
|
|
||
|
proc_calc_audit_token(p, my_cred, &audit_token);
|
||
|
|
||
|
sec_token.val[0] = kauth_cred_getuid(my_cred);
|
||
|
sec_token.val[1] = kauth_cred_getgid(my_cred);
|
||
|
|
||
|
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
|
||
|
|
||
|
#if DEVELOPMENT || DEBUG
|
||
|
/*
|
||
|
* Update the pid an proc name for importance base if any
|
||
|
*/
|
||
|
task_importance_update_owner_info(task);
|
||
|
#endif
|
||
|
|
||
|
return task_set_security_tokens(task, sec_token, audit_token,
|
||
|
host_priv) != KERN_SUCCESS;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
proc_parent_audit_token(proc_t p, audit_token_t *token_out)
|
||
|
{
|
||
|
proc_t parent;
|
||
|
kauth_cred_t my_cred;
|
||
|
|
||
|
proc_list_lock();
|
||
|
|
||
|
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);
|
||
|
|
||
|
proc_list_unlock();
|
||
|
}
|
||
|
|
||
|
|
||
|
int get_audit_token_pid(audit_token_t *audit_token);
|
||
|
|
||
|
int
|
||
|
get_audit_token_pid(audit_token_t *audit_token)
|
||
|
{
|
||
|
/* keep in-sync with set_security_token (above) */
|
||
|
if (audit_token) {
|
||
|
return (int)audit_token->val[5];
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Fill in a struct xucred based on a kauth_cred_t.
|
||
|
*/
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Copy kauth_cred into a virtual address by assignment.
|
||
|
* Needed because elements of kauth_cred are PACed
|
||
|
* so memcpy doesn't work.
|
||
|
*/
|
||
|
void
|
||
|
kauth_cred_copy(const uintptr_t kv, const uintptr_t new_data)
|
||
|
{
|
||
|
*(kauth_cred_t)kv = *(kauth_cred_t)new_data;
|
||
|
}
|