cURL
Haxx ad
libcurl

curl's project page on SourceForge.net

Sponsors:
Haxx

cURL > Mailing List > Monthly Index > Single Mail

curl-tracker Archives

[ curl-Bugs-3545398 ] authp->picked not being cleared on failed auth

From: SourceForge.net <noreply_at_sourceforge.net>
Date: Wed, 18 Jul 2012 07:54:31 -0700

Bugs item #3545398, was opened at 2012-07-18 07:06
Message generated for change (Comment added) made by jmasonrim
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3545398&group_id=976

Please note that this message will contain a full copy of the comment thread,
including the initial issue submission, for this request,
not just the latest update.
Category: libcurl
Group: wrong behaviour
Status: Open
Resolution: None
Priority: 5
Private: No
Submitted By: Joe Mason (jmasonrim)
Assigned to: Daniel Stenberg (bagder)
Summary: authp->picked not being cleared on failed auth

Initial Comment:
Using libcurl 7.24.0:

I'm trying to set up an app to connect to a server that supports GSS-Negotiate and NTLM auth. If the client-side auth setup is incorrect I want to fall back to using NTLM.

So this is pretty simple:

void doRequest(const char* url, const char* username, const char* password, bool allowNegotiate)
{
    int numErrors = 0;

    CURL* handle = curl_easy_init();
    curl_easy_setopt(handle, CURLOPT_URL, url);
    curl_easy_setopt(handle, CURLOPT_HTTPGET, true);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback); // increments numErrors on every 401
    curl_easy_setopt(handle, CURLOPT_HEADERDATA, &numErrors);
    curl_easy_setopt(handle, CURLOPT_USERNAME, username);
    curl_easy_setopt(handle, CURLOPT_PASSWORD, password);
    long allowedAuthSchemes = CURLAUTH_NTLM;
    if (allowNegotiate) {
        allowedAuthSchemes |= CURLAUTH_GSSNEGOTIATE;
    }
    curl_easy_setopt(handle, CURLOPT_HTTPAUTH, allowedAuthSchemes);
    curl_easy_perform(handle);
    curl_easy_cleanup(handle);

    // expect one 401 to be sent during auth setup; anything more than that means auth failed
    if (numErrors > 1 && allowNegotiate) {
        // try again without Negotiate
        doRequest(url, username, password, false);
    }
}

We set up a request allowing CURLAUTH_NTLM | CURLAUTH_GSSNEGOTIATE and, if it receives too many 401 errors, send it again with just CURLAUTH_NTLM.

To both requests, the server will send back:

WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM

The problem comes in Curl_http_input_auth:

    if(checkprefix("GSS-Negotiate", start) ||
       checkprefix("Negotiate", start)) {
      int neg;
      *availp |= CURLAUTH_GSSNEGOTIATE;
      authp->avail |= CURLAUTH_GSSNEGOTIATE;

      if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
        if(data->state.negotiate.state == GSS_AUTHSENT) {
          /* if we sent GSS authentication in the outgoing request and we get
             this back, we're in trouble */
          infof(data, "Authentication problem. Ignoring this.\n");
          data->state.authproblem = TRUE;
        }
        else {
          neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
          if(neg == 0) {
            DEBUGASSERT(!data->req.newurl);
            data->req.newurl = strdup(data->change.url);
            if(!data->req.newurl)
              return CURLE_OUT_OF_MEMORY;
            data->state.authproblem = FALSE;
            /* we received GSS auth info and we dealt with it fine */
            data->state.negotiate.state = GSS_AUTHRECV;
          }
          else
            data->state.authproblem = TRUE;
        }
      }
    }

The first time through, it reads the Negotiate header, updates "avail", and then does nothing since "picked" is 0. After reading all the headers, it will set set "picked" to CURLAUTH_GSSNEGOTIATE because that's the highest priority, and attempt to output a negotiate header. (Which will fail because, for this test, GSS auth is set up wrong.)

The second time through, it reads the Negotiate header, updates "avail", and then goes into the "if" statement because "picked" is still CURLAUTH_NEGOTIATE from the first pass. Even though we're using a different handle! It then calls Curl_input_negotiate, which will call some gss functions and return an error code when they fail. It then sets authproblem to TRUE, which means that even though it reads the NTLM header it will not continue to send the NTLM auth as we want. It seems to me that it should be ignoring this Negotiate header entirely, since in this second pass CURLAUTH_GSSNEGOTIATE isn't even included in CURLOPT_HTTPAUTH.

(I've also tried adding CURLOPT_FRESH_CONNECT when allowNegotiate is false, to force it to use a new connection, but "picked" is still set.)

So it looks like "picked" should be cleared at some point when Negotiate auth fails, so that it doesn't keep trying to use it for further requests. But I'm not sure where this is should be done.

Another option would be to change that if statement to "if(authp->picked == CURLAUTH_GSSNEGOTIATE && authp->want & CURLAUTH_GSSNEGOTIATE)", so that if we don't want negotiate it just ignores the value of picked here, but I think that would just be hiding the problem.

----------------------------------------------------------------------

>Comment By: Joe Mason (jmasonrim)
Date: 2012-07-18 07:54

Message:
Actually I think picked needs to be cleared even on a successful auth. I
have a server that serves most resources with "WWW-Authenticate: Negotiate"
and "WWW-Authenticate: Basic" headers, but one resource (call it B) that
only has Basic for some reason. When fetching a page that includes
subresources A, B, C, curl chooses Negotiate for A and Basic for B (since
there's no Negotiate header to trigger the code above). I would expect curl
to choose Negotiate again for C since it's most secure, but actually it
chooses Basic again because picked is still set from the last request.

----------------------------------------------------------------------

You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3545398&group_id=976
Received on 2012-07-18

These mail archives are generated by hypermail.

donate! Page updated January 05, 2012.
web site info

File upload with ASP.NET