0002-Add-new-PGOAUTHDEBUG-option-issuer-mismatch.patch
application/octet-stream
Filename: 0002-Add-new-PGOAUTHDEBUG-option-issuer-mismatch.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch 0002
Subject: Add new PGOAUTHDEBUG option: issuer-mismatch
| File | + | − |
|---|---|---|
| doc/src/sgml/libpq.sgml | 15 | 1 |
| src/interfaces/libpq/fe-auth-oauth.c | 11 | 7 |
| src/interfaces/libpq/fe-auth-oauth-debug.c | 7 | 0 |
| src/interfaces/libpq/fe-auth-oauth.h | 1 | 0 |
| src/interfaces/libpq-oauth/oauth-curl.c | 9 | 4 |
| src/test/modules/oauth_validator/t/001_server.pl | 9 | 6 |
From 8f4331cfbb099e4b641a5b31f55a05be969915df Mon Sep 17 00:00:00 2001
From: Zsolt Parragi <zsolt.parragi@cancellar.hu>
Date: Wed, 18 Feb 2026 14:51:46 +0100
Subject: [PATCH 2/2] Add new PGOAUTHDEBUG option: issuer-mismatch
This new unsafe option allows to connection to proceed if the issuer
configured on the server and client mismatch, allowing to write
mismatched-issuer tests for validators.
Validators should test scenarios like this, as the wire allows this
situation, but previously libpq/psql prevented it, making writing tests
for this more difficult.
---
doc/src/sgml/libpq.sgml | 16 +++++++++++++++-
src/interfaces/libpq-oauth/oauth-curl.c | 13 +++++++++----
src/interfaces/libpq/fe-auth-oauth-debug.c | 7 +++++++
src/interfaces/libpq/fe-auth-oauth.c | 18 +++++++++++-------
src/interfaces/libpq/fe-auth-oauth.h | 1 +
.../modules/oauth_validator/t/001_server.pl | 15 +++++++++------
6 files changed, 52 insertions(+), 18 deletions(-)
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 5d70cc2b261..6f29a4a7756 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -10559,6 +10559,20 @@ PGOAUTHDEBUG=UNSAFE <lineannotation>legacy format; enables all options</linea
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>issuer-mismatch</literal> (unsafe)</term>
+ <listitem>
+ <para>
+ Tolerates a mismatch between the client's configured
+ <literal>oauth_issuer</literal> and the issuer found in the server's
+ discovery document. This disables the mix-up attack protection from
+ RFC 9207 and should only be used in development or testing environments
+ where the server's issuer identifier does not match the client
+ configuration.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>fast-retry</literal> (safe)</term>
<listitem>
@@ -10595,7 +10609,7 @@ PGOAUTHDEBUG=UNSAFE <lineannotation>legacy format; enables all options</linea
<para>
Unsafe options (<literal>http</literal>, <literal>trace</literal>,
- <literal>custom-ca</literal>) require the <literal>UNSAFE:</literal> prefix.
+ <literal>custom-ca</literal>, <literal>issuer-mismatch</literal>) require the <literal>UNSAFE:</literal> prefix.
If unsafe options are specified without this prefix, a warning is printed
to standard error and that option is ignored. Other valid options in the
list continue to work. Safe options (<literal>fast-retry</literal>,
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index ac8b4631d53..d9512ef17dd 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -2223,10 +2223,15 @@ check_issuer(struct async_ctx *actx, PGconn *conn)
*/
if (strcmp(oauth_issuer_id, provider->issuer) != 0)
{
- actx_error(actx,
- "the issuer identifier (%s) does not match oauth_issuer (%s)",
- provider->issuer, oauth_issuer_id);
- return false;
+ if (!actx->debug_flags.issuer_mismatch)
+ {
+ actx_error(actx,
+ "the issuer identifier (%s) does not match oauth_issuer (%s)",
+ provider->issuer, oauth_issuer_id);
+ return false;
+ }
+
+ return true;
}
return true;
diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/fe-auth-oauth-debug.c
index f65f069fed8..558b34da561 100644
--- a/src/interfaces/libpq/fe-auth-oauth-debug.c
+++ b/src/interfaces/libpq/fe-auth-oauth-debug.c
@@ -53,6 +53,12 @@ parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe
*is_unsafe = true;
return true;
}
+ else if (strcmp(option, "issuer-mismatch") == 0)
+ {
+ flags->issuer_mismatch = true;
+ *is_unsafe = true;
+ return true;
+ }
/* Safe options */
else if (strcmp(option, "fast-retry") == 0)
{
@@ -103,6 +109,7 @@ oauth_get_debug_flags(void)
flags.http = true;
flags.trace = true;
flags.custom_ca = true;
+ flags.issuer_mismatch = true;
flags.fast_retry = true;
flags.poll_counts = true;
flags.print_plugin_errors = true;
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 5dff354c19b..b6d472a0330 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -606,13 +606,16 @@ handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
if (strcmp(conn->oauth_issuer_id, discovery_issuer) != 0)
{
- libpq_append_conn_error(conn,
- "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
- ctx.discovery_uri, discovery_issuer,
- conn->oauth_issuer_id);
+ if (!oauth_get_debug_flags().issuer_mismatch)
+ {
+ libpq_append_conn_error(conn,
+ "server's discovery document at %s (issuer \"%s\") is incompatible with oauth_issuer (%s)",
+ ctx.discovery_uri, discovery_issuer,
+ conn->oauth_issuer_id);
- free(discovery_issuer);
- goto cleanup;
+ free(discovery_issuer);
+ goto cleanup;
+ }
}
free(discovery_issuer);
@@ -625,7 +628,8 @@ handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
else
{
/* This must match the URI we'd previously determined. */
- if (strcmp(conn->oauth_discovery_uri, ctx.discovery_uri) != 0)
+ if (strcmp(conn->oauth_discovery_uri, ctx.discovery_uri) != 0
+ && !oauth_get_debug_flags().issuer_mismatch)
{
libpq_append_conn_error(conn,
"server's discovery document has moved to %s (previous location was %s)",
diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h
index 272638ea359..918681f16a5 100644
--- a/src/interfaces/libpq/fe-auth-oauth.h
+++ b/src/interfaces/libpq/fe-auth-oauth.h
@@ -52,6 +52,7 @@ typedef struct oauth_debug_flags
bool http; /* allow HTTP (unencrypted) connections */
bool trace; /* log HTTP traffic (exposes secrets) */
bool custom_ca; /* allow custom CA certificate file */
+ bool issuer_mismatch; /* tolerate issuer mismatch */
/* SAFE features - allowed without UNSAFE: prefix */
bool fast_retry; /* allow zero-second retry intervals */
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index 6b649c0b06f..b587b51bb19 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -136,12 +136,15 @@ $node->connect_ok(
]);
# The issuer linked by the server must match the client's oauth_issuer setting.
-$node->connect_fails(
- "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0636",
- "oauth_issuer must match discovery",
- expected_stderr =>
- qr@server's discovery document at \Q$issuer/.well-known/oauth-authorization-server/alternate\E \(issuer "\Q$issuer/alternate\E"\) is incompatible with oauth_issuer \(\Q$issuer\E\)@
-);
+{
+ local $ENV{PGOAUTHDEBUG} = "UNSAFE:http";
+ $node->connect_fails(
+ "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0636",
+ "oauth_issuer must match discovery",
+ expected_stderr =>
+ qr@server's discovery document at \Q$issuer/.well-known/oauth-authorization-server/alternate\E \(issuer "\Q$issuer/alternate\E"\) is incompatible with oauth_issuer \(\Q$issuer\E\)@
+ );
+}
# Test require_auth settings against OAUTHBEARER.
my @cases = (
--
2.43.0