OSDN Git Service

Merge 4.4.175 into android-4.4
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / net / xfrm / xfrm_user.c
index 68010a0..9d57500 100644 (file)
@@ -121,22 +121,17 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
        struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
        struct xfrm_replay_state_esn *rs;
 
-       if (p->flags & XFRM_STATE_ESN) {
-               if (!rt)
-                       return -EINVAL;
-
-               rs = nla_data(rt);
+       if (!rt)
+               return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0;
 
-               if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
-                       return -EINVAL;
+       rs = nla_data(rt);
 
-               if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
-                   nla_len(rt) != sizeof(*rs))
-                       return -EINVAL;
-       }
+       if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
+               return -EINVAL;
 
-       if (!rt)
-               return 0;
+       if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
+           nla_len(rt) != sizeof(*rs))
+               return -EINVAL;
 
        /* As only ESP and AH support ESN feature. */
        if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH))
@@ -156,10 +151,16 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
        err = -EINVAL;
        switch (p->family) {
        case AF_INET:
+               if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32)
+                       goto out;
+
                break;
 
        case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
+               if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128)
+                       goto out;
+
                break;
 #else
                err = -EAFNOSUPPORT;
@@ -868,13 +869,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
                              &x->replay);
        if (ret)
                goto out;
-       if (x->security)
-               ret = copy_sec_ctx(x->security, skb);
        if (x->props.output_mark) {
                ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark);
                if (ret)
                        goto out;
        }
+       if (x->security)
+               ret = copy_sec_ctx(x->security, skb);
 out:
        return ret;
 }
@@ -993,10 +994,12 @@ static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
 {
        struct sock *nlsk = rcu_dereference(net->xfrm.nlsk);
 
-       if (nlsk)
-               return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC);
-       else
-               return -1;
+       if (!nlsk) {
+               kfree_skb(skb);
+               return -EPIPE;
+       }
+
+       return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC);
 }
 
 static inline size_t xfrm_spdinfo_msgsize(void)
@@ -1323,10 +1326,16 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
 
        switch (p->sel.family) {
        case AF_INET:
+               if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32)
+                       return -EINVAL;
+
                break;
 
        case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
+               if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128)
+                       return -EINVAL;
+
                break;
 #else
                return  -EAFNOSUPPORT;
@@ -1384,11 +1393,14 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
 
 static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
 {
+       u16 prev_family;
        int i;
 
        if (nr > XFRM_MAX_DEPTH)
                return -EINVAL;
 
+       prev_family = family;
+
        for (i = 0; i < nr; i++) {
                /* We never validated the ut->family value, so many
                 * applications simply leave it at zero.  The check was
@@ -1400,6 +1412,20 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
                if (!ut[i].family)
                        ut[i].family = family;
 
+               switch (ut[i].mode) {
+               case XFRM_MODE_TUNNEL:
+               case XFRM_MODE_BEET:
+                       break;
+               default:
+                       if (ut[i].family != prev_family)
+                               return -EINVAL;
+                       break;
+               }
+               if (ut[i].mode >= XFRM_MODE_MAX)
+                       return -EINVAL;
+
+               prev_family = ut[i].family;
+
                switch (ut[i].family) {
                case AF_INET:
                        break;
@@ -1410,6 +1436,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
                default:
                        return -EINVAL;
                }
+
+               switch (ut[i].id.proto) {
+               case IPPROTO_AH:
+               case IPPROTO_ESP:
+               case IPPROTO_COMP:
+#if IS_ENABLED(CONFIG_IPV6)
+               case IPPROTO_ROUTING:
+               case IPPROTO_DSTOPTS:
+#endif
+               case IPSEC_PROTO_ANY:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
        }
 
        return 0;
@@ -1613,9 +1654,11 @@ static inline size_t userpolicy_type_attrsize(void)
 #ifdef CONFIG_XFRM_SUB_POLICY
 static int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
 {
-       struct xfrm_userpolicy_type upt = {
-               .type = type,
-       };
+       struct xfrm_userpolicy_type upt;
+
+       /* Sadly there are two holes in struct xfrm_userpolicy_type */
+       memset(&upt, 0, sizeof(upt));
+       upt.type = type;
 
        return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);
 }
@@ -1660,32 +1703,34 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
 
 static int xfrm_dump_policy_done(struct netlink_callback *cb)
 {
-       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
        struct net *net = sock_net(cb->skb->sk);
 
        xfrm_policy_walk_done(walk, net);
        return 0;
 }
 
+static int xfrm_dump_policy_start(struct netlink_callback *cb)
+{
+       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
+
+       BUILD_BUG_ON(sizeof(*walk) > sizeof(cb->args));
+
+       xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
+       return 0;
+}
+
 static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);
-       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+       struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
        struct xfrm_dump_info info;
 
-       BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) >
-                    sizeof(cb->args) - sizeof(cb->args[0]));
-
        info.in_skb = cb->skb;
        info.out_skb = skb;
        info.nlmsg_seq = cb->nlh->nlmsg_seq;
        info.nlmsg_flags = NLM_F_MULTI;
 
-       if (!cb->args[0]) {
-               cb->args[0] = 1;
-               xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
-       }
-
        (void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
 
        return skb->len;
@@ -2437,6 +2482,7 @@ static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
 
 static const struct xfrm_link {
        int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
+       int (*start)(struct netlink_callback *);
        int (*dump)(struct sk_buff *, struct netlink_callback *);
        int (*done)(struct netlink_callback *);
        const struct nla_policy *nla_pol;
@@ -2450,6 +2496,7 @@ static const struct xfrm_link {
        [XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_add_policy    },
        [XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy    },
        [XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy,
+                                                  .start = xfrm_dump_policy_start,
                                                   .dump = xfrm_dump_policy,
                                                   .done = xfrm_dump_policy_done },
        [XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi },
@@ -2479,7 +2526,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 
 #ifdef CONFIG_COMPAT
        if (is_compat_task())
-               return -ENOTSUPP;
+               return -EOPNOTSUPP;
 #endif
 
        type = nlh->nlmsg_type;
@@ -2501,6 +2548,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 
                {
                        struct netlink_dump_control c = {
+                               .start = link->start,
                                .dump = link->dump,
                                .done = link->done,
                        };