#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 <bsm/audit_kernel.h>
#include <sys/mount_internal.h>
#include <sys/sysproto.h>
#include <mach/message.h>
#include <mach/host_security.h>
#include <kern/host.h>
int groupmember(gid_t gid, kauth_cred_t cred);
int is_suser(void);
int is_suser1(void);
extern int prepare_profile_database(int user);
int
setprivexec(struct proc *p, struct setprivexec_args *uap, register_t *retval)
{
AUDIT_ARG(value, uap->flag);
*retval = p->p_debugger;
p->p_debugger = (uap->flag != 0);
return(0);
}
int
getpid(struct proc *p, __unused struct getpid_args *uap, register_t *retval)
{
*retval = p->p_pid;
return (0);
}
int
getppid(struct proc *p, __unused struct getppid_args *uap, register_t *retval)
{
*retval = p->p_pptr->p_pid;
return (0);
}
int
getpgrp(struct proc *p, __unused struct getpgrp_args *uap, register_t *retval)
{
*retval = p->p_pgrp->pg_id;
return (0);
}
int
getpgid(struct proc *p, struct getpgid_args *uap, register_t *retval)
{
struct proc *pt;
pt = p;
if (uap->pid == 0)
goto found;
if ((pt = pfind(uap->pid)) == 0)
return (ESRCH);
found:
*retval = pt->p_pgrp->pg_id;
return (0);
}
int
getsid(struct proc *p, struct getsid_args *uap, register_t *retval)
{
struct proc *pt;
pt = p;
if (uap->pid == 0)
goto found;
if ((pt = pfind(uap->pid)) == 0)
return (ESRCH);
found:
*retval = pt->p_session->s_sid;
return (0);
}
int
getuid(__unused struct proc *p, __unused struct getuid_args *uap, register_t *retval)
{
*retval = kauth_getruid();
return (0);
}
int
geteuid(__unused struct proc *p, __unused struct geteuid_args *uap, register_t *retval)
{
*retval = kauth_getuid();
return (0);
}
int
gettid(__unused struct proc *p, struct gettid_args *uap, register_t *retval)
{
struct uthread *uthread = get_bsdthread_info(current_thread());
int error;
if (!(uthread->uu_flag & UT_SETUID))
return (ESRCH);
if ((error = suword(uap->uidp, uthread->uu_ucred->cr_ruid)))
return (error);
if ((error = suword(uap->gidp, uthread->uu_ucred->cr_rgid)))
return (error);
*retval = 0;
return (0);
}
int
getgid(__unused struct proc *p, __unused struct getgid_args *uap, register_t *retval)
{
*retval = kauth_getrgid();
return (0);
}
int
getegid(struct proc *p, __unused struct getegid_args *uap, register_t *retval)
{
*retval = kauth_getgid();
return (0);
}
int
getgroups(__unused struct proc *p, struct getgroups_args *uap, register_t *retval)
{
register int ngrp;
int error;
kauth_cred_t cred;
cred = kauth_cred_get_with_ref();
if ((ngrp = uap->gidsetsize) == 0) {
*retval = cred->cr_ngroups;
kauth_cred_rele(cred);
return (0);
}
if (ngrp < cred->cr_ngroups) {
kauth_cred_rele(cred);
return (EINVAL);
}
ngrp = cred->cr_ngroups;
if ((error = copyout((caddr_t)cred->cr_groups,
uap->gidset,
ngrp * sizeof(gid_t)))) {
kauth_cred_rele(cred);
return (error);
}
kauth_cred_rele(cred);
*retval = ngrp;
return (0);
}
#warning XXX implement
int
getsgroups(__unused struct proc *p, __unused struct getsgroups_args *uap, __unused register_t *retval)
{
return(ENOTSUP);
}
#warning XXX implement
int
getwgroups(__unused struct proc *p, __unused struct getwgroups_args *uap, __unused register_t *retval)
{
return(ENOTSUP);
}
int
setsid(struct proc *p, __unused struct setsid_args *uap, register_t *retval)
{
if (p->p_pgid == p->p_pid || pgfind(p->p_pid) || p->p_flag & P_INVFORK) {
return (EPERM);
} else {
(void)enterpgrp(p, p->p_pid, 1);
*retval = p->p_pid;
return (0);
}
}
int
setpgid(struct proc *curp, register struct setpgid_args *uap, __unused register_t *retval)
{
register struct proc *targp;
register struct pgrp *pgrp;
if (uap->pid != 0 && uap->pid != curp->p_pid) {
if ((targp = pfind(uap->pid)) == 0 || !inferior(targp))
return (ESRCH);
if (targp->p_session != curp->p_session)
return (EPERM);
if (targp->p_flag & P_EXEC)
return (EACCES);
} else
targp = curp;
if (SESS_LEADER(targp))
return (EPERM);
if (uap->pgid < 0)
return(EINVAL);
if (uap->pgid == 0)
uap->pgid = targp->p_pid;
else if (uap->pgid != targp->p_pid)
if ((pgrp = pgfind(uap->pgid)) == 0 ||
pgrp->pg_session != curp->p_session)
return (EPERM);
return (enterpgrp(targp, uap->pgid, 0));
}
int
issetugid(struct proc *p, __unused struct issetugid_args *uap, register_t *retval)
{
*retval = (p->p_flag & P_SUGID) ? 1 : 0;
return (0);
}
int
setuid(struct proc *p, struct setuid_args *uap, __unused register_t *retval)
{
register uid_t uid;
int error;
kauth_cred_t my_cred, my_new_cred;
uid = uap->uid;
AUDIT_ARG(uid, uid, 0, 0, 0);
if (uid != p->p_ucred->cr_ruid &&
(error = suser(p->p_ucred, &p->p_acflag)))
return (error);
prepare_profile_database(uap->uid);
(void)chgproccnt(kauth_getruid(), -1);
(void)chgproccnt(uid, 1);
for (;;) {
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_setuid(my_cred, uid);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_rele(my_cred);
kauth_cred_rele(my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
p->p_flag |= P_SUGID;
proc_unlock(p);
}
kauth_cred_rele(my_cred);
break;
}
set_security_token(p);
return (0);
}
int
seteuid(struct proc *p, struct seteuid_args *uap, __unused register_t *retval)
{
register uid_t euid;
int error;
kauth_cred_t my_cred, my_new_cred;
euid = uap->euid;
AUDIT_ARG(uid, 0, euid, 0, 0);
if (euid != p->p_ucred->cr_ruid && euid != p->p_ucred->cr_svuid &&
(error = suser(p->p_ucred, &p->p_acflag)))
return (error);
for (;;) {
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_seteuid(p->p_ucred, euid);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_rele(my_cred);
kauth_cred_rele(my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
p->p_flag |= P_SUGID;
proc_unlock(p);
}
kauth_cred_rele(my_cred);
break;
}
set_security_token(p);
return (0);
}
int
setgid(struct proc *p, struct setgid_args *uap, __unused register_t *retval)
{
register gid_t gid;
int error;
kauth_cred_t my_cred, my_new_cred;
gid = uap->gid;
AUDIT_ARG(gid, gid, 0, 0, 0);
if (gid != p->p_ucred->cr_rgid && (error = suser(p->p_ucred, &p->p_acflag)))
return (error);
for (;;) {
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_setgid(p->p_ucred, gid);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_rele(my_cred);
kauth_cred_rele(my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
p->p_flag |= P_SUGID;
proc_unlock(p);
}
kauth_cred_rele(my_cred);
break;
}
set_security_token(p);
return (0);
}
int
setegid(struct proc *p, struct setegid_args *uap, __unused register_t *retval)
{
register gid_t egid;
int error;
kauth_cred_t my_cred, my_new_cred;
egid = uap->egid;
AUDIT_ARG(gid, 0, egid, 0, 0);
if (egid != p->p_ucred->cr_rgid && egid != p->p_ucred->cr_svgid &&
(error = suser(p->p_ucred, &p->p_acflag)))
return (error);
for (;;) {
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_setegid(p->p_ucred, egid);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_rele(my_cred);
kauth_cred_rele(my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
p->p_flag |= P_SUGID;
proc_unlock(p);
}
kauth_cred_rele(my_cred);
break;
}
set_security_token(p);
return (0);
}
int
settid(struct proc *p, struct settid_args *uap, __unused register_t *retval)
{
kauth_cred_t uc;
struct uthread *uthread = get_bsdthread_info(current_thread());
register uid_t uid;
register gid_t gid;
uid = uap->uid;
gid = uap->gid;
AUDIT_ARG(uid, uid, gid, gid, 0);
if (suser(p->p_ucred, &p->p_acflag) != 0) {
return (EPERM);
}
if (uid == KAUTH_UID_NONE) {
if ((uthread->uu_flag & UT_SETUID) == 0)
return (EPERM);
uc = kauth_cred_proc_ref(p);
kauth_cred_rele(uthread->uu_ucred);
uthread->uu_ucred = uc;
uthread->uu_flag &= ~UT_SETUID;
} else {
kauth_cred_t my_cred, my_new_cred;
if ((uthread->uu_flag & UT_SETUID) != 0) {
return (EPERM);
}
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;
kauth_cred_rele(my_cred);
}
return (0);
}
int
settid_with_pid(struct proc *p, struct settid_with_pid_args *uap, __unused register_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;
AUDIT_ARG(pid, uap->pid);
AUDIT_ARG(value, uap->assume);
if (suser(p->p_ucred, &p->p_acflag) != 0) {
return (EPERM);
}
if (uap->assume != 0) {
if ((uthread->uu_flag & UT_SETUID) != 0)
return (EPERM);
target_proc = pfind(uap->pid);
if (target_proc == NULL || target_proc == kernproc) {
return (ESRCH);
}
kauth_cred_ref(uthread->uu_ucred);
my_cred = uthread->uu_ucred;
my_target_cred = kauth_cred_proc_ref(target_proc);
my_new_cred = kauth_cred_setuidgid(my_cred, my_target_cred->cr_uid, my_target_cred->cr_gid);
if (my_cred != my_new_cred)
uthread->uu_ucred = my_new_cred;
uthread->uu_flag |= UT_SETUID;
kauth_cred_rele(my_cred);
kauth_cred_rele(my_target_cred);
return (0);
}
if ((uthread->uu_flag & UT_SETUID) == 0)
return (EPERM);
my_new_cred = kauth_cred_proc_ref(p);
kauth_cred_rele(uthread->uu_ucred);
uthread->uu_ucred = my_new_cred;
uthread->uu_flag &= ~UT_SETUID;
return (0);
}
static int
setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused register_t *retval)
{
register 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());
if ((error = suser(p->p_ucred, &p->p_acflag)))
return (error);
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);
}
}
if ((uthread->uu_flag & UT_SETUID) != 0) {
my_cred = uthread->uu_ucred;
uthread->uu_ucred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, my_cred->cr_gmuid);
} else {
for (;;) {
my_cred = kauth_cred_proc_ref(p);
my_new_cred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid);
if (my_cred != my_new_cred) {
proc_lock(p);
if (p->p_ucred != my_cred) {
proc_unlock(p);
kauth_cred_rele(my_cred);
kauth_cred_rele(my_new_cred);
continue;
}
p->p_ucred = my_new_cred;
p->p_flag |= P_SUGID;
proc_unlock(p);
}
kauth_cred_rele(my_cred);
break;
}
AUDIT_ARG(groupset, p->p_ucred->cr_groups, ngrp);
set_security_token(p);
}
return (0);
}
int
initgroups(struct proc *p, struct initgroups_args *uap, __unused register_t *retval)
{
return(setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval));
}
int
setgroups(struct proc *p, struct setgroups_args *uap, __unused register_t *retval)
{
return(setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval));
}
#warning XXX implement
int
setsgroups(__unused struct proc *p, __unused struct setsgroups_args *uap, __unused register_t *retval)
{
return(ENOTSUP);
}
#warning XXX implement
int
setwgroups(__unused struct proc *p, __unused struct setwgroups_args *uap, __unused register_t *retval)
{
return(ENOTSUP);
}
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);
}
int
suser(kauth_cred_t cred, u_short *acflag)
{
#if DIAGNOSTIC
if (cred == NOCRED || cred == FSCRED)
panic("suser");
#endif
if (kauth_cred_getuid(cred) == 0) {
if (acflag)
*acflag |= ASU;
return (0);
}
return (EPERM);
}
int
is_suser(void)
{
struct proc *p = current_proc();
if (!p)
return (0);
return (suser(p->p_ucred, &p->p_acflag) == 0);
}
int
is_suser1(void)
{
struct proc *p = current_proc();
if (!p)
return (0);
return (suser(p->p_ucred, &p->p_acflag) == 0 ||
p->p_ucred->cr_ruid == 0 || p->p_ucred->cr_svuid == 0);
}
int
getlogin(struct proc *p, struct getlogin_args *uap, __unused register_t *retval)
{
if (uap->namelen > sizeof (p->p_pgrp->pg_session->s_login))
uap->namelen = sizeof (p->p_pgrp->pg_session->s_login);
return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
uap->namebuf, uap->namelen));
}
int
setlogin(struct proc *p, struct setlogin_args *uap, __unused register_t *retval)
{
int error;
int dummy=0;
if ((error = suser(p->p_ucred, &p->p_acflag)))
return (error);
error = copyinstr(uap->namebuf,
(caddr_t) p->p_pgrp->pg_session->s_login,
sizeof (p->p_pgrp->pg_session->s_login) - 1, (size_t *)&dummy);
if (!error)
AUDIT_ARG(text, p->p_pgrp->pg_session->s_login);
else if (error == ENAMETOOLONG)
error = EINVAL;
return (error);
}
int
set_security_token(struct proc * p)
{
security_token_t sec_token;
audit_token_t audit_token;
if (p->task == current_task()) {
uthread_t uthread;
uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread->uu_flag & UT_VFORK)
return (1);
}
if (p->p_ucred != NOCRED && p->p_ucred != FSCRED) {
sec_token.val[0] = kauth_cred_getuid(p->p_ucred);
sec_token.val[1] = p->p_ucred->cr_gid;
} else {
sec_token.val[0] = 0;
sec_token.val[1] = 0;
}
audit_token.val[0] = p->p_ucred->cr_au.ai_auid;
audit_token.val[1] = p->p_ucred->cr_uid;
audit_token.val[2] = p->p_ucred->cr_gid;
audit_token.val[3] = p->p_ucred->cr_ruid;
audit_token.val[4] = p->p_ucred->cr_rgid;
audit_token.val[5] = p->p_pid;
audit_token.val[6] = p->p_ucred->cr_au.ai_asid;
audit_token.val[7] = p->p_ucred->cr_au.ai_termid.port;
return (host_security_set_task_token(host_security_self(),
p->task,
sec_token,
audit_token,
(sec_token.val[0]) ?
HOST_PRIV_NULL :
host_priv_self()) != KERN_SUCCESS);
}
__private_extern__
void
cru2x(kauth_cred_t cr, struct xucred *xcr)
{
bzero(xcr, sizeof(*xcr));
xcr->cr_version = XUCRED_VERSION;
xcr->cr_uid = kauth_cred_getuid(cr);
xcr->cr_ngroups = cr->cr_ngroups;
bcopy(cr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups));
}