Blame SOURCES/0008-Fix-signature-checking-on-unsigned-response-with-mul.patch

e5600e
From ea7e5efe9741e1b1787a58af16cb15b40c23be5a Mon Sep 17 00:00:00 2001
e5600e
From: Benjamin Dauvergne <bdauvergne@entrouvert.com>
e5600e
Date: Mon, 8 Mar 2021 11:33:26 +0100
e5600e
Subject: [PATCH] Fix signature checking on unsigned response with multiple
e5600e
 assertions
e5600e
e5600e
CVE-2021-28091 : when AuthnResponse messages are not signed (which is
e5600e
permitted by the specifiation), all assertion's signatures should be
e5600e
checked, but currently after the first signed assertion is checked all
e5600e
following assertions are accepted without checking their signature, and
e5600e
the last one is considered the main assertion.
e5600e
e5600e
This patch :
e5600e
* check signatures from all assertions if the message is not signed,
e5600e
* refuse messages with assertion from different issuers than the one on
e5600e
  the message, to prevent assertion bundling event if they are signed.
e5600e
---
e5600e
 lasso/saml-2.0/login.c | 102 +++++++++++++++++++++++++++++------------
e5600e
 1 file changed, 73 insertions(+), 29 deletions(-)
e5600e
e5600e
diff --git a/lasso/saml-2.0/login.c b/lasso/saml-2.0/login.c
e5600e
index 0d4bb1da1..cf62c1cc9 100644
e5600e
--- a/lasso/saml-2.0/login.c
e5600e
+++ b/lasso/saml-2.0/login.c
e5600e
@@ -1257,7 +1257,11 @@ lasso_saml20_login_check_assertion_signature(LassoLogin *login,
e5600e
 	original_node = lasso_node_get_original_xmlnode(LASSO_NODE(assertion));
e5600e
 	goto_cleanup_if_fail_with_rc(original_node, LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE);
e5600e
 
e5600e
-	rc = profile->signature_status = lasso_provider_verify_saml_signature(remote_provider, original_node, NULL);
e5600e
+	/* Shouldn't set the profile->signature_status here as we're only
e5600e
+	 * checking the assertion signature.
e5600e
+	 * Instead, we'll set the status after all the assertions are iterated.
e5600e
+	 */
e5600e
+	rc = lasso_provider_verify_saml_signature(remote_provider, original_node, NULL);
e5600e
 
e5600e
 #define log_verify_assertion_signature_error(msg) \
e5600e
 			message(G_LOG_LEVEL_WARNING, "Could not verify signature of assertion" \
e5600e
@@ -1282,18 +1286,6 @@ cleanup:
e5600e
 	return rc;
e5600e
 }
e5600e
 
e5600e
-static gboolean
e5600e
-_lasso_check_assertion_issuer(LassoSaml2Assertion *assertion, const gchar *provider_id)
e5600e
-{
e5600e
-	if (! LASSO_SAML2_ASSERTION(assertion) || ! provider_id)
e5600e
-		return FALSE;
e5600e
-
e5600e
-	if (! assertion->Issuer || ! assertion->Issuer->content)
e5600e
-		return FALSE;
e5600e
-
e5600e
-	return lasso_strisequal(assertion->Issuer->content,provider_id);
e5600e
-}
e5600e
-
e5600e
 static gint
e5600e
 _lasso_saml20_login_decrypt_assertion(LassoLogin *login, LassoSamlp2Response *samlp2_response)
e5600e
 {
e5600e
@@ -1358,11 +1350,23 @@ _lasso_saml20_login_decrypt_assertion(LassoLogin *login, LassoSamlp2Response *sa
e5600e
 	return 0;
e5600e
 }
e5600e
 
e5600e
+/* Verify that an assertion comes from a designated Issuer */
e5600e
+static gboolean
e5600e
+_lasso_check_assertion_issuer(LassoSaml2Assertion *assertion, const gchar *provider_id)
e5600e
+{
e5600e
+	if (! LASSO_SAML2_ASSERTION(assertion) || ! provider_id)
e5600e
+		return FALSE;
e5600e
+	if (! assertion->Issuer || ! assertion->Issuer->content)
e5600e
+		return FALSE;
e5600e
+	return lasso_strisequal(assertion->Issuer->content,provider_id);
e5600e
+}
e5600e
+
e5600e
 static gint
e5600e
 lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login)
e5600e
 {
e5600e
 	LassoSamlp2StatusResponse *response;
e5600e
 	LassoSamlp2Response *samlp2_response = NULL;
e5600e
+	LassoSaml2Assertion *last_assertion = NULL;
e5600e
 	LassoProfile *profile;
e5600e
 	char *status_value;
e5600e
 	lasso_error_t rc = 0;
e5600e
@@ -1404,34 +1408,62 @@ lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login)
e5600e
 
e5600e
 	/* Decrypt all EncryptedAssertions */
e5600e
 	_lasso_saml20_login_decrypt_assertion(login, samlp2_response);
e5600e
-	/* traverse all assertions */
e5600e
-	goto_cleanup_if_fail_with_rc (samlp2_response->Assertion != NULL,
e5600e
-			LASSO_PROFILE_ERROR_MISSING_ASSERTION);
e5600e
 
e5600e
+	/* Check there is at least one assertion */
e5600e
+	goto_cleanup_if_fail_with_rc (samlp2_response->Assertion != NULL, LASSO_PROFILE_ERROR_MISSING_ASSERTION);
e5600e
+
e5600e
+	/* In case of verify_hint as 'FORCE', if there's no response signature,
e5600e
+	 * we reject.
e5600e
+	 * In case of 'MAYBE', if response signature is present and valid, or
e5600e
+	 * not present, then we proceed with checking assertion signature(s).
e5600e
+	 * In any case, if there's a response signature and it's not valid,
e5600e
+	 * we reject.
e5600e
+	*/
e5600e
 	verify_hint = lasso_profile_get_signature_verify_hint(profile);
e5600e
+	if (profile->signature_status == LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
e5600e
+		if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) {
e5600e
+			goto_cleanup_with_rc(profile->signature_status);
e5600e
+		}
e5600e
+	} else if (profile->signature_status != 0) {
e5600e
+		goto_cleanup_with_rc(profile->signature_status);
e5600e
+	}
e5600e
 
e5600e
 	lasso_foreach_full_begin(LassoSaml2Assertion*, assertion, it, samlp2_response->Assertion);
e5600e
 		LassoSaml2Subject *subject = NULL;
e5600e
 
e5600e
-		lasso_assign_gobject (login->private_data->saml2_assertion, assertion);
e5600e
+		/* All Assertions MUST come from the same issuer as the Response. */
e5600e
+		if (! _lasso_check_assertion_issuer(assertion, profile->remote_providerID)) {
e5600e
+			goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_ISSUER);
e5600e
+		}
e5600e
 
e5600e
-		/* If signature has already been verified on the message, and assertion has the same
e5600e
-		 * issuer as the message, the assertion is covered. So no need to verify a second
e5600e
-		 * time */
e5600e
-		if (profile->signature_status != 0 
e5600e
-			|| ! _lasso_check_assertion_issuer(assertion,
e5600e
-				profile->remote_providerID)
e5600e
-			|| verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) {
e5600e
+		if (profile->signature_status != 0) {
e5600e
+			/* When response signature is not present */
e5600e
+			if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE) {
e5600e
+				assertion_signature_status =
e5600e
+					lasso_saml20_login_check_assertion_signature(login, assertion);
e5600e
+				if (assertion_signature_status) {
e5600e
+					goto_cleanup_with_rc(assertion_signature_status);
e5600e
+				}
e5600e
+			}
e5600e
+		} else {
e5600e
+			/* response signature is present and valid */
e5600e
 			assertion_signature_status = lasso_saml20_login_check_assertion_signature(login,
e5600e
-					assertion);
e5600e
-			/* If signature validation fails, it is the return code for this function */
e5600e
+							assertion);
e5600e
 			if (assertion_signature_status) {
e5600e
-				rc = LASSO_PROFILE_ERROR_CANNOT_VERIFY_SIGNATURE;
e5600e
+				/* assertion signature is not valid or not present */
e5600e
+				if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE) {
e5600e
+					/* In case of FORCE, we reject right away */
e5600e
+					goto_cleanup_with_rc(assertion_signature_status);
e5600e
+				} else if (verify_hint == LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE) {
e5600e
+					/* In case of MAYBE, if assertion signature is present and invalid, then we reject */
e5600e
+					if (assertion_signature_status != LASSO_DS_ERROR_SIGNATURE_NOT_FOUND) {
e5600e
+						goto_cleanup_with_rc(assertion_signature_status);
e5600e
+					}
e5600e
+				}
e5600e
 			}
e5600e
 		}
e5600e
-
e5600e
 		lasso_extract_node_or_fail(subject, assertion->Subject, SAML2_SUBJECT,
e5600e
-				LASSO_PROFILE_ERROR_MISSING_SUBJECT);
e5600e
+			LASSO_PROFILE_ERROR_MISSING_SUBJECT);
e5600e
 
e5600e
 		/* Verify Subject->SubjectConfirmationData->InResponseTo */
e5600e
 		if (login->private_data->request_id) {
e5600e
@@ -1446,8 +1478,20 @@ lasso_saml20_login_process_response_status_and_assertion(LassoLogin *login)
e5600e
 		/** Handle nameid */
e5600e
 		lasso_check_good_rc(lasso_saml20_profile_process_name_identifier_decryption(profile,
e5600e
 					&subject->NameID, &subject->EncryptedID));
e5600e
+
e5600e
+		last_assertion = assertion;
e5600e
 	lasso_foreach_full_end();
e5600e
 
e5600e
+	/* set the profile signature status only after all the signatures are
e5600e
+	 * verified.
e5600e
+	 */
e5600e
+	profile->signature_status = rc;
e5600e
+
e5600e
+	/* set the default assertion to the last one */
e5600e
+	if (last_assertion) {
e5600e
+		lasso_assign_gobject (login->private_data->saml2_assertion, last_assertion);
e5600e
+	}
e5600e
+
e5600e
 	switch (verify_hint) {
e5600e
 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_FORCE:
e5600e
 		case LASSO_PROFILE_SIGNATURE_VERIFY_HINT_MAYBE:
e5600e
-- 
e5600e
2.26.3
e5600e