diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 5d3524c26b2b..cd9fce1d9432 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -161,6 +161,8 @@
#define SO_PEERPIDFD_THREAD 87
+#define SO_RIGHTS_SEALED_SUPPORTED 88
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index 245a43f52fb1..393898479ce6 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -172,6 +172,8 @@
#define SO_PEERPIDFD_THREAD 87
+#define SO_RIGHTS_SEALED_SUPPORTED 88
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index f23710e1c671..055b532ccc14 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -153,6 +153,8 @@
#define SO_PEERPIDFD_THREAD 0x4055
+#define SO_RIGHTS_SEALED_SUPPORTED 0x4056
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index b35b25bdefc2..e152e2bdf42e 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -154,6 +154,8 @@
#define SO_PEERPIDFD_THREAD 0x0060
+#define SO_RIGHTS_SEALED_SUPPORTED 0x0061
+
#if !defined(__KERNEL__)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index b17fdb7d38a7..90e4bc5d3f91 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -345,7 +345,9 @@ struct ucred {
#define MSG_SOCK_DEVMEM 0x2000000 /* Receive devmem skbs as cmsg */
#define MSG_ZEROCOPY 0x4000000 /* Use user data in kernel path */
#define MSG_SPLICE_PAGES 0x8000000 /* Splice the pages from the iterator in sendmsg() */
-#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */
+#define MSG_CMSG_SEALED 0x10000000 /* Seal file descriptors received
+ through SCM_RIGHTS */
+#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */
#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exec for file
descriptor received through
SCM_RIGHTS */diff --git a/include/net/scm.h b/include/net/scm.h
index b68a143f42de..53b999868104 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -108,7 +108,7 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags);
int scm_recv_one_fd(struct file *f, int __user *ufd, unsigned int flags,
- bool notrunc);
+ bool notrunc, bool sealed);
#endif /* __LINUX_NET_SCM_H */
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index 56fed7ab27ab..14bf06367ef1 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -156,6 +156,8 @@
#define SO_PEERPIDFD_THREAD 87
+#define SO_RIGHTS_SEALED_SUPPORTED 88
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
diff --git a/io_uring/net.c b/io_uring/net.c
index eeab88e77fd8..b579d542b7e2 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -992,7 +992,7 @@ static int io_recvmsg_multishot(struct socket *sock, struct io_sr_msg *io,
if (kmsg->namelen)
kmsg->msg.msg_name = &hdr.addr;
- kmsg->msg.msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
+ kmsg->msg.msg_flags = flags & (MSG_CMSG_CLOEXEC | MSG_CMSG_SEALED | MSG_CMSG_COMPAT);
kmsg->msg.msg_namelen = 0;
if (sock->file->f_flags & O_NONBLOCK)
diff --git a/net/compat.c b/net/compat.c
index 6bdf4a2c9077..29816b47f675 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -296,7 +296,8 @@ void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm, bool notr
int err = 0, i;
for (i = 0; i < fdmax; i++) {
- err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags, notrunc);
+ err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags, notrunc,
+ msg->msg_flags & MSG_CMSG_SEALED);
if (err < 0)
break;
}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
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/errqueue.h>
#include <linux/io_uring.h>
+#include <linux/anon_inodes.h>
#include <linux/uaccess.h>
@@ -66,7 +67,19 @@ static __inline__ int scm_check_creds(struct ucred *creds)
return -EPERM;
}
-static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+static int envelope_release(struct inode *inode, struct file *file)
+{
+ struct file *inner = file->private_data;
+
+ fput(inner);
+ return 0;
+}
+
+static const struct file_operations envelope_ops = {
+ .release = envelope_release,
+};
+
+static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp, bool sealed)
{
int *fdp = (int*)CMSG_DATA(cmsg);
struct scm_fp_list *fpl = *fplp;@@ -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;
+
+ file = (struct file *)file->private_data;
+
+ /*
+ * 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;
+
+ /* don't allow io_uring files */
+ if (io_is_uring_fops(file)) {
+ fput(file);
+ return -EINVAL;
+ }
}
if (unix_get_socket(file))
fpl->count_unix++;@@ -203,7 +235,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
case SCM_RIGHTS:
if (!ops || ops->family != PF_UNIX)
goto error;
- err=scm_fp_copy(cmsg, &p->fp);
+ err = scm_fp_copy(cmsg, &p->fp,
+ msg->msg_flags & MSG_CMSG_SEALED);
if (err<0)
goto error;
break;
@@ -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;
+ if (sealed) {
+ /*
+ * Receive fd sealed in an "envelope" anonymous fd.
+ * This will install a special file that can be unsealed
+ * upon sending to another process, otherwise the underlying
+ * file will not be accessible to this process, avoiding the
+ * need to pass the LSM check below.
+ */
+ f_received = anon_inode_getfile("[envelope]", &envelope_ops, f,
+ O_RDONLY);
+ if (IS_ERR(f_received))
+ return PTR_ERR(f_received);
+
+ get_file(f);
+ } else {
+ error = security_file_receive(f);
+ if (error)
+ return notrunc ? put_user(error, ufd) : error;
- FD_PREPARE(fdf, flags, get_file(f));
+ f_received = get_file(f);
+ }
+
+ FD_PREPARE(fdf, flags, f_received);
if (fdf->fd < 0)
return fdf->fd;
@@ -384,7 +436,9 @@ int scm_recv_one_fd(struct file *f, int __user *ufd, unsigned int flags,
if (error)
return error;
- __receive_sock(fdf->file);
+ if (!sealed)
+ __receive_sock(fdf->file);
+
return fd_publish(fdf);
}
@@ -407,7 +461,8 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm, bool notrunc)
}
for (i = 0; i < fdmax; i++) {
- err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags, notrunc);
+ err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags, notrunc,
+ msg->msg_flags & MSG_CMSG_SEALED);
if (err < 0)
break;
}diff --git a/net/core/sock.c b/net/core/sock.c
index e7405be8941f..68a447d8bf52 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1959,6 +1959,15 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
v.val = sk->sk_scm_rights;
break;
+ case SO_RIGHTS_SEALED_SUPPORTED:
+ if (!sk_is_unix(sk))
+ return -EOPNOTSUPP;
+
+ /* Just succeed without returning anything, signaling
+ * support.
+ */
+ break;
+
case SO_PEERCRED:
{
struct ucred peercred;diff --git a/net/socket.c b/net/socket.c
index 2e3b5737c481..dbb3c7c6ff66 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2921,7 +2921,7 @@ static int ____sys_recvmsg(struct socket *sock, struct msghdr *msg_sys,
msg_sys->msg_name = &addr;
cmsg_ptr = (unsigned long)msg_sys->msg_control;
- msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
+ msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC | MSG_CMSG_SEALED | MSG_CMSG_COMPAT);
/* We assume all kernel code knows the size of sockaddr_storage */
msg_sys->msg_namelen = 0;
diff --git a/tools/include/uapi/asm-generic/socket.h b/tools/include/uapi/asm-generic/socket.h
index f333a0ac4ee4..fb9e98a7b370 100644
--- a/tools/include/uapi/asm-generic/socket.h
+++ b/tools/include/uapi/asm-generic/socket.h
@@ -147,6 +147,8 @@
#define SO_PASSRIGHTS 83
+#define SO_RIGHTS_SEALED_SUPPORTED 88
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
--
2.54.0