Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Nov 2025 15:18:40 +0000
From:      Cy Schubert <cy@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 821774dfbdaa - main - ipfilter: Verify ipnat on entry into kernel
Message-ID:  <69271a50.2d16b.65aa5942@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by cy:

URL: https://cgit.FreeBSD.org/src/commit/?id=821774dfbdaa12ef072ff7eaea8f9966a7e63935

commit 821774dfbdaa12ef072ff7eaea8f9966a7e63935
Author:     Cy Schubert <cy@FreeBSD.org>
AuthorDate: 2025-11-03 04:59:15 +0000
Commit:     Cy Schubert <cy@FreeBSD.org>
CommitDate: 2025-11-26 15:16:47 +0000

    ipfilter: Verify ipnat on entry into kernel
    
    The ipnat struct is built by ipnat(8), specifically ipnat_y.y when
    parsing the ipnat configuration file (typically ipnat.conf). ipnat
    contains a variable length string field at the end of the struct. This
    data field, called in_names, may contain various text strings such as
    NIC names. There is no upper bound limit to the length of strings as
    long as the in_namelen length field specifies the length of in_names
    within the ipnat structure and in_size specifies the size of the ipnat
    structure itself.
    
    Reported by:            Ilja Van Sprundel <ivansprundel@ioactive.com>
    Reviewed by:            markj
    MFC after:              1 week
    Differential revision:  https://reviews.freebsd.org/D53843
---
 sbin/ipf/libipf/interror.c            |  6 +++++
 sys/netpfil/ipfilter/netinet/ip_nat.c | 42 ++++++++++++++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c
index 4d48825bb3ad..6d5391f58ba2 100644
--- a/sbin/ipf/libipf/interror.c
+++ b/sbin/ipf/libipf/interror.c
@@ -363,6 +363,12 @@ log" },
 	{	60074,	"unknown next address type (ipv6)" },
 	{	60075,	"one object at a time must be copied" },
 	{	60076,	"NAT ioctl denied in jail without VNET" },
+	{	60077,	"in_names offset is wrapped negative" },
+	{	60078,	"in_names larger than in_namelen" },
+	{	60079,	"ipnat larger than in_size" },
+	{	60080,	"ipnat and in_namelen mismatch in_size" },
+	{	60081,	"ip_names runs off the end of ipnat" },
+	{	60082,	"in_namelen too large" },
 /* -------------------------------------------------------------------------- */
 	{	70001,	"incorrect object size to get pool stats" },
 	{	70002,	"could not malloc memory for new pool node" },
diff --git a/sys/netpfil/ipfilter/netinet/ip_nat.c b/sys/netpfil/ipfilter/netinet/ip_nat.c
index 53c180cdfbca..44ab7e9283de 100644
--- a/sys/netpfil/ipfilter/netinet/ip_nat.c
+++ b/sys/netpfil/ipfilter/netinet/ip_nat.c
@@ -974,9 +974,13 @@ ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
 	int mode, int uid, void *ctx)
 {
 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
-	int error = 0, ret, arg, getlock;
+	int error = 0, ret, arg, getlock, interr, i;
+	int interr_tbl[3] = { 60077, 60081, 60078 };
 	ipnat_t *nat, *nt, *n;
 	ipnat_t natd;
+	char *name;
+	size_t v_in_size, v_element_size;
+	int v_rem_namelen, v_in_toend;
 	SPL_INT(s);
 
 #if !SOLARIS && defined(_KERNEL)
@@ -1027,6 +1031,16 @@ ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
 				error = EINVAL;
 				goto done;
 			}
+			if (sizeof(natd) + natd.in_namelen != natd.in_size) {
+				IPFERROR(60080);
+				error = EINVAL;
+				goto done;
+			}
+			if (natd.in_namelen < 0 || natd.in_namelen > softc->ipf_max_namelen) {
+				IPFERROR(60082);
+				error = EINVAL;
+				goto done;
+			}
 			KMALLOCS(nt, ipnat_t *, natd.in_size);
 			if (nt == NULL) {
 				IPFERROR(60070);
@@ -1041,6 +1055,32 @@ ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
 			nat = nt;
 		}
 
+		/*
+		 * Validate the incoming ipnat_t.
+		 */
+		if ((interr = ipf_check_names_string(nat->in_names, nat->in_namelen, nat->in_ifnames[0])) != 0) {
+			IPFERROR(interr_tbl[interr-1]);
+			error = EINVAL;
+			goto done;
+		}
+		if (nat->in_ifnames[0] != nat->in_ifnames[1]) {
+			if ((interr = ipf_check_names_string(nat->in_names, nat->in_namelen, nat->in_ifnames[1])) != 0) {
+				IPFERROR(interr_tbl[interr-1]);
+				error = EINVAL;
+				goto done;
+			}
+		}
+		if ((interr = ipf_check_names_string(nat->in_names, nat->in_namelen, nat->in_plabel)) != 0) {
+			IPFERROR(interr_tbl[interr-1]);
+			error = EINVAL;
+			goto done;
+		}
+		if ((interr = ipf_check_names_string(nat->in_names, nat->in_namelen, nat->in_pconfig)) != 0) {
+			IPFERROR(interr_tbl[interr-1]);
+			error = EINVAL;
+			goto done;
+		}
+
 		/*
 		 * For add/delete, look to see if the NAT entry is
 		 * already present


help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69271a50.2d16b.65aa5942>