Have each thread keeps its own (counted!) reference to the process's ucreds
authorguenther <guenther@openbsd.org>
Fri, 18 Apr 2014 11:51:16 +0000 (11:51 +0000)
committerguenther <guenther@openbsd.org>
Fri, 18 Apr 2014 11:51:16 +0000 (11:51 +0000)
to avoid possible use-after-free references when swapping ids in threaded
processes.  "Do I have the right creds?" checks are always made with the
threads creds.

Inspired by FreeBSD and NetBSD
"right time" deraadt@

25 files changed:
sys/arch/alpha/alpha/trap.c
sys/arch/amd64/amd64/trap.c
sys/arch/arm/arm/fault.c
sys/arch/hppa/hppa/trap.c
sys/arch/hppa64/hppa64/trap.c
sys/arch/i386/i386/trap.c
sys/arch/m88k/m88k/trap.c
sys/arch/mips64/mips64/trap.c
sys/arch/powerpc/powerpc/trap.c
sys/arch/sh/sh/trap.c
sys/arch/solbourne/solbourne/trap.c
sys/arch/sparc/sparc/trap.c
sys/arch/sparc64/sparc64/trap.c
sys/arch/vax/vax/trap.c
sys/dev/systrace.c
sys/kern/init_main.c
sys/kern/kern_exec.c
sys/kern/kern_exit.c
sys/kern/kern_fork.c
sys/kern/kern_prot.c
sys/kern/kern_sig.c
sys/sys/cdefs.h
sys/sys/proc.h
sys/sys/syscall_mi.h
sys/sys/ucred.h

index e4003e6..45c44d0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: trap.c,v 1.71 2014/03/30 21:54:48 guenther Exp $ */
+/* $OpenBSD: trap.c,v 1.72 2014/04/18 11:51:16 guenther Exp $ */
 /* $NetBSD: trap.c,v 1.52 2000/05/24 16:48:33 thorpej Exp $ */
 
 /*-
@@ -244,8 +244,10 @@ trap(a0, a1, a2, entry, framep)
        ucode = 0;
        v = 0;
        user = (framep->tf_regs[FRAME_PS] & ALPHA_PSL_USERMODE) != 0;
-       if (user)
+       if (user) {
                p->p_md.md_tf = framep;
+               refreshcreds(p);
+       }
 
        switch (entry) {
        case ALPHA_KENTRY_UNA:
index 84985e5..3987b2c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.35 2014/03/30 21:54:48 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.36 2014/04/18 11:51:16 guenther Exp $      */
 /*     $NetBSD: trap.c,v 1.2 2003/05/04 23:51:56 fvdl Exp $    */
 
 /*-
@@ -175,7 +175,8 @@ trap(struct trapframe *frame)
        if (!KERNELMODE(frame->tf_cs, frame->tf_rflags)) {
                type |= T_USER;
                p->p_md.md_regs = frame;
-       }
+       } else /* if (type != T_NMI) */
+               refreshcreds(p);
 
        switch (type) {
 
index 8143bdb..79d0d46 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fault.c,v 1.15 2014/03/30 21:54:49 guenther Exp $     */
+/*     $OpenBSD: fault.c,v 1.16 2014/04/18 11:51:16 guenther Exp $     */
 /*     $NetBSD: fault.c,v 1.46 2004/01/21 15:39:21 skrll Exp $ */
 
 /*
@@ -255,8 +255,10 @@ data_abort_handler(trapframe_t *tf)
         * the MMU.
         */
 
-       if (user)
+       if (user) {
                p->p_addr->u_pcb.pcb_tf = tf;
+               refreshcreds(p);
+       }
 
        /*
         * Make sure the Program Counter is sane. We could fall foul of
index 2596857..4ff9408 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.129 2014/04/08 09:34:23 mpi Exp $  */
+/*     $OpenBSD: trap.c,v 1.130 2014/04/18 11:51:16 guenther Exp $     */
 
 /*
  * Copyright (c) 1998-2004 Michael Shalayeff
@@ -219,6 +219,9 @@ trap(int type, struct trapframe *frame)
                mtctl(frame->tf_eiem, CR_EIEM);
        }
 
+       if (type & T_USER)
+               refreshcreds(p);
+
        switch (type) {
        case T_NONEXIST:
        case T_NONEXIST | T_USER:
index 02472f0..8310c42 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.35 2014/04/08 09:34:23 mpi Exp $   */
+/*     $OpenBSD: trap.c,v 1.36 2014/04/18 11:51:16 guenther Exp $      */
 
 /*
  * Copyright (c) 2005 Michael Shalayeff
@@ -212,6 +212,9 @@ trap(int type, struct trapframe *frame)
                ssm(PSL_I, mask);
        }
 
+       if (type & T_USER)
+               refreshcreds(p);
+
        switch (type) {
        case T_NONEXIST:
        case T_NONEXIST | T_USER:
index 216d166..31d4c77 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.113 2014/03/26 05:23:42 guenther Exp $     */
+/*     $OpenBSD: trap.c,v 1.114 2014/04/18 11:51:17 guenther Exp $     */
 /*     $NetBSD: trap.c,v 1.95 1996/05/05 06:50:02 mycroft Exp $        */
 
 /*-
@@ -155,7 +155,8 @@ trap(struct trapframe *frame)
        if (!KERNELMODE(frame->tf_cs, frame->tf_eflags)) {
                type |= T_USER;
                p->p_md.md_regs = frame;
-       }
+       } else if (type != T_NMI)
+               refreshcreds(p);
 
        switch (type) {
 
index e9c9012..b60fcff 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.89 2014/03/26 05:23:42 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.90 2014/04/18 11:51:17 guenther Exp $      */
 /*
  * Copyright (c) 2004, Miodrag Vallat.
  * Copyright (c) 1998 Steve Murphree, Jr.
@@ -245,6 +245,7 @@ m88100_trap(u_int type, struct trapframe *frame)
        if (USERMODE(frame->tf_epsr)) {
                type += T_USER;
                p->p_md.md_tf = frame;  /* for ptrace/signals */
+               refreshcreds(p);
        }
        fault_type = SI_NOINFO;
        fault_code = 0;
@@ -713,6 +714,7 @@ m88110_trap(u_int type, struct trapframe *frame)
        if (USERMODE(frame->tf_epsr)) {
                type += T_USER;
                p->p_md.md_tf = frame;  /* for ptrace/signals */
+               refreshcreds(p);
        }
 
        if (sig != 0)
index 8f20faf..5576f84 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.92 2014/04/09 21:10:35 miod Exp $  */
+/*     $OpenBSD: trap.c,v 1.93 2014/04/18 11:51:17 guenther Exp $      */
 
 /*
  * Copyright (c) 1988 University of Utah.
@@ -189,6 +189,7 @@ trap(struct trap_frame *trapframe)
                atomic_add_int(&uvmexp.traps, 1);
        if (USERMODE(trapframe->sr)) {
                type |= T_USER;
+               refreshcreds(p);
        }
 
        /*
index 7d4e23d..bca3dab 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.95 2014/03/26 05:23:42 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.96 2014/04/18 11:51:17 guenther Exp $      */
 /*     $NetBSD: trap.c,v 1.3 1996/10/13 03:31:37 christos Exp $        */
 
 /*
@@ -254,6 +254,7 @@ trap(struct trapframe *frame)
 
        if (frame->srr1 & PSL_PR) {
                type |= EXC_USER;
+               refreshcreds(p);
        }
 
        switch (type) {
index 4c1b9c1..b6f80f9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.24 2014/03/30 21:54:49 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.25 2014/04/18 11:51:17 guenther Exp $      */
 /*     $NetBSD: exception.c,v 1.32 2006/09/04 23:57:52 uwe Exp $       */
 /*     $NetBSD: syscall.c,v 1.6 2006/03/07 07:21:50 thorpej Exp $      */
 
@@ -173,6 +173,7 @@ general_exception(struct proc *p, struct trapframe *tf, uint32_t va)
                        goto do_panic;
                KDASSERT(p->p_md.md_regs == tf); /* check exception depth */
                expevt |= EXP_USER;
+               refreshcreds(p);
        }
 
        switch (expevt) {
@@ -336,6 +337,7 @@ tlb_exception(struct proc *p, struct trapframe *tf, uint32_t va)
        usermode = !KERNELMODE(tf->tf_ssr);
        if (usermode) {
                KDASSERT(p->p_md.md_regs == tf);
+               refreshcreds(p);
        } else {
                KDASSERT(p == NULL ||           /* idle */
                    p == &proc0 ||              /* kthread */
@@ -479,6 +481,7 @@ ast(struct proc *p, struct trapframe *tf)
                p->p_md.md_astpending = 0;
                uvmexp.softs++;
 
+               refreshcreds(p);
                if (p->p_flag & P_OWEUPC) {
                        ADDUPROF(p);
                }
index 1796ae3..dbd3007 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.18 2014/03/26 05:23:42 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.19 2014/04/18 11:51:17 guenther Exp $      */
 /*     OpenBSD: trap.c,v 1.42 2004/12/06 20:12:25 miod Exp     */
 
 /*
@@ -286,6 +286,7 @@ trap(type, psr, pc, tf)
                p = &proc0;
        pcb = &p->p_addr->u_pcb;
        p->p_md.md_tf = tf;     /* for ptrace/signals */
+       refreshcreds(p);
 
        switch (type) {
 
index 8c65ce5..d5dcac3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.64 2014/03/26 05:23:42 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.65 2014/04/18 11:51:17 guenther Exp $      */
 /*     $NetBSD: trap.c,v 1.58 1997/09/12 08:55:01 pk Exp $ */
 
 /*
@@ -279,6 +279,7 @@ trap(type, psr, pc, tf)
                p = &proc0;
        pcb = &p->p_addr->u_pcb;
        p->p_md.md_tf = tf;     /* for ptrace/signals */
+       refreshcreds(p);
 
        switch (type) {
 
index 42734cf..d1605f0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.80 2014/03/30 21:54:49 guenther Exp $      */
+/*     $OpenBSD: trap.c,v 1.81 2014/04/18 11:51:17 guenther Exp $      */
 /*     $NetBSD: trap.c,v 1.73 2001/08/09 01:03:01 eeh Exp $ */
 
 /*
@@ -430,6 +430,7 @@ trap(tf, type, pc, tstate)
                p = &proc0;
        pcb = &p->p_addr->u_pcb;
        p->p_md.md_tf = tf;     /* for ptrace/signals */
+       refreshcreds(p);
 
        switch (type) {
 
index d7e06b1..bcd9a5a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: trap.c,v 1.50 2014/03/30 21:54:49 guenther Exp $     */
+/*     $OpenBSD: trap.c,v 1.51 2014/04/18 11:51:17 guenther Exp $     */
 /*     $NetBSD: trap.c,v 1.47 1999/08/21 19:26:20 matt Exp $     */
 /*
  * Copyright (c) 1994 Ludd, University of Lule}, Sweden.
@@ -110,6 +110,7 @@ arithflt(frame)
        if ((umode = USERMODE(frame))) {
                type |= T_USER;
                p->p_addr->u_pcb.framep = frame;
+               refreshcreds(p);
        }
 
        type&=~(T_WRITE|T_PTEFETCH);
index a9c66b4..122a885 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: systrace.c,v 1.66 2014/03/30 21:54:48 guenther Exp $  */
+/*     $OpenBSD: systrace.c,v 1.67 2014/04/18 11:51:17 guenther Exp $  */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * All rights reserved.
@@ -59,8 +59,6 @@ int   systraceopen(dev_t, int, int, struct proc *);
 int    systraceclose(dev_t, int, int, struct proc *);
 int    systraceioctl(dev_t, u_long, caddr_t, int, struct proc *);
 
-uid_t  systrace_seteuid(struct proc *,  uid_t);
-gid_t  systrace_setegid(struct proc *,  gid_t);
 int    systracef_read(struct file *, off_t *, struct uio *, struct ucred *);
 int    systracef_write(struct file *, off_t *, struct uio *, struct ucred *);
 int    systracef_ioctl(struct file *, u_long, caddr_t, struct proc *p);
@@ -117,9 +115,7 @@ struct str_process {
        u_int16_t seqnr;        /* expected reply sequence number */
 
        uid_t seteuid;
-       uid_t saveuid;
        gid_t setegid;
-       gid_t savegid;
 
        int isscript;
        char scriptname[MAXPATHLEN];
@@ -666,10 +662,9 @@ systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
        struct str_policy *strpolicy;
        struct fsystrace *fst = NULL;
        struct emul *oldemul;
-       struct ucred *uc;
-       uid_t olduid;
-       gid_t oldgid;
        int policy, error = 0, report = 0, maycontrol = 0, issuser = 0;
+       uid_t old_ruid = p->p_ucred->cr_ruid;
+       gid_t old_rgid = p->p_ucred->cr_rgid;
 
        systrace_lock();
        strp = p->p_systrace;
@@ -701,8 +696,8 @@ systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
                maycontrol = 1;
                issuser = 1;
        } else if (!ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) {
-               maycontrol = fst->p_ruid == p->p_ucred->cr_ruid &&
-                   fst->p_rgid == p->p_ucred->cr_rgid;
+               maycontrol = fst->p_ruid == old_ruid &&
+                   fst->p_rgid == old_rgid;
        }
 
        if (!maycontrol) {
@@ -787,23 +782,38 @@ systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
                goto out_unlock;
 
        oldemul = pr->ps_emul;
-       uc = p->p_ucred;
-       olduid = uc->cr_ruid;
-       oldgid = uc->cr_rgid;
                
        /* Elevate privileges as desired */
-       if (issuser) {
-               if (ISSET(strp->flags, STR_PROC_SETEUID))
-                       strp->saveuid = systrace_seteuid(p, strp->seteuid);
-               if (ISSET(strp->flags, STR_PROC_SETEGID))
-                       strp->savegid = systrace_setegid(p, strp->setegid);
-       } else
-               CLR(strp->flags, STR_PROC_SETEUID|STR_PROC_SETEGID);
+       if (issuser && ISSET(strp->flags, STR_PROC_SETEUID|STR_PROC_SETEGID)) {
+               struct ucred *uc, *newcred;
+
+               uc = p->p_ucred;
+               newcred = NULL;
+               if (ISSET(strp->flags, STR_PROC_SETEUID) &&
+                   uc->cr_uid != strp->seteuid) {
+                       newcred = crdup(uc);
+                       newcred->cr_uid = strp->seteuid;
+               }
+               if (ISSET(strp->flags, STR_PROC_SETEGID) &&
+                   uc->cr_gid != strp->setegid) {
+                       if (newcred == NULL)
+                               newcred = crdup(uc);
+                       newcred->cr_gid = strp->setegid;
+               }
+               if (newcred != NULL) {
+                       p->p_ucred = newcred;
+                       crfree(uc);
+                       atomic_setbits_int(&pr->ps_flags, PS_SUGID);
+               }
+       }
 
        rw_exit_write(&fst->lock);
                                
        error = (*callp->sy_call)(p, v, retval);
 
+       /* reset the credentials on the thread */
+       refreshcreds(p);
+
        /* Return to old privileges */
        systrace_lock();
        if ((strp = p->p_systrace) == NULL) {
@@ -811,19 +821,6 @@ systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
                return (error);
        }
 
-       if (issuser) {
-               if (ISSET(strp->flags, STR_PROC_SETEUID)) {
-                       if (uc->cr_uid == strp->seteuid)
-                               systrace_seteuid(p, strp->saveuid);
-                       CLR(strp->flags, STR_PROC_SETEUID);
-               }
-               if (ISSET(strp->flags, STR_PROC_SETEGID)) {
-                       if (uc->cr_gid == strp->setegid)
-                               systrace_setegid(p, strp->savegid);
-                       CLR(strp->flags, STR_PROC_SETEGID);
-               }
-       }
-
        systrace_replacefree(strp);
 
        if (ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) {
@@ -858,8 +855,8 @@ systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
        }
 
        /* Report if effective uid or gid changed */
-       if (olduid != p->p_ucred->cr_ruid ||
-           oldgid != p->p_ucred->cr_rgid) {
+       if (old_ruid != pr->ps_ucred->cr_ruid ||
+           old_rgid != pr->ps_ucred->cr_rgid) {
                systrace_msg_ugid(fst, strp);
 
                REACQUIRE_LOCK;
@@ -880,44 +877,6 @@ out:
        return (error);
 }
 
-uid_t
-systrace_seteuid(struct proc *p,  uid_t euid)
-{
-       struct ucred *uc = p->p_ucred;
-       uid_t oeuid = uc->cr_uid;
-
-       if (oeuid == euid)
-               return (oeuid);
-
-       /*
-        * Copy credentials so other references do not see our changes.
-        */
-       p->p_ucred = uc = crcopy(uc);
-       uc->cr_uid = euid;
-       atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
-
-       return (oeuid);
-}
-
-gid_t
-systrace_setegid(struct proc *p,  gid_t egid)
-{
-       struct ucred *uc = p->p_ucred;
-       gid_t oegid = uc->cr_gid;
-
-       if (oegid == egid)
-               return (oegid);
-
-       /*
-        * Copy credentials so other references do not see our changes.
-        */
-       p->p_ucred = uc = crcopy(uc);
-       uc->cr_gid = egid;
-       atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
-
-       return (oegid);
-}
-
 /* Called with fst locked */
 
 int
@@ -1201,15 +1160,18 @@ int
 systrace_attach(struct fsystrace *fst, pid_t pid)
 {
        int error = 0;
-       struct proc *proc, *p = curproc;
+       struct proc *p = curproc;
+       struct proc *t;                         /* target thread */
+       struct process *tr;                     /* target process */
        struct str_process *newstrp;
 
-       if ((proc = pfind(pid)) == NULL || (proc->p_flag & P_THREAD)) {
+       if ((t = pfind(pid)) == NULL || (t->p_flag & P_THREAD)) {
                error = ESRCH;
                goto out;
        }
+       tr = t->p_p;
 
-       if (ISSET(proc->p_p->ps_flags, PS_INEXEC)) {
+       if (ISSET(tr->ps_flags, PS_INEXEC)) {
                error = EAGAIN;
                goto out;
        }
@@ -1218,7 +1180,7 @@ systrace_attach(struct fsystrace *fst, pid_t pid)
         * You can't attach to a process if:
         *      (1) it's the process that's doing the attaching,
         */
-       if (proc->p_p == p->p_p) {
+       if (tr == p->p_p) {
                error = EINVAL;
                goto out;
        }
@@ -1226,7 +1188,7 @@ systrace_attach(struct fsystrace *fst, pid_t pid)
        /*
         *      (2) it's a system process
         */
-       if (ISSET(proc->p_flag, P_SYSTEM)) {
+       if (ISSET(t->p_flag, P_SYSTEM)) {
                error = EPERM;
                goto out;
        }
@@ -1234,7 +1196,7 @@ systrace_attach(struct fsystrace *fst, pid_t pid)
        /*
         *      (3) it's being traced already
         */
-       if (ISSET(proc->p_flag, P_SYSTRACE)) {
+       if (ISSET(t->p_flag, P_SYSTRACE)) {
                error = EBUSY;
                goto out;
        }
@@ -1250,8 +1212,8 @@ systrace_attach(struct fsystrace *fst, pid_t pid)
         *      special privileges using setuid() from being
         *      traced. This is good security.]
         */
-       if ((proc->p_ucred->cr_ruid != p->p_ucred->cr_ruid ||
-               ISSET(proc->p_p->ps_flags, PS_SUGID | PS_SUGIDEXEC)) &&
+       if ((tr->ps_ucred->cr_ruid != p->p_ucred->cr_ruid ||
+               ISSET(tr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) &&
            (error = suser(p, 0)) != 0)
                goto out;
 
@@ -1261,13 +1223,13 @@ systrace_attach(struct fsystrace *fst, pid_t pid)
         *          compiled with permanently insecure mode turned
         *          on.
         */
-       if ((proc->p_p->ps_pid == 1) && (securelevel > -1)) {
+       if ((tr->ps_pid == 1) && (securelevel > -1)) {
                error = EPERM;
                goto out;
        }
 
        newstrp = systrace_getproc();
-       systrace_insert_process(fst, proc, newstrp);
+       systrace_insert_process(fst, t, newstrp);
 
  out:
        return (error);
@@ -1713,10 +1675,10 @@ int
 systrace_msg_ugid(struct fsystrace *fst, struct str_process *strp)
 {
        struct str_msg_ugid *msg_ugid = &strp->msg.msg_data.msg_ugid;
-       struct proc *p = strp->proc;
+       struct ucred *uc = strp->proc->p_p->ps_ucred;
 
-       msg_ugid->uid = p->p_ucred->cr_ruid;
-       msg_ugid->gid = p->p_ucred->cr_rgid;
+       msg_ugid->uid = uc->cr_ruid;
+       msg_ugid->gid = uc->cr_rgid;
 
        return (systrace_make_msg(strp, SYSTR_MSG_UGID));
 }
index 420e782..eaea6e4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: init_main.c,v 1.210 2014/03/31 19:37:15 kettenis Exp $        */
+/*     $OpenBSD: init_main.c,v 1.211 2014/04/18 11:51:17 guenther Exp $        */
 /*     $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $   */
 
 /*
@@ -295,6 +295,9 @@ main(void *framep)
        pr->ps_ucred = crget();
        pr->ps_ucred->cr_ngroups = 1;   /* group 0 */
 
+       p->p_ucred = pr->ps_ucred;      /* prime the thread's cache */
+       crhold(p->p_ucred);
+
        /* Initialize signal state for process 0. */
        signal_init();
        pr->ps_sigacts = &sigacts0;
index 2790e48..969ac15 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_exec.c,v 1.140 2014/03/30 21:54:48 guenther Exp $        */
+/*     $OpenBSD: kern_exec.c,v 1.141 2014/04/18 11:51:17 guenther Exp $        */
 /*     $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $  */
 
 /*-
@@ -595,7 +595,10 @@ sys_execve(struct proc *p, void *v, register_t *retval)
        } else
                atomic_clearbits_int(&pr->ps_flags, PS_SUGID);
 
-       /* reset the saved ugids */
+       /*
+        * Reset the saved ugids and update the process's copy of the
+        * creds if the creds have been changed
+        */
        if (cred->cr_uid != cred->cr_svuid ||
            cred->cr_gid != cred->cr_svgid) {
                /* make sure we have unshared ucreds */
@@ -604,6 +607,15 @@ sys_execve(struct proc *p, void *v, register_t *retval)
                cred->cr_svgid = cred->cr_gid;
        }
 
+       if (pr->ps_ucred != cred) {
+               struct ucred *ocred;
+
+               ocred = pr->ps_ucred;
+               crhold(cred);
+               pr->ps_ucred = cred;
+               crfree(ocred);
+       }
+
        if (pr->ps_flags & PS_SUGIDEXEC) {
                int i, s = splclock();
 
index 9f16c28..37e7c39 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_exit.c,v 1.139 2014/04/17 14:52:50 guenther Exp $        */
+/*     $OpenBSD: kern_exit.c,v 1.140 2014/04/18 11:51:17 guenther Exp $        */
 /*     $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $  */
 
 /*
@@ -411,6 +411,7 @@ exit2(struct proc *p)
 void
 proc_free(struct proc *p)
 {
+       crfree(p->p_ucred);
        pool_put(&proc_pool, p);
        nthreads--;
 }
index 3cec607..bad4221 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_fork.c,v 1.162 2014/03/30 21:54:48 guenther Exp $        */
+/*     $OpenBSD: kern_fork.c,v 1.163 2014/04/18 11:51:17 guenther Exp $        */
 /*     $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $  */
 
 /*
@@ -175,8 +175,9 @@ process_new(struct proc *p, struct process *parent, int flags)
            (caddr_t)&pr->ps_endcopy - (caddr_t)&pr->ps_startcopy);
 
        /* post-copy fixups */
-       pr->ps_ucred = parent->ps_ucred;
+       pr->ps_ucred = p->p_ucred;
        crhold(pr->ps_ucred);
+       KASSERT(p->p_ucred->cr_ref >= 3); /* fork thr, new thr, new process */
        pr->ps_limit->p_refcnt++;
 
        /* bump references to the text vnode (for procfs) */
@@ -318,6 +319,7 @@ fork1(struct proc *curp, int flags, void *stack, pid_t *tidptr,
            (caddr_t)&p->p_endzero - (caddr_t)&p->p_startzero);
        memcpy(&p->p_startcopy, &curp->p_startcopy,
            (caddr_t)&p->p_endcopy - (caddr_t)&p->p_startcopy);
+       crhold(p->p_ucred);
 
        /*
         * Initialize the timeouts.
index 71e527f..ce960e1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_prot.c,v 1.59 2014/03/30 21:54:48 guenther Exp $ */
+/*     $OpenBSD: kern_prot.c,v 1.60 2014/04/18 11:51:17 guenther Exp $ */
 /*     $NetBSD: kern_prot.c,v 1.33 1996/02/09 18:59:42 christos Exp $  */
 
 /*
 # include <machine/tcb.h>
 #endif
 
-/* ARGSUSED */
+inline void
+crset(struct ucred *newcr, const struct ucred *cr)
+{
+       KASSERT(cr->cr_ref > 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)
 {
@@ -65,7 +74,6 @@ sys_getpid(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_getthrid(struct proc *p, void *v, register_t *retval)
 {
@@ -74,7 +82,6 @@ sys_getthrid(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_getppid(struct proc *p, void *v, register_t *retval)
 {
@@ -136,7 +143,6 @@ found:
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_getuid(struct proc *p, void *v, register_t *retval)
 {
@@ -145,7 +151,6 @@ sys_getuid(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_geteuid(struct proc *p, void *v, register_t *retval)
 {
@@ -154,7 +159,6 @@ sys_geteuid(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_issetugid(struct proc *p, void *v, register_t *retval)
 {
@@ -165,7 +169,6 @@ sys_issetugid(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_getgid(struct proc *p, void *v, register_t *retval)
 {
@@ -179,7 +182,6 @@ sys_getgid(struct proc *p, void *v, register_t *retval)
  * via getgroups.  This syscall exists because it is somewhat painful to do
  * correctly in a library function.
  */
-/* ARGSUSED */
 int
 sys_getegid(struct proc *p, void *v, register_t *retval)
 {
@@ -214,7 +216,6 @@ sys_getgroups(struct proc *p, void *v, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_setsid(struct proc *p, void *v, register_t *retval)
 {
@@ -250,7 +251,6 @@ sys_setsid(struct proc *p, void *v, register_t *retval)
  *     there must exist some pid in same session having pgid (EPERM)
  * pid must not be session leader (EPERM)
  */
-/* ARGSUSED */
 int
 sys_setpgid(struct proc *curp, void *v, register_t *retval)
 {
@@ -305,7 +305,6 @@ out:
        return (error);
 }
 
-/* ARGSUSED */
 int
 sys_getresuid(struct proc *p, void *v, register_t *retval)
 {
@@ -332,7 +331,6 @@ sys_getresuid(struct proc *p, void *v, register_t *retval)
        return (error1 ? error1 : error2 ? error2 : error3);
 }
 
-/* ARGSUSED */
 int
 sys_setresuid(struct proc *p, void *v, register_t *retval)
 {
@@ -341,7 +339,8 @@ sys_setresuid(struct proc *p, void *v, register_t *retval)
                syscallarg(uid_t) euid;
                syscallarg(uid_t) suid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        uid_t ruid, euid, suid;
        int error;
 
@@ -349,9 +348,14 @@ sys_setresuid(struct proc *p, void *v, register_t *retval)
        euid = SCARG(uap, euid);
        suid = SCARG(uap, suid);
 
-       if ((ruid == -1 || ruid == uc->cr_ruid) &&
-           (euid == -1 || euid == uc->cr_uid) &&
-           (suid == -1 || suid == uc->cr_svuid))
+       /*
+        * make permission checks against the thread's ucred,
+        * but the actual changes will be to the process's ucred
+        */
+       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);                     /* no change */
 
        /*
@@ -381,31 +385,35 @@ sys_setresuid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
+       newcred = crget();
+       pruc = pr->ps_ucred;
+       crset(newcred, pruc);
 
        /*
         * Note that unlike the other set*uid() calls, each
         * uid type is set independently of the others.
         */
-       if (ruid != (uid_t)-1 && ruid != uc->cr_ruid) {
-               /*
-                * Transfer proc count to new user.
-                */
-               (void)chgproccnt(uc->cr_ruid, -1);
-               (void)chgproccnt(ruid, 1);
-               uc->cr_ruid = ruid;
-       }
+       if (ruid != (uid_t)-1)
+               newcred->cr_ruid = ruid;
        if (euid != (uid_t)-1)
-               uc->cr_uid = euid;
+               newcred->cr_uid = euid;
        if (suid != (uid_t)-1)
-               uc->cr_svuid = suid;
-
+               newcred->cr_svuid = suid;
+       pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
+
+       /* now that we can sleep, transfer proc count to new user */
+       if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
+               chgproccnt(pruc->cr_ruid, -1);
+               chgproccnt(ruid, 1);
+       }
+       crfree(pruc);
+
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_getresgid(struct proc *p, void *v, register_t *retval)
 {
@@ -432,7 +440,6 @@ sys_getresgid(struct proc *p, void *v, register_t *retval)
        return (error1 ? error1 : error2 ? error2 : error3);
 }
 
-/* ARGSUSED */
 int
 sys_setresgid(struct proc *p, void *v, register_t *retval)
 {
@@ -441,7 +448,8 @@ sys_setresgid(struct proc *p, void *v, register_t *retval)
                syscallarg(gid_t) egid;
                syscallarg(gid_t) sgid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        gid_t rgid, egid, sgid;
        int error;
 
@@ -449,9 +457,14 @@ sys_setresgid(struct proc *p, void *v, register_t *retval)
        egid = SCARG(uap, egid);
        sgid = SCARG(uap, sgid);
 
-       if ((rgid == -1 || rgid == uc->cr_rgid) &&
-           (egid == -1 || egid == uc->cr_gid) &&
-           (sgid == -1 || sgid == uc->cr_svgid))
+       /*
+        * make permission checks against the thread's ucred,
+        * but the actual changes will be to the process's ucred
+        */
+       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);                     /* no change */
 
        /*
@@ -481,25 +494,28 @@ sys_setresgid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
+       newcred = crget();
+       pruc = pr->ps_ucred;
+       crset(newcred, pruc);
 
        /*
         * Note that unlike the other set*gid() calls, each
         * gid type is set independently of the others.
         */
        if (rgid != (gid_t)-1)
-               uc->cr_rgid = rgid;
+               newcred->cr_rgid = rgid;
        if (egid != (gid_t)-1)
-               uc->cr_gid = egid;
+               newcred->cr_gid = egid;
        if (sgid != (gid_t)-1)
-               uc->cr_svgid = sgid;
-
+               newcred->cr_svgid = sgid;
+       pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
+       crfree(pruc);
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_setregid(struct proc *p, void *v, register_t *retval)
 {
@@ -507,12 +523,62 @@ sys_setregid(struct proc *p, void *v, register_t *retval)
                syscallarg(gid_t) rgid;
                syscallarg(gid_t) egid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
-       struct sys_setresgid_args sresgidargs;
+       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);
+
+       /*
+        * make permission checks against the thread's ucred,
+        * but the actual changes will be to the process's ucred
+        *
+        * The saved gid check here is complicated: we reset the
+        * saved gid to the real gid if the real gid is specified
+        * *and* either it's changing _or_ the saved gid won't equal
+        * the effective gid.  So, the svgid *won't* change when
+        * the rgid isn't specified or when the rgid isn't changing
+        * and the svgid equals the requested 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);                     /* no change */
+
+       /*
+        * Any of the real, effective, and saved gids may be changed
+        * to the current value of one of the three (root is not limited).
+        */
+       if (rgid != (gid_t)-1 &&
+           rgid != uc->cr_rgid &&
+           rgid != uc->cr_gid &&
+           rgid != uc->cr_svgid &&
+           (error = suser(p, 0)))
+               return (error);
+
+       if (egid != (gid_t)-1 &&
+           egid != uc->cr_rgid &&
+           egid != uc->cr_gid &&
+           egid != uc->cr_svgid &&
+           (error = suser(p, 0)))
+               return (error);
+
+       /*
+        * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
+        */
+       newcred = crget();
+       pruc = pr->ps_ucred;
+       crset(newcred, pruc);
 
-       rgid = SCARG(&sresgidargs, rgid) = SCARG(uap, rgid);
-       egid = SCARG(&sresgidargs, egid) = SCARG(uap, egid);
+       if (rgid != (gid_t)-1)
+               newcred->cr_rgid = rgid;
+       if (egid != (gid_t)-1)
+               newcred->cr_gid = egid;
 
        /*
         * The saved gid presents a bit of a dilemma, as it did not
@@ -520,16 +586,15 @@ sys_setregid(struct proc *p, void *v, register_t *retval)
         * gid when the real gid is specified and either its value would
         * change, or where the saved and effective gids are different.
         */
-       if (rgid != (gid_t)-1 && (rgid != uc->cr_rgid ||
-           uc->cr_svgid != (egid != (gid_t)-1 ? egid : uc->cr_gid)))
-               SCARG(&sresgidargs, sgid) = rgid;
-       else
-               SCARG(&sresgidargs, sgid) = (gid_t)-1;
-
-       return (sys_setresgid(p, &sresgidargs, retval));
+       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);
 }
 
-/* ARGSUSED */
 int
 sys_setreuid(struct proc *p, void *v, register_t *retval)
 {
@@ -537,12 +602,62 @@ sys_setreuid(struct proc *p, void *v, register_t *retval)
                syscallarg(uid_t) ruid;
                syscallarg(uid_t) euid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
-       struct sys_setresuid_args sresuidargs;
+       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);
 
-       ruid = SCARG(&sresuidargs, ruid) = SCARG(uap, ruid);
-       euid = SCARG(&sresuidargs, euid) = SCARG(uap, euid);
+       /*
+        * make permission checks against the thread's ucred,
+        * but the actual changes will be to the process's ucred
+        *
+        * The saved uid check here is complicated: we reset the
+        * saved uid to the real uid if the real uid is specified
+        * *and* either it's changing _or_ the saved uid won't equal
+        * the effective uid.  So, the svuid *won't* change when
+        * the ruid isn't specified or when the ruid isn't changing
+        * and the svuid equals the requested 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);                     /* no change */
+
+       /*
+        * Any of the real, effective, and saved uids may be changed
+        * to the current value of one of the three (root is not limited).
+        */
+       if (ruid != (uid_t)-1 &&
+           ruid != uc->cr_ruid &&
+           ruid != uc->cr_uid &&
+           ruid != uc->cr_svuid &&
+           (error = suser(p, 0)))
+               return (error);
+
+       if (euid != (uid_t)-1 &&
+           euid != uc->cr_ruid &&
+           euid != uc->cr_uid &&
+           euid != uc->cr_svuid &&
+           (error = suser(p, 0)))
+               return (error);
+
+       /*
+        * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
+        */
+       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;
 
        /*
         * The saved uid presents a bit of a dilemma, as it did not
@@ -550,31 +665,39 @@ sys_setreuid(struct proc *p, void *v, register_t *retval)
         * uid when the real uid is specified and either its value would
         * change, or where the saved and effective uids are different.
         */
-       if (ruid != (uid_t)-1 && (ruid != uc->cr_ruid ||
-           uc->cr_svuid != (euid != (uid_t)-1 ? euid : uc->cr_uid)))
-               SCARG(&sresuidargs, suid) = ruid;
-       else
-               SCARG(&sresuidargs, suid) = (uid_t)-1;
+       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);
+
+       /* now that we can sleep, transfer proc count to new user */
+       if (ruid != (uid_t)-1 && ruid != pruc->cr_ruid) {
+               chgproccnt(pruc->cr_ruid, -1);
+               chgproccnt(ruid, 1);
+       }
+       crfree(pruc);
 
-       return (sys_setresuid(p, &sresuidargs, retval));
+       return (0);
 }
 
-/* ARGSUSED */
 int
 sys_setuid(struct proc *p, void *v, register_t *retval)
 {
        struct sys_setuid_args /* {
                syscallarg(uid_t) uid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        uid_t uid;
-       int error;
+       int did_real, error;
 
        uid = SCARG(uap, uid);
 
-       if (uc->cr_uid == uid &&
-           uc->cr_ruid == uid &&
-           uc->cr_svuid == 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 &&
@@ -585,43 +708,51 @@ sys_setuid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
+       newcred = crget();
+       pruc = pr->ps_ucred;
+       crset(newcred, pruc);
 
        /*
         * Everything's okay, do it.
         */
-       if (uid == uc->cr_uid || suser(p, 0) == 0) {
-               /*
-                * Transfer proc count to new user.
-                */
-               if (uid != uc->cr_ruid) {
-                       (void)chgproccnt(uc->cr_ruid, -1);
-                       (void)chgproccnt(uid, 1);
-               }
-               uc->cr_ruid = uid;
-               uc->cr_svuid = uid;
+       if (uid == pruc->cr_uid || suser(p, 0) == 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);
+
+       /*
+        * Transfer proc count to new user.
+        */
+       if (did_real && uid != pruc->cr_ruid) {
+               chgproccnt(pruc->cr_ruid, -1);
+               chgproccnt(uid, 1);
        }
+       crfree(pruc);
 
-       uc->cr_uid = uid;
-       atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_seteuid(struct proc *p, void *v, register_t *retval)
 {
        struct sys_seteuid_args /* {
                syscallarg(uid_t) euid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        uid_t euid;
        int error;
 
        euid = SCARG(uap, euid);
 
-       if (uc->cr_uid == euid)
+       if (pr->ps_ucred->cr_uid == euid)
                return (0);
 
        if (euid != uc->cr_ruid && euid != uc->cr_svuid &&
@@ -630,29 +761,35 @@ sys_seteuid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
-       uc->cr_uid = euid;
+       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);
 }
 
-/* ARGSUSED */
 int
 sys_setgid(struct proc *p, void *v, register_t *retval)
 {
        struct sys_setgid_args /* {
                syscallarg(gid_t) gid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        gid_t gid;
        int error;
 
        gid = SCARG(uap, gid);
 
-       if (uc->cr_gid == gid &&
-           uc->cr_rgid == gid &&
-           uc->cr_svgid == 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 &&
@@ -663,33 +800,37 @@ sys_setgid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
+       newcred = crget();
+       pruc = pr->ps_ucred;
+       crset(newcred, pruc);
 
-       if (gid == uc->cr_gid || suser(p, 0) == 0) {
-               uc->cr_rgid = gid;
-               uc->cr_svgid = gid;
+       if (gid == pruc->cr_gid || suser(p, 0) == 0) {
+               newcred->cr_rgid = gid;
+               newcred->cr_svgid = gid;
        }
-
-       uc->cr_gid = gid;
+       newcred->cr_gid = gid;
+       pr->ps_ucred = newcred;
        atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
+       crfree(pruc);
        return (0);
 }
 
-/* ARGSUSED */
 int
 sys_setegid(struct proc *p, void *v, register_t *retval)
 {
        struct sys_setegid_args /* {
                syscallarg(gid_t) egid;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred, *uc = p->p_ucred;
        gid_t egid;
        int error;
 
        egid = SCARG(uap, egid);
 
-       if (uc->cr_gid == egid)
+       if (pr->ps_ucred->cr_gid == egid)
                return (0);
 
        if (egid != uc->cr_rgid && egid != uc->cr_svgid &&
@@ -698,14 +839,18 @@ sys_setegid(struct proc *p, void *v, register_t *retval)
 
        /*
         * Copy credentials so other references do not see our changes.
+        * ps_ucred may change during the crget().
         */
-       p->p_ucred = uc = crcopy(uc);
-       uc->cr_gid = egid;
+       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);
 }
 
-/* ARGSUSED */
 int
 sys_setgroups(struct proc *p, void *v, register_t *retval)
 {
@@ -713,7 +858,9 @@ sys_setgroups(struct proc *p, void *v, register_t *retval)
                syscallarg(int) gidsetsize;
                syscallarg(const gid_t *) gidset;
        } */ *uap = v;
-       struct ucred *uc = p->p_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *pruc, *newcred;
+       gid_t groups[NGROUPS];
        u_int ngrp;
        int error;
 
@@ -722,13 +869,18 @@ sys_setgroups(struct proc *p, void *v, register_t *retval)
        ngrp = SCARG(uap, gidsetsize);
        if (ngrp > NGROUPS)
                return (EINVAL);
-       p->p_ucred = uc = crcopy(uc);
-       error = copyin(SCARG(uap, gidset), uc->cr_groups, ngrp * sizeof(gid_t));
-       if (error)
-               return (error);
-       uc->cr_ngroups = ngrp;
-       atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
-       return (0);
+       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);
 }
 
 /*
@@ -850,7 +1002,6 @@ crfromxucred(struct ucred *cr, const struct xucred *xcr)
 /*
  * Get login name, if available.
  */
-/* ARGSUSED */
 int
 sys_getlogin(struct proc *p, void *v, register_t *retval)
 {
@@ -869,7 +1020,6 @@ sys_getlogin(struct proc *p, void *v, register_t *retval)
 /*
  * Set login name.
  */
-/* ARGSUSED */
 int
 sys_setlogin(struct proc *p, void *v, register_t *retval)
 {
@@ -929,3 +1079,20 @@ sys___get_tcb(struct proc *p, void *v, register_t *retval)
        *retval = (register_t)TCB_GET(p);
        return (0);
 }
+
+/*
+ * Refresh the thread's reference to the process's credentials
+ */
+void
+dorefreshcreds(struct process *pr, struct proc *p)
+{
+       struct ucred *uc = p->p_ucred;
+
+       KERNEL_LOCK();          /* XXX should be PROCESS_RLOCK(pr) */
+       if (uc != pr->ps_ucred) {
+               p->p_ucred = pr->ps_ucred;
+               crhold(p->p_ucred);
+               crfree(uc);
+       }
+       KERNEL_UNLOCK();
+}
index 0b39125..5356365 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_sig.c,v 1.163 2014/03/30 21:54:48 guenther Exp $ */
+/*     $OpenBSD: kern_sig.c,v 1.164 2014/04/18 11:51:17 guenther Exp $ */
 /*     $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $   */
 
 /*
@@ -79,7 +79,7 @@ void proc_stop(struct proc *p, int);
 void proc_stop_sweep(void *);
 struct timeout proc_stop_to;
 
-int cansignal(struct process *, struct process *, int);
+int cansignal(struct proc *, struct process *, int);
 
 struct pool sigacts_pool;      /* memory pool for sigacts structures */
 
@@ -87,9 +87,10 @@ struct pool sigacts_pool;    /* memory pool for sigacts structures */
  * Can thread p, send the signal signum to process qr?
  */
 int
-cansignal(struct process *pr, struct process *qr, int signum)
+cansignal(struct proc *p, struct process *qr, int signum)
 {
-       struct ucred *uc = pr->ps_ucred;
+       struct process *pr = p->p_p;
+       struct ucred *uc = p->p_ucred;
        struct ucred *quc = qr->ps_ucred;
 
        if (uc->cr_uid == 0)
@@ -589,7 +590,7 @@ sys_kill(struct proc *cp, void *v, register_t *retval)
                                return (ESRCH);
                        if (p->p_flag & P_THREAD)
                                return (ESRCH);
-                       if (!cansignal(cp->p_p, p->p_p, signum))
+                       if (!cansignal(cp, p->p_p, signum))
                                return (EPERM);
                }
 
@@ -628,7 +629,7 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                LIST_FOREACH(pr, &allprocess, ps_list) {
                        p = pr->ps_mainproc;
                        if (pr->ps_pid <= 1 || p->p_flag & P_SYSTEM ||
-                           pr == cp->p_p || !cansignal(cp->p_p, pr, signum))
+                           pr == cp->p_p || !cansignal(cp, pr, signum))
                                continue;
                        nfound++;
                        if (signum)
@@ -648,7 +649,7 @@ killpg1(struct proc *cp, int signum, int pgid, int all)
                LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
                        p = pr->ps_mainproc;
                        if (pr->ps_pid <= 1 || p->p_flag & P_SYSTEM ||
-                           !cansignal(cp->p_p, pr, signum))
+                           !cansignal(cp, pr, signum))
                                continue;
                        nfound++;
                        if (signum && P_ZOMBIE(p) == 0)
index b7d7efe..98fe8f6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cdefs.h,v 1.38 2014/03/19 05:11:06 guenther Exp $     */
+/*     $OpenBSD: cdefs.h,v 1.39 2014/04/18 11:51:17 guenther Exp $     */
 /*     $NetBSD: cdefs.h,v 1.16 1996/04/03 20:46:39 christos Exp $      */
 
 /*
 #define        __END_HIDDEN_DECLS      __END_EXTERN_C
 #endif
 
-#define        __BEGIN_DECLS   __BEGIN_PUBLIC_DECLS
-#define        __END_DECLS     __END_PUBLIC_DECLS
+#define        __BEGIN_DECLS   __BEGIN_EXTERN_C
+#define        __END_DECLS     __END_EXTERN_C
 
 /*
  * "The nice thing about standards is that there are so many to choose from."
index a65f1f2..33ec6d8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: proc.h,v 1.183 2014/03/31 22:20:15 matthew Exp $      */
+/*     $OpenBSD: proc.h,v 1.184 2014/04/18 11:51:17 guenther Exp $     */
 /*     $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $       */
 
 /*-
@@ -260,7 +260,6 @@ struct proc {
        /* substructures: */
        struct  filedesc *p_fd;         /* Ptr to open files structure. */
        struct  vmspace *p_vmspace;     /* Address space. */
-#define        p_ucred         p_p->ps_ucred
 #define        p_rlimit        p_p->ps_limit->pl_rlimit
 
        int     p_flag;                 /* P_* flags. */
@@ -321,6 +320,7 @@ struct proc {
 # define TCB_GET(p)            ((p)->p_tcb)
 #endif
 
+       struct  ucred *p_ucred;         /* cached credentials */
        struct  sigaltstack p_sigstk;   /* sp & on stack state variable */
 
        u_long  p_prof_addr;    /* tmp storage for profiling addr until AST */
@@ -488,6 +488,17 @@ void       cpu_exit(struct proc *);
 int    fork1(struct proc *, int, void *, pid_t *, void (*)(void *),
            void *, register_t *, struct proc **);
 int    groupmember(gid_t, struct ucred *);
+void   dorefreshcreds(struct process *, struct proc *);
+
+static inline void
+refreshcreds(struct proc *p)
+{
+       struct process *pr = p->p_p;
+
+       /* this is an unlocked access to ps_ucred, but the result is benign */
+       if (pr->ps_ucred != p->p_ucred)
+               dorefreshcreds(pr, p);
+}
 
 enum single_thread_mode {
        SINGLE_SUSPEND,         /* other threads to stop wherever they are */
index bb12b9b..20293f0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscall_mi.h,v 1.2 2012/08/07 23:22:38 guenther Exp $ */
+/*     $OpenBSD: syscall_mi.h,v 1.3 2014/04/18 11:51:17 guenther Exp $ */
 
 /*
  * Copyright (c) 1982, 1986, 1989, 1993
@@ -55,6 +55,9 @@ mi_syscall(struct proc *p, register_t code, const struct sysent *callp,
        int lock = !(callp->sy_flags & SY_NOLOCK);
        int error;
 
+       /* refresh the thread's cache of the process's creds */
+       refreshcreds(p);
+
 #ifdef SYSCALL_DEBUG
        KERNEL_LOCK();
        scdebug_call(p, code, argp);
index 5bb37e5..dd22f5a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ucred.h,v 1.8 2014/03/30 21:54:48 guenther Exp $      */
+/*     $OpenBSD: ucred.h,v 1.9 2014/04/18 11:51:17 guenther Exp $      */
 /*     $NetBSD: ucred.h,v 1.12 1995/06/01 22:44:50 jtc Exp $   */
 
 /*
@@ -40,6 +40,9 @@
  */
 struct ucred {
        u_int   cr_ref;                 /* reference count */
+
+/* The following fields are all copied by crset() */
+#define        cr_startcopy    cr_uid
        uid_t   cr_uid;                 /* effective user id */
        uid_t   cr_ruid;                /* Real user id. */
        uid_t   cr_svuid;               /* Saved effective user id. */
@@ -68,6 +71,7 @@ struct xucred {
 #define SUSER_NOACCT   0x1     /* don't mark accounting flags */
 
 void           crfromxucred(struct ucred *, const struct xucred *);
+void           crset(struct ucred *, const struct ucred *);
 struct ucred   *crcopy(struct ucred *cr);
 struct ucred   *crdup(struct ucred *cr);
 void           crfree(struct ucred *cr);