Module Name:    src
Committed By:   maxv
Date:           Tue May 15 19:16:38 UTC 2018

Modified Files:
        src/sys/netinet: ip_reass.c
        src/sys/netinet6: frag6.c

Log Message:
When reassembling IPv4/IPv6 packets, ensure each fragment has been subject
to the same IPsec processing. That is to say, that all fragments are ESP,
or AH, or AH+ESP, or none.

The reassembly mechanism can be used both on the wire and inside an IPsec
tunnel, so we need to make sure all fragments of a packet were received
on only one side.

Even though I haven't tried, I believe there are configurations where it
would be possible for an attacker to inject an unencrypted fragment into a
legitimate stream of already-decrypted-and-authenticated fragments.

Typically on IPsec gateways with ESP tunnels, where we can encapsulate
fragments (as opposed to the general case, where we fragment encapsulated
data).

Note, for the record: a funnier thing, under IPv4, would be to send a
zero-sized !MFF fragment at the head of the packet, and manage to trigger
an ICMP error; M_DECRYPTED gets lost by the reassembly, and ICMP will reply
with the packet in clear (not encrypted).


To generate a diff of this commit:
cvs rdiff -u -r1.16 -r1.17 src/sys/netinet/ip_reass.c
cvs rdiff -u -r1.73 -r1.74 src/sys/netinet6/frag6.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/netinet/ip_reass.c
diff -u src/sys/netinet/ip_reass.c:1.16 src/sys/netinet/ip_reass.c:1.17
--- src/sys/netinet/ip_reass.c:1.16	Thu May  3 07:25:49 2018
+++ src/sys/netinet/ip_reass.c	Tue May 15 19:16:38 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_reass.c,v 1.16 2018/05/03 07:25:49 maxv Exp $	*/
+/*	$NetBSD: ip_reass.c,v 1.17 2018/05/15 19:16:38 maxv Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1988, 1993
@@ -46,7 +46,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.16 2018/05/03 07:25:49 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.17 2018/05/15 19:16:38 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -93,7 +93,8 @@ typedef struct ipfr_queue {
 	struct in_addr		ipq_src;
 	struct in_addr		ipq_dst;
 	uint16_t		ipq_nfrags;	/* frags in this queue entry */
-	uint8_t 		ipq_tos;	/* TOS of this fragment */
+	uint8_t			ipq_tos;	/* TOS of this fragment */
+	int			ipq_ipsec;	/* IPsec flags */
 } ipfr_queue_t;
 
 /*
@@ -217,6 +218,7 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t
 	struct ip *ip = ipqe->ipqe_ip, *qip;
 	const int hlen = ip->ip_hl << 2;
 	struct mbuf *m = ipqe->ipqe_m, *t;
+	int ipsecflags = m->m_flags & (M_DECRYPTED|M_AUTHIPHDR);
 	ipfr_qent_t *nq, *p, *q;
 	int i, next;
 
@@ -269,6 +271,7 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t
 		fp->ipq_p = ip->ip_p;
 		fp->ipq_id = ip->ip_id;
 		fp->ipq_tos = ip->ip_tos;
+		fp->ipq_ipsec = ipsecflags;
 		fp->ipq_src = ip->ip_src;
 		fp->ipq_dst = ip->ip_dst;
 		LIST_INSERT_HEAD(&ip_frags[hash], fp, ipq_q);
@@ -614,6 +617,7 @@ ip_reass_packet(struct mbuf **m0, struct
 	const int hlen = ip->ip_hl << 2;
 	const int len = ntohs(ip->ip_len);
 	struct mbuf *m = *m0;
+	int ipsecflags = m->m_flags & (M_DECRYPTED|M_AUTHIPHDR);
 	ipfr_queue_t *fp;
 	ipfr_qent_t *ipqe;
 	u_int hash, off, flen;
@@ -669,11 +673,20 @@ ip_reass_packet(struct mbuf **m0, struct
 		break;
 	}
 
-	/* Make sure that TOS matches previous fragments. */
-	if (fp && fp->ipq_tos != ip->ip_tos) {
-		IP_STATINC(IP_STAT_BADFRAGS);
-		mutex_exit(&ipfr_lock);
-		return EINVAL;
+	if (fp) {
+		/* All fragments must have the same IPsec flags. */
+		if (fp->ipq_ipsec != ipsecflags) {
+			IP_STATINC(IP_STAT_BADFRAGS);
+			mutex_exit(&ipfr_lock);
+			return EINVAL;
+		}
+
+		/* Make sure that TOS matches previous fragments. */
+		if (fp->ipq_tos != ip->ip_tos) {
+			IP_STATINC(IP_STAT_BADFRAGS);
+			mutex_exit(&ipfr_lock);
+			return EINVAL;
+		}
 	}
 
 	/*

Index: src/sys/netinet6/frag6.c
diff -u src/sys/netinet6/frag6.c:1.73 src/sys/netinet6/frag6.c:1.74
--- src/sys/netinet6/frag6.c:1.73	Thu May  3 07:25:49 2018
+++ src/sys/netinet6/frag6.c	Tue May 15 19:16:38 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: frag6.c,v 1.73 2018/05/03 07:25:49 maxv Exp $	*/
+/*	$NetBSD: frag6.c,v 1.74 2018/05/15 19:16:38 maxv Exp $	*/
 /*	$KAME: frag6.c,v 1.40 2002/05/27 21:40:31 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: frag6.c,v 1.73 2018/05/03 07:25:49 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: frag6.c,v 1.74 2018/05/15 19:16:38 maxv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -76,6 +76,7 @@ struct	ip6q {
 	struct ip6q	*ip6q_prev;
 	int		ip6q_unfrglen;	/* len of unfragmentable part */
 	int		ip6q_nfrag;	/* # of fragments */
+	int		ip6q_ipsec;	/* IPsec flags */
 };
 
 struct	ip6asfrag {
@@ -162,6 +163,7 @@ frag6_input(struct mbuf **mp, int *offp,
 	struct ip6q *q6;
 	struct ip6asfrag *af6, *ip6af, *af6dwn;
 	int offset = *offp, nxt, i, next;
+	int ipsecflags = m->m_flags & (M_DECRYPTED|M_AUTHIPHDR);
 	int first_frag = 0;
 	int fragoff, frgpartlen;	/* must be larger than u_int16_t */
 	struct ifnet *dstifp;
@@ -247,6 +249,13 @@ frag6_input(struct mbuf **mp, int *offp,
 		    IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst))
 			break;
 
+	if (q6 != &ip6q) {
+		/* All fragments must have the same IPsec flags. */
+		if (q6->ip6q_ipsec != ipsecflags) {
+			goto dropfrag;
+		}
+	}
+
 	if (q6 == &ip6q) {
 		/*
 		 * the first fragment to arrive, create a reassembly queue.
@@ -279,8 +288,8 @@ frag6_input(struct mbuf **mp, int *offp,
 		q6->ip6q_src	= ip6->ip6_src;
 		q6->ip6q_dst	= ip6->ip6_dst;
 		q6->ip6q_unfrglen = -1;	/* The 1st fragment has not arrived. */
-
 		q6->ip6q_nfrag = 0;
+		q6->ip6q_ipsec = ipsecflags;
 	}
 
 	/*

Reply via email to