1
0
mirror of https://github.com/apache/httpd.git synced 2026-10-23 22:49:01 +03:00

mod_auth_digest: Send Digest domain= for a reverse-proxied request:

The domain was suppressed for any proxy request, but a reverse-proxied
request authenticates to this server with an origin-style
WWW-Authenticate, so AuthDigestDomain applies to it as it does to a
non-proxied request; only a forward-proxy (PROXYREQ_PROXY) request, whose
challenge is Proxy-Authenticate, should omit it.

* modules/aaa/mod_auth_digest.c (note_digest_auth_failure): Suppress
  domain= only for PROXYREQ_PROXY, matching the rest of the file.

* test/modules/aaa/env.py: Load mod_proxy and mod_proxy_http.

* test/modules/aaa/conftest.py: Add a reverse-proxied location which
  authenticates with Digest and sets AuthDigestDomain.

* test/modules/aaa/test_013_proxy_domain.py: New test suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GitHub: PR #787


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1939035 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Joe Orton
2026-10-11 13:44:17 +00:00
parent ecdf990056
commit 288f73763c
4 changed files with 42 additions and 2 deletions

View File

@@ -1302,7 +1302,7 @@ static int note_digest_auth_failure(request_rec *r,
/* Setup domain, which tells the client which URIs share this
* protection space, so that it does not send the Authorization header
* (usually more than 200 bytes) where it is not needed. */
if (r->proxyreq || !conf->uri_list) {
if (r->proxyreq == PROXYREQ_PROXY || !conf->uri_list) {
domain = NULL;
}
else {

View File

@@ -99,6 +99,21 @@ def env(pytestconfig) -> AAATestEnv:
# has no per-request Digest record; an ErrorDocument redirecting it into
# a Digest-protected location must not then dereference that record.
conf.add('ErrorDocument 400 /digest/default/secret.txt')
# A reverse-proxied location which authenticates with Digest at the
# proxy (so the backend is never reached and need not be reachable).
# The challenge is an origin-style WWW-Authenticate, so AuthDigestDomain
# applies to it just as for a non-proxied location.
conf.add([
'ProxyPass "/pxdomain/" "http://127.0.0.1:1/unused/"',
'<Location "/pxdomain/">',
' AuthType Digest',
f' AuthName "{AAATestEnv.REALM}"',
' AuthDigestProvider file',
f' AuthUserFile "{pwfile}"',
' AuthDigestDomain "/pxdomain/"',
' Require valid-user',
'</Location>',
])
conf.install()
assert env.apache_restart() == 0
return env

View File

@@ -16,7 +16,7 @@ class AAATestSetup(HttpdTestSetup):
super().__init__(env=env)
self.add_source_dir(os.path.dirname(inspect.getfile(AAATestSetup)))
self.add_modules(["auth_digest", "authn_file", "authn_core",
"authz_core", "authz_user"])
"authz_core", "authz_user", "proxy", "proxy_http"])
class AAATestEnv(HttpdTestEnv):

View File

@@ -0,0 +1,25 @@
"""AuthDigestDomain on a reverse-proxied location.
Digest auth can run at a reverse proxy, for a location mapped by ProxyPass.
Such a request carries PROXYREQ_REVERSE, but its 401 is an ordinary
origin-style WWW-Authenticate (the client authenticates to this server, not
through it), so the configured AuthDigestDomain belongs in the challenge
just as for a non-proxied location.
The suppression test treated any proxy request alike and dropped domain=
for the reverse-proxied case, so clients re-authenticated per URI prefix
and sent the Authorization header outside the intended protection space.
"""
from . import digest_client as dc
class TestDigestProxyDomain:
def test_digest_130_domain_sent_for_reverse_proxy(self, env):
r = env.curl_get(env.mkurl("http", "aaa", "/pxdomain/secret.txt"))
assert r.response["status"] == 401
challenge = dc.DigestChallenge.parse(
r.response["header"]["www-authenticate"])
assert "/pxdomain/" in challenge.domain_list(), \
"AuthDigestDomain was dropped from a reverse-proxied challenge"