[svn] Fix bugs in NTML handling.

This commit is contained in:
hniksic 2005-04-22 08:15:25 -07:00
parent e09eb765f4
commit add6c24a19
3 changed files with 87 additions and 39 deletions

View 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> 2005-04-21 Hrvoje Niksic <hniksic@xemacs.org>
* gen_sslfunc.c (ssl_init): Fix warning message text; mark the * gen_sslfunc.c (ssl_init): Fix warning message text; mark the

View File

@ -147,6 +147,8 @@ int ntlm_input (struct ntlmdata *ntlm, const char *header)
int size; int size;
char *buffer = (char *) alloca (strlen (header)); char *buffer = (char *) alloca (strlen (header));
DEBUGP (("Received a type-2 NTLM message.\n"));
size = base64_decode (header, buffer); size = base64_decode (header, buffer);
if (size < 0) if (size < 0)
return 0; /* malformed base64 from server */ return 0; /* malformed base64 from server */
@ -162,8 +164,12 @@ int ntlm_input (struct ntlmdata *ntlm, const char *header)
else else
{ {
if (ntlm->state >= NTLMSTATE_TYPE1) 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 */ 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 */ default: /* for the weird cases we (re)start here */
hostoff = 32; hostoff = 32;
domoff = hostoff + hostlen; domoff = hostoff + hostlen;
DEBUGP (("Creating a type-1 NTLM message.\n"));
/* Create and send a type-1 message: /* 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; const char *usr;
int userlen; int userlen;
DEBUGP (("Creating a type-3 NTLM message.\n"));
usr = strchr(user, '\\'); usr = strchr(user, '\\');
if(!usr) if(!usr)
usr = strchr(user, '/'); usr = strchr(user, '/');

View File

@ -1071,9 +1071,9 @@ free_hstat (struct http_stat *hs)
static char *create_authorization_line PARAMS ((const char *, const char *, static char *create_authorization_line PARAMS ((const char *, const char *,
const char *, const char *, const char *, const char *,
const char *)); const char *, int *));
static char *basic_authentication_encode PARAMS ((const char *, const char *)); 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 *)); 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 sock = -1;
int flags; int flags;
/* Whether authorization has been already tried. */ /* Set to 1 when the authorization has failed permanently and should
int auth_tried_already; not be tried again. */
int auth_finished = 0;
/* Whether our connection to the remote host is through SSL. */ /* Whether our connection to the remote host is through SSL. */
int using_ssl = 0; 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. */ know the local filename so we can save to it. */
assert (*hs->local_file != NULL); assert (*hs->local_file != NULL);
auth_tried_already = 0;
/* Initialize certain elements of struct http_stat. */ /* Initialize certain elements of struct http_stat. */
hs->len = 0; hs->len = 0;
hs->contlen = -1; hs->contlen = -1;
@ -1588,7 +1587,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
CLOSE_FINISH (sock); CLOSE_FINISH (sock);
else else
CLOSE_INVALIDATE (sock); 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 /* If we have tried it already, then there is not point
retrying it. */ retrying it. */
@ -1596,13 +1595,26 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
} }
else else
{ {
char *www_authenticate = resp_header_strdup (resp, /* IIS sometimes sends two instances of WWW-Authenticate
"WWW-Authenticate"); header, one with the keyword "negotiate", and other with
/* If the authentication scheme is unknown or if it's the useful data. Loop over all occurrences of this header
"Basic" authentication (which we try by default), there's and use the one we recognize. */
no sense in retrying. */ 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 if (!www_authenticate
|| !known_authentication_scheme_p (www_authenticate)
|| BEGINS_WITH (www_authenticate, "Basic")) || BEGINS_WITH (www_authenticate, "Basic"))
{ {
xfree_null (www_authenticate); xfree_null (www_authenticate);
@ -1611,13 +1623,13 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
else else
{ {
char *pth; char *pth;
auth_tried_already = 1;
pth = url_full_path (u); pth = url_full_path (u);
request_set_header (req, "Authorization", request_set_header (req, "Authorization",
create_authorization_line (www_authenticate, create_authorization_line (www_authenticate,
user, passwd, user, passwd,
request_method (req), request_method (req),
pth), pth,
&auth_finished),
rel_value); rel_value);
xfree (pth); xfree (pth);
xfree (www_authenticate); xfree (www_authenticate);
@ -2833,26 +2845,33 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
} }
#endif /* ENABLE_DIGEST */ #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) \ /* Whether chars in [b, e) begin with the literal string provided as
(!strncasecmp (line, string_constant, sizeof (string_constant) - 1) \ first argument and are followed by whitespace or terminating \0.
&& (ISSPACE (line[sizeof (string_constant) - 1]) \ The comparison is case-insensitive. */
|| !line[sizeof (string_constant) - 1])) #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 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 #ifdef ENABLE_DIGEST
|| BEGINS_WITH (au, "Digest") || STARTS ("Digest", hdrbeg, hdrend)
#endif #endif
#ifdef ENABLE_NTLM #ifdef ENABLE_NTLM
|| BEGINS_WITH (au, "NTLM") || STARTS ("NTLM", hdrbeg, hdrend)
#endif #endif
; ;
} }
#undef BEGINS_WITH #undef STARTS
/* Create the HTTP authorization request header. When the /* Create the HTTP authorization request header. When the
`WWW-Authenticate' response header is seen, according to the `WWW-Authenticate' response header is seen, according to the
@ -2862,25 +2881,34 @@ known_authentication_scheme_p (const char *au)
static char * static char *
create_authorization_line (const char *au, const char *user, create_authorization_line (const char *au, const char *user,
const char *passwd, const char *method, const char *passwd, const char *method,
const char *path) const char *path, int *finished)
{ {
if (0 == strncasecmp (au, "Basic", 5)) /* We are called only with known schemes, so we can dispatch on the
return basic_authentication_encode (user, passwd); first letter. */
switch (TOUPPER (*au))
{
case 'B': /* Basic */
*finished = 1;
return basic_authentication_encode (user, passwd);
#ifdef ENABLE_DIGEST #ifdef ENABLE_DIGEST
if (0 == strncasecmp (au, "Digest", 6)) case 'D': /* Digest */
return digest_authentication_encode (au, user, passwd, method, path); *finished = 1;
return digest_authentication_encode (au, user, passwd, method, path);
#endif #endif
#ifdef ENABLE_NTLM #ifdef ENABLE_NTLM
if (0 == strncasecmp (au, "NTLM", 4)) case 'N': /* NTLM */
{ if (!ntlm_input (&pconn.ntlm, au))
int ok = ntlm_input (&pconn.ntlm, au); {
if (!ok) *finished = 1;
return NULL; return NULL;
/* #### we shouldn't ignore the OK that ntlm_output returns. */ }
return ntlm_output (&pconn.ntlm, user, passwd, &ok); return ntlm_output (&pconn.ntlm, user, passwd, finished);
}
#endif #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 void