Module Name:    src
Committed By:   martin
Date:           Thu Feb 15 14:28:38 UTC 2018

Modified Files:
        src/sys/netipsec [netbsd-8]: xform_ipip.c

Log Message:
Pull up following revision(s) (requested by maxv in ticket #551):
        sys/netipsec/xform_ipip.c: revision 1.56-1.63

Fix use-after-free. There is a path where the mbuf gets pulled up without
a proper mtod afterwards:

218     ipo = mtod(m, struct ip *);
281     m = m_pullup(m, hlen);
232     ipo->ip_src.s_addr

Found by Mootja.

Meanwhile it seems to me that 'ipo' should be set to NULL if the inner
packet is IPv6, but I'll revisit that later.
As I said in my last commit in this file, ipo should be set to NULL;
otherwise the 'local address spoofing' check below is always wrong on
IPv6.

Style and remove dead code.

dedup

Fix the IPIP_STAT_IBYTES stats; we did m_adj(m, iphlen) which substracted
iphlen, so no need to re-substract it again.

Remove broken MROUTING code, rename ipo->ip4, and simplify.


To generate a diff of this commit:
cvs rdiff -u -r1.49.2.2 -r1.49.2.3 src/sys/netipsec/xform_ipip.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/netipsec/xform_ipip.c
diff -u src/sys/netipsec/xform_ipip.c:1.49.2.2 src/sys/netipsec/xform_ipip.c:1.49.2.3
--- src/sys/netipsec/xform_ipip.c:1.49.2.2	Sun Dec 10 09:41:32 2017
+++ src/sys/netipsec/xform_ipip.c	Thu Feb 15 14:28:38 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: xform_ipip.c,v 1.49.2.2 2017/12/10 09:41:32 snj Exp $	*/
+/*	$NetBSD: xform_ipip.c,v 1.49.2.3 2018/02/15 14:28:38 martin Exp $	*/
 /*	$FreeBSD: src/sys/netipsec/xform_ipip.c,v 1.3.2.1 2003/01/24 05:11:36 sam Exp $	*/
 /*	$OpenBSD: ip_ipip.c,v 1.25 2002/06/10 18:04:55 itojun Exp $ */
 
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xform_ipip.c,v 1.49.2.2 2017/12/10 09:41:32 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xform_ipip.c,v 1.49.2.3 2018/02/15 14:28:38 martin Exp $");
 
 /*
  * IP-inside-IP processing
@@ -74,10 +74,6 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ipip.c
 
 #include <netipsec/ipip_var.h>
 
-#ifdef MROUTING
-#include <netinet/ip_mroute.h>
-#endif
-
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netipsec/ipsec6.h>
@@ -88,84 +84,41 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ipip.c
 #include <netipsec/key.h>
 #include <netipsec/key_debug.h>
 
-typedef void	pr_in_input_t (struct mbuf *m, ...);
+/* XXX IPCOMP */
+#define	M_IPSEC	(M_AUTHIPHDR|M_AUTHIPDGM|M_DECRYPTED)
 
-/*
- * We can control the acceptance of IP4 packets by altering the sysctl
- * net.inet.ipip.allow value.  Zero means drop them, all else is acceptance.
- */
-int	ipip_allow = 0;
+typedef void pr_in_input_t(struct mbuf *m, ...);
 
+int ipip_allow = 0;
 percpu_t *ipipstat_percpu;
 
-#ifdef SYSCTL_DECL
-SYSCTL_DECL(_net_inet_ipip);
-
-SYSCTL_INT(_net_inet_ipip, OID_AUTO,
-	ipip_allow,	CTLFLAG_RW,	&ipip_allow,	0, "");
-SYSCTL_STRUCT(_net_inet_ipip, IPSECCTL_STATS,
-	stats,		CTLFLAG_RD,	&ipipstat,	ipipstat, "");
-
-#endif
-
 void ipe4_attach(void);
 
-
-/* XXX IPCOMP */
-#define	M_IPSEC	(M_AUTHIPHDR|M_AUTHIPDGM|M_DECRYPTED)
-
 static void _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp);
 
 #ifdef INET6
-/*
- * Really only a wrapper for ipip_input(), for use with IPv6.
- */
 int
 ip4_input6(struct mbuf **m, int *offp, int proto, void *eparg __unused)
 {
-#if 0
-	/* If we do not accept IP-in-IP explicitly, drop.  */
-	if (!ipip_allow && ((*m)->m_flags & M_IPSEC) == 0) {
-		DPRINTF(("%s: dropped due to policy\n", __func__));
-		IPIP_STATINC(IPIP_STAT_PDROPS);
-		m_freem(*m);
-		return IPPROTO_DONE;
-	}
-#endif
 	_ipip_input(*m, *offp, NULL);
 	return IPPROTO_DONE;
 }
-#endif /* INET6 */
+#endif
 
 #ifdef INET
-/*
- * Really only a wrapper for ipip_input(), for use with IPv4.
- */
 void
 ip4_input(struct mbuf *m, int off, int proto, void *eparg __unused)
 {
-
-#if 0
-	/* If we do not accept IP-in-IP explicitly, drop.  */
-	if (!ipip_allow && (m->m_flags & M_IPSEC) == 0) {
-		DPRINTF(("%s: dropped due to policy\n", __func__));
-		IPIP_STATINC(IPIP_STAT_PDROPS);
-		m_freem(m);
-		return;
-	}
-#endif
-
 	_ipip_input(m, off, NULL);
 }
-#endif /* INET */
+#endif
 
 /*
  * ipip_input gets called when we receive an IP{46} encapsulated packet,
  * either because we got it at a real interface, or because AH or ESP
  * were being used in tunnel mode (in which case the rcvif element will
- * contain the address of the encX interface associated with the tunnel.
+ * contain the address of the encX interface associated with the tunnel).
  */
-
 static void
 _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp)
 {
@@ -173,7 +126,7 @@ _ipip_input(struct mbuf *m, int iphlen, 
 	register struct ifnet *ifp;
 	register struct ifaddr *ifa;
 	pktqueue_t *pktq = NULL;
-	struct ip *ipo;
+	struct ip *ip4 = NULL;
 #ifdef INET6
 	register struct sockaddr_in6 *sin6;
 	struct ip6_hdr *ip6 = NULL;
@@ -189,21 +142,21 @@ _ipip_input(struct mbuf *m, int iphlen, 
 
 	switch (v >> 4) {
 #ifdef INET
-        case 4:
+	case 4:
 		hlen = sizeof(struct ip);
 		break;
-#endif /* INET */
+#endif
 #ifdef INET6
-        case 6:
+	case 6:
 		hlen = sizeof(struct ip6_hdr);
 		break;
 #endif
-        default:
+	default:
 		DPRINTF(("%s: bad protocol version 0x%x (%u) "
 		    "for outer header\n", __func__, v, v>>4));
 		IPIP_STATINC(IPIP_STAT_FAMILY);
 		m_freem(m);
-		return /* EAFNOSUPPORT */;
+		return;
 	}
 
 	/* Bring the IP header in the first mbuf, if not there already */
@@ -215,24 +168,13 @@ _ipip_input(struct mbuf *m, int iphlen, 
 		}
 	}
 
-	ipo = mtod(m, struct ip *);
-
-#ifdef MROUTING
-	if (ipo->ip_v == IPVERSION && ipo->ip_p == IPPROTO_IPV4) {
-		if (IN_MULTICAST(((struct ip *)((char *) ipo + iphlen))->ip_dst.s_addr)) {
-			ipip_mroute_input (m, iphlen);
-			return;
-		}
-	}
-#endif /* MROUTING */
-
 	/* Keep outer ecn field. */
 	switch (v >> 4) {
 #ifdef INET
 	case 4:
-		otos = ipo->ip_tos;
+		otos = mtod(m, struct ip *)->ip_tos;
 		break;
-#endif /* INET */
+#endif
 #ifdef INET6
 	case 6:
 		otos = (ntohl(mtod(m, struct ip6_hdr *)->ip6_flow) >> 20) & 0xff;
@@ -256,14 +198,15 @@ _ipip_input(struct mbuf *m, int iphlen, 
 
 	switch (v >> 4) {
 #ifdef INET
-        case 4:
+	case 4:
 		hlen = sizeof(struct ip);
+		pktq = ip_pktq;
 		break;
-#endif /* INET */
-
+#endif
 #ifdef INET6
-        case 6:
+	case 6:
 		hlen = sizeof(struct ip6_hdr);
+		pktq = ip6_pktq;
 		break;
 #endif
 	default:
@@ -271,7 +214,7 @@ _ipip_input(struct mbuf *m, int iphlen, 
 		    "for inner header\n", __func__, v, v >> 4));
 		IPIP_STATINC(IPIP_STAT_FAMILY);
 		m_freem(m);
-		return; /* EAFNOSUPPORT */
+		return;
 	}
 
 	/*
@@ -294,19 +237,19 @@ _ipip_input(struct mbuf *m, int iphlen, 
 	/* Some sanity checks in the inner IP header */
 	switch (v >> 4) {
 #ifdef INET
-    	case 4:
-                ipo = mtod(m, struct ip *);
-		ip_ecn_egress(ip4_ipsec_ecn, &otos, &ipo->ip_tos);
-                break;
-#endif /* INET */
+	case 4:
+		ip4 = mtod(m, struct ip *);
+		ip_ecn_egress(ip4_ipsec_ecn, &otos, &ip4->ip_tos);
+		break;
+#endif
 #ifdef INET6
-    	case 6:
-                ip6 = (struct ip6_hdr *) ipo;
+	case 6:
+		ip6 = mtod(m, struct ip6_hdr *);
 		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
 		ip_ecn_egress(ip6_ipsec_ecn, &otos, &itos);
 		ip6->ip6_flow &= ~htonl(0xff << 20);
-		ip6->ip6_flow |= htonl((uint32_t) itos << 20);
-                break;
+		ip6->ip6_flow |= htonl((uint32_t)itos << 20);
+		break;
 #endif
 	default:
 		panic("%s: unknown ip version %u (inner)", __func__, v>>4);
@@ -320,7 +263,7 @@ _ipip_input(struct mbuf *m, int iphlen, 
 		IFNET_READER_FOREACH(ifp) {
 			IFADDR_READER_FOREACH(ifa, ifp) {
 #ifdef INET
-				if (ipo) {
+				if (ip4) {
 					if (ifa->ifa_addr->sa_family !=
 					    AF_INET)
 						continue;
@@ -328,7 +271,7 @@ _ipip_input(struct mbuf *m, int iphlen, 
 					sin = (struct sockaddr_in *) ifa->ifa_addr;
 
 					if (sin->sin_addr.s_addr ==
-					    ipo->ip_src.s_addr)	{
+					    ip4->ip_src.s_addr)	{
 						pserialize_read_exit(s);
 						IPIP_STATINC(IPIP_STAT_SPOOF);
 						m_freem(m);
@@ -359,8 +302,8 @@ _ipip_input(struct mbuf *m, int iphlen, 
 		pserialize_read_exit(s);
 	}
 
-	/* Statistics */
-	IPIP_STATADD(IPIP_STAT_IBYTES, m->m_pkthdr.len - iphlen);
+	/* Statistics: m->m_pkthdr.len is the length of the inner packet */
+	IPIP_STATADD(IPIP_STAT_IBYTES, m->m_pkthdr.len);
 
 	/*
 	 * Interface pointer stays the same; if no IPsec processing has
@@ -370,21 +313,6 @@ _ipip_input(struct mbuf *m, int iphlen, 
 	 * untrusted packets.
 	 */
 
-	switch (v >> 4) {
-#ifdef INET
-	case 4:
-		pktq = ip_pktq;
-		break;
-#endif
-#ifdef INET6
-	case 6:
-		pktq = ip6_pktq;
-		break;
-#endif
-	default:
-		panic("%s: should never reach here", __func__);
-	}
-
 	int s = splnet();
 	if (__predict_false(!pktq_enqueue(pktq, m, 0))) {
 		IPIP_STATINC(IPIP_STAT_QFULL);
@@ -394,26 +322,20 @@ _ipip_input(struct mbuf *m, int iphlen, 
 }
 
 int
-ipip_output(
-    struct mbuf *m,
-    const struct ipsecrequest *isr,
-    struct secasvar *sav,
-    struct mbuf **mp,
-    int skip,
-    int protoff
-)
+ipip_output(struct mbuf *m, const struct ipsecrequest *isr,
+    struct secasvar *sav, struct mbuf **mp, int skip, int protoff)
 {
 	char buf[IPSEC_ADDRSTRLEN];
 	uint8_t tp, otos;
 	struct secasindex *saidx;
-	int error;
+	int error, iphlen;
 #ifdef INET
 	uint8_t itos;
 	struct ip *ipo;
-#endif /* INET */
+#endif
 #ifdef INET6
 	struct ip6_hdr *ip6, *ip6o;
-#endif /* INET6 */
+#endif
 
 	IPSEC_SPLASSERT_SOFTNET(__func__);
 	KASSERT(sav != NULL);
@@ -440,15 +362,16 @@ ipip_output(
 		}
 
 		M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
-		if (m == 0) {
+		if (m == NULL) {
 			DPRINTF(("%s: M_PREPEND failed\n", __func__));
 			IPIP_STATINC(IPIP_STAT_HDROPS);
 			error = ENOBUFS;
 			goto bad;
 		}
 
-		ipo = mtod(m, struct ip *);
+		iphlen = sizeof(struct ip);
 
+		ipo = mtod(m, struct ip *);
 		ipo->ip_v = IPVERSION;
 		ipo->ip_hl = 5;
 		ipo->ip_len = htons(m->m_pkthdr.len);
@@ -523,13 +446,15 @@ ipip_output(
 		}
 
 		M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
-		if (m == 0) {
+		if (m == NULL) {
 			DPRINTF(("%s: M_PREPEND failed\n", __func__));
 			IPIP_STATINC(IPIP_STAT_HDROPS);
 			error = ENOBUFS;
 			goto bad;
 		}
 
+		iphlen = sizeof(struct ip6_hdr);
+
 		/* Initialize IPv6 header */
 		ip6o = mtod(m, struct ip6_hdr *);
 		ip6o->ip6_flow = 0;
@@ -555,19 +480,19 @@ ipip_output(
 			ip6o->ip6_nxt = IPPROTO_IPIP;
 		} else
 #endif /* INET */
-			if (tp == (IPV6_VERSION >> 4)) {
-				uint32_t itos32;
+		if (tp == (IPV6_VERSION >> 4)) {
+			uint32_t itos32;
 
-				/* Save ECN notification. */
-				m_copydata(m, sizeof(struct ip6_hdr) +
-				    offsetof(struct ip6_hdr, ip6_flow),
-				    sizeof(uint32_t), &itos32);
-				itos = ntohl(itos32) >> 20;
-
-				ip6o->ip6_nxt = IPPROTO_IPV6;
-			} else {
-				goto nofamily;
-			}
+			/* Save ECN notification. */
+			m_copydata(m, sizeof(struct ip6_hdr) +
+			    offsetof(struct ip6_hdr, ip6_flow),
+			    sizeof(uint32_t), &itos32);
+			itos = ntohl(itos32) >> 20;
+
+			ip6o->ip6_nxt = IPPROTO_IPV6;
+		} else {
+			goto nofamily;
+		}
 
 		otos = 0;
 		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
@@ -580,43 +505,25 @@ nofamily:
 		DPRINTF(("%s: unsupported protocol family %u\n", __func__,
 		    saidx->dst.sa.sa_family));
 		IPIP_STATINC(IPIP_STAT_FAMILY);
-		error = EAFNOSUPPORT;		/* XXX diffs from openbsd */
+		error = EAFNOSUPPORT;
 		goto bad;
 	}
 
 	IPIP_STATINC(IPIP_STAT_OPACKETS);
-	*mp = m;
-
-#ifdef INET
-	if (saidx->dst.sa.sa_family == AF_INET) {
+	IPIP_STATADD(IPIP_STAT_OBYTES, m->m_pkthdr.len - iphlen);
 #if 0
-		if (sav->tdb_xform->xf_type == XF_IP4)
-			tdb->tdb_cur_bytes +=
-			    m->m_pkthdr.len - sizeof(struct ip);
+	if (sav->tdb_xform->xf_type == XF_IP4)
+		tdb->tdb_cur_bytes += m->m_pkthdr.len - iphlen;
 #endif
-		IPIP_STATADD(IPIP_STAT_OBYTES,
-			     m->m_pkthdr.len - sizeof(struct ip));
-	}
-#endif /* INET */
-
-#ifdef INET6
-	if (saidx->dst.sa.sa_family == AF_INET6) {
-#if 0
-		if (sav->tdb_xform->xf_type == XF_IP4)
-			tdb->tdb_cur_bytes +=
-			    m->m_pkthdr.len - sizeof(struct ip6_hdr);
-#endif
-		IPIP_STATADD(IPIP_STAT_OBYTES,
-		    m->m_pkthdr.len - sizeof(struct ip6_hdr));
-	}
-#endif /* INET6 */
 
+	*mp = m;
 	return 0;
+
 bad:
 	if (m)
 		m_freem(m);
 	*mp = NULL;
-	return (error);
+	return error;
 }
 
 static int
@@ -634,12 +541,7 @@ ipe4_zeroize(struct secasvar *sav)
 }
 
 static int
-ipe4_input(
-    struct mbuf *m,
-    struct secasvar *sav,
-    int skip,
-    int protoff
-)
+ipe4_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
 {
 	/* This is a rather serious mistake, so no conditional printing. */
 	printf("%s: should never be called\n", __func__);
@@ -680,11 +582,7 @@ static struct encapsw ipe4_encapsw6 = {
  * Check the encapsulated packet to see if we want it
  */
 static int
-ipe4_encapcheck(struct mbuf *m,
-    int off,
-    int proto,
-    void *arg
-)
+ipe4_encapcheck(struct mbuf *m, int off, int proto, void *arg)
 {
 	/*
 	 * Only take packets coming from IPSEC tunnels; the rest
@@ -710,12 +608,12 @@ ipe4_attach(void)
 	(void)encap_lock_enter();
 	/* ipe4_encapsw and ipe4_encapsw must be added atomically */
 #ifdef INET
-	(void) encap_attach_func(AF_INET, -1,
-		ipe4_encapcheck, &ipe4_encapsw, NULL);
+	(void)encap_attach_func(AF_INET, -1, ipe4_encapcheck, &ipe4_encapsw,
+	    NULL);
 #endif
 #ifdef INET6
-	(void) encap_attach_func(AF_INET6, -1,
-		ipe4_encapcheck, &ipe4_encapsw6, NULL);
+	(void)encap_attach_func(AF_INET6, -1, ipe4_encapcheck, &ipe4_encapsw6,
+	    NULL);
 #endif
 	encap_lock_exit();
 }

Reply via email to