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
This commit is contained in:
Cy Schubert
2025-11-02 20:59:15 -08:00
parent eda1756d04
commit 821774dfbd
2 changed files with 47 additions and 1 deletions
+6
View File
@@ -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" },
+41 -1
View File
@@ -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