diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | libsoup/auth/soup-auth-manager.c | 114 | ||||
-rw-r--r-- | tests/auth-test.c | 71 |
3 files changed, 142 insertions, 45 deletions
@@ -1,3 +1,3 @@ /po/libsoup.pot -/meson-build/ +/*build/ /html/ diff --git a/libsoup/auth/soup-auth-manager.c b/libsoup/auth/soup-auth-manager.c index 015c7c74..47ce2a2d 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,38 +371,56 @@ 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++) { + /* TODO: 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) { + g_strfreev (challenges); + return auth; + } + } + + g_strfreev (challenges); } - return auth; + return NULL; } 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) { + challenges = g_new0 (char*, 2); + challenges[0] = g_strdup (scheme); + 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..5dbc319b 100644 --- a/tests/auth-test.c +++ b/tests/auth-test.c @@ -1760,6 +1760,76 @@ 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); + 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 +1861,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 (); |