[libsoup/pgriffis/multiple-digest-algorithms] auth: Add support for multiple authentication challenges at once This allows a server sending two di
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/pgriffis/multiple-digest-algorithms] auth: Add support for multiple authentication challenges at once This allows a server sending two di
- Date: Thu, 7 Oct 2021 16:58:45 +0000 (UTC)
commit 1bdb2df54eee04bb53437b45e6676961377bf866
Author: Patrick Griffis <pgriffis igalia com>
Date: Thu Oct 7 11:57:43 2021 -0500
auth: Add support for multiple authentication challenges at once
This allows a server sending two different Digest headers with
different algorithms. libsoup will now parse both and use one
that is supported.
libsoup/auth/soup-auth-manager.c | 111 ++++++++++++++++++++++++---------------
tests/auth-test.c | 73 +++++++++++++++++++++++++
2 files changed, 141 insertions(+), 43 deletions(-)
---
diff --git a/libsoup/auth/soup-auth-manager.c b/libsoup/auth/soup-auth-manager.c
index 015c7c74..5737ca0c 100644
--- a/libsoup/auth/soup-auth-manager.c
+++ b/libsoup/auth/soup-auth-manager.c
@@ -311,41 +311,49 @@ next_challenge_start (GSList *items)
return NULL;
}
-static char *
-soup_auth_manager_extract_challenge (const char *challenges, const char *scheme)
+static GStrv
+soup_auth_manager_extract_challenges (const char *challenges, const char *scheme)
{
+ GPtrArray *challenge_list = g_ptr_array_new ();
GSList *items, *i, *next;
int schemelen = strlen (scheme);
char *item;
GString *challenge;
- items = soup_header_parse_list (challenges);
-
- /* First item will start with the scheme name, followed by
- * either nothing, or else a space and then the first
- * auth-param.
- */
- for (i = items; i; i = next_challenge_start (i->next)) {
- item = i->data;
- if (!g_ascii_strncasecmp (item, scheme, schemelen) &&
- (!item[schemelen] || g_ascii_isspace (item[schemelen])))
- break;
- }
- if (!i) {
- soup_header_free_list (items);
- return NULL;
- }
-
- next = next_challenge_start (i->next);
- challenge = g_string_new (item);
- for (i = i->next; i != next; i = i->next) {
- item = i->data;
- g_string_append (challenge, ", ");
- g_string_append (challenge, item);
- }
+ i = items = soup_header_parse_list (challenges);
+
+ /* We need to split this list into individual challenges. */
+ while (i) {
+ /* First item will start with the scheme name, followed by
+ * either nothing, or else a space and then the first
+ * auth-param.
+ */
+ for (; i; i = next_challenge_start (i->next)) {
+ item = i->data;
+ if (!g_ascii_strncasecmp (item, scheme, schemelen) &&
+ (!item[schemelen] || g_ascii_isspace (item[schemelen])))
+ break;
+ }
+ if (!i)
+ break;
+
+ next = next_challenge_start (i->next);
+ challenge = g_string_new (item);
+ for (i = i->next; i != next; i = i->next) {
+ item = i->data;
+ g_string_append (challenge, ", ");
+ g_string_append (challenge, item);
+ }
+
+ i = next;
+ g_ptr_array_add (challenge_list, g_string_free (challenge, FALSE));
+ };
soup_header_free_list (items);
- return g_string_free (challenge, FALSE);
+
+ if (challenge_list->len)
+ g_ptr_array_add (challenge_list, NULL); /* Trailing NULL for GStrv. */
+ return (GStrv)g_ptr_array_free (challenge_list, FALSE);
}
static SoupAuth *
@@ -353,7 +361,7 @@ create_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
{
const char *header;
SoupAuthClass *auth_class;
- char *challenge = NULL;
+ GStrv challenges;
SoupAuth *auth = NULL;
int i;
@@ -363,13 +371,19 @@ create_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
for (i = priv->auth_types->len - 1; i >= 0; i--) {
auth_class = priv->auth_types->pdata[i];
- challenge = soup_auth_manager_extract_challenge (header, auth_class->scheme_name);
- if (!challenge)
- continue;
- auth = soup_auth_new (G_TYPE_FROM_CLASS (auth_class), msg, challenge);
- g_free (challenge);
- if (auth)
- break;
+ challenges = soup_auth_manager_extract_challenges (header, auth_class->scheme_name);
+ if (!challenges)
+ continue;
+
+ for (int j = 0; challenges[j]; j++) {
+ /* We use the first successfully parsed auth, in the future this should
+ * prioritise more secure ones when they are supported. */
+ auth = soup_auth_new (G_TYPE_FROM_CLASS (auth_class), msg, challenges[j]);
+ if (auth)
+ break;
+ }
+
+ g_strfreev (challenges);
}
return auth;
@@ -379,22 +393,33 @@ static gboolean
check_auth (SoupMessage *msg, SoupAuth *auth)
{
const char *header, *scheme;
- char *challenge = NULL;
+ GStrv challenges = NULL;
gboolean ok = TRUE;
+ gboolean a_challenge_was_ok = FALSE;
scheme = soup_auth_get_scheme_name (auth);
header = auth_header_for_message (msg);
if (header)
- challenge = soup_auth_manager_extract_challenge (header, scheme);
- if (!challenge) {
- ok = FALSE;
- challenge = g_strdup (scheme);
+ challenges = soup_auth_manager_extract_challenges (header, scheme);
+ if (!challenges) {
+ GStrvBuilder *builder = g_strv_builder_new ();
+ g_strv_builder_add (builder, scheme);
+ challenges = g_strv_builder_end (builder);
+ ok = FALSE;
}
- if (!soup_auth_update (auth, msg, challenge))
- ok = FALSE;
- g_free (challenge);
+ for (int i = 0; challenges[i]; i++) {
+ if (soup_auth_update (auth, msg, challenges[i])) {
+ a_challenge_was_ok = TRUE;
+ break;
+ }
+ }
+
+ if (!a_challenge_was_ok)
+ ok = FALSE;
+
+ g_strfreev (challenges);
return ok;
}
diff --git a/tests/auth-test.c b/tests/auth-test.c
index c3bef39c..48067c7a 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -1760,6 +1760,78 @@ do_auth_uri_test (void)
soup_test_session_abort_unref (session);
}
+static void
+on_request_read (SoupServer *server,
+ SoupServerMessage *msg,
+ gpointer user_data)
+{
+ SoupMessageHeaders *response_headers = soup_server_message_get_response_headers (msg);
+ char *old_header = g_strdup (soup_message_headers_get_one (response_headers, "WWW-Authenticate"));
+ if (old_header) {
+ /* These must be in order to ensure libsoup passes over the invalid one. */
+ soup_message_headers_replace (response_headers, "WWW-Authenticate",
+ "Digest realm=\"auth-test\", nonce=\"0000000000001\", qop=\"auth\",
algorithm=FAKE");
+ soup_message_headers_append (response_headers, "WWW-Authenticate", old_header);
+ g_free (old_header);
+ }
+}
+
+static gboolean
+on_digest_authenticate (SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ gpointer user_data)
+{
+ g_assert_false (retrying);
+ soup_auth_authenticate (auth, "user", "good");
+ return TRUE;
+}
+
+static void
+do_multiple_digest_algorithms (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupServer *server;
+ SoupAuthDomain *digest_auth_domain;
+ gint status;
+ GUri *uri;
+
+ server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ uri = soup_test_server_get_uri (server, "http", NULL);
+
+ /* Add one real authentication option, later we will add
+ a fake one with an unsupported algorithm. */
+ digest_auth_domain = soup_auth_domain_digest_new (
+ "realm", "auth-test",
+ "auth-callback", server_digest_auth_callback,
+ NULL);
+ soup_auth_domain_add_path (digest_auth_domain, "/");
+ soup_server_add_auth_domain (server, digest_auth_domain);
+ g_object_unref (digest_auth_domain);
+
+ /* We wait for the message to come in and will add a header. */
+ g_signal_connect (server, "request-read",
+ G_CALLBACK (on_request_read),
+ NULL);
+
+ session = soup_test_session_new (NULL);
+ loop = g_main_loop_new (NULL, FALSE);
+
+ msg = soup_message_new_from_uri ("GET", uri);
+ g_signal_connect (msg, "authenticate",
+ G_CALLBACK (on_digest_authenticate),
+ NULL);
+
+ status = soup_test_session_send_message (session, msg);
+
+ g_assert_cmpint (status, ==, SOUP_STATUS_OK);
+ g_uri_unref (uri);
+ soup_test_server_quit_unref (server);
+}
+
int
main (int argc, char **argv)
{
@@ -1791,6 +1863,7 @@ main (int argc, char **argv)
g_test_add_func ("/auth/cancel-on-authenticate", do_cancel_on_authenticate);
g_test_add_func ("/auth/auth-uri", do_auth_uri_test);
g_test_add_func ("/auth/cancel-request-on-authenticate", do_cancel_request_on_authenticate);
+ g_test_add_func ("/auth/multiple-algorithms", do_multiple_digest_algorithms);
ret = g_test_run ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]