Skip to content

Commit

Permalink
pf: fix potential NULL dereference in SCTP multihome handling
Browse files Browse the repository at this point in the history
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 <[email protected]>
MFC after:	3 weeks
Sponsored by:	Orange Business Services

(cherry picked from commit c22c987)
  • Loading branch information
kprovost committed Dec 24, 2024
1 parent 3fa5d13 commit fd8dadb
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 10 deletions.
7 changes: 1 addition & 6 deletions sys/netpfil/pf/pf.c
Original file line number Diff line number Diff line change
Expand Up @@ -5804,12 +5804,7 @@ pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif,
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, pd->dir, V_pfi_all,
ret = pf_test_rule(&r, &sm, pd->dir, kif,
j->m, off, &j->pd, &ra, &rs, NULL);
PF_RULES_RUNLOCK();
SDT_PROBE4(pf, sctp, multihome, test, kif, r, j->m, ret);
Expand Down
3 changes: 3 additions & 0 deletions sys/netpfil/pf/pf_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,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);

Expand Down
77 changes: 73 additions & 4 deletions tests/sys/netpfil/pf/sctp.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ def test_multihome(self):
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)
Expand Down Expand Up @@ -305,6 +306,7 @@ def test_multihome_asconf(self):
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.
Expand Down Expand Up @@ -362,7 +364,7 @@ def test_multihome_asconf(self):


@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"]
Expand All @@ -374,6 +376,38 @@ def test_permutation(self):
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.
Expand All @@ -387,9 +421,9 @@ def test_permutation(self):
# 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):
Expand Down Expand Up @@ -417,6 +451,7 @@ def test_multihome(self):
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.
Expand Down Expand Up @@ -454,6 +489,7 @@ def test_multihome_asconf(self):
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.
Expand Down Expand Up @@ -518,9 +554,42 @@ def test_permutation(self):
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.
Expand Down

0 comments on commit fd8dadb

Please sign in to comment.