Re: [RFC PATCH] af_unix: introduce MSG_CMSG_SEALED

From: Stephen Smalley

Date: Mon Nov 02 2026 - 14:07:07 EST


On Mon, Nov 2, 2026 at 10:14 AM Stephen Smalley
<stephen.smalley.work@xxxxxxxxx> wrote:
>
> On Mon, Nov 2, 2026 at 8:05 AM Ondrej Mosnáček <omosnacek@xxxxxxxxx> wrote:
> >
> > Currently, when a process receives a file through AF_UNIX's SCM_RIGHTS
> > mechanism, the security_file_receive() LSM hook is calledto allow LSMs
> > like SELinux to validate the receiver's access to the received file
> > (similarly to a process inheriting file descriptors through execve(2)).
> >
> > However, there are cases (such as dbus daemon/dbus-broker) where the
> > receiver doesn't intend to access the received file and merely wants to
> > forward it to a third process. In this case the forwarding process would
> > need to have the appropriate r/w access to the file in order to receive
> > it, even though it will never actually acess it. That leads to the
> > security policy having to grant the forwarder access to a potentially
> > wide range of files (in case of dbus-broker basically all files), which
> > is not ideal.
> >
> > This patch adds a new CMSG flag MSG_CMSG_SEALED that, when passed to
> > recvmsg(2), wraps any received files into an opaque anonymous inode with
> > the name "[envelope]", skipping the security_file_receive() LSM check.
> > The returned "[envelope]" file descriptor can't be used in any
> > meaningful way other than being passed forward via sendmsg(2) with the
> > MSG_CMSG_SEALED flag. In that case the original file is unwrapped and
> > sent via SCM_RIGHTS to the receiver. The receiver can then either
> > receive the file as normal (subject to the LSM check) or again "sealed"
> > in the "[envelope]" anon inode for further forwarding.
> >
> > Note that sending the "[envelope]" file is only allowed with the
> > MSG_CMSG_SEALED flag set, to make the unsealing explicit and avoid
> > unexpected behavior. Attempting to send it without this flag will fail
> > with an error. This also avoids recursive nesting of "[envelope]" files,
> > which would only make this feature harder to audit for possible issues.
> >
> > This patch also adds a simple read-only sockopt for UNIX sockets that
> > allows userspace to determine if this feature is present (passing
> > unknown CMSG flags unfortunately doesn't lead to an error currently).
> > The minimal code to do so can look like this:
> >
> > #ifndef SO_RIGHTS_SEALED_SUPPORTED
> > #define SO_RIGHTS_SEALED_SUPPORTED 88
> > #endif
> >
> > #include <sys/socket.h>
> > #include <unistd.h>
> > #include <stdbool.h>
> >
> > bool MSG_CMSG_SEALED_supported(void)
> > {
> > int sock, ret;
> > socklen_t optlen = 0;
> >
> > sock = socket(AF_UNIX, SOCK_STREAM, 0);
> > if (sock < 0) {
> > /* handle error */
> > /* ... */
> > }
> >
> > ret = getsockopt(sock, SOL_SOCKET, SO_RIGHTS_SEALED_SUPPORTED,
> > NULL, &optlen);
> > close(sock);
> >
> > return ret == 0;
> > }
> >
> > Related: https://github.com/fedora-selinux/selinux-policy/issues/3286
> > Related: https://github.com/fedora-selinux/selinux-policy/issues/3287
> > Signed-off-by: Ondrej Mosnáček <omosnacek@xxxxxxxxx>
> > ---
>
> > diff --git a/net/core/scm.c b/net/core/scm.c
> > index ae38f3c1165c..08822b2a1f22 100644
> > --- a/net/core/scm.c
> > +++ b/net/core/scm.c
> > @@ -114,10 +127,29 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
> >
> > if (fd < 0 || !(file = fget_raw(fd)))
> > return -EBADF;
> > - /* don't allow io_uring files */
> > - if (io_is_uring_fops(file)) {
> > - fput(file);
> > - return -EINVAL;
> > +
> > + if (sealed) {
> > + /* Only allow sealed files */
> > + if (file->f_op != &envelope_ops)
> > + return -EINVAL;
>
> This leaks a file reference.
>
> > +
> > + file = (struct file *)file->private_data;
>
> This fails to take a reference to the underlying file but will later get fput().
>
> > +
> > + /*
> > + * No need to check for io_uring files here as the
> > + * underlying file had already successfully passed the
> > + * check when it was first sent.
> > + */
> > + } else {
> > + /* Don't allow sealed files */
> > + if (file->f_op == &envelope_ops)
> > + return -EINVAL;
>
> This also leaks a file reference.
>
> > @@ -365,18 +398,37 @@ static int scm_max_fds(struct msghdr *msg)
> > }
> >
> > int scm_recv_one_fd(struct file *f, int __user *ufd, unsigned int flags,
> > - bool notrunc)
> > + bool notrunc, bool sealed)
> > {
> > + struct file *f_received;
> > int error;
> >
> > if (!ufd)
> > return -EFAULT;
> >
> > - error = security_file_receive(f);
> > - if (error)
> > - return notrunc ? put_user(error, ufd) : error;
>
> Design point: I would suggest adding and using a new LSM hook that
> allows security modules (like SELinux) to still be able to check a
> (new) permission that governs whether a process can pass along these
> sealed fds so that we do not entirely lose control over their use.

One other design point: you are creating the envelope with
anon_inode_getfile(); consider using anon_inode_getfile_secure()
instead so that security_inode_init_security_anon() can give the
envelope a labelable context, which SELinux could then use to label
and control envelope use directly.