netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH ipsec 0/2] xfrm: Fix bugs in stacked XFRM-I tunnels
@ 2022-08-10 18:22 Benedict Wong
  2022-08-10 18:22 ` [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input Benedict Wong
  2022-08-10 18:22 ` [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries Benedict Wong
  0 siblings, 2 replies; 6+ messages in thread
From: Benedict Wong @ 2022-08-10 18:22 UTC (permalink / raw)
  To: steffen.klassert, netdev; +Cc: nharold, benedictwong, lorenzo

This patch set fixes bugs that prevent stacked IPsec tunnels (via XFRM
interfaces) from receiving packets properly. The apparent cause of the
issues is that the inner tunnel’s policy checks fail to validate the
outer tunnel’s secpath entries (since it no longer has a reference to
the outer tunnel policies, and each call validates ALL secpath entries)
prior to verifying the inner tunnel’s. This patch set fixes this by
caching the list of verified secpath entries, and skipping them upon
future validation runs.

PATCH 1/2 Ensures that policies for nested tunnel mode transforms are
checked before additional decapsulation. This ensures that entries in
the secpath are verified while the context (intermediate IP addresses,
marks, etc) can be appropriately matched.

PATCH 2/2 Skips template matching for previously verified entries in
the secpath. This ensures that each tunnel is responsible for
incrementally verifying the secpath entries associated with it.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input
  2022-08-10 18:22 [PATCH ipsec 0/2] xfrm: Fix bugs in stacked XFRM-I tunnels Benedict Wong
@ 2022-08-10 18:22 ` Benedict Wong
  2022-08-15  8:45   ` Steffen Klassert
  2022-08-10 18:22 ` [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries Benedict Wong
  1 sibling, 1 reply; 6+ messages in thread
From: Benedict Wong @ 2022-08-10 18:22 UTC (permalink / raw)
  To: steffen.klassert, netdev; +Cc: nharold, benedictwong, lorenzo

This change ensures that all nested XFRM packets have their policy
checked before decryption of the next layer, so that policies are
verified at each intermediate step of the decryption process.

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.

Test: Tested against Android Kernel Unit Tests
Signed-off-by: Benedict Wong <benedictwong@google.com>
Change-Id: I20c5abf39512d7f6cf438c0921a78a84e281b4e9
---
 net/xfrm/xfrm_input.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 144238a50f3d..b24df8a44585 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -585,6 +585,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 			goto drop;
 		}
 
+		// If nested tunnel, check outer states before context is lost.
+		if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL
+				&& sp->len > 0
+				&& !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) {
+			goto drop;
+		}
+
 		skb->mark = xfrm_smark_get(skb->mark, x);
 
 		sp->xvec[sp->len++] = x;
-- 
2.37.1.595.g718a3a8f04-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries
  2022-08-10 18:22 [PATCH ipsec 0/2] xfrm: Fix bugs in stacked XFRM-I tunnels Benedict Wong
  2022-08-10 18:22 ` [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input Benedict Wong
@ 2022-08-10 18:22 ` Benedict Wong
  2022-08-15  8:50   ` Steffen Klassert
  1 sibling, 1 reply; 6+ messages in thread
From: Benedict Wong @ 2022-08-10 18:22 UTC (permalink / raw)
  To: steffen.klassert, netdev; +Cc: nharold, benedictwong, lorenzo

This change fixes a bug where inbound packets to nested IPsec tunnels
fails to pass policy checks due to the inner tunnel's policy checks
not having a reference to the outer policy/template. This causes the
policy check to fail, since the first entries in the secpath correlate
to the outer tunnel, while the templates being verified are for the
inner tunnel.

In order to ensure that the appropriate policy and template context is
searchable, the policy checks must be done incrementally after each
decryption step. As such, this marks secpath entries as having been
successfully matched, skipping these on subsequent policy checks.

By skipping the immediate error return in the case where the secpath
entry had previously been validated, this change allows secpath entries
that matched a policy/template previously, while still requiring that
each searched template find a match in the secpath.

For security:
- All templates must have matching secpath entries
  - Unchanged by current patch; templates that do not match any secpath
    entry still return -1. This patch simply allows skipping earlier
    blocks of verified secpath entries
- All entries (except trailing transport mode entries) must have a
  matching template
  - Unvalidated entries, including transport-mode entries still return
    the errored index if it does not match the correct template.

Test: Tested against Android Kernel Unit Tests
Signed-off-by: Benedict Wong <benedictwong@google.com>
Change-Id: Ic32831cb00151d0de2e465f18ec37d5f7b680e54
---
 include/net/xfrm.h     |  1 +
 net/xfrm/xfrm_input.c  |  3 ++-
 net/xfrm/xfrm_policy.c | 11 ++++++++++-
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index c39d910d4b45..a2f2840aba6b 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1031,6 +1031,7 @@ struct xfrm_offload {
 struct sec_path {
 	int			len;
 	int			olen;
+	int			verified_cnt;
 
 	struct xfrm_state	*xvec[XFRM_MAX_DEPTH];
 	struct xfrm_offload	ovec[XFRM_MAX_OFFLOAD_DEPTH];
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index b24df8a44585..895935077a91 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -129,6 +129,7 @@ struct sec_path *secpath_set(struct sk_buff *skb)
 	memset(sp->ovec, 0, sizeof(sp->ovec));
 	sp->olen = 0;
 	sp->len = 0;
+	sp->verified_cnt = 0;
 
 	return sp;
 }
@@ -587,7 +588,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 
 		// If nested tunnel, check outer states before context is lost.
 		if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL
-				&& sp->len > 0
+				&& sp->len > sp->verified_cnt
 				&& !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) {
 			goto drop;
 		}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index f1a0bab920a5..ee620a856c6f 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3261,7 +3261,7 @@ xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x,
  */
 static inline int
 xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int start,
-	       unsigned short family)
+			   unsigned short family)
 {
 	int idx = start;
 
@@ -3274,6 +3274,11 @@ xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int star
 		if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
 			return ++idx;
 		if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
+			if (idx < sp->verified_cnt) {
+				// Secpath entry previously verified, continue searching
+				continue;
+			}
+
 			if (start == -1)
 				start = -2-idx;
 			break;
@@ -3650,6 +3655,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 		 * Order is _important_. Later we will implement
 		 * some barriers, but at the moment barriers
 		 * are implied between each two transformations.
+		 * Skips verifying secpath entries that have already been
+		 * verified in the past.
 		 */
 		for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
 			k = xfrm_policy_ok(tpp[i], sp, k, family);
@@ -3668,6 +3675,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 		}
 
 		xfrm_pols_put(pols, npols);
+		sp->verified_cnt = k;
+
 		return 1;
 	}
 	XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
-- 
2.37.1.595.g718a3a8f04-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input
  2022-08-10 18:22 ` [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input Benedict Wong
@ 2022-08-15  8:45   ` Steffen Klassert
       [not found]     ` <CANrj0baLB5a5QpdmmcNYZLyxe1r0gySLhT3krXVFXKOzBb8aww@mail.gmail.com>
  0 siblings, 1 reply; 6+ messages in thread
From: Steffen Klassert @ 2022-08-15  8:45 UTC (permalink / raw)
  To: Benedict Wong; +Cc: netdev, nharold, lorenzo

On Wed, Aug 10, 2022 at 06:22:09PM +0000, Benedict Wong wrote:
> This change ensures that all nested XFRM packets have their policy
> checked before decryption of the next layer, so that policies are
> verified at each intermediate step of the decryption process.
> 
> 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.
> 
> Test: Tested against Android Kernel Unit Tests
> Signed-off-by: Benedict Wong <benedictwong@google.com>
> Change-Id: I20c5abf39512d7f6cf438c0921a78a84e281b4e9
> ---
>  net/xfrm/xfrm_input.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
> index 144238a50f3d..b24df8a44585 100644
> --- a/net/xfrm/xfrm_input.c
> +++ b/net/xfrm/xfrm_input.c
> @@ -585,6 +585,13 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>  			goto drop;
>  		}
>  
> +		// If nested tunnel, check outer states before context is lost.

Please use networking style comments like so /* ... */

> +		if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL
> +				&& sp->len > 0

Please align this to the opening brace of the if statement
like it is done everywhere in networking code. If you are
unsure about coding style, try checkpatch it helps in that
case.

> +				&& !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) {

Hm, shouldn't the xfrm_policy_check called along the
packet path for each round after decapsulation?

Do you use ESP transformation offload (INET_ESP_OFFLOAD/
INET6_ESP_OFFLOAD)?

> +			goto drop;
> +		}
> +
>  		skb->mark = xfrm_smark_get(skb->mark, x);
>  
>  		sp->xvec[sp->len++] = x;
> -- 
> 2.37.1.595.g718a3a8f04-goog

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries
  2022-08-10 18:22 ` [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries Benedict Wong
@ 2022-08-15  8:50   ` Steffen Klassert
  0 siblings, 0 replies; 6+ messages in thread
From: Steffen Klassert @ 2022-08-15  8:50 UTC (permalink / raw)
  To: Benedict Wong; +Cc: netdev, nharold, lorenzo

On Wed, Aug 10, 2022 at 06:22:10PM +0000, Benedict Wong wrote:
> This change fixes a bug where inbound packets to nested IPsec tunnels
> fails to pass policy checks due to the inner tunnel's policy checks
> not having a reference to the outer policy/template. This causes the
> policy check to fail, since the first entries in the secpath correlate
> to the outer tunnel, while the templates being verified are for the
> inner tunnel.
> 
> In order to ensure that the appropriate policy and template context is
> searchable, the policy checks must be done incrementally after each
> decryption step. As such, this marks secpath entries as having been
> successfully matched, skipping these on subsequent policy checks.
> 
> By skipping the immediate error return in the case where the secpath
> entry had previously been validated, this change allows secpath entries
> that matched a policy/template previously, while still requiring that
> each searched template find a match in the secpath.
> 
> For security:
> - All templates must have matching secpath entries
>   - Unchanged by current patch; templates that do not match any secpath
>     entry still return -1. This patch simply allows skipping earlier
>     blocks of verified secpath entries
> - All entries (except trailing transport mode entries) must have a
>   matching template
>   - Unvalidated entries, including transport-mode entries still return
>     the errored index if it does not match the correct template.
> 
> Test: Tested against Android Kernel Unit Tests
> Signed-off-by: Benedict Wong <benedictwong@google.com>
> Change-Id: Ic32831cb00151d0de2e465f18ec37d5f7b680e54

This ID is meaningless on a mainline kernel, please remove it.

> ---
>  include/net/xfrm.h     |  1 +
>  net/xfrm/xfrm_input.c  |  3 ++-
>  net/xfrm/xfrm_policy.c | 11 ++++++++++-
>  3 files changed, 13 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net/xfrm.h b/include/net/xfrm.h
> index c39d910d4b45..a2f2840aba6b 100644
> --- a/include/net/xfrm.h
> +++ b/include/net/xfrm.h
> @@ -1031,6 +1031,7 @@ struct xfrm_offload {
>  struct sec_path {
>  	int			len;
>  	int			olen;
> +	int			verified_cnt;
>  
>  	struct xfrm_state	*xvec[XFRM_MAX_DEPTH];
>  	struct xfrm_offload	ovec[XFRM_MAX_OFFLOAD_DEPTH];
> diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
> index b24df8a44585..895935077a91 100644
> --- a/net/xfrm/xfrm_input.c
> +++ b/net/xfrm/xfrm_input.c
> @@ -129,6 +129,7 @@ struct sec_path *secpath_set(struct sk_buff *skb)
>  	memset(sp->ovec, 0, sizeof(sp->ovec));
>  	sp->olen = 0;
>  	sp->len = 0;
> +	sp->verified_cnt = 0;
>  
>  	return sp;
>  }
> @@ -587,7 +588,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>  
>  		// If nested tunnel, check outer states before context is lost.

Please use networking style comments here too.

>  		if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL
> -				&& sp->len > 0
> +				&& sp->len > sp->verified_cnt
>  				&& !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) {

As in the first patch, please use common networking code
alignment.

Thanks!

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input
       [not found]     ` <CANrj0baLB5a5QpdmmcNYZLyxe1r0gySLhT3krXVFXKOzBb8aww@mail.gmail.com>
@ 2022-08-16  8:53       ` Steffen Klassert
  0 siblings, 0 replies; 6+ messages in thread
From: Steffen Klassert @ 2022-08-16  8:53 UTC (permalink / raw)
  To: Benedict Wong; +Cc: netdev, nharold, lorenzo

On Mon, Aug 15, 2022 at 02:25:42PM -0700, Benedict Wong wrote:
> >
> > Hm, shouldn't the xfrm_policy_check called along the
> > packet path for each round after decapsulation?
> >
> > Do you use ESP transformation offload (INET_ESP_OFFLOAD/
> > INET6_ESP_OFFLOAD)?
> 
> Been a while since I've gotten a chance to look through the
> code, but when I previously looked through the stack, it looked
> like we have policy checks in the following places:
> - IPv4/IPv6 deliver to host
> - UDP/TCP/ICMP/L2TP/SCTP/VTI/raw in direct rcv methods
> 
> Additionally, we have a conditional check in XFRM-I, but
> *only if the packet is crossing network namespaces* (which
> in the Android case, it isn't)

Yes, this is because the secpath is cleared when crossing
network namespaces. The inbound policy check in the packet
path would fail in this case. That's why we do the policy
check there.

> Notably, it appears that the missing case is when the outer
> tunnel is an unencap'd ESP packet, which simply calls xfrm_input
> via xfrm(4|6)_rcv_spi. This changes adds that call to ensure
> that the verification is always performed in each packet path.

Please note that all policy checks are done for the traffic
selector of the inner packets. The inbound policy check makes
sure that the inner packets are allowed to pass and really
came through the SA that is recorded in the secpath.

When receiving an ESP packet, the packets IPsec ID (daddr/
SPI/proto) is mached against the SADB. If a matching SA is
there, it is used to decapsulate. The TS of the decapsulated
packet is used to do the policy lookup then.

If the decapsulated packet is not dropped by the policy lookup
and is again an ESP packet, we start with the SADB lookup as
described above.

So I think the behaviour is correct as it is implemented.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-08-16 10:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-10 18:22 [PATCH ipsec 0/2] xfrm: Fix bugs in stacked XFRM-I tunnels Benedict Wong
2022-08-10 18:22 ` [PATCH ipsec 1/2] xfrm: Check policy for nested XFRM packets in xfrm_input Benedict Wong
2022-08-15  8:45   ` Steffen Klassert
     [not found]     ` <CANrj0baLB5a5QpdmmcNYZLyxe1r0gySLhT3krXVFXKOzBb8aww@mail.gmail.com>
2022-08-16  8:53       ` Steffen Klassert
2022-08-10 18:22 ` [PATCH ipsec 2/2] xfrm: Skip checking of already-verified secpath entries Benedict Wong
2022-08-15  8:50   ` Steffen Klassert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).