Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

741 lines
15 KiB
C

/* @(#)kern_prot.c 1.1 92/07/30 SMI; from UCB 5.17 83/05/27 */
/*
* System calls related to processes and protection
*/
#include <machine/reg.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/session.h>
#include <sys/timeb.h>
#include <sys/times.h>
#include <sys/reboot.h>
#include <sys/buf.h>
#include <sys/acct.h>
#include <sys/debug.h>
getpid()
{
u.u_r.r_val1 = u.u_procp->p_pid;
u.u_r.r_val2 = u.u_procp->p_ppid;
}
getpgrp()
{
register struct a {
int pid;
} *uap = (struct a *)u.u_ap;
register struct proc *p;
if (uap->pid == 0)
uap->pid = u.u_procp->p_pid;
p = pfind(uap->pid);
if (p == 0) {
u.u_error = ESRCH;
return;
}
u.u_r.r_val1 = p->p_pgrp;
}
getuid()
{
u.u_r.r_val1 = u.u_ruid;
u.u_r.r_val2 = u.u_uid;
}
getgid()
{
u.u_r.r_val1 = u.u_rgid;
u.u_r.r_val2 = u.u_gid;
}
getgroups()
{
register struct a {
int gidsetsize;
int *gidset;
} *uap = (struct a *)u.u_ap;
register int *gp;
for (gp = &u.u_groups[NGROUPS]; gp > u.u_groups; gp--)
if (gp[-1] != NOGROUP)
break;
if (uap->gidsetsize == 0) {
u.u_r.r_val1 = gp - u.u_groups;
return;
}
if (uap->gidsetsize < gp - u.u_groups) {
u.u_error = EINVAL;
return;
}
uap->gidsetsize = gp - u.u_groups;
u.u_error = copyout((caddr_t)u.u_groups, (caddr_t)uap->gidset,
(u_int)(uap->gidsetsize * sizeof (u.u_groups[0])));
if (u.u_error)
return;
u.u_r.r_val1 = uap->gidsetsize;
}
sys_setsid()
{
register struct a {
int flag;
} *uap = (struct a *)u.u_ap;
int ret;
if (ret = setsid(uap->flag)) {
u.u_error = ret;
} else {
u.u_rval1 = u.u_procp->p_pgrp;
u.u_procp->p_flag |= SSETSID;
}
}
struct sess sess0; /* default session */
struct sess* allsess = &sess0; /* linked list; sess0 always on end */
/*
* setsid() - implements s5 setpgrp() and posix setsid()
*
* Make a process the session & the process group leader.
* check that this process is not already a session leader.
* check that there is not an existing process group == u.u_pid.
* detach it from its current controlling terminal.
* orphan it & check for possible orphaned children.
* allocate & initialize a session.
*
* N.B. The flag needs to be SESS_NEW for POSIX semantics.
* SESS_SYS is used to return to a BSD like startup state.
*/
setsid(flag) /* tags note: you probably want sys_setsid() */
{
register struct proc *p;
register struct proc *p2;
register struct sess *sp;
register pid;
int s;
p = u.u_procp;
sp = p->p_sessp;
pid = p->p_pid;
switch (flag) {
case SESS_NEW:
if (pid == sp->s_sid && sp->s_sid != SESS_SYS) {
/*
* Allow a process to call setsid() if it hasn't called
* it before.
* XXX - compat kludge; rm for 5.0
*/
if (p->p_flag & SSETSID) /* this is not the first time */
return EPERM;
if (sp->s_members != 1)
return (EPERM);
if (sp->s_vp != NULL)
return (EPERM);
return (0);
}
for (p2 = pgfind(p->p_pgrp); p2; p2 = pgnext(p2)) {
if (p2->p_pgrp == pid && p2 != p)
return (EPERM);
}
sp = (struct sess *)new_kmem_zalloc(sizeof (*sp), KMEM_SLEEP);
break;
case SESS_SYS:
if (pid == sp->s_sid && sp->s_members > 1)
return (EINVAL);
break;
default:
return (EINVAL);
}
/*
* protected because you should always be able to dereference
* u.u_procp->p_sessp->*
*/
s = splimp();
if (flag == SESS_SYS) {
sp = &sess0;
pid = 0;
} else {
sp->s_next = allsess;
allsess = sp;
}
SESS_EXIT(p);
pgexit(p);
pgenter(p, pid);
SESS_ORPH(p);
SESS_ENTER(p, sp);
(void) splx(s);
sp->s_sid = p->p_pgrp = pid;
return (0);
}
/*
* remove session from session list & free it
*/
freesess(sp)
register struct sess *sp;
{
register struct sess *f;
ASSERT(sp->s_sid != SESS_SYS);
ASSERT(sp->s_members == 0);
SESS_VN_RELE(sp);
if (allsess == sp) {
allsess = sp->s_next;
ASSERT(allsess);
} else {
for (f = allsess; f; f = f->s_next) {
if (f->s_next == sp) {
f->s_next = sp->s_next;
break;
}
}
ASSERT(f->s_next == sp->s_next);
}
kmem_free((char*)sp, sizeof (struct sess));
}
/*
* Is the tty a controlling tty?
*/
struct sess*
isactty(vp)
register struct vnode *vp;
{
register struct sess *sp;
if (vp == NULL || vp->v_stream == NULL)
return (NULL);
for (sp = allsess; sp; sp = sp->s_next) {
if (vp == sp->s_vp)
return (sp);
}
return (NULL);
}
/*
* BSD setpgrp()
*
* setpgrp(getpid(), 0) gets mapped to setsid()
* all others get mapped to setpgid()
*/
setpgrp()
{
register struct a {
int pid;
int pgrp;
} *uap = (struct a *)u.u_ap;
/*
* If they are trying to protect themselves from the tty,
* give them a new session.
*/
if (uap->pgrp == 0 &&
((uap->pid == 0) || (uap->pid == u.u_procp->p_pid))) {
(void) setsid(SESS_NEW);
} else {
csetpgid(1);
}
}
setpgid()
{
csetpgid(0);
}
/*
* setpgid() - posix version of setpgrp()
*
* Either create a new pgrp or join an existing one within the session.
*
* Permission checks: (done in this order)
* range check pgrp (EINVAL)
* range check pid & pid must exist (ESRCH)
* pid must be calling process or child thereof (ESRCH)
* pid must not be a session leader (EPERM)
* if pid a child of the calling process, pid can't have execed (EACCES)
* pid must be same session as calling process (EPERM)
* if joining a pgrp, pgrp must be in the same session as pid (EPERM)
*/
csetpgid(setpgrp_flag)
{
register struct a {
int pid;
int pgrp;
} *uap = (struct a *)u.u_ap;
register struct proc *p; /* pid's proc struct */
struct sess *sp; /* pid's session */
register struct proc *tmp;
register pid_t pid; /* fast version of uap->pid */
register pid_t pgrp; /* fast version of uap->pgrp */
if (uap->pgrp < 0 || uap->pgrp >= MAXPID) {
u.u_error = EINVAL;
return;
}
if (uap->pid < 0 || uap->pid >= MAXPID) {
u.u_error = ESRCH;
return;
}
p = (uap->pid && uap->pid != u.u_procp->p_pid) ?
pfind(uap->pid) : u.u_procp;
if (!p) {
u.u_error = ESRCH;
return;
}
sp = p->p_sessp;
pid = p->p_pid;
pgrp = uap->pgrp ? uap->pgrp : pid;
if (setpgrp_flag && p->p_pgrp == pgrp)
return;
if (p != u.u_procp && p->p_pptr != u.u_procp) {
u.u_error = ESRCH;
return;
}
if (pid == sp->s_sid) {
u.u_error = EPERM;
return;
}
if (p->p_pptr == u.u_procp && (p->p_flag & SEXECED)) {
u.u_error = EACCES;
return;
}
if (sp != u.u_procp->p_sessp) {
u.u_error = EPERM;
return;
}
/*
* XXX - clean up for 5.0.
*
* POSIX does not allow setpgid(B, A) if A is not an existing
* pgrp in this session. We need to allow it until the shells
* are rewritten for the following reason: if the first process
* of a pipeline exits quickly, the shell may reap this first
* process, A, before forking the second process, B. Then
* setpgid(B, A) will fail because A, and more importantly
* the pgrp A, is gone. The fix is that the shell should block
* SIGCHLD while forking off the pipeline but ksh does not (csh
* may also be a problem).
*
* Another reason: You may do a pipline A | B and the shell may
* try to set up B with A as a pgrp before setting up A with
* A as a pgrp. Binary compat is a mondo drag.
*
* We loosen the POSIX checks to allow setpgid(B, A) when we
* arrived here via setpgrp(2) and the process in question is
* creating its own pgrp. This is a heuristic but extremely
* likely to succeed for the case discribed above.
* As a further sanity check we don't allow a process to take
* the pgrp that matches an existing process in another session.
*
* Tty pgrps do not come into question since they are no longer
* allowed to have stale values.
*
* See also TIOCSPGRP, TIOCSETPGRP in str_io.c.
*/
if (tmp = pgfind(pgrp)) {
if (tmp->p_sessp != sp) {
u.u_error = EPERM;
return;
}
} else if (pid != pgrp) {
if (setpgrp_flag) {
/*
* XXX - heuristic
*/
if ((tmp = pfind(pgrp)) && tmp->p_sessp != sp) {
u.u_error = EPERM;
return;
}
} else {
u.u_error = EPERM;
return;
}
}
/*
* if we're done, don't bother with the orphan checks
* N.B. This call is here, not at the top, since we want
* the error returns to be valid.
*/
if (p->p_pgrp == pgrp)
return;
/*
* orphan checks
*
* if there are other members of the old pgrp, check that pgrp
* if in sess0 then we are orphaned by definition
* else
* if creating pgrp
* my orphan status depends on my parent
* else (joining a pgrp)
* if I don't have an ok parent
* my status is the group's status
* else
* I am not an orphan
* if the group is orphaned, unorphan them
*/
pgexit(p);
if (pgfind(p->p_pgrp))
(void) orphan_chk(p->p_pgrp);
if (sp->s_sid == SESS_SYS)
p->p_flag |= SORPHAN;
else {
int ok_pp;
ok_pp = p->p_pptr->p_sessp == sp && p->p_pptr->p_stat != SZOMB;
tmp = pgfind(pgrp);
if (!tmp) {
if (ok_pp)
p->p_flag &= ~SORPHAN;
else
p->p_flag |= SORPHAN;
} else {
if (!ok_pp) {
if (tmp->p_flag & SORPHAN)
p->p_flag |= SORPHAN;
else
p->p_flag &= ~SORPHAN;
} else {
p->p_flag &= ~SORPHAN;
if (tmp->p_flag & SORPHAN) {
for ( ; tmp; tmp = pgnext(tmp))
tmp->p_flag &= ~SORPHAN;
}
}
}
}
pgenter(p, pgrp);
p->p_pgrp = pgrp;
}
/*
*
* for all (active) p in process group pgrp
* do if (active) parent is in same session but different pgrp, return 0
* done
* orphan process group pgrp
*
* N.B. Counts on
* 1) If there is a process leaving the pgrp that would orphan it
* it has already been removed.
* 2) If there is a parent process that is leaving the session or exiting
* it has already reset its sessp or is in a zombie state.
*/
orphan_chk(pgrp)
register pgrp;
{
register struct proc *p;
register int stopped;
if ((p = pgfind(pgrp)) == NULL)
return (0);
if (p->p_flag & SORPHAN) /* pgrp orphaned already */
return (0);
for ( ; p; p = pgnext(p)) {
register struct proc *pp;
if ((p->p_stat == SZOMB) || (pp = p->p_pptr)->p_stat == SZOMB)
continue;
if (pp->p_pgrp != pgrp &&
pp->p_sessp == p->p_sessp &&
pp->p_stat != SZOMB)
return (0);
}
stopped = 0;
for (p = pgfind(pgrp); p; p = pgnext(p)) {
p->p_flag |= SORPHAN;
if (p->p_stat == SSTOP && (p->p_flag & STRC) == 0)
stopped++;
}
return (stopped);
}
setreuid()
{
struct a {
int ruid;
int euid;
} *uap;
register int ruid, euid;
uap = (struct a *)u.u_ap;
ruid = uap->ruid;
if (ruid != -1) {
if (!shortval(ruid))
return;
if (u.u_ruid != ruid && u.u_uid != ruid && !suser())
return;
}
euid = uap->euid;
if (euid != -1) {
if (!shortval(euid))
return;
if (u.u_ruid != euid && u.u_uid != euid &&
u.u_procp->p_suid != euid && !suser())
return;
}
/*
* Everything's okay, do it.
*/
u.u_cred = crcopy(u.u_cred);
if (euid != -1)
u.u_uid = euid;
if (ruid != -1) {
u.u_procp->p_uid = ruid;
u.u_ruid = ruid;
}
if (ruid != -1 || u.u_uid != u.u_ruid)
u.u_procp->p_suid = u.u_uid;
}
setregid()
{
register struct a {
int rgid;
int egid;
} *uap;
register int rgid, egid;
uap = (struct a *)u.u_ap;
rgid = uap->rgid;
if (rgid != -1) {
if (!shortval(rgid))
return;
if (u.u_rgid != rgid && u.u_gid != rgid && !suser())
return;
}
egid = uap->egid;
if (egid != -1) {
if (!shortval(egid))
return;
if (u.u_rgid != egid && u.u_gid != egid &&
u.u_procp->p_sgid != egid && !suser())
return;
}
/*
* Everything's okay, do it.
*/
u.u_cred = crcopy(u.u_cred);
if (egid != -1)
u.u_gid = egid;
if (rgid != -1) {
if (u.u_rgid != rgid) {
u.u_rgid = rgid;
}
}
if (rgid != -1 || u.u_gid != u.u_rgid)
u.u_procp->p_sgid = u.u_gid;
}
setgroups()
{
register struct a {
u_int gidsetsize;
int *gidset;
} *uap = (struct a *)u.u_ap;
register int *gp;
struct ucred *newcr, *tmpcr;
if (!suser())
return;
if (uap->gidsetsize > sizeof (u.u_groups) / sizeof (u.u_groups[0])) {
u.u_error = EINVAL;
return;
}
newcr = crdup(u.u_cred);
u.u_error = copyin((caddr_t)uap->gidset, (caddr_t)newcr->cr_groups,
uap->gidsetsize * sizeof (newcr->cr_groups[0]));
if (u.u_error) {
crfree(newcr);
return;
}
tmpcr = u.u_cred;
u.u_cred = newcr;
crfree(tmpcr);
for (gp = &u.u_groups[uap->gidsetsize]; gp < &u.u_groups[NGROUPS]; gp++)
*gp = NOGROUP;
}
/*
* Group utility functions.
*/
#ifdef COMPAT
/*
* Delete gid from the group set.
*/
leavegroup(gid)
int gid;
{
register int *gp;
for (gp = u.u_groups; gp < &u.u_groups[NGROUPS]; gp++)
if (*gp == gid)
goto found;
return;
found:
for (; gp < &u.u_groups[NGROUPS-1]; gp++)
*gp = *(gp+1);
*gp = NOGROUP;
}
/*
* Add gid to the group set.
*/
entergroup(gid)
int gid;
{
register int *gp;
for (gp = u.u_groups; gp < &u.u_groups[NGROUPS]; gp++)
if (*gp == gid)
return (0);
for (gp = u.u_groups; gp < &u.u_groups[NGROUPS]; gp++)
if (*gp < 0) {
*gp = gid;
return (0);
}
return (-1);
}
#endif COMPAT
/*
* Check if gid is a member of the group set.
*/
groupmember(gid)
int gid;
{
register int *gp;
if (u.u_gid == gid)
return (1);
for (gp = u.u_groups; gp < &u.u_groups[NGROUPS] && *gp != NOGROUP; gp++)
if (*gp == gid)
return (1);
return (0);
}
/*
* Test if the current user is the super user.
*/
suser()
{
if (u.u_uid == 0) {
u.u_acflag |= ASU;
return (1);
}
u.u_error = EPERM;
return (0);
}
/*
* Test to see if a user-supplied uid or gid is valid.
*/
shortval(val)
int val;
{
if (((u_int)val & ~(u_int)((u_short)~0)) != 0) {
u.u_error = EINVAL;
return (0);
}
return (1);
}
/*
* Routines to allocate and free credentials structures
*/
int cractive = 0; /* number of active ucred's */
int crallocsz = 0x10; /* number of ucred's to allocate at a time */
struct ucred *crfreelist; /* ucred free list */
/*
* Allocate a zeroed cred structure and crhold it.
*/
struct ucred *
crget()
{
register struct ucred *cr;
if (servicing_interrupt() && panicstr == NULL)
panic("crget");
cr = (struct ucred *)new_kmem_fast_alloc((caddr_t *)&crfreelist,
sizeof (*cr), crallocsz, KMEM_SLEEP);
bzero((caddr_t)cr, sizeof (*cr));
crhold(cr);
cractive++;
return (cr);
}
/*
* Free a cred structure.
* Throws away space when ref count gets to 0.
*/
void
crfree(cr)
struct ucred *cr;
{
if ((servicing_interrupt() && panicstr == NULL) ||
cr == NULL || cr->cr_ref == 0)
panic("crfree");
if (--cr->cr_ref != 0)
return;
kmem_fast_free((caddr_t *)&crfreelist, (caddr_t)cr);
cractive--;
}
/*
* Copy cred structure to a new one and free the old one.
*/
struct ucred *
crcopy(cr)
struct ucred *cr;
{
struct ucred *newcr;
newcr = crget();
*newcr = *cr; /* structure copy */
crfree(cr);
newcr->cr_ref = 1;
return (newcr);
}
/*
* Duplicate cred structure to a new held one.
*/
struct ucred *
crdup(cr)
struct ucred *cr;
{
struct ucred *newcr;
newcr = crget();
*newcr = *cr; /* structure copy */
newcr->cr_ref = 1;
return (newcr);
}
/*
* Return the (held) credentials for the current running process.
*/
struct ucred *
crgetcred()
{
crhold(u.u_cred);
return (u.u_cred);
}