xfrm: Ensure policies always checked on XFRM-I input path
authorBenedict Wong <benedictwong@google.com>
Wed, 10 May 2023 01:30:22 +0000 (01:30 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Jun 2023 09:12:28 +0000 (11:12 +0200)
[ Upstream commit a287f5b0cfc6804c5b12a4be13c7c9fe27869e90 ]

This change adds methods in the XFRM-I input path that ensures that
policies are checked prior to processing of the subsequent decapsulated
packet, after which the relevant policies may no longer be resolvable
(due to changing src/dst/proto/etc).

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.

Fixes: b0355dbbf13c ("Fix XFRM-I support for nested ESP tunnels")
Test: Verified with additional Android Kernel Unit tests
Test: Verified against Android CTS
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>
net/xfrm/xfrm_interface_core.c

index 5a67b12..94a3609 100644 (file)
@@ -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;
@@ -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,
@@ -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,