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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
65bbbf6c20
7 changed files with 125 additions and 5 deletions
|
@ -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_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
|
||||
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(struct sock *sk, 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)
|
|||
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,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
@ -33,7 +39,11 @@ static inline int xfrm4_rcv_encap_finish(struct net *net, struct sock *sk,
|
|||
iph->tos, skb->dev))
|
||||
goto drop;
|
||||
}
|
||||
return dst_input(skb);
|
||||
|
||||
if (xfrm_trans_queue(skb, xfrm4_rcv_encap_finish2))
|
||||
goto drop;
|
||||
|
||||
return 0;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
|
|
|
@ -32,6 +32,14 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 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)
|
||||
{
|
||||
struct xfrm_offload *xo = xfrm_offload(skb);
|
||||
|
@ -56,7 +64,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
|
|||
|
||||
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
|
||||
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
|
||||
ip6_rcv_finish);
|
||||
xfrm6_transport_finish2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,29 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/bottom_half.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <net/dst.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/ip_tunnels.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 DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
|
||||
|
@ -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 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 err = 0;
|
||||
|
@ -207,7 +223,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||
xfrm_address_t *daddr;
|
||||
struct xfrm_mode *inner_mode;
|
||||
u32 mark = skb->mark;
|
||||
unsigned int family;
|
||||
unsigned int family = AF_UNSPEC;
|
||||
int decaps = 0;
|
||||
int async = 0;
|
||||
bool xfrm_gro = false;
|
||||
|
@ -216,6 +232,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||
|
||||
if (encap_type < 0) {
|
||||
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;
|
||||
|
||||
/* An encap_type of -1 indicates async resumption. */
|
||||
|
@ -467,9 +493,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
|
|||
}
|
||||
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)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
|
||||
init_dummy_netdev(&xfrm_napi_dev);
|
||||
err = gro_cells_init(&gro_cells, &xfrm_napi_dev);
|
||||
|
@ -480,4 +538,13 @@ void __init xfrm_input_init(void)
|
|||
sizeof(struct sec_path),
|
||||
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
|
||||
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,
|
|||
again:
|
||||
pol = rcu_dereference(sk->sk_policy[dir]);
|
||||
if (pol != NULL) {
|
||||
bool match = xfrm_selector_match(&pol->selector, fl, family);
|
||||
bool match;
|
||||
int err = 0;
|
||||
|
||||
if (pol->family != family) {
|
||||
pol = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
match = xfrm_selector_match(&pol->selector, fl, family);
|
||||
if (match) {
|
||||
if ((sk->sk_mark & pol->mark.m) != pol->mark.v) {
|
||||
pol = NULL;
|
||||
|
@ -1833,6 +1839,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
|||
sizeof(struct xfrm_policy *) * num_pols) == 0 &&
|
||||
xfrm_xdst_can_reuse(xdst, xfrm, err)) {
|
||||
dst_hold(&xdst->u.dst);
|
||||
xfrm_pols_put(pols, num_pols);
|
||||
while (err > 0)
|
||||
xfrm_state_put(xfrm[--err]);
|
||||
return xdst;
|
||||
|
|
|
@ -1343,6 +1343,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
|
|||
|
||||
if (orig->aead) {
|
||||
x->aead = xfrm_algo_aead_clone(orig->aead);
|
||||
x->geniv = orig->geniv;
|
||||
if (!x->aead)
|
||||
goto error;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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
|
||||
|
@ -1435,6 +1438,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
|
|||
if (!ut[i].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) {
|
||||
case AF_INET:
|
||||
break;
|
||||
|
@ -1445,6 +1454,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;
|
||||
|
@ -2470,7 +2494,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
|
|||
[XFRMA_PROTO] = { .type = NLA_U8 },
|
||||
[XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
|
||||
[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] = {
|
||||
|
|
Loading…
Reference in a new issue