]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: Restrict SO_PASS{CRED,PIDFD,SEC} to AF_{UNIX,NETLINK,BLUETOOTH}.
authorKuniyuki Iwashima <kuniyu@amazon.com>
Mon, 19 May 2025 20:57:56 +0000 (13:57 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 23 May 2025 09:24:18 +0000 (10:24 +0100)
SCM_CREDENTIALS and SCM_SECURITY can be recv()ed by calling
scm_recv() or scm_recv_unix(), and SCM_PIDFD is only used by
scm_recv_unix().

scm_recv() is called from AF_NETLINK and AF_BLUETOOTH.

scm_recv_unix() is literally called from AF_UNIX.

Let's restrict SO_PASSCRED and SO_PASSSEC to such sockets and
SO_PASSPIDFD to AF_UNIX only.

Later, SOCK_PASS{CRED,PIDFD,SEC} will be moved to struct sock
and united with another field.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sock.h
net/core/sock.c

index 35ca6b13c6d21ff560f43fba931de5b07b33fd4c..483522377955c130da1ae1ab9751818365b56b01 100644 (file)
@@ -2773,9 +2773,14 @@ static inline bool sk_is_udp(const struct sock *sk)
               sk->sk_protocol == IPPROTO_UDP;
 }
 
+static inline bool sk_is_unix(const struct sock *sk)
+{
+       return sk->sk_family == AF_UNIX;
+}
+
 static inline bool sk_is_stream_unix(const struct sock *sk)
 {
-       return sk->sk_family == AF_UNIX && sk->sk_type == SOCK_STREAM;
+       return sk_is_unix(sk) && sk->sk_type == SOCK_STREAM;
 }
 
 static inline bool sk_is_vsock(const struct sock *sk)
@@ -2783,6 +2788,13 @@ static inline bool sk_is_vsock(const struct sock *sk)
        return sk->sk_family == AF_VSOCK;
 }
 
+static inline bool sk_may_scm_recv(const struct sock *sk)
+{
+       return (IS_ENABLED(CONFIG_UNIX) && sk->sk_family == AF_UNIX) ||
+               sk->sk_family == AF_NETLINK ||
+               (IS_ENABLED(CONFIG_BT) && sk->sk_family == AF_BLUETOOTH);
+}
+
 /**
  * sk_eat_skb - Release a skb if it is no longer needed
  * @sk: socket to eat this skb from
index d7d6d3a8efe5849fd2f20bd43e1f55281de0a55f..fd5f9d3873c14bc76bee86856cce37ef328d41ab 100644 (file)
@@ -1221,12 +1221,21 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
                }
                return -EPERM;
        case SO_PASSSEC:
+               if (!IS_ENABLED(CONFIG_SECURITY_NETWORK) || sk_may_scm_recv(sk))
+                       return -EOPNOTSUPP;
+
                assign_bit(SOCK_PASSSEC, &sock->flags, valbool);
                return 0;
        case SO_PASSCRED:
+               if (!sk_may_scm_recv(sk))
+                       return -EOPNOTSUPP;
+
                assign_bit(SOCK_PASSCRED, &sock->flags, valbool);
                return 0;
        case SO_PASSPIDFD:
+               if (!sk_is_unix(sk))
+                       return -EOPNOTSUPP;
+
                assign_bit(SOCK_PASSPIDFD, &sock->flags, valbool);
                return 0;
        case SO_TYPE:
@@ -1855,10 +1864,16 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
                break;
 
        case SO_PASSCRED:
+               if (!sk_may_scm_recv(sk))
+                       return -EOPNOTSUPP;
+
                v.val = !!test_bit(SOCK_PASSCRED, &sock->flags);
                break;
 
        case SO_PASSPIDFD:
+               if (!sk_is_unix(sk))
+                       return -EOPNOTSUPP;
+
                v.val = !!test_bit(SOCK_PASSPIDFD, &sock->flags);
                break;
 
@@ -1956,6 +1971,9 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
                break;
 
        case SO_PASSSEC:
+               if (!IS_ENABLED(CONFIG_SECURITY_NETWORK) || !sk_may_scm_recv(sk))
+                       return -EOPNOTSUPP;
+
                v.val = !!test_bit(SOCK_PASSSEC, &sock->flags);
                break;