diff options
author | Joe Mason <jmason@rim.com> | 2012-08-08 18:15:04 -0400 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2012-08-31 22:53:17 +0200 |
commit | 3b7d31c1eddc52431d8944aae6c7ab8aa7e29570 (patch) | |
tree | e7add305a666bee9085057c9fe47b23455f19828 | |
parent | d4af0bb8f6b7b0f3cee8f387c8fa4dba69ba63d8 (diff) |
NTLM: verify multiple connections work
Add test2032 to test that NTLM does not switch connections in the middle
of the handshake
-rw-r--r-- | tests/data/Makefile.am | 3 | ||||
-rw-r--r-- | tests/data/test2032 | 143 | ||||
-rw-r--r-- | tests/libtest/.gitignore | 1 | ||||
-rw-r--r-- | tests/libtest/Makefile.inc | 4 | ||||
-rw-r--r-- | tests/libtest/libntlmconnect.c | 246 |
5 files changed, 395 insertions, 2 deletions
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index fa1a7d7b3..ee6a82a3b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -98,7 +98,8 @@ test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ test2016 test2017 test2018 test2019 test2020 test2021 test2022 \ test2023 test2024 test2025 \ test2026 test2027 test2028 \ -test2029 test2030 test2031 +test2029 test2030 test2031 \ +test2032 EXTRA_DIST = $(TESTCASES) DISABLED diff --git a/tests/data/test2032 b/tests/data/test2032 new file mode 100644 index 000000000..4e1f230d9 --- /dev/null +++ b/tests/data/test2032 @@ -0,0 +1,143 @@ +<testcase> +<info> +<keywords> +HTTP +HTTP GET +HTTP Basic auth +HTTP NTLM auth +</keywords> +</info> +# Server-side +<reply> + +<!-- Basic auth --> +<data100> +HTTP/1.1 401 Need Basic or NTLM auth +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 29 +WWW-Authenticate: NTLM +WWW-Authenticate: Basic realm="testrealm" + +This is a bad password page! +</data100> + +<!-- NTML auth --> +<data200> +HTTP/1.1 401 Need Basic or NTLM auth (2) +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 27 +WWW-Authenticate: NTLM +WWW-Authenticate: Basic realm="testrealm" + +This is not the real page! +</data200> + +<data1201> +HTTP/1.1 401 NTLM intermediate (2) +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 33 +WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADAAAAAGggEAq6U1NAWaJCIAAAAAAAAAAAAAAAA4AAAATlRMTUF1dGg= + +This is still not the real page! +</data1201> + +<data1202> +HTTP/1.1 200 Things are fine in server land +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 32 + +Finally, this is the real page! +</data1202> + +<datacheck> +HTTP/1.1 401 Need Basic or NTLM auth +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 29 +WWW-Authenticate: NTLM +WWW-Authenticate: Basic realm="testrealm" + +This is a bad password page! +HTTP/1.1 401 Need Basic or NTLM auth +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 29 +WWW-Authenticate: NTLM +WWW-Authenticate: Basic realm="testrealm" + +This is a bad password page! +HTTP/1.1 401 NTLM intermediate (2) +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 33 +WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADAAAAAGggEAq6U1NAWaJCIAAAAAAAAAAAAAAAA4AAAATlRMTUF1dGg= + +HTTP/1.1 200 Things are fine in server land +Server: Microsoft-IIS/5.0 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 32 + +Finally, this is the real page! +</datacheck> + +</reply> + +# Client-side +<client> +<server> +http +</server> +<tool> +libntlmconnect +</tool> + + <name> +NTLM connection mapping + </name> + <setenv> +# we force our own host name, in order to make the test machine independent +CURL_GETHOSTNAME=curlhost +# we try to use the LD_PRELOAD hack, if not a debug build +LD_PRELOAD=%PWD/libtest/.libs/libhostname.so + </setenv> + <command> +http://%HOSTIP:%HTTPPORT/2032 +</command> +<precheck> +chkhostname curlhost +</precheck> +</client> + +# Verify data after the test has been "shot" +<verify> +<strip> +^User-Agent:.* +</strip> +<protocol> +GET /20320100 HTTP/1.1 +Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= +Host: 127.0.0.1:8990 +Accept: */* + +GET /20320100 HTTP/1.1 +Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M= +Host: 127.0.0.1:8990 +Accept: */* + +GET /20320200 HTTP/1.1 +Authorization: NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA= +Host: 127.0.0.1:8990 +Accept: */* + +GET /20320200 HTTP/1.1 +Authorization: NTLM TlRMTVNTUAADAAAAGAAYAEAAAAAYABgAWAAAAAAAAABwAAAACAAIAHAAAAAIAAgAeAAAAAAAAAAAAAAABoIBAI+/Fp9IERAQ74OsdNPbBpg7o8CVwLSO4DtFyIcZHUMKVktWIu92s2892OVpd2JzqnRlc3R1c2VyY3VybGhvc3Q= +Host: 127.0.0.1:8990 +Accept: */* + +</protocol> +</verify> +</testcase> diff --git a/tests/libtest/.gitignore b/tests/libtest/.gitignore index 7f8b619af..0a19470ab 100644 --- a/tests/libtest/.gitignore +++ b/tests/libtest/.gitignore @@ -1,3 +1,4 @@ chkhostname lib5[0-9][0-9] libauthretry +libntlmconnect diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 4b3edc11e..01594b8c9 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -19,7 +19,7 @@ noinst_PROGRAMS = chkhostname \ lib543 lib544 lib545 lib547 lib548 lib549 lib552 lib553 lib554 lib555 \ lib556 lib539 lib557 lib560 lib562 lib564 lib565 lib566 lib567 lib568 \ lib569 lib570 lib571 lib572 lib573 lib582 lib583 lib585 lib586 lib587 \ - lib590 lib591 lib597 lib598 lib599 libauthretry + lib590 lib591 lib597 lib598 lib599 libauthretry libntlmconnect chkhostname_SOURCES = chkhostname.c $(top_srcdir)/lib/curl_gethostname.c chkhostname_LDADD = @CURL_NETWORK_LIBS@ @@ -187,3 +187,5 @@ lib598_SOURCES = lib598.c $(SUPPORTFILES) lib599_SOURCES = lib599.c $(SUPPORTFILES) libauthretry_SOURCES = libauthretry.c $(SUPPORTFILES) + +libntlmconnect_SOURCES = libntlmconnect.c $(SUPPORTFILES) $(TESTUTIL) diff --git a/tests/libtest/libntlmconnect.c b/tests/libtest/libntlmconnect.c new file mode 100644 index 000000000..8486f3c31 --- /dev/null +++ b/tests/libtest/libntlmconnect.c @@ -0,0 +1,246 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "test.h" + +#include <assert.h> +#include "testutil.h" +#include "memdebug.h" + +#define TEST_HANG_TIMEOUT 5 * 1000 +#define MAX_EASY_HANDLES 3 + +CURL *easy[MAX_EASY_HANDLES]; +curl_socket_t sockets[MAX_EASY_HANDLES]; +int res = 0; + +static size_t callback(char* ptr, size_t size, size_t nmemb, void* data) +{ + int idx = ((CURL **) data) - easy; + curl_socket_t sock; + + char *output = malloc(size * nmemb + 1); + memcpy(output, ptr, size * nmemb); + output[size * nmemb] = '\0'; + fprintf(stdout, "%s", output); + free(output); + + res = curl_easy_getinfo(easy[idx], CURLINFO_LASTSOCKET, &sock); + if (CURLE_OK != res) { + fprintf(stderr, "Error reading CURLINFO_LASTSOCKET\n"); + return 0; + } + /* sock will only be set for NTLM requests; for others it is -1 */ + if (sock != -1) { + if (sockets[idx] == -1) { + /* Data was written for this request before the socket was detected by + multi_fdset. Record the socket now. */ + sockets[idx] = sock; + } + else if (sock != sockets[idx]) { + fprintf(stderr, "Handle %d started on socket %d and moved to %d\n", idx, + sockets[idx], sock); + res = TEST_ERR_MAJOR_BAD; + return 0; + } + } + return size * nmemb; +} + +enum HandleState { + ReadyForNewHandle, + NeedSocketForNewHandle, + NoMoreHandles +}; + +int test(char *url) +{ + CURLM *multi = NULL; + int running; + int i, j; + int num_handles = 0; + enum HandleState state = ReadyForNewHandle; + char* full_url = malloc(strlen(url) + 4 + 1); + + start_test_timing(); + + if (!full_url) { + fprintf(stderr, "Not enough memory for full url\n"); + return CURLE_OUT_OF_MEMORY; + } + + for (i = 0; i < MAX_EASY_HANDLES; ++i) { + easy[i] = NULL; + sockets[i] = -1; + } + + res = 0; + res_global_init(CURL_GLOBAL_ALL); + if(res) { + return res; + } + + multi_init(multi); + + for(;;) { + struct timeval interval; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + long timeout = -99; + curl_socket_t maxfd = -99; + bool found_new_socket = FALSE; + + /* Start a new handle if we aren't at the max */ + if (state == ReadyForNewHandle) { + easy_init(easy[num_handles]); + + if (num_handles % 3 == 2) { + sprintf(full_url, "%s0200", url); + easy_setopt(easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_NTLM); + } else { + sprintf(full_url, "%s0100", url); + easy_setopt(easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + } + easy_setopt(easy[num_handles], CURLOPT_FRESH_CONNECT, 1L); + easy_setopt(easy[num_handles], CURLOPT_URL, full_url); + easy_setopt(easy[num_handles], CURLOPT_VERBOSE, 1L); + easy_setopt(easy[num_handles], CURLOPT_HTTPGET, 1L); + easy_setopt(easy[num_handles], CURLOPT_USERPWD, "testuser:testpass"); + easy_setopt(easy[num_handles], CURLOPT_WRITEFUNCTION, callback); + easy_setopt(easy[num_handles], CURLOPT_WRITEDATA, easy + num_handles); + easy_setopt(easy[num_handles], CURLOPT_HEADER, 1L); + + multi_add_handle(multi, easy[num_handles]); + num_handles += 1; + state = NeedSocketForNewHandle; + } + + multi_perform(multi, &running); + if (0 != res) + break; + + abort_on_test_timeout(); + + if(!running && state == NoMoreHandles) + break; /* done */ + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); + + /* At this point, maxfd is guaranteed to be greater or equal than -1. */ + + /* Any socket which is new in fdread is associated with the new handle */ + for (i = 0; i <= maxfd; ++i) { + bool socket_exists = FALSE; + if (!FD_ISSET(i, &fdread)) { + continue; + } + + /* Check if this socket was already detected for an earlier handle (or + for this handle, num_handles-1, in the callback */ + for (j = 0; j < num_handles; ++j) { + if (sockets[j] == i) { + socket_exists = TRUE; + break; + } + } + if (socket_exists) { + continue; + } + + if (found_new_socket || state != NeedSocketForNewHandle) { + fprintf(stderr, "Unexpected new socket\n"); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + /* Now we know the socket is for the most recent handle, num_handles-1 */ + if (sockets[num_handles-1] != -1) { + /* A socket for this handle was already detected in the callback; if it + matched socket_exists should be true and we would never get here */ + assert(i != sockets[num_handles-1]); + fprintf(stderr, "Handle %d wrote to socket %d then detected on %d\n", + num_handles-1, sockets[num_handles-1], i); + res = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + else { + sockets[num_handles-1] = i; + found_new_socket = TRUE; + /* continue to make sure there's only one new handle */ + } + } + + if (state == NeedSocketForNewHandle) { + if (found_new_socket) { + fprintf(stderr, "Warning: socket did not open immediately for new " + "handle (trying again)\n"); + continue; + } + state = num_handles < MAX_EASY_HANDLES ? ReadyForNewHandle + : NoMoreHandles; + } + + multi_timeout(multi, &timeout); + + /* At this point, timeout is guaranteed to be greater or equal than -1. */ + + if(timeout != -1L) { + interval.tv_sec = timeout/1000; + interval.tv_usec = (timeout%1000)*1000; + } + else { + interval.tv_sec = TEST_HANG_TIMEOUT/1000+1; + interval.tv_usec = 0; + } + + select_test(maxfd+1, &fdread, &fdwrite, &fdexcep, &interval); + + abort_on_test_timeout(); + } + +test_cleanup: + + for (i = 0; i < MAX_EASY_HANDLES; ++i) { + if (easy[i]) { + if (multi) { + curl_multi_remove_handle(multi, easy[i]); + } + curl_easy_cleanup(easy[i]); + } + } + + if (multi) { + curl_multi_cleanup(multi); + } + + curl_global_cleanup(); + + if (full_url) { + free(full_url); + } + + return res; +} |