Thanks for the report. Having access to your test server allowed me
to work on this.
The following patch (now applied) fixes all the bugs you noticed
except #3. Which means that the infrastructure is all there, all the
right functions are called, we just need to figure out what we are
doing wrong. I'll look into it later, I don't have time right now.
2005-04-22 Hrvoje Niksic <[EMAIL PROTECTED]>
* http.c (gethttp): Handle multiple WWW-Authentication headers,
only one of which is recognized. Those are sent by IIS with NTLM
authorization.
(create_authorization_line): Propagate information whether
authorization is finished.
(gethttp): Only stop authorization when it's really finished, not
after fixed two steps.
Index: src/http-ntlm.c
===================================================================
RCS file: /pack/anoncvs/wget/src/http-ntlm.c,v
retrieving revision 1.4
diff -u -r1.4 http-ntlm.c
--- src/http-ntlm.c 2005/04/21 14:00:11 1.4
+++ src/http-ntlm.c 2005/04/22 15:10:44
@@ -147,6 +147,8 @@
int size;
char *buffer = (char *) alloca (strlen (header));
+ DEBUGP (("Received a type-2 NTLM message.\n"));
+
size = base64_decode (header, buffer);
if (size < 0)
return 0; /* malformed base64 from server */
@@ -162,8 +164,12 @@
else
{
if (ntlm->state >= NTLMSTATE_TYPE1)
- return 0; /* this is an error */
+ {
+ DEBUGP (("Unexpected empty NTLM message.\n"));
+ return 0; /* this is an error */
+ }
+ DEBUGP (("Empty NTLM message, starting transaction.\n"));
ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
}
@@ -326,6 +332,8 @@
default: /* for the weird cases we (re)start here */
hostoff = 32;
domoff = hostoff + hostlen;
+
+ DEBUGP (("Creating a type-1 NTLM message.\n"));
/* Create and send a type-1 message:
@@ -411,6 +419,8 @@
#endif
const char *usr;
int userlen;
+
+ DEBUGP (("Creating a type-3 NTLM message.\n"));
usr = strchr(user, '\\');
if(!usr)
Index: src/http.c
===================================================================
RCS file: /pack/anoncvs/wget/src/http.c,v
retrieving revision 1.162
diff -u -r1.162 http.c
--- src/http.c 2005/04/20 20:15:05 1.162
+++ src/http.c 2005/04/22 15:10:48
@@ -1071,9 +1071,9 @@
static char *create_authorization_line PARAMS ((const char *, const char *,
const char *, const char *,
- const char *));
+ const char *, int *));
static char *basic_authentication_encode PARAMS ((const char *, const char *));
-static int known_authentication_scheme_p PARAMS ((const char *));
+static int known_authentication_scheme_p PARAMS ((const char *, const char *));
time_t http_atotm PARAMS ((const char *));
@@ -1109,8 +1109,9 @@
int sock = -1;
int flags;
- /* Whether authorization has been already tried. */
- int auth_tried_already;
+ /* Set to 1 when the authorization has failed permanently and should
+ not be tried again. */
+ int auth_finished = 0;
/* Whether our connection to the remote host is through SSL. */
int using_ssl = 0;
@@ -1176,8 +1177,6 @@
know the local filename so we can save to it. */
assert (*hs->local_file != NULL);
- auth_tried_already = 0;
-
/* Initialize certain elements of struct http_stat. */
hs->len = 0;
hs->contlen = -1;
@@ -1588,7 +1587,7 @@
CLOSE_FINISH (sock);
else
CLOSE_INVALIDATE (sock);
- if (auth_tried_already || !(user && passwd))
+ if (auth_finished || !(user && passwd))
{
/* If we have tried it already, then there is not point
retrying it. */
@@ -1596,13 +1595,26 @@
}
else
{
- char *www_authenticate = resp_header_strdup (resp,
- "WWW-Authenticate");
- /* If the authentication scheme is unknown or if it's the
- "Basic" authentication (which we try by default), there's
- no sense in retrying. */
+ /* IIS sometimes sends two instances of WWW-Authenticate
+ header, one with the keyword "negotiate", and other with
+ useful data. Loop over all occurrences of this header
+ and use the one we recognize. */
+ int wapos;
+ const char *wabeg, *waend;
+ char *www_authenticate = NULL;
+ for (wapos = 0;
+ (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos,
+ &wabeg, &waend)) != -1;
+ ++wapos)
+ if (known_authentication_scheme_p (wabeg, waend))
+ {
+ www_authenticate = strdupdelim (wabeg, waend);
+ break;
+ }
+ /* If the authentication header is missing or recognized, or
+ if the authentication scheme is "Basic" (which we send by
+ default), there's no sense in retrying. */
if (!www_authenticate
- || !known_authentication_scheme_p (www_authenticate)
|| BEGINS_WITH (www_authenticate, "Basic"))
{
xfree_null (www_authenticate);
@@ -1611,13 +1623,13 @@
else
{
char *pth;
- auth_tried_already = 1;
pth = url_full_path (u);
request_set_header (req, "Authorization",
create_authorization_line (www_authenticate,
user, passwd,
request_method
(req),
- pth),
+ pth,
+ &auth_finished),
rel_value);
xfree (pth);
xfree (www_authenticate);
@@ -2832,27 +2844,34 @@
return res;
}
#endif /* ENABLE_DIGEST */
-
-#define BEGINS_WITH(line, string_constant) \
- (!strncasecmp (line, string_constant, sizeof (string_constant) - 1) \
- && (ISSPACE (line[sizeof (string_constant) - 1]) \
- || !line[sizeof (string_constant) - 1]))
+/* Computing the size of a string literal must take into account that
+ value returned by sizeof includes the terminating \0. */
+#define STRSIZE(literal) (sizeof (literal) - 1)
+
+/* Whether chars in [b, e) begin with the literal string provided as
+ first argument and are followed by whitespace or terminating \0.
+ The comparison is case-insensitive. */
+#define STARTS(literal, b, e) \
+ ((e) - (b) >= STRSIZE (literal) \
+ && 0 == strncasecmp (b, literal, STRSIZE (literal)) \
+ && ((e) - (b) == STRSIZE (literal) \
+ || ISSPACE (b[STRSIZE (literal)])))
static int
-known_authentication_scheme_p (const char *au)
+known_authentication_scheme_p (const char *hdrbeg, const char *hdrend)
{
- return BEGINS_WITH (au, "Basic")
+ return STARTS ("Basic", hdrbeg, hdrend)
#ifdef ENABLE_DIGEST
- || BEGINS_WITH (au, "Digest")
+ || STARTS ("Digest", hdrbeg, hdrend)
#endif
#ifdef ENABLE_NTLM
- || BEGINS_WITH (au, "NTLM")
+ || STARTS ("NTLM", hdrbeg, hdrend)
#endif
;
}
-#undef BEGINS_WITH
+#undef STARTS
/* Create the HTTP authorization request header. When the
`WWW-Authenticate' response header is seen, according to the
@@ -2862,25 +2881,34 @@
static char *
create_authorization_line (const char *au, const char *user,
const char *passwd, const char *method,
- const char *path)
+ const char *path, int *finished)
{
- if (0 == strncasecmp (au, "Basic", 5))
- return basic_authentication_encode (user, passwd);
+ /* We are called only with known schemes, so we can dispatch on the
+ first letter. */
+ switch (TOUPPER (*au))
+ {
+ case 'B': /* Basic */
+ *finished = 1;
+ return basic_authentication_encode (user, passwd);
#ifdef ENABLE_DIGEST
- if (0 == strncasecmp (au, "Digest", 6))
- return digest_authentication_encode (au, user, passwd, method, path);
+ case 'D': /* Digest */
+ *finished = 1;
+ return digest_authentication_encode (au, user, passwd, method, path);
#endif
#ifdef ENABLE_NTLM
- if (0 == strncasecmp (au, "NTLM", 4))
- {
- int ok = ntlm_input (&pconn.ntlm, au);
- if (!ok)
- return NULL;
- /* #### we shouldn't ignore the OK that ntlm_output returns. */
- return ntlm_output (&pconn.ntlm, user, passwd, &ok);
- }
+ case 'N': /* NTLM */
+ if (!ntlm_input (&pconn.ntlm, au))
+ {
+ *finished = 1;
+ return NULL;
+ }
+ return ntlm_output (&pconn.ntlm, user, passwd, finished);
#endif
- return NULL;
+ default:
+ /* We shouldn't get here -- this function should be only called
+ with values approved by known_authentication_scheme_p. */
+ abort ();
+ }
}
void