diff options
author | gordon <gordon@FreeBSD.org> | 2018-04-04 05:37:52 +0000 |
---|---|---|
committer | gordon <gordon@FreeBSD.org> | 2018-04-04 05:37:52 +0000 |
commit | 0ce10b6784cbf4e1763f41eaadbf25a600ea4f89 (patch) | |
tree | 0147334bd9c4925d11ef333a680ade7b534347f0 | |
parent | 0e2c6eafc8392d9e3fb835393c03c3f02b0e00fb (diff) | |
download | FreeBSD-src-0ce10b6784cbf4e1763f41eaadbf25a600ea4f89.zip FreeBSD-src-0ce10b6784cbf4e1763f41eaadbf25a600ea4f89.tar.gz |
Fix ipsec crash or denial of service. [SA-18:05.ipsec]
Reported by: Maxime Villard
Approved by: so
Security: CVE-2018-6918
Security: FreeBSD-SA-18:05.ipsec
-rw-r--r-- | sys/netipsec/xform_ah.c | 72 |
1 files changed, 28 insertions, 44 deletions
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c index 98d22f5..6716e70 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) { @@ -293,7 +293,7 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out) else ip->ip_off = htons(0); - ptr = mtod(m, unsigned char *) + sizeof(struct ip); + ptr = mtod(m, unsigned char *); /* IPv4 option processing */ for (off = sizeof(struct ip); off < skip;) { @@ -374,7 +374,7 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out) /* Zeroize all other options. */ count = ptr[off + 1]; - bcopy(ipseczeroes, ptr, count); + bcopy(ipseczeroes, ptr + off, count); off += count; break; } @@ -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); |