Commit 65bbbf6c authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec

Steffen Klassert says:

====================
pull request (net): ipsec 2017-12-22

1) Check for valid id proto in validate_tmpl(), otherwise
   we may trigger a warning in xfrm_state_fini().
   From Cong Wang.

2) Fix a typo on XFRMA_OUTPUT_MARK policy attribute.
   From Michal Kubecek.

3) Verify the state is valid when encap_type < 0,
   otherwise we may crash on IPsec GRO .
   From Aviv Heller.

4) Fix stack-out-of-bounds read on socket policy lookup.
   We access the flowi of the wrong address family in the
   IPv4 mapped IPv6 case, fix this by catching address
   family missmatches before we do the lookup.

5) fix xfrm_do_migrate() with AEAD to copy the geniv
   field too. Otherwise the state is not fully initialized
   and migration fails. From Antony Antony.

6) Fix stack-out-of-bounds with misconfigured transport
   mode policies. Our policy template validation is not
   strict enough. It is possible to configure policies
   with transport mode template where the address family
   of the template does not match the selectors address
   family. Fix this by refusing such a configuration,
   address family can not change on transport mode.

7) Fix a policy reference leak when reusing pcpu xdst
   entry. From Florian Westphal.

8) Reinject transport-mode packets through tasklet,
   otherwise it is possible to reate a recursion
   loop. From Herbert Xu.

Please pull or let me know if there are problems.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 178e5f57 acf568ee
...@@ -1570,6 +1570,9 @@ int xfrm_init_state(struct xfrm_state *x); ...@@ -1570,6 +1570,9 @@ int xfrm_init_state(struct xfrm_state *x);
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr); int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
int xfrm_trans_queue(struct sk_buff *skb,
int (*finish)(struct net *, struct sock *,
struct sk_buff *));
int xfrm_output_resume(struct sk_buff *skb, int err); int xfrm_output_resume(struct sk_buff *skb, int err);
int xfrm_output(struct sock *sk, struct sk_buff *skb); int xfrm_output(struct sock *sk, struct sk_buff *skb);
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
......
...@@ -23,6 +23,12 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -23,6 +23,12 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
return xfrm4_extract_header(skb); return xfrm4_extract_header(skb);
} }
static int xfrm4_rcv_encap_finish2(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
return dst_input(skb);
}
static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk, static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -33,7 +39,11 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk, ...@@ -33,7 +39,11 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
iph->tos, skb->dev)) iph->tos, skb->dev))
goto drop; goto drop;
} }
return dst_input(skb);
if (xfrm_trans_queue(skb, xfrm4_rcv_encap_finish2))
goto drop;
return 0;
drop: drop:
kfree_skb(skb); kfree_skb(skb);
return NET_RX_DROP; return NET_RX_DROP;
......
...@@ -32,6 +32,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi, ...@@ -32,6 +32,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi,
} }
EXPORT_SYMBOL(xfrm6_rcv_spi); EXPORT_SYMBOL(xfrm6_rcv_spi);
static int xfrm6_transport_finish2(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
if (xfrm_trans_queue(skb, ip6_rcv_finish))
__kfree_skb(skb);
return -1;
}
int xfrm6_transport_finish(struct sk_buff *skb, int async) int xfrm6_transport_finish(struct sk_buff *skb, int async)
{ {
struct xfrm_offload *xo = xfrm_offload(skb); struct xfrm_offload *xo = xfrm_offload(skb);
...@@ -56,7 +64,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) ...@@ -56,7 +64,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
dev_net(skb->dev), NULL, skb, skb->dev, NULL, dev_net(skb->dev), NULL, skb, skb->dev, NULL,
ip6_rcv_finish); xfrm6_transport_finish2);
return -1; return -1;
} }
......
...@@ -8,15 +8,29 @@ ...@@ -8,15 +8,29 @@
* *
*/ */
#include <linux/bottom_half.h>
#include <linux/interrupt.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/percpu.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ip_tunnels.h> #include <net/ip_tunnels.h>
#include <net/ip6_tunnel.h> #include <net/ip6_tunnel.h>
struct xfrm_trans_tasklet {
struct tasklet_struct tasklet;
struct sk_buff_head queue;
};
struct xfrm_trans_cb {
int (*finish)(struct net *net, struct sock *sk, struct sk_buff *skb);
};
#define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0]))
static struct kmem_cache *secpath_cachep __read_mostly; static struct kmem_cache *secpath_cachep __read_mostly;
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
...@@ -25,6 +39,8 @@ static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1]; ...@@ -25,6 +39,8 @@ static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1];
static struct gro_cells gro_cells; static struct gro_cells gro_cells;
static struct net_device xfrm_napi_dev; static struct net_device xfrm_napi_dev;
static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet);
int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo) int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo)
{ {
int err = 0; int err = 0;
...@@ -207,7 +223,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -207,7 +223,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
xfrm_address_t *daddr; xfrm_address_t *daddr;
struct xfrm_mode *inner_mode; struct xfrm_mode *inner_mode;
u32 mark = skb->mark; u32 mark = skb->mark;
unsigned int family; unsigned int family = AF_UNSPEC;
int decaps = 0; int decaps = 0;
int async = 0; int async = 0;
bool xfrm_gro = false; bool xfrm_gro = false;
...@@ -216,6 +232,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -216,6 +232,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (encap_type < 0) { if (encap_type < 0) {
x = xfrm_input_state(skb); x = xfrm_input_state(skb);
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
if (x->km.state == XFRM_STATE_ACQ)
XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
else
XFRM_INC_STATS(net,
LINUX_MIB_XFRMINSTATEINVALID);
goto drop;
}
family = x->outer_mode->afinfo->family; family = x->outer_mode->afinfo->family;
/* An encap_type of -1 indicates async resumption. */ /* An encap_type of -1 indicates async resumption. */
...@@ -467,9 +493,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr) ...@@ -467,9 +493,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
} }
EXPORT_SYMBOL(xfrm_input_resume); EXPORT_SYMBOL(xfrm_input_resume);
static void xfrm_trans_reinject(unsigned long data)
{
struct xfrm_trans_tasklet *trans = (void *)data;
struct sk_buff_head queue;
struct sk_buff *skb;
__skb_queue_head_init(&queue);
skb_queue_splice_init(&trans->queue, &queue);
while ((skb = __skb_dequeue(&queue)))
XFRM_TRANS_SKB_CB(skb)->finish(dev_net(skb->dev), NULL, skb);
}
int xfrm_trans_queue(struct sk_buff *skb,
int (*finish)(struct net *, struct sock *,
struct sk_buff *))
{
struct xfrm_trans_tasklet *trans;
trans = this_cpu_ptr(&xfrm_trans_tasklet);
if (skb_queue_len(&trans->queue) >= netdev_max_backlog)
return -ENOBUFS;
XFRM_TRANS_SKB_CB(skb)->finish = finish;
skb_queue_tail(&trans->queue, skb);
tasklet_schedule(&trans->tasklet);
return 0;
}
EXPORT_SYMBOL(xfrm_trans_queue);
void __init xfrm_input_init(void) void __init xfrm_input_init(void)
{ {
int err; int err;
int i;
init_dummy_netdev(&xfrm_napi_dev); init_dummy_netdev(&xfrm_napi_dev);
err = gro_cells_init(&gro_cells, &xfrm_napi_dev); err = gro_cells_init(&gro_cells, &xfrm_napi_dev);
...@@ -480,4 +538,13 @@ void __init xfrm_input_init(void) ...@@ -480,4 +538,13 @@ void __init xfrm_input_init(void)
sizeof(struct sec_path), sizeof(struct sec_path),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL); NULL);
for_each_possible_cpu(i) {
struct xfrm_trans_tasklet *trans;
trans = &per_cpu(xfrm_trans_tasklet, i);
__skb_queue_head_init(&trans->queue);
tasklet_init(&trans->tasklet, xfrm_trans_reinject,
(unsigned long)trans);
}
} }
...@@ -1168,9 +1168,15 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, ...@@ -1168,9 +1168,15 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
again: again:
pol = rcu_dereference(sk->sk_policy[dir]); pol = rcu_dereference(sk->sk_policy[dir]);
if (pol != NULL) { if (pol != NULL) {
bool match = xfrm_selector_match(&pol->selector, fl, family); bool match;
int err = 0; int err = 0;
if (pol->family != family) {
pol = NULL;
goto out;
}
match = xfrm_selector_match(&pol->selector, fl, family);
if (match) { if (match) {
if ((sk->sk_mark & pol->mark.m) != pol->mark.v) { if ((sk->sk_mark & pol->mark.m) != pol->mark.v) {
pol = NULL; pol = NULL;
...@@ -1833,6 +1839,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, ...@@ -1833,6 +1839,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
sizeof(struct xfrm_policy *) * num_pols) == 0 && sizeof(struct xfrm_policy *) * num_pols) == 0 &&
xfrm_xdst_can_reuse(xdst, xfrm, err)) { xfrm_xdst_can_reuse(xdst, xfrm, err)) {
dst_hold(&xdst->u.dst); dst_hold(&xdst->u.dst);
xfrm_pols_put(pols, num_pols);
while (err > 0) while (err > 0)
xfrm_state_put(xfrm[--err]); xfrm_state_put(xfrm[--err]);
return xdst; return xdst;
......
...@@ -1343,6 +1343,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, ...@@ -1343,6 +1343,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
if (orig->aead) { if (orig->aead) {
x->aead = xfrm_algo_aead_clone(orig->aead); x->aead = xfrm_algo_aead_clone(orig->aead);
x->geniv = orig->geniv;
if (!x->aead) if (!x->aead)
goto error; goto error;
} }
......
...@@ -1419,11 +1419,14 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, ...@@ -1419,11 +1419,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) static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
{ {
u16 prev_family;
int i; int i;
if (nr > XFRM_MAX_DEPTH) if (nr > XFRM_MAX_DEPTH)
return -EINVAL; return -EINVAL;
prev_family = family;
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
/* We never validated the ut->family value, so many /* We never validated the ut->family value, so many
* applications simply leave it at zero. The check was * applications simply leave it at zero. The check was
...@@ -1435,6 +1438,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) ...@@ -1435,6 +1438,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
if (!ut[i].family) if (!ut[i].family)
ut[i].family = family; ut[i].family = family;
if ((ut[i].mode == XFRM_MODE_TRANSPORT) &&
(ut[i].family != prev_family))
return -EINVAL;
prev_family = ut[i].family;
switch (ut[i].family) { switch (ut[i].family) {
case AF_INET: case AF_INET:
break; break;
...@@ -1445,6 +1454,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) ...@@ -1445,6 +1454,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
default: default:
return -EINVAL; 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; return 0;
...@@ -2470,7 +2494,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { ...@@ -2470,7 +2494,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_PROTO] = { .type = NLA_U8 }, [XFRMA_PROTO] = { .type = NLA_U8 },
[XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
[XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) },
[XFRMA_OUTPUT_MARK] = { .len = NLA_U32 }, [XFRMA_OUTPUT_MARK] = { .type = NLA_U32 },
}; };
static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment