New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP POST with Negotiate fails #1261
Comments
Can anybody reproduce this? |
It happens to our system also. Unless the "Expect: ..." is suppressed it doesn't work. Due to lack of time, setting this header is our official workaround. Although not ideal as curl has to re-send the data after auth. |
For me suppressing the Expect-Header works if directly connected to the HTTP server for small requests, but if going through a proxy (CCProxy 8.0 on Windows in my test), the first request which is a POST request in my case succeeds, but the second request (on the same cURL handle) which is a GET request does not get any response and hangs/times out. Could be that this is a CCProxy issue, but I have also seen that problem without proxy after some more requests have already been performed with cURL, maybe depending on the state of the cURL's connection cache. |
I'd expect Negotiate POST to be not optimal in case of multi-round negotiation (usually NTLM inside), because there is no special handling like the 'authneg' logic for NTLM, see more in this mail thread: However, it sounds like some flows (like with Expect) behave better than others so perhaps it could be fixed up to at least work. |
No, I can't get it working with uploading big files. cURL calls the read function multiple times (for each challenge request) twice, in combination with the seek function and and after that the server responds always with 401; the full content is never transferred to the server. #define CURL_STATICLIB
#include <curl/curl.h>
// Test case #1
#define TEST_NEGOTIATE
#define UPLOAD_SIZE (1 * 1024 * 1024)
#define TEST_SUPPRESS_EXPECT_CONTINUE
// Test case #2
/*
#define TEST_NTLM
#define UPLOAD_SIZE (1 * 1024 * 1024)
*/
// Test case #3
/*
#define TEST_NEGOTIATE
#define UPLOAD_SIZE 10
#define TEST_SUPPRESS_EXPECT_CONTINUE
*/
static int CURLdebugfunction(CURL *, curl_infotype infotype, char *str, size_t len, void *)
{
const char *infostring = "CURLINFO_???";
switch (infotype) {
#define ENTRY(NAME) case NAME: infostring = TEXT(#NAME); break;
ENTRY(CURLINFO_TEXT)
ENTRY(CURLINFO_HEADER_IN)
ENTRY(CURLINFO_HEADER_OUT)
ENTRY(CURLINFO_DATA_IN)
ENTRY(CURLINFO_DATA_OUT)
#undef ENTRY
}
printf("CURLDEBUGINFO %s: %*.*s\n",
(const char *)infostring, (int)len, (int)len, (const char *)str);
return 0;
}
size_t CURLheaderfunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
char *hdr = (char *)malloc(size*nmemb + 1);
if (hdr) {
memcpy(hdr, ptr, size*nmemb);
hdr[size*nmemb] = '\0';
printf("RECEIVED HEADER: %s", (const char *)hdr);
free(hdr);
}
else {
printf("HEADER: OUT OF MEMORY\n");
}
return size*nmemb;
}
static size_t CURLreadpos = 0;
size_t CURLreadfunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t read = 0;
for (; CURLreadpos < UPLOAD_SIZE && read < size * nmemb;) {
((char *)ptr)[read] = 'a';
read++;
CURLreadpos++;
}
printf("READ REQUEST DATA: %llu -> %llu\n", size*nmemb, (UINT64)read);
return read;
}
int CURLseekfunction(void *ptr, curl_off_t offset, int origin)
{
printf("SEEK: %lld, %d\n", (INT64)offset, origin);
if (origin == SEEK_SET) {
CURLreadpos = offset;
return CURL_SEEKFUNC_OK;
}
printf("SEEK: NOT IMPL\n");
return CURL_SEEKFUNC_CANTSEEK;
}
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://url-to-server-with-windows-auth-goes-here");
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, CURLheaderfunction);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, CURLreadfunction);
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, CURLseekfunction);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, CURLdebugfunction);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#if defined(TEST_NEGOTIATE)
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE);
#endif
#if defined(TEST_NTLM)
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
#endif
curl_easy_setopt(curl, CURLOPT_USERNAME, "");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, UPLOAD_SIZE);
#if defined(TEST_SUPPRESS_EXPECT_CONTINUE)
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Expect:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
#endif
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
printf("curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else {
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
printf("CURL STATUS: %ld\n", http_code);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
} test program outputNegotiate with suppressing expect/continue and uploading 1 MiB NTLM with uploading 1 MiB (not suppressing expect/continue) Negotiate with suppressing expect/continue and uploading 10 bytes |
We have the same problem in our environment with negotiate, is there any possability to get it fixed? |
I have created a fix which solves the following problems with CURLAUTH_NEGOTIATE:
I changed the following:
I have tested this code (only on Windows!) by simultaneously downloading and uploading small and big amounts of data and reusing already created connections and either directly connecting to a server or doing negotiate authentication through a proxy server. I have not tested proxy negotiate authentication. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
PR is still active |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
The PR is still active. |
I did this
On Windows I want to perform a POST (or another custom) request (CURLOPT_CUSTOMREQUEST) with some data specified with CURLOPT_UPLOAD/CURLOPT_INFILESIZE_LARGE/CURLOPT_READFUNCTION/CURLOPT_SEEKFUNCTION by using negotiate authentication (CURLOPT_HTTPAUTH CURLAUTH_NEGOTIATE, CURLOPT_USERNAME (empty for Windows credentials), CURLOPT_PASSWORD (empty for Windows credentials)), but the authentication fails. Performing a GET request with the same setup succeeds.
Explicitly suppressing the "Expect: 100-continue" header seems to get the request working, but this is not an acceptable solution.
Using CURLOPT_POSTFIELDS instead of CURLOPT_UPLOAD, CURLOPT_READFUNCTION, CURLOPT_SEEKFUNCTION also works. But this is not the intended way to upload large amounts of data.
Using CURLOPT_POSTFIELDS with explicitly defining the "Expect: 100-continue" header fails.
Using CURLAUTH_NTLM instead of CURLAUTH_NEGOTIATE with the same cURL configuration works (also with the "Expect: 100-continue" mechanism). But this is less secure.
Connecting to an IIS with all authentication methods disabled but "Windows Authentication"
I expected the following
Negotiate authentication works also for POST/PUT-requests with CURLOPT_UPLOAD/CURLOPT_READFUNCTION. Handles with a GET request which performed a negotiate challenge can be reused to avoid challenge requests on every new request re-using a CURL handle.
curl/libcurl version
Used cURL Library: Version 7.52.1 64 Bit on Windows built with
-DBUILDING_LIBCURL -DHTTP_ONLY _DWITHOUT_MM_LIB -DUSE_SSLEAY -DUSE_OPENSSL -DCURL_WANTS_CA_BUNDLE_ENV -DENABLE_IPV6 -DUSE_WINDOWS_SSPI -DHAVE_STRUCT_POLLFD
operating system
Client: Tested on Windows 7 and Windows 2008 R2
connection close after negotiate challenge?
In cURL source code I found the following which seems to kill the connection after each negotiation. Why is this? Shouldn't the connection be kept after a (successful) negotiation for re-use?
If I comment out the "streamclose" part and I perform a GET request with negotiate authentication and reuse the same CURL handle for a following POST request with CURLOPT_UPLOAD the POST request succeeds as the authenticated connection will not be killed after the GET. Of course I cannot use this experimental trial as a workaround. I just wanted to find out how the connection handling of cURL works.
code to reproduce the problem:
test program output
Output test case # 1 (Negotiate upload)
#1_negotiate_upload_failed.txt
Output test case # 2 (Negotiate postfields)
#2_negotiate_postfields_succeeded.txt
Output test case # 3 (NTLM upload)
#3_ntlm_upload_succeeded.txt
Output test case # 4 (Negotiate upload with same handle after GET)
#4_negotiate_upload_after_successful_get_failed.txt
The text was updated successfully, but these errors were encountered: