[RFC PATCH v1 2/3] pidfd: Extend PIDFD_GET_INFO with PIDFD_INFO_LANDLOCK_*_DOMAIN

Mickaël Salaün mic at digikod.net
Fri Jan 31 16:34:46 UTC 2025


Because Landlock enables users to create nested sandboxes (i.e.
domains), we might need to identify the domain with all restrictions
(latest), or the domain we created (i.e. closest domain).  Indeed,
because any process can create its own domain, the latest domain may not
be known by the requester.

The PIDFD_INFO_LANDLOCK_LAST_DOMAIN flag enables user space to get the
latest (i.e. most nested) Landlock domain ID related to a sandboxed
task, if any.  The domain ID is set in the pidfd_info's
landlock_last_domain field according to the related mask.

The PIDFD_INFO_LANDLOCK_FIRST_DOMAIN flag enables user space to get the
closest (i.e. first hierarchy relative to the pidfd's credentials)
Landlock domain ID related to a sandboxed task, if any.  The domain ID
is set in the pidfd_info's landlock_first_domain field according to the
related mask.

It is only allowed to get information about a Landlock domain if the
task's domain that created the pidfd is a parent of the PID's domain.
Following the object-capability model, the pidfd's credentials are used
instead of the caller's credentials.  This makes this command
idenmpotent wrt the referenced process's state.

If Landlock is not supported or if access to this information is denied,
then the IOCTL does not set the PIDFD_INFO_LANDLOCK_*_DOMAIN flag in the
returned mask.

If PIDFD_INFO_LANDLOCK_LAST_DOMAIN or PIDFD_INFO_LANDLOCK_FIRST_DOMAIN
is specified but the provided struct pidfd_info is not large enough to
contain the related field, then -EINVAL is returned.

Cc: Christian Brauner <brauner at kernel.org>
Cc: Günther Noack <gnoack at google.com>
Cc: Luca Boccassi <luca.boccassi at gmail.com>
Cc: Praveen K Paladugu <prapal at linux.microsoft.com>
Closes: https://github.com/landlock-lsm/linux/issues/26
Signed-off-by: Mickaël Salaün <mic at digikod.net>
Link: https://lore.kernel.org/r/20250131163447.1140564-3-mic@digikod.net
---
 fs/pidfs.c                 | 24 ++++++++++++++++++++++--
 include/uapi/linux/pidfd.h |  4 ++++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/fs/pidfs.c b/fs/pidfs.c
index 049352f973de..4ff5b6c776ce 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -20,6 +20,7 @@
 #include <linux/time_namespace.h>
 #include <linux/utsname.h>
 #include <net/net_namespace.h>
+#include <linux/landlock.h>
 
 #include "internal.h"
 #include "mount.h"
@@ -207,7 +208,8 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
 	return poll_flags;
 }
 
-static long pidfd_info(struct task_struct *task, unsigned int cmd, unsigned long arg)
+static long pidfd_info(const struct cred *cred, struct task_struct *task,
+		       unsigned int cmd, unsigned long arg)
 {
 	struct pidfd_info __user *uinfo = (struct pidfd_info __user *)arg;
 	size_t usize = _IOC_SIZE(cmd);
@@ -227,6 +229,14 @@ static long pidfd_info(struct task_struct *task, unsigned int cmd, unsigned long
 	if (copy_from_user(&mask, &uinfo->mask, sizeof(mask)))
 		return -EFAULT;
 
+	if ((mask & PIDFD_INFO_LANDLOCK_LAST_DOMAIN) &&
+	    usize < offsetofend(typeof(*uinfo), landlock_last_domain))
+		return -EINVAL;
+
+	if ((mask & PIDFD_INFO_LANDLOCK_FIRST_DOMAIN) &&
+	    usize < offsetofend(typeof(*uinfo), landlock_first_domain))
+		return -EINVAL;
+
 	c = get_task_cred(task);
 	if (!c)
 		return -ESRCH;
@@ -253,6 +263,16 @@ static long pidfd_info(struct task_struct *task, unsigned int cmd, unsigned long
 	rcu_read_unlock();
 #endif
 
+	if ((mask & PIDFD_INFO_LANDLOCK_LAST_DOMAIN) &&
+	    !landlock_read_domain_id(cred, task, true,
+				     &kinfo.landlock_last_domain))
+		kinfo.mask |= PIDFD_INFO_LANDLOCK_LAST_DOMAIN;
+
+	if ((mask & PIDFD_INFO_LANDLOCK_FIRST_DOMAIN) &&
+	    !landlock_read_domain_id(cred, task, false,
+				     &kinfo.landlock_first_domain))
+		kinfo.mask |= PIDFD_INFO_LANDLOCK_FIRST_DOMAIN;
+
 	/*
 	 * Copy pid/tgid last, to reduce the chances the information might be
 	 * stale. Note that it is not possible to ensure it will be valid as the
@@ -328,7 +348,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 	/* Extensible IOCTL that does not open namespace FDs, take a shortcut */
 	if (_IOC_NR(cmd) == _IOC_NR(PIDFD_GET_INFO))
-		return pidfd_info(task, cmd, arg);
+		return pidfd_info(file->f_cred, task, cmd, arg);
 
 	if (arg)
 		return -EINVAL;
diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h
index 4540f6301b8c..267991bd266c 100644
--- a/include/uapi/linux/pidfd.h
+++ b/include/uapi/linux/pidfd.h
@@ -20,6 +20,8 @@
 #define PIDFD_INFO_PID			(1UL << 0) /* Always returned, even if not requested */
 #define PIDFD_INFO_CREDS		(1UL << 1) /* Always returned, even if not requested */
 #define PIDFD_INFO_CGROUPID		(1UL << 2) /* Always returned if available, even if not requested */
+#define PIDFD_INFO_LANDLOCK_LAST_DOMAIN	(1UL << 3) /* Only returned if requested */
+#define PIDFD_INFO_LANDLOCK_FIRST_DOMAIN	(1UL << 4) /* Only returned if requested */
 
 #define PIDFD_INFO_SIZE_VER0		64 /* sizeof first published struct */
 
@@ -63,6 +65,8 @@ struct pidfd_info {
 	__u32 fsuid;
 	__u32 fsgid;
 	__u32 spare0[1];
+	__u64 landlock_last_domain;
+	__u64 landlock_first_domain;
 };
 
 #define PIDFS_IOCTL_MAGIC 0xFF
-- 
2.48.1




More information about the Linux-security-module-archive mailing list