strongswan: add fix for CVE-2022-40617 20678/head
authorFlorian Eckert <fe@dev.tdt.de>
Wed, 15 Mar 2023 07:19:50 +0000 (08:19 +0100)
committerFlorian Eckert <fe@dev.tdt.de>
Thu, 16 Mar 2023 13:59:50 +0000 (14:59 +0100)
Full details of the CVE can be found at the following link:
https://www.strongswan.org/blog/2022/10/03/strongswan-vulnerability-(cve-2022-40617).html

Signed-off-by: Florian Eckert <fe@dev.tdt.de>
net/strongswan/Makefile
net/strongswan/patches/730-strongswan-5.1.0-5.9.7_cert_online_validate.patch [new file with mode: 0644]

index 8863a928cdbc79ce6e39c42354b936a2e604b8d5..f0c08439c16fe8142daf0daf89a93338ae204348 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=strongswan
 PKG_VERSION:=5.9.5
-PKG_RELEASE:=$(AUTORELEASE).1
+PKG_RELEASE:=13
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://download.strongswan.org/ https://download2.strongswan.org/
diff --git a/net/strongswan/patches/730-strongswan-5.1.0-5.9.7_cert_online_validate.patch b/net/strongswan/patches/730-strongswan-5.1.0-5.9.7_cert_online_validate.patch
new file mode 100644 (file)
index 0000000..71c103e
--- /dev/null
@@ -0,0 +1,200 @@
+From 66d3b2e0e596a6eac1ebcd15c83a8d9368fe7b34 Mon Sep 17 00:00:00 2001
+From: Tobias Brunner <tobias@strongswan.org>
+Date: Fri, 22 Jul 2022 15:37:43 +0200
+Subject: [PATCH] credential-manager: Do online revocation checks only after
+ basic trust chain validation
+
+This avoids querying URLs of potentially untrusted certificates, e.g. if
+an attacker sends a specially crafted end-entity and intermediate CA
+certificate with a CDP that points to a server that completes the
+TCP handshake but then does not send any further data, which will block
+the fetcher thread (depending on the plugin) for as long as the default
+timeout for TCP.  Doing that multiple times will block all worker threads,
+leading to a DoS attack.
+
+The logging during the certificate verification obviously changes.  The
+following example shows the output of `pki --verify` for the current
+strongswan.org certificate:
+
+new:
+
+  using certificate "CN=www.strongswan.org"
+  using trusted intermediate ca certificate "C=US, O=Let's Encrypt, CN=R3"
+  using trusted ca certificate "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+  reached self-signed root ca with a path length of 1
+checking certificate status of "CN=www.strongswan.org"
+  requesting ocsp status from 'http://r3.o.lencr.org' ...
+  ocsp response correctly signed by "C=US, O=Let's Encrypt, CN=R3"
+  ocsp response is valid: until Jul 27 12:59:58 2022
+certificate status is good
+checking certificate status of "C=US, O=Let's Encrypt, CN=R3"
+ocsp response verification failed, no signer certificate 'C=US, O=Let's Encrypt, CN=R3' found
+  fetching crl from 'http://x1.c.lencr.org/' ...
+  using trusted certificate "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+  crl correctly signed by "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+  crl is valid: until Apr 18 01:59:59 2023
+certificate status is good
+certificate trusted, lifetimes valid, certificate not revoked
+
+old:
+
+  using certificate "CN=www.strongswan.org"
+  using trusted intermediate ca certificate "C=US, O=Let's Encrypt, CN=R3"
+checking certificate status of "CN=www.strongswan.org"
+  requesting ocsp status from 'http://r3.o.lencr.org' ...
+  ocsp response correctly signed by "C=US, O=Let's Encrypt, CN=R3"
+  ocsp response is valid: until Jul 27 12:59:58 2022
+certificate status is good
+  using trusted ca certificate "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+checking certificate status of "C=US, O=Let's Encrypt, CN=R3"
+ocsp response verification failed, no signer certificate 'C=US, O=Let's Encrypt, CN=R3' found
+  fetching crl from 'http://x1.c.lencr.org/' ...
+  using trusted certificate "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+  crl correctly signed by "C=US, O=Internet Security Research Group, CN=ISRG Root X1"
+  crl is valid: until Apr 18 01:59:59 2023
+certificate status is good
+  reached self-signed root ca with a path length of 1
+certificate trusted, lifetimes valid, certificate not revoked
+
+Note that this also fixes an issue with the previous dual-use of the
+`trusted` flag.  It not only indicated whether the chain is trusted but
+also whether the current issuer is the root anchor (the corresponding
+flag in the `cert_validator_t` interface is called `anchor`).  This was
+a problem when building multi-level trust chains for pre-trusted
+end-entity certificates (i.e. where `trusted` is TRUE from the start).
+This caused the main loop to get aborted after the first intermediate CA
+certificate and the mentioned `anchor` flag wasn't correct in any calls
+to `cert_validator_t` implementations.
+
+Fixes: CVE-2022-40617
+---
+ .../credentials/credential_manager.c          | 54 +++++++++++++++----
+ 1 file changed, 45 insertions(+), 9 deletions(-)
+
+--- a/src/libstrongswan/credentials/credential_manager.c
++++ b/src/libstrongswan/credentials/credential_manager.c
+@@ -555,7 +555,7 @@ static void cache_queue(private_credenti
+  */
+ static bool check_lifetime(private_credential_manager_t *this,
+                                                  certificate_t *cert, char *label,
+-                                                 int pathlen, bool trusted, auth_cfg_t *auth)
++                                                 int pathlen, bool anchor, auth_cfg_t *auth)
+ {
+       time_t not_before, not_after;
+       cert_validator_t *validator;
+@@ -570,7 +570,7 @@ static bool check_lifetime(private_crede
+                       continue;
+               }
+               status = validator->check_lifetime(validator, cert,
+-                                                                                 pathlen, trusted, auth);
++                                                                                 pathlen, anchor, auth);
+               if (status != NEED_MORE)
+               {
+                       break;
+@@ -603,13 +603,13 @@ static bool check_lifetime(private_crede
+  */
+ static bool check_certificate(private_credential_manager_t *this,
+                               certificate_t *subject, certificate_t *issuer, bool online,
+-                              int pathlen, bool trusted, auth_cfg_t *auth)
++                              int pathlen, bool anchor, auth_cfg_t *auth)
+ {
+       cert_validator_t *validator;
+       enumerator_t *enumerator;
+       if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) ||
+-              !check_lifetime(this, issuer, "issuer", pathlen + 1, trusted, auth))
++              !check_lifetime(this, issuer, "issuer", pathlen + 1, anchor, auth))
+       {
+               return FALSE;
+       }
+@@ -622,7 +622,7 @@ static bool check_certificate(private_cr
+                       continue;
+               }
+               if (!validator->validate(validator, subject, issuer,
+-                                                               online, pathlen, trusted, auth))
++                                                               online, pathlen, anchor, auth))
+               {
+                       enumerator->destroy(enumerator);
+                       return FALSE;
+@@ -725,6 +725,7 @@ static bool verify_trust_chain(private_c
+       auth_cfg_t *auth;
+       signature_params_t *scheme;
+       int pathlen;
++      bool is_anchor = FALSE;
+       auth = auth_cfg_create();
+       get_key_strength(subject, auth);
+@@ -742,7 +743,7 @@ static bool verify_trust_chain(private_c
+                               auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer));
+                               DBG1(DBG_CFG, "  using trusted ca certificate \"%Y\"",
+                                                         issuer->get_subject(issuer));
+-                              trusted = TRUE;
++                              trusted = is_anchor = TRUE;
+                       }
+                       else
+                       {
+@@ -777,11 +778,18 @@ static bool verify_trust_chain(private_c
+                               DBG1(DBG_CFG, "  issuer is \"%Y\"",
+                                        current->get_issuer(current));
+                               call_hook(this, CRED_HOOK_NO_ISSUER, current);
++                              if (trusted)
++                              {
++                                      DBG1(DBG_CFG, "  reached end of incomplete trust chain for "
++                                               "trusted certificate \"%Y\"",
++                                               subject->get_subject(subject));
++                              }
+                               break;
+                       }
+               }
+-              if (!check_certificate(this, current, issuer, online,
+-                                                         pathlen, trusted, auth))
++              /* don't do online verification here */
++              if (!check_certificate(this, current, issuer, FALSE,
++                                                         pathlen, is_anchor, auth))
+               {
+                       trusted = FALSE;
+                       issuer->destroy(issuer);
+@@ -793,7 +801,7 @@ static bool verify_trust_chain(private_c
+               }
+               current->destroy(current);
+               current = issuer;
+-              if (trusted)
++              if (is_anchor)
+               {
+                       DBG1(DBG_CFG, "  reached self-signed root ca with a "
+                                "path length of %d", pathlen);
+@@ -806,6 +814,34 @@ static bool verify_trust_chain(private_c
+               DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN);
+               call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject);
+       }
++      else if (trusted && online)
++      {
++              enumerator_t *enumerator;
++              auth_rule_t rule;
++
++              /* do online revocation checks after basic validation of the chain */
++              pathlen = 0;
++              current = subject;
++              enumerator = auth->create_enumerator(auth);
++              while (enumerator->enumerate(enumerator, &rule, &issuer))
++              {
++                      if (rule == AUTH_RULE_CA_CERT || rule == AUTH_RULE_IM_CERT)
++                      {
++                              if (!check_certificate(this, current, issuer, TRUE, pathlen++,
++                                                                         rule == AUTH_RULE_CA_CERT, auth))
++                              {
++                                      trusted = FALSE;
++                                      break;
++                              }
++                              else if (rule == AUTH_RULE_CA_CERT)
++                              {
++                                      break;
++                              }
++                              current = issuer;
++                      }
++              }
++              enumerator->destroy(enumerator);
++      }
+       if (trusted)
+       {
+               result->merge(result, auth, FALSE);