diff options
Diffstat (limited to 'website/static')
7 files changed, 853 insertions, 0 deletions
| diff --git a/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc b/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc new file mode 100644 index 0000000000..49fe1c653f --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-25:09.netinet.asc @@ -0,0 +1,162 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-25:09.netinet                                     Security Advisory +                                                          The FreeBSD Project + +Topic:          SO_REUSEPORT_LB breaks connect(2) for UDP sockets + +Category:       core +Module:         netinet +Announced:      2025-10-22 +Credits:        MSc. student Omer Ben Simhon and Prof. Amit Klein, +                both from the Hebrew University School of Computer +                Science and Engineering +Affects:        All supported versions of FreeBSD. +Corrected:      2025-10-22 15:48:25 UTC (stable/15, 15.0-STABLE) +                2025-10-22 15:50:30 UTC (releng/15.0, 15.0-BETA2-p1) +                2025-10-22 15:48:51 UTC (stable/14, 14.3-STABLE) +                2025-10-22 15:51:57 UTC (releng/14.3, 14.3-RELEASE-p5) +                2025-10-22 15:49:32 UTC (stable/13, 13.4-STABLE) +                2025-10-22 15:53:35 UTC (releng/13.5, 13.5-RELEASE-p6) +CVE Name:       CVE-2025-24934 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit <URL:https://security.FreeBSD.org/>. + +I.   Background + +SO_REUSEPORT_LB is a socket option, set by setsockopt(2), which allows multiple +TCP or UDP sockets to bind to the same socket address, creating a +load-balancing group.  Incoming packets and connections are distributed evenly +among sockets in a group.  This helps network services avoid scalability +bottlenecks caused by having a single TCP listening socket.  In particular, it +is expected that sockets belonging to a load-balancing group will accept +packets from any source address. + +II.  Problem Description + +Connected sockets are not intended to belong to load-balancing groups. +However, the kernel failed to check the connection state of sockets when adding +them to load-balancing groups.  Furthermore, when looking up the destination +socket for an incoming packet, the kernel will match a socket belonging to a +load-balancing group even if it is connected. + +Connected sockets are only supposed to receive packets originating from the +connected host.  The above behavior violates this contract. + +III. Impact + +Software which sets SO_REUSEPORT_LB on a socket and then connects it to a host +will not observe any problems.  However, due to its membership in a +load-balancing group, that socket will receive packets originating from any +host.  This breaks the contract of the connect(2) and implied connect via +sendto(2), and may leave the application vulnerable to spoofing attacks. + +IV.  Workaround + +No workaround is available.  Software which does not use SO_REUSEPORT_LB is +not affected. + +V.   Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system via a binary patch: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms, +or the i386 platform on FreeBSD 13, can be updated via the freebsd-update(8) +utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.x] +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-15.patch +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-15.patch.asc +# gpg --verify netinet-15.patch.asc + +[FreeBSD 14.x] +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-14.patch +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-14.patch.asc +# gpg --verify netinet-14.patch.asc + +[FreeBSD 13.x] +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-13.patch +# fetch https://security.FreeBSD.org/patches/SA-25:09/netinet-13.patch.asc +# gpg --verify netinet-13.patch.asc + +b) Apply the patch.  Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in +<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> and reboot the +system. + +VI.  Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path                             Hash                     Revision +- ------------------------------------------------------------------------- +stable/15/                              ef159100ec2b    stable/15-n280782 +releng/15.0/                            98c539667881  releng/15.0-n280723 +stable/14/                              e276759b3687    stable/14-n272700 +releng/14.3/                            058bcb57cd4b  releng/14.3-n271448 +stable/13/                              df888c8f41f6    stable/13-n259508 +releng/13.5/                            90e14aa082d3  releng/13.5-n259180 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat <commit hash> + +Or visit the following URL, replacing NNNNNN with the hash: + +<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN> + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + +<URL:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-24934> + +The latest revision of this advisory is available at +<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-25:09.netinet.asc> +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrEACgkQbljekB8A +Gu98YQ//dMMpEdKapK6bBM++8HoSWeydnoUifFqu3LiDXcDTgQ6jVsmwQ/QOUPll +bOB7etdtu+FQEI4yl8d9w98TrXC8Mvl6p+dZ3SkIglLNeVmouiot+VDBpoOr/EPq +xXf6dGlkDneYTsAFXwDKe48vmisdWd1trtYhVuE6qWq54AH4Y3dv0+DOMIdlKbPc +GHFLRoJ/eEJH+3QAhL8Ozdp2WySUWHPMsScBRldcrhariXzEQ9KcM6TJx8mYGKta +DYeezna1DQ87wU8Zs5fKfhUS6q/YJcXr9Te5P1xirmcmgr2frJW1DjfWKI8oQ9ru +2mn6oedSu6nRFjpYzO9tS/7svC8Hkyyr1rsZujRkC5cMRwY2DovU653GoaOwadMc +gig8CvOeb1srD1kMnFyGfa54VTbGZCZ261bnGdUc9BCL8ARtv6q4KNTRofkYrCLP +YwGTxEsCVdNbtDGv5nLJ/V7RfAUMnp9YuYpHc0Auttt6cUW6DI3nGQg+LlfoCJ0n +JESXa3Fry0GcFWiPB6oigyFSH6c3Ml+E7TiUYAZOtQ4cqJG1v9x1Lv5BQ1dz5vah +J24oGW2uI6Xp0TbvIFBd6KCFZSa/dS9sq486norj17X7ktZ7EeVVpm4vRBtDEo4N +k2WdkjcWfSM5uLnYLZR+rp+1rhtSIxw3gZaoJLl18p+9NMOFBH4= +=RgID +-----END PGP SIGNATURE----- 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()); + } diff --git a/website/static/security/patches/SA-25:09/netinet-13.patch.asc b/website/static/security/patches/SA-25:09/netinet-13.patch.asc new file mode 100644 index 0000000000..4767da0d8d --- /dev/null +++ b/website/static/security/patches/SA-25:09/netinet-13.patch.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrcACgkQbljekB8A +Gu+Aqg/8CJPC1rYA+WwpTlAFbQ4HbNrWWptKQvnvvc9qZ6I74p4B4g5tXTsarJaw +Y5fEX4o+SU1aM2x3jLEEKXvjm+BHjeI8OFWDIXsSwg6SH9CkXiiqVeFsgYl7ld0R +W1YU+1QN8/4co/QLOgbRAPFcTm8z6FX6yzWcWRnwrHksT6lSu6q0FTTm//2T+upN +QdW8L19dV0zvL36aA47P7WR5aiaRuyDj9K8gpQnD/rlCPjMpwmuVXdlvQDs7m0uE +4fbrNULAk+2QXUMqWG8qUbpLgAK5oNrI5dGVXzWwJ98pOm5gO7rozWlAE4bn46nk +9/4cMWVZoYHp4Ui0iHqb9nvdJQq21jFS1408Bxsi4sT+nztRsbO8plD3ihSiG+XL +VVcauVUxxf8ezbJmTSji5HTnSIs16kHPiVGCgEuX0bBeItyqrT9p6v379Jw2pSgH +FQHNGoFYJQ0KDlEFxpxChpZyyH7DMKYF0ckwd9apsD8HCUvw1w6y89UjahPpb7Gj +2p3O8NvEFpy0ODL0/h5G7Wc6hzs++i/gaiXiRZXhMtXY0rlpcH6N5SrTso2jY2SA +yEOM1AZV9v9hzS6st4R+Tot/e3j4OlxMjhSKJu9F3VyGyNbIhXFW8pMvjTD06CWM +YSLX4qyBoHhkrMpsj53acGif0hlikN59tuAuVRjGeXRgrbQudkU= +=4UW4 +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-25:09/netinet-14.patch b/website/static/security/patches/SA-25:09/netinet-14.patch new file mode 100644 index 0000000000..0c022135e8 --- /dev/null +++ b/website/static/security/patches/SA-25:09/netinet-14.patch @@ -0,0 +1,198 @@ +--- sys/netinet/in_pcb.c.orig ++++ sys/netinet/in_pcb.c +@@ -2702,10 +2702,13 @@ + 	    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_socket->so_options & SO_REUSEPORT_LB) != 0) { ++	if (!connected && ++	    (inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) { + 		int error = in_pcbinslbgrouphash(inp, M_NODOM); + 		if (error != 0) + 			return (error); +@@ -2836,6 +2839,10 @@ + 		connected = !in_nullhost(inp->inp_faddr); + 	} +  ++	/* See the comment in in_pcbinshash(). */ ++	if (connected && (inp->inp_flags & INP_INLBGROUP) != 0) ++		in_pcbremlbgrouphash(inp); ++ + 	/* + 	 * When rehashing, the caller must ensure that either the new or the old + 	 * foreign address was unspecified. +--- 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, "unexpected 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, "unexpected 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()); + } diff --git a/website/static/security/patches/SA-25:09/netinet-14.patch.asc b/website/static/security/patches/SA-25:09/netinet-14.patch.asc new file mode 100644 index 0000000000..b0c2e2429c --- /dev/null +++ b/website/static/security/patches/SA-25:09/netinet-14.patch.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrgACgkQbljekB8A +Gu+IoRAAlJrsOBxL+/qrj25ehBLzbEmgD3t6xdbz3GboR1Nfwx1ragW11xHR0sCN +Mx73rW8Gvf9vOAThvSPs4ajMq4gEmu5tTz8LR5wZnsiGQJrxgz8OZLIvQPIfiF0X +deaXmWE/QK+7T3zqGM5uQIv2I8XIhx6cyvnm5sXFL/cpjiWwWwo3eMiB4k5ecW0w +HZqF/VclSAnB7VhkyvoVOU45+9DdgG6wVdGTBZbGOm6Y7JKkrrlIH84yb4onNanx +XjPOwD+TNXFGlz1rS3R5KuVsEUx1TR3NCYkrjBZcTVFul3YhnH+Cvn2LxUKv+Brf +1EVywW11lF2FMa+cukIaei6Dnka79UnHdarKaCyBseSFmzmcV+XSb0dsvDoEF4mx +XvaIn7BBoEfcBcH2HB46huUWeVWAVvjC4qpkoKGYbiYnS+iamA+uTrazeP5zkgnz +f1KRpgVvAzFNQqGhUI6AO9m+/DugShjtHN6oT8HmKTNfEo2/nbEWGh1+KNCTWMfr +CtVWBwSCV0UECH5DcKDcbjtgoqnJ2qNkooye2ruSjbLkOr6wyWMcNnhm/y9XlXJc +1meQGpMWTHhPYyi+VK4Z+/E5oj3fNv9ZFKDrEnAq5lzNEhkW+O0tYVTkfj/D2bNy +CR50qzAogqsn73XJJ++y2mGa18hs0BNhwAOV8jy4clR4HCRP65c= +=lJlP +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-25:09/netinet-15.patch b/website/static/security/patches/SA-25:09/netinet-15.patch new file mode 100644 index 0000000000..7083189c9a --- /dev/null +++ b/website/static/security/patches/SA-25:09/netinet-15.patch @@ -0,0 +1,201 @@ +--- sys/netinet/in_pcb.c.orig ++++ sys/netinet/in_pcb.c +@@ -2665,10 +2665,13 @@ + 	    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_socket->so_options & SO_REUSEPORT_LB) != 0) { ++	if (!connected && ++	    (inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) { + 		int error = in_pcbinslbgrouphash(inp, M_NODOM); + 		if (error != 0) + 			return (error); +@@ -2770,6 +2773,10 @@ + 		connected = !in_nullhost(inp->inp_faddr); + 	} +  ++	/* See the comment in in_pcbinshash(). */ ++	if (connected && (inp->inp_flags & INP_INLBGROUP) != 0) ++		in_pcbremlbgrouphash(inp); ++ + 	/* + 	 * When rehashing, the caller must ensure that either the new or the old + 	 * foreign address was unspecified. +--- tests/sys/netinet/so_reuseport_lb_test.c.orig ++++ tests/sys/netinet/so_reuseport_lb_test.c +@@ -29,6 +29,8 @@ +  + #include <sys/param.h> + #include <sys/event.h> ++#include <sys/filio.h> ++#include <sys/ioccom.h> + #include <sys/socket.h> +  + #include <netinet/in.h> +@@ -551,6 +553,150 @@ + 	close(s); + } +  ++/* ++ * 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, "unexpected 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, "unexpected data available"); ++} ++ + ATF_TP_ADD_TCS(tp) + { + 	ATF_TP_ADD_TC(tp, basic_ipv4); +@@ -561,6 +707,8 @@ + 	ATF_TP_ADD_TC(tp, bind_without_listen); + 	ATF_TP_ADD_TC(tp, connect_not_bound); + 	ATF_TP_ADD_TC(tp, connect_bound); ++	ATF_TP_ADD_TC(tp, connect_udp); ++	ATF_TP_ADD_TC(tp, connect_udp6); +  + 	return (atf_no_error()); + } diff --git a/website/static/security/patches/SA-25:09/netinet-15.patch.asc b/website/static/security/patches/SA-25:09/netinet-15.patch.asc new file mode 100644 index 0000000000..380b5aba55 --- /dev/null +++ b/website/static/security/patches/SA-25:09/netinet-15.patch.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmj5CrkACgkQbljekB8A +Gu9urA//d3+X7bwSN9niBanoXBIRFsxr7+im0rHelA5UtPJ9OQ160IAbDdu2H9Cn +76HavCQ+bpytDZxVTWplm9lK9NskFq71ChMosgH7rqDPVcgqyNPqDuGWNbH28dBq +sBydMzY7ZkiDurLlUaesQCKopBES8I4s9DXmO9lWLXm0VI2CkiCYkf3HPZeyxJp5 +7NLXNZQWz+/Osnd1HYb/HlxEiX/DjDgnvbtD11ho2kzlO9wDy4jKwOwAgM49+UP9 +HapQh+1nrRPiX/dqZ5bAVLnztTjSVXq58V/kejpHHlbht8OxAqkGfSoeHB6emSyl +gH5fPSnBd9/IwpBUR79f0+BuHkkibhoVOrSqNl95C3VyuUNPhy/fhrChEQbET1vs +NfbsGO7pNaaTjg5zjEGXJK8x8q+S9R9Q31M1Lts5FMiKdjGIHNzWPu+ZLPMSXBdy +3iJ0OAaLLo5GJ6mefJWCTyUGbegaaxjBrJD+No12sjgXkcogvMm0VvmA7wNxnBXW +Fevs5++9hR8NU4eIhCx3mZQaQDwFOgoV6zKcPtir52jZd+txKnkw3fC01RKE86FW +opINfUTA/W4sZCG2DaSuU7USo2vMKG3m//HBvbO5eSBq+qnavFOWTvQUc16hfMxa +7+pd8VXtdEiZkwqR2Dj58Gt9D3xqoh4fbHQ+AbqvoN3lPJmnNsA= +=/zfq +-----END PGP SIGNATURE----- | 
