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).
This commit is contained in:
maxv 2018-05-15 19:16:38 +00:00
parent 82b545a2e8
commit fbb9ed35f8
2 changed files with 33 additions and 11 deletions

View File

@ -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 *fp, const u_int hash)
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, const u_int hash)
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 ip *ip)
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 ip *ip)
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;
}
}
/*

View File

@ -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, int proto)
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, int proto)
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, int proto)
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;
}
/*