The branch stable/13 has been updated by cy:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=75fbf1d27b6d00fb12e22b41d22b43c4279fd77d

commit 75fbf1d27b6d00fb12e22b41d22b43c4279fd77d
Author:     Cy Schubert <[email protected]>
AuthorDate: 2025-11-03 04:59:15 +0000
Commit:     Cy Schubert <[email protected]>
CommitDate: 2026-01-05 03:40:10 +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 <[email protected]>
    Reviewed by:            markj
    MFC after:              1 week
    Differential revision:  https://reviews.freebsd.org/D53843
    
    (cherry picked from commit 821774dfbdaa12ef072ff7eaea8f9966a7e63935)
---
 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 1fe58fd56197..5619b24200d7 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 8b343acf1211..1bad910d1db1 100644
--- a/sys/netpfil/ipfilter/netinet/ip_nat.c
+++ b/sys/netpfil/ipfilter/netinet/ip_nat.c
@@ -978,9 +978,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)
@@ -1031,6 +1035,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);
@@ -1045,6 +1059,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

Reply via email to