Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103749739
D47832.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D47832.diff
View Options
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -966,9 +966,7 @@
(inp->inp_socket->so_type != SOCK_STREAM ||
in_nullhost(t->inp_faddr)) &&
(!in_nullhost(laddr) ||
- !in_nullhost(t->inp_laddr) ||
- (t->inp_socket->so_options & SO_REUSEPORT) ||
- (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) &&
+ !in_nullhost(t->inp_laddr)) &&
(inp->inp_cred->cr_uid != t->inp_cred->cr_uid))
return (EADDRINUSE);
}
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -245,9 +245,7 @@
(inp->inp_socket->so_type != SOCK_STREAM ||
IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) &&
(!IN6_IS_ADDR_UNSPECIFIED(laddr) ||
- !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
- (t->inp_socket->so_options & SO_REUSEPORT) ||
- (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr)) &&
(inp->inp_cred->cr_uid != t->inp_cred->cr_uid))
return (EADDRINUSE);
diff --git a/tests/sys/netinet/socket_afinet.c b/tests/sys/netinet/socket_afinet.c
--- a/tests/sys/netinet/socket_afinet.c
+++ b/tests/sys/netinet/socket_afinet.c
@@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Bjoern A. Zeeb
+ * Copyright (c) 2024 Stormshield
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,11 +26,17 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
+#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/wait.h>
+
#include <netinet/in.h>
+
#include <errno.h>
#include <poll.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
#include <atf-c.h>
@@ -281,6 +288,235 @@
ATF_CHECK_EQ(0, rc);
}
+/*
+ * Make sure that unprivileged users can't set the IP_BINDANY or IPV6_BINDANY
+ * socket options.
+ */
+ATF_TC(socket_afinet_bindany);
+ATF_TC_HEAD(socket_afinet_bindany, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(socket_afinet_bindany, tc)
+{
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET6, SOCK_STREAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+}
+
+/*
+ * Bind a socket to the specified address, optionally dropping privileges and
+ * setting one of the SO_REUSE* options first.
+ *
+ * Returns true if the bind succeeded, and false if it failed with EADDRINUSE.
+ */
+static bool
+child_bind(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt, bool unpriv)
+{
+ const char *user;
+ pid_t child;
+
+ if (unpriv) {
+ if (!atf_tc_has_config_var(tc, "unprivileged_user"))
+ atf_tc_skip("unprivileged_user not set");
+ user = atf_tc_get_config_var(tc, "unprivileged_user");
+ } else {
+ user = NULL;
+ }
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ int s;
+
+ if (user != NULL) {
+ struct passwd *passwd;
+
+ passwd = getpwnam(user);
+ if (seteuid(passwd->pw_uid) != 0)
+ _exit(1);
+ }
+
+ s = socket(sa->sa_family, type, 0);
+ if (s < 0)
+ _exit(2);
+ if (bind(s, sa, sa->sa_len) == 0)
+ _exit(3);
+ if (errno != EADDRINUSE)
+ _exit(4);
+ if (opt != 0) {
+ if (setsockopt(s, SOL_SOCKET, opt, &(int){1},
+ sizeof(int)) != 0)
+ _exit(5);
+ }
+ if (bind(s, sa, sa->sa_len) == 0)
+ _exit(6);
+ if (errno != EADDRINUSE)
+ _exit(7);
+ _exit(0);
+ } else {
+ int status;
+
+ ATF_REQUIRE_EQ(waitpid(child, &status, 0), child);
+ ATF_REQUIRE(WIFEXITED(status));
+ status = WEXITSTATUS(status);
+ ATF_REQUIRE_MSG(status == 0 || status == 6,
+ "child exited with %d", status);
+ return (status == 6);
+ }
+}
+
+static bool
+child_bind_priv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt)
+{
+ return (child_bind(tc, type, sa, opt, false));
+}
+
+static bool
+child_bind_unpriv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt)
+{
+ return (child_bind(tc, type, sa, opt, true));
+}
+
+static int
+bind_socket(int domain, int type, int opt, bool unspec, struct sockaddr *sa)
+{
+ socklen_t slen;
+ int s;
+
+ s = socket(domain, type, 0);
+ ATF_REQUIRE(s >= 0);
+
+ if (domain == AF_INET) {
+ struct sockaddr_in sin;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(unspec ?
+ INADDR_ANY : INADDR_LOOPBACK);
+ sin.sin_port = htons(0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ slen = sizeof(sin);
+ } else /* if (domain == AF_INET6) */ {
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = unspec ? in6addr_any : in6addr_loopback;
+ sin6.sin6_port = htons(0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == 0);
+
+ slen = sizeof(sin6);
+ }
+
+ if (opt != 0) {
+ ATF_REQUIRE(setsockopt(s, SOL_SOCKET, opt, &(int){1},
+ sizeof(int)) == 0);
+ }
+
+ ATF_REQUIRE(getsockname(s, sa, &slen) == 0);
+
+ return (s);
+}
+
+static void
+multibind_test(const atf_tc_t *tc, int domain, int type)
+{
+ struct sockaddr_storage ss;
+ int opts[4] = { 0, SO_REUSEADDR, SO_REUSEPORT, SO_REUSEPORT_LB };
+ int s;
+ bool flags[2] = { false, true };
+ bool res;
+
+ for (size_t flagi = 0; flagi < nitems(flags); flagi++) {
+ for (size_t opti = 0; opti < nitems(opts); opti++) {
+ s = bind_socket(domain, type, opts[opti], flags[flagi],
+ (struct sockaddr *)&ss);
+ for (size_t optj = 0; optj < nitems(opts); optj++) {
+ int opt;
+
+ opt = opts[optj];
+ res = child_bind_priv(tc, type,
+ (struct sockaddr *)&ss, opt);
+ /*
+ * Multi-binding is only allowed when both
+ * sockets have SO_REUSEPORT or SO_REUSEPORT_LB
+ * set.
+ */
+ if (opts[opti] != 0 &&
+ opts[opti] != SO_REUSEADDR && opti == optj)
+ ATF_REQUIRE(res);
+ else
+ ATF_REQUIRE(!res);
+
+ res = child_bind_unpriv(tc, type,
+ (struct sockaddr *)&ss, opt);
+ /*
+ * Multi-binding is only allowed when both
+ * sockets have the same owner.
+ *
+ * XXX-MJ we for some reason permit this when
+ * binding to the unspecified address, but I
+ * don't think that's right
+ */
+ if (flags[flagi] && opts[opti] != 0 &&
+ opts[opti] != SO_REUSEADDR && opti == optj)
+ ATF_REQUIRE(res);
+ else
+ ATF_REQUIRE(!res);
+ }
+ ATF_REQUIRE(close(s) == 0);
+ }
+ }
+}
+
+/*
+ * Try to bind two sockets to the same address/port tuple. Under some
+ * conditions this is permitted.
+ */
+ATF_TC(socket_afinet_multibind);
+ATF_TC_HEAD(socket_afinet_multibind, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "unprivileged_user");
+}
+ATF_TC_BODY(socket_afinet_multibind, tc)
+{
+ multibind_test(tc, AF_INET, SOCK_STREAM);
+ multibind_test(tc, AF_INET, SOCK_DGRAM);
+ multibind_test(tc, AF_INET6, SOCK_STREAM);
+ multibind_test(tc, AF_INET6, SOCK_DGRAM);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, socket_afinet);
@@ -289,6 +525,8 @@
ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup);
ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup);
ATF_TP_ADD_TC(tp, socket_afinet_stream_reconnect);
+ ATF_TP_ADD_TC(tp, socket_afinet_bindany);
+ ATF_TP_ADD_TC(tp, socket_afinet_multibind);
return atf_no_error();
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 29, 10:12 PM (3 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14932534
Default Alt Text
D47832.diff (7 KB)
Attached To
Mode
D47832: inpcb: Remove bogus SO_REUSEPORT(_LB) checks in in_pcbbind()
Attached
Detach File
Event Timeline
Log In to Comment