diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c --- a/sbin/ipf/libipf/interror.c +++ b/sbin/ipf/libipf/interror.c @@ -363,6 +363,12 @@ { 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 --- a/sys/netpfil/ipfilter/netinet/ip_nat.c +++ b/sys/netpfil/ipfilter/netinet/ip_nat.c @@ -977,6 +977,9 @@ int error = 0, ret, arg, getlock; 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 +1030,16 @@ error = EINVAL; goto done; } + if (sizeof(natd) + natd.in_namelen != natd.in_size) { + IPFERROR(60080); + error = EINVAL; + goto done; + } + if (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 +1054,47 @@ nat = nt; } + /* + * Validate the incoming ipnat_t. + */ +#define IN_NAME(_n, _i) (_n)->in_names + (_n)->_i +#define VFY_IN_NAME(_a, _b) \ + if (_a->_b != -1) { \ + if (_a->_b < -1) { \ + IPFERROR(60077); \ + error = EINVAL; \ + goto done; \ + } \ + name = IN_NAME(_a, _b); \ + v_in_toend = _a->in_namelen - _a->_b; \ + if (v_in_toend < 0) { \ + IPFERROR(60081); \ + error = EINVAL; \ + goto done; \ + } \ + v_element_size = strnlen(name, v_in_toend) + 1; \ + v_in_size += v_element_size; \ + v_rem_namelen -= v_element_size; \ + if (v_rem_namelen < 0 || v_in_size > nat->in_size) { \ + IPFERROR(60078); \ + error = EINVAL; \ + goto done; \ + } \ + } + + v_in_size = sizeof(*nat); + v_rem_namelen = nat->in_namelen; + VFY_IN_NAME(nat, in_ifnames[0]); + if (nat->in_ifnames[0] != nat->in_ifnames[1]) + VFY_IN_NAME(nat, in_ifnames[1]); + VFY_IN_NAME(nat, in_plabel); + VFY_IN_NAME(nat, in_pconfig); + if (v_in_size > nat->in_size) { + IPFERROR(60079); + error = EINVAL; + goto done; + } + /* * For add/delete, look to see if the NAT entry is * already present