diff --git a/SOURCES/0007-Fix-signature-checking-on-unsigned-response-with-mul.patch b/SOURCES/0007-Fix-signature-checking-on-unsigned-response-with-mul.patch new file mode 100644 index 0000000..18773f8 --- /dev/null +++ b/SOURCES/0007-Fix-signature-checking-on-unsigned-response-with-mul.patch @@ -0,0 +1,183 @@ +From ea7e5efe9741e1b1787a58af16cb15b40c23be5a Mon Sep 17 00:00:00 2001 +From: Benjamin Dauvergne +Date: Mon, 8 Mar 2021 11:33:26 +0100 +Subject: [PATCH] Fix signature checking on unsigned response with multiple + assertions + +CVE-2021-28091 : when AuthnResponse messages are not signed (which is +permitted by the specifiation), all assertion's signatures should be +checked, but currently after the first signed assertion is checked all +following assertions are accepted without checking their signature, and +the last one is considered the main assertion. + +This patch : +* check signatures from all assertions if the message is not signed, +* refuse messages with assertion from different issuers than the one on + the message, to prevent assertion bundling event if they are signed. +--- + lasso/saml-2.0/login.c | 102 +++++++++++++++++++++++++++++------------ + 1 file changed, 73 insertions(+), 29 deletions(-) + +diff --git a/lasso/saml-2.0/login.c b/lasso/saml-2.0/login.c +index 0d4bb1da1..cf62c1cc9 100644 +--- a/lasso/saml-2.0/login.c ++++ b/lasso/saml-2.0/login.c +@@ -1257,7 +1257,11 @@ lasso_saml20_login_check_assertion_signature(LassoLogin *login, + original_node = lasso_node_get_original_xmlnode(LASSO_NODE(assertion)); + goto_cleanup_if_fail_with_rc(original_node, LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE); + +- rc = profile->signature_status = lasso_provider_verify_saml_signature(remote_provider, original_node, NULL); ++ /* Shouldn't set the profile->signature_status here as we're only ++ * checking the assertion signature. ++ * Instead, we'll set the status after all the assertions are iterated. ++ */ ++ rc = lasso_provider_verify_saml_signature(remote_provider, original_node, NULL); + + #define log_verify_assertion_signature_error(msg) \ + message(G_LOG_LEVEL_WARNING, "Could not verify signature of assertion" \ +@@ -1282,18 +1286,6 @@ cleanup: + return rc; + } + +-static gboolean +-_lasso_check_assertion_issuer(LassoSaml2Assertion *assertion, const gchar *provider_id) +-{ +- if (! LASSO_SAML2_ASSERTION(assertion) || ! provider_id) +- return FALSE; +- +- if (! assertion->Issuer || ! assertion->Issuer->content) +- return FALSE; +- +- return lasso_strisequal(assertion->Issuer->content,provider_id); +-} +- + static gint + _lasso_saml20_login_decrypt_assertion(LassoLogin *login, LassoSamlp2Response *samlp2_response) + { +@@ -1358,11 +1350,23 @@ _lasso_saml20_login_decrypt_assertion(LassoLogin *login, LassoSamlp2Response *sa + return 0; + } + ++/* Verify that an assertion comes from a designated Issuer */ ++static gboolean ++_lasso_check_assertion_issuer(LassoSaml2Assertion *assertion, const gchar *provider_id) ++{ ++ if (! LASSO_SAML2_ASSERTION(assertion) || ! provider_id) ++ return FALSE; ++ if (! assertion->Issuer || ! assertion->Issuer->content) ++ return FALSE; ++ return lasso_strisequal(assertion->Issuer->content,provider_id); ++} ++ + static gint + lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login) + { + LassoSamlp2StatusResponse *response; + LassoSamlp2Response *samlp2_response = NULL; ++ LassoSaml2Assertion *last_assertion = NULL; + LassoProfile *profile; + char *status_value; + lasso_error_t rc = 0; +@@ -1404,34 +1408,62 @@ lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login) + + /* Decrypt all EncryptedAssertions */ + _lasso_saml20_login_decrypt_assertion(login, samlp2_response); +- /* traverse all assertions */ +- goto_cleanup_if_fail_with_rc (samlp2_response->Assertion != NULL, +- LASSO_PROFILE_ERROR_MISSING_ASSERTION); + ++ /* Check there is at least one assertion */ ++ goto_cleanup_if_fail_with_rc (samlp2_response->Assertion != NULL, LASSO_PROFILE_ERROR_MISSING_ASSERTION); ++ ++ /* In case of verify_hint as 'FORCE', if there's no response signature, ++ * we reject. ++ * In case of 'MAYBE', if response signature is present and valid, or ++ * not present, then we proceed with checking assertion signature(s). ++ * In any case, if there's a response signature and it's not valid, ++ * we reject. ++ */ + verify_hint = lasso_profile_get_signature_verify_hint(profile); ++ if (profile->signature_status == LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) { ++ if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) { ++ goto_cleanup_with_rc(profile->signature_status); ++ } ++ } else if (profile->signature_status != 0) { ++ goto_cleanup_with_rc(profile->signature_status); ++ } + + lasso_foreach_full_begin(LassoSaml2Assertion*, assertion, it, samlp2_response->Assertion); + LassoSaml2Subject *subject = NULL; + +- lasso_assign_gobject (login->private_data->saml2_assertion, assertion); ++ /* All Assertions MUST come from the same issuer as the Response. */ ++ if (! _lasso_check_assertion_issuer(assertion, profile->remote_providerID)) { ++ goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_ISSUER); ++ } + +- /* If signature has already been verified on the message, and assertion has the same +- * issuer as the message, the assertion is covered. So no need to verify a second +- * time */ +- if (profile->signature_status != 0 +- || ! _lasso_check_assertion_issuer(assertion, +- profile->remote_providerID) +- || verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) { ++ if (profile->signature_status != 0) { ++ /* When response signature is not present */ ++ if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE) { ++ assertion_signature_status = ++ lasso_saml20_login_check_assertion_signature(login, assertion); ++ if (assertion_signature_status) { ++ goto_cleanup_with_rc(assertion_signature_status); ++ } ++ } ++ } else { ++ /* response signature is present and valid */ + assertion_signature_status = lasso_saml20_login_check_assertion_signature(login, +- assertion); +- /* If signature validation fails, it is the return code for this function */ ++ assertion); + if (assertion_signature_status) { +- rc = LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE; ++ /* assertion signature is not valid or not present */ ++ if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) { ++ /* In case of FORCE, we reject right away */ ++ goto_cleanup_with_rc(assertion_signature_status); ++ } else if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE) { ++ /* In case of MAYBE, if assertion signature is present and invalid, then we reject */ ++ if (assertion_signature_status != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) { ++ goto_cleanup_with_rc(assertion_signature_status); ++ } ++ } + } + } +- + lasso_extract_node_or_fail(subject, assertion->Subject, SAML2_SUBJECT, +- LASSO_PROFILE_ERROR_MISSING_SUBJECT); ++ LASSO_PROFILE_ERROR_MISSING_SUBJECT); + + /* Verify Subject->SubjectConfirmationData->InResponseTo */ + if (login->private_data->request_id) { +@@ -1446,8 +1478,20 @@ lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login) + /** Handle nameid */ + lasso_check_good_rc(lasso_saml20_profile_process_name_identifier_decryption(profile, + &subject->NameID, &subject->EncryptedID)); ++ ++ last_assertion = assertion; + lasso_foreach_full_end(); + ++ /* set the profile signature status only after all the signatures are ++ * verified. ++ */ ++ profile->signature_status = rc; ++ ++ /* set the default assertion to the last one */ ++ if (last_assertion) { ++ lasso_assign_gobject (login->private_data->saml2_assertion, last_assertion); ++ } ++ + switch (verify_hint) { + case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: + case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: +-- +2.26.3 + diff --git a/SOURCES/0008-lasso_saml20_login_process_response_status_and_asser.patch b/SOURCES/0008-lasso_saml20_login_process_response_status_and_asser.patch new file mode 100644 index 0000000..df5a09f --- /dev/null +++ b/SOURCES/0008-lasso_saml20_login_process_response_status_and_asser.patch @@ -0,0 +1,38 @@ +diff -up lasso-2.5.1/lasso/saml-2.0/login.c.coverity lasso-2.5.1/lasso/saml-2.0/login.c +--- lasso-2.5.1/lasso/saml-2.0/login.c.coverity 2021-07-27 10:23:31.976845852 +0200 ++++ lasso-2.5.1/lasso/saml-2.0/login.c 2021-07-27 10:23:55.358913123 +0200 +@@ -1371,7 +1371,7 @@ lasso_saml20_login_process_response_stat + char *status_value; + lasso_error_t rc = 0; + lasso_error_t assertion_signature_status = 0; +- LassoProfileSignatureVerifyHint verify_hint; ++ LassoProfileSignatureVerifyHint verify_hint = LASSO_PROFILE_SIGNATURE_VERIFY_HINT_LAST; + + profile = &login->parent; + lasso_extract_node_or_fail(response, profile->response, SAMLP2_STATUS_RESPONSE, +@@ -1492,20 +1492,12 @@ lasso_saml20_login_process_response_stat + lasso_assign_gobject (login->private_data->saml2_assertion, last_assertion); + } + +- switch (verify_hint) { +- case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE: +- case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE: +- break; +- case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE: +- /* ignore signature errors */ +- if (rc == LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE) { +- rc = 0; +- } +- break; +- default: +- g_assert(0); +- } + cleanup: ++ if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_IGNORE && ++ rc == LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE) { ++ profile->signature_status = rc; ++ rc = 0; ++ } + return rc; + } + diff --git a/SPECS/lasso.spec b/SPECS/lasso.spec index 52b2d91..bcf6e6c 100644 --- a/SPECS/lasso.spec +++ b/SPECS/lasso.spec @@ -15,7 +15,7 @@ Summary: Liberty Alliance Single Sign On Name: lasso Version: 2.5.1 -Release: 5%{?dist} +Release: 8%{?dist} License: GPLv2+ Group: System Environment/Libraries Source: http://dev.entrouvert.org/lasso/lasso-%{version}.tar.gz @@ -37,6 +37,8 @@ patch3: 0003-Choose-the-Reference-transform-based-on-the-chosen-S.patch patch4: 0004-Fix-ECP-signature-not-found-error-when-only-assertio.patch patch5: 0005-PAOS-Do-not-populate-Destination-attribute.patch patch6: 0006-tests-use-self-generated-certificate-to-sign-federat.patch +patch7: 0007-Fix-signature-checking-on-unsigned-response-with-mul.patch +patch8: 0008-lasso_saml20_login_process_response_status_and_asser.patch %description Lasso is a library that implements the Liberty Alliance Single Sign On @@ -118,6 +120,8 @@ library. %patch4 -p1 %patch5 -p1 %patch6 -p1 +%patch7 -p1 +%patch8 -p1 %build autoreconf -vif @@ -226,6 +230,20 @@ rm -fr %{buildroot}%{_defaultdocdir}/%{name} %endif %changelog +* Wed Jun 2 2021 Jakub Hrozek - 2.5.1-8 +- Fix Coverity warning introduced by the previous patch +- Related: #1963855 - CVE-2021-28091 lasso: XML signature wrapping + vulnerability when parsing SAML responses + +* Wed Jun 2 2021 Jakub Hrozek - 2.5.1-7 +- Fix Coverity warning introduced by the previous patch +- Related: #1963855 - CVE-2021-28091 lasso: XML signature wrapping + vulnerability when parsing SAML responses + +* Wed Jun 2 2021 Jakub Hrozek - 2.5.1-6 +- Resolves: #1963855 - CVE-2021-28091 lasso: XML signature wrapping + vulnerability when parsing SAML responses + * Tue Aug 6 2019 Jakub Hrozek - 2.5.1-5 - Resolves: #1719014 - Expired certificate prevents tests from running - Actually apply the patch file for the previous build