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;
+}