OSDN Git Service

Merge 4.4.175 into android-4.4
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / net / xfrm / xfrm_user.c
index 76944a4..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;
@@ -584,6 +585,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 
        xfrm_mark_get(attrs, &x->mark);
 
+       if (attrs[XFRMA_OUTPUT_MARK])
+               x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]);
+
        err = __xfrm_init_state(x, false);
        if (err)
                goto error;
@@ -865,6 +869,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
                              &x->replay);
        if (ret)
                goto out;
+       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:
@@ -985,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)
@@ -1315,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;
@@ -1376,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
@@ -1392,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;
@@ -1402,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;
@@ -1605,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);
 }
@@ -1693,6 +1744,10 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
        struct sk_buff *skb;
        int err;
 
+       err = verify_policy_dir(dir);
+       if (err)
+               return ERR_PTR(err);
+
        skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!skb)
                return ERR_PTR(-ENOMEM);
@@ -2218,6 +2273,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
        int n = 0;
        struct net *net = sock_net(skb->sk);
 
+       err = verify_policy_dir(pi->dir);
+       if (err)
+               return err;
+
        if (attrs[XFRMA_MIGRATE] == NULL)
                return -EINVAL;
 
@@ -2333,6 +2392,11 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
 {
        struct net *net = &init_net;
        struct sk_buff *skb;
+       int err;
+
+       err = verify_policy_dir(dir);
+       if (err)
+               return err;
 
        skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC);
        if (skb == NULL)
@@ -2408,6 +2472,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
        [XFRMA_SA_EXTRA_FLAGS]  = { .type = NLA_U32 },
        [XFRMA_PROTO]           = { .type = NLA_U8 },
        [XFRMA_ADDRESS_FILTER]  = { .len = sizeof(struct xfrm_address_filter) },
+       [XFRMA_OUTPUT_MARK]     = { .len = NLA_U32 },
 };
 
 static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
@@ -2461,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;
@@ -2627,6 +2692,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
                l += nla_total_size(sizeof(*x->coaddr));
        if (x->props.extra_flags)
                l += nla_total_size(sizeof(x->props.extra_flags));
+       if (x->props.output_mark)
+               l += nla_total_size(sizeof(x->props.output_mark));
 
        /* Must count x->lastused as it may become non-zero behind our back. */
        l += nla_total_size(sizeof(u64));
@@ -2990,6 +3057,11 @@ out_free_skb:
 
 static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
 {
+       int err;
+
+       err = verify_policy_dir(dir);
+       if (err)
+               return err;
 
        switch (c->event) {
        case XFRM_MSG_NEWPOLICY: