Synopsis: Close-on-exec, SUID and ptrace(2) NetBSD versions: 1.5, 1.5.1, 1.5.2 Thanks to: Christos Zoulas Reported in NetBSD Security Advisory: NetBSD-SA2001-001 Index: sys/miscfs/procfs/procfs_vnops.c =================================================================== RCS file: /cvsroot/syssrc/sys/miscfs/procfs/procfs_vnops.c,v retrieving revision 1.70.4.1 retrieving revision 1.70.4.2 diff -c -p -r1.70.4.1 -r1.70.4.2 *** sys/miscfs/procfs/procfs_vnops.c 2001/03/30 21:50:16 1.70.4.1 --- sys/miscfs/procfs/procfs_vnops.c 2002/01/14 10:59:32 1.70.4.2 *************** procfs_open(v) *** 253,259 **** return (EBUSY); if ((error = procfs_checkioperm(p1, p2)) != 0) ! return (EPERM); if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); --- 253,259 ---- return (EBUSY); if ((error = procfs_checkioperm(p1, p2)) != 0) ! 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.11.28.1 retrieving revision 1.11.28.2 diff -c -p -r1.11.28.1 -r1.11.28.2 *** sys/miscfs/procfs/procfs_regs.c 2001/03/30 21:48:56 1.11.28.1 --- sys/miscfs/procfs/procfs_regs.c 2002/01/14 10:55:34 1.11.28.2 *************** procfs_doregs(curp, p, pfs, uio) *** 63,69 **** int kl; if ((error = procfs_checkioperm(curp, p)) != 0) ! return (EPERM); kl = sizeof(r); kv = (char *) &r; --- 63,69 ---- 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.23 retrieving revision 1.23.2.1 diff -c -p -r1.23 -r1.23.2.1 *** sys/miscfs/procfs/procfs_mem.c 1999/03/25 04:45:57 1.23 --- sys/miscfs/procfs/procfs_mem.c 2002/01/14 15:20:24 1.23.2.1 *************** procfs_checkioperm(p, t) *** 140,146 **** /* * 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 || --- 140,152 ---- /* * 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) *** 149,155 **** 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. */ --- 155,161 ---- 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) *** 157,168 **** 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); } --- 163,173 ---- 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.17 retrieving revision 1.17.12.1 diff -c -p -r1.17 -r1.17.12.1 *** sys/miscfs/procfs/procfs_ctl.c 1999/07/22 18:13:38 1.17 --- sys/miscfs/procfs/procfs_ctl.c 2002/01/12 01:09:56 1.17.12.1 *************** procfs_control(curp, p, op, sig) *** 106,115 **** int 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: --- 106,121 ---- int 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/sys/proc.h =================================================================== RCS file: /cvsroot/syssrc/sys/sys/proc.h,v retrieving revision 1.98.2.2 retrieving revision 1.98.2.3 diff -c -p -r1.98.2.2 -r1.98.2.3 *** sys/sys/proc.h 2000/09/06 08:41:41 1.98.2.2 --- sys/sys/proc.h 2002/01/12 01:02:20 1.98.2.3 *************** struct proc { *** 242,266 **** #define P_ZOMBIE(p) ((p)->p_stat == SZOMB || (p)->p_stat == SDEAD) /* 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_32 0x40000 /* 32-bit process -- only used on 64-bit kernels */ /* * Macro to compute the exit signal to be delivered. --- 242,267 ---- #define P_ZOMBIE(p) ((p)->p_stat == SZOMB || (p)->p_stat == SDEAD) /* These flags are kept in p_flag. */ ! #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_32 0x040000 /* 32-bit process -- only used on 64-bit kernels */ ! #define P_INEXEC 0x100000 /* Process is exec'ing and cannot be traced */ /* * Macro to compute the exit signal to be delivered. Index: sys/kern/sys_process.c =================================================================== RCS file: /cvsroot/syssrc/sys/kern/sys_process.c,v retrieving revision 1.61.18.2 retrieving revision 1.61.18.3 diff -c -p -r1.61.18.2 -r1.61.18.3 *** sys/kern/sys_process.c 2001/03/30 21:47:02 1.61.18.2 --- sys/kern/sys_process.c 2002/01/12 01:03:07 1.61.18.3 *************** sys_ptrace(p, v, retval) *** 104,109 **** --- 104,113 ---- 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)) { Index: sys/kern/kern_exec.c =================================================================== RCS file: /cvsroot/syssrc/sys/kern/kern_exec.c,v retrieving revision 1.110.4.5 retrieving revision 1.110.4.7 diff -c -p -r1.110.4.5 -r1.110.4.7 *** sys/kern/kern_exec.c 2001/06/16 20:19:30 1.110.4.5 --- sys/kern/kern_exec.c 2002/01/14 10:49:30 1.110.4.7 *************** sys_execve(p, v, retval) *** 233,238 **** --- 233,247 ---- extern struct emul emul_netbsd; /* + * 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; + + /* * figure out the maximum size of an exec header, if necessary. * XXX should be able to keep LKM code from modifying exec switch * when we're still using it, but... *************** sys_execve(p, v, retval) *** 548,556 **** --- 557,567 ---- ktremul(p); #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 */ *************** bad: *** 566,575 **** --- 577,588 ---- 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