mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 02:53:36 +02:00
Merge branch 'so_passrights'
Kuniyuki Iwashima says:
====================
af_unix: Introduce SO_PASSRIGHTS.
As long as recvmsg() or recvmmsg() is used with cmsg, it is not
possible to avoid receiving file descriptors via SCM_RIGHTS.
This series introduces a new socket option, SO_PASSRIGHTS, to allow
disabling SCM_RIGHTS. The option is enabled by default.
See patch 8 for background/context.
This series is related to [0], but is split into a separate series,
as most of the patches are specific to af_unix.
The v2 of the BPF LSM extension part will be posted later, once
this series is merged into net-next and has landed in bpf-next.
[0]: https://lore.kernel.org/bpf/20250505215802.48449-1-kuniyu@amazon.com/
Changes:
v5:
* Patch 4
* Fix BPF selftest failure (setget_sockopt.c)
v4: https://lore.kernel.org/netdev/20250515224946.6931-1-kuniyu@amazon.com/
* Patch 6
* Group sk->sk_scm_XXX bits by struct
* Patch 9
* Remove errno handling
v3: https://lore.kernel.org/netdev/20250514165226.40410-1-kuniyu@amazon.com/
* Patch 3
* Remove inline in scm.c
* Patch 4 & 5 & 8
* Return -EOPNOTSUPP in getsockopt()
* Patch 5
* Add CONFIG_SECURITY_NETWORK check for SO_PASSSEC
* Patch 6
* Add kdoc for sk_scm_unused
* Update sk_scm_XXX under lock_sock() in setsockopt()
* Patch 7
* Update changelog (recent change -> aed6ecef55)
v2: https://lore.kernel.org/netdev/20250510015652.9931-1-kuniyu@amazon.com/
* Added patch 4 & 5 to reuse sk_txrehash for scm_recv() flags
v1: https://lore.kernel.org/netdev/20250508013021.79654-1-kuniyu@amazon.com/
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
819aad967d
|
|
@ -150,6 +150,8 @@
|
|||
|
||||
#define SO_RCVPRIORITY 82
|
||||
|
||||
#define SO_PASSRIGHTS 83
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@
|
|||
|
||||
#define SO_RCVPRIORITY 82
|
||||
|
||||
#define SO_PASSRIGHTS 83
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@
|
|||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 0x4050
|
||||
|
||||
#define SO_PASSRIGHTS 0x4051
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@
|
|||
|
||||
#define SO_RCVPRIORITY 0x005b
|
||||
|
||||
#define SO_PASSRIGHTS 0x005c
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,14 +36,13 @@ struct net;
|
|||
* in sock->flags, but moved into sk->sk_wq->flags to be RCU protected.
|
||||
* Eventually all flags will be in sk->sk_wq->flags.
|
||||
*/
|
||||
#define SOCKWQ_ASYNC_NOSPACE 0
|
||||
#define SOCKWQ_ASYNC_WAITDATA 1
|
||||
#define SOCK_NOSPACE 2
|
||||
#define SOCK_PASSCRED 3
|
||||
#define SOCK_PASSSEC 4
|
||||
#define SOCK_SUPPORT_ZC 5
|
||||
#define SOCK_CUSTOM_SOCKOPT 6
|
||||
#define SOCK_PASSPIDFD 7
|
||||
enum socket_flags {
|
||||
SOCKWQ_ASYNC_NOSPACE,
|
||||
SOCKWQ_ASYNC_WAITDATA,
|
||||
SOCK_NOSPACE,
|
||||
SOCK_SUPPORT_ZC,
|
||||
SOCK_CUSTOM_SOCKOPT,
|
||||
};
|
||||
|
||||
#ifndef ARCH_HAS_SOCKET_TYPES
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -102,123 +102,10 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
|
|||
return __scm_send(sock, msg, scm);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
|
||||
{
|
||||
struct lsm_context ctx;
|
||||
int err;
|
||||
|
||||
if (test_bit(SOCK_PASSSEC, &sock->flags)) {
|
||||
err = security_secid_to_secctx(scm->secid, &ctx);
|
||||
|
||||
if (err >= 0) {
|
||||
put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
|
||||
ctx.context);
|
||||
security_release_secctx(&ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool scm_has_secdata(struct socket *sock)
|
||||
{
|
||||
return test_bit(SOCK_PASSSEC, &sock->flags);
|
||||
}
|
||||
#else
|
||||
static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
|
||||
{ }
|
||||
|
||||
static inline bool scm_has_secdata(struct socket *sock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
|
||||
{
|
||||
struct file *pidfd_file = NULL;
|
||||
int len, 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_flags & MSG_CMSG_COMPAT)
|
||||
len = sizeof(struct compat_cmsghdr) + sizeof(int);
|
||||
else
|
||||
len = sizeof(struct cmsghdr) + sizeof(int);
|
||||
|
||||
if (msg->msg_controllen < len) {
|
||||
msg->msg_flags |= MSG_CTRUNC;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scm->pid)
|
||||
return;
|
||||
|
||||
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 bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!msg->msg_control) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
if (test_bit(SOCK_PASSCRED, &sock->flags)) {
|
||||
struct user_namespace *current_ns = current_user_ns();
|
||||
struct ucred ucreds = {
|
||||
.pid = scm->creds.pid,
|
||||
.uid = from_kuid_munged(current_ns, scm->creds.uid),
|
||||
.gid = from_kgid_munged(current_ns, scm->creds.gid),
|
||||
};
|
||||
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
|
||||
}
|
||||
|
||||
scm_passec(sock, msg, scm);
|
||||
|
||||
if (scm->fp)
|
||||
scm_detach_fds(msg, scm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void scm_recv(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!__scm_recv_common(sock, msg, scm, flags))
|
||||
return;
|
||||
|
||||
scm_destroy_cred(scm);
|
||||
}
|
||||
|
||||
static inline void scm_recv_unix(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!__scm_recv_common(sock, msg, scm, flags))
|
||||
return;
|
||||
|
||||
if (test_bit(SOCK_PASSPIDFD, &sock->flags))
|
||||
scm_pidfd_recv(msg, scm);
|
||||
|
||||
scm_destroy_cred(scm);
|
||||
}
|
||||
void scm_recv(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags);
|
||||
void scm_recv_unix(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags);
|
||||
|
||||
static inline int scm_recv_one_fd(struct file *f, int __user *ufd,
|
||||
unsigned int flags)
|
||||
|
|
|
|||
|
|
@ -337,6 +337,12 @@ struct sk_filter;
|
|||
* @sk_txtime_deadline_mode: set deadline mode for SO_TXTIME
|
||||
* @sk_txtime_report_errors: set report errors mode for SO_TXTIME
|
||||
* @sk_txtime_unused: unused txtime flags
|
||||
* @sk_scm_recv_flags: all flags used by scm_recv()
|
||||
* @sk_scm_credentials: flagged by SO_PASSCRED to recv SCM_CREDENTIALS
|
||||
* @sk_scm_security: flagged by SO_PASSSEC to recv SCM_SECURITY
|
||||
* @sk_scm_pidfd: flagged by SO_PASSPIDFD to recv SCM_PIDFD
|
||||
* @sk_scm_rights: flagged by SO_PASSRIGHTS to recv SCM_RIGHTS
|
||||
* @sk_scm_unused: unused flags for scm_recv()
|
||||
* @ns_tracker: tracker for netns reference
|
||||
* @sk_user_frags: xarray of pages the user is holding a reference on.
|
||||
* @sk_owner: reference to the real owner of the socket that calls
|
||||
|
|
@ -523,7 +529,17 @@ struct sock {
|
|||
#endif
|
||||
int sk_disconnects;
|
||||
|
||||
u8 sk_txrehash;
|
||||
union {
|
||||
u8 sk_txrehash;
|
||||
u8 sk_scm_recv_flags;
|
||||
struct {
|
||||
u8 sk_scm_credentials : 1,
|
||||
sk_scm_security : 1,
|
||||
sk_scm_pidfd : 1,
|
||||
sk_scm_rights : 1,
|
||||
sk_scm_unused : 4;
|
||||
};
|
||||
};
|
||||
u8 sk_clockid;
|
||||
u8 sk_txtime_deadline_mode : 1,
|
||||
sk_txtime_report_errors : 1,
|
||||
|
|
@ -2773,9 +2789,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 +2804,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
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@
|
|||
|
||||
#define SO_RCVPRIORITY 82
|
||||
|
||||
#define SO_PASSRIGHTS 83
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
||||
|
|
|
|||
122
net/core/scm.c
122
net/core/scm.c
|
|
@ -404,3 +404,125 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
|
|||
return new_fpl;
|
||||
}
|
||||
EXPORT_SYMBOL(scm_fp_dup);
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
static void scm_passec(struct sock *sk, struct msghdr *msg, struct scm_cookie *scm)
|
||||
{
|
||||
struct lsm_context ctx;
|
||||
int err;
|
||||
|
||||
if (sk->sk_scm_security) {
|
||||
err = security_secid_to_secctx(scm->secid, &ctx);
|
||||
|
||||
if (err >= 0) {
|
||||
put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
|
||||
ctx.context);
|
||||
|
||||
security_release_secctx(&ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool scm_has_secdata(struct sock *sk)
|
||||
{
|
||||
return sk->sk_scm_security;
|
||||
}
|
||||
#else
|
||||
static void scm_passec(struct sock *sk, struct msghdr *msg, struct scm_cookie *scm)
|
||||
{
|
||||
}
|
||||
|
||||
static bool scm_has_secdata(struct sock *sk)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
|
||||
{
|
||||
struct file *pidfd_file = NULL;
|
||||
int len, 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_flags & MSG_CMSG_COMPAT)
|
||||
len = sizeof(struct compat_cmsghdr) + sizeof(int);
|
||||
else
|
||||
len = sizeof(struct cmsghdr) + sizeof(int);
|
||||
|
||||
if (msg->msg_controllen < len) {
|
||||
msg->msg_flags |= MSG_CTRUNC;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scm->pid)
|
||||
return;
|
||||
|
||||
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 bool __scm_recv_common(struct sock *sk, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!msg->msg_control) {
|
||||
if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
|
||||
scm->fp || scm_has_secdata(sk))
|
||||
msg->msg_flags |= MSG_CTRUNC;
|
||||
|
||||
scm_destroy(scm);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sk->sk_scm_credentials) {
|
||||
struct user_namespace *current_ns = current_user_ns();
|
||||
struct ucred ucreds = {
|
||||
.pid = scm->creds.pid,
|
||||
.uid = from_kuid_munged(current_ns, scm->creds.uid),
|
||||
.gid = from_kgid_munged(current_ns, scm->creds.gid),
|
||||
};
|
||||
|
||||
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
|
||||
}
|
||||
|
||||
scm_passec(sk, msg, scm);
|
||||
|
||||
if (scm->fp)
|
||||
scm_detach_fds(msg, scm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void scm_recv(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!__scm_recv_common(sock->sk, msg, scm, flags))
|
||||
return;
|
||||
|
||||
scm_destroy_cred(scm);
|
||||
}
|
||||
EXPORT_SYMBOL(scm_recv);
|
||||
|
||||
void scm_recv_unix(struct socket *sock, struct msghdr *msg,
|
||||
struct scm_cookie *scm, int flags)
|
||||
{
|
||||
if (!__scm_recv_common(sock->sk, msg, scm, flags))
|
||||
return;
|
||||
|
||||
if (sock->sk->sk_scm_pidfd)
|
||||
scm_pidfd_recv(msg, scm);
|
||||
|
||||
scm_destroy_cred(scm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1220,15 +1220,6 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
|
|||
return 0;
|
||||
}
|
||||
return -EPERM;
|
||||
case SO_PASSSEC:
|
||||
assign_bit(SOCK_PASSSEC, &sock->flags, valbool);
|
||||
return 0;
|
||||
case SO_PASSCRED:
|
||||
assign_bit(SOCK_PASSCRED, &sock->flags, valbool);
|
||||
return 0;
|
||||
case SO_PASSPIDFD:
|
||||
assign_bit(SOCK_PASSPIDFD, &sock->flags, valbool);
|
||||
return 0;
|
||||
case SO_TYPE:
|
||||
case SO_PROTOCOL:
|
||||
case SO_DOMAIN:
|
||||
|
|
@ -1276,6 +1267,8 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
|
|||
return 0;
|
||||
}
|
||||
case SO_TXREHASH:
|
||||
if (!sk_is_tcp(sk))
|
||||
return -EOPNOTSUPP;
|
||||
if (val < -1 || val > 1)
|
||||
return -EINVAL;
|
||||
if ((u8)val == SOCK_TXREHASH_DEFAULT)
|
||||
|
|
@ -1557,6 +1550,33 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
|
|||
sock_valbool_flag(sk, SOCK_SELECT_ERR_QUEUE, valbool);
|
||||
break;
|
||||
|
||||
case SO_PASSCRED:
|
||||
if (sk_may_scm_recv(sk))
|
||||
sk->sk_scm_credentials = valbool;
|
||||
else
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case SO_PASSSEC:
|
||||
if (IS_ENABLED(CONFIG_SECURITY_NETWORK) && sk_may_scm_recv(sk))
|
||||
sk->sk_scm_security = valbool;
|
||||
else
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case SO_PASSPIDFD:
|
||||
if (sk_is_unix(sk))
|
||||
sk->sk_scm_pidfd = valbool;
|
||||
else
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case SO_PASSRIGHTS:
|
||||
if (sk_is_unix(sk))
|
||||
sk->sk_scm_rights = valbool;
|
||||
else
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case SO_INCOMING_CPU:
|
||||
reuseport_update_incoming_cpu(sk, val);
|
||||
|
|
@ -1853,11 +1873,24 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
|
|||
break;
|
||||
|
||||
case SO_PASSCRED:
|
||||
v.val = !!test_bit(SOCK_PASSCRED, &sock->flags);
|
||||
if (!sk_may_scm_recv(sk))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
v.val = sk->sk_scm_credentials;
|
||||
break;
|
||||
|
||||
case SO_PASSPIDFD:
|
||||
v.val = !!test_bit(SOCK_PASSPIDFD, &sock->flags);
|
||||
if (!sk_is_unix(sk))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
v.val = sk->sk_scm_pidfd;
|
||||
break;
|
||||
|
||||
case SO_PASSRIGHTS:
|
||||
if (!sk_is_unix(sk))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
v.val = sk->sk_scm_rights;
|
||||
break;
|
||||
|
||||
case SO_PEERCRED:
|
||||
|
|
@ -1954,7 +1987,10 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
|
|||
break;
|
||||
|
||||
case SO_PASSSEC:
|
||||
v.val = !!test_bit(SOCK_PASSSEC, &sock->flags);
|
||||
if (!IS_ENABLED(CONFIG_SECURITY_NETWORK) || !sk_may_scm_recv(sk))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
v.val = sk->sk_scm_security;
|
||||
break;
|
||||
|
||||
case SO_PEERSEC:
|
||||
|
|
@ -2102,6 +2138,9 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
|
|||
break;
|
||||
|
||||
case SO_TXREHASH:
|
||||
if (!sk_is_tcp(sk))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Paired with WRITE_ONCE() in sk_setsockopt() */
|
||||
v.val = READ_ONCE(sk->sk_txrehash);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -765,6 +765,11 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
|
|||
spin_unlock(&sk->sk_peer_lock);
|
||||
}
|
||||
|
||||
static bool unix_may_passcred(const struct sock *sk)
|
||||
{
|
||||
return sk->sk_scm_credentials || sk->sk_scm_pidfd;
|
||||
}
|
||||
|
||||
static int unix_listen(struct socket *sock, int backlog)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -1010,6 +1015,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,
|
|||
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
sk->sk_scm_rights = 1;
|
||||
sk->sk_hash = unix_unbound_hash(sk);
|
||||
sk->sk_allocation = GFP_KERNEL_ACCOUNT;
|
||||
sk->sk_write_space = unix_write_space;
|
||||
|
|
@ -1411,9 +1417,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
||||
test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
|
||||
!READ_ONCE(unix_sk(sk)->addr)) {
|
||||
if (unix_may_passcred(sk) && !READ_ONCE(unix_sk(sk)->addr)) {
|
||||
err = unix_autobind(sk);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -1531,9 +1535,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
||||
test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
|
||||
!READ_ONCE(u->addr)) {
|
||||
if (unix_may_passcred(sk) && !READ_ONCE(u->addr)) {
|
||||
err = unix_autobind(sk);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -1625,10 +1627,12 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
|
|||
/* The way is open! Fastly set all the necessary fields... */
|
||||
|
||||
sock_hold(sk);
|
||||
unix_peer(newsk) = sk;
|
||||
newsk->sk_state = TCP_ESTABLISHED;
|
||||
newsk->sk_type = sk->sk_type;
|
||||
unix_peer(newsk) = sk;
|
||||
newsk->sk_state = TCP_ESTABLISHED;
|
||||
newsk->sk_type = sk->sk_type;
|
||||
newsk->sk_scm_recv_flags = other->sk_scm_recv_flags;
|
||||
init_peercred(newsk);
|
||||
|
||||
newu = unix_sk(newsk);
|
||||
newu->listener = other;
|
||||
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
|
||||
|
|
@ -1709,17 +1713,6 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void unix_sock_inherit_flags(const struct socket *old,
|
||||
struct socket *new)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static int unix_accept(struct socket *sock, struct socket *newsock,
|
||||
struct proto_accept_arg *arg)
|
||||
{
|
||||
|
|
@ -1756,7 +1749,6 @@ static int unix_accept(struct socket *sock, struct socket *newsock,
|
|||
unix_state_lock(tsk);
|
||||
unix_update_edges(unix_sk(tsk));
|
||||
newsock->state = SS_CONNECTED;
|
||||
unix_sock_inherit_flags(sock, newsock);
|
||||
sock_graft(tsk, newsock);
|
||||
unix_state_unlock(tsk);
|
||||
return 0;
|
||||
|
|
@ -1865,7 +1857,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
UNIXCB(skb).pid = get_pid(scm->pid);
|
||||
UNIXCB(skb).pid = get_pid(scm->pid);
|
||||
UNIXCB(skb).uid = scm->creds.uid;
|
||||
UNIXCB(skb).gid = scm->creds.gid;
|
||||
UNIXCB(skb).fp = NULL;
|
||||
|
|
@ -1877,28 +1869,19 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
|
|||
return err;
|
||||
}
|
||||
|
||||
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_PASSPIDFD, &other->sk_socket->flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some apps rely on write() giving SCM_CREDENTIALS
|
||||
* We include credentials if source or destination socket
|
||||
* asserted SOCK_PASSCRED.
|
||||
*/
|
||||
static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
|
||||
const struct sock *other)
|
||||
static void unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
|
||||
const struct sock *other)
|
||||
{
|
||||
if (UNIXCB(skb).pid)
|
||||
return;
|
||||
if (unix_passcred_enabled(sock, other)) {
|
||||
UNIXCB(skb).pid = get_pid(task_tgid(current));
|
||||
|
||||
if (unix_may_passcred(sk) || unix_may_passcred(other)) {
|
||||
UNIXCB(skb).pid = get_pid(task_tgid(current));
|
||||
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
|
||||
}
|
||||
}
|
||||
|
|
@ -1974,9 +1957,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
||||
test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
|
||||
!READ_ONCE(u->addr)) {
|
||||
if (unix_may_passcred(sk) && !READ_ONCE(u->addr)) {
|
||||
err = unix_autobind(sk);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -2093,6 +2074,11 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (UNIXCB(skb).fp && !other->sk_scm_rights) {
|
||||
err = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (sk->sk_type != SOCK_SEQPACKET) {
|
||||
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
|
||||
if (err)
|
||||
|
|
@ -2139,7 +2125,8 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
|
||||
if (sock_flag(other, SOCK_RCVTSTAMP))
|
||||
__net_timestamp(skb);
|
||||
maybe_add_creds(skb, sock, other);
|
||||
|
||||
unix_maybe_add_creds(skb, sk, other);
|
||||
scm_stat_add(other, skb);
|
||||
skb_queue_tail(&other->sk_receive_queue, skb);
|
||||
unix_state_unlock(other);
|
||||
|
|
@ -2167,14 +2154,14 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
#define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))
|
||||
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other,
|
||||
static int queue_oob(struct sock *sk, struct msghdr *msg, struct sock *other,
|
||||
struct scm_cookie *scm, bool fds_sent)
|
||||
{
|
||||
struct unix_sock *ousk = unix_sk(other);
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
skb = sock_alloc_send_skb(sock->sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
skb = sock_alloc_send_skb(sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
|
||||
if (!skb)
|
||||
return err;
|
||||
|
|
@ -2193,12 +2180,16 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other
|
|||
|
||||
if (sock_flag(other, SOCK_DEAD) ||
|
||||
(other->sk_shutdown & RCV_SHUTDOWN)) {
|
||||
unix_state_unlock(other);
|
||||
err = -EPIPE;
|
||||
goto out;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
maybe_add_creds(skb, sock, other);
|
||||
if (UNIXCB(skb).fp && !other->sk_scm_rights) {
|
||||
err = -EPERM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
unix_maybe_add_creds(skb, sk, other);
|
||||
scm_stat_add(other, skb);
|
||||
|
||||
spin_lock(&other->sk_receive_queue.lock);
|
||||
|
|
@ -2211,6 +2202,8 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other
|
|||
other->sk_data_ready(other);
|
||||
|
||||
return 0;
|
||||
out_unlock:
|
||||
unix_state_unlock(other);
|
||||
out:
|
||||
consume_skb(skb);
|
||||
return err;
|
||||
|
|
@ -2314,7 +2307,13 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
(other->sk_shutdown & RCV_SHUTDOWN))
|
||||
goto out_pipe_unlock;
|
||||
|
||||
maybe_add_creds(skb, sock, other);
|
||||
if (UNIXCB(skb).fp && !other->sk_scm_rights) {
|
||||
unix_state_unlock(other);
|
||||
err = -EPERM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
unix_maybe_add_creds(skb, sk, other);
|
||||
scm_stat_add(other, skb);
|
||||
skb_queue_tail(&other->sk_receive_queue, skb);
|
||||
unix_state_unlock(other);
|
||||
|
|
@ -2324,7 +2323,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
if (msg->msg_flags & MSG_OOB) {
|
||||
err = queue_oob(sock, msg, other, &scm, fds_sent);
|
||||
err = queue_oob(sk, msg, other, &scm, fds_sent);
|
||||
if (err)
|
||||
goto out_err;
|
||||
sent++;
|
||||
|
|
@ -2846,8 +2845,7 @@ 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) ||
|
||||
test_bit(SOCK_PASSPIDFD, &sock->flags)) {
|
||||
} else if (unix_may_passcred(sk)) {
|
||||
/* Copy credentials */
|
||||
scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
|
||||
unix_set_secdata(&scm, skb);
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@
|
|||
|
||||
#define SO_RCVPRIORITY 82
|
||||
|
||||
#define SO_PASSRIGHTS 83
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
||||
|
|
|
|||
|
|
@ -83,6 +83,14 @@ struct loop_ctx {
|
|||
struct sock *sk;
|
||||
};
|
||||
|
||||
static bool sk_is_tcp(struct sock *sk)
|
||||
{
|
||||
return (sk->__sk_common.skc_family == AF_INET ||
|
||||
sk->__sk_common.skc_family == AF_INET6) &&
|
||||
sk->sk_type == SOCK_STREAM &&
|
||||
sk->sk_protocol == IPPROTO_TCP;
|
||||
}
|
||||
|
||||
static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
|
||||
const struct sockopt_test *t,
|
||||
int level)
|
||||
|
|
@ -91,6 +99,9 @@ static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
|
|||
|
||||
opt = t->opt;
|
||||
|
||||
if (opt == SO_TXREHASH && !sk_is_tcp(sk))
|
||||
return 0;
|
||||
|
||||
if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)))
|
||||
return 1;
|
||||
/* kernel initialized txrehash to 255 */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ FIXTURE_VARIANT(scm_rights)
|
|||
int type;
|
||||
int flags;
|
||||
bool test_listener;
|
||||
bool disabled;
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, dgram)
|
||||
|
|
@ -31,6 +32,16 @@ FIXTURE_VARIANT_ADD(scm_rights, dgram)
|
|||
.type = SOCK_DGRAM,
|
||||
.flags = 0,
|
||||
.test_listener = false,
|
||||
.disabled = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, dgram_disabled)
|
||||
{
|
||||
.name = "UNIX ",
|
||||
.type = SOCK_DGRAM,
|
||||
.flags = 0,
|
||||
.test_listener = false,
|
||||
.disabled = true,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream)
|
||||
|
|
@ -39,6 +50,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream)
|
|||
.type = SOCK_STREAM,
|
||||
.flags = 0,
|
||||
.test_listener = false,
|
||||
.disabled = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_disabled)
|
||||
{
|
||||
.name = "UNIX-STREAM ",
|
||||
.type = SOCK_STREAM,
|
||||
.flags = 0,
|
||||
.test_listener = false,
|
||||
.disabled = true,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
|
||||
|
|
@ -47,6 +68,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
|
|||
.type = SOCK_STREAM,
|
||||
.flags = MSG_OOB,
|
||||
.test_listener = false,
|
||||
.disabled = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_oob_disabled)
|
||||
{
|
||||
.name = "UNIX-STREAM ",
|
||||
.type = SOCK_STREAM,
|
||||
.flags = MSG_OOB,
|
||||
.test_listener = false,
|
||||
.disabled = true,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
|
||||
|
|
@ -55,6 +86,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
|
|||
.type = SOCK_STREAM,
|
||||
.flags = 0,
|
||||
.test_listener = true,
|
||||
.disabled = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_listener_disabled)
|
||||
{
|
||||
.name = "UNIX-STREAM ",
|
||||
.type = SOCK_STREAM,
|
||||
.flags = 0,
|
||||
.test_listener = true,
|
||||
.disabled = true,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
|
||||
|
|
@ -63,6 +104,16 @@ FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
|
|||
.type = SOCK_STREAM,
|
||||
.flags = MSG_OOB,
|
||||
.test_listener = true,
|
||||
.disabled = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob_disabled)
|
||||
{
|
||||
.name = "UNIX-STREAM ",
|
||||
.type = SOCK_STREAM,
|
||||
.flags = MSG_OOB,
|
||||
.test_listener = true,
|
||||
.disabled = true,
|
||||
};
|
||||
|
||||
static int count_sockets(struct __test_metadata *_metadata,
|
||||
|
|
@ -105,6 +156,9 @@ FIXTURE_SETUP(scm_rights)
|
|||
ret = unshare(CLONE_NEWNET);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
if (variant->disabled)
|
||||
return;
|
||||
|
||||
ret = count_sockets(_metadata, variant);
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
|
|
@ -113,6 +167,9 @@ FIXTURE_TEARDOWN(scm_rights)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (variant->disabled)
|
||||
return;
|
||||
|
||||
sleep(1);
|
||||
|
||||
ret = count_sockets(_metadata, variant);
|
||||
|
|
@ -121,6 +178,7 @@ FIXTURE_TEARDOWN(scm_rights)
|
|||
|
||||
static void create_listeners(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(scm_rights) *self,
|
||||
const FIXTURE_VARIANT(scm_rights) *variant,
|
||||
int n)
|
||||
{
|
||||
struct sockaddr_un addr = {
|
||||
|
|
@ -140,6 +198,12 @@ static void create_listeners(struct __test_metadata *_metadata,
|
|||
ret = listen(self->fd[i], -1);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
if (variant->disabled) {
|
||||
ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
|
||||
&(int){0}, sizeof(int));
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
|
@ -164,6 +228,12 @@ static void create_socketpairs(struct __test_metadata *_metadata,
|
|||
for (i = 0; i < n * 2; i += 2) {
|
||||
ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
if (variant->disabled) {
|
||||
ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
|
||||
&(int){0}, sizeof(int));
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +245,7 @@ static void __create_sockets(struct __test_metadata *_metadata,
|
|||
ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
|
||||
|
||||
if (variant->test_listener)
|
||||
create_listeners(_metadata, self, n);
|
||||
create_listeners(_metadata, self, variant, n);
|
||||
else
|
||||
create_socketpairs(_metadata, self, variant, n);
|
||||
}
|
||||
|
|
@ -230,7 +300,13 @@ void __send_fd(struct __test_metadata *_metadata,
|
|||
int ret;
|
||||
|
||||
ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
|
||||
ASSERT_EQ(MSGLEN, ret);
|
||||
|
||||
if (variant->disabled) {
|
||||
ASSERT_EQ(-1, ret);
|
||||
ASSERT_EQ(-EPERM, -errno);
|
||||
} else {
|
||||
ASSERT_EQ(MSGLEN, ret);
|
||||
}
|
||||
}
|
||||
|
||||
#define create_sockets(n) \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user