mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	scm: add SO_PASSPIDFD and SCM_PIDFD
Implement SCM_PIDFD, a new type of CMSG type analogical to SCM_CREDENTIALS, but it contains pidfd instead of plain pid, which allows programmers not to care about PID reuse problem. We mask SO_PASSPIDFD feature if CONFIG_UNIX is not builtin because it depends on a pidfd_prepare() API which is not exported to the kernel modules. Idea comes from UAPI kernel group: https://uapi-group.org/kernel-features/ Big thanks to Christian Brauner and Lennart Poettering for productive discussions about this. Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Leon Romanovsky <leon@kernel.org> Cc: David Ahern <dsahern@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Kees Cook <keescook@chromium.org> Cc: Christian Brauner <brauner@kernel.org> Cc: Kuniyuki Iwashima <kuniyu@amazon.com> Cc: Lennart Poettering <mzxreary@0pointer.de> Cc: Luca Boccassi <bluca@debian.org> Cc: linux-kernel@vger.kernel.org Cc: netdev@vger.kernel.org Cc: linux-arch@vger.kernel.org Tested-by: Luca Boccassi <bluca@debian.org> Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com> Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									55d7c91406
								
							
						
					
					
						commit
						5e2ff6704a
					
				
					 12 changed files with 76 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -137,6 +137,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK		75
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD		76
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
#if __BITS_PER_LONG == 64
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,6 +148,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK		75
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD		76
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
#if __BITS_PER_LONG == 64
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -129,6 +129,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK		0x4049
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD		0x404A
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
#if __BITS_PER_LONG == 64
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,6 +130,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK               0x0054
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD             0x0055
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ struct net;
 | 
			
		|||
#define SOCK_PASSSEC		4
 | 
			
		||||
#define SOCK_SUPPORT_ZC		5
 | 
			
		||||
#define SOCK_CUSTOM_SOCKOPT	6
 | 
			
		||||
#define SOCK_PASSPIDFD		7
 | 
			
		||||
 | 
			
		||||
#ifndef ARCH_HAS_SOCKET_TYPES
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -177,6 +177,7 @@ static inline size_t msg_data_left(struct msghdr *msg)
 | 
			
		|||
#define	SCM_RIGHTS	0x01		/* rw: access rights (array of int) */
 | 
			
		||||
#define SCM_CREDENTIALS 0x02		/* rw: struct ucred		*/
 | 
			
		||||
#define SCM_SECURITY	0x03		/* rw: security label		*/
 | 
			
		||||
#define SCM_PIDFD	0x04		/* ro: pidfd (int)		*/
 | 
			
		||||
 | 
			
		||||
struct ucred {
 | 
			
		||||
	__u32	pid;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,12 +120,44 @@ static inline bool scm_has_secdata(struct socket *sock)
 | 
			
		|||
}
 | 
			
		||||
#endif /* CONFIG_SECURITY_NETWORK */
 | 
			
		||||
 | 
			
		||||
static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
 | 
			
		||||
{
 | 
			
		||||
	struct file *pidfd_file = NULL;
 | 
			
		||||
	int pidfd;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * put_cmsg() doesn't return an error if CMSG is truncated,
 | 
			
		||||
	 * that's why we need to opencode these checks here.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((msg->msg_controllen <= sizeof(struct cmsghdr)) ||
 | 
			
		||||
	    (msg->msg_controllen - sizeof(struct cmsghdr)) < sizeof(int)) {
 | 
			
		||||
		msg->msg_flags |= MSG_CTRUNC;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	WARN_ON_ONCE(!scm->pid);
 | 
			
		||||
	pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
 | 
			
		||||
 | 
			
		||||
	if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
 | 
			
		||||
		if (pidfd_file) {
 | 
			
		||||
			put_unused_fd(pidfd);
 | 
			
		||||
			fput(pidfd_file);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pidfd_file)
 | 
			
		||||
		fd_install(pidfd, pidfd_file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
 | 
			
		||||
				struct scm_cookie *scm, int flags)
 | 
			
		||||
{
 | 
			
		||||
	if (!msg->msg_control) {
 | 
			
		||||
		if (test_bit(SOCK_PASSCRED, &sock->flags) || scm->fp ||
 | 
			
		||||
		    scm_has_secdata(sock))
 | 
			
		||||
		if (test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
		    test_bit(SOCK_PASSPIDFD, &sock->flags) ||
 | 
			
		||||
		    scm->fp || scm_has_secdata(sock))
 | 
			
		||||
			msg->msg_flags |= MSG_CTRUNC;
 | 
			
		||||
		scm_destroy(scm);
 | 
			
		||||
		return;
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +173,9 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
 | 
			
		|||
		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (test_bit(SOCK_PASSPIDFD, &sock->flags))
 | 
			
		||||
		scm_pidfd_recv(msg, scm);
 | 
			
		||||
 | 
			
		||||
	scm_destroy_cred(scm);
 | 
			
		||||
 | 
			
		||||
	scm_passec(sock, msg, scm);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,6 +132,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK		75
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD		76
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1246,6 +1246,13 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
			clear_bit(SOCK_PASSCRED, &sock->flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_PASSPIDFD:
 | 
			
		||||
		if (valbool)
 | 
			
		||||
			set_bit(SOCK_PASSPIDFD, &sock->flags);
 | 
			
		||||
		else
 | 
			
		||||
			clear_bit(SOCK_PASSPIDFD, &sock->flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_TIMESTAMP_OLD:
 | 
			
		||||
	case SO_TIMESTAMP_NEW:
 | 
			
		||||
	case SO_TIMESTAMPNS_OLD:
 | 
			
		||||
| 
						 | 
				
			
			@ -1732,6 +1739,10 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
		v.val = !!test_bit(SOCK_PASSCRED, &sock->flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_PASSPIDFD:
 | 
			
		||||
		v.val = !!test_bit(SOCK_PASSPIDFD, &sock->flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_PEERCRED:
 | 
			
		||||
	{
 | 
			
		||||
		struct ucred peercred;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -355,6 +355,7 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
 | 
			
		|||
	case SO_BROADCAST:
 | 
			
		||||
	case SO_BSDCOMPAT:
 | 
			
		||||
	case SO_PASSCRED:
 | 
			
		||||
	case SO_PASSPIDFD:
 | 
			
		||||
	case SO_PASSSEC:
 | 
			
		||||
	case SO_RXQ_OVFL:
 | 
			
		||||
	case SO_WIFI_STATUS:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1361,7 +1361,8 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
 | 
			
		|||
		if (err)
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		if (test_bit(SOCK_PASSCRED, &sock->flags) &&
 | 
			
		||||
		if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
		     test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
 | 
			
		||||
		    !unix_sk(sk)->addr) {
 | 
			
		||||
			err = unix_autobind(sk);
 | 
			
		||||
			if (err)
 | 
			
		||||
| 
						 | 
				
			
			@ -1469,7 +1470,8 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
 | 
			
		|||
	if (err)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr) {
 | 
			
		||||
	if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
	     test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) {
 | 
			
		||||
		err = unix_autobind(sk);
 | 
			
		||||
		if (err)
 | 
			
		||||
			goto out;
 | 
			
		||||
| 
						 | 
				
			
			@ -1670,6 +1672,8 @@ static void unix_sock_inherit_flags(const struct socket *old,
 | 
			
		|||
{
 | 
			
		||||
	if (test_bit(SOCK_PASSCRED, &old->flags))
 | 
			
		||||
		set_bit(SOCK_PASSCRED, &new->flags);
 | 
			
		||||
	if (test_bit(SOCK_PASSPIDFD, &old->flags))
 | 
			
		||||
		set_bit(SOCK_PASSPIDFD, &new->flags);
 | 
			
		||||
	if (test_bit(SOCK_PASSSEC, &old->flags))
 | 
			
		||||
		set_bit(SOCK_PASSSEC, &new->flags);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1819,8 +1823,10 @@ static bool unix_passcred_enabled(const struct socket *sock,
 | 
			
		|||
				  const struct sock *other)
 | 
			
		||||
{
 | 
			
		||||
	return test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
	       test_bit(SOCK_PASSPIDFD, &sock->flags) ||
 | 
			
		||||
	       !other->sk_socket ||
 | 
			
		||||
	       test_bit(SOCK_PASSCRED, &other->sk_socket->flags);
 | 
			
		||||
	       test_bit(SOCK_PASSCRED, &other->sk_socket->flags) ||
 | 
			
		||||
	       test_bit(SOCK_PASSPIDFD, &other->sk_socket->flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1904,7 +1910,8 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
 | 
			
		|||
			goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr) {
 | 
			
		||||
	if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
	     test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) {
 | 
			
		||||
		err = unix_autobind(sk);
 | 
			
		||||
		if (err)
 | 
			
		||||
			goto out;
 | 
			
		||||
| 
						 | 
				
			
			@ -2718,7 +2725,8 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
 | 
			
		|||
			/* Never glue messages from different writers */
 | 
			
		||||
			if (!unix_skb_scm_eq(skb, &scm))
 | 
			
		||||
				break;
 | 
			
		||||
		} else if (test_bit(SOCK_PASSCRED, &sock->flags)) {
 | 
			
		||||
		} else if (test_bit(SOCK_PASSCRED, &sock->flags) ||
 | 
			
		||||
			   test_bit(SOCK_PASSPIDFD, &sock->flags)) {
 | 
			
		||||
			/* Copy credentials */
 | 
			
		||||
			scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
 | 
			
		||||
			unix_set_secdata(&scm, skb);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -121,6 +121,8 @@
 | 
			
		|||
 | 
			
		||||
#define SO_RCVMARK		75
 | 
			
		||||
 | 
			
		||||
#define SO_PASSPIDFD		76
 | 
			
		||||
 | 
			
		||||
#if !defined(__KERNEL__)
 | 
			
		||||
 | 
			
		||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue