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
This commit is contained in:
parent
e3612a4c1f
commit
677258f7e7
@ -29,7 +29,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd December 16, 2014
|
.Dd December 29, 2014
|
||||||
.Dt PROCCTL 2
|
.Dt PROCCTL 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -275,7 +275,61 @@ delivery failed, e.g. due to the permission problems.
|
|||||||
If no such process exist, the
|
If no such process exist, the
|
||||||
.Fa rk_fpid
|
.Fa rk_fpid
|
||||||
field is set to -1.
|
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
|
.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
|
.Sh RETURN VALUES
|
||||||
If an error occurs, a value of -1 is returned and
|
If an error occurs, a value of -1 is returned and
|
||||||
.Va errno
|
.Va errno
|
||||||
@ -343,11 +397,34 @@ The
|
|||||||
.Dv PROC_REAP_ACQUIRE
|
.Dv PROC_REAP_ACQUIRE
|
||||||
request was issued by a process that had already acquired reaper status
|
request was issued by a process that had already acquired reaper status
|
||||||
and has not yet released it.
|
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
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
|
.Xr dtrace 1 ,
|
||||||
.Xr kill 2 ,
|
.Xr kill 2 ,
|
||||||
|
.Xr ktrace 2 ,
|
||||||
.Xr ptrace 2 ,
|
.Xr ptrace 2 ,
|
||||||
.Xr wait 2 ,
|
.Xr wait 2 ,
|
||||||
|
.Xr hwpmc 4 ,
|
||||||
.Xr init 8
|
.Xr init 8
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
The
|
The
|
||||||
|
@ -2969,6 +2969,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||||||
|
|
||||||
switch (uap->com) {
|
switch (uap->com) {
|
||||||
case PROC_SPROTECT:
|
case PROC_SPROTECT:
|
||||||
|
case PROC_TRACE_CTL:
|
||||||
error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
|
error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
@ -2997,6 +2998,9 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||||||
return (error);
|
return (error);
|
||||||
data = &x.rk;
|
data = &x.rk;
|
||||||
break;
|
break;
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
|
data = &flags;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -3012,6 +3016,10 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
|||||||
if (error == 0)
|
if (error == 0)
|
||||||
error = error1;
|
error = error1;
|
||||||
break;
|
break;
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
|
if (error == 0)
|
||||||
|
error = copyout(&flags, uap->data, sizeof(flags));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
@ -634,6 +634,8 @@ interpret:
|
|||||||
* it that it now has its own resources back
|
* it that it now has its own resources back
|
||||||
*/
|
*/
|
||||||
p->p_flag |= P_EXEC;
|
p->p_flag |= P_EXEC;
|
||||||
|
if ((p->p_flag2 & P2_NOTRACE_EXEC) == 0)
|
||||||
|
p->p_flag2 &= ~P2_NOTRACE;
|
||||||
if (p->p_flag & P_PPWAIT) {
|
if (p->p_flag & P_PPWAIT) {
|
||||||
p->p_flag &= ~(P_PPWAIT | P_PPTRACE);
|
p->p_flag &= ~(P_PPWAIT | P_PPTRACE);
|
||||||
cv_broadcast(&p->p_pwait);
|
cv_broadcast(&p->p_pwait);
|
||||||
|
@ -494,7 +494,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
|
|||||||
* Increase reference counts on shared objects.
|
* Increase reference counts on shared objects.
|
||||||
*/
|
*/
|
||||||
p2->p_flag = P_INMEM;
|
p2->p_flag = P_INMEM;
|
||||||
p2->p_flag2 = 0;
|
p2->p_flag2 = p1->p_flag2 & (P2_NOTRACE | P2_NOTRACE_EXEC);
|
||||||
p2->p_swtick = ticks;
|
p2->p_swtick = ticks;
|
||||||
if (p1->p_flag & P_PROFIL)
|
if (p1->p_flag & P_PROFIL)
|
||||||
startprofclock(p2);
|
startprofclock(p2);
|
||||||
|
@ -280,6 +280,62 @@ reap_kill(struct thread *td, struct proc *p, struct procctl_reaper_kill *rk)
|
|||||||
return (error);
|
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_
|
#ifndef _SYS_SYSPROTO_H_
|
||||||
struct procctl_args {
|
struct procctl_args {
|
||||||
idtype_t idtype;
|
idtype_t idtype;
|
||||||
@ -302,6 +358,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||||||
|
|
||||||
switch (uap->com) {
|
switch (uap->com) {
|
||||||
case PROC_SPROTECT:
|
case PROC_SPROTECT:
|
||||||
|
case PROC_TRACE_CTL:
|
||||||
error = copyin(uap->data, &flags, sizeof(flags));
|
error = copyin(uap->data, &flags, sizeof(flags));
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
@ -328,6 +385,9 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||||||
return (error);
|
return (error);
|
||||||
data = &x.rk;
|
data = &x.rk;
|
||||||
break;
|
break;
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
|
data = &flags;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -342,6 +402,10 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
|||||||
if (error == 0)
|
if (error == 0)
|
||||||
error = error1;
|
error = error1;
|
||||||
break;
|
break;
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
|
if (error == 0)
|
||||||
|
error = copyout(&flags, uap->data, sizeof(flags));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -364,6 +428,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
|
|||||||
return (reap_getpids(td, p, data));
|
return (reap_getpids(td, p, data));
|
||||||
case PROC_REAP_KILL:
|
case PROC_REAP_KILL:
|
||||||
return (reap_kill(td, p, data));
|
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:
|
default:
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -375,6 +443,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||||||
struct pgrp *pg;
|
struct pgrp *pg;
|
||||||
struct proc *p;
|
struct proc *p;
|
||||||
int error, first_error, ok;
|
int error, first_error, ok;
|
||||||
|
bool tree_locked;
|
||||||
|
|
||||||
switch (com) {
|
switch (com) {
|
||||||
case PROC_REAP_ACQUIRE:
|
case PROC_REAP_ACQUIRE:
|
||||||
@ -382,6 +451,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||||||
case PROC_REAP_STATUS:
|
case PROC_REAP_STATUS:
|
||||||
case PROC_REAP_GETPIDS:
|
case PROC_REAP_GETPIDS:
|
||||||
case PROC_REAP_KILL:
|
case PROC_REAP_KILL:
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
if (idtype != P_PID)
|
if (idtype != P_PID)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
@ -391,11 +461,17 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||||||
case PROC_REAP_STATUS:
|
case PROC_REAP_STATUS:
|
||||||
case PROC_REAP_GETPIDS:
|
case PROC_REAP_GETPIDS:
|
||||||
case PROC_REAP_KILL:
|
case PROC_REAP_KILL:
|
||||||
|
case PROC_TRACE_CTL:
|
||||||
sx_slock(&proctree_lock);
|
sx_slock(&proctree_lock);
|
||||||
|
tree_locked = true;
|
||||||
break;
|
break;
|
||||||
case PROC_REAP_ACQUIRE:
|
case PROC_REAP_ACQUIRE:
|
||||||
case PROC_REAP_RELEASE:
|
case PROC_REAP_RELEASE:
|
||||||
sx_xlock(&proctree_lock);
|
sx_xlock(&proctree_lock);
|
||||||
|
tree_locked = true;
|
||||||
|
break;
|
||||||
|
case PROC_TRACE_STATUS:
|
||||||
|
tree_locked = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
@ -456,6 +532,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sx_unlock(&proctree_lock);
|
if (tree_locked)
|
||||||
|
sx_unlock(&proctree_lock);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
@ -1714,6 +1714,13 @@ p_candebug(struct thread *td, struct proc *p)
|
|||||||
if ((p->p_flag & P_INEXEC) != 0)
|
if ((p->p_flag & P_INEXEC) != 0)
|
||||||
return (EBUSY);
|
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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3247,7 +3247,8 @@ coredump(struct thread *td)
|
|||||||
MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td);
|
MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td);
|
||||||
_STOPEVENT(p, S_CORE, 0);
|
_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);
|
PROC_UNLOCK(p);
|
||||||
return (EFAULT);
|
return (EFAULT);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@
|
|||||||
#define PRIV_DEBUG_DIFFCRED 80 /* Exempt debugging other users. */
|
#define PRIV_DEBUG_DIFFCRED 80 /* Exempt debugging other users. */
|
||||||
#define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */
|
#define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */
|
||||||
#define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */
|
#define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */
|
||||||
|
#define PRIV_DEBUG_DENIED 83 /* Exempt P2_NOTRACE. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dtrace privileges.
|
* Dtrace privileges.
|
||||||
|
@ -675,6 +675,8 @@ struct proc {
|
|||||||
|
|
||||||
/* These flags are kept in p_flag2. */
|
/* These flags are kept in p_flag2. */
|
||||||
#define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */
|
#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. */
|
/* Flags protected by proctree_lock, kept in p_treeflags. */
|
||||||
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
|
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
|
||||||
|
@ -41,6 +41,8 @@
|
|||||||
#define PROC_REAP_STATUS 4 /* reaping status */
|
#define PROC_REAP_STATUS 4 /* reaping status */
|
||||||
#define PROC_REAP_GETPIDS 5 /* get descendants */
|
#define PROC_REAP_GETPIDS 5 /* get descendants */
|
||||||
#define PROC_REAP_KILL 6 /* kill 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). */
|
/* Operations for PROC_SPROTECT (passed in integer arg). */
|
||||||
#define PPROT_OP(x) ((x) & 0xf)
|
#define PPROT_OP(x) ((x) & 0xf)
|
||||||
@ -96,6 +98,10 @@ struct procctl_reaper_kill {
|
|||||||
#define REAPER_KILL_CHILDREN 0x00000001
|
#define REAPER_KILL_CHILDREN 0x00000001
|
||||||
#define REAPER_KILL_SUBTREE 0x00000002
|
#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
|
#ifndef _KERNEL
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
int procctl(idtype_t, id_t, int, void *);
|
int procctl(idtype_t, id_t, int, void *);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user