@@ -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:
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 <tom@quantonium.net> --- net/ipv6/ah6.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-)