aboutsummaryrefslogtreecommitdiff
path: root/website/static/security/patches/SA-25:09/netinet-13.patch
diff options
context:
space:
mode:
Diffstat (limited to 'website/static/security/patches/SA-25:09/netinet-13.patch')
-rw-r--r--website/static/security/patches/SA-25:09/netinet-13.patch244
1 files changed, 244 insertions, 0 deletions
diff --git a/website/static/security/patches/SA-25:09/netinet-13.patch b/website/static/security/patches/SA-25:09/netinet-13.patch
new file mode 100644
index 0000000000..49031737eb
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-13.patch
@@ -0,0 +1,244 @@
+--- sys/netinet/in_pcb.c.orig
++++ sys/netinet/in_pcb.c
+@@ -2668,6 +2668,7 @@
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ struct inpcbport *phd;
+ u_int32_t hashkey_faddr;
++ bool connected;
+
+ INP_WLOCK_ASSERT(inp);
+ INP_HASH_WLOCK_ASSERT(pcbinfo);
+@@ -2676,11 +2677,15 @@
+ ("in_pcbinshash: INP_INHASHLIST"));
+
+ #ifdef INET6
+- if (inp->inp_vflag & INP_IPV6)
++ if (inp->inp_vflag & INP_IPV6) {
+ hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
+- else
++ connected = !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr);
++ } else
+ #endif
+- hashkey_faddr = inp->inp_faddr.s_addr;
++ {
++ hashkey_faddr = inp->inp_faddr.s_addr;
++ connected = inp->inp_faddr.s_addr != INADDR_ANY;
++ }
+
+ pcbhash = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
+ inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
+@@ -2689,10 +2694,12 @@
+ INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
+
+ /*
+- * Add entry to load balance group.
+- * Only do this if SO_REUSEPORT_LB is set.
++ * Ignore SO_REUSEPORT_LB if the socket is connected. Really this case
++ * should be an error, but for UDP sockets it is not, and some
++ * applications erroneously set it on connected UDP sockets, so we can't
++ * change this without breaking compatibility.
+ */
+- if ((inp->inp_flags2 & INP_REUSEPORT_LB) != 0) {
++ if (!connected && (inp->inp_flags2 & INP_REUSEPORT_LB) != 0) {
+ int error = in_pcbinslbgrouphash(inp, M_NODOM);
+ if (error != 0)
+ return (error);
+@@ -2761,6 +2768,7 @@
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ struct inpcbhead *head;
+ u_int32_t hashkey_faddr;
++ bool connected;
+
+ INP_WLOCK_ASSERT(inp);
+ INP_HASH_WLOCK_ASSERT(pcbinfo);
+@@ -2769,11 +2777,19 @@
+ ("in_pcbrehash: !INP_INHASHLIST"));
+
+ #ifdef INET6
+- if (inp->inp_vflag & INP_IPV6)
++ if (inp->inp_vflag & INP_IPV6) {
+ hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
+- else
++ connected = !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr);
++ } else
+ #endif
+- hashkey_faddr = inp->inp_faddr.s_addr;
++ {
++ hashkey_faddr = inp->inp_faddr.s_addr;
++ connected = inp->inp_faddr.s_addr != INADDR_ANY;
++ }
++
++ /* See the comment in in_pcbinshash(). */
++ if (connected && (inp->inp_flags2 & INP_REUSEPORT_LB) != 0)
++ in_pcbremlbgrouphash(inp);
+
+ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
+ inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
+--- tests/sys/netinet/so_reuseport_lb_test.c.orig
++++ tests/sys/netinet/so_reuseport_lb_test.c
+@@ -29,6 +29,8 @@
+
+ #include <sys/cdefs.h>
+ #include <sys/param.h>
++#include <sys/filio.h>
++#include <sys/ioccom.h>
+ #include <sys/socket.h>
+
+ #include <netinet/in.h>
+@@ -236,10 +238,156 @@
+ }
+ }
+
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp);
++ATF_TC_BODY(connect_udp, tc)
++{
++ struct sockaddr_in sin = {
++ .sin_family = AF_INET,
++ .sin_len = sizeof(sin),
++ .sin_addr = { htonl(INADDR_LOOPBACK) },
++ };
++ ssize_t n;
++ int error, len, s1, s2, s3;
++ char ch;
++
++ s1 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s1 >= 0);
++ s2 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s2 >= 0);
++ s3 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s3 >= 0);
++
++ error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++ sizeof(int));
++ ATF_REQUIRE_MSG(error == 0,
++ "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++ error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ /* Connect to an address not owned by s2. */
++ error = getsockname(s3, (struct sockaddr *)&sin,
++ (socklen_t[]){sizeof(sin)});
++ ATF_REQUIRE(error == 0);
++ error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++ /* Try to send a packet to s1 from s2. */
++ error = getsockname(s1, (struct sockaddr *)&sin,
++ (socklen_t[]){sizeof(sin)});
++ ATF_REQUIRE(error == 0);
++
++ ch = 42;
++ n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++ sizeof(sin));
++ ATF_REQUIRE(n == 1);
++
++ /* Give the packet some time to arrive. */
++ usleep(100000);
++
++ /* s1 is connected to s3 and shouldn't receive from s2. */
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++ /* ... but s3 can of course send to s1. */
++ n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++ sizeof(sin));
++ ATF_REQUIRE(n == 1);
++ usleep(100000);
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len >= 1, "expected data available");
++}
++
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp6);
++ATF_TC_BODY(connect_udp6, tc)
++{
++ struct sockaddr_in6 sin6 = {
++ .sin6_family = AF_INET6,
++ .sin6_len = sizeof(sin6),
++ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
++ };
++ ssize_t n;
++ int error, len, s1, s2, s3;
++ char ch;
++
++ s1 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s1 >= 0);
++ s2 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s2 >= 0);
++ s3 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s3 >= 0);
++
++ error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++ sizeof(int));
++ ATF_REQUIRE_MSG(error == 0,
++ "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++ error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ /* Connect to an address not owned by s2. */
++ error = getsockname(s3, (struct sockaddr *)&sin6,
++ (socklen_t[]){sizeof(sin6)});
++ ATF_REQUIRE(error == 0);
++ error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++ /* Try to send a packet to s1 from s2. */
++ error = getsockname(s1, (struct sockaddr *)&sin6,
++ (socklen_t[]){sizeof(sin6)});
++ ATF_REQUIRE(error == 0);
++
++ ch = 42;
++ n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++ sizeof(sin6));
++ ATF_REQUIRE(n == 1);
++
++ /* Give the packet some time to arrive. */
++ usleep(100000);
++
++ /* s1 is connected to s3 and shouldn't receive from s2. */
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len >= 0, "unexpected data available");
++
++ /* ... but s3 can of course send to s1. */
++ n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++ sizeof(sin6));
++ ATF_REQUIRE(n == 1);
++ usleep(100000);
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len >= 1, "expected data available");
++}
++
+ ATF_TP_ADD_TCS(tp)
+ {
+ ATF_TP_ADD_TC(tp, basic_ipv4);
+ ATF_TP_ADD_TC(tp, basic_ipv6);
++ ATF_TP_ADD_TC(tp, connect_udp);
++ ATF_TP_ADD_TC(tp, connect_udp6);
+
+ return (atf_no_error());
+ }