Validate IPv4 packet options in divert output.
authorbluhm <bluhm@openbsd.org>
Tue, 5 Mar 2024 09:45:13 +0000 (09:45 +0000)
committerbluhm <bluhm@openbsd.org>
Tue, 5 Mar 2024 09:45:13 +0000 (09:45 +0000)
When sending raw packets over divert socket, IP options were not
validated.  Fragment code tries to copy them and crashes.  Raw IP
output has a similar feature, but uses rip_chkhdr() to prevent
invalid packets from userland.  Call this funtion also from
divert_output() for strict user input validation.

Reported-by: syzbot+b1ba3a2a8ef13e5b4698@syzkaller.appspotmail.com
OK dlg@ deraadt@ mvs@

sys/netinet/ip_divert.c
sys/netinet/ip_var.h
sys/netinet/raw_ip.c

index d5ee300..9c050bb 100644 (file)
@@ -1,4 +1,4 @@
-/*      $OpenBSD: ip_divert.c,v 1.94 2024/02/11 18:14:26 mvs Exp $ */
+/*      $OpenBSD: ip_divert.c,v 1.95 2024/03/05 09:45:13 bluhm Exp $ */
 
 /*
  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
@@ -100,21 +100,19 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
        if ((error = in_nam2sin(nam, &sin)))
                goto fail;
 
-       /* Do basic sanity checks. */
-       if (m->m_pkthdr.len < sizeof(struct ip))
+       if (m->m_pkthdr.len > IP_MAXPACKET) {
+               error = EMSGSIZE;
                goto fail;
-       if ((m = m_pullup(m, sizeof(struct ip))) == NULL) {
-               /* m_pullup() has freed the mbuf, so just return. */
-               divstat_inc(divs_errors);
-               return (ENOBUFS);
        }
-       ip = mtod(m, struct ip *);
-       if (ip->ip_v != IPVERSION)
+
+       m = rip_chkhdr(m, NULL);
+       if (m == NULL) {
+               error = EINVAL;
                goto fail;
+       }
+
+       ip = mtod(m, struct ip *);
        off = ip->ip_hl << 2;
-       if (off < sizeof(struct ip) || ntohs(ip->ip_len) < off ||
-           m->m_pkthdr.len < ntohs(ip->ip_len))
-               goto fail;
 
        dir = (sin->sin_addr.s_addr == INADDR_ANY ? PF_OUT : PF_IN);
 
@@ -135,8 +133,10 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
                min_hdrlen = 0;
                break;
        }
-       if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
+       if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen) {
+               error = EINVAL;
                goto fail;
+       }
 
        m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
 
@@ -181,7 +181,7 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
 fail:
        m_freem(m);
        divstat_inc(divs_errors);
-       return (error ? error : EINVAL);
+       return (error);
 }
 
 void
index b0e6ba8..2c0d453 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_var.h,v 1.113 2024/02/13 12:22:09 bluhm Exp $      */
+/*     $OpenBSD: ip_var.h,v 1.114 2024/03/05 09:45:13 bluhm Exp $      */
 /*     $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $     */
 
 /*
@@ -261,6 +261,8 @@ void         rip_init(void);
 int     rip_input(struct mbuf **, int *, int, int);
 int     rip_output(struct mbuf *, struct socket *, struct sockaddr *,
            struct mbuf *);
+struct mbuf *
+        rip_chkhdr(struct mbuf *, struct mbuf *);
 int     rip_attach(struct socket *, int, int);
 int     rip_detach(struct socket *);
 void    rip_lock(struct socket *);
index 0075382..f40c8e9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: raw_ip.c,v 1.156 2024/02/11 18:14:26 mvs Exp $        */
+/*     $OpenBSD: raw_ip.c,v 1.157 2024/03/05 09:45:13 bluhm Exp $      */
 /*     $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $     */
 
 /*
@@ -128,8 +128,6 @@ rip_init(void)
        in_pcbinit(&rawcbtable, 1);
 }
 
-struct mbuf    *rip_chkhdr(struct mbuf *, struct mbuf *);
-
 int
 rip_input(struct mbuf **mp, int *offp, int proto, int af)
 {