Skip to content

Commit 376e4d6

Browse files
committed
Fix a security issue with pf with SCTP packet validations.
Obtained: FreeBSD
1 parent 85ab75c commit 376e4d6

2 files changed

Lines changed: 103 additions & 5 deletions

File tree

sys/netpfil/pf/pf.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5929,7 +5929,7 @@ pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif,
59295929

59305930
static int
59315931
pf_multihome_scan(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
5932-
struct pfi_kkif *kif, int op)
5932+
struct pfi_kkif *kif, int op, bool asconf)
59335933
{
59345934
int off = 0;
59355935
struct pf_sctp_multihome_job *job;
@@ -6023,13 +6023,16 @@ pf_multihome_scan(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
60236023
int ret;
60246024
struct sctp_asconf_paramhdr ah;
60256025

6026+
if (asconf)
6027+
return (PF_DROP);
6028+
60266029
if (!pf_pull_hdr(m, start + off, &ah, sizeof(ah),
60276030
NULL, NULL, pd->af))
60286031
return (PF_DROP);
60296032

60306033
ret = pf_multihome_scan(m, start + off + sizeof(ah),
60316034
ntohs(ah.ph.param_length) - sizeof(ah), pd, kif,
6032-
SCTP_ADD_IP_ADDRESS);
6035+
SCTP_ADD_IP_ADDRESS, true);
60336036
if (ret != PF_PASS)
60346037
return (ret);
60356038
break;
@@ -6038,12 +6041,15 @@ pf_multihome_scan(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
60386041
int ret;
60396042
struct sctp_asconf_paramhdr ah;
60406043

6044+
if (asconf)
6045+
return (PF_DROP);
6046+
60416047
if (!pf_pull_hdr(m, start + off, &ah, sizeof(ah),
60426048
NULL, NULL, pd->af))
60436049
return (PF_DROP);
60446050
ret = pf_multihome_scan(m, start + off + sizeof(ah),
60456051
ntohs(ah.ph.param_length) - sizeof(ah), pd, kif,
6046-
SCTP_DEL_IP_ADDRESS);
6052+
SCTP_DEL_IP_ADDRESS, true);
60476053
if (ret != PF_PASS)
60486054
return (ret);
60496055
break;
@@ -6065,7 +6071,8 @@ pf_multihome_scan_init(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
60656071
start += sizeof(struct sctp_init_chunk);
60666072
len -= sizeof(struct sctp_init_chunk);
60676073

6068-
return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS));
6074+
return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS,
6075+
false));
60696076
}
60706077

60716078
int
@@ -6075,7 +6082,8 @@ pf_multihome_scan_asconf(struct mbuf *m, int start, int len,
60756082
start += sizeof(struct sctp_asconf_chunk);
60766083
len -= sizeof(struct sctp_asconf_chunk);
60776084

6078-
return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS));
6085+
return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS,
6086+
false));
60796087
}
60806088

60816089
int

tests/sys/netpfil/pf/sctp.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,96 @@ def test_initiate_tag_check(self):
493493
assert r.getlayer(sp.SCTPChunkInitAck)
494494
assert r.getlayer(sp.SCTP).tag == 42
495495

496+
class TestSCTP_SRV(VnetTestTemplate):
497+
REQUIRED_MODULES = ["sctp", "pf"]
498+
TOPOLOGY = {
499+
"vnet1": {"ifaces": ["if1"]},
500+
"vnet2": {"ifaces": ["if1"]},
501+
"if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
502+
}
503+
504+
def vnet2_handler(self, vnet):
505+
ToolsHelper.print_output("/sbin/pfctl -e")
506+
ToolsHelper.pf_rules([
507+
"set state-policy if-bound",
508+
"pass inet proto sctp",
509+
"pass on lo"])
510+
511+
# Start an SCTP server process, pipe the ppid + data back to the other vnet?
512+
srv = SCTPServer(socket.AF_INET, port=1234)
513+
while True:
514+
srv.accept(vnet)
515+
516+
@pytest.mark.require_user("root")
517+
@pytest.mark.require_progs(["scapy"])
518+
def test_initiate_tag_check(self):
519+
# Ensure we don't send ABORTs in response to the other end's INIT_ACK
520+
# That'd interfere with our test.
521+
ToolsHelper.print_output("/sbin/sysctl net.inet.sctp.blackhole=2")
522+
523+
import scapy.all as sp
524+
525+
packet = sp.IP(src="192.0.2.1", dst="192.0.2.2") \
526+
/ sp.SCTP(sport=1234, dport=1234) \
527+
/ sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, a_rwnd=1500)
528+
packet.show()
529+
530+
r = sp.sr1(packet, timeout=3)
531+
assert r
532+
r.show()
533+
assert r.getlayer(sp.SCTP)
534+
assert r.getlayer(sp.SCTPChunkInitAck)
535+
assert r.getlayer(sp.SCTP).tag == 1
536+
537+
# Send another INIT with the same initiate tag, expect another init ack
538+
packet = sp.IP(src="192.0.2.1", dst="192.0.2.2") \
539+
/ sp.SCTP(sport=1234, dport=1234) \
540+
/ sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, a_rwnd=1500)
541+
packet.show()
542+
543+
r = sp.sr1(packet, timeout=3)
544+
assert r
545+
r.show()
546+
assert r.getlayer(sp.SCTP)
547+
assert r.getlayer(sp.SCTPChunkInitAck)
548+
assert r.getlayer(sp.SCTP).tag == 1
549+
550+
# Send an INIT with a different initiate tag, expect another init ack
551+
packet = sp.IP(src="192.0.2.1", dst="192.0.2.2") \
552+
/ sp.SCTP(sport=1234, dport=1234) \
553+
/ sp.SCTPChunkInit(init_tag=42, n_in_streams=1, n_out_streams=1, a_rwnd=1500)
554+
packet.show()
555+
556+
r = sp.sr1(packet, timeout=3)
557+
assert r
558+
r.show()
559+
assert r.getlayer(sp.SCTP)
560+
assert r.getlayer(sp.SCTPChunkInitAck)
561+
assert r.getlayer(sp.SCTP).tag == 42
562+
563+
@pytest.mark.require_user("root")
564+
@pytest.mark.require_progs(["scapy"])
565+
def test_too_many_add_ip(self):
566+
import scapy.all as sp
567+
DEPTH=90
568+
params=[]
569+
for i in range(0, DEPTH):
570+
ch = sp.SCTPChunkParamAddIPAddr(len=(DEPTH - i) * 8)
571+
params.append(ch)
572+
packet = sp.IP(src="192.0.2.1", dst="192.0.2.2") \
573+
/ sp.SCTP(sport=4321, dport=1234) \
574+
/ sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, a_rwnd=1500,
575+
params=params)
576+
packet.show()
577+
sp.hexdump(packet)
578+
print("len %d" % len(packet))
579+
580+
r = sp.sr1(packet, timeout=3)
581+
# We should not get a reply to this
582+
if r:
583+
r.show()
584+
assert not r
585+
496586
class TestSCTPv6(VnetTestTemplate):
497587
REQUIRED_MODULES = ["sctp", "pf"]
498588
TOPOLOGY = {

0 commit comments

Comments
 (0)