From 5edc97f3f8dec4f4bc32a2e7acdb06872a1bbb79 Mon Sep 17 00:00:00 2001 From: Tim Ruehsen Date: Sat, 22 Nov 2014 22:00:28 +0100 Subject: [PATCH] Select most secure challenge from WWW-Authenticate This patch also adds support for multiple challenges per WWW-Authenticate header line. The test Test-auth-both.py now succeeds and thus is taken away from XFAIL_TESTS (expected to fail tests). --- src/ChangeLog | 5 +++ src/http.c | 67 +++++++++++++++++++++++++----- testenv/ChangeLog | 4 ++ testenv/Makefile.am | 3 +- testenv/server/http/http_server.py | 2 +- 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index f412fcad..699a95be 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2014-11-26 Tim Ruehsen + + * http.c: Select strongest challenge from WWW-Authenticate, + support multiple challenges per header line. + 2014-11-26 Tim Ruehsen * gnutls.c (ssl_connect_wget): Implement missing code for diff --git a/src/http.c b/src/http.c index 073d694b..c219fcf4 100644 --- a/src/http.c +++ b/src/http.c @@ -2381,26 +2381,64 @@ read_header: the value "negotiate", and other(s) with data. Loop over all the occurrences and pick the one we recognize. */ int wapos; + char *buf; + const char *www_authenticate = NULL; const char *wabeg, *waend; - char *www_authenticate = NULL; - for (wapos = 0; - (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos, + const char *digest = NULL, *basic = NULL, *ntlm = NULL; + for (wapos = 0; !ntlm + && (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos, &wabeg, &waend)) != -1; ++wapos) - if (known_authentication_scheme_p (wabeg, waend)) - { - BOUNDED_TO_ALLOCA (wabeg, waend, www_authenticate); - break; - } + { + param_token name, value; - if (!www_authenticate) + BOUNDED_TO_ALLOCA (wabeg, waend, buf); + www_authenticate = buf; + + for (;!ntlm;) + { + /* extract the auth-scheme */ + while (c_isspace (*www_authenticate)) www_authenticate++; + name.e = name.b = www_authenticate; + while (*name.e && !c_isspace (*name.e)) name.e++; + + if (name.b == name.e) + break; + + DEBUGP (("Auth scheme found '%.*s'\n", (int) (name.e - name.b), name.b)); + + if (known_authentication_scheme_p (name.b, name.e)) + { + if (BEGINS_WITH (name.b, "NTLM")) + { + ntlm = name.b; + break; /* this is the most secure challenge, stop here */ + } + else if (!digest && BEGINS_WITH (name.b, "Digest")) + digest = name.b; + else if (!basic && BEGINS_WITH (name.b, "Basic")) + basic = name.b; + } + + /* now advance over the auth-params */ + www_authenticate = name.e; + DEBUGP (("Auth param list '%s'\n", www_authenticate)); + while (extract_param (&www_authenticate, &name, &value, ',', NULL) && name.b && value.b) + { + DEBUGP (("Auth param %.*s=%.*s\n", + (int) (name.e - name.b), name.b, (int) (value.e - value.b), value.b)); + } + } + } + + if (!basic && !digest && !ntlm) { /* If the authentication header is missing or unrecognized, there's no sense in retrying. */ logputs (LOG_NOTQUIET, _("Unknown authentication scheme.\n")); } else if (!basic_auth_finished - || !BEGINS_WITH (www_authenticate, "Basic")) + || !basic) { char *pth = url_full_path (u); const char *value; @@ -2408,6 +2446,15 @@ read_header: auth_stat = xmalloc (sizeof (uerr_t)); *auth_stat = RETROK; + if (ntlm) + www_authenticate = ntlm; + else if (digest) + www_authenticate = digest; + else + www_authenticate = basic; + + logprintf (LOG_NOTQUIET, _("Authentication selected: %s\n"), www_authenticate); + value = create_authorization_line (www_authenticate, user, passwd, request_method (req), diff --git a/testenv/ChangeLog b/testenv/ChangeLog index fb34a97f..d1419cbc 100644 --- a/testenv/ChangeLog +++ b/testenv/ChangeLog @@ -1,3 +1,7 @@ +2014-11-26 Tim Ruehsen + + * Makefile.am: Removed Test-auth-both.py from XFAIL_TESTS + 2014-11-21 Tim Ruehsen * server/http/http_server.py: Fixed typo Blackisted to Blacklisted diff --git a/testenv/Makefile.am b/testenv/Makefile.am index 8574ad98..1ed52b29 100644 --- a/testenv/Makefile.am +++ b/testenv/Makefile.am @@ -53,7 +53,8 @@ if HAVE_PYTHON3 Test-504.py \ Test--spider-r.py - XFAIL_TESTS = Test-auth-both.py + # added test cases expected to fail here and under TESTS + XFAIL_TESTS = endif EXTRA_DIST = certs conf exc misc server test README $(TESTS) $(XFAIL_TESTS) diff --git a/testenv/server/http/http_server.py b/testenv/server/http/http_server.py index 52c49131..9128b3eb 100644 --- a/testenv/server/http/http_server.py +++ b/testenv/server/http/http_server.py @@ -207,8 +207,8 @@ class _Handler (BaseHTTPRequestHandler): def send_challenge (self, auth_type): auth_type = auth_type.lower() if auth_type == "both": - self.send_challenge ("digest") self.send_challenge ("basic") + self.send_challenge ("digest") return if auth_type == "basic": challenge_str = 'BasIc realm="Wget-Test"'