From owner-svn-src-all@FreeBSD.ORG Sun Jan 18 15:13:14 2015 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id C13BE7D3; Sun, 18 Jan 2015 15:13:14 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id AB94D9A2; Sun, 18 Jan 2015 15:13:14 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t0IFDE8M084176; Sun, 18 Jan 2015 15:13:14 GMT (envelope-from kib@FreeBSD.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t0IFDBLu084161; Sun, 18 Jan 2015 15:13:11 GMT (envelope-from kib@FreeBSD.org) Message-Id: <201501181513.t0IFDBLu084161@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: kib set sender to kib@FreeBSD.org using -f From: Konstantin Belousov Date: Sun, 18 Jan 2015 15:13:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r277322 - in head: lib/libc/sys sys/compat/freebsd32 sys/kern sys/sys X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 18 Jan 2015 15:13:14 -0000 Author: kib Date: Sun Jan 18 15:13:11 2015 New Revision: 277322 URL: https://svnweb.freebsd.org/changeset/base/277322 Log: Add procctl(2) PROC_TRACE_CTL command to enable or disable debugger attachment to the process. Note that the command is not intended to be a security measure, rather it is an obfuscation feature, implemented for parity with other operating systems. Discussed with: jilles, rwatson Man page fixes by: rwatson Sponsored by: The FreeBSD Foundation MFC after: 1 week Modified: head/lib/libc/sys/procctl.2 head/sys/compat/freebsd32/freebsd32_misc.c head/sys/kern/kern_exec.c head/sys/kern/kern_fork.c head/sys/kern/kern_procctl.c head/sys/kern/kern_prot.c head/sys/kern/kern_sig.c head/sys/sys/priv.h head/sys/sys/proc.h head/sys/sys/procctl.h Modified: head/lib/libc/sys/procctl.2 ============================================================================== --- head/lib/libc/sys/procctl.2 Sun Jan 18 15:03:26 2015 (r277321) +++ head/lib/libc/sys/procctl.2 Sun Jan 18 15:13:11 2015 (r277322) @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 16, 2014 +.Dd December 29, 2014 .Dt PROCCTL 2 .Os .Sh NAME @@ -275,7 +275,61 @@ delivery failed, e.g. due to the permiss If no such process exist, the .Fa rk_fpid field is set to -1. +.It Dv PROC_TRACE_CTL +Enable or disable tracing of the specified process(es), according to the +value of the integer argument. +Tracing includes attachment to the process using +.Xr ptrace 2 +and +.Xr ktrace 2 , +debugging sysctls, +.Xr hwpmc 4 , +.Xr dtrace 1 +and core dumping. +Possible values for the +.Fa data +argument are: +.Bl -tag -width "Dv PROC_TRACE_CTL_DISABLE_EXEC" +.It Dv PROC_TRACE_CTL_ENABLE +Enable tracing, after it was disabled by +.Dv PROC_TRACE_CTL_DISABLE . +Only allowed for self. +.It Dv PROC_TRACE_CTL_DISABLE +Disable tracing for the specified process. +Tracing is re-enabled when the process changes the executing +program with +.Xr execve 2 +syscall. +A child inherits the trace settings from the parent on +.Xr fork 2 . +.It Dv PROC_TRACE_CTL_DISABLE_EXEC +Same as +.Dv PROC_TRACE_CTL_DISABLE , +but the setting persist for the process even after +.Xr execve 2 . +.El +.It Dv PROC_TRACE_STATUS +Returns the current tracing status for the specified process in +the integer variable pointed to by +.Fa data . +If tracing is disabled, +.Fa data +is set to -1. +If tracing is enabled, but no debugger is attached by +.Xr ptrace 2 +syscall, +.Fa data +is set to 0. +If a debugger is attached, +.Fa data +is set to the pid of the debugger process. .El +.Sh NOTES +Disabling tracing on a process should not be considered a security +feature, as it is bypassable both by the kernel and privileged processes, +and via other system mechanisms. +As such, it should not be relied on to reliably protect cryptographic +keying material or other confidential data. .Sh RETURN VALUES If an error occurs, a value of -1 is returned and .Va errno @@ -343,11 +397,34 @@ The .Dv PROC_REAP_ACQUIRE request was issued by a process that had already acquired reaper status and has not yet released it. +.It Bq Er EBUSY +The +.Dv PROC_TRACE_CTL +request was issued for a process already being traced. +.It Bq Er EPERM +The +.Dv PROC_TRACE_CTL +request to re-enable tracing of the process ( +.Dv PROC_TRACE_CTL_ENABLE ) , +or to disable persistence of the +.Dv PROC_TRACE_CTL_DISABLE +on +.Xr execve 2 +was issued for a non-current process. +.It Bq Er EINVAL +The value of the integer +.Fa data +parameter for the +.Dv PROC_TRACE_CTL +request is invalid. .El .Sh SEE ALSO +.Xr dtrace 1 , .Xr kill 2 , +.Xr ktrace 2 , .Xr ptrace 2 , .Xr wait 2 , +.Xr hwpmc 4 , .Xr init 8 .Sh HISTORY The Modified: head/sys/compat/freebsd32/freebsd32_misc.c ============================================================================== --- head/sys/compat/freebsd32/freebsd32_misc.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/compat/freebsd32/freebsd32_misc.c Sun Jan 18 15:13:11 2015 (r277322) @@ -2969,6 +2969,7 @@ freebsd32_procctl(struct thread *td, str switch (uap->com) { case PROC_SPROTECT: + case PROC_TRACE_CTL: error = copyin(PTRIN(uap->data), &flags, sizeof(flags)); if (error != 0) return (error); @@ -2997,6 +2998,9 @@ freebsd32_procctl(struct thread *td, str return (error); data = &x.rk; break; + case PROC_TRACE_STATUS: + data = &flags; + break; default: return (EINVAL); } @@ -3012,6 +3016,10 @@ freebsd32_procctl(struct thread *td, str if (error == 0) error = error1; break; + case PROC_TRACE_STATUS: + if (error == 0) + error = copyout(&flags, uap->data, sizeof(flags)); + break; } return (error); } Modified: head/sys/kern/kern_exec.c ============================================================================== --- head/sys/kern/kern_exec.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/kern/kern_exec.c Sun Jan 18 15:13:11 2015 (r277322) @@ -634,6 +634,8 @@ interpret: * it that it now has its own resources back */ p->p_flag |= P_EXEC; + if ((p->p_flag2 & P2_NOTRACE_EXEC) == 0) + p->p_flag2 &= ~P2_NOTRACE; if (p->p_flag & P_PPWAIT) { p->p_flag &= ~(P_PPWAIT | P_PPTRACE); cv_broadcast(&p->p_pwait); Modified: head/sys/kern/kern_fork.c ============================================================================== --- head/sys/kern/kern_fork.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/kern/kern_fork.c Sun Jan 18 15:13:11 2015 (r277322) @@ -494,7 +494,7 @@ do_fork(struct thread *td, int flags, st * Increase reference counts on shared objects. */ p2->p_flag = P_INMEM; - p2->p_flag2 = 0; + p2->p_flag2 = p1->p_flag2 & (P2_NOTRACE | P2_NOTRACE_EXEC); p2->p_swtick = ticks; if (p1->p_flag & P_PROFIL) startprofclock(p2); Modified: head/sys/kern/kern_procctl.c ============================================================================== --- head/sys/kern/kern_procctl.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/kern/kern_procctl.c Sun Jan 18 15:13:11 2015 (r277322) @@ -280,6 +280,62 @@ reap_kill(struct thread *td, struct proc return (error); } +static int +trace_ctl(struct thread *td, struct proc *p, int state) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + + /* + * Ktrace changes p_traceflag from or to zero under the + * process lock, so the test does not need to acquire ktrace + * mutex. + */ + if ((p->p_flag & P_TRACED) != 0 || p->p_traceflag != 0) + return (EBUSY); + + switch (state) { + case PROC_TRACE_CTL_ENABLE: + if (td->td_proc != p) + return (EPERM); + p->p_flag2 &= ~(P2_NOTRACE | P2_NOTRACE_EXEC); + break; + case PROC_TRACE_CTL_DISABLE_EXEC: + p->p_flag2 |= P2_NOTRACE_EXEC | P2_NOTRACE; + break; + case PROC_TRACE_CTL_DISABLE: + if ((p->p_flag2 & P2_NOTRACE_EXEC) != 0) { + KASSERT((p->p_flag2 & P2_NOTRACE) != 0, + ("dandling P2_NOTRACE_EXEC")); + if (td->td_proc != p) + return (EPERM); + p->p_flag2 &= ~P2_NOTRACE_EXEC; + } else { + p->p_flag2 |= P2_NOTRACE; + } + break; + default: + return (EINVAL); + } + return (0); +} + +static int +trace_status(struct thread *td, struct proc *p, int *data) +{ + + if ((p->p_flag2 & P2_NOTRACE) != 0) { + KASSERT((p->p_flag & P_TRACED) == 0, + ("%d traced but tracing disabled", p->p_pid)); + *data = -1; + } else if ((p->p_flag & P_TRACED) != 0) { + *data = p->p_pptr->p_pid; + } else { + *data = 0; + } + return (0); +} + #ifndef _SYS_SYSPROTO_H_ struct procctl_args { idtype_t idtype; @@ -302,6 +358,7 @@ sys_procctl(struct thread *td, struct pr switch (uap->com) { case PROC_SPROTECT: + case PROC_TRACE_CTL: error = copyin(uap->data, &flags, sizeof(flags)); if (error != 0) return (error); @@ -328,6 +385,9 @@ sys_procctl(struct thread *td, struct pr return (error); data = &x.rk; break; + case PROC_TRACE_STATUS: + data = &flags; + break; default: return (EINVAL); } @@ -342,6 +402,10 @@ sys_procctl(struct thread *td, struct pr if (error == 0) error = error1; break; + case PROC_TRACE_STATUS: + if (error == 0) + error = copyout(&flags, uap->data, sizeof(flags)); + break; } return (error); } @@ -364,6 +428,10 @@ kern_procctl_single(struct thread *td, s return (reap_getpids(td, p, data)); case PROC_REAP_KILL: return (reap_kill(td, p, data)); + case PROC_TRACE_CTL: + return (trace_ctl(td, p, *(int *)data)); + case PROC_TRACE_STATUS: + return (trace_status(td, p, data)); default: return (EINVAL); } @@ -375,6 +443,7 @@ kern_procctl(struct thread *td, idtype_t struct pgrp *pg; struct proc *p; int error, first_error, ok; + bool tree_locked; switch (com) { case PROC_REAP_ACQUIRE: @@ -382,6 +451,7 @@ kern_procctl(struct thread *td, idtype_t case PROC_REAP_STATUS: case PROC_REAP_GETPIDS: case PROC_REAP_KILL: + case PROC_TRACE_STATUS: if (idtype != P_PID) return (EINVAL); } @@ -391,11 +461,17 @@ kern_procctl(struct thread *td, idtype_t case PROC_REAP_STATUS: case PROC_REAP_GETPIDS: case PROC_REAP_KILL: + case PROC_TRACE_CTL: sx_slock(&proctree_lock); + tree_locked = true; break; case PROC_REAP_ACQUIRE: case PROC_REAP_RELEASE: sx_xlock(&proctree_lock); + tree_locked = true; + break; + case PROC_TRACE_STATUS: + tree_locked = false; break; default: return (EINVAL); @@ -456,6 +532,7 @@ kern_procctl(struct thread *td, idtype_t error = EINVAL; break; } - sx_unlock(&proctree_lock); + if (tree_locked) + sx_unlock(&proctree_lock); return (error); } Modified: head/sys/kern/kern_prot.c ============================================================================== --- head/sys/kern/kern_prot.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/kern/kern_prot.c Sun Jan 18 15:13:11 2015 (r277322) @@ -1714,6 +1714,13 @@ p_candebug(struct thread *td, struct pro if ((p->p_flag & P_INEXEC) != 0) return (EBUSY); + /* Denied explicitely */ + if ((p->p_flag2 & P2_NOTRACE) != 0) { + error = priv_check(td, PRIV_DEBUG_DENIED); + if (error != 0) + return (error); + } + return (0); } Modified: head/sys/kern/kern_sig.c ============================================================================== --- head/sys/kern/kern_sig.c Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/kern/kern_sig.c Sun Jan 18 15:13:11 2015 (r277322) @@ -3247,7 +3247,8 @@ coredump(struct thread *td) MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td); _STOPEVENT(p, S_CORE, 0); - if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0)) { + if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0) || + (p->p_flag2 & P2_NOTRACE) != 0) { PROC_UNLOCK(p); return (EFAULT); } Modified: head/sys/sys/priv.h ============================================================================== --- head/sys/sys/priv.h Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/sys/priv.h Sun Jan 18 15:13:11 2015 (r277322) @@ -111,6 +111,7 @@ #define PRIV_DEBUG_DIFFCRED 80 /* Exempt debugging other users. */ #define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */ #define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */ +#define PRIV_DEBUG_DENIED 83 /* Exempt P2_NOTRACE. */ /* * Dtrace privileges. Modified: head/sys/sys/proc.h ============================================================================== --- head/sys/sys/proc.h Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/sys/proc.h Sun Jan 18 15:13:11 2015 (r277322) @@ -675,6 +675,8 @@ struct proc { /* These flags are kept in p_flag2. */ #define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */ +#define P2_NOTRACE 0x00000002 /* No ptrace(2) attach or coredumps. */ +#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ Modified: head/sys/sys/procctl.h ============================================================================== --- head/sys/sys/procctl.h Sun Jan 18 15:03:26 2015 (r277321) +++ head/sys/sys/procctl.h Sun Jan 18 15:13:11 2015 (r277322) @@ -41,6 +41,8 @@ #define PROC_REAP_STATUS 4 /* reaping status */ #define PROC_REAP_GETPIDS 5 /* get descendants */ #define PROC_REAP_KILL 6 /* kill descendants */ +#define PROC_TRACE_CTL 7 /* en/dis ptrace and coredumps */ +#define PROC_TRACE_STATUS 8 /* query tracing status */ /* Operations for PROC_SPROTECT (passed in integer arg). */ #define PPROT_OP(x) ((x) & 0xf) @@ -96,6 +98,10 @@ struct procctl_reaper_kill { #define REAPER_KILL_CHILDREN 0x00000001 #define REAPER_KILL_SUBTREE 0x00000002 +#define PROC_TRACE_CTL_ENABLE 1 +#define PROC_TRACE_CTL_DISABLE 2 +#define PROC_TRACE_CTL_DISABLE_EXEC 3 + #ifndef _KERNEL __BEGIN_DECLS int procctl(idtype_t, id_t, int, void *);