add definition for ability to produce a backtrace
[unix-history] / usr / src / sys / kern / kern_prot.c
index 018aaaa..2e4f483 100644 (file)
-/*     kern_prot.c     5.1     82/07/15        */
+/*
+ * 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.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)kern_prot.c 8.9 (Berkeley) %G%
+ */
+
+/*
+ * 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.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/malloc.h>
+
+#include <sys/mount.h>
+#include <sys/syscallargs.h>
+
+/* ARGSUSED */
+int
+getpid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_pid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_pptr->p_pid;
+#endif
+       return (0);
+}
+
+/* ARGSUSED */
+int
+getppid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_pptr->p_pid;
+       return (0);
+}
+
+/* Get process group ID; note that POSIX getpgrp takes no parameter */
+int
+getpgrp(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_pgrp->pg_id;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+getuid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_cred->p_ruid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_ucred->cr_uid;
+#endif
+       return (0);
+}
+
+/* ARGSUSED */
+int
+geteuid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_ucred->cr_uid;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+getgid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_cred->p_rgid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_ucred->cr_groups[0];
+#endif
+       return (0);
+}
 
 /*
 
 /*
- * System calls related to protection
+ * Get effective group ID.  The "egid" is groups[0], and could be obtained
+ * via getgroups.  This syscall exists because it is somewhat painful to do
+ * correctly in a library function.
  */
  */
+/* ARGSUSED */
+int
+getegid(p, uap, retval)
+       struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       *retval = p->p_ucred->cr_groups[0];
+       return (0);
+}
+
+int
+getgroups(p, uap, retval)
+       struct proc *p;
+       register struct getgroups_args /* {
+               syscallarg(u_int) gidsetsize;
+               syscallarg(gid_t *) gidset;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register u_int ngrp;
+       int error;
+
+       if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
+               *retval = pc->pc_ucred->cr_ngroups;
+               return (0);
+       }
+       if (ngrp < pc->pc_ucred->cr_ngroups)
+               return (EINVAL);
+       ngrp = pc->pc_ucred->cr_ngroups;
+       if (error = copyout((caddr_t)pc->pc_ucred->cr_groups,
+           (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t)))
+               return (error);
+       *retval = ngrp;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+setsid(p, uap, retval)
+       register struct proc *p;
+       void *uap;
+       register_t *retval;
+{
+
+       if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) {
+               return (EPERM);
+       } else {
+               (void)enterpgrp(p, p->p_pid, 1);
+               *retval = p->p_pid;
+               return (0);
+       }
+}
+
+/*
+ * set process group (setpgid/old setpgrp)
+ *
+ * caller does setpgid(targpid, targpgid)
+ *
+ * pid must be caller or child of caller (ESRCH)
+ * if a child
+ *     pid must be in same session (EPERM)
+ *     pid can't have done an exec (EACCES)
+ * if pgid != pid
+ *     there must exist some pid in same session having pgid (EPERM)
+ * pid must not be session leader (EPERM)
+ */
+/* ARGSUSED */
+int
+setpgid(curp, uap, retval)
+       struct proc *curp;
+       register struct setpgid_args /* {
+               syscallarg(int) pid;
+               syscallarg(int) pgid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct proc *targp;            /* target process */
+       register struct pgrp *pgrp;             /* target pgrp */
+
+       if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) {
+               if ((targp = pfind(SCARG(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 (SCARG(uap, pgid) == 0)
+               SCARG(uap, pgid) = targp->p_pid;
+       else if (SCARG(uap, pgid) != targp->p_pid)
+               if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 ||
+                   pgrp->pg_session != curp->p_session)
+                       return (EPERM);
+       return (enterpgrp(targp, SCARG(uap, pgid), 0));
+}
+
+/* ARGSUSED */
+int
+setuid(p, uap, retval)
+       struct proc *p;
+       struct setuid_args /* {
+               syscallarg(uid_t) uid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register uid_t uid;
+       int error;
+
+       uid = SCARG(uap, uid);
+       if (uid != pc->p_ruid &&
+           (error = suser(pc->pc_ucred, &p->p_acflag)))
+               return (error);
+       /*
+        * Everything's okay, do it.
+        * Transfer proc count to new user.
+        * Copy credentials so other references do not see our changes.
+        */
+       (void)chgproccnt(pc->p_ruid, -1);
+       (void)chgproccnt(uid, 1);
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       pc->pc_ucred->cr_uid = uid;
+       pc->p_ruid = uid;
+       pc->p_svuid = uid;
+       p->p_flag |= P_SUGID;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+seteuid(p, uap, retval)
+       struct proc *p;
+       struct seteuid_args /* {
+               syscallarg(uid_t) euid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register uid_t euid;
+       int error;
+
+       euid = SCARG(uap, euid);
+       if (euid != pc->p_ruid && euid != pc->p_svuid &&
+           (error = suser(pc->pc_ucred, &p->p_acflag)))
+               return (error);
+       /*
+        * Everything's okay, do it.  Copy credentials so other references do
+        * not see our changes.
+        */
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       pc->pc_ucred->cr_uid = euid;
+       p->p_flag |= P_SUGID;
+       return (0);
+}
 
 
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/reg.h"
-#include "../h/inode.h"
-#include "../h/proc.h"
-#include "../h/clock.h"
-#include "../h/mtpr.h"
-#include "../h/timeb.h"
-#include "../h/times.h"
-#include "../h/reboot.h"
-#include "../h/fs.h"
-#include "../h/conf.h"
-#include "../h/buf.h"
-#include "../h/mount.h"
-
-getuid()
-{
-
-       u.u_r.r_val1 = u.u_ruid;
-       u.u_r.r_val2 = u.u_uid;
-}
-
-setuid()
-{
-       register uid;
-       register struct a {
-               int     uid;
-       } *uap;
-
-       uap = (struct a *)u.u_ap;
-       uid = uap->uid;
-       if (u.u_ruid == uid || u.u_uid == uid || suser()) {
-               u.u_uid = uid;
-               u.u_procp->p_uid = uid;
-               u.u_ruid = uid;
+/* ARGSUSED */
+int
+setgid(p, uap, retval)
+       struct proc *p;
+       struct setgid_args /* {
+               syscallarg(gid_t) gid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register gid_t gid;
+       int error;
+
+       gid = SCARG(uap, gid);
+       if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag)))
+               return (error);
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       pc->pc_ucred->cr_groups[0] = gid;
+       pc->p_rgid = gid;
+       pc->p_svgid = gid;              /* ??? */
+       p->p_flag |= P_SUGID;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+setegid(p, uap, retval)
+       struct proc *p;
+       struct setegid_args /* {
+               syscallarg(gid_t) egid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register gid_t egid;
+       int error;
+
+       egid = SCARG(uap, egid);
+       if (egid != pc->p_rgid && egid != pc->p_svgid &&
+           (error = suser(pc->pc_ucred, &p->p_acflag)))
+               return (error);
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       pc->pc_ucred->cr_groups[0] = egid;
+       p->p_flag |= P_SUGID;
+       return (0);
+}
+
+/* ARGSUSED */
+int
+setgroups(p, uap, retval)
+       struct proc *p;
+       struct setgroups_args /* {
+               syscallarg(u_int) gidsetsize;
+               syscallarg(gid_t *) gidset;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register u_int ngrp;
+       int error;
+
+       if (error = suser(pc->pc_ucred, &p->p_acflag))
+               return (error);
+       ngrp = SCARG(uap, gidsetsize);
+       if (ngrp < 1 || ngrp > NGROUPS)
+               return (EINVAL);
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       if (error = copyin((caddr_t)SCARG(uap, gidset),
+           (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)))
+               return (error);
+       pc->pc_ucred->cr_ngroups = ngrp;
+       p->p_flag |= P_SUGID;
+       return (0);
+}
+
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+/* ARGSUSED */
+int
+compat_43_setreuid(p, uap, retval)
+       register struct proc *p;
+       struct compat_43_setreuid_args /* {
+               syscallarg(int) ruid;
+               syscallarg(int) euid;
+       } */ *uap;
+       register_t *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       union {
+               struct setuid_args sa;
+               struct seteuid_args ea;
+       } args;
+
+       /*
+        * If ruid == euid then setreuid is being used to emulate setuid,
+        * just do it.
+        */
+       if (SCARG(uap, ruid) != -1 && SCARG(uap, ruid) == SCARG(uap, euid)) {
+               SCARG(&args.sa, uid) = SCARG(uap, ruid);
+               return (setuid(p, &args.sa, retval));
        }
        }
+       /*
+        * Otherwise we assume that the intent of setting ruid is to be
+        * able to get back ruid priviledge (i.e. swapping ruid and euid).
+        * So we make sure that we will be able to do so, but do not
+        * actually set the ruid.
+        */
+       if (SCARG(uap, ruid) != (uid_t)-1 && SCARG(uap, ruid) != pc->p_ruid &&
+           SCARG(uap, ruid) != pc->p_svuid)
+               return (EPERM);
+       if (SCARG(uap, euid) == (uid_t)-1)
+               return (0);
+       SCARG(&args.ea, euid) = SCARG(uap, euid);
+       return (seteuid(p, &args.ea, retval));
 }
 
 }
 
-getgid()
+/* ARGSUSED */
+int
+compat_43_setregid(p, uap, retval)
+       register struct proc *p;
+       struct compat_43_setregid_args /* {
+               syscallarg(int) rgid;
+               syscallarg(int) egid;
+       } */ *uap;
+       register_t *retval;
 {
 {
+       register struct pcred *pc = p->p_cred;
+       union {
+               struct setgid_args sa;
+               struct setegid_args ea;
+       } args;
 
 
-       u.u_r.r_val1 = u.u_rgid;
-       u.u_r.r_val2 = u.u_gid;
+       /*
+        * If rgid == egid then setreuid is being used to emulate setgid,
+        * just do it.
+        */
+       if (SCARG(uap, rgid) != -1 && SCARG(uap, rgid) == SCARG(uap, egid)) {
+               SCARG(&args.sa, gid) = SCARG(uap, rgid);
+               return (setgid(p, &args.sa, retval));
+       }
+       /*
+        * Otherwise we assume that the intent of setting rgid is to be
+        * able to get back rgid priviledge (i.e. swapping rgid and egid).
+        * So we make sure that we will be able to do so, but do not
+        * actually set the rgid.
+        */
+       if (SCARG(uap, rgid) != (gid_t)-1 && SCARG(uap, rgid) != pc->p_rgid &&
+           SCARG(uap, rgid) != pc->p_svgid)
+               return (EPERM);
+       if (SCARG(uap, egid) == (gid_t)-1)
+               return (0);
+       SCARG(&args.ea, egid) = SCARG(uap, egid);
+       return (setegid(p, &args.ea, retval));
 }
 }
+#endif /* defined(COMPAT_43) || defined(COMPAT_SUNOS) */
 
 
-setgid()
+/*
+ * Check if gid is a member of the group set.
+ */
+int
+groupmember(gid, cred)
+       gid_t gid;
+       register struct ucred *cred;
 {
 {
-       register gid;
-       register struct a {
-               int     gid;
-       } *uap;
+       register gid_t *gp;
+       gid_t *egp;
+
+       egp = &(cred->cr_groups[cred->cr_ngroups]);
+       for (gp = cred->cr_groups; gp < egp; gp++)
+               if (*gp == gid)
+                       return (1);
+       return (0);
+}
 
 
-       uap = (struct a *)u.u_ap;
-       gid = uap->gid;
-       if (u.u_rgid == gid || u.u_gid == gid || suser()) {
-               u.u_gid = gid;
-               u.u_rgid = gid;
+/*
+ * 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.
+ */
+int
+suser(cred, acflag)
+       struct ucred *cred;
+       u_short *acflag;
+{
+       if (cred->cr_uid == 0) {
+               if (acflag)
+                       *acflag |= ASU;
+               return (0);
        }
        }
+       return (EPERM);
+}
+
+/*
+ * Allocate a zeroed cred structure.
+ */
+struct ucred *
+crget()
+{
+       register struct ucred *cr;
+
+       MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
+       bzero((caddr_t)cr, sizeof(*cr));
+       cr->cr_ref = 1;
+       return (cr);
+}
+
+/*
+ * Free a cred structure.
+ * Throws away space when ref count gets to 0.
+ */
+void
+crfree(cr)
+       struct ucred *cr;
+{
+       int s;
+
+       s = splimp();                           /* ??? */
+       if (--cr->cr_ref == 0)
+               FREE((caddr_t)cr, M_CRED);
+       (void) splx(s);
+}
+
+/*
+ * Copy cred structure to a new one and free the old one.
+ */
+struct ucred *
+crcopy(cr)
+       struct ucred *cr;
+{
+       struct ucred *newcr;
+
+       if (cr->cr_ref == 1)
+               return (cr);
+       newcr = crget();
+       *newcr = *cr;
+       crfree(cr);
+       newcr->cr_ref = 1;
+       return (newcr);
+}
+
+/*
+ * Dup cred struct to a new held one.
+ */
+struct ucred *
+crdup(cr)
+       struct ucred *cr;
+{
+       struct ucred *newcr;
+
+       newcr = crget();
+       *newcr = *cr;
+       newcr->cr_ref = 1;
+       return (newcr);
+}
+
+/*
+ * Get login name, if available.
+ */
+/* ARGSUSED */
+int
+getlogin(p, uap, retval)
+       struct proc *p;
+       struct getlogin_args /* {
+               syscallarg(char *) namebuf;
+               syscallarg(u_int) namelen;
+       } */ *uap;
+       register_t *retval;
+{
+
+       if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login))
+               SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login);
+       return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
+           (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen)));
+}
+
+/*
+ * Set login name.
+ */
+/* ARGSUSED */
+int
+setlogin(p, uap, retval)
+       struct proc *p;
+       struct setlogin_args /* {
+               syscallarg(char *) namebuf;
+       } */ *uap;
+       register_t *retval;
+{
+       int error;
+
+       if (error = suser(p->p_ucred, &p->p_acflag))
+               return (error);
+       error = copyinstr((caddr_t) SCARG(uap, namebuf),
+           (caddr_t) p->p_pgrp->pg_session->s_login,
+           sizeof (p->p_pgrp->pg_session->s_login) - 1, (u_int *)0);
+       if (error == ENAMETOOLONG)
+               error = EINVAL;
+       return (error);
 }
 }