Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168070631
D58694.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
D58694.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58694: procctl(2) addressed by process descriptor (P_PROCDESC)
Attached
Detach File
Event Timeline
Log In to Comment