#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/systm.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/pool.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <machine/tcb.h>
inline void
crset(struct ucred *newcr, const struct ucred *cr)
{
KASSERT(cr->cr_refcnt.r_refs > 0);
memcpy(
(char *)newcr + offsetof(struct ucred, cr_startcopy),
(const char *)cr + offsetof(struct ucred, cr_startcopy),
sizeof(*cr) - offsetof(struct ucred, cr_startcopy));
}
int
sys_getpid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_p->ps_pid;
return (0);
}
int
sys_getthrid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_tid + THREAD_PID_OFFSET;
return (0);
}
int
sys_getppid(struct proc *p, void *v, register_t *retval)
{
mtx_enter(&p->p_p->ps_mtx);
*retval = p->p_p->ps_ppid;
mtx_leave(&p->p_p->ps_mtx);
return (0);
}
int
sys_getpgrp(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_p->ps_pgrp->pg_id;
return (0);
}
int
sys_getpgid(struct proc *curp, void *v, register_t *retval)
{
struct sys_getpgid_args
*uap = v;
struct process *targpr = curp->p_p;
if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
goto found;
if ((targpr = prfind(SCARG(uap, pid))) == NULL)
return (ESRCH);
if (targpr->ps_session != curp->p_p->ps_session)
return (EPERM);
found:
*retval = targpr->ps_pgid;
return (0);
}
int
sys_getsid(struct proc *curp, void *v, register_t *retval)
{
struct sys_getsid_args
*uap = v;
struct process *targpr = curp->p_p;
if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
goto found;
if ((targpr = prfind(SCARG(uap, pid))) == NULL)
return (ESRCH);
if (targpr->ps_session != curp->p_p->ps_session)
return (EPERM);
found:
if (targpr->ps_pgrp->pg_session->s_leader == NULL)
return (ESRCH);
*retval = targpr->ps_pgrp->pg_session->s_leader->ps_pid;
return (0);
}
int
sys_getuid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_ruid;
return (0);
}
int
sys_geteuid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_uid;
return (0);
}
int
sys_issetugid(struct proc *p, void *v, register_t *retval)
{
if (p->p_p->ps_flags & PS_SUGIDEXEC)
*retval = 1;
else
*retval = 0;
return (0);
}
int
sys_getgid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_rgid;
return (0);
}
int
sys_getegid(struct proc *p, void *v, register_t *retval)
{
*retval = p->p_ucred->cr_gid;
return (0);
}
int
sys_getgroups(struct proc *p, void *v, register_t *retval)
{
struct sys_getgroups_args
*uap = v;
struct ucred *uc = p->p_ucred;
int ngrp;
int error;
if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
*retval = uc->cr_ngroups;
return (0);
}
if (ngrp < uc->cr_ngroups)
return (EINVAL);
ngrp = uc->cr_ngroups;
error = copyout(uc->cr_groups, SCARG(uap, gidset),
ngrp * sizeof(gid_t));
if (error)
return (error);
*retval = ngrp;
return (0);
}
int
sys_setsid(struct proc *p, void *v, register_t *retval)
{
struct session *newsess;
struct pgrp *newpgrp;
struct process *pr = p->p_p;
pid_t pid = pr->ps_pid;
newsess = pool_get(&session_pool, PR_WAITOK);
newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
if (pr->ps_pgid == pid || pgfind(pid) != NULL) {
pool_put(&pgrp_pool, newpgrp);
pool_put(&session_pool, newsess);
return (EPERM);
} else {
enternewpgrp(pr, newpgrp, newsess);
*retval = pid;
return (0);
}
}
int
sys_setpgid(struct proc *curp, void *v, register_t *retval)
{
struct sys_setpgid_args
*uap = v;
struct process *curpr = curp->p_p;
struct process *targpr;
struct pgrp *pgrp, *newpgrp;
pid_t pid, pgid;
int error;
pid = SCARG(uap, pid);
pgid = SCARG(uap, pgid);
if (pgid < 0)
return (EINVAL);
newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
if (pid != 0 && pid != curpr->ps_pid) {
if ((targpr = prfind(pid)) == NULL ||
!inferior(targpr, curpr)) {
error = ESRCH;
goto out;
}
if (targpr->ps_session != curpr->ps_session) {
error = EPERM;
goto out;
}
if (targpr->ps_flags & PS_EXEC) {
error = EACCES;
goto out;
}
} else
targpr = curpr;
if (SESS_LEADER(targpr)) {
error = EPERM;
goto out;
}
if (pgid == 0)
pgid = targpr->ps_pid;
error = 0;
if ((pgrp = pgfind(pgid)) == NULL) {
if (pgid != targpr->ps_pid)
error = EPERM;
else {
enternewpgrp(targpr, newpgrp, NULL);
newpgrp = NULL;
}
} else if (pgrp != targpr->ps_pgrp) {
if (pgid != targpr->ps_pid &&
pgrp->pg_session != curpr->ps_session)
error = EPERM;
else
enterthispgrp(targpr, pgrp);
}
out:
if (newpgrp != NULL)
pool_put(&pgrp_pool, newpgrp);
return (error);
}
int
sys_getresuid(struct proc *p, void *v, register_t *retval)
{
struct sys_getresuid_args
*uap = v;
struct ucred *uc = p->p_ucred;
uid_t *ruid, *euid, *suid;
int error1 = 0, error2 = 0, error3 = 0;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
suid = SCARG(uap, suid);
if (ruid != NULL)
error1 = copyout(&uc->cr_ruid, ruid, sizeof(*ruid));
if (euid != NULL)
error2 = copyout(&uc->cr_uid, euid, sizeof(*euid));
if (suid != NULL)
error3 = copyout(&uc->cr_svuid, suid, sizeof(*suid));
return (error1 ? error1 : error2 ? error2 : error3);
}
int
sys_setresuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setresuid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t ruid, euid, suid;
int error;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
suid = SCARG(uap, suid);
pruc = pr->ps_ucred;
if ((ruid == (uid_t)-1 || ruid == pruc->cr_ruid) &&
(euid == (uid_t)-1 || euid == pruc->cr_uid) &&
(suid == (uid_t)-1 || suid == pruc->cr_svuid))
return (0);
if (ruid != (uid_t)-1 &&
ruid != uc->cr_ruid &&
ruid != uc->cr_uid &&
ruid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (euid != (uid_t)-1 &&
euid != uc->cr_ruid &&
euid != uc->cr_uid &&
euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (suid != (uid_t)-1 &&
suid != uc->cr_ruid &&
suid != uc->cr_uid &&
suid != uc->cr_svuid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (ruid != (uid_t)-1)
newcred->cr_ruid = ruid;
if (euid != (uid_t)-1)
newcred->cr_uid = euid;
if (suid != (uid_t)-1)
newcred->cr_svuid = suid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(ruid, 1);
}
crfree(pruc);
return (0);
}
int
sys_getresgid(struct proc *p, void *v, register_t *retval)
{
struct sys_getresgid_args
*uap = v;
struct ucred *uc = p->p_ucred;
gid_t *rgid, *egid, *sgid;
int error1 = 0, error2 = 0, error3 = 0;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
sgid = SCARG(uap, sgid);
if (rgid != NULL)
error1 = copyout(&uc->cr_rgid, rgid, sizeof(*rgid));
if (egid != NULL)
error2 = copyout(&uc->cr_gid, egid, sizeof(*egid));
if (sgid != NULL)
error3 = copyout(&uc->cr_svgid, sgid, sizeof(*sgid));
return (error1 ? error1 : error2 ? error2 : error3);
}
int
sys_setresgid(struct proc *p, void *v, register_t *retval)
{
struct sys_setresgid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t rgid, egid, sgid;
int error;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
sgid = SCARG(uap, sgid);
pruc = pr->ps_ucred;
if ((rgid == (gid_t)-1 || rgid == pruc->cr_rgid) &&
(egid == (gid_t)-1 || egid == pruc->cr_gid) &&
(sgid == (gid_t)-1 || sgid == pruc->cr_svgid))
return (0);
if (rgid != (gid_t)-1 &&
rgid != uc->cr_rgid &&
rgid != uc->cr_gid &&
rgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (egid != (gid_t)-1 &&
egid != uc->cr_rgid &&
egid != uc->cr_gid &&
egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (sgid != (gid_t)-1 &&
sgid != uc->cr_rgid &&
sgid != uc->cr_gid &&
sgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (rgid != (gid_t)-1)
newcred->cr_rgid = rgid;
if (egid != (gid_t)-1)
newcred->cr_gid = egid;
if (sgid != (gid_t)-1)
newcred->cr_svgid = sgid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setregid(struct proc *p, void *v, register_t *retval)
{
struct sys_setregid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t rgid, egid;
int error;
rgid = SCARG(uap, rgid);
egid = SCARG(uap, egid);
pruc = pr->ps_ucred;
if ((rgid == (gid_t)-1 || rgid == pruc->cr_rgid) &&
(egid == (gid_t)-1 || egid == pruc->cr_gid) &&
(rgid == (gid_t)-1 || (rgid == pruc->cr_rgid &&
pruc->cr_svgid == (egid != (gid_t)-1 ? egid : pruc->cr_gid))))
return (0);
if (rgid != (gid_t)-1 &&
rgid != uc->cr_rgid &&
rgid != uc->cr_gid &&
rgid != uc->cr_svgid &&
(error = suser(p)))
return (error);
if (egid != (gid_t)-1 &&
egid != uc->cr_rgid &&
egid != uc->cr_gid &&
egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (rgid != (gid_t)-1)
newcred->cr_rgid = rgid;
if (egid != (gid_t)-1)
newcred->cr_gid = egid;
if (rgid != (gid_t)-1 && (rgid != pruc->cr_rgid ||
pruc->cr_svgid != (egid != (gid_t)-1 ? egid : pruc->cr_gid)))
newcred->cr_svgid = rgid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setreuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setreuid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t ruid, euid;
int error;
ruid = SCARG(uap, ruid);
euid = SCARG(uap, euid);
pruc = pr->ps_ucred;
if ((ruid == (uid_t)-1 || ruid == pruc->cr_ruid) &&
(euid == (uid_t)-1 || euid == pruc->cr_uid) &&
(ruid == (uid_t)-1 || (ruid == pruc->cr_ruid &&
pruc->cr_svuid == (euid != (uid_t)-1 ? euid : pruc->cr_uid))))
return (0);
if (ruid != (uid_t)-1 &&
ruid != uc->cr_ruid &&
ruid != uc->cr_uid &&
ruid != uc->cr_svuid &&
(error = suser(p)))
return (error);
if (euid != (uid_t)-1 &&
euid != uc->cr_ruid &&
euid != uc->cr_uid &&
euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (ruid != (uid_t)-1)
newcred->cr_ruid = ruid;
if (euid != (uid_t)-1)
newcred->cr_uid = euid;
if (ruid != (uid_t)-1 && (ruid != pruc->cr_ruid ||
pruc->cr_svuid != (euid != (uid_t)-1 ? euid : pruc->cr_uid)))
newcred->cr_svuid = ruid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(ruid, 1);
}
crfree(pruc);
return (0);
}
int
sys_setuid(struct proc *p, void *v, register_t *retval)
{
struct sys_setuid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t uid;
int did_real, error;
uid = SCARG(uap, uid);
pruc = pr->ps_ucred;
if (pruc->cr_uid == uid &&
pruc->cr_ruid == uid &&
pruc->cr_svuid == uid)
return (0);
if (uid != uc->cr_ruid &&
uid != uc->cr_svuid &&
uid != uc->cr_uid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (uid == pruc->cr_uid || suser(p) == 0) {
did_real = 1;
newcred->cr_ruid = uid;
newcred->cr_svuid = uid;
} else
did_real = 0;
newcred->cr_uid = uid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
if (did_real && uid != pruc->cr_ruid) {
chgproccnt(pruc->cr_ruid, -1);
chgproccnt(uid, 1);
}
crfree(pruc);
return (0);
}
int
sys_seteuid(struct proc *p, void *v, register_t *retval)
{
struct sys_seteuid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
uid_t euid;
int error;
euid = SCARG(uap, euid);
if (pr->ps_ucred->cr_uid == euid)
return (0);
if (euid != uc->cr_ruid && euid != uc->cr_svuid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
newcred->cr_uid = euid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setgid(struct proc *p, void *v, register_t *retval)
{
struct sys_setgid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t gid;
int error;
gid = SCARG(uap, gid);
pruc = pr->ps_ucred;
if (pruc->cr_gid == gid &&
pruc->cr_rgid == gid &&
pruc->cr_svgid == gid)
return (0);
if (gid != uc->cr_rgid &&
gid != uc->cr_svgid &&
gid != uc->cr_gid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
if (gid == pruc->cr_gid || suser(p) == 0) {
newcred->cr_rgid = gid;
newcred->cr_svgid = gid;
}
newcred->cr_gid = gid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setegid(struct proc *p, void *v, register_t *retval)
{
struct sys_setegid_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred, *uc = p->p_ucred;
gid_t egid;
int error;
egid = SCARG(uap, egid);
if (pr->ps_ucred->cr_gid == egid)
return (0);
if (egid != uc->cr_rgid && egid != uc->cr_svgid &&
(error = suser(p)))
return (error);
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
newcred->cr_gid = egid;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
return (0);
}
int
sys_setgroups(struct proc *p, void *v, register_t *retval)
{
struct sys_setgroups_args
*uap = v;
struct process *pr = p->p_p;
struct ucred *pruc, *newcred;
gid_t groups[NGROUPS_MAX];
int ngrp;
int error;
if ((error = suser(p)) != 0)
return (error);
ngrp = SCARG(uap, gidsetsize);
if (ngrp > NGROUPS_MAX || ngrp < 0)
return (EINVAL);
error = copyin(SCARG(uap, gidset), groups, ngrp * sizeof(gid_t));
if (error == 0) {
newcred = crget();
pruc = pr->ps_ucred;
crset(newcred, pruc);
memcpy(newcred->cr_groups, groups, ngrp * sizeof(gid_t));
newcred->cr_ngroups = ngrp;
pr->ps_ucred = newcred;
atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
crfree(pruc);
}
return (error);
}
int
groupmember(gid_t gid, struct ucred *cred)
{
gid_t *gp;
gid_t *egp;
if (cred->cr_gid == gid)
return (1);
egp = &(cred->cr_groups[cred->cr_ngroups]);
for (gp = cred->cr_groups; gp < egp; gp++)
if (*gp == gid)
return (1);
return (0);
}
int
suser(struct proc *p)
{
struct ucred *cred = p->p_ucred;
if (cred->cr_uid == 0)
return (0);
return (EPERM);
}
int
suser_ucred(struct ucred *cred)
{
if (cred->cr_uid == 0)
return (0);
return (EPERM);
}
struct ucred *
crget(void)
{
struct ucred *cr;
cr = pool_get(&ucred_pool, PR_WAITOK|PR_ZERO);
refcnt_init(&cr->cr_refcnt);
return (cr);
}
struct ucred *
crhold(struct ucred *cr)
{
refcnt_take(&cr->cr_refcnt);
return (cr);
}
void
crfree(struct ucred *cr)
{
if (refcnt_rele(&cr->cr_refcnt))
pool_put(&ucred_pool, cr);
}
struct ucred *
crcopy(struct ucred *cr)
{
struct ucred *newcr;
if (!refcnt_shared(&cr->cr_refcnt))
return (cr);
newcr = crget();
*newcr = *cr;
crfree(cr);
refcnt_init(&newcr->cr_refcnt);
return (newcr);
}
struct ucred *
crdup(struct ucred *cr)
{
struct ucred *newcr;
newcr = crget();
*newcr = *cr;
refcnt_init(&newcr->cr_refcnt);
return (newcr);
}
int
crfromxucred(struct ucred *cr, const struct xucred *xcr)
{
if (xcr->cr_ngroups < 0 || xcr->cr_ngroups > NGROUPS_MAX)
return (EINVAL);
refcnt_init(&cr->cr_refcnt);
cr->cr_uid = xcr->cr_uid;
cr->cr_gid = xcr->cr_gid;
cr->cr_ngroups = xcr->cr_ngroups;
memcpy(cr->cr_groups, xcr->cr_groups,
sizeof(cr->cr_groups[0]) * xcr->cr_ngroups);
return (0);
}
int
sys_getlogin_r(struct proc *p, void *v, register_t *retval)
{
struct sys_getlogin_r_args
*uap = v;
size_t namelen = SCARG(uap, namelen);
struct session *s = p->p_p->ps_pgrp->pg_session;
int error;
if (namelen > sizeof(s->s_login))
namelen = sizeof(s->s_login);
error = copyoutstr(s->s_login, SCARG(uap, namebuf), namelen, NULL);
if (error == ENAMETOOLONG)
error = ERANGE;
*retval = error;
return (0);
}
int
sys_setlogin(struct proc *p, void *v, register_t *retval)
{
struct sys_setlogin_args
*uap = v;
struct session *s = p->p_p->ps_pgrp->pg_session;
char buf[sizeof(s->s_login)];
int error;
if ((error = suser(p)) != 0)
return (error);
error = copyinstr(SCARG(uap, namebuf), buf, sizeof(buf), NULL);
if (error == 0)
strlcpy(s->s_login, buf, sizeof(s->s_login));
else if (error == ENAMETOOLONG)
error = EINVAL;
return (error);
}
int
proc_cansugid(struct proc *p)
{
if ((p->p_p->ps_flags & PS_TRACED) != 0)
return (0);
if (p->p_fd->fd_refcnt > 1)
return (0);
return (1);
}
int
sys___set_tcb(struct proc *p, void *v, register_t *retval)
{
struct sys___set_tcb_args
*uap = v;
void *tcb = SCARG(uap, tcb);
#ifdef TCB_INVALID
if (TCB_INVALID(tcb))
return EINVAL;
#endif
TCB_SET(p, tcb);
return (0);
}
int
sys___get_tcb(struct proc *p, void *v, register_t *retval)
{
*retval = (register_t)TCB_GET(p);
return (0);
}
int
sys_getthrname(struct proc *curp, void *v, register_t *retval)
{
struct sys_getthrname_args
*uap = v;
struct proc *p;
size_t len;
int tid = SCARG(uap, tid);
int error;
p = tid ? tfind_user(tid, curp->p_p) : curp;
if (p == NULL)
return ESRCH;
len = SCARG(uap, len);
if (len > sizeof(p->p_name))
len = sizeof(p->p_name);
error = copyoutstr(p->p_name, SCARG(uap, name), len, NULL);
if (error == ENAMETOOLONG)
error = ERANGE;
*retval = error;
return 0;
}
int
sys_setthrname(struct proc *curp, void *v, register_t *retval)
{
struct sys_setthrname_args
*uap = v;
struct proc *p;
char buf[sizeof p->p_name];
int tid = SCARG(uap, tid);
int error;
p = tid ? tfind_user(tid, curp->p_p) : curp;
if (p == NULL)
return ESRCH;
error = copyinstr(SCARG(uap, name), buf, sizeof buf, NULL);
if (error == ENAMETOOLONG) {
buf[sizeof(buf) - 1] = '\0';
error = 0;
}
if (error == 0)
strlcpy(p->p_name, buf, sizeof(p->p_name));
*retval = error;
return 0;
}
void
dorefreshcreds(struct process *pr, struct proc *p)
{
struct ucred *uc = p->p_ucred;
KERNEL_LOCK();
if (uc != pr->ps_ucred) {
p->p_ucred = pr->ps_ucred;
crhold(p->p_ucred);
crfree(uc);
}
KERNEL_UNLOCK();
}