Skip to content

Commit

Permalink
Fix XFRM-I support for nested ESP tunnels
Browse files Browse the repository at this point in the history
[ Upstream commit b0355db ]

This change adds support for nested IPsec tunnels by ensuring that
XFRM-I verifies existing policies before decapsulating a subsequent
policies. Addtionally, this clears the secpath entries after policies
are verified, ensuring that previous tunnels with no-longer-valid
do not pollute subsequent policy checks.

This is necessary especially for nested tunnels, as the IP addresses,
protocol and ports may all change, thus not matching the previous
policies. In order to ensure that packets match the relevant inbound
templates, the xfrm_policy_check should be done before handing off to
the inner XFRM protocol to decrypt and decapsulate.

Notably, raw ESP/AH packets did not perform policy checks inherently,
whereas all other encapsulated packets (UDP, TCP encapsulated) do policy
checks after calling xfrm_input handling in the respective encapsulation
layer.

Test: Verified with additional Android Kernel Unit tests
Signed-off-by: Benedict Wong <benedictwong@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Benedict Wong authored and gregkh committed Mar 3, 2023
1 parent 7d54cb2 commit 0b892d8
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 4 deletions.
54 changes: 50 additions & 4 deletions net/xfrm/xfrm_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
skb->mark = 0;
}

static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi,
int encap_type, unsigned short family)
{
struct sec_path *sp;

sp = skb_sec_path(skb);
if (sp && (sp->len || sp->olen) &&
!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
goto discard;

XFRM_SPI_SKB_CB(skb)->family = family;
if (family == AF_INET) {
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
} else {
XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
}

return xfrm_input(skb, nexthdr, spi, encap_type);
discard:
kfree_skb(skb);
return 0;
}

static int xfrmi4_rcv(struct sk_buff *skb)
{
return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET);
}

static int xfrmi6_rcv(struct sk_buff *skb)
{
return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
0, 0, AF_INET6);
}

static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET);
}

static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6);
}

static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{
const struct xfrm_mode *inner_mode;
Expand Down Expand Up @@ -937,8 +983,8 @@ static struct pernet_operations xfrmi_net_ops = {
};

static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = {
.handler = xfrm6_rcv,
.input_handler = xfrm_input,
.handler = xfrmi6_rcv,
.input_handler = xfrmi6_input,
.cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi6_err,
.priority = 10,
Expand Down Expand Up @@ -988,8 +1034,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = {
#endif

static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
.handler = xfrm4_rcv,
.input_handler = xfrm_input,
.handler = xfrmi4_rcv,
.input_handler = xfrmi4_input,
.cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi4_err,
.priority = 10,
Expand Down
3 changes: 3 additions & 0 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3670,6 +3670,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
goto reject;
}

if (if_id)
secpath_reset(skb);

xfrm_pols_put(pols, npols);
return 1;
}
Expand Down

0 comments on commit 0b892d8

Please sign in to comment.