Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Sep 2025 12:41:40 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 2be46b52f5db - main - pfctl: fix once rules
Message-ID:  <202509251241.58PCfeEK004637@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=2be46b52f5db0630550ec60ad8f92a7e7d7d78ab

commit 2be46b52f5db0630550ec60ad8f92a7e7d7d78ab
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-08-27 19:32:33 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-09-25 12:41:09 +0000

    pfctl: fix once rules
    
    parse.y revision 1.682 from 16.07.2018 errornously allowed `match once' and
    `anchor "a" once'.
    
    Fix both by checking for PF_DROP not PF_MATCH and creating anchors in the
    parser already such that they can be used to distinguish anchor rules in
    the same check as well.
    
    Found and fixed by Petr Hoffmann <petr.hoffmann at oracle dot com>, thanks!
    
    While here, remove an unneeded cast and make pfctl_add_rule() void as it
    always returned 0.
    
    OK sashan
    
    Obtained from:  OpenBSD, kn <kn@openbsd.org>, 6da84b37b3
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sbin/pfctl/parse.y        | 69 +++++++++++++++++++++++++++++++----------------
 sbin/pfctl/pfctl.c        | 30 ++-------------------
 sbin/pfctl/pfctl_parser.h |  2 +-
 3 files changed, 49 insertions(+), 52 deletions(-)

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index c75632c740b3..babe6b99013e 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -372,8 +372,8 @@ int		 validate_range(uint8_t, uint16_t, uint16_t);
 int		 disallow_table(struct node_host *, const char *);
 int		 disallow_urpf_failed(struct node_host *, const char *);
 int		 disallow_alias(struct node_host *, const char *);
-int		 rule_consistent(struct pfctl_rule *, int);
-int		 filter_consistent(struct pfctl_rule *, int);
+int		 rule_consistent(struct pfctl_rule *);
+int		 filter_consistent(struct pfctl_rule *);
 int		 nat_consistent(struct pfctl_rule *);
 int		 rdr_consistent(struct pfctl_rule *);
 int		 process_tabledef(char *, struct table_opts *, int);
@@ -403,7 +403,7 @@ void		 expand_rule(struct pfctl_rule *, bool, struct node_if *,
 		    struct node_proto *, struct node_os *, struct node_host *,
 		    struct node_port *, struct node_host *, struct node_port *,
 		    struct node_uid *, struct node_gid *, struct node_if *,
-		    struct node_icmp *, const char *);
+		    struct node_icmp *);
 int		 expand_altq(struct pf_altq *, struct node_if *,
 		    struct node_queue *, struct node_queue_bw bwspec,
 		    struct node_queue_opt *);
@@ -994,6 +994,7 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 		{
 			struct pfctl_rule	r;
 			struct node_proto	*proto;
+			char				*p;
 
 			if (check_rulestate(PFCTL_STATE_FILTER)) {
 				if ($2)
@@ -1039,6 +1040,30 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 					    "rules must specify a name");
 					YYERROR;
 				}
+				/*
+				 * Don't make non-brace anchors part of the main anchor pool.
+				 */
+				if ((r.anchor = calloc(1, sizeof(*r.anchor))) == NULL) {
+					err(1, "anchorrule: calloc");
+				}
+				pf_init_ruleset(&r.anchor->ruleset);
+				r.anchor->ruleset.anchor = r.anchor;
+				if (strlcpy(r.anchor->path, $2,
+				    sizeof(r.anchor->path)) >= sizeof(r.anchor->path)) {
+					errx(1, "anchorrule: strlcpy");
+				}
+				if ((p = strrchr($2, '/')) != NULL) {
+					if (strlen(p) == 1) {
+						yyerror("anchorrule: bad anchor name %s",
+						    $2);
+						YYERROR;
+					}
+				} else
+					p = $2;
+				if (strlcpy(r.anchor->name, p,
+				    sizeof(r.anchor->name)) >= sizeof(r.anchor->name)) {
+					errx(1, "anchorrule: strlcpy");
+				}
 			}
 			r.direction = $3;
 			r.quick = $4.quick;
@@ -1075,8 +1100,7 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 
 			expand_rule(&r, false, $5, NULL, NULL, NULL,
 			    $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host,
-			    $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec,
-			    pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+			    $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec);
 			free($2);
 			pf->astack[pf->asd + 1] = NULL;
 		}
@@ -1099,7 +1123,7 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 
 			expand_rule(&r, false, $3, NULL, NULL, NULL,
 			    $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
-			    $6.dst.port, 0, 0, 0, 0, $2);
+			    $6.dst.port, 0, 0, 0, 0);
 			free($2);
 		}
 		| RDRANCHOR string interface af proto fromto rtable {
@@ -1142,7 +1166,7 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 
 			expand_rule(&r, false, $3, NULL, NULL, NULL,
 			    $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
-			    $6.dst.port, 0, 0, 0, 0, $2);
+			    $6.dst.port, 0, 0, 0, 0);
 			free($2);
 		}
 		| BINATANCHOR string interface af proto fromto rtable {
@@ -1178,7 +1202,7 @@ anchorrule	: ANCHOR anchorname dir quick interface af proto fromto
 			decide_address_family($6.src.host, &r.af);
 			decide_address_family($6.dst.host, &r.af);
 
-			pfctl_append_rule(pf, &r, $2);
+			pfctl_append_rule(pf, &r);
 			free($2);
 		}
 		;
@@ -1465,7 +1489,7 @@ scrubrule	: scrubaction dir logquick interface af proto fromto scrub_opts
 
 			expand_rule(&r, false, $4, NULL, NULL, NULL,
 			    $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host,
-			    $7.dst.port, NULL, NULL, NULL, NULL, "");
+			    $7.dst.port, NULL, NULL, NULL, NULL);
 		}
 		;
 
@@ -1630,7 +1654,7 @@ antispoof	: ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
 				if (h != NULL)
 					expand_rule(&r, false, j, NULL, NULL,
 					    NULL, NULL, NULL, h, NULL, NULL,
-					    NULL, NULL, NULL, NULL, NULL, "");
+					    NULL, NULL, NULL, NULL, NULL);
 
 				if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
 					bzero(&r, sizeof(r));
@@ -1653,7 +1677,7 @@ antispoof	: ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
 						expand_rule(&r, false, NULL,
 						    NULL, NULL, NULL, NULL,
 						    NULL, h, NULL, NULL, NULL,
-						    NULL, NULL, NULL, NULL, "");
+						    NULL, NULL, NULL, NULL);
 				} else
 					free(hh);
 			}
@@ -2736,7 +2760,7 @@ pfrule		: action dir logquick interface route af proto fromto
 
 			expand_rule(&r, false, $4, $9.nat, $9.rdr, $5.redirspec,
 			    $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host,
-			    $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, "");
+			    $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec);
 		}
 		;
 
@@ -4871,7 +4895,7 @@ natrule		: nataction interface af proto fromto tag tagged rtable
 
 			expand_rule(&r, false, $2, NULL, $9, NULL, $4,
 			    $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
-			    $5.dst.port, 0, 0, 0, 0, "");
+			    $5.dst.port, 0, 0, 0, 0);
 		}
 		;
 
@@ -5050,7 +5074,7 @@ binatrule	: no BINAT natpasslog interface af proto FROM ipspec toipspec tag
 				free($13);
 			}
 
-			pfctl_append_rule(pf, &binat, "");
+			pfctl_append_rule(pf, &binat);
 		}
 		;
 
@@ -5277,7 +5301,7 @@ disallow_alias(struct node_host *h, const char *fmt)
 }
 
 int
-rule_consistent(struct pfctl_rule *r, int anchor_call)
+rule_consistent(struct pfctl_rule *r)
 {
 	int	problems = 0;
 
@@ -5287,7 +5311,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
 	case PF_DROP:
 	case PF_SCRUB:
 	case PF_NOSCRUB:
-		problems = filter_consistent(r, anchor_call);
+		problems = filter_consistent(r);
 		break;
 	case PF_NAT:
 	case PF_NONAT:
@@ -5306,7 +5330,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
 }
 
 int
-filter_consistent(struct pfctl_rule *r, int anchor_call)
+filter_consistent(struct pfctl_rule *r)
 {
 	int	problems = 0;
 
@@ -6346,7 +6370,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
     struct node_os *src_oses, struct node_host *src_hosts,
     struct node_port *src_ports, struct node_host *dst_hosts,
     struct node_port *dst_ports, struct node_uid *uids, struct node_gid *gids,
-    struct node_if *rcv, struct node_icmp *icmp_types, const char *anchor_call)
+    struct node_if *rcv, struct node_icmp *icmp_types)
 {
 	sa_family_t		 af = r->af;
 	int			 added = 0, error = 0;
@@ -6513,11 +6537,11 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
 				error += check_binat_redirspec(src_host, r, af);
 		}
 
-		if (rule_consistent(r, anchor_call[0]) < 0 || error)
+		if (rule_consistent(r) < 0 || error)
 			yyerror("skipping rule due to errors");
 		else {
 			r->nr = pf->astack[pf->asd]->match++;
-			pfctl_append_rule(pf, r, anchor_call);
+			pfctl_append_rule(pf, r);
 			added++;
 		}
 
@@ -6533,8 +6557,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
 
 			expand_rule(&rdr_rule, true, interface, NULL, rdr_redirspec,
 			    NULL, proto, src_os, dst_host, dst_port,
-			    rdr_dst_host, src_port, uid, gid, rcv, icmp_type,
-			    "");
+			    rdr_dst_host, src_port, uid, gid, rcv, icmp_type);
 		}
 
 		if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) {
@@ -7743,7 +7766,7 @@ int
 filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
 {
 	if (opts->marker & FOM_ONCE) {
-		if (r->action != PF_PASS && r->action != PF_MATCH) {
+		if ((r->action != PF_PASS && r->action != PF_DROP) || r->anchor) {
 			yyerror("'once' only applies to pass/block rules");
 			return (1);
 		}
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 7e1195fdf2f0..b8f4305a3e38 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1861,14 +1861,12 @@ pfctl_init_rule(struct pfctl_rule *r)
 	TAILQ_INIT(&(r->route.list));
 }
 
-int
-pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
-    const char *anchor_call)
+void
+pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r)
 {
 	u_int8_t		rs_num;
 	struct pfctl_rule	*rule;
 	struct pfctl_ruleset	*rs;
-	char 			*p;
 
 	rs_num = pf_get_ruleset_number(r->action);
 	if (rs_num == PF_RULESET_MAX)
@@ -1876,29 +1874,6 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
 
 	rs = &pf->anchor->ruleset;
 
-	if (anchor_call[0] && r->anchor == NULL) {
-		/* 
-		 * Don't make non-brace anchors part of the main anchor pool.
-		 */
-		if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
-			err(1, "pfctl_append_rule: calloc");
-		
-		pf_init_ruleset(&r->anchor->ruleset);
-		r->anchor->ruleset.anchor = r->anchor;
-		if (strlcpy(r->anchor->path, anchor_call,
-		    sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
-			errx(1, "pfctl_append_rule: strlcpy");
-		if ((p = strrchr(anchor_call, '/')) != NULL) {
-			if (!strlen(p))
-				err(1, "pfctl_append_rule: bad anchor name %s",
-				    anchor_call);
-		} else
-			p = (char *)anchor_call;
-		if (strlcpy(r->anchor->name, p,
-		    sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
-			errx(1, "pfctl_append_rule: strlcpy");
-	}
-
 	if ((rule = calloc(1, sizeof(*rule))) == NULL)
 		err(1, "calloc");
 	bcopy(r, rule, sizeof(*rule));
@@ -1910,7 +1885,6 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
 	pfctl_move_pool(&r->route, &rule->route);
 
 	TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
-	return (0);
 }
 
 int
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index e96ff0195e03..44ddfb45fbe1 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -288,7 +288,7 @@ int	pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
 int	pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
 
 void	pfctl_init_rule(struct pfctl_rule *r);
-int	pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
+void	pfctl_append_rule(struct pfctl *, struct pfctl_rule *);
 int	pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
 	    const char *);
 int	pfctl_add_altq(struct pfctl *, struct pf_altq *);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509251241.58PCfeEK004637>