From 852e0d9620ce08cbb97768f83f85ac62f2dae737 Mon Sep 17 00:00:00 2001 From: gordon Date: Tue, 21 Apr 2020 15:52:22 +0000 Subject: [PATCH] Fix ipfw invalid mbuf handling. Approved by: so Security: FreeBSD-SA-20:10.ipfw Security: CVE-2019-5614 Security: CVE-2019-15874 --- sys/netpfil/ipfw/ip_fw2.c | 64 ++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 97c8a6687d0..1b89fb8e7d4 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -328,50 +328,71 @@ ipopts_match(struct ip *ip, ipfw_insn *cmd) return (flags_match(cmd, bits)); } +/* + * Parse TCP options. The logic copied from tcp_dooptions(). + */ static int -tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) +tcpopts_parse(const struct tcphdr *tcp, uint16_t *mss) { + const u_char *cp = (const u_char *)(tcp + 1); int optlen, bits = 0; - u_char *cp = (u_char *)(tcp + 1); - int x = (tcp->th_off << 2) - sizeof(struct tcphdr); + int cnt = (tcp->th_off << 2) - sizeof(struct tcphdr); - for (; x > 0; x -= optlen, cp += optlen) { + for (; cnt > 0; cnt -= optlen, cp += optlen) { int opt = cp[0]; if (opt == TCPOPT_EOL) break; if (opt == TCPOPT_NOP) optlen = 1; else { + if (cnt < 2) + break; optlen = cp[1]; - if (optlen <= 0) + if (optlen < 2 || optlen > cnt) break; } switch (opt) { - default: break; case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) + break; bits |= IP_FW_TCPOPT_MSS; + if (mss != NULL) + *mss = be16dec(cp + 2); break; case TCPOPT_WINDOW: - bits |= IP_FW_TCPOPT_WINDOW; + if (optlen == TCPOLEN_WINDOW) + bits |= IP_FW_TCPOPT_WINDOW; break; case TCPOPT_SACK_PERMITTED: + if (optlen == TCPOLEN_SACK_PERMITTED) + bits |= IP_FW_TCPOPT_SACK; + break; + case TCPOPT_SACK: - bits |= IP_FW_TCPOPT_SACK; + if (optlen > 2 && (optlen - 2) % TCPOLEN_SACK == 0) + bits |= IP_FW_TCPOPT_SACK; break; case TCPOPT_TIMESTAMP: - bits |= IP_FW_TCPOPT_TS; + if (optlen == TCPOLEN_TIMESTAMP) + bits |= IP_FW_TCPOPT_TS; break; - } } - return (flags_match(cmd, bits)); + return (bits); +} + +static int +tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) +{ + + return (flags_match(cmd, tcpopts_parse(tcp, NULL))); } static int @@ -1419,17 +1440,31 @@ ipfw_chk(struct ip_fw_args *args) * this way). */ #define PULLUP_TO(_len, p, T) PULLUP_LEN(_len, p, sizeof(T)) -#define PULLUP_LEN(_len, p, T) \ +#define _PULLUP_LOCKED(_len, p, T, unlock) \ do { \ int x = (_len) + T; \ if ((m)->m_len < x) { \ args->m = m = m_pullup(m, x); \ - if (m == NULL) \ + if (m == NULL) { \ + unlock; \ goto pullup_failed; \ + } \ } \ p = (mtod(m, char *) + (_len)); \ } while (0) +#define PULLUP_LEN(_len, p, T) _PULLUP_LOCKED(_len, p, T, ) +#define PULLUP_LEN_LOCKED(_len, p, T) \ + _PULLUP_LOCKED(_len, p, T, IPFW_PF_RUNLOCK(chain)); \ + UPDATE_POINTERS() +/* + * In case pointers got stale after pullups, update them. + */ +#define UPDATE_POINTERS() \ +do { \ + ip = mtod(m, struct ip *); \ +} while (0) + /* * if we have an ether header, */ @@ -2255,7 +2290,7 @@ do { \ case O_TCPOPTS: if (proto == IPPROTO_TCP && offset == 0 && ulp){ - PULLUP_LEN(hlen, ulp, + PULLUP_LEN_LOCKED(hlen, ulp, (TCP(ulp)->th_off << 2)); match = tcpopts_match(TCP(ulp), cmd); } @@ -3106,6 +3141,7 @@ do { \ } /* end of inner loop, scan opcodes */ #undef PULLUP_LEN +#undef PULLUP_LEN_LOCKED if (done) break; -- 2.45.0