← Commit list

f30d9ef341

Stefan Eissing
Stefan Eissing
2026-08-26
SUCCESS Duration: 423s

Subject

http digest: tie peer/credentials on input

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/http_digest.c+54 -28
@@ -26,11 +26,41 @@
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
#include "urldata.h"
+#include "curl_trc.h"
#include "strcase.h"
#include "vauth/vauth.h"
#include "http_digest.h"
#include "curlx/strparse.h"
+/* Flush the Digest state if it was created for a different origin or with
+ different credentials than the ones now in use, then link the current
+ ones. */
+static void digest_flush_stale(struct Curl_easy *data,
+ struct digestdata *digest,
+ struct Curl_peer *peer,
+ struct Curl_creds *creds)
+{
+ bool flush = FALSE;
+ if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) {
+ CURL_TRC_M(data, "http_digest, reset on peer change to %s:%u",
+ peer->hostname, peer->port);
+ flush = TRUE;
+ }
+ else if(digest->creds && !Curl_creds_same(creds, digest->creds)) {
+ CURL_TRC_M(data, "http_digest, reset on creds change to %s",
+ creds ? creds->user : "-");
+ flush = TRUE;
+ }
+
+ if(flush) {
+ /* flush Digest state */
+ Curl_auth_digest_cleanup(digest);
+ }
+
+ Curl_peer_link(&digest->origin, peer);
+ Curl_creds_link(&digest->creds, creds);
+}
+
/* Test example headers:
WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
@@ -43,42 +73,37 @@ CURLcode Curl_input_digest(struct Curl_easy *data,
{
/* Point to the correct struct with this */
struct digestdata *digest;
+ struct Curl_peer *origin = NULL;
+ CURLcode result;
if(proxy) {
digest = &data->state.proxydigest;
+#ifdef CURL_DISABLE_PROXY
+ Curl_auth_digest_cleanup(digest);
+ return CURLE_OK; /* just ignore such a header without proxy support */
+#else
+ origin = data->conn->http_proxy.peer;
+#endif
}
else {
digest = &data->state.digest;
+ origin = data->state.origin;
}
- if(!checkprefix("Digest", header) || !ISBLANK(header[6]))
+ if(!checkprefix("Digest", header) || !ISBLANK(header[6])) {
+ Curl_auth_digest_cleanup(digest);
return CURLE_AUTH_ERROR;
+ }
header += CURL_CSTRLEN("Digest");
curlx_str_passblanks(&header);
- return Curl_auth_decode_digest_http_message(header, digest);
-}
-
-/* Flush the Digest state if it was created for a different origin or with
- different credentials than the ones now in use, then link the current
- ones. */
-static void digest_flush_stale(struct digestdata *digest,
- struct Curl_peer *peer,
- struct Curl_creds *creds)
-{
- bool flush = FALSE;
- if(digest->origin && !Curl_peer_same_destination(peer, digest->origin))
- flush = TRUE;
- else if(digest->creds && !Curl_creds_same(creds, digest->creds))
- flush = TRUE;
-
- if(flush)
- /* flush Digest state */
- Curl_auth_digest_cleanup(digest);
-
- Curl_peer_link(&digest->origin, peer);
- Curl_creds_link(&digest->creds, creds);
+ /* This resets the digest struct before decoding */
+ result = Curl_auth_decode_digest_http_message(header, digest);
+ /* Remember only the peer, the data we take in has no relation to creds
+ * at this time. We can use it even if creds change. */
+ Curl_peer_link(&digest->origin, origin);
+ return result;
}
CURLcode Curl_output_digest(struct Curl_easy *data,
@@ -86,6 +111,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
const unsigned char *request,
const unsigned char *uripath)
{
+ struct Curl_peer *origin = NULL;
CURLcode result;
char *response;
size_t len;
@@ -107,22 +133,22 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
return CURLE_NOT_BUILT_IN;
#else
digest = &data->state.proxydigest;
- digest_flush_stale(digest, data->conn->http_proxy.peer,
- data->conn->http_proxy.creds);
- allocuserpwd = &data->req.hd_proxy_auth;
+ origin = data->conn->http_proxy.peer;
creds = data->conn->http_proxy.creds;
+ allocuserpwd = &data->req.hd_proxy_auth;
authp = &data->state.authproxy;
#endif
}
else {
DEBUGASSERT(data->state.origin);
digest = &data->state.digest;
- digest_flush_stale(digest, data->state.origin, data->state.creds);
- allocuserpwd = &data->req.hd_auth;
+ origin = data->state.origin;
creds = data->state.creds;
+ allocuserpwd = &data->req.hd_auth;
authp = &data->state.authhost;
}
+ digest_flush_stale(data, digest, origin, creds);
curlx_safefree(*allocuserpwd);
#ifdef USE_WINDOWS_SSPI
tests/data/Makefile.am+1 -0
@@ -1808,6 +1808,7 @@ TESTCASES = \
test1982 \
test1983 \
test1984 \
+ test1985 \
\
test2000 \
test2001 \
tests/data/test1985+79 -0
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+Digest
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 401 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+WWW-Authenticate: Digest realm="host-a-realm", nonce="host-a-nonce-0123456789", algorithm=MD5, qop="auth"
+
+</data>
+<data1 crlf="headers">
+HTTP/1.1 401 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+WWW-Authenticate: Digest realm="host-b-realm", nonce="2"
+
+</data1>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+Digest response when not asked, then use Digest
+</name>
+<features>
+!SSPI
+crypto
+digest
+</features>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://firsthost:%HTTPPORT/%TESTNUMBER http://secondhost:%HTTPPORT/%TESTNUMBER0001 %HTTPPORT %HOSTIP
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="headers">
+GET /%TESTNUMBER HTTP/1.1
+Host: firsthost:%HTTPPORT
+Accept: */*
+
+GET /%TESTNUMBER0001 HTTP/1.1
+Host: secondhost:%HTTPPORT
+Accept: */*
+
+GET /19850001 HTTP/1.1
+Host: secondhost:%HTTPPORT
+Authorization: Digest username="alice", realm="host-b-realm", nonce="2", uri="/19850001", response="b3186bc857ad5bb63947c39476d37290"
+Accept: */*
+
+GET /19850001 HTTP/1.1
+Host: secondhost:%HTTPPORT
+Authorization: Digest username="alice", realm="host-b-realm", nonce="2", uri="/19850001", response="b3186bc857ad5bb63947c39476d37290"
+Accept: */*
+
+</protocol>
+<errorcode>
+52
+</errorcode>
+</verify>
+</testcase>
tests/libtest/Makefile.inc+1 -0
@@ -289,6 +289,7 @@ TESTS_C = \
lib1975.c \
lib1977.c \
lib1978.c \
+ lib1985.c \
lib2023.c \
lib2032.c \
lib2082.c \
tests/libtest/lib1985.c+77 -0
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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"
+
+/* URL1, URL2, port, address */
+
+static CURLcode test_lib1985(const char *URL)
+{
+ char host1[80];
+ char host2[80];
+ CURL *curl = NULL;
+ struct curl_slist *slist = NULL;
+ struct curl_slist *slist2 = NULL;
+ CURLcode r1 = CURLE_OK, r2 = CURLE_OK;
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ curl_msnprintf(host1, sizeof(host1), "firsthost:%s:%s",
+ libtest_arg3, libtest_arg4);
+ curl_msnprintf(host2, sizeof(host2), "secondhost:%s:%s",
+ libtest_arg3, libtest_arg4);
+ slist = curl_slist_append(slist, host1);
+ if(!slist)
+ goto error;
+ slist2 = curl_slist_append(slist, host2);
+ if(!slist2)
+ goto error;
+ slist = slist2;
+
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_RESOLVE, slist);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb);
+
+ curl_easy_setopt(curl, CURLOPT_USERPWD,
+ "alice:correct-horse-battery-staple");
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, "unused-token");
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH,
+ (long)(CURLAUTH_BASIC | CURLAUTH_BEARER));
+ curl_easy_setopt(curl, CURLOPT_URL, URL);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ r1 = curl_easy_perform(curl);
+ curl_mprintf("STEP1_CODE=%d\n", (int)r1);
+
+ /* Step 2: reuse handle for HostB with Digest stale state leaks */
+ curl_easy_setopt(curl, CURLOPT_URL, libtest_arg2);
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST);
+ r2 = curl_easy_perform(curl);
+ curl_mprintf("STEP2_CODE=%d\n", (int)r2);
+ }
+
+error:
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(slist);
+ curl_global_cleanup();
+ return r2;
+}