[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>
* 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;
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, '/');

View 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 ();
}
}
void