Skip to content

Commit

Permalink
xfrm: Add possibility to set the default to block if we have no policy
Browse files Browse the repository at this point in the history
[ Upstream commit 2d151d3 ]

As the default we assume the traffic to pass, if we have no
matching IPsec policy. With this patch, we have a possibility to
change this default from allow to block. It can be configured
via netlink. Each direction (input/output/forward) can be
configured separately. With the default to block configuered,
we need allow policies for all packet flows we accept.
We do not use default policy lookup for the loopback device.

v1->v2
 - fix compiling when XFRM is disabled
 - Reported-by: kernel test robot <lkp@intel.com>

Co-developed-by: Christian Langrock <christian.langrock@secunet.com>
Signed-off-by: Christian Langrock <christian.langrock@secunet.com>
Co-developed-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
klassert authored and gregkh committed May 25, 2022
1 parent 243e72e commit 5b7f84b
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 6 deletions.
7 changes: 7 additions & 0 deletions include/net/netns/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ struct netns_xfrm {
u32 sysctl_aevent_rseqth;
int sysctl_larval_drop;
u32 sysctl_acq_expires;

u8 policy_default;
#define XFRM_POL_DEFAULT_IN 1
#define XFRM_POL_DEFAULT_OUT 2
#define XFRM_POL_DEFAULT_FWD 4
#define XFRM_POL_DEFAULT_MASK 7

#ifdef CONFIG_SYSCTL
struct ctl_table_header *sysctl_hdr;
#endif
Expand Down
36 changes: 30 additions & 6 deletions include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,22 @@ xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, un
}

#ifdef CONFIG_XFRM
static inline bool
xfrm_default_allow(struct net *net, int dir)
{
u8 def = net->xfrm.policy_default;

switch (dir) {
case XFRM_POLICY_IN:
return def & XFRM_POL_DEFAULT_IN ? false : true;
case XFRM_POLICY_OUT:
return def & XFRM_POL_DEFAULT_OUT ? false : true;
case XFRM_POLICY_FWD:
return def & XFRM_POL_DEFAULT_FWD ? false : true;
}
return false;
}

int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb,
unsigned short family);

Expand All @@ -1101,9 +1117,13 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
if (sk && sk->sk_policy[XFRM_POLICY_IN])
return __xfrm_policy_check(sk, ndir, skb, family);

return (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) ||
(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
__xfrm_policy_check(sk, ndir, skb, family);
if (xfrm_default_allow(net, dir))
return (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) ||
(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
__xfrm_policy_check(sk, ndir, skb, family);
else
return (skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
__xfrm_policy_check(sk, ndir, skb, family);
}

static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, unsigned short family)
Expand Down Expand Up @@ -1155,9 +1175,13 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
struct net *net = dev_net(skb->dev);

return !net->xfrm.policy_count[XFRM_POLICY_OUT] ||
(skb_dst(skb)->flags & DST_NOXFRM) ||
__xfrm_route_forward(skb, family);
if (xfrm_default_allow(net, XFRM_POLICY_FWD))
return !net->xfrm.policy_count[XFRM_POLICY_OUT] ||
(skb_dst(skb)->flags & DST_NOXFRM) ||
__xfrm_route_forward(skb, family);
else
return (skb_dst(skb)->flags & DST_NOXFRM) ||
__xfrm_route_forward(skb, family);
}

static inline int xfrm4_route_forward(struct sk_buff *skb)
Expand Down
10 changes: 10 additions & 0 deletions include/uapi/linux/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ enum {
XFRM_MSG_GETSPDINFO,
#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO

XFRM_MSG_SETDEFAULT,
#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT
XFRM_MSG_GETDEFAULT,
#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT

XFRM_MSG_MAPPING,
#define XFRM_MSG_MAPPING XFRM_MSG_MAPPING
__XFRM_MSG_MAX
Expand Down Expand Up @@ -515,6 +520,11 @@ struct xfrm_user_offload {
#define XFRM_OFFLOAD_IPV6 1
#define XFRM_OFFLOAD_INBOUND 2

struct xfrm_userpolicy_default {
__u8 dirmask;
__u8 action;
};

#ifndef __KERNEL__
/* backwards compatibility for userspace */
#define XFRMGRP_ACQUIRE 1
Expand Down
16 changes: 16 additions & 0 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3161,6 +3161,11 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
return dst;

nopol:
if (!(dst_orig->dev->flags & IFF_LOOPBACK) &&
!xfrm_default_allow(net, dir)) {
err = -EPERM;
goto error;
}
if (!(flags & XFRM_LOOKUP_ICMP)) {
dst = dst_orig;
goto ok;
Expand Down Expand Up @@ -3608,6 +3613,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}

if (!pol) {
if (!xfrm_default_allow(net, dir)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0;
}

if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) {
xfrm_secpath_reject(xerr_idx, skb, &fl);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
Expand Down Expand Up @@ -3662,6 +3672,12 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
tpp[ti++] = &pols[pi]->xfrm_vec[i];
}
xfrm_nr = ti;

if (!xfrm_default_allow(net, dir) && !xfrm_nr) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
goto reject;
}

if (npols > 1) {
xfrm_tmpl_sort(stp, tpp, xfrm_nr, family);
tpp = stp;
Expand Down
52 changes: 52 additions & 0 deletions net/xfrm/xfrm_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -1914,6 +1914,54 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
return skb;
}

static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_default *up = nlmsg_data(nlh);
u8 dirmask = (1 << up->dirmask) & XFRM_POL_DEFAULT_MASK;
u8 old_default = net->xfrm.policy_default;

net->xfrm.policy_default = (old_default & (0xff ^ dirmask))
| (up->action << up->dirmask);

rt_genid_bump_all(net);

return 0;
}

static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
struct sk_buff *r_skb;
struct nlmsghdr *r_nlh;
struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_default *r_up, *up;
int len = NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_default));
u32 portid = NETLINK_CB(skb).portid;
u32 seq = nlh->nlmsg_seq;

up = nlmsg_data(nlh);

r_skb = nlmsg_new(len, GFP_ATOMIC);
if (!r_skb)
return -ENOMEM;

r_nlh = nlmsg_put(r_skb, portid, seq, XFRM_MSG_GETDEFAULT, sizeof(*r_up), 0);
if (!r_nlh) {
kfree_skb(r_skb);
return -EMSGSIZE;
}

r_up = nlmsg_data(r_nlh);

r_up->action = ((net->xfrm.policy_default & (1 << up->dirmask)) >> up->dirmask);
r_up->dirmask = up->dirmask;
nlmsg_end(r_skb, r_nlh);

return nlmsg_unicast(net->xfrm.nlsk, r_skb, portid);
}

static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
Expand Down Expand Up @@ -2621,6 +2669,8 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
};
EXPORT_SYMBOL_GPL(xfrm_msg_min);

Expand Down Expand Up @@ -2700,6 +2750,8 @@ static const struct xfrm_link {
.nla_pol = xfrma_spd_policy,
.nla_max = XFRMA_SPD_MAX },
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
};

static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
Expand Down

0 comments on commit 5b7f84b

Please sign in to comment.