http digest: tie peer/credentials on input
No binary (build failed)
—
(log yok)
| @@ -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 | |
| @@ -1808,6 +1808,7 @@ TESTCASES = \ | |
| test1982 \ | |
| test1983 \ | |
| test1984 \ | |
| + | test1985 \ |
| \ | |
| test2000 \ | |
| test2001 \ | |
| @@ -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> |
| @@ -289,6 +289,7 @@ TESTS_C = \ | |
| lib1975.c \ | |
| lib1977.c \ | |
| lib1978.c \ | |
| + | lib1985.c \ |
| lib2023.c \ | |
| lib2032.c \ | |
| lib2082.c \ | |
| @@ -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; |
| + | } |