]> sjero.net Git - wget/commitdiff
[svn] Fix bugs in NTML handling.
authorhniksic <devnull@localhost>
Fri, 22 Apr 2005 15:15:25 +0000 (08:15 -0700)
committerhniksic <devnull@localhost>
Fri, 22 Apr 2005 15:15:25 +0000 (08:15 -0700)
src/ChangeLog
src/http-ntlm.c
src/http.c

index 61f178f3454cb03e668e1be112448711c890e956..8eb30a28caed2c4dc02c21ccba364084859614b8 100644 (file)
@@ -1,3 +1,13 @@
+2005-04-22  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * 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.
+
 2005-04-21  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * gen_sslfunc.c (ssl_init): Fix warning message text; mark the
index 91ca02801b0db622d570aab84df21c34ca8fe910..39e738ca0f046f44e51492167ce41dc00007774e 100644 (file)
@@ -147,6 +147,8 @@ int ntlm_input (struct ntlmdata *ntlm, const char *header)
       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 @@ int ntlm_input (struct ntlmdata *ntlm, const char *header)
   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 @@ char *ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd,
   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:
 
@@ -412,6 +420,8 @@ char *ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd,
     const char *usr;
     int userlen;
 
+    DEBUGP (("Creating a type-3 NTLM message.\n"));
+
     usr = strchr(user, '\\');
     if(!usr)
       usr = strchr(user, '/');
index b233e0ef3aad9dd616187744ff032b66418c23b5..7f7fb3f71e34dd6bdf2349de97ac3de608d39674 100644 (file)
@@ -1071,9 +1071,9 @@ free_hstat (struct http_stat *hs)
 
 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 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   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 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        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 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        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 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        }
       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 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
          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);
@@ -2833,26 +2845,33 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
 }
 #endif /* ENABLE_DIGEST */
 
+/* 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)
 
-#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]))
+/* 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 @@ known_authentication_scheme_p (const char *au)
 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 ();
+    }
 }
 \f
 void