diff --git a/lib/http_digest.c b/lib/http_digest.c index d20e165623..6949b3bd0d 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -94,6 +94,22 @@ CURLcode Curl_output_digest(struct Curl_easy *data, #endif } else { + bool flush = FALSE; + DEBUGASSERT(data->conn->origin); + if(data->state.digest.origin && + !Curl_peer_same_destination(data->conn->origin, + data->state.digest.origin)) + flush = TRUE; + else if(data->state.digest.creds && + !Curl_creds_same(data->state.creds, data->state.digest.creds)) + flush = TRUE; + + if(flush) + /* flush host Digest state */ + Curl_auth_digest_cleanup(&data->state.digest); + + Curl_peer_link(&data->state.digest.origin, data->conn->origin); + Curl_creds_link(&data->state.digest.creds, data->state.creds); digest = &data->state.digest; allocuserpwd = &data->req.hd_auth; creds = data->state.creds; diff --git a/lib/urldata.h b/lib/urldata.h index e5363cf569..f746b5c301 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -148,13 +148,12 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { + struct Curl_creds *creds; + struct Curl_peer *origin; #ifdef USE_WINDOWS_SSPI BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* linked credentials used to make the identity for http_context. - may be NULL. */ - struct Curl_creds *creds; #else char *nonce; char *cnonce; diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 6cc4edbee1..9c57affaf9 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -1039,6 +1039,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, */ void Curl_auth_digest_cleanup(struct digestdata *digest) { + Curl_peer_unlink(&digest->origin); + Curl_creds_unlink(&digest->creds); curlx_safefree(digest->nonce); curlx_safefree(digest->cnonce); curlx_safefree(digest->realm); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 74d654fc46..aca6237735 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -630,6 +630,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Free the copy of user/passwd used to make the identity for http_context */ Curl_creds_unlink(&digest->creds); + Curl_peer_unlink(&digest->origin); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 10d72fd7c1..96cbda0b8b 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -221,7 +221,7 @@ test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ test1677 test1680 test1681 test1682 test1683 test1684 \ -test1685 \ +test1685 test1686 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ diff --git a/tests/data/test1686 b/tests/data/test1686 new file mode 100644 index 0000000000..2d419ad608 --- /dev/null +++ b/tests/data/test1686 @@ -0,0 +1,84 @@ + + + + +HTTP +Digest + + + + + +HTTP/1.1 401 Authorization Required +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Digest realm="my-backyard", nonce="314156295" +Content-Length: 26 + +This is not the real page + + +# This is supposed to be returned when the server gets a +# Authorization: Digest line passed-in from the client + +HTTP/1.1 200 OK +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 + +This IS the real page! + + + + + + +!SSPI +crypto +digest + + +http + + +HTTP Digest to different origins and switching credentials + + +lib%TESTNUMBER + + +%HOSTIP %HTTPPORT + + + + + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Accept: */* + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/api", response="4ecc00e567c37a9d537727890c2e5b32" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/hook", response="d3a7738fb6a23f5543fb8dacc0f0f253" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="bob", realm="my-backyard", nonce="314156295", uri="/hook", response="777e68eddb77294d9cbd6134973cbbab" +Accept: */* + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 22fff2272f..98c9939994 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -101,6 +101,7 @@ TESTS_C = \ lib1598.c lib1599.c \ lib1647.c lib1648.c lib1649.c \ lib1662.c \ + lib1686.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ diff --git a/tests/libtest/lib1686.c b/tests/libtest/lib1686.c new file mode 100644 index 0000000000..e457012bb9 --- /dev/null +++ b/tests/libtest/lib1686.c @@ -0,0 +1,96 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 https://curl.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. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "first.h" + +static size_t devnull_1686(char *p, size_t s, size_t n, void *u) +{ + (void)p; + (void)u; + return s * n; +} + +#define FIRSTHOST "first.test" +#define SECONDHOST "second.test" + +static CURLcode test_lib1686(const char *hostip) +{ + CURL *curl = NULL; + CURLcode result = CURLE_OK; + const char *httpport = libtest_arg2; + char firsturl[100]; + char secondurl[100]; + char firstres[100]; + char secondres[100]; + struct curl_slist *host = NULL; + struct curl_slist *host2 = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + /* create strings for CURLOPT_RESOLVE */ + curl_msnprintf(firstres, sizeof(firstres), "%s:%s:%s", + FIRSTHOST, httpport, hostip); + curl_msnprintf(secondres, sizeof(secondres), "%s:%s:%s", + SECONDHOST, httpport, hostip); + + /* create URLs */ + curl_msnprintf(firsturl, sizeof(firsturl), "http://%s:%s/api", + FIRSTHOST, httpport); + curl_msnprintf(secondurl, sizeof(secondurl), "http://%s:%s/hook", + SECONDHOST, httpport); + + host = curl_slist_append(NULL, firstres); + if(!host) + goto test_cleanup; + host2 = curl_slist_append(host, secondres); + if(!host2) + goto test_cleanup; + host = host2; + + curl = curl_easy_init(); + if(curl) { + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "alice:bond"); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, devnull_1686); + + easy_setopt(curl, CURLOPT_URL, firsturl); + result = curl_easy_perform(curl); + + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + + easy_setopt(curl, CURLOPT_USERPWD, "bob:secret"); + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(host); + return result; +}