Re: [PATCH nf v2 1/1] netfilter: validate L4 headers after userspace packet writes

Pablo Neira Ayuso <[email protected]> Thu, 30 Jul 2026 13:34:51 +0200
Newsgroups gmane.comp.security.firewalls.netfilter.devel,gmane.linux.network
Message-ID <ams22yfvR5oNSxIN@chamomile>
On Thu, Jul 30, 2026 at 01:06:27PM +0200, Florian Westphal wrote:
> Pablo Neira Ayuso <[email protected]> wrote:
> > > +	const struct nf_conn *ct;
> > > +
> > > +	ct = nf_ct_get(e->skb, &ctinfo);
> > > +	if (ct && !nf_ct_is_template(ct) && nf_ct_protonum(ct) != proto)
> > 
> > I think it should be easier to disallow protocol number mangling in
> > the IP header (layer 3 restrictions), if not done already.
> 
> How?  nfqueue is whole-replace, not a delta.
> Or do you mean checking ip_hdr(skb) vs. the protocol field in userspace
> provided buffer?
> 
> I would prefer this solution (i.e. check ct protocol), it still allows theoretical nfqueue based
> tunneling header insertion, if done in prerouting before conntrack.

OK

> > > +	switch (proto) {
> > > +	case IPPROTO_TCP: {
> > > +		const struct tcphdr *th = (const struct tcphdr *)data;
> > 
> > This needs to use skb_header_pointer() here, you cannot assume the tcp
> > header is in a linear area.
> 
> data is a linear buffer coming from userspace
> (nla_data(nfqa[NFQA_PAYLOAD]).

Indeed.

> > > +	case IPPROTO_SCTP:
> > > +		return data_len >= sizeof(struct sctphdr);
> > > +	case IPPROTO_GRE:
> > > +		return data_len >= sizeof(struct gre_base_hdr);
> > > +	case IPPROTO_NONE:
> > 
> > Remove this and make it part of default and return true if protocol is
> > unknown.
> 
> Hmm.  Its likely safe to accept unknown headers, here.
> 
> Perhaps next iteration should indeed do what you suggest but also check
> check ESP and AH.
> 
> > Default is false for an unknown protocol, should be true.
> 
> I suggested it this way, i.e. don't permit unknown l4 protocols, but
> maybe its too restrictive.
> 
> > > +	if (pkt->tprot != IPPROTO_TCP)
> > > +		return true;
> > > +
> > > +	return priv->offset > doff || priv->offset + priv->len <= doff;
> >                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > 
> > maybe simply check priv->offset >= doff here?
> 
> Could you elaborate?  The above LGTM.  priv->offset > doff is already
> tested?  I mean, write is ok either if offset exceeds doff (lhs)
> or if offset + length doesn't touch doff area (rhs).
> 
> Did you mean "just reject everything exceeding doff"?
> 
> Patch LGTM, except perhaps switching to "allow unknowns" in nfqueue.

OK, thanks for explaining.