aboutsummaryrefslogtreecommitdiff
path: root/sys/netipsec
diff options
context:
space:
mode:
authorAndrey V. Elsukov <ae@FreeBSD.org>2018-01-24 19:48:25 +0000
committerAndrey V. Elsukov <ae@FreeBSD.org>2018-01-24 19:48:25 +0000
commit055679e67e139c132e2928466f84c2e085bf1f16 (patch)
tree60cc3b8064e9af551db6afdb383c43f82d4d5650 /sys/netipsec
parentbd555da94b1517d6441660317193ff901bb5387b (diff)
downloadsrc-055679e67e139c132e2928466f84c2e085bf1f16.tar.gz
src-055679e67e139c132e2928466f84c2e085bf1f16.zip
Adopt revision 1.76 and 1.77 from NetBSD:
Fix a vulnerability in IPsec-IPv6-AH, that allows an attacker to remotely crash the kernel with a single packet. In this loop we need to increment 'ad' by two, because the length field of the option header does not count the size of the option header itself. If the length is zero, then 'count' is incremented by zero, and there's an infinite loop. Beyond that, this code was written with the assumption that since the IPv6 packet already went through the generic IPv6 option parser, several fields are guaranteed to be valid; but this assumption does not hold because of the missing '+2', and there's as a result a triggerable buffer overflow (write zeros after the end of the mbuf, potentially to the next mbuf in memory since it's a pool). Add the missing '+2', this place will be reinforced in separate commits. Reported by: Maxime Villard <maxv at NetBSD.org> MFC after: 1 week
Notes
Notes: svn path=/head/; revision=328352
Diffstat (limited to 'sys/netipsec')
-rw-r--r--sys/netipsec/xform_ah.c68
1 files changed, 26 insertions, 42 deletions
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c
index e241ad9efccd..8ff21c24ce41 100644
--- a/sys/netipsec/xform_ah.c
+++ b/sys/netipsec/xform_ah.c
@@ -264,7 +264,7 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
#ifdef INET6
struct ip6_ext *ip6e;
struct ip6_hdr ip6;
- int alloc, len, ad;
+ int ad, alloc, nxt, noff;
#endif /* INET6 */
switch (proto) {
@@ -447,61 +447,44 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
} else
break;
- off = ip6.ip6_nxt & 0xff; /* Next header type. */
+ nxt = ip6.ip6_nxt & 0xff; /* Next header type. */
- for (len = 0; len < skip - sizeof(struct ip6_hdr);)
- switch (off) {
+ for (off = 0; off < skip - sizeof(struct ip6_hdr);)
+ switch (nxt) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
- ip6e = (struct ip6_ext *) (ptr + len);
+ ip6e = (struct ip6_ext *)(ptr + off);
+ noff = off + ((ip6e->ip6e_len + 1) << 3);
+
+ /* Sanity check. */
+ if (noff > skip - sizeof(struct ip6_hdr))
+ goto error6;
/*
- * Process the mutable/immutable
- * options -- borrows heavily from the
- * KAME code.
+ * Zero out mutable options.
*/
- for (count = len + sizeof(struct ip6_ext);
- count < len + ((ip6e->ip6e_len + 1) << 3);) {
+ for (count = off + sizeof(struct ip6_ext);
+ count < noff;) {
if (ptr[count] == IP6OPT_PAD1) {
count++;
continue; /* Skip padding. */
}
- /* Sanity check. */
- if (count > len +
- ((ip6e->ip6e_len + 1) << 3)) {
- m_freem(m);
-
- /* Free, if we allocated. */
- if (alloc)
- free(ptr, M_XDATA);
- return EINVAL;
- }
+ ad = ptr[count + 1] + 2;
+ if (count + ad > noff)
+ goto error6;
- ad = ptr[count + 1];
-
- /* If mutable option, zeroize. */
if (ptr[count] & IP6OPT_MUTABLE)
- bcopy(ipseczeroes, ptr + count,
- ptr[count + 1]);
-
+ memset(ptr + count, 0, ad);
count += ad;
-
- /* Sanity check. */
- if (count >
- skip - sizeof(struct ip6_hdr)) {
- m_freem(m);
-
- /* Free, if we allocated. */
- if (alloc)
- free(ptr, M_XDATA);
- return EINVAL;
- }
}
+ if (count != noff)
+ goto error6;
+
/* Advance. */
- len += ((ip6e->ip6e_len + 1) << 3);
- off = ip6e->ip6e_nxt;
+ off += ((ip6e->ip6e_len + 1) << 3);
+ nxt = ip6e->ip6e_nxt;
break;
case IPPROTO_ROUTING:
@@ -509,14 +492,15 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
* Always include routing headers in
* computation.
*/
- ip6e = (struct ip6_ext *) (ptr + len);
- len += ((ip6e->ip6e_len + 1) << 3);
- off = ip6e->ip6e_nxt;
+ ip6e = (struct ip6_ext *) (ptr + off);
+ off += ((ip6e->ip6e_len + 1) << 3);
+ nxt = ip6e->ip6e_nxt;
break;
default:
DPRINTF(("%s: unexpected IPv6 header type %d",
__func__, off));
+error6:
if (alloc)
free(ptr, M_XDATA);
m_freem(m);