#833 POST + NTLM broken

closed-fixed
http (200)
5
2014-03-13
2009-05-31
Aaron Oneal
No

Using 7.19.5, it is not possible to use NTLM and do a POST against a server like IIS7.

Flow:
1. Request w/ content length but no body! <== bad libcurl! must send body or 0 content length
2. Response 401 authorization required
3. Request (NTLM authorized) w/ content length + body
4. Response 400 bad request

IIS correctly tells libcurl to go away with its authorized request because it didn't finish sending the body from the first request.

I discovered this while trying to do what I would expect is a very common scenario -- use libcurl (via Axis2/C) to send a SOAP request to a web service.

Discussion

1 2 3 > >> (Page 1 of 3)
  • Aaron Oneal
    Aaron Oneal
    2009-05-31

    Also worth mentioning, I'm using MinGW and building as follows:
    mingw32-make -f Makefile.m32 libcurl.a SSL=1 ZLIB=1 SSPI=1 IPV6=1

     
  • Dan Fandrich
    Dan Fandrich
    2009-05-31

    This is a common scenario and it has several tests in the curl test suite (e.g. 267, 553, 1100). How does your scenario differ from those? Are you sure you're not running into libcurl's Expect: 100-continue behaviour, which is desired? Can you post the log of your failing session so we can see exactly what's going on?

     
  • Aaron Oneal
    Aaron Oneal
    2009-05-31

    The Expect header is not being sent in the request and the server responds immediately with a 401. I looked at the Axis2/C code and it explicitly sets the header to "Expect:" which I presume is how you tell libcurl not to send the header, though I don't know why they would do that. I'll have a look at those tests to see if I can compare it with what the Axis2/C code is doing and will also attach the relevant file here.

    I have attached a NetMon 3.3 trace which demonstrates the problem.

    You can grab NetMon here if you don't have it handy:
    http://www.microsoft.com/downloads/details.aspx?FamilyID=983b941d-06cb-4658-b7f6-3088333d062f&displaylang=en

     
  • Aaron Oneal
    Aaron Oneal
    2009-05-31

    Axis2/C file calling libcurl

     
    Attachments
  • Aaron Oneal
    Aaron Oneal
    2009-05-31

    Libcurl verbose output

     
  • Aaron Oneal
    Aaron Oneal
    2009-05-31

    I checked out lib553.c and see it's using CURLOPT_READFUNCTION instead of CURLOPT_POSTFILEDS, but otherwise I don't notice anything out of the ordinary (besides it not using NTLM). The other tests are in a format I'm not familiar with.

    It could be the server the test suite runs against isn't waiting around for the body of the first request once it sends the 401 and so the test is passing.

    You can see from the traces though what's going across the wire and this would seem to indicate a problem.

     
  • isn't this then as test 267? It _does_ send a Content-Length: 0 btw so your case must obviously be different. The question is probably How?

     
  • Aaron Oneal
    Aaron Oneal
    2009-06-02

    I got a debug build going and figured out what's going on. Axis2/C is actually setting a content-length header, and Curl is using it on the first request even though it knows it's not going to send the body. So, that's not right. Here's the problem area starting on line 2774 of http.c (and there are similar problems with PUT)

    case HTTPREQ_POST:
    /* this is the simple POST, using x-www-form-urlencoded style */

    if(conn->bits.authneg)
    postsize = 0;
    else {
    /* figure out the size of the postfields */
    postsize = (data->set.postfieldsize != -1)?
    data->set.postfieldsize:
    (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
    }
    if(!data->req.upload_chunky) {
    /* We only set Content-Length and allow a custom Content-Length if
    we don't upload data chunked, as RFC2616 forbids us to set both
    kinds of headers (Transfer-Encoding: chunked and Content-Length) */

    if(!checkheaders(data, "Content-Length:")) {
    /* we allow replacing this header, although it isn't very wise to
    actually set your own */
    result = add_bufferf(req_buffer,
    "Content-Length: %" FORMAT_OFF_T"\r\n",
    postsize);
    if(result)
    return result;
    }
    }

    You can see that it correctly sets the postsize to 0 because of conn->bits.authneg, but because the caller specified content-length, it uses that value instead. Since Curl knows it's not going to send the body, it should remove the caller specified content-length on the first request and use it on the subsequent one when it actually intends to send the body.

     
  • Dan Fandrich
    Dan Fandrich
    2009-06-02

    And now you know why "it isn't very wise to actually set your own" Content-Length header. What's the matter with just setting CURLOPT_POSTFIELDSIZE_LARGE?

     
1 2 3 > >> (Page 1 of 3)