]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/ipv6/exthdrs.c
[IPV6]: Disallow RH0 by default (CVE-2007-2242)
[karo-tx-linux.git] / net / ipv6 / exthdrs.c
index be6faf311387ce8d6f5adf1b7ec251b5b89752f2..a7cac228f5b51ebcc1cc095a1d839a99c8a907d3 100644 (file)
@@ -152,7 +152,7 @@ static struct tlvtype_proc tlvprocdestopt_lst[] = {
        {-1,                    NULL}
 };
 
-static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
+static int ipv6_destopt_rcv(struct sk_buff **skbp)
 {
        struct sk_buff *skb = *skbp;
        struct inet6_skb_parm *opt = IP6CB(skb);
@@ -169,7 +169,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
 
        if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
                skb->h.raw += ((skb->h.raw[1]+1)<<3);
-               *nhoffp = opt->dst1;
+               opt->nhoff = opt->dst1;
                return 1;
        }
 
@@ -192,7 +192,7 @@ void __init ipv6_destopt_init(void)
   NONE header. No data in packet.
  ********************************/
 
-static int ipv6_nodata_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
+static int ipv6_nodata_rcv(struct sk_buff **skbp)
 {
        struct sk_buff *skb = *skbp;
 
@@ -215,16 +215,33 @@ void __init ipv6_nodata_init(void)
   Routing header.
  ********************************/
 
-static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
+static int ipv6_rthdr_rcv(struct sk_buff **skbp)
 {
        struct sk_buff *skb = *skbp;
        struct inet6_skb_parm *opt = IP6CB(skb);
        struct in6_addr *addr;
        struct in6_addr daddr;
+       struct inet6_dev *idev;
        int n, i;
-
        struct ipv6_rt_hdr *hdr;
        struct rt0_hdr *rthdr;
+       int accept_source_route = ipv6_devconf.accept_source_route;
+
+       if (accept_source_route < 0 ||
+           ((idev = in6_dev_get(skb->dev)) == NULL)) {
+               kfree_skb(skb);
+               return -1;
+       }
+       if (idev->cnf.accept_source_route < 0) {
+               in6_dev_put(idev);
+               kfree_skb(skb);
+               return -1;
+       }
+
+       if (accept_source_route > idev->cnf.accept_source_route)
+               accept_source_route = idev->cnf.accept_source_route;
+
+       in6_dev_put(idev);
 
        if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
            !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
@@ -235,6 +252,18 @@ static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
 
        hdr = (struct ipv6_rt_hdr *) skb->h.raw;
 
+       switch (hdr->type) {
+       case IPV6_SRCRT_TYPE_0:
+               if (accept_source_route > 0)
+                       break;
+               kfree_skb(skb);
+               return -1;
+       default:
+               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
+               return -1;
+       }
+
        if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
            skb->pkt_type != PACKET_HOST) {
                IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
@@ -249,16 +278,10 @@ looped_back:
                skb->h.raw += (hdr->hdrlen + 1) << 3;
                opt->dst0 = opt->dst1;
                opt->dst1 = 0;
-               *nhoffp = (&hdr->nexthdr) - skb->nh.raw;
+               opt->nhoff = (&hdr->nexthdr) - skb->nh.raw;
                return 1;
        }
 
-       if (hdr->type != IPV6_SRCRT_TYPE_0) {
-               IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
-               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
-               return -1;
-       }
-       
        if (hdr->hdrlen & 0x01) {
                IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
                icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
@@ -413,6 +436,8 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
        return opt;
 }
 
+EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
+
 /**********************************
   Hop-by-hop options.
  **********************************/
@@ -485,9 +510,26 @@ static struct tlvtype_proc tlvprochopopt_lst[] = {
 
 int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
 {
-       IP6CB(skb)->hop = sizeof(struct ipv6hdr);
-       if (ip6_parse_tlv(tlvprochopopt_lst, skb))
+       struct inet6_skb_parm *opt = IP6CB(skb);
+
+       /*
+        * skb->nh.raw is equal to skb->data, and
+        * skb->h.raw - skb->nh.raw is always equal to
+        * sizeof(struct ipv6hdr) by definition of
+        * hop-by-hop options.
+        */
+       if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
+           !pskb_may_pull(skb, sizeof(struct ipv6hdr) + ((skb->h.raw[1] + 1) << 3))) {
+               kfree_skb(skb);
+               return -1;
+       }
+
+       opt->hop = sizeof(struct ipv6hdr);
+       if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
+               skb->h.raw += (skb->h.raw[1]+1)<<3;
+               opt->nhoff = sizeof(struct ipv6hdr);
                return sizeof(struct ipv6hdr);
+       }
        return -1;
 }
 
@@ -579,6 +621,8 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
        return opt2;
 }
 
+EXPORT_SYMBOL_GPL(ipv6_dup_options);
+
 static int ipv6_renew_option(void *ohdr,
                             struct ipv6_opt_hdr __user *newopt, int newoptlen,
                             int inherit,
@@ -614,14 +658,17 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
        struct ipv6_txoptions *opt2;
        int err;
 
-       if (newtype != IPV6_HOPOPTS && opt->hopopt)
-               tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
-       if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
-               tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
-       if (newtype != IPV6_RTHDR && opt->srcrt)
-               tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
-       if (newtype != IPV6_DSTOPTS && opt->dst1opt)
-               tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
+       if (opt) {
+               if (newtype != IPV6_HOPOPTS && opt->hopopt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
+               if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
+               if (newtype != IPV6_RTHDR && opt->srcrt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
+               if (newtype != IPV6_DSTOPTS && opt->dst1opt)
+                       tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
+       }
+
        if (newopt && newoptlen)
                tot_len += CMSG_ALIGN(newoptlen);
 
@@ -638,25 +685,25 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
        opt2->tot_len = tot_len;
        p = (char *)(opt2 + 1);
 
-       err = ipv6_renew_option(opt->hopopt, newopt, newoptlen,
+       err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
                                newtype != IPV6_HOPOPTS,
                                &opt2->hopopt, &p);
        if (err)
                goto out;
 
-       err = ipv6_renew_option(opt->dst0opt, newopt, newoptlen,
+       err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
                                newtype != IPV6_RTHDRDSTOPTS,
                                &opt2->dst0opt, &p);
        if (err)
                goto out;
 
-       err = ipv6_renew_option(opt->srcrt, newopt, newoptlen,
+       err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
                                newtype != IPV6_RTHDR,
-                               (struct ipv6_opt_hdr **)opt2->srcrt, &p);
+                               (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
        if (err)
                goto out;
 
-       err = ipv6_renew_option(opt->dst1opt, newopt, newoptlen,
+       err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
                                newtype != IPV6_DSTOPTS,
                                &opt2->dst1opt, &p);
        if (err)