Synopsis: Close-on-exec, SUID and ptrace(2) NetBSD versions: 1.5, 1.5.1, 1.5.2 Thanks to: Christos Zoulas and Havard Eidnes Reported in NetBSD Security Advisory: NetBSD-SA2002-001 Index: sys/kern/kern_exec.c =================================================================== RCS file: /cvsroot/syssrc/sys/kern/kern_exec.c,v retrieving revision 1.100.2.4 diff -u -r1.100.2.4 kern_exec.c --- sys/kern/kern_exec.c 2001/07/19 13:36:19 1.100.2.4 +++ sys/kern/kern_exec.c 2002/01/14 14:29:09 @@ -98,6 +98,15 @@ struct nameidata *ndp; size_t resid; + /* + * Lock the process and set the P_INEXEC flag to indicate that + * it should be left alone until we're done here. This is + * necessary to avoid race conditions - e.g. in ptrace() - + * that might allow a local user to illicitly obtain elevated + * privileges. + */ + p->p_flag |= P_INEXEC; + ndp = epp->ep_ndp; ndp->ni_cnd.cn_nameiop = LOOKUP; ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF | SAVENAME; @@ -498,9 +507,11 @@ ktremul(p->p_tracep, p, p->p_emul->e_name); #endif + p->p_flag &= ~P_INEXEC; return (EJUSTRETURN); bad: + p->p_flag &= ~P_INEXEC; /* free the vmspace-creation commands, and release their references */ kill_vmcmds(&pack.ep_vmcmds); /* kill any opened file descriptor, if necessary */ @@ -516,10 +527,12 @@ uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); freehdr: + p->p_flag &= ~P_INEXEC; FREE(pack.ep_hdr, M_EXEC); return error; exec_abort: + p->p_flag &= ~P_INEXEC; /* * the old process doesn't exist anymore. exit gracefully. * get rid of the (new) address space we have created, if any, get rid Index: sys/sys/proc.h =================================================================== RCS file: /cvsroot/syssrc/sys/sys/proc.h,v retrieving revision 1.74.2.2 diff -u -r1.74.2.2 proc.h --- sys/sys/proc.h 2000/04/30 20:12:04 1.74.2.2 +++ sys/sys/proc.h 2002/01/14 14:29:20 @@ -210,24 +210,25 @@ #define SZOMB 5 /* Awaiting collection by parent. */ /* These flags are kept in p_flag. */ -#define P_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */ -#define P_CONTROLT 0x00002 /* Has a controlling terminal. */ -#define P_INMEM 0x00004 /* Loaded into memory. */ -#define P_NOCLDSTOP 0x00008 /* No SIGCHLD when children stop. */ -#define P_PPWAIT 0x00010 /* Parent is waiting for child to exec/exit. */ -#define P_PROFIL 0x00020 /* Has started profiling. */ -#define P_SELECT 0x00040 /* Selecting; wakeup/waiting danger. */ -#define P_SINTR 0x00080 /* Sleep is interruptible. */ -#define P_SUGID 0x00100 /* Had set id privileges since last exec. */ -#define P_SYSTEM 0x00200 /* System proc: no sigs, stats or swapping. */ -#define P_TIMEOUT 0x00400 /* Timing out during sleep. */ -#define P_TRACED 0x00800 /* Debugged process being traced. */ -#define P_WAITED 0x01000 /* Debugging process has waited for child. */ -#define P_WEXIT 0x02000 /* Working on exiting. */ -#define P_EXEC 0x04000 /* Process called exec. */ -#define P_OWEUPC 0x08000 /* Owe process an addupc() call at next ast. */ -#define P_FSTRACE 0x10000 /* Debugger process being traced by procfs */ -#define P_NOCLDWAIT 0x20000 /* No zombies if child dies */ +#define P_ADVLOCK 0x000001 /* Process may hold a POSIX advisory lock. */ +#define P_CONTROLT 0x000002 /* Has a controlling terminal. */ +#define P_INMEM 0x000004 /* Loaded into memory. */ +#define P_NOCLDSTOP 0x000008 /* No SIGCHLD when children stop. */ +#define P_PPWAIT 0x000010 /* Parent is waiting for child to exec/exit. */ +#define P_PROFIL 0x000020 /* Has started profiling. */ +#define P_SELECT 0x000040 /* Selecting; wakeup/waiting danger. */ +#define P_SINTR 0x000080 /* Sleep is interruptible. */ +#define P_SUGID 0x000100 /* Had set id privileges since last exec. */ +#define P_SYSTEM 0x000200 /* System proc: no sigs, stats or swapping. */ +#define P_TIMEOUT 0x000400 /* Timing out during sleep. */ +#define P_TRACED 0x000800 /* Debugged process being traced. */ +#define P_WAITED 0x001000 /* Debugging process has waited for child. */ +#define P_WEXIT 0x002000 /* Working on exiting. */ +#define P_EXEC 0x004000 /* Process called exec. */ +#define P_OWEUPC 0x008000 /* Owe process an addupc() call at next ast. */ +#define P_FSTRACE 0x010000 /* Debugger process being traced by procfs */ +#define P_NOCLDWAIT 0x020000 /* No zombies if child dies */ +#define P_INEXEC 0x100000 /* Process is exec'ing and cannot be traced */ /* * These flags are kept in schedflags. schedflags may be modified Index: sys/miscfs/procfs/procfs_vnops.c =================================================================== RCS file: /cvsroot/syssrc/sys/miscfs/procfs/procfs_vnops.c,v retrieving revision 1.61.2.1 diff -u -r1.61.2.1 procfs_vnops.c --- sys/miscfs/procfs/procfs_vnops.c 1999/08/28 23:28:16 1.61.2.1 +++ sys/miscfs/procfs/procfs_vnops.c 2002/01/14 14:29:33 @@ -233,7 +233,7 @@ return (EBUSY); if ((error = procfs_checkioperm(p1, p2)) != 0) - return (EPERM); + return (error); if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); Index: sys/miscfs/procfs/procfs_regs.c =================================================================== RCS file: /cvsroot/syssrc/sys/miscfs/procfs/procfs_regs.c,v retrieving revision 1.14 retrieving revision 1.15 diff -c -p -r1.14 -r1.15 *** sys/miscfs/procfs/procfs_regs.c 2001/12/05 00:58:05 1.14 --- sys/miscfs/procfs/procfs_regs.c 2002/01/12 18:51:56 1.15 *************** procfs_doregs(curp, p, pfs, uio) *** 65,71 **** int kl; if ((error = procfs_checkioperm(curp, p)) != 0) ! return (EPERM); kl = sizeof(r); kv = (char *) &r; --- 65,71 ---- int kl; if ((error = procfs_checkioperm(curp, p)) != 0) ! return error; kl = sizeof(r); kv = (char *) &r; Index: sys/miscfs/procfs/procfs_mem.c =================================================================== RCS file: /cvsroot/syssrc/sys/miscfs/procfs/procfs_mem.c,v retrieving revision 1.29 retrieving revision 1.30 diff -c -p -r1.29 -r1.30 *** sys/miscfs/procfs/procfs_mem.c 2001/11/10 13:33:43 1.29 --- sys/miscfs/procfs/procfs_mem.c 2002/01/12 18:51:31 1.30 *************** procfs_checkioperm(p, t) *** 122,128 **** /* * You cannot attach to a processes mem/regs if: * ! * (1) it's not owned by you, or is set-id on exec * (unless you're root), or... */ if ((t->p_cred->p_ruid != p->p_cred->p_ruid || --- 122,134 ---- /* * You cannot attach to a processes mem/regs if: * ! * (1) It is currently exec'ing ! */ ! if (ISSET(t->p_flag, P_INEXEC)) ! return (EAGAIN); ! ! /* ! * (2) it's not owned by you, or is set-id on exec * (unless you're root), or... */ if ((t->p_cred->p_ruid != p->p_cred->p_ruid || *************** procfs_checkioperm(p, t) *** 131,137 **** return (error); /* ! * (2) ...it's init, which controls the security level * of the entire system, and the system was not * compiled with permanetly insecure mode turned on. */ --- 137,143 ---- return (error); /* ! * (3) ...it's init, which controls the security level * of the entire system, and the system was not * compiled with permanetly insecure mode turned on. */ *************** procfs_checkioperm(p, t) *** 139,150 **** return (EPERM); /* ! * (3) the tracer is chrooted, and its root directory is ! * not at or above the root directory of the tracee */ - if (!proc_isunder(t, p)) ! return EPERM; return (0); } --- 145,155 ---- return (EPERM); /* ! * (4) the tracer is chrooted, and its root directory is ! * not at or above the root directory of the tracee */ if (!proc_isunder(t, p)) ! return (EPERM); return (0); } Index: sys/miscfs/procfs/procfs_ctl.c =================================================================== RCS file: /cvsroot/syssrc/sys/miscfs/procfs/procfs_ctl.c,v retrieving revision 1.21 retrieving revision 1.22 diff -c -p -r1.21 -r1.22 *** sys/miscfs/procfs/procfs_ctl.c 2001/12/05 00:58:05 1.21 --- sys/miscfs/procfs/procfs_ctl.c 2002/01/11 22:02:56 1.22 *************** procfs_control(curp, p, op, sig) *** 108,117 **** int s, error; /* * Attach - attaches the target process for debugging * by the calling process. */ - switch (op) { case PROCFS_CTL_ATTACH: /* * You can't attach to a process if: --- 108,123 ---- int s, error; /* + * You cannot do anything to the process if it is currently exec'ing + */ + if (ISSET(p->p_flag, P_INEXEC)) + return (EAGAIN); + + switch (op) { + /* * Attach - attaches the target process for debugging * by the calling process. */ case PROCFS_CTL_ATTACH: /* * You can't attach to a process if: Index: sys/kern/sys_process.c =================================================================== RCS file: /cvsroot/syssrc/sys/kern/sys_process.c,v retrieving revision 1.71 retrieving revision 1.72 diff -c -p -r1.71 -r1.72 *** sys/kern/sys_process.c 2001/12/05 00:58:05 1.71 --- sys/kern/sys_process.c 2002/01/11 21:16:28 1.72 *************** sys_ptrace(p, v, retval) *** 106,111 **** --- 106,115 ---- if ((t = pfind(SCARG(uap, pid))) == NULL) return (ESRCH); } + + /* Can't trace a process that's currently exec'ing. */ + if ((t->p_flag & P_INEXEC) != 0) + return EAGAIN; /* Make sure we can operate on it. */ switch (SCARG(uap, req)) {