The current code assumes that all routing headers can be processed as type 0 when rearranging the routing header for AH verification. Change this to be explicit. Type 0 and type 2 are supported and are processed the same way with regards to AH.
Also check if rearranging routing header fails. Update reference in comment to more current RFC. Signed-off-by: Tom Herbert <t...@quantonium.net> --- net/ipv6/ah6.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 1e80157..032491c 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -145,7 +145,7 @@ static bool zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) /** * ipv6_rearrange_destopt - rearrange IPv6 destination options header * @iph: IPv6 header - * @destopt: destionation options header + * @destopt: destination options header */ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) { @@ -204,15 +204,16 @@ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *des #endif /** - * ipv6_rearrange_rthdr - rearrange IPv6 routing header + * ipv6_rearrange_type0_rthdr - rearrange type 0 IPv6 routing header * @iph: IPv6 header * @rthdr: routing header * * Rearrange the destination address in @iph and the addresses in @rthdr * so that they appear in the order they will at the final destination. - * See Appendix A2 of RFC 2402 for details. + * See Appendix A2 of RFC 4302 for details. */ -static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) +static bool ipv6_rearrange_type0_rthdr(struct ipv6hdr *iph, + struct ipv6_rt_hdr *rthdr) { int segments, segments_left; struct in6_addr *addrs; @@ -220,15 +221,13 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) segments_left = rthdr->segments_left; if (segments_left == 0) - return; + return true; rthdr->segments_left = 0; /* The value of rthdr->hdrlen has been verified either by the system * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming * packets. So we can assume that it is even and that segments is * greater than or equal to segments_left. - * - * For the same reason we can assume that this option is of type 0. */ segments = rthdr->hdrlen >> 1; @@ -240,6 +239,24 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) addrs[0] = iph->daddr; iph->daddr = final_addr; + + return true; +} + +static bool ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) +{ + switch (rthdr->type) { + case IPV6_SRCRT_TYPE_2: + /* Simplified format of type 0 so same processing */ + /* fallthrough */ + case IPV6_SRCRT_TYPE_0: /* Deprecated */ + return ipv6_rearrange_type0_rthdr(iph, rthdr); + default: + /* Bad or unidentified routing header, we don't know how + * to fix this header for security purposes. Return failure. + */ + return false; + } } static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) @@ -271,7 +288,11 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) break; case NEXTHDR_ROUTING: - ipv6_rearrange_rthdr(iph, exthdr.rth); + if (!ipv6_rearrange_rthdr(iph, exthdr.rth)) { + net_dbg_ratelimited("bad routing header\n"); + return -EINVAL; + } + break; default: -- 2.7.4