add USL's copyright notice
[unix-history] / usr / src / sys / kern / kern_prot.c
index 1caa884..668bd82 100644 (file)
-/*     kern_prot.c     5.15    83/02/16        */
+/*
+ * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
+ *     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.5 (Berkeley) %G%
+ */
 
 /*
  * System calls related to processes and protection
  */
 
 
 /*
  * System calls related to processes and protection
  */
 
-#include "../machine/reg.h"
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/inode.h"
-#include "../h/proc.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"
-#include "../h/quota.h"
-
-getpid()
+#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>
+
+struct args {
+       int     dummy;
+};
+
+/* ARGSUSED */
+getpid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 
 {
 
-       u.u_r.r_val1 = u.u_procp->p_pid;
-       u.u_r.r_val2 = u.u_procp->p_ppid;
+       *retval = p->p_pid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_pptr->p_pid;
+#endif
+       return (0);
 }
 
 }
 
-getpgrp()
+/* ARGSUSED */
+getppid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 {
-       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;
+       *retval = p->p_pptr->p_pid;
+       return (0);
 }
 
 }
 
-getuid()
+/* Get process group ID; note that POSIX getpgrp takes no parameter */
+getpgrp(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 
 {
 
-       u.u_r.r_val1 = u.u_ruid;
-       u.u_r.r_val2 = u.u_uid;
+       *retval = p->p_pgrp->pg_id;
+       return (0);
 }
 
 }
 
-getgid()
+/* ARGSUSED */
+getuid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 
 {
 
-       u.u_r.r_val1 = u.u_rgid;
-       u.u_r.r_val2 = u.u_gid;
+       *retval = p->p_cred->p_ruid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_ucred->cr_uid;
+#endif
+       return (0);
 }
 
 }
 
-getgroups()
+/* ARGSUSED */
+geteuid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 {
-       register struct a {
-               u_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] >= 0)
-                       break;
-       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,
-           uap->gidsetsize * sizeof (u.u_groups[0]));
-       if (u.u_error)
-               return;
-       u.u_r.r_val1 = uap->gidsetsize;
+
+       *retval = p->p_ucred->cr_uid;
+       return (0);
 }
 
 }
 
-setpgrp()
+/* ARGSUSED */
+getgid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
 {
 {
-       register struct proc *p;
-       register struct a {
-               int     pid;
-               int     pgrp;
-       } *uap = (struct a *)u.u_ap;
-
-       if (uap->pid == 0)
-               uap->pid = u.u_procp->p_pid;
-       p = pfind(uap->pid);
-       if (p == 0) {
-               u.u_error = ESRCH;
-               return;
+
+       *retval = p->p_cred->p_rgid;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+       retval[1] = p->p_ucred->cr_groups[0];
+#endif
+       return (0);
+}
+
+/*
+ * 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 */
+getegid(p, uap, retval)
+       struct proc *p;
+       struct args *uap;
+       int *retval;
+{
+
+       *retval = p->p_ucred->cr_groups[0];
+       return (0);
+}
+
+struct getgroups_args {
+       u_int   gidsetsize;
+       gid_t   *gidset;
+};
+getgroups(p, uap, retval)
+       struct proc *p;
+       register struct getgroups_args *uap;
+       int *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register u_int ngrp;
+       int error;
+
+       if ((ngrp = uap->gidsetsize) == 0) {
+               *retval = pc->pc_ucred->cr_ngroups;
+               return (0);
        }
        }
-/* need better control mechanisms for process groups */
-       if (p->p_uid != u.u_uid && u.u_uid && !inferior(p)) {
-               u.u_error = EPERM;
-               return;
+       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)uap->gidset, ngrp * sizeof(gid_t)))
+               return (error);
+       *retval = ngrp;
+       return (0);
+}
+
+/* ARGSUSED */
+setsid(p, uap, retval)
+       register struct proc *p;
+       struct args *uap;
+       int *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);
        }
        }
-       p->p_pgrp = uap->pgrp;
 }
 
 }
 
-setreuid()
+/*
+ * 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)
+ */
+struct setpgid_args {
+       int     pid;    /* target process id */
+       int     pgid;   /* target pgrp id */
+};
+/* ARGSUSED */
+setpgid(curp, uap, retval)
+       struct proc *curp;
+       register struct setpgid_args *uap;
+       int *retval;
 {
 {
-       struct a {
-               int     ruid;
-               int     euid;
-       } *uap;
-       register int ruid, euid;
-
-       uap = (struct a *)u.u_ap;
-       ruid = uap->ruid;
-       if (ruid == -1)
-               ruid = u.u_ruid;
-       if (u.u_ruid != ruid && u.u_uid != ruid && !suser())
-               return;
-       euid = uap->euid;
-       if (euid == -1)
-               euid = u.u_uid;
-       if (u.u_ruid != euid && u.u_uid != euid && !suser())
-               return;
+       register struct proc *targp;            /* target process */
+       register struct pgrp *pgrp;             /* target 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)
+               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));
+}
+
+struct setuid_args {
+       uid_t   uid;
+};
+/* ARGSUSED */
+setuid(p, uap, retval)
+       struct proc *p;
+       struct setuid_args *uap;
+       int *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register uid_t uid;
+       int error;
+
+       uid = uap->uid;
+       if (uid != pc->p_ruid &&
+           (error = suser(pc->pc_ucred, &p->p_acflag)))
+               return (error);
        /*
         * Everything's okay, do it.
        /*
         * Everything's okay, do it.
+        * Transfer proc count to new user.
+        * Copy credentials so other references do not see our changes.
         */
         */
-#ifdef QUOTA
-       if (u.u_quota->q_uid != ruid) {
-               qclean();
-               qstart(getquota(ruid, 0, 0));
-       }
-#endif
-       u.u_procp->p_uid = ruid;
-       u.u_ruid = ruid;
-       u.u_uid = euid;
+       (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);
 }
 
 }
 
-#ifndef NOCOMPAT
-osetuid()
+struct seteuid_args {
+       uid_t   euid;
+};
+/* ARGSUSED */
+seteuid(p, uap, retval)
+       struct proc *p;
+       struct seteuid_args *uap;
+       int *retval;
 {
 {
-       register uid;
-       register struct a {
-               int     uid;
-       } *uap;
+       register struct pcred *pc = p->p_cred;
+       register uid_t euid;
+       int error;
 
 
-       uap = (struct a *)u.u_ap;
-       uid = uap->uid;
-       if (u.u_ruid == uid || u.u_uid == uid || suser()) {
-#ifdef QUOTA
-               if (u.u_quota->q_uid != uid) {
-                       qclean();
-                       qstart(getquota(uid, 0, 0));
-               }
-#endif
-               u.u_uid = uid;
-               u.u_procp->p_uid = uid;
-               u.u_ruid = uid;
-       }
+       euid = 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);
 }
 }
-#endif
 
 
-setregid()
+struct setgid_args {
+       gid_t   gid;
+};
+/* ARGSUSED */
+setgid(p, uap, retval)
+       struct proc *p;
+       struct setgid_args *uap;
+       int *retval;
 {
 {
-       register struct a {
-               int     rgid;
-               int     egid;
-       } *uap;
-       register int rgid, egid;
-
-       uap = (struct a *)u.u_ap;
-       rgid = uap->rgid;
-       if (rgid == -1)
-               rgid = u.u_rgid;
-       if (u.u_rgid != rgid && u.u_gid != rgid && !suser())
-               return;
+       register struct pcred *pc = p->p_cred;
+       register gid_t gid;
+       int error;
+
+       gid = 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);
+}
+
+struct setegid_args {
+       gid_t   egid;
+};
+/* ARGSUSED */
+setegid(p, uap, retval)
+       struct proc *p;
+       struct setegid_args *uap;
+       int *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       register gid_t egid;
+       int error;
+
        egid = uap->egid;
        egid = uap->egid;
-       if (egid == -1)
-               egid = u.u_gid;
-       if (u.u_rgid != egid && u.u_gid != egid && !suser())
-               return;
-       if (u.u_rgid != rgid) {
-               leavegroup(u.u_rgid);
-               (void) entergroup(u.u_rgid);
-               u.u_rgid = rgid;
-       }
-       if (u.u_gid != egid) {
-               leavegroup(u.u_gid);
-               (void) entergroup(egid);
-               u.u_gid = 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);
 }
 
 }
 
-#ifndef NOCOMPAT
-osetgid()
+struct setgroups_args {
+       u_int   gidsetsize;
+       gid_t   *gidset;
+};
+/* ARGSUSED */
+setgroups(p, uap, retval)
+       struct proc *p;
+       struct setgroups_args *uap;
+       int *retval;
 {
 {
-       register gid;
-       register struct a {
-               int     gid;
-       } *uap;
+       register struct pcred *pc = p->p_cred;
+       register u_int ngrp;
+       int error;
 
 
-       uap = (struct a *)u.u_ap;
-       gid = uap->gid;
-       if (u.u_rgid == gid || u.u_gid == gid || suser()) {
-               leavegroup(u.u_gid); leavegroup(u.u_rgid);
-               (void) entergroup(gid);
-               u.u_gid = gid;
-               u.u_rgid = gid;
-       }
+       if (error = suser(pc->pc_ucred, &p->p_acflag))
+               return (error);
+       if ((ngrp = uap->gidsetsize) > NGROUPS)
+               return (EINVAL);
+       pc->pc_ucred = crcopy(pc->pc_ucred);
+       if (error = copyin((caddr_t)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);
 }
 
 }
 
-setgroups()
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+struct setreuid_args {
+       int     ruid;
+       int     euid;
+};
+/* ARGSUSED */
+osetreuid(p, uap, retval)
+       register struct proc *p;
+       struct setreuid_args *uap;
+       int *retval;
 {
 {
-       register struct a {
-               u_int   gidsetsize;
-               int     *gidset;
-       } *uap = (struct a *)u.u_ap;
-       register int *gp;
-
-       if (!suser())
-               return;
-       if (uap->gidsetsize > sizeof (u.u_groups) / sizeof (u.u_groups[0])) {
-               u.u_error = EINVAL;
-               return;
-       }
-       u.u_error = copyin((caddr_t)uap->gidset, (caddr_t)u.u_groups,
-           uap->gidsetsize * sizeof (u.u_groups[0]));
-       if (u.u_error)
-               return;
-       for (gp = &u.u_groups[uap->gidsetsize]; gp < &u.u_groups[NGROUPS]; gp++)
-               *gp = -1;
+       register struct pcred *pc = p->p_cred;
+       struct seteuid_args args;
+
+       /*
+        * we assume that the intent of setting ruid is to be able to get
+        * back ruid priviledge. So we make sure that we will be able to
+        * do so, but do not actually set the ruid.
+        */
+       if (uap->ruid != (uid_t)-1 && uap->ruid != pc->p_ruid &&
+           uap->ruid != pc->p_svuid)
+               return (EPERM);
+       if (uap->euid == (uid_t)-1)
+               return (0);
+       args.euid = uap->euid;
+       return (seteuid(p, &args, retval));
+}
+
+struct setregid_args {
+       int     rgid;
+       int     egid;
+};
+/* ARGSUSED */
+osetregid(p, uap, retval)
+       register struct proc *p;
+       struct setregid_args *uap;
+       int *retval;
+{
+       register struct pcred *pc = p->p_cred;
+       struct setegid_args args;
+
+       /*
+        * we assume that the intent of setting rgid is to be able to get
+        * back rgid priviledge. So we make sure that we will be able to
+        * do so, but do not actually set the rgid.
+        */
+       if (uap->rgid != (gid_t)-1 && uap->rgid != pc->p_rgid &&
+           uap->rgid != pc->p_svgid)
+               return (EPERM);
+       if (uap->egid == (gid_t)-1)
+               return (0);
+       args.egid = uap->egid;
+       return (setegid(p, &args, retval));
 }
 }
+#endif /* defined(COMPAT_43) || defined(COMPAT_SUNOS) */
 
 /*
 
 /*
- * Pid of zero implies current process.
- * Pgrp -1 is getpgrp system call returning
- * current process group.
+ * Check if gid is a member of the group set.
  */
  */
-osetpgrp()
+groupmember(gid, cred)
+       gid_t gid;
+       register struct ucred *cred;
 {
 {
-       register struct proc *p;
-       register struct a {
-               int     pid;
-               int     pgrp;
-       } *uap;
-
-       uap = (struct a *)u.u_ap;
-       if (uap->pid == 0)
-               p = u.u_procp;
-       else {
-               p = pfind(uap->pid);
-               if (p == 0) {
-                       u.u_error = ESRCH;
-                       return;
-               }
-       }
-       if (uap->pgrp <= 0) {
-               u.u_r.r_val1 = p->p_pgrp;
-               return;
-       }
-       if (p->p_uid != u.u_uid && u.u_uid && !inferior(p)) {
-               u.u_error = EPERM;
-               return;
+       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);
+}
+
+/*
+ * 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.
+ */
+suser(cred, acflag)
+       struct ucred *cred;
+       short *acflag;
+{
+       if (cred->cr_uid == 0) {
+               if (acflag)
+                       *acflag |= ASU;
+               return (0);
        }
        }
-       p->p_pgrp = uap->pgrp;
+       return (EPERM);
 }
 }
-/* END DEFUNCT */
 
 
-leavegroup(gid)
-       int gid;
+/*
+ * Allocate a zeroed cred structure.
+ */
+struct ucred *
+crget()
 {
 {
-       register int *gp;
+       register struct ucred *cr;
 
 
-       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 = -1;
+       MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
+       bzero((caddr_t)cr, sizeof(*cr));
+       cr->cr_ref = 1;
+       return (cr);
 }
 
 }
 
-entergroup(gid)
-       int gid;
+/*
+ * Free a cred structure.
+ * Throws away space when ref count gets to 0.
+ */
+crfree(cr)
+       struct ucred *cr;
 {
 {
-       register int *gp;
+       int s;
 
 
-       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);
+       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.
+ */
+struct getlogin_args {
+       char    *namebuf;
+       u_int   namelen;
+};
+/* ARGSUSED */
+getlogin(p, uap, retval)
+       struct proc *p;
+       struct getlogin_args *uap;
+       int *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,
+           (caddr_t) uap->namebuf, uap->namelen));
+}
+
+/*
+ * Set login name.
+ */
+struct setlogin_args {
+       char    *namebuf;
+};
+/* ARGSUSED */
+setlogin(p, uap, retval)
+       struct proc *p;
+       struct setlogin_args *uap;
+       int *retval;
+{
+       int error;
+
+       if (error = suser(p->p_ucred, &p->p_acflag))
+               return (error);
+       error = copyinstr((caddr_t) 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);
 }
 }