diff --git a/lasso/saml-2.0/login.c b/lasso/saml-2.0/login.c index 8594e6e9..95779d17 100644 --- a/lasso/saml-2.0/login.c +++ b/lasso/saml-2.0/login.c @@ -303,7 +303,9 @@ lasso_saml20_login_process_authn_request_msg(LassoLogin *login, const char *auth remote_provider->role = LASSO_PROVIDER_ROLE_SP; server->parent.role = LASSO_PROVIDER_ROLE_IDP; - /* all those attributes are mutually exclusive */ + /* Normally those three attributes are mutually exclusive, but Google Apps send + * ProtocolBinding and AssertionConsumerServiceURL at the same time, so we support this case + * by validating that it matches the same endpoint */ if (((authn_request->ProtocolBinding != NULL) || (authn_request->AssertionConsumerServiceURL != NULL)) && (authn_request->AssertionConsumerServiceIndex != -1)) @@ -314,41 +316,24 @@ lasso_saml20_login_process_authn_request_msg(LassoLogin *login, const char *auth /* try to find a protocol profile for sending the response */ protocol_binding = authn_request->ProtocolBinding; - if (protocol_binding == NULL && authn_request->AssertionConsumerServiceIndex) { - /* protocol binding not set; so it will look into - * AssertionConsumerServiceIndex - * Also, if AssertionConsumerServiceIndex is not set in request, - * its value will be -1, which is just the right value to get - * default assertion consumer... (convenient) - */ - gchar *binding; - int service_index = authn_request->AssertionConsumerServiceIndex; + if (protocol_binding || authn_request->AssertionConsumerServiceURL) + { + const gchar *acs_url_binding = NULL; - binding = lasso_saml20_provider_get_assertion_consumer_service_binding( - remote_provider, service_index); - if (binding == NULL) { - if (service_index == -1) - return LASSO_LOGIN_ERROR_NO_DEFAULT_ENDPOINT; - } else if (lasso_strisequal(binding,"HTTP-Artifact")) { - login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART; - } else if (lasso_strisequal(binding,"HTTP-POST")) { - login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST; - } else if (lasso_strisequal(binding,"HTTP-Redirect")) { - login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_REDIRECT; - } else if (lasso_strisequal(binding,"SOAP")) { - login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP; - } else if (lasso_strisequal(binding,"PAOS")) { - login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP; - } - lasso_release_string(binding); - } else { - // If we just received an URL, try to find the corresponding protocol binding - if (protocol_binding == NULL && authn_request->AssertionConsumerServiceURL) { - protocol_binding = - lasso_saml20_provider_get_assertion_consumer_service_binding_by_url( - remote_provider, - authn_request->AssertionConsumerServiceURL); + if (authn_request->AssertionConsumerServiceURL) { + acs_url_binding = lasso_saml20_provider_get_assertion_consumer_service_binding_by_url( + remote_provider, authn_request->AssertionConsumerServiceURL); + if (! acs_url_binding) { + // Sent ACS URL is unknown + rc = LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE; + goto cleanup; + } + if (! protocol_binding) { + // Only ACS URL sent + protocol_binding = acs_url_binding; + } } + if (lasso_strisequal(protocol_binding,LASSO_SAML2_METADATA_BINDING_ARTIFACT)) { login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART; } else if (lasso_strisequal(protocol_binding,LASSO_SAML2_METADATA_BINDING_POST)) { @@ -364,6 +349,41 @@ lasso_saml20_login_process_authn_request_msg(LassoLogin *login, const char *auth rc = LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE; goto cleanup; } + // We received both a protocolbinding and an acs url, check both matches + if (acs_url_binding && g_strcmp0(protocol_binding, acs_url_binding) != 0) { + rc = LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE; + goto cleanup; + } + } else { + /* protocol binding not set; so it will look into + * AssertionConsumerServiceIndex + * Also, if AssertionConsumerServiceIndex is not set in request, + * its value will be -1, which is just the right value to get + * default assertion consumer... (convenient) + */ + gchar *binding; + int service_index = authn_request->AssertionConsumerServiceIndex; + + binding = lasso_saml20_provider_get_assertion_consumer_service_binding( + remote_provider, service_index); + if (binding == NULL) { + if (service_index == -1) { + goto_cleanup_with_rc(LASSO_LOGIN_ERROR_NO_DEFAULT_ENDPOINT); + } else { + goto_cleanup_with_rc(LASSO_PROFILE_ERROR_INVALID_PROTOCOLPROFILE); + } + } else if (lasso_strisequal(binding,"HTTP-Artifact")) { + login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_ART; + } else if (lasso_strisequal(binding,"HTTP-POST")) { + login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_POST; + } else if (lasso_strisequal(binding,"HTTP-Redirect")) { + login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_REDIRECT; + } else if (lasso_strisequal(binding,"SOAP")) { + login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP; + } else if (lasso_strisequal(binding,"PAOS")) { + login->protocolProfile = LASSO_LOGIN_PROTOCOL_PROFILE_BRWS_LECP; + } + lasso_release_string(binding); } diff --git a/tests/login_tests_saml2.c b/tests/login_tests_saml2.c index e1c578e0..719917a6 100644 --- a/tests/login_tests_saml2.c +++ b/tests/login_tests_saml2.c @@ -1002,6 +1002,111 @@ START_TEST(test07_sso_sp_with_hmac_sha1_signatures) } END_TEST +typedef struct { + char *assertion_consumer_service_url; + char *protocol_binding; + gboolean use_assertion_consumer_service_idx; + int assertion_consumer_service_idx; + gboolean stop_after_build_assertion; +} SsoSettings; + +static void +sso_initiated_by_sp2(LassoServer *idp_context, LassoServer *sp_context, SsoSettings sso_settings) +{ + LassoLogin *idp_login_context; + LassoLogin *sp_login_context; + LassoSamlp2AuthnRequest *request; + char *authn_request_query; + + check_not_null(idp_login_context = lasso_login_new(idp_context)); + check_not_null(sp_login_context = lasso_login_new(sp_context)) + + /* Create response */ + check_good_rc(lasso_login_init_authn_request(sp_login_context, NULL, LASSO_HTTP_METHOD_REDIRECT)); + request = (LassoSamlp2AuthnRequest*)sp_login_context->parent.request; + if (sso_settings.assertion_consumer_service_url) { + lasso_assign_string(request->AssertionConsumerServiceURL, sso_settings.assertion_consumer_service_url); + } + if (sso_settings.protocol_binding) { + lasso_assign_string(request->ProtocolBinding, sso_settings.protocol_binding); + } + if (sso_settings.use_assertion_consumer_service_idx) { + request->AssertionConsumerServiceIndex = sso_settings.assertion_consumer_service_idx; + } + lasso_assign_string(LASSO_SAMLP2_AUTHN_REQUEST(sp_login_context->parent.request)->NameIDPolicy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT); + LASSO_SAMLP2_AUTHN_REQUEST(sp_login_context->parent.request)->NameIDPolicy->AllowCreate = 1; + check_good_rc(lasso_login_build_authn_request_msg(sp_login_context)); + check_not_null(sp_login_context->parent.msg_url); + authn_request_query = strchr(sp_login_context->parent.msg_url, '?'); + check_not_null(authn_request_query); + authn_request_query += 1; + check_good_rc(lasso_login_process_authn_request_msg(idp_login_context, authn_request_query)); + + check_good_rc(lasso_login_validate_request_msg(idp_login_context, + 1, /* authentication_result */ + 0 /* is_consent_obtained */ + )); + + check_good_rc(lasso_login_build_assertion(idp_login_context, + LASSO_SAML_AUTHENTICATION_METHOD_PASSWORD, + "FIXME: authenticationInstant", + "FIXME: reauthenticateOnOrAfter", + "FIXME: notBefore", + "FIXME: notOnOrAfter")); + if (sso_settings.stop_after_build_assertion) { + goto cleanup; + } + check_good_rc(lasso_login_build_authn_response_msg(idp_login_context)); + check_not_null(idp_login_context->parent.msg_body); + check_not_null(idp_login_context->parent.msg_url); + + /* Process response */ + check_good_rc(lasso_login_process_authn_response_msg(sp_login_context, + idp_login_context->parent.msg_body)); + check_good_rc(lasso_login_accept_sso(sp_login_context)); + + /* Cleanup */ +cleanup: + lasso_release_gobject(idp_login_context); + lasso_release_gobject(sp_login_context); +} + +START_TEST(test08_test_authnrequest_flags) +{ + LassoServer *idp_context = NULL; + LassoServer *sp_context = NULL; + GList *providers; + + /* Create an IdP context for IdP initiated SSO with provider metadata 1 */ + make_context(idp_context, "idp5-saml2", "", LASSO_PROVIDER_ROLE_SP, "sp5-saml2", "") + make_context(sp_context, "sp5-saml2", "", LASSO_PROVIDER_ROLE_IDP, "idp5-saml2", "") + + block_lasso_logs; + sso_initiated_by_sp2(idp_context, sp_context, + (SsoSettings) { + .use_assertion_consumer_service_idx = 1, + .assertion_consumer_service_idx = 0, + .stop_after_build_assertion = 1, + }); + sso_initiated_by_sp2(idp_context, sp_context, + (SsoSettings) { + .assertion_consumer_service_url = "http://sp5/singleSignOnPost", + .stop_after_build_assertion = 1, + }); + sso_initiated_by_sp2(idp_context, sp_context, + (SsoSettings) { + .protocol_binding = LASSO_SAML2_METADATA_BINDING_ARTIFACT, + .stop_after_build_assertion = 1, + }); + unblock_lasso_logs; + + /* Cleanup */ + lasso_release_gobject(idp_context); + lasso_release_gobject(sp_context); +} +END_TEST + Suite* login_saml2_suite() { @@ -1027,6 +1132,7 @@ login_saml2_suite() tcase_add_test(tc_idpKeyRollover, test05_sso_idp_with_key_rollover); tcase_add_test(tc_spKeyRollover, test06_sso_sp_with_key_rollover); tcase_add_test(tc_hmacSignature, test07_sso_sp_with_hmac_sha1_signatures); + tcase_add_test(tc_spLogin, test08_test_authnrequest_flags); return s; }