BSDSec

deadsimple BSD Security Advisories and Announcements

pfctl errata Nov 17

Patches are now available for 5.5 and 5.6 to fix an issue with pfctl
and certain rules combining IPv4 and IPv6 addresses (in that order) with
a dynamic interface address using the "(interface)" format. The patch
for 5.6 follows.

This problem can be worked around by reversing the order of addresses
(listing IPv6 first, then IPv4).


untrusted comment: signature from openbsd 5.6 base private key
RWR0EANmo9nqhlxzEl3gBOqqoVrDRpZgSYhTqFK031hbFJ9tX84ZYqS72CcubFWty11KDc7YCvei+VSQu1PdYPo/MU/nReUdBQU
OpenBSD 5.6 errata 7, Nov 17, 2014:  A PF rule using an IPv4 address
followed by an IPv6 address and then a dynamic address, e.g. "pass
from {192.0.2.1 2001:db8::1} to (pppoe0)", will have an incorrect /32
mask applied to the dynamic address.

Apply patch using:

    signify -Vep /etc/signify/openbsd-56-base.pub -x 007.pfctl.patch.sig \
        -m - | (cd /usr/src && patch -p0)

Then build and install pfctl:
    cd /usr/src/sbin/pfctl
    make obj
    make
    make install

Index: sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.636
retrieving revision 1.636.4.1
diff -u -p -r1.636 -r1.636.4.1
--- sbin/pfctl/parse.y	2 Jul 2014 13:03:41 -0000	1.636
+++ sbin/pfctl/parse.y	29 Oct 2014 15:29:33 -0000	1.636.4.1
@@ -4516,7 +4516,7 @@ expand_rule(struct pf_rule *r, int keepr
 	char			 tagname[PF_TAG_NAME_SIZE];
 	char			 match_tagname[PF_TAG_NAME_SIZE];
 	u_int8_t		 flags, flagset, keep_state;
-	struct node_host	*srch, *dsth;
+	struct node_host	*srch, *dsth, *osrch, *odsth;
 	struct redirspec	 binat;
 	struct pf_rule		 rb;
 	int			 dir = r->direction;
@@ -4607,6 +4607,18 @@ expand_rule(struct pf_rule *r, int keepr
 		    r->af, src_host, src_port, dst_host, dst_port,
 		    proto->proto);
 
+		osrch = odsth = NULL;
+		if (src_host->addr.type == PF_ADDR_DYNIFTL) {
+			osrch = src_host;
+			if ((src_host = gen_dynnode(src_host, r->af)) == NULL)
+				err(1, "expand_rule: calloc");
+		}
+		if (dst_host->addr.type == PF_ADDR_DYNIFTL) {
+			odsth = dst_host;
+			if ((dst_host = gen_dynnode(dst_host, r->af)) == NULL)
+				err(1, "expand_rule: calloc");
+		}
+
 		error += check_netmask(src_host, r->af);
 		error += check_netmask(dst_host, r->af);
 
@@ -4757,6 +4769,14 @@ expand_rule(struct pf_rule *r, int keepr
 			    uid, gid, rcv, icmp_type, anchor_call);
 		}
 
+		if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) {
+			free(src_host);
+			src_host = osrch;
+		}
+		if (odsth && dst_host->addr.type == PF_ADDR_DYNIFTL) {
+			free(dst_host);
+			dst_host = odsth;
+		}
 	))))))))));
 
 	if (!keeprule) {
Index: sbin/pfctl/pfctl_parser.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.298
retrieving revision 1.298.6.1
diff -u -p -r1.298 -r1.298.6.1
--- sbin/pfctl/pfctl_parser.c	20 Jan 2014 02:59:13 -0000	1.298
+++ sbin/pfctl/pfctl_parser.c	29 Oct 2014 15:29:34 -0000	1.298.6.1
@@ -1244,16 +1244,12 @@ int
 check_netmask(struct node_host *h, sa_family_t af)
 {
 	struct node_host	*n = NULL;
-	struct pf_addr	*m;
+	struct pf_addr		*m;
 
 	for (n = h; n != NULL; n = n->next) {
 		if (h->addr.type == PF_ADDR_TABLE)
 			continue;
 		m = &h->addr.v.a.mask;
-		/* fix up netmask for dynaddr */
-		if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL &&
-		    unmask(m, AF_INET6) > 32)
-			set_ipmask(n, 32);
 		/* netmasks > 32 bit are invalid on v4 */
 		if (af == AF_INET &&
 		    (m->addr32[1] || m->addr32[2] || m->addr32[3])) {
@@ -1263,6 +1259,30 @@ check_netmask(struct node_host *h, sa_fa
 		}
 	}
 	return (0);
+}
+
+struct node_host *
+gen_dynnode(struct node_host *h, sa_family_t af)
+{
+	struct node_host	*n;
+	struct pf_addr		*m;
+
+	if (h->addr.type != PF_ADDR_DYNIFTL)
+		return (NULL);
+
+	if ((n = calloc(1, sizeof(*n))) == NULL)
+		return (NULL);
+	bcopy(h, n, sizeof(*n));
+	n->ifname = NULL;
+	n->next = NULL;
+	n->tail = NULL;
+
+	/* fix up netmask */
+	m = &n->addr.v.a.mask;
+	if (af == AF_INET && unmask(m, AF_INET6) > 32)
+		set_ipmask(n, 32);
+
+	return (n);
 }
 
 /* interface lookup routines */
Index: sbin/pfctl/pfctl_parser.h
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.h,v
retrieving revision 1.102
retrieving revision 1.102.4.1
diff -u -p -r1.102 -r1.102.4.1
--- sbin/pfctl/pfctl_parser.h	19 Apr 2014 14:22:32 -0000	1.102
+++ sbin/pfctl/pfctl_parser.h	29 Oct 2014 15:29:34 -0000	1.102.4.1
@@ -265,6 +265,7 @@ extern const struct pf_timeout pf_timeou
 void			 set_ipmask(struct node_host *, u_int8_t);
 int			 check_netmask(struct node_host *, sa_family_t);
 int			 unmask(struct pf_addr *, sa_family_t);
+struct node_host	*gen_dynnode(struct node_host *, sa_family_t);
 void			 ifa_load(void);
 unsigned int		 ifa_nametoindex(const char *);
 char			*ifa_indextoname(unsigned int, char *);