net: Fix use after free by removing length arg from sk_data_ready callbacks.
[linux.git] / net / key / af_key.c
index 1a04c13293628eb420088717dce841266328b09f..f3c83073afc49f7aad98262cc5776b3edddc7f87 100644 (file)
@@ -205,7 +205,7 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
                if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
                        skb_set_owner_r(*skb2, sk);
                        skb_queue_tail(&sk->sk_receive_queue, *skb2);
-                       sk->sk_data_ready(sk, (*skb2)->len);
+                       sk->sk_data_ready(sk);
                        *skb2 = NULL;
                        err = 0;
                }
@@ -365,6 +365,7 @@ static const u8 sadb_ext_min_len[] = {
        [SADB_X_EXT_NAT_T_OA]           = (u8) sizeof(struct sadb_address),
        [SADB_X_EXT_SEC_CTX]            = (u8) sizeof(struct sadb_x_sec_ctx),
        [SADB_X_EXT_KMADDRESS]          = (u8) sizeof(struct sadb_x_kmaddress),
+       [SADB_X_EXT_FILTER]             = (u8) sizeof(struct sadb_x_filter),
 };
 
 /* Verify sadb_address_{len,prefixlen} against sa_family.  */
@@ -433,12 +434,13 @@ static inline int verify_sec_ctx_len(const void *p)
        return 0;
 }
 
-static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx)
+static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(const struct sadb_x_sec_ctx *sec_ctx,
+                                                                    gfp_t gfp)
 {
        struct xfrm_user_sec_ctx *uctx = NULL;
        int ctx_size = sec_ctx->sadb_x_ctx_len;
 
-       uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL);
+       uctx = kmalloc((sizeof(*uctx)+ctx_size), gfp);
 
        if (!uctx)
                return NULL;
@@ -1124,7 +1126,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
 
        sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
        if (sec_ctx != NULL) {
-               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
 
                if (!uctx)
                        goto out;
@@ -1798,6 +1800,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
 static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
 {
        u8 proto;
+       struct xfrm_address_filter *filter = NULL;
        struct pfkey_sock *pfk = pfkey_sk(sk);
 
        if (pfk->dump.dump != NULL)
@@ -1807,11 +1810,27 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
        if (proto == 0)
                return -EINVAL;
 
+       if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
+               struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
+
+               filter = kmalloc(sizeof(*filter), GFP_KERNEL);
+               if (filter == NULL)
+                       return -ENOMEM;
+
+               memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
+                      sizeof(xfrm_address_t));
+               memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr,
+                      sizeof(xfrm_address_t));
+               filter->family = xfilter->sadb_x_filter_family;
+               filter->splen = xfilter->sadb_x_filter_splen;
+               filter->dplen = xfilter->sadb_x_filter_dplen;
+       }
+
        pfk->dump.msg_version = hdr->sadb_msg_version;
        pfk->dump.msg_portid = hdr->sadb_msg_pid;
        pfk->dump.dump = pfkey_dump_sa;
        pfk->dump.done = pfkey_dump_sa_done;
-       xfrm_state_walk_init(&pfk->dump.u.state, proto);
+       xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
 
        return pfkey_do_dump(pfk);
 }
@@ -2231,14 +2250,14 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_
 
        sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
        if (sec_ctx != NULL) {
-               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
 
                if (!uctx) {
                        err = -ENOBUFS;
                        goto out;
                }
 
-               err = security_xfrm_policy_alloc(&xp->security, uctx);
+               err = security_xfrm_policy_alloc(&xp->security, uctx, GFP_KERNEL);
                kfree(uctx);
 
                if (err)
@@ -2335,12 +2354,12 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa
 
        sec_ctx = ext_hdrs[SADB_X_EXT_SEC_CTX - 1];
        if (sec_ctx != NULL) {
-               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+               struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_KERNEL);
 
                if (!uctx)
                        return -ENOMEM;
 
-               err = security_xfrm_policy_alloc(&pol_ctx, uctx);
+               err = security_xfrm_policy_alloc(&pol_ctx, uctx, GFP_KERNEL);
                kfree(uctx);
                if (err)
                        return err;
@@ -3059,6 +3078,24 @@ static u32 get_acqseq(void)
        return res;
 }
 
+static bool pfkey_is_alive(const struct km_event *c)
+{
+       struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
+       struct sock *sk;
+       bool is_alive = false;
+
+       rcu_read_lock();
+       sk_for_each_rcu(sk, &net_pfkey->table) {
+               if (pfkey_sk(sk)->registered) {
+                       is_alive = true;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       return is_alive;
+}
+
 static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
 {
        struct sk_buff *skb;
@@ -3239,8 +3276,8 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
                }
                if ((*dir = verify_sec_ctx_len(p)))
                        goto out;
-               uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
-               *dir = security_xfrm_policy_alloc(&xp->security, uctx);
+               uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx, GFP_ATOMIC);
+               *dir = security_xfrm_policy_alloc(&xp->security, uctx, GFP_ATOMIC);
                kfree(uctx);
 
                if (*dir)
@@ -3784,6 +3821,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
        .new_mapping    = pfkey_send_new_mapping,
        .notify_policy  = pfkey_send_policy_notify,
        .migrate        = pfkey_send_migrate,
+       .is_alive       = pfkey_is_alive,
 };
 
 static int __net_init pfkey_net_init(struct net *net)