From nobody Tue Dec 24 14:26:14 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4YHcdL4Hd9z5hFng; Tue, 24 Dec 2024 14:26:14 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4YHcdL1VL4z3xcy; Tue, 24 Dec 2024 14:26:14 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1735050374; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=g1GhDGrdaf1b2mUVrKuP/YcYDAwQ/oBtNBK442uIDHk=; b=ytB5t1PqTIdnFTyPUqlS+ysoLDjm/W74LnOnG7SJNqqPQRR3xoTfUmMopEe+xYlsyubTvL 6TiaJpR7Zb9iQIq+5otWz+UQL6Ckxq0WDlWHjGmo5GXYhAHwnayor0wpiR5XoeVwqIRQBk FwbX0Hi1vNZ352NDbQwHS6s5sIfMaac/8IMmmqubVrYrSw1DouHY712LNvaQsAl/gYKTjr 3/0RISpUUB3JEhq2QBbOjylGakqgSbopgDrJltP5tvR44UKPDo+DfxN+wlePTK1PAi1aTO T1n+ujWfvaWyfYaOH1nU6hVp0u2i9Zalitk0M36i16HDM/LFKKvtQMZPsrOTWw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1735050374; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=g1GhDGrdaf1b2mUVrKuP/YcYDAwQ/oBtNBK442uIDHk=; b=dDJHPSwnHS0daWNIeUItCHNTmcW60YARWF7q7XtfHQIO3pd9NKx9PWW8657zkDuKfkzCJF nexECN+ozY3BbJ3bK1UL61L1BSFV0WjV9lAkQ9P2imC58SylreSYIPJkO5PjAPfu3Alz/G lR1b8Ib0Tf3vgl0DiyQcm5jydhk2KJyS+bLz/z6YD3L8hQSqDqffFyTvv3PhwtsJLr+MPA w4rynnO9ffEZ9/mV/jpTLwll5fITg+Uyo6kKKkJCS15dAynbcQkiZ7pKi7LJzoyHHP9Vo2 Fmixw7zgMQjYUuusSfcEB/TNjxdabqS3BSCqGWcrgDagJ8mhvEC52fIAvtv8eA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1735050374; a=rsa-sha256; cv=none; b=nmNFDsPBLtY5ilFFvChoUevlma/gAZ5h7J9BdiIVmX/ayxlxJBr8T3G0XZGLz/H0BKl83i lWDfi1U00U4cIuGaQBNWf04L9mnK/JJkCOZAF8cbgRx8lPR6b4wAq6gxM+oRgXnvl7+dCh LpTRPCYpdIahOcAfW631AlV7somBQK2zfwnOjWp1MR/vu6Vhg8sF0GJhBPjwXCCgPvctR/ oudVdTkmco8og8+T4mooP1pzdW9oqDIQCzyHvEts0GYWA1dpFiVjJyH2oidHclFCcNTgK7 9rS/RKqbmO7cD74VBrrKfROWyaV6917UsXiJ0trpcOMfejsNIriE2Jmm1EC8JQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4YHcdL1385zj8c; Tue, 24 Dec 2024 14:26:14 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 4BOEQE16054198; Tue, 24 Dec 2024 14:26:14 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4BOEQExk054195; Tue, 24 Dec 2024 14:26:14 GMT (envelope-from git) Date: Tue, 24 Dec 2024 14:26:14 GMT Message-Id: <202412241426.4BOEQExk054195@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: 13ea23ee6eeb - stable/14 - pf: fix potential NULL dereference in SCTP multihome handling List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 13ea23ee6eeb7aa95e1a86793dac1fdb7304c181 Auto-Submitted: auto-generated The branch stable/14 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=13ea23ee6eeb7aa95e1a86793dac1fdb7304c181 commit 13ea23ee6eeb7aa95e1a86793dac1fdb7304c181 Author: Kristof Provost AuthorDate: 2024-12-03 18:27:49 +0000 Commit: Kristof Provost CommitDate: 2024-12-24 10:18:58 +0000 pf: fix potential NULL dereference in SCTP multihome handling When processing an SCTP ASCONF we re-run the rules processing to check if the new state should be allowed as well. We used to do so against the 'all' interface, to allow new connections to use any interface. This is problematic for two reasons, the first being it may unexpectedly bypass interface restrictions. The more important one is that it can trigger panics. If the ruleset contains a rule which filters on interface group we'd attempt to process the group list for the 'all' interface. As this isn't a real interface it doesn't have an associated struct ifnet, and we end up dereferencing a NULL pointer. Solve this by not overriding the interface, instead leaving the physical interface the SCTP ASCONF arrived on. This implies that we may end up binding to that interface (if if-bound), and thus denying traffic on other interfaces. Users can allow this anyway by setting 'state-policy floating' on the relevant SCTP rules. This arguably better reflects user intent as well. That is, we'll consider SCTP multihomed states to be floating if we're in floating mode, and if-bound if we're if-bound. Update the test cases to account for this, while adding a "pass on lo" (i.e. pass on an interface group") rule to provoke this issue. Add separate test cases for the floating and if-bound scenarios. Reported by: Franco Fichtner MFC after: 3 weeks Sponsored by: Orange Business Services (cherry picked from commit c22c9879845653abb365e468daaa621e3f8f767a) --- sys/netpfil/pf/pf.c | 7 +--- sys/netpfil/pf/pf_if.c | 3 ++ tests/sys/netpfil/pf/sctp.py | 77 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 80f3fdae1861..53c4fcb492da 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -6407,12 +6407,7 @@ again: j->pd.sctp_flags |= PFDESC_SCTP_ADD_IP; PF_RULES_RLOCK(); sm = NULL; - /* - * New connections need to be floating, because - * we cannot know what interfaces it will use. - * That's why we pass V_pfi_all rather than kif. - */ - ret = pf_test_rule(&r, &sm, V_pfi_all, + ret = pf_test_rule(&r, &sm, kif, j->m, off, &j->pd, &ra, &rs, NULL); PF_RULES_RUNLOCK(); SDT_PROBE4(pf, sctp, multihome, test, kif, r, j->m, ret); diff --git a/sys/netpfil/pf/pf_if.c b/sys/netpfil/pf/pf_if.c index 95f63ed663ca..650a7e4db799 100644 --- a/sys/netpfil/pf/pf_if.c +++ b/sys/netpfil/pf/pf_if.c @@ -426,6 +426,9 @@ pfi_kkif_match(struct pfi_kkif *rule_kif, struct pfi_kkif *packet_kif) NET_EPOCH_ASSERT(); + MPASS(packet_kif != NULL); + MPASS(packet_kif->pfik_ifp != NULL); + if (rule_kif == NULL || rule_kif == packet_kif) return (1); diff --git a/tests/sys/netpfil/pf/sctp.py b/tests/sys/netpfil/pf/sctp.py index 6042badffb64..38bb9f2dff74 100644 --- a/tests/sys/netpfil/pf/sctp.py +++ b/tests/sys/netpfil/pf/sctp.py @@ -268,7 +268,8 @@ class TestSCTP(VnetTestTemplate): ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "block proto sctp", - "pass inet proto sctp to 192.0.2.0/24"]) + "pass inet proto sctp to 192.0.2.0/24", + "pass on lo"]) # Sanity check, we can communicate with the primary address. client = SCTPClient("192.0.2.3", 1234) @@ -305,6 +306,7 @@ class TestSCTP(VnetTestTemplate): ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "block proto sctp", + "pass on lo", "pass inet proto sctp from 192.0.2.0/24"]) # Sanity check, we can communicate with the primary address. @@ -362,7 +364,7 @@ class TestSCTP(VnetTestTemplate): @pytest.mark.require_user("root") - def test_permutation(self): + def test_permutation_if_bound(self): # Test that we generate all permutations of src/dst addresses. # Assign two addresses to each end, and check for the expected states srv_vnet = self.vnet_map["vnet2"] @@ -374,6 +376,38 @@ class TestSCTP(VnetTestTemplate): ToolsHelper.pf_rules([ "set state-policy if-bound", "block proto sctp", + "pass on lo", + "pass inet proto sctp to 192.0.2.0/24"]) + + # Sanity check, we can communicate with the primary address. + client = SCTPClient("192.0.2.3", 1234) + client.send(b"hello", 0) + rcvd = self.wait_object(srv_vnet.pipe) + print(rcvd) + assert rcvd['ppid'] == 0 + assert rcvd['data'] == "hello" + + # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4 + states = ToolsHelper.get_output("/sbin/pfctl -ss") + print(states) + assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states) + assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states) + assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.3:1234", states) + assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.2:1234", states) + + @pytest.mark.require_user("root") + def test_permutation_floating(self): + # Test that we generate all permutations of src/dst addresses. + # Assign two addresses to each end, and check for the expected states + srv_vnet = self.vnet_map["vnet2"] + + ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.4/24" % ifname) + + ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.pf_rules([ + "block proto sctp", + "pass on lo", "pass inet proto sctp to 192.0.2.0/24"]) # Sanity check, we can communicate with the primary address. @@ -387,9 +421,9 @@ class TestSCTP(VnetTestTemplate): # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4 states = ToolsHelper.get_output("/sbin/pfctl -ss") print(states) - assert re.search(r".*sctp 192.0.2.1:.*192.0.2.3:1234", states) + assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states) assert re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states) - assert re.search(r".*sctp 192.0.2.4:.*192.0.2.3:1234", states) + assert re.search(r"all sctp 192.0.2.4:.*192.0.2.3:1234", states) assert re.search(r"all sctp 192.0.2.4:.*192.0.2.2:1234", states) class TestSCTPv6(VnetTestTemplate): @@ -417,6 +451,7 @@ class TestSCTPv6(VnetTestTemplate): ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "block proto sctp", + "pass on lo", "pass inet6 proto sctp to 2001:db8::0/64"]) # Sanity check, we can communicate with the primary address. @@ -454,6 +489,7 @@ class TestSCTPv6(VnetTestTemplate): ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "block proto sctp", + "pass on lo", "pass inet6 proto sctp from 2001:db8::/64"]) # Sanity check, we can communicate with the primary address. @@ -518,9 +554,42 @@ class TestSCTPv6(VnetTestTemplate): ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname) + ToolsHelper.print_output("/sbin/pfctl -e") + ToolsHelper.pf_rules([ + "set state-policy if-bound", + "block proto sctp", + "pass on lo", + "pass inet6 proto sctp to 2001:db8::0/64"]) + + # Sanity check, we can communicate with the primary address. + client = SCTPClient("2001:db8::3", 1234) + client.send(b"hello", 0) + rcvd = self.wait_object(srv_vnet.pipe) + print(rcvd) + assert rcvd['ppid'] == 0 + assert rcvd['data'] == "hello" + + # Check that we have a state for 2001:db8::3 and 2001:db8::2 to 2001:db8::1, but also to 2001:db8::4 + states = ToolsHelper.get_output("/sbin/pfctl -ss") + print(states) + assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states) + assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states) + assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::2\[1234\]", states) + assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::3\[1234\]", states) + + @pytest.mark.require_user("root") + def test_permutation_floating(self): + # Test that we generate all permutations of src/dst addresses. + # Assign two addresses to each end, and check for the expected states + srv_vnet = self.vnet_map["vnet2"] + + ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname) + ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "block proto sctp", + "pass on lo", "pass inet6 proto sctp to 2001:db8::0/64"]) # Sanity check, we can communicate with the primary address.