From: Demi Marie Obenour Abstract sockets have been a security risk in the past. Since they aren't associated with filesystem paths, they bypass all filesystem access controls. This means that they can allow file descriptors to be passed out of sandboxes that do not allow connecting to named sockets. On systems using the Nix daemon, this allowed privilege escalation to root, and fixing the bug required Nix to use a complete user-mode network stack. Furthermore, anyone can bind to any abstract socket path, so anyone connecting to an abstract socket has no idea who they are connecting to. This allows disabling the security hole by preventing all connections to abstract sockets. For compatibility, it is still possible to bind to abstract socket paths, but such sockets will never receive any connections or datagrams. Signed-off-by: Demi Marie Obenour --- net/unix/Kconfig | 12 ++++++++++++ net/unix/af_unix.c | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/net/unix/Kconfig b/net/unix/Kconfig index 6f1783c1659b81c3c3c89cb7634a9ce780144f26..c34f222f21b097ce4a735ce02d8ce11fc71bde19 100644 --- a/net/unix/Kconfig +++ b/net/unix/Kconfig @@ -16,6 +16,18 @@ config UNIX Say Y unless you know what you are doing. +config UNIX_ABSTRACT + bool "UNIX: abstract sockets" + depends on UNIX + default y + help + Support for "abstract" sockets (those not bound to a path). + These have been used in the past, but they can also represent + a security risk because anyone can bind to any abstract + socket. If you disable this option, programs can still bind + to abstract sockets, but any attempt to connect to one fails + with -ECONNREFUSED. + config AF_UNIX_OOB bool "UNIX: out-of-bound messages" depends on UNIX diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 52b155123985a18632fc12dc986150e38f2fee70..81d55849dac58e4e68c28ed03a9bc978777cfe4f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -332,7 +332,8 @@ static inline void unix_release_addr(struct unix_address *addr) * - if started by zero, it is abstract name. */ -static int unix_validate_addr(struct sockaddr_un *sunaddr, int addr_len) +static int unix_validate_addr(struct sockaddr_un *sunaddr, int addr_len, + bool bind) { if (addr_len <= offsetof(struct sockaddr_un, sun_path) || addr_len > sizeof(*sunaddr)) @@ -341,6 +342,9 @@ static int unix_validate_addr(struct sockaddr_un *sunaddr, int addr_len) if (sunaddr->sun_family != AF_UNIX) return -EINVAL; + if (!bind && !IS_ENABLED(CONFIG_UNIX_ABSTRACT) && !sunaddr->sun_path[0]) + return -ECONNREFUSED; /* pretend nobody is listening */ + return 0; } @@ -1253,6 +1257,8 @@ static struct sock *unix_find_other(struct net *net, if (sunaddr->sun_path[0]) sk = unix_find_bsd(sunaddr, addr_len, type, flags); + else if (!IS_ENABLED(CONFIG_UNIX_ABSTRACT)) + sk = ERR_PTR(-EPERM); else sk = unix_find_abstract(net, sunaddr, addr_len, type); @@ -1444,7 +1450,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) sunaddr->sun_family == AF_UNIX) return unix_autobind(sk); - err = unix_validate_addr(sunaddr, addr_len); + err = unix_validate_addr(sunaddr, addr_len, true); if (err) return err; @@ -1493,7 +1499,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, goto out; if (addr->sa_family != AF_UNSPEC) { - err = unix_validate_addr(sunaddr, alen); + err = unix_validate_addr(sunaddr, alen, false); if (err) goto out; @@ -1612,7 +1618,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, long timeo; int err; - err = unix_validate_addr(sunaddr, addr_len); + err = unix_validate_addr(sunaddr, addr_len, false); if (err) goto out; @@ -2048,7 +2054,9 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, } if (msg->msg_namelen) { - err = unix_validate_addr(msg->msg_name, msg->msg_namelen); + err = unix_validate_addr(msg->msg_name, + msg->msg_namelen, + false); if (err) goto out; --- base-commit: 038d61fd642278bab63ee8ef722c50d10ab01e8f change-id: 20250731-no-abstract-6672e8ad03e1 Best regards, -- Demi Marie Obenour