git: 00cd3145d4c6 - stable/13 - pf: improve SCTP state validation

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Mon, 02 Oct 2023 09:33:25 UTC
The branch stable/13 has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=00cd3145d4c650446ae54007ec50745244942ef2

commit 00cd3145d4c650446ae54007ec50745244942ef2
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-09-01 11:33:56 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-10-02 08:51:43 +0000

    pf: improve SCTP state validation
    
    Only create new states for INIT chunks, or when we're creating a
    secondary state for a multihomed association.
    
    Store and verify verification tag.
    
    MFC after:      3 weeks
    Sponsored by:   Orange Business Services
    
    (cherry picked from commit 51a78dd2764beabfd19a58b8a8b04387a547f02e)
---
 sys/net/pfvar.h          |  8 +++++++-
 sys/netpfil/pf/pf.c      | 25 +++++++++++++++----------
 sys/netpfil/pf/pf_norm.c | 18 ++++++++++++++++++
 3 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 07a4140e450f..3cb093ba8b02 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -700,7 +700,10 @@ struct pf_state_scrub {
 #define PFSS_DATA_NOTS	0x0080		/* no timestamp on data packets	*/
 	u_int8_t	pfss_ttl;	/* stashed TTL			*/
 	u_int8_t	pad;
-	u_int32_t	pfss_ts_mod;	/* timestamp modulation		*/
+	union {
+		u_int32_t	pfss_ts_mod;	/* timestamp modulation		*/
+		u_int32_t	pfss_v_tag;	/* SCTP verification tag	*/
+	};
 };
 
 struct pf_state_host {
@@ -1332,6 +1335,7 @@ struct pf_pdesc {
 #define PFDESC_SCTP_DATA	0x0040
 #define PFDESC_SCTP_ASCONF	0x0080
 #define PFDESC_SCTP_OTHER	0x0100
+#define PFDESC_SCTP_ADD_IP	0x0200
 	u_int16_t	 sctp_flags;
 	u_int32_t	 sctp_initiate_tag;
 
@@ -2044,6 +2048,8 @@ int	pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *,
 int	pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *,
 	    u_short *, struct tcphdr *, struct pf_kstate *,
 	    struct pf_state_peer *, struct pf_state_peer *, int *);
+int	pf_normalize_sctp_init(struct mbuf *, int, struct pf_pdesc *,
+	    struct pf_state_peer *, struct pf_state_peer *);
 int	pf_normalize_sctp(int, struct pfi_kkif *, struct mbuf *, int,
 	    int, void *, struct pf_pdesc *);
 u_int32_t
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index cb4ab2da4633..551bfd01732f 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -4340,11 +4340,7 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 		if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m,
 		    off, pd, th, &s->src, &s->dst)) {
 			REASON_SET(&reason, PFRES_MEMORY);
-			pf_src_tree_remove_state(s);
-			s->timeout = PFTM_UNLINKED;
-			STATE_DEC_COUNTERS(s);
-			pf_free_state(s);
-			return (PF_DROP);
+			goto drop;
 		}
 		if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub &&
 		    pf_normalize_tcp_stateful(m, off, pd, &reason, th, s,
@@ -4353,12 +4349,13 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 			DPFPRINTF(PF_DEBUG_URGENT,
 			    ("pf_normalize_tcp_stateful failed on first "
 			     "pkt\n"));
-			pf_src_tree_remove_state(s);
-			s->timeout = PFTM_UNLINKED;
-			STATE_DEC_COUNTERS(s);
-			pf_free_state(s);
-			return (PF_DROP);
+			goto drop;
 		}
+	} else if (pd->proto == IPPROTO_SCTP) {
+		if (pf_normalize_sctp_init(m, off, pd, &s->src, &s->dst))
+			goto drop;
+		if (! (pd->sctp_flags & (PFDESC_SCTP_INIT | PFDESC_SCTP_ADD_IP)))
+			goto drop;
 	}
 	s->direction = pd->dir;
 
@@ -5312,6 +5309,13 @@ pf_test_state_sctp(struct pf_kstate **state, struct pfi_kkif *kif,
 		}
 	}
 
+	if (src->scrub != NULL) {
+		if (src->scrub->pfss_v_tag == 0) {
+			src->scrub->pfss_v_tag = pd->hdr.sctp.v_tag;
+		} else  if (src->scrub->pfss_v_tag != pd->hdr.sctp.v_tag)
+			return (PF_DROP);
+	}
+
 	(*state)->expire = time_uptime;
 
 	/* translate source/destination address, if necessary */
@@ -5352,6 +5356,7 @@ pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif,
 
 	TAILQ_FOREACH_SAFE(j, &pd->sctp_multihome_jobs, next, tmp) {
 		PF_RULES_RLOCK();
+		j->pd.sctp_flags |= PFDESC_SCTP_ADD_IP;
 		action = pf_test_rule(&r, &sm, pd->dir, kif,
 		    j->m, off, &j->pd, &ra, &rs, NULL);
 		PF_RULES_RUNLOCK();
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index ec3f63c9f262..a8ef4dc346b0 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -1529,6 +1529,7 @@ pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd,
 void
 pf_normalize_tcp_cleanup(struct pf_kstate *state)
 {
+	/* XXX Note: this also cleans up SCTP. */
 	if (state->src.scrub)
 		uma_zfree(V_pf_state_scrub_z, state->src.scrub);
 	if (state->dst.scrub)
@@ -1537,6 +1538,23 @@ pf_normalize_tcp_cleanup(struct pf_kstate *state)
 	/* Someday... flush the TCP segment reassembly descriptors. */
 }
 
+int
+pf_normalize_sctp_init(struct mbuf *m, int off, struct pf_pdesc *pd,
+	    struct pf_state_peer *src, struct pf_state_peer *dst)
+{
+	src->scrub = uma_zalloc(V_pf_state_scrub_z, M_ZERO | M_NOWAIT);
+	if (src->scrub == NULL)
+		return (1);
+
+	dst->scrub = uma_zalloc(V_pf_state_scrub_z, M_ZERO | M_NOWAIT);
+	if (dst->scrub == NULL) {
+		uma_zfree(V_pf_state_scrub_z, src);
+		return (1);
+	}
+
+	return (0);
+}
+
 int
 pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd,
     u_short *reason, struct tcphdr *th, struct pf_kstate *state,