Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170668776
D58989.id185325.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D58989.id185325.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
@@ -176,6 +176,18 @@
.Er ESRCH
error.
.Pp
+The
+.Fn pdopenpid
+system call is allowed in the capability mode
+.Pq see Xr capsicum 4
+when the target process is the child of the calling process, or
+when the calling process is the debugger of the target process.
+The debugger is attached to its target by
+.Xr ptrace 2 ,
+.Xr pdptrace 2 ,
+or by other means, e.g., by debugging the target process' parent with
+the follow-on-fork mode enabled.
+.Pp
.Fn pdgetpid
queries the process ID (PID) in the process descriptor
.Fa fd .
@@ -454,6 +466,27 @@
refers to a file that cannot be duplicated across a process boundary,
such as a kqueue.
.El
+.Pp
+The
+.Fn pdptrace
+system call returns the same errors as
+.Xr ptrace 2 ,
+as well as the following specific errors:
+.Bl -tag -width Er
+.It Bq Er ECAPMODE
+The process issuing the
+.Fn pdptrace
+call is in capability mode,
+and the
+.Dv security.bsd.allow_ptrace_in_cap_mode
+tunable is set to
+.Dv false .
+.It Bq Er ENOTCAPABLE
+The process called
+.Fn pdptrace
+on the process descriptor that does not have the
+.Dv CAP_PTRACE
+capability enabled.
.Sh SEE ALSO
.Xr close 2 ,
.Xr fork 2 ,
diff --git a/lib/libsys/ptrace.2 b/lib/libsys/ptrace.2
--- a/lib/libsys/ptrace.2
+++ b/lib/libsys/ptrace.2
@@ -180,6 +180,12 @@
process'.
These requests will fail with error
.Er ESRCH .
+.It Dv security.bsd.allow_ptrace_in_cap_mode
+Setting this sysctl to true allows the
+.Fn ptrace
+requests issued by the processes in capability mode,
+see
+.Xr pdptrace 2 .
.It Dv securelevel and init
The
.Xr init 1
@@ -1522,6 +1528,10 @@
.Fa pve_pathlen
holds the minimum buffer size required on return.
.El
+.It Bq ECAPMODE
+The process issuing the
+.Fn ptrace
+call is in capability mode.
.El
.Sh SEE ALSO
.Xr execve 2 ,
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -1050,9 +1050,6 @@
void *addr;
int data, error, i;
- if (!allow_ptrace)
- return (ENOSYS);
-
error = 0;
addr = &r;
data = udata;
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
@@ -93,7 +93,7 @@
{ .sy_narg = AS(setuid_args), .sy_call = (sy_call_t *)sys_setuid, .sy_auevent = AUE_SETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 23 = setuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_getuid, .sy_auevent = AUE_GETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 24 = getuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_geteuid, .sy_auevent = AUE_GETEUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 25 = geteuid */
- { .sy_narg = AS(freebsd32_ptrace_args), .sy_call = (sy_call_t *)freebsd32_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 26 = freebsd32_ptrace */
+ { .sy_narg = AS(freebsd32_ptrace_args), .sy_call = (sy_call_t *)freebsd32_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 26 = freebsd32_ptrace */
{ .sy_narg = AS(freebsd32_recvmsg_args), .sy_call = (sy_call_t *)freebsd32_recvmsg, .sy_auevent = AUE_RECVMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 27 = freebsd32_recvmsg */
{ .sy_narg = AS(freebsd32_sendmsg_args), .sy_call = (sy_call_t *)freebsd32_sendmsg, .sy_auevent = AUE_SENDMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 28 = freebsd32_sendmsg */
{ .sy_narg = AS(recvfrom_args), .sy_call = (sy_call_t *)sys_recvfrom, .sy_auevent = AUE_RECVFROM, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 29 = recvfrom */
@@ -670,7 +670,7 @@
{ .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */
{ .sy_narg = AS(freebsd32_pdwait_args), .sy_call = (sy_call_t *)freebsd32_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = freebsd32_pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
- { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
{ .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
- { .sy_narg = AS(freebsd32_pdptrace_args), .sy_call = (sy_call_t *)freebsd32_pdptrace, .sy_auevent = AUE_PDPTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 605 = freebsd32_pdptrace */
+ { .sy_narg = AS(freebsd32_pdptrace_args), .sy_call = (sy_call_t *)freebsd32_pdptrace, .sy_auevent = AUE_PDPTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 605 = freebsd32_pdptrace */
};
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
@@ -92,7 +92,7 @@
{ .sy_narg = AS(setuid_args), .sy_call = (sy_call_t *)sys_setuid, .sy_auevent = AUE_SETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 23 = setuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_getuid, .sy_auevent = AUE_GETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 24 = getuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_geteuid, .sy_auevent = AUE_GETEUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 25 = geteuid */
- { .sy_narg = AS(ptrace_args), .sy_call = (sy_call_t *)sys_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 26 = ptrace */
+ { .sy_narg = AS(ptrace_args), .sy_call = (sy_call_t *)sys_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 26 = ptrace */
{ .sy_narg = AS(recvmsg_args), .sy_call = (sy_call_t *)sys_recvmsg, .sy_auevent = AUE_RECVMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 27 = recvmsg */
{ .sy_narg = AS(sendmsg_args), .sy_call = (sy_call_t *)sys_sendmsg, .sy_auevent = AUE_SENDMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 28 = sendmsg */
{ .sy_narg = AS(recvfrom_args), .sy_call = (sy_call_t *)sys_recvfrom, .sy_auevent = AUE_RECVFROM, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 29 = recvfrom */
@@ -669,7 +669,7 @@
{ .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */
{ .sy_narg = AS(pdwait_args), .sy_call = (sy_call_t *)sys_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
- { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
{ .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
- { .sy_narg = AS(pdptrace_args), .sy_call = (sy_call_t *)sys_pdptrace, .sy_auevent = AUE_PDPTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 605 = pdptrace */
+ { .sy_narg = AS(pdptrace_args), .sy_call = (sy_call_t *)sys_pdptrace, .sy_auevent = AUE_PDPTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 605 = pdptrace */
};
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -46,14 +46,13 @@
* System calls related to processes and protection
*/
-#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_inet6.h"
-#include <sys/param.h>
#include <sys/systm.h>
#include <sys/abi_compat.h>
#include <sys/acct.h>
+#include <sys/capsicum.h>
#include <sys/imgact.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
@@ -2313,6 +2312,31 @@
return (uidsubset && grpsubset);
}
+/*
+ * Determine whether the td thread allowed to do pdopenpid(2) on the
+ * process p. The permissions are scoped to the PIDs namespace and
+ * processes hierarhy, and do not imply permissions to perform
+ * operations on the resulting process descriptor, e.g. pdkill(2) and
+ * other.
+ */
+int
+p_canopen(struct thread *td, struct proc *p)
+{
+#ifdef INVARIANTS
+ if (IN_CAPABILITY_MODE(td))
+ sx_assert(&proctree_lock, SX_LOCKED);
+#endif
+
+ /*
+ * Allow implicit parent in cap mode: either real parent or
+ * debugger can open pid.
+ */
+ if (!IN_CAPABILITY_MODE(td) || (allow_ptrace_in_cap_mode &&
+ (td->td_proc == p->p_pptr || p->p_oppid == td->td_proc->p_pid)))
+ return (0);
+ return (ECAPMODE);
+}
+
/*-
* Determine whether td may debug p.
* Returns: 0 for permitted, an errno value otherwise
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
@@ -665,6 +665,12 @@
PROC_UNLOCK(p);
return (EBUSY);
}
+ error = p_canopen(td, p);
+ if (error != 0) {
+ PROC_UNLOCK(p);
+ return (error);
+ }
+
pd = p->p_procdesc;
if (pd != NULL) {
MPASS((p->p_zombieref & PZOMBIEREF_PROCDESC) != 0);
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -51,6 +51,7 @@
#include <sys/sleepqueue.h>
#include <sys/sx.h>
#include <sys/syscallsubr.h>
+#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/vnode.h>
@@ -70,6 +71,11 @@
#include <sys/procfs.h>
#endif
+bool allow_ptrace_in_cap_mode = true;
+SYSCTL_BOOL(_security_bsd, OID_AUTO, allow_ptrace_in_cap_mode, CTLFLAG_RWTUN,
+ &allow_ptrace_in_cap_mode, 0,
+ "Allow ptrace(2) in capability mode");
+
/* Assert it's safe to unlock a process, e.g. to allocate working memory */
#define PROC_ASSERT_TRACEREQ(p) MPASS(((p)->p_flag2 & P2_PTRACEREQ) != 0)
@@ -703,6 +709,24 @@
return (error);
}
+static int
+ptrace_check_allowed(struct thread *td, int req, bool pd_mode, pid_t pid)
+{
+ if (!allow_ptrace)
+ return (ENOSYS);
+ if (!IN_CAPABILITY_MODE(td))
+ return (0);
+ if (!allow_ptrace_in_cap_mode)
+ return (ECAPMODE);
+ if (pd_mode)
+ return (0);
+ if (req == PT_GET_CHILDREN && pid == td->td_proc->p_pid)
+ return (0);
+ if (req == PT_CLEARSTEP && pid == td->td_tid)
+ return (0);
+ return (ECAPMODE);
+}
+
/*
* Process debugging system call.
*/
@@ -743,12 +767,11 @@
void *addr;
int error, data;
- if (!allow_ptrace)
- return (ENOSYS);
+ error = ptrace_check_allowed(td, req, pd_mode, pid);
+ if (error != 0)
+ return (error);
- error = 0;
addr = &r;
-
switch (req) {
case PT_GET_EVENT_MASK:
case PT_LWPINFO:
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -264,7 +264,7 @@
25 AUE_GETEUID STD|CAPENABLED {
uid_t geteuid(void);
}
-26 AUE_PTRACE STD {
+26 AUE_PTRACE STD|CAPENABLED {
int ptrace(
int req,
pid_t pid,
@@ -3429,7 +3429,7 @@
int flags
);
}
-603 AUE_PDOPENPID STD {
+603 AUE_PDOPENPID STD|CAPENABLED {
int pdopenpid(
pid_t pid,
int flags
@@ -3442,7 +3442,7 @@
int flags
);
}
-605 AUE_PDPTRACE STD {
+605 AUE_PDPTRACE STD|CAPENABLED {
int pdptrace(
int req,
int pfd,
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1195,6 +1195,7 @@
void maybe_yield(void);
void mi_switch(int flags);
int p_candebug(struct thread *td, struct proc *p);
+int p_canopen(struct thread *td, struct proc *p);
int p_cansee(struct thread *td, struct proc *p);
int p_cansched(struct thread *td, struct proc *p);
int p_cansignal(struct thread *td, struct proc *p, int signum);
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -296,6 +296,7 @@
int pfd, lwpid_t lwpid, void *addr, int data);
extern bool allow_ptrace;
+extern bool allow_ptrace_in_cap_mode;
#else /* !_KERNEL */
diff --git a/tests/sys/kern/procdesc.c b/tests/sys/kern/procdesc.c
--- a/tests/sys/kern/procdesc.c
+++ b/tests/sys/kern/procdesc.c
@@ -343,10 +343,10 @@
}
/*
- * pdopenpid should fail in capability mode.
+ * pdopenpid works for children in capability mode.
*/
-ATF_TC_WITHOUT_HEAD(pdopenpid_capmode);
-ATF_TC_BODY(pdopenpid_capmode, tc)
+ATF_TC_WITHOUT_HEAD(pdopenpid_child);
+ATF_TC_BODY(pdopenpid_child, tc)
{
pid_t child, parent;
@@ -362,7 +362,36 @@
}
ATF_REQUIRE_MSG(cap_enter() == 0, "cap_enter: %s", strerror(errno));
- ATF_REQUIRE_ERRNO(ECAPMODE, pdopenpid(child, 0) < 0);
+ ATF_REQUIRE_MSG(pdopenpid(child, 0) >= 0, "pdopenpid: %s",
+ strerror(errno));
+}
+
+/*
+ * pdopenpid should fail in capability mode.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_capmode);
+ATF_TC_BODY(pdopenpid_capmode, tc)
+{
+ pid_t child, grandchild;
+
+ ATF_REQUIRE_FEATURE("security_capability_mode");
+
+ child = vfork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ grandchild = fork();
+ ATF_REQUIRE_MSG(grandchild >= 0, "fork: %s", strerror(errno));
+ if (grandchild == 0) {
+ for (;;)
+ sleep(1);
+ } else {
+ _exit(0);
+ }
+ }
+
+ ATF_REQUIRE_MSG(cap_enter() == 0, "cap_enter: %s", strerror(errno));
+ ATF_REQUIRE_ERRNO(ECAPMODE, pdopenpid(grandchild, 0) < 0);
+ kill(grandchild, SIGKILL);
}
/*
@@ -759,6 +788,7 @@
ATF_TP_ADD_TC(tp, pdopenpid_einval);
ATF_TP_ADD_TC(tp, pdopenpid_emfile);
ATF_TP_ADD_TC(tp, pdopenpid_esrch);
+ ATF_TP_ADD_TC(tp, pdopenpid_child);
ATF_TP_ADD_TC(tp, pdopenpid_capmode);
ATF_TP_ADD_TC(tp, pdopenpid_pdfork_then_open);
ATF_TP_ADD_TC(tp, pdopenpid_fork_then_open);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Sep 6, 10:31 PM (19 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38436467
Default Alt Text
D58989.id185325.diff (14 KB)
Attached To
Mode
D58989: Allow pdptrace(2) and pdopenpid(2) in capability mode
Attached
Detach File
Event Timeline
Log In to Comment