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 @@ -177,6 +177,11 @@ { 149, "object size validation failed for kernel copyout" }, { 150, "error copying data out for kernel copyout" }, { 151, "version mismatch for kernel copyout" }, + { 152, "fr_names offset is wrapped negative" }, + { 153, "fr_names larger than fr_namelen" }, + { 154, "frentry larger than fr_size" }, + { 155, "frentry and fr_namelen mismatch fr_size" }, + { 156, "fr_namelen too large" }, /* -------------------------------------------------------------------------- */ { 10001, "could not find token for auth iterator" }, { 10002, "write permissions require to add/remove auth rule" }, diff --git a/sys/netpfil/ipfilter/netinet/fil.c b/sys/netpfil/ipfilter/netinet/fil.c --- a/sys/netpfil/ipfilter/netinet/fil.c +++ b/sys/netpfil/ipfilter/netinet/fil.c @@ -363,6 +363,10 @@ "ip_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_iptimeout), 0, NULL, ipf_settimeout }, + { { (void *)offsetof(ipf_main_softc_t, ipf_max_namelen) }, + "max_namelen", 0, 0x7fffffff, + stsizeof(ipf_main_softc_t, ipf_max_namelen), + 0, NULL, NULL }, #if defined(INSTANCES) && defined(_KERNEL) { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, "intercept_loopback", 0, 1, @@ -4408,7 +4412,9 @@ void *ptr, *uptr; u_int *p, *pp; frgroup_t *fg; - char *group; + char *group, *name; + size_t v_fr_size, v_element_size; + int v_rem_namelen, v_fr_toend; ptr = NULL; fg = NULL; @@ -4423,6 +4429,17 @@ IPFERROR(6); return (EINVAL); } + if (fp->fr_size < sizeof(frd)) { + return (EINVAL); + } + if (sizeof(frd) + fp->fr_namelen != fp->fr_size ) { + IPFERROR(155); + return (EINVAL); + } + if (fp->fr_namelen > softc->ipf_max_namelen) { + IPFERROR(156); + return (EINVAL); + } KMALLOCS(f, frentry_t *, fp->fr_size); if (f == NULL) { IPFERROR(131); @@ -4449,6 +4466,51 @@ fp->fr_ptr = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; + /* + * Validate the incoming frentry_t. + */ +#define VFY_FR_NAME(_a, _b) \ + if (_a->_b != -1) { \ + if (_a->_b < -1) { \ + IPFERROR(152); \ + error = EINVAL; \ + goto donenolock; \ + } \ + name = FR_NAME(_a, _b); \ + v_fr_toend = _a->fr_namelen - _a->_b; \ + if (v_fr_toend < 0) { \ + IPFERROR(156); \ + error = EINVAL; \ + goto donenolock; \ + } \ + v_element_size = strnlen(name, v_fr_toend) + 1; \ + v_fr_size += v_element_size; \ + v_rem_namelen -= v_element_size; \ + if (v_rem_namelen < 0 || v_fr_size > fp->fr_size) { \ + IPFERROR(153); \ + error = EINVAL; \ + goto donenolock; \ + } \ + } + + v_fr_size = sizeof(*fp); + v_rem_namelen = fp->fr_namelen; + VFY_FR_NAME(fp, fr_comment); + VFY_FR_NAME(fp, fr_group); + VFY_FR_NAME(fp, fr_grhead); + VFY_FR_NAME(fp, fr_grhead); + VFY_FR_NAME(fp, fr_ifnames[0]); + VFY_FR_NAME(fp, fr_ifnames[1]); + VFY_FR_NAME(fp, fr_ifnames[2]); + VFY_FR_NAME(fp, fr_ifnames[3]); + VFY_FR_NAME(fp, fr_tif.fd_name); + VFY_FR_NAME(fp, fr_rif.fd_name); + VFY_FR_NAME(fp, fr_dif.fd_name); + if (v_fr_size > fp->fr_size) { + IPFERROR(154); + error = EINVAL; + goto donenolock; + } } else { fp = (frentry_t *)data; if ((fp->fr_type & FR_T_BUILTIN) == 0) { @@ -9040,6 +9102,7 @@ #endif softc->ipf_minttl = 4; softc->ipf_icmpminfragmtu = 68; + softc->ipf_max_namelen = 128; softc->ipf_flags = IPF_LOGGING; #ifdef LARGE_NAT diff --git a/sys/netpfil/ipfilter/netinet/ip_fil.h b/sys/netpfil/ipfilter/netinet/ip_fil.h --- a/sys/netpfil/ipfilter/netinet/ip_fil.h +++ b/sys/netpfil/ipfilter/netinet/ip_fil.h @@ -1529,6 +1529,7 @@ int ipf_pass; int ipf_minttl; int ipf_icmpminfragmtu; + int ipf_max_namelen; int ipf_interror; /* Should be in a struct that is per */ /* thread or process. Does not belong */ /* here but there's a lot more work */ diff --git a/sys/netpfil/ipfilter/netinet/mlfk_ipl.c b/sys/netpfil/ipfilter/netinet/mlfk_ipl.c --- a/sys/netpfil/ipfilter/netinet/mlfk_ipl.c +++ b/sys/netpfil/ipfilter/netinet/mlfk_ipl.c @@ -135,6 +135,7 @@ SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_chksrc), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_minttl), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, large_nat, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &VNET_NAME(ipfmain.ipf_large_nat), 0, "large_nat"); +SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_max_namelen, CTLFLAG_RWTUN, &VNET_NAME(ipfmain.ipf_max_namelen), 0, "max_namelen"); #define CDEV_MAJOR 79 #include