Page MenuHomeFreeBSD

D58694.diff
No OneTemporary

D58694.diff

diff --git a/lib/libsys/pdfork.2 b/lib/libsys/pdfork.2
--- a/lib/libsys/pdfork.2
+++ b/lib/libsys/pdfork.2
@@ -83,7 +83,7 @@
.Fn pdfork
can accept the
.Fa pdflags:
-.Bl -tag -width PD_CLOEXEC
+.Bl -tag -width PD_PROCCTL_CAP
.It Dv PD_DAEMON
Instead of the default terminate-on-close behaviour, allow the process to
live until it is explicitly killed with
@@ -106,6 +106,14 @@
.It Dv PD_NOWAITPID
The parent cannot obtain the child's status with
.Xr waitpid 2 .
+.It Dv PD_PROCCTL_CAP
+Grant the process descriptor the
+.Dv CAP_PROCCTL
+right, which
+.Xr procctl 2
+with
+.Dv P_PROCDESC
+requires and which is otherwise not granted.
.El
.Pp
The
diff --git a/lib/libsys/procctl.2 b/lib/libsys/procctl.2
--- a/lib/libsys/procctl.2
+++ b/lib/libsys/procctl.2
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 21, 2025
+.Dd August 6, 2026
.Dt PROCCTL 2
.Os
.Sh NAME
@@ -65,8 +65,33 @@
.It Dv P_PGID
Control processes belonging to the process group with the ID
.Fa id .
+.It Dv P_PROCDESC
+Control the process referenced by the process descriptor
+.Fa id .
+The descriptor is required to have the
+.Dv CAP_PROCCTL
+right, which
+.Xr pdfork 2
+grants only when created with the
+.Dv PD_PROCCTL_CAP
+flag.
.El
.Pp
+Unlike the other identifier types, which name processes by numbers in a
+global namespace,
+.Dv P_PROCDESC
+names a process by a descriptor the caller already holds.
+It is therefore the only identifier type accepted in capability mode;
+see
+.Xr cap_enter 2 .
+.Pp
+In capability mode, moreover, only the commands that act solely on the named
+process are permitted.
+Commands that reach beyond it, such as the process reaping controls, are
+denied and fail with
+.Er ECAPMODE ;
+this restriction is conservative and may be relaxed in the future.
+.Pp
The control request to perform is specified by the
.Fa cmd
argument.
@@ -111,6 +136,8 @@
control and only honors the
.Xr elfctl 1
flag and system-wide policy.
+.Pp
+This request is permitted in capability mode.
.It Dv PROC_ASLR_STATUS
Returns the current status of ASLR enablement for the target process.
The
@@ -127,6 +154,8 @@
the
.Dv PROC_ASLR_ACTIVE
flag is or-ed with the value listed above.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_LOGSIGEXIT_CTL
Controls the logging of exits due to signals that would normally cause a core
dump.
@@ -149,6 +178,8 @@
MIB variable
.Va kern.logsigexit .
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_LOGSIGEXIT_STATUS
Returns the current status of logging for the target process.
The
@@ -160,6 +191,8 @@
.It Dv PROC_LOGSIGEXIT_CTL_FORCE_DISABLE
.It Dv PROC_LOGSIGEXIT_CTL_NOFORCE
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_PROTMAX_CTL
Controls the maximum protection used for
.Xr mmap 2
@@ -207,6 +240,8 @@
flag takes precedence over this control.
Executing a binary with this flag set will always use RWX as the implicit
maximum protection.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_PROTMAX_STATUS
Returns the current status of the implicit PROT_MAX control for the
target process.
@@ -224,6 +259,8 @@
control enabled, the
.Dv PROC_PROTMAX_ACTIVE
flag is or-ed with the value listed above.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_SPROTECT
Set process protection state.
This is used to mark a process as protected from being killed if the system
@@ -251,6 +288,8 @@
mark all future child processes of each selected process as protected.
Future child processes will also mark all of their future child processes.
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_REAP_ACQUIRE
Enable orphaned process reaping for future children of the current process.
.Pp
@@ -273,11 +312,15 @@
After system initialization,
.Xr init 8
is the default reaper.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_REAP_RELEASE
Disable orphaned process reaping for the current process.
.Pp
Any processes for whom the current process was the reaper are reassigned to
the current process's reaper.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_REAP_STATUS
Provides a consistent snapshot of information about the reaper
of the specified process,
@@ -332,6 +375,8 @@
returns the pid of one reaper child if there are any processes that can be
reapead;
otherwise, it is set to \-1.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_REAP_GETPIDS
Queries the list of processes that can be reaped
by the reaper of the specified process.
@@ -405,6 +450,8 @@
.It Dv REAPER_PIDINFO_EXITING
The reported process is in the process of exiting (but not yet a zombie).
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_REAP_KILL
Request to deliver a signal to some subset of the descendants of the reaper.
The
@@ -459,6 +506,8 @@
If no such process exists, the
.Fa rk_fpid
field is set to \-1.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_TRACE_CTL
Enable or disable tracing of the specified process(es), according to the
value of the integer argument.
@@ -492,6 +541,8 @@
but the setting persists for the process even after
.Xr execve 2 .
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_TRACE_STATUS
Returns the current tracing status for the specified process in
the integer variable pointed to by
@@ -507,6 +558,8 @@
If a debugger is attached,
.Fa data
is set to the pid of the debugger process.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_TRAPCAP_CTL
Controls the capability mode sandbox actions for the specified
sandboxed processes
@@ -567,6 +620,8 @@
See
.Xr capsicum 4
for more information about capability mode.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_TRAPCAP_STATUS
Return the current status of raising
.Dv SIGTRAP
@@ -584,6 +639,8 @@
See the note about sysctl
.Dv kern.trap_enotcap
above, which gives independent global control of signal delivery.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_PDEATHSIG_CTL
Request the delivery of a signal when the parent of the calling
process exits.
@@ -601,6 +658,8 @@
indicating the signal
that should be delivered to the caller.
Use zero to cancel a previously requested signal delivery.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_PDEATHSIG_STATUS
Query the current signal number that will be delivered when the parent
of the calling process exits.
@@ -615,6 +674,8 @@
.Vt int .
If signal delivery has not been requested, it will contain zero
on return.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_STACKGAP_CTL
Controls stack gaps in the specified process.
A stack gap is one or more virtual memory pages at the end of the
@@ -662,6 +723,8 @@
.Pp
The stack gap state is inherited from the parent on
.Xr fork 2 .
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_STACKGAP_STATUS
Returns the current stack gap state for the specified process.
.Fa data
@@ -690,6 +753,8 @@
However, the controls value can still be inherited by child processes, and
executing a binary without this flag set will revert to the behavior specified
by the control.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_NO_NEW_PRIVS_CTL
Allows one to ignore the set-user-ID and set-group-ID bits on the program
images activated by
@@ -705,6 +770,8 @@
.El
.Pp
It is not possible to disable this control once it has been enabled.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_NO_NEW_PRIVS_STATUS
Returns the current status of set-ID bits enablement for the target process.
The
@@ -715,6 +782,8 @@
.It Dv PROC_NO_NEW_PRIVS_ENABLE
.It Dv PROC_NO_NEW_PRIVS_DISABLE
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_WXMAP_CTL
Controls the creation of mappings with both write and execute permissions
in a process's address space.
@@ -753,6 +822,8 @@
flag and
.Xr execve 2
an image.
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_WXMAP_STATUS
Returns the current status of the controls over creation of mappings with
both write and execute permissions for the specified process.
@@ -777,6 +848,8 @@
creation, the
.Dv PROC_WXORX_ENFORCE
flag is set in the returned value.
+.Pp
+This request is not permitted in capability mode.
.El
.Sh x86 MACHINE-SPECIFIC REQUESTS
.Bl -tag -width indent
@@ -806,6 +879,8 @@
.Va PRIV_IO
privilege can use this option.
.El
+.Pp
+This request is not permitted in capability mode.
.It Dv PROC_KPTI_STATUS
Returns the current KPTI status for the specified process.
.Fa data
@@ -819,6 +894,8 @@
The status is or-ed with
.Va PROC_KPTI_STATUS_ACTIVE
if KPTI is active for the current address space of the process.
+.Pp
+This request is not permitted in capability mode.
.El
.Sh NOTES
Disabling tracing on a process should not be considered a security
@@ -854,14 +931,52 @@
The
.Fa idtype
argument specifies an unsupported identifier type.
+.Pp
+The
+.Fa idtype
+argument is
+.Dv P_PROCDESC
+and
+.Fa id
+is a descriptor of a type other than a process descriptor.
.It Bq Er EPERM
The calling process does not have permission to perform the requested
operation on any of the selected processes.
+.It Bq Er ECAPMODE
+The calling process is in capability mode and the
+.Fa idtype
+argument is not
+.Dv P_PROCDESC ,
+or the
+.Fa cmd
+is not one of those permitted in capability mode.
+.It Bq Er EBADF
+The
+.Fa idtype
+argument is
+.Dv P_PROCDESC
+and
+.Fa id
+is not a valid descriptor.
+.It Bq Er ENOTCAPABLE
+The
+.Fa idtype
+argument is
+.Dv P_PROCDESC
+and
+.Fa id
+lacks the
+.Dv CAP_PROCCTL
+right.
.It Bq Er ESRCH
No processes matched the requested
.Fa idtype
and
.Fa id .
+A
+.Dv P_PROCDESC
+descriptor also stops matching once the process it referenced has exited and
+been reaped.
.It Bq Er ESRCH
No descendant processes can be found matching criteria specified in the
.Dv PROC_REAP_KILL
@@ -944,6 +1059,7 @@
.Xr ktrace 2 ,
.Xr mmap 2 ,
.Xr mprotect 2 ,
+.Xr pdfork 2 ,
.Xr ptrace 2 ,
.Xr wait 2 ,
.Xr capsicum 4 ,
diff --git a/share/man/man4/rights.4 b/share/man/man4/rights.4
--- a/share/man/man4/rights.4
+++ b/share/man/man4/rights.4
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 22, 2025
+.Dd August 6, 2026
.Dt RIGHTS 4
.Os
.Sh NAME
@@ -501,6 +501,13 @@
.Dv CAP_READ
and
.Dv CAP_SEEK .
+.It Dv CAP_PROCCTL
+Permit
+.Xr procctl 2
+with an
+.Fa idtype
+of
+.Dv P_PROCDESC .
.It Dv CAP_PWRITE
An alias to
.Dv CAP_SEEK
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -611,7 +611,7 @@
{ .sy_narg = AS(accept4_args), .sy_call = (sy_call_t *)sys_accept4, .sy_auevent = AUE_ACCEPT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 541 = accept4 */
{ .sy_narg = AS(pipe2_args), .sy_call = (sy_call_t *)sys_pipe2, .sy_auevent = AUE_PIPE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 542 = pipe2 */
{ .sy_narg = AS(freebsd32_aio_mlock_args), .sy_call = (sy_call_t *)freebsd32_aio_mlock, .sy_auevent = AUE_AIO_MLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 543 = freebsd32_aio_mlock */
- { .sy_narg = AS(freebsd32_procctl_args), .sy_call = (sy_call_t *)freebsd32_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 544 = freebsd32_procctl */
+ { .sy_narg = AS(freebsd32_procctl_args), .sy_call = (sy_call_t *)freebsd32_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 544 = freebsd32_procctl */
{ .sy_narg = AS(freebsd32_ppoll_args), .sy_call = (sy_call_t *)freebsd32_ppoll, .sy_auevent = AUE_POLL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 545 = freebsd32_ppoll */
{ .sy_narg = AS(freebsd32_futimens_args), .sy_call = (sy_call_t *)freebsd32_futimens, .sy_auevent = AUE_FUTIMES, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 546 = freebsd32_futimens */
{ .sy_narg = AS(freebsd32_utimensat_args), .sy_call = (sy_call_t *)freebsd32_utimensat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 547 = freebsd32_utimensat */
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -610,7 +610,7 @@
{ .sy_narg = AS(accept4_args), .sy_call = (sy_call_t *)sys_accept4, .sy_auevent = AUE_ACCEPT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 541 = accept4 */
{ .sy_narg = AS(pipe2_args), .sy_call = (sy_call_t *)sys_pipe2, .sy_auevent = AUE_PIPE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 542 = pipe2 */
{ .sy_narg = AS(aio_mlock_args), .sy_call = (sy_call_t *)sys_aio_mlock, .sy_auevent = AUE_AIO_MLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 543 = aio_mlock */
- { .sy_narg = AS(procctl_args), .sy_call = (sy_call_t *)sys_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 544 = procctl */
+ { .sy_narg = AS(procctl_args), .sy_call = (sy_call_t *)sys_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 544 = procctl */
{ .sy_narg = AS(ppoll_args), .sy_call = (sy_call_t *)sys_ppoll, .sy_auevent = AUE_POLL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 545 = ppoll */
{ .sy_narg = AS(futimens_args), .sy_call = (sy_call_t *)sys_futimens, .sy_auevent = AUE_FUTIMES, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 546 = futimens */
{ .sy_narg = AS(utimensat_args), .sy_call = (sy_call_t *)sys_utimensat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 547 = utimensat */
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -131,6 +131,8 @@
filecaps_fill(&fcaps);
if ((uap->flags & PD_PTRACE_CAP) == 0)
cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
+ if ((uap->flags & PD_PROCCTL_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PROCCTL);
fr.fr_pd_fcaps = &fcaps;
AUDIT_ARG_FFLAGS(uap->flags);
/*
@@ -236,6 +238,8 @@
filecaps_fill(&fcaps);
if ((uap->pdflags & PD_PTRACE_CAP) == 0)
cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
+ if ((uap->pdflags & PD_PROCCTL_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PROCCTL);
fr.fr_pd_fcaps = &fcaps;
error = fork1(td, &fr);
if (error == 0) {
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -33,6 +33,8 @@
#include <sys/_unrhdr.h>
#include <sys/systm.h>
#include <sys/capsicum.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mman.h>
@@ -40,6 +42,7 @@
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/procctl.h>
+#include <sys/procdesc.h>
#include <sys/ptrace.h>
#include <sys/sx.h>
#include <sys/syscallsubr.h>
@@ -1012,6 +1015,19 @@
bool copyout_on_error : 1;
bool no_nonnull_data : 1;
bool need_candebug : 1;
+ /*
+ * Whether the command may be issued in capability mode against a
+ * process descriptor. We err on the side of prohibiting: it is true
+ * only for commands audited to act solely on the named process, while
+ * reaping and group operations, which reach beyond it, leave it false.
+ * Some of these prohibitions may be revisited in the future.
+ *
+ * A finer design would limit which commands a given descriptor permits,
+ * the way cap_ioctls_limit(2) and cap_fcntls_limit(2) restrict the
+ * ioctl(2) and fcntl(2) requests allowed on a descriptor, rather than
+ * gating every command behind CAP_PROCCTL plus this one shared bit.
+ */
+ bool cap_safe : 1;
int copyin_sz;
int copyout_sz;
int (*exec)(struct thread *, struct proc *, void *);
@@ -1021,39 +1037,39 @@
[PROC_SPROTECT] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = false,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = protect_set, .copyout_on_error = false, },
[PROC_REAP_ACQUIRE] =
{ .lock_tree = PCTL_XLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = true,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = 0,
.exec = reap_acquire, .copyout_on_error = false, },
[PROC_REAP_RELEASE] =
{ .lock_tree = PCTL_XLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = true,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = 0,
.exec = reap_release, .copyout_on_error = false, },
[PROC_REAP_STATUS] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0,
.copyout_sz = sizeof(struct procctl_reaper_status),
.exec = reap_status, .copyout_on_error = false, },
[PROC_REAP_GETPIDS] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = sizeof(struct procctl_reaper_pids),
.copyout_sz = 0,
.exec = reap_getpids, .copyout_on_error = false, },
[PROC_REAP_KILL] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = sizeof(struct procctl_reaper_kill),
.copyout_sz = sizeof(struct procctl_reaper_kill),
.exec = reap_kill, .copyout_on_error = true,
@@ -1061,109 +1077,109 @@
[PROC_TRACE_CTL] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = false,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = trace_ctl, .copyout_on_error = false, },
[PROC_TRACE_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = trace_status, .copyout_on_error = false, },
[PROC_TRAPCAP_CTL] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = false,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = trapcap_ctl, .copyout_on_error = false, },
[PROC_TRAPCAP_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = trapcap_status, .copyout_on_error = false, },
[PROC_PDEATHSIG_CTL] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = true, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = pdeathsig_ctl, .copyout_on_error = false, },
[PROC_PDEATHSIG_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = true, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = pdeathsig_status, .copyout_on_error = false, },
[PROC_ASLR_CTL] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = true,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = aslr_ctl, .copyout_on_error = false, },
[PROC_ASLR_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = aslr_status, .copyout_on_error = false, },
[PROC_PROTMAX_CTL] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = protmax_ctl, .copyout_on_error = false, },
[PROC_PROTMAX_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = protmax_status, .copyout_on_error = false, },
[PROC_STACKGAP_CTL] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = stackgap_ctl, .copyout_on_error = false, },
[PROC_STACKGAP_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = stackgap_status, .copyout_on_error = false, },
[PROC_NO_NEW_PRIVS_CTL] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = no_new_privs_ctl, .copyout_on_error = false, },
[PROC_NO_NEW_PRIVS_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = no_new_privs_status, .copyout_on_error = false, },
[PROC_WXMAP_CTL] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = wxmap_ctl, .copyout_on_error = false, },
[PROC_WXMAP_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = wxmap_status, .copyout_on_error = false, },
[PROC_LOGSIGEXIT_CTL] =
{ .lock_tree = PCTL_SLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = true,
+ .need_candebug = true, .cap_safe = false,
.copyin_sz = sizeof(int), .copyout_sz = 0,
.exec = logsigexit_ctl, .copyout_on_error = false, },
[PROC_LOGSIGEXIT_STATUS] =
{ .lock_tree = PCTL_UNLOCKED, .one_proc = true,
.esrch_is_einval = false, .no_nonnull_data = false,
- .need_candebug = false,
+ .need_candebug = false, .cap_safe = false,
.copyin_sz = 0, .copyout_sz = sizeof(int),
.exec = logsigexit_status, .copyout_on_error = false, },
};
@@ -1180,9 +1196,25 @@
const struct procctl_cmd_info *cmd_info;
int error, error1;
- if (uap->com >= PROC_PROCCTL_MD_MIN)
+ /*
+ * In capability mode only P_PROCDESC is available: the other identifier
+ * types name a process through a global namespace the sandbox is not
+ * supposed to be able to reach into.
+ */
+ if (IN_CAPABILITY_MODE(td) && uap->idtype != P_PROCDESC)
+ return (ECAPMODE);
+
+ if (uap->com >= PROC_PROCCTL_MD_MIN) {
+ /*
+ * Machine-dependent commands have not been audited for
+ * capability mode, so deny them there for now, as with the
+ * commands below that are not cap_safe.
+ */
+ if (IN_CAPABILITY_MODE(td))
+ return (ECAPMODE);
return (cpu_procctl(td, uap->idtype, uap->id,
uap->com, uap->data));
+ }
if (uap->com <= 0 || uap->com >= nitems(procctl_cmds_info))
return (EINVAL);
cmd_info = &procctl_cmds_info[uap->com];
@@ -1221,18 +1253,38 @@
return (error);
}
+/*
+ * A single-process caller returns on error; P_PGID skips the process on error.
+ */
+static int
+kern_procctl_permit(struct thread *td, struct proc *p,
+ const struct procctl_cmd_info *cmd_info)
+{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ return (cmd_info->need_candebug ? p_candebug(td, p) : p_cansee(td, p));
+}
+
int
kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
{
struct pgrp *pg;
struct proc *p;
+ struct file *fp;
const struct procctl_cmd_info *cmd_info;
int error, first_error, ok;
bool sapblk;
MPASS(com > 0 && com < nitems(procctl_cmds_info));
cmd_info = &procctl_cmds_info[com];
- if (idtype != P_PID && cmd_info->one_proc)
+ /*
+ * In capability mode only commands that act solely on the named
+ * process are permitted; sys_procctl() has already limited the idtype
+ * to P_PROCDESC.
+ */
+ if (IN_CAPABILITY_MODE(td) && !cmd_info->cap_safe)
+ return (ECAPMODE);
+ /* P_PID and P_PROCDESC name exactly one process. */
+ if (idtype != P_PID && idtype != P_PROCDESC && cmd_info->one_proc)
return (EINVAL);
sapblk = false;
@@ -1266,13 +1318,28 @@
EINVAL : ESRCH;
break;
}
- error = cmd_info->need_candebug ? p_candebug(td, p) :
- p_cansee(td, p);
+ error = kern_procctl_permit(td, p, cmd_info);
}
if (error == 0)
error = kern_procctl_single(td, p, com, data);
PROC_UNLOCK(p);
break;
+ case P_PROCDESC:
+ if (cmd_info->lock_tree == PCTL_UNLOCKED)
+ sx_slock(&proctree_lock);
+ error = fget_procdesc(td, (int)id, &cap_procctl_rights, EINVAL,
+ &fp, NULL, &p);
+ if (cmd_info->lock_tree == PCTL_UNLOCKED)
+ sx_sunlock(&proctree_lock);
+ if (error == 0) {
+ error = kern_procctl_permit(td, p, cmd_info);
+ if (error == 0)
+ error = kern_procctl_single(td, p, com, data);
+ PROC_UNLOCK(p);
+ }
+ if (fp != NULL)
+ fdrop(fp, td);
+ break;
case P_PGID:
/*
* Attempt to apply the operation to all members of the
@@ -1292,8 +1359,7 @@
PROC_LOCK(p);
if (p->p_state == PRS_NEW ||
p->p_state == PRS_ZOMBIE ||
- (cmd_info->need_candebug ? p_candebug(td, p) :
- p_cansee(td, p)) != 0) {
+ kern_procctl_permit(td, p, cmd_info) != 0) {
PROC_UNLOCK(p);
continue;
}
diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c
--- a/sys/kern/subr_capability.c
+++ b/sys/kern/subr_capability.c
@@ -93,6 +93,7 @@
const cap_rights_t cap_pdkill_rights = CAP_RIGHTS_INITIALIZER(CAP_PDKILL);
const cap_rights_t cap_pdwait_rights = CAP_RIGHTS_INITIALIZER(CAP_PDWAIT);
const cap_rights_t cap_pread_rights = CAP_RIGHTS_INITIALIZER(CAP_PREAD);
+const cap_rights_t cap_procctl_rights = CAP_RIGHTS_INITIALIZER(CAP_PROCCTL);
const cap_rights_t cap_ptrace_rights = CAP_RIGHTS_INITIALIZER(CAP_PTRACE);
const cap_rights_t cap_pwrite_rights = CAP_RIGHTS_INITIALIZER(CAP_PWRITE);
const cap_rights_t cap_read_rights = CAP_RIGHTS_INITIALIZER(CAP_READ);
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -705,6 +705,8 @@
filecaps_fill(&fcaps);
if ((flags & PD_PTRACE_CAP) == 0)
cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
+ if ((flags & PD_PROCCTL_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PROCCTL);
sx_xlock(&proctree_lock);
error = pdopenpid1(td, pid, &pdf, fp);
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3020,7 +3020,7 @@
_In_ _Contains_long_ptr_ struct aiocb *aiocbp
);
}
-544 AUE_PROCCTL STD {
+544 AUE_PROCCTL STD|CAPENABLED {
int procctl(
idtype_t idtype,
id_t id,
diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h
--- a/sys/sys/caprights.h
+++ b/sys/sys/caprights.h
@@ -95,6 +95,7 @@
extern const cap_rights_t cap_pdkill_rights;
extern const cap_rights_t cap_pdwait_rights;
extern const cap_rights_t cap_pread_rights;
+extern const cap_rights_t cap_procctl_rights;
extern const cap_rights_t cap_ptrace_rights;
extern const cap_rights_t cap_pwrite_rights;
extern const cap_rights_t cap_read_rights;
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -301,8 +301,9 @@
/* Allows ptrace(PT_PROCDESC) */
#define CAP_PTRACE CAPRIGHT(1, 0x0000000001000000ULL)
+/* Allows procctl(P_PROCDESC) */
+#define CAP_PROCCTL CAPRIGHT(1, 0x0000000002000000ULL)
-#define CAP_UNUSED1_26 CAPRIGHT(1, 0x0000000002000000ULL)
#define CAP_UNUSED1_27 CAPRIGHT(1, 0x0000000004000000ULL)
#define CAP_UNUSED1_28 CAPRIGHT(1, 0x0000000008000000ULL)
#define CAP_UNUSED1_29 CAPRIGHT(1, 0x0000000010000000ULL)
@@ -336,7 +337,7 @@
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
/* All default bits for index 1. */
-#define CAP_ALL1 CAPRIGHT(1, 0x0000000001FFFFFFULL)
+#define CAP_ALL1 CAPRIGHT(1, 0x0000000003FFFFFFULL)
/* Backward compatibility. */
#define CAP_POLL_EVENT CAP_EVENT
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -161,10 +161,12 @@
#define PD_DAEMON 0x00000001 /* Don't exit when procdesc closes. */
#define PD_CLOEXEC 0x00000002 /* Close file descriptor on exec. */
#define PD_NOWAITPID 0x00000004 /* Reap without waitpid(). */
-#define PD_PTRACE_CAP 0x00000008 /* Allow PT_PROCDESC in cap mode. */
+#define PD_PTRACE_CAP 0x00000008 /* Allow ptrace(PT_PROCDESC) in cap mode. */
+#define PD_PROCCTL_CAP 0x00000010 /* Allow procctl(P_PROCDESC) in cap mode. */
#define PD_ALLOWED_AT_FORK \
- (PD_DAEMON | PD_CLOEXEC | PD_NOWAITPID | PD_PTRACE_CAP)
-#define PD_ALLOWED_AT_OPENPID (PD_DAEMON | PD_CLOEXEC | PD_PTRACE_CAP)
+ (PD_DAEMON | PD_CLOEXEC | PD_NOWAITPID | PD_PTRACE_CAP | PD_PROCCTL_CAP)
+#define PD_ALLOWED_AT_OPENPID \
+ (PD_DAEMON | PD_CLOEXEC | PD_PTRACE_CAP | PD_PROCCTL_CAP)
#endif /* !_SYS_PROCDESC_H_ */
diff --git a/sys/sys/wait.h b/sys/sys/wait.h
--- a/sys/sys/wait.h
+++ b/sys/sys/wait.h
@@ -118,7 +118,8 @@
P_JAILID, /* A zone identifier. */
P_CTID, /* A (process) contract identifier. */
P_CPUID, /* CPU identifier. */
- P_PSETID /* Processor set identifier. */
+ P_PSETID, /* Processor set identifier. */
+ P_PROCDESC /* A process descriptor; only for procctl(2). */
} idtype_t; /* The type of id_t we are using. */
#if __BSD_VISIBLE

File Metadata

Mime Type
text/plain
Expires
Thu, Aug 27, 6:02 AM (6 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37357329
Default Alt Text
D58694.diff (30 KB)

Event Timeline