v49-0004-v49-Fixups-proposed-by-Daniel.patch
application/octet-stream
Filename: v49-0004-v49-Fixups-proposed-by-Daniel.patch
Type: application/octet-stream
Part: 3
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v49-0004
Subject: v49 Fixups proposed by Daniel
| File | + | − |
|---|---|---|
| config/programs.m4 | 2 | 2 |
| doc/src/sgml/client-auth.sgml | 4 | 4 |
| doc/src/sgml/installation.sgml | 5 | 5 |
| doc/src/sgml/libpq.sgml | 13 | 12 |
| doc/src/sgml/oauth-validators.sgml | 14 | 13 |
| meson.build | 4 | 4 |
| src/backend/libpq/auth-oauth.c | 57 | 22 |
| src/include/common/oauth-common.h | 1 | 1 |
| src/include/libpq/oauth.h | 4 | 3 |
| src/include/pg_config.h.in | 1 | 1 |
| src/interfaces/libpq/fe-auth-oauth.c | 2 | 2 |
| src/interfaces/libpq/fe-auth-oauth-curl.c | 12 | 6 |
| src/test/modules/oauth_validator/fail_validator.c | 3 | 3 |
| src/test/modules/oauth_validator/magic_validator.c | 48 | 0 |
| src/test/modules/oauth_validator/Makefile | 1 | 1 |
| src/test/modules/oauth_validator/meson.build | 17 | 1 |
| src/test/modules/oauth_validator/oauth_hook_client.c | 1 | 1 |
| src/test/modules/oauth_validator/README | 3 | 3 |
| src/test/modules/oauth_validator/t/001_server.pl | 24 | 7 |
| src/test/modules/oauth_validator/t/002_client.pl | 1 | 1 |
| src/test/modules/oauth_validator/t/OAuth/Server.pm | 2 | 2 |
| src/test/modules/oauth_validator/validator.c | 1 | 1 |
From eaa6078e9fe70940c359cca0883227746456fc94 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 13 Feb 2025 23:45:50 +0100
Subject: [PATCH v49 4/4] v49 Fixups proposed by Daniel
---
config/programs.m4 | 4 +-
doc/src/sgml/client-auth.sgml | 8 +-
doc/src/sgml/installation.sgml | 10 +--
doc/src/sgml/libpq.sgml | 25 +++---
doc/src/sgml/oauth-validators.sgml | 27 ++++---
meson.build | 8 +-
src/backend/libpq/auth-oauth.c | 79 +++++++++++++------
src/include/common/oauth-common.h | 2 +-
src/include/libpq/oauth.h | 7 +-
src/include/pg_config.h.in | 2 +-
src/interfaces/libpq/fe-auth-oauth-curl.c | 18 +++--
src/interfaces/libpq/fe-auth-oauth.c | 4 +-
src/test/modules/oauth_validator/Makefile | 2 +-
src/test/modules/oauth_validator/README | 6 +-
.../modules/oauth_validator/fail_validator.c | 6 +-
.../modules/oauth_validator/magic_validator.c | 48 +++++++++++
src/test/modules/oauth_validator/meson.build | 18 ++++-
.../oauth_validator/oauth_hook_client.c | 2 +-
.../modules/oauth_validator/t/001_server.pl | 31 ++++++--
.../modules/oauth_validator/t/002_client.pl | 2 +-
.../modules/oauth_validator/t/OAuth/Server.pm | 4 +-
src/test/modules/oauth_validator/validator.c | 2 +-
22 files changed, 220 insertions(+), 95 deletions(-)
create mode 100644 src/test/modules/oauth_validator/magic_validator.c
diff --git a/config/programs.m4 b/config/programs.m4
index ead427046f5..061b13376ac 100644
--- a/config/programs.m4
+++ b/config/programs.m4
@@ -280,7 +280,7 @@ AC_DEFUN([PGAC_CHECK_STRIP],
# PGAC_CHECK_LIBCURL
# ------------------
# Check for required libraries and headers, and test to see whether the current
-# installation of libcurl is threadsafe.
+# installation of libcurl is thread-safe.
AC_DEFUN([PGAC_CHECK_LIBCURL],
[
@@ -313,7 +313,7 @@ AC_DEFUN([PGAC_CHECK_LIBCURL],
[pgac_cv__libcurl_threadsafe_init=unknown])])
if test x"$pgac_cv__libcurl_threadsafe_init" = xyes ; then
AC_DEFINE(HAVE_THREADSAFE_CURL_GLOBAL_INIT, 1,
- [Define to 1 if curl_global_init() is guaranteed to be threadsafe.])
+ [Define to 1 if curl_global_init() is guaranteed to be thread-safe.])
fi
# Warn if a thread-friendly DNS resolver isn't built.
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index f84085dbac4..6fc0da57f1b 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -2397,7 +2397,7 @@ host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""
<term>Resource Server</term>
<listitem>
<para>
- The system which hosts the protected resources which are
+ The system hosting the protected resources which are
accessed by the client. The <productname>PostgreSQL</productname>
cluster being connected to is the resource server.
</para>
@@ -2409,7 +2409,7 @@ host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""
<listitem>
<para>
The organization, product vendor, or other entity which develops and/or
- administers the OAuth servers and clients for a given application.
+ administers the OAuth resource servers and clients for a given application.
Different providers typically choose different implementation details
for their OAuth systems; a client of one provider is not generally
guaranteed to have access to the servers of another.
@@ -2432,7 +2432,7 @@ host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""
The system which receives requests from, and issues access tokens to,
the client after the authenticated resource owner has given approval.
<productname>PostgreSQL</productname> does not provide an authorization
- server; it's obtained from the OAuth provider.
+ server; it is the responsibility of the OAuth provider.
</para>
</listitem>
</varlistentry>
@@ -2500,7 +2500,7 @@ host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""
exactly match the issuer identifier which is provided in the discovery
document, which must in turn match the client's
<xref linkend="libpq-connect-oauth-issuer"/> setting. No variations in
- case or format are permitted.
+ case or formatting are permitted.
</para>
</warning>
</listitem>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 96e433179b9..3c95c15a1e4 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1148,8 +1148,8 @@ build-postgresql:
<listitem>
<para>
Build with libcurl support for OAuth 2.0 client flows.
- This requires the <productname>curl</productname> package to be
- installed. Building with this will check for the required header files
+ Libcurl version 7.61.0 or later is required for this feature.
+ Building with this will check for the required header files
and libraries to make sure that your <productname>curl</productname>
installation is sufficient before proceeding.
</para>
@@ -2602,9 +2602,9 @@ ninja install
<listitem>
<para>
Build with libcurl support for OAuth 2.0 client flows.
- This requires the <productname>curl</productname> package to be
- installed. Building with this will check for the required header files
- and libraries to make sure that your <productname>curl</productname>
+ Libcurl version 7.61.0 or later is required for this feature.
+ Building with this will check for the required header files
+ and libraries to make sure that your <productname>Curl</productname>
installation is sufficient before proceeding. The default for this
option is auto.
</para>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b2abae8deee..ca84226755d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2390,7 +2390,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
The HTTPS URL of a trusted issuer to contact if the server requests an
OAuth token for the connection. This parameter is required for all OAuth
connections; it should exactly match the <literal>issuer</literal>
- setting in <link linkend="auth-oauth">the server's HBA configuration.</link>
+ setting in <link linkend="auth-oauth">the server's HBA configuration</link>.
</para>
<para>
As part of the standard authentication handshake, <application>libpq</application>
@@ -2399,8 +2399,9 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
provide a URL that is directly constructed from the components of the
<literal>oauth_issuer</literal>, and this value must exactly match the
issuer identifier that is declared in the discovery document itself, or
- the connection will fail. This is required to prevent a class of "mix-up
- attacks" on OAuth clients.
+ the connection will fail. This is required to prevent a class of
+ <ulink url="https://mailarchive.ietf.org/arch/msg/oauth/JIVxFBGsJBVtm7ljwJhPUm3Fr-w/">
+ "mix-up attacks"</ulink> on OAuth clients.
</para>
<para>
You may also explicitly set <literal>oauth_issuer</literal> to the
@@ -10140,7 +10141,7 @@ void PQinitSSL(int do_ssl);
<link linkend="auth-oauth">requests a bearer token</link> during
authentication. This flow can be utilized even if the system running the
client application does not have a usable web browser, for example when
- running a client via SSH. Client applications may implement their own flows
+ running a client via <application>SSH</application>. Client applications may implement their own flows
instead; see <xref linkend="libpq-oauth-authdata-hooks"/>.
</para>
<para>
@@ -10152,11 +10153,11 @@ Visit https://example.com/device and enter the code: ABCD-EFGH
</programlisting>
(This prompt may be
<link linkend="libpq-oauth-authdata-prompt-oauth-device">customized</link>.)
- You will then log into your OAuth provider, which will ask whether you want
- to allow libpq and the server to perform actions on your behalf. It is always
+ The user will then log into their OAuth provider, which will ask whether
+ to allow libpq and the server to perform actions on their behalf. It is always
a good idea to carefully review the URL and permissions displayed, to ensure
- they match your expectations, before continuing. Do not give permissions to
- untrusted third parties.
+ they match expectations, before continuing. Permissions should not be given
+ to untrusted third parties.
</para>
<para>
For an OAuth client flow to be usable, the connection string must at minimum
@@ -10199,7 +10200,7 @@ void PQsetAuthDataHook(PQauthDataHook_type hook);
<programlisting>
int hook_fn(PGauthData type, PGconn *conn, void *data);
</programlisting>
- which <application>libpq</application> will call when when action is
+ which <application>libpq</application> will call when an action is
required of the application. <replaceable>type</replaceable> describes
the request being made, <replaceable>conn</replaceable> is the
connection handle being authenticated, and <replaceable>data</replaceable>
@@ -10431,7 +10432,7 @@ typedef struct _PGoauthBearerRequest
</listitem>
<listitem>
<para>
- sprays HTTP traffic (containing several critical secrets) to standard
+ prints HTTP traffic (containing several critical secrets) to standard
error during the OAuth flow
</para>
</listitem>
@@ -10526,13 +10527,13 @@ int PQisthreadsafe();
</para>
<para>
- Similarly, if you are using Curl inside your application,
+ Similarly, if you are using <productname></productname>Curl</productname> inside your application,
<emphasis>and</emphasis> you do not already
<ulink url="https://curl.se/libcurl/c/curl_global_init.html">initialize
libcurl globally</ulink> before starting new threads, you will need to
cooperatively lock (again via <function>PQregisterThreadLock</function>)
around any code that may initialize libcurl. This restriction is lifted for
- more recent versions of Curl that are built to support threadsafe
+ more recent versions of <productname>Curl</productname> that are built to support thread-safe
initialization; those builds can be identified by the advertisement of a
<literal>threadsafe</literal> feature in their version metadata.
</para>
diff --git a/doc/src/sgml/oauth-validators.sgml b/doc/src/sgml/oauth-validators.sgml
index eb8c4431c2d..e9d28d3daea 100644
--- a/doc/src/sgml/oauth-validators.sgml
+++ b/doc/src/sgml/oauth-validators.sgml
@@ -10,8 +10,8 @@
custom modules to perform server-side validation of OAuth bearer tokens.
Because OAuth implementations vary so wildly, and bearer token validation is
heavily dependent on the issuing party, the server cannot check the token
- itself; validator modules provide the glue between the server and the OAuth
- provider in use.
+ itself; validator modules provide the integration layer between the server
+ and the OAuth provider in use.
</para>
<para>
OAuth validator modules must at least consist of an initialization function
@@ -21,7 +21,7 @@
<warning>
<para>
Since a misbehaving validator might let unauthorized users into the database,
- correct implementation is critical. See
+ validating the correctness of the implementation is critical. See
<xref linkend="oauth-validator-design"/> for design considerations.
</para>
</warning>
@@ -290,8 +290,9 @@
<primary>_PG_oauth_validator_module_init</primary>
</indexterm>
<para>
- An OAuth validator module is loaded by dynamically loading one of the shared
+ OAuth validator modules are dynamically loaded from the shared
libraries listed in <xref linkend="guc-oauth-validator-libraries"/>.
+ Modules are loaded on demand when requested from a login in progress.
The normal library search path is used to locate the library. To
provide the validator callbacks and to indicate that the library is an OAuth
validator module a function named
@@ -356,7 +357,7 @@ typedef bool (*ValidatorValidateCB) (const ValidatorModuleState *state,
</programlisting>
<replaceable>token</replaceable> will contain the bearer token to validate.
- The server has ensured that the token is well-formed syntactically, but no
+ <application>libpq</application> has ensured that the token is well-formed syntactically, but no
other validation has been performed. <replaceable>role</replaceable> will
contain the role the user has requested to log in as. The callback must
set output parameters in the <literal>result</literal> struct, which is
@@ -371,10 +372,10 @@ typedef struct ValidatorModuleResult
</programlisting>
The connection will only proceed if the module sets
- <structfield>authorized</structfield> to <literal>true</literal>. To
+ <structfield>result->authorized</structfield> to <literal>true</literal>. To
authenticate the user, the authenticated user name (as determined using the
- token) shall be palloc'd and returned in the <structfield>authn_id</structfield>
- field. Alternatively, <structfield>authn_id</structfield> may be set to
+ token) shall be palloc'd and returned in the <structfield>result->authn_id</structfield>
+ field. Alternatively, <structfield>result->authn_id</structfield> may be set to
NULL if the token is valid but the associated user identity cannot be
determined.
</para>
@@ -386,12 +387,12 @@ typedef struct ValidatorModuleResult
</para>
<para>
The behavior after <function>validate_cb</function> returns depends on the
- specific HBA setup. Normally, the <structfield>authn_id</structfield> user
+ specific HBA setup. Normally, the <structfield>result->authn_id</structfield> user
name must exactly match the role that the user is logging in as. (This
behavior may be modified with a usermap.) But when authenticating against
- an HBA rule with <literal>delegate_ident_mapping</literal> turned on, the
- server will not perform any checks on the value of
- <structfield>authn_id</structfield> at all; in this case it is up to the
+ an HBA rule with <literal>delegate_ident_mapping</literal> turned on,
+ <productname>PostgreSQL</productname> will not perform any checks on the value of
+ <structfield>result->authn_id</structfield> at all; in this case it is up to the
validator to ensure that the token carries enough privileges for the user to
log in under the indicated <replaceable>role</replaceable>.
</para>
@@ -402,7 +403,7 @@ typedef struct ValidatorModuleResult
<para>
The <function>shutdown_cb</function> callback is executed when the backend
process associated with the connection exits. If the validator module has
- any state, this callback should free it to avoid resource leaks.
+ any allocated state, this callback should free it to avoid resource leaks.
<programlisting>
typedef void (*ValidatorShutdownCB) (ValidatorModuleState *state);
</programlisting>
diff --git a/meson.build b/meson.build
index 5c35159b4f1..574f992ed49 100644
--- a/meson.build
+++ b/meson.build
@@ -867,7 +867,7 @@ if not libcurlopt.disabled()
if libcurl.found()
cdata.set('USE_LIBCURL', 1)
- # Check to see whether the current platform supports threadsafe Curl
+ # Check to see whether the current platform supports thread-safe Curl
# initialization.
libcurl_threadsafe_init = false
@@ -897,11 +897,11 @@ if not libcurlopt.disabled()
assert(r.compiled())
if r.returncode() == 0
libcurl_threadsafe_init = true
- message('curl_global_init is threadsafe')
+ message('curl_global_init is thread-safe')
elif r.returncode() == 1
- message('curl_global_init is not threadsafe')
+ message('curl_global_init is not thread-safe')
else
- message('curl_global_init failed; assuming not threadsafe')
+ message('curl_global_init failed; assuming not thread-safe')
endif
endif
diff --git a/src/backend/libpq/auth-oauth.c b/src/backend/libpq/auth-oauth.c
index e2b5d1ed913..db56cd8c200 100644
--- a/src/backend/libpq/auth-oauth.c
+++ b/src/backend/libpq/auth-oauth.c
@@ -4,9 +4,9 @@
* Server-side implementation of the SASL OAUTHBEARER mechanism.
*
* See the following RFC for more details:
- * - RFC 7628: https://tools.ietf.org/html/rfc7628
+ * - RFC 7628: https://datatracker.ietf.org/doc/html/rfc7628
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/backend/libpq/auth-oauth.c
@@ -70,7 +70,7 @@ struct oauth_ctx
const char *scope;
};
-static char *sanitize_char(char c);
+static void sanitize_char(char c, char *buf, size_t buflen);
static char *parse_kvpairs_for_auth(char **input);
static void generate_error_response(struct oauth_ctx *ctx, char **output, int *outputlen);
static bool validate(Port *port, const char *auth);
@@ -139,6 +139,7 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
char cbind_flag;
char *auth;
int status;
+ char errmsgbuf[5];
struct oauth_ctx *ctx = opaq;
@@ -162,6 +163,7 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
/*
* Check that the input length agrees with the string length of the input.
+ * Possible reasons for discrepancies include embedded nulls in the string.
*/
if (inputlen == 0)
ereport(ERROR,
@@ -223,22 +225,29 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
case 'y': /* fall through */
case 'n':
- p++;
+ if (!*(++p))
+ goto endofmessage;
+
if (*p != ',')
+ {
+ sanitize_char(*p, errmsgbuf, sizeof(errmsgbuf));
ereport(ERROR,
errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("malformed OAUTHBEARER message"),
errdetail("Comma expected, but found character \"%s\".",
- sanitize_char(*p)));
- p++;
+ errmsgbuf));
+ }
+ if (!*(++p))
+ goto endofmessage;
break;
default:
+ sanitize_char(*p, errmsgbuf, sizeof(errmsgbuf));
ereport(ERROR,
errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("malformed OAUTHBEARER message"),
errdetail("Unexpected channel-binding flag \"%s\".",
- sanitize_char(cbind_flag)));
+ errmsgbuf));
}
/*
@@ -249,21 +258,29 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("client uses authorization identity, but it is not supported"));
if (*p != ',')
+ {
+ sanitize_char(*p, errmsgbuf, sizeof(errmsgbuf));
ereport(ERROR,
errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("malformed OAUTHBEARER message"),
errdetail("Unexpected attribute \"%s\" in client-first-message.",
- sanitize_char(*p)));
- p++;
+ errmsgbuf));
+ }
+ if (!*(++p))
+ goto endofmessage;
/* All remaining fields are separated by the RFC's kvsep (\x01). */
if (*p != KVSEP)
+ {
+ sanitize_char(*p, errmsgbuf, sizeof(errmsgbuf));
ereport(ERROR,
errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("malformed OAUTHBEARER message"),
errdetail("Key-value separator expected, but found character \"%s\".",
- sanitize_char(*p)));
- p++;
+ errmsgbuf));
+ }
+ if (!*(++p))
+ goto endofmessage;
auth = parse_kvpairs_for_auth(&p);
if (!auth)
@@ -296,6 +313,13 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
explicit_bzero(input_copy, inputlen);
return status;
+
+endofmessage:
+ explicit_bzero(input_copy, inputlen);
+ ereport(ERROR,
+ errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("malformed OAUTHBEARER message"));
+ pg_unreachable();
}
/*
@@ -303,19 +327,14 @@ oauth_exchange(void *opaq, const char *input, int inputlen,
*
* If it's a printable ASCII character, print it as a single character.
* otherwise, print it in hex.
- *
- * The returned pointer points to a static buffer.
*/
-static char *
-sanitize_char(char c)
+static void
+sanitize_char(char c, char *buf, size_t buflen)
{
- static char buf[5];
-
if (c >= 0x21 && c <= 0x7E)
- snprintf(buf, sizeof(buf), "'%c'", c);
+ snprintf(buf, buflen, "'%c'", c);
else
- snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
- return buf;
+ snprintf(buf, buflen, "0x%02x", (unsigned char) c);
}
/*
@@ -660,7 +679,9 @@ validate(Port *port, const char *auth)
if (!ValidatorCallbacks->validate_cb(validator_module_state, token,
port->user_name, ret))
{
- ereport(LOG, errmsg("internal error in OAuth validator module"));
+ ereport(WARNING,
+ errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("internal error in OAuth validator module"));
return false;
}
@@ -738,6 +759,11 @@ load_validator_library(const char *libname)
OAuthValidatorModuleInit validator_init;
MemoryContextCallback *mcb;
+ /*
+ * Thre presence, and validity, of libname has already been established by
+ * check_oauth_validator so we don't need to perform more than Assert level
+ * checking here.
+ */
Assert(libname && *libname);
validator_init = (OAuthValidatorModuleInit)
@@ -768,6 +794,15 @@ load_validator_library(const char *libname)
errdetail("Server has magic number 0x%08X, module has 0x%08X.",
PG_OAUTH_VALIDATOR_MAGIC, ValidatorCallbacks->magic));
+ /*
+ * Make sure all required callbacks are present in the ValidatorCallbacks
+ * structure. Right now only the validation callback is required.
+ */
+ if (ValidatorCallbacks->validate_cb == NULL)
+ ereport(ERROR,
+ errmsg("%s module \"%s\" must define the symbol %s",
+ "OAuth validator", libname, "validate_cb"));
+
/* Allocate memory for validator library private state data */
validator_module_state = (ValidatorModuleState *) palloc0(sizeof(ValidatorModuleState));
validator_module_state->sversion = PG_VERSION_NUM;
@@ -804,7 +839,7 @@ bool
check_oauth_validator(HbaLine *hbaline, int elevel, char **err_msg)
{
int line_num = hbaline->linenumber;
- char *file_name = hbaline->sourcefile;
+ const char *file_name = hbaline->sourcefile;
char *rawstring;
List *elemlist = NIL;
diff --git a/src/include/common/oauth-common.h b/src/include/common/oauth-common.h
index 8fe56267780..5fb559d84b2 100644
--- a/src/include/common/oauth-common.h
+++ b/src/include/common/oauth-common.h
@@ -3,7 +3,7 @@
* oauth-common.h
* Declarations for helper functions used for OAuth/OIDC authentication
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/common/oauth-common.h
diff --git a/src/include/libpq/oauth.h b/src/include/libpq/oauth.h
index 7e249613e10..2f01b669633 100644
--- a/src/include/libpq/oauth.h
+++ b/src/include/libpq/oauth.h
@@ -3,7 +3,7 @@
* oauth.h
* Interface to libpq/auth-oauth.c
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/libpq/oauth.h
@@ -83,8 +83,9 @@ typedef struct OAuthValidatorCallbacks
} OAuthValidatorCallbacks;
/*
- * Type of the shared library symbol _PG_oauth_validator_module_init that is
- * looked up when loading a validator module.
+ * Type of the shared library symbol _PG_oauth_validator_module_init which is
+ * required for all validator modules. This function will be invoked during
+ * module loading.
*/
typedef const OAuthValidatorCallbacks *(*OAuthValidatorModuleInit) (void);
extern PGDLLEXPORT const OAuthValidatorCallbacks *_PG_oauth_validator_module_init(void);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c04ee38d086..db6454090d2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -445,7 +445,7 @@
/* Define to 1 if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
-/* Define to 1 if curl_global_init() is guaranteed to be threadsafe. */
+/* Define to 1 if curl_global_init() is guaranteed to be thread-safe. */
#undef HAVE_THREADSAFE_CURL_GLOBAL_INIT
/* Define to 1 if your compiler understands `typeof' or something similar. */
diff --git a/src/interfaces/libpq/fe-auth-oauth-curl.c b/src/interfaces/libpq/fe-auth-oauth-curl.c
index 74323de309a..c9aa51b1007 100644
--- a/src/interfaces/libpq/fe-auth-oauth-curl.c
+++ b/src/interfaces/libpq/fe-auth-oauth-curl.c
@@ -4,7 +4,7 @@
* The libcurl implementation of OAuth/OIDC authentication, using the
* OAuth Device Authorization Grant (RFC 8628).
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
@@ -917,7 +917,7 @@ parse_interval(struct async_ctx *actx, const char *interval_str)
if (parsed < 1)
return actx->debugging ? 0 : 1;
- else if (INT_MAX <= parsed)
+ else if (parsed >= INT_MAX)
return INT_MAX;
return parsed;
@@ -940,7 +940,7 @@ parse_expires_in(struct async_ctx *actx, const char *expires_in_str)
parsed = parse_json_number(expires_in_str);
parsed = round(parsed);
- if (INT_MAX <= parsed)
+ if (parsed >= INT_MAX)
return INT_MAX;
else if (parsed <= INT_MIN)
return INT_MIN;
@@ -966,6 +966,10 @@ parse_device_authz(struct async_ctx *actx, struct device_authz *authz)
*/
{"verification_url", JSON_TOKEN_STRING, {&authz->verification_uri}, REQUIRED},
+ /*
+ * There is no evidence of verification_uri_complete being spelled
+ * with "url" instead with any service provider, so only support "uri".
+ */
{"verification_uri_complete", JSON_TOKEN_STRING, {&authz->verification_uri_complete}, OPTIONAL},
{"interval", JSON_TOKEN_NUMBER, {&authz->interval_str}, OPTIONAL},
@@ -1870,6 +1874,7 @@ append_urlencoded(PQExpBuffer buf, const char *s)
char *haystack;
char *match;
+ /* The first parameter to curl_easy_escape is deprecated by Curl */
escaped = curl_easy_escape(NULL, s, 0);
if (!escaped)
{
@@ -2273,6 +2278,7 @@ finish_device_authz(struct async_ctx *actx)
return false;
}
+ /* Copy the token error into the context error buffer */
record_token_error(actx, &err);
free_token_error(&err);
@@ -2541,7 +2547,7 @@ initialize_curl(PGconn *conn)
/*
* If we determined at configure time that the Curl installation is
- * threadsafe, our job here is much easier. We simply initialize above
+ * thread-safe, our job here is much easier. We simply initialize above
* without any locking (concurrent or duplicated calls are fine in that
* situation), then double-check to make sure the runtime setting agrees,
* to try to catch silent downgrades.
@@ -2553,8 +2559,8 @@ initialize_curl(PGconn *conn)
* In a downgrade situation, the damage is already done. Curl global
* state may be corrupted. Be noisy.
*/
- libpq_append_conn_error(conn, "libcurl is no longer threadsafe\n"
- "\tCurl initialization was reported threadsafe when libpq\n"
+ libpq_append_conn_error(conn, "libcurl is no longer thread-safe\n"
+ "\tCurl initialization was reported thread-safe when libpq\n"
"\twas compiled, but the currently installed version of\n"
"\tlibcurl reports that it is not. Recompile libpq against\n"
"\tthe installed version of libcurl.");
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 8beae9604c7..24448c3e209 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -4,7 +4,7 @@
* The front-end (client) implementation of OAuth/OIDC authentication
* using the SASL OAUTHBEARER mechanism.
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
@@ -272,7 +272,7 @@ oauth_json_scalar(void *state, char *token, JsonTokenType type)
* Assert and don't continue any further for production builds.
*/
Assert(false);
- oauth_json_set_error(ctx, /* don't bother translating */
+ oauth_json_set_error(ctx,
"internal error: target scalar found at nesting level %d during OAUTHBEARER parsing",
ctx->nested);
return JSON_SEM_ACTION_FAILED;
diff --git a/src/test/modules/oauth_validator/Makefile b/src/test/modules/oauth_validator/Makefile
index f297ed5c968..bbd2a98023b 100644
--- a/src/test/modules/oauth_validator/Makefile
+++ b/src/test/modules/oauth_validator/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for src/test/modules/oauth_validator
#
-# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# src/test/modules/oauth_validator/Makefile
diff --git a/src/test/modules/oauth_validator/README b/src/test/modules/oauth_validator/README
index 138a8104622..54eac5b117e 100644
--- a/src/test/modules/oauth_validator/README
+++ b/src/test/modules/oauth_validator/README
@@ -8,6 +8,6 @@ by t/OAuth/Server.pm and t/oauth_server.py, to run the libpq Device
Authorization flow. The tests in t/002_client exercise custom OAuth flows and
don't need an authorization server.
-Tests in this folder generally require 'oauth' to be present in PG_TEST_EXTRA,
-since localhost HTTP servers will be started. A Python installation is required
-to run the mock authorization server.
+Tests in this folder require 'oauth' to be present in PG_TEST_EXTRA, since
+HTTPS servers listening on localhost with TCP/IP sockets will be started. A
+Python installation is required to run the mock authorization server.
diff --git a/src/test/modules/oauth_validator/fail_validator.c b/src/test/modules/oauth_validator/fail_validator.c
index 7b1e69518d9..a4c7a4451d3 100644
--- a/src/test/modules/oauth_validator/fail_validator.c
+++ b/src/test/modules/oauth_validator/fail_validator.c
@@ -1,10 +1,10 @@
/*-------------------------------------------------------------------------
*
* fail_validator.c
- * Test module for serverside OAuth token validation callbacks, which always
- * fails
+ * Test module for serverside OAuth token validation callbacks, which is
+ * guaranteed to always fail in the validation callback
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/test/modules/oauth_validator/fail_validator.c
diff --git a/src/test/modules/oauth_validator/magic_validator.c b/src/test/modules/oauth_validator/magic_validator.c
new file mode 100644
index 00000000000..5ce68cdf405
--- /dev/null
+++ b/src/test/modules/oauth_validator/magic_validator.c
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * magic_validator.c
+ * Test module for serverside OAuth token validation callbacks, which
+ * should fail due to using the wrong PG_OAUTH_VALIDATOR_MAGIC marker
+ * and thus the wrong ABI version
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/test/modules/oauth_validator/fail_validator.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "libpq/oauth.h"
+
+PG_MODULE_MAGIC;
+
+static bool validate_token(const ValidatorModuleState *state,
+ const char *token,
+ const char *role,
+ ValidatorModuleResult *result);
+
+/* Callback implementations (we only need the main one) */
+static const OAuthValidatorCallbacks validator_callbacks = {
+ 0xdeadbeef,
+
+ .validate_cb = validate_token,
+};
+
+const OAuthValidatorCallbacks *
+_PG_oauth_validator_module_init(void)
+{
+ return &validator_callbacks;
+}
+
+static bool
+validate_token(const ValidatorModuleState *state,
+ const char *token, const char *role,
+ ValidatorModuleResult *res)
+{
+ elog(FATAL, "magic_validator: this should be unreachable");
+ pg_unreachable();
+}
diff --git a/src/test/modules/oauth_validator/meson.build b/src/test/modules/oauth_validator/meson.build
index 4b78c90557c..36d1b26369f 100644
--- a/src/test/modules/oauth_validator/meson.build
+++ b/src/test/modules/oauth_validator/meson.build
@@ -1,4 +1,4 @@
-# Copyright (c) 2024, PostgreSQL Global Development Group
+# Copyright (c) 2025, PostgreSQL Global Development Group
validator_sources = files(
'validator.c',
@@ -32,6 +32,22 @@ fail_validator = shared_module('fail_validator',
)
test_install_libs += fail_validator
+magic_validator_sources = files(
+ 'magic_validator.c',
+)
+
+if host_system == 'windows'
+ magic_validator_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'magic_validator',
+ '--FILEDESC', 'magic_validator - ABI incompatible OAuth validator module',])
+endif
+
+magic_validator = shared_module('magic_validator',
+ magic_validator_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += magic_validator
+
oauth_hook_client_sources = files(
'oauth_hook_client.c',
)
diff --git a/src/test/modules/oauth_validator/oauth_hook_client.c b/src/test/modules/oauth_validator/oauth_hook_client.c
index fc003030ff8..9f553792c05 100644
--- a/src/test/modules/oauth_validator/oauth_hook_client.c
+++ b/src/test/modules/oauth_validator/oauth_hook_client.c
@@ -4,7 +4,7 @@
* Test driver for t/002_client.pl, which verifies OAuth hook
* functionality in libpq.
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index d2dda62a2d4..dada89e95cc 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -3,7 +3,7 @@
# Tests the libpq builtin OAuth flow, as well as server-side HBA and validator
# setup.
#
-# Copyright (c) 2021-2024, PostgreSQL Global Development Group
+# Copyright (c) 2021-2025, PostgreSQL Global Development Group
#
use strict;
@@ -14,24 +14,23 @@ use MIME::Base64 qw(encode_base64);
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
-use Config;
use FindBin;
use lib $FindBin::RealBin;
use OAuth::Server;
-if ($Config{osname} eq 'MSWin32')
-{
- plan skip_all => 'OAuth server-side tests are not supported on Windows';
-}
-
if (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\boauth\b/)
{
plan skip_all =>
'Potentially unsafe test oauth not enabled in PG_TEST_EXTRA';
}
+if ($windows_os)
+{
+ plan skip_all => 'OAuth server-side tests are not supported on Windows';
+}
+
if ($ENV{with_libcurl} ne 'yes')
{
plan skip_all => 'client-side OAuth not supported by this build';
@@ -570,6 +569,24 @@ $node->connect_fails(
"fail_validator is used for $user",
expected_stderr => qr/FATAL:\s+fail_validator: sentinel error/);
+#
+# Test ABI compatability magic marker
+#
+$node->append_conf('postgresql.conf',
+ "oauth_validator_libraries = 'magic_validator'\n");
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf(
+ 'pg_hba.conf', qq{
+local all test oauth validator=magic_validator issuer="$issuer" scope="openid postgres"
+});
+$node->restart;
+
+$log_start = $node->wait_for_log(qr/ready to accept connections/, $log_start);
+
+$node->connect_fails(
+ "user=test dbname=postgres oauth_issuer=$issuer/.well-known/oauth-authorization-server/alternate oauth_client_id=f02c6361-0636",
+ "magic_validator is used for $user",
+ expected_stderr => qr/FATAL:\s+OAuth validator module "magic_validator": magic number mismatch/);
$node->stop;
done_testing();
diff --git a/src/test/modules/oauth_validator/t/002_client.pl b/src/test/modules/oauth_validator/t/002_client.pl
index 95cccf90dd8..ab83258d736 100644
--- a/src/test/modules/oauth_validator/t/002_client.pl
+++ b/src/test/modules/oauth_validator/t/002_client.pl
@@ -2,7 +2,7 @@
# Exercises the API for custom OAuth client flows, using the oauth_hook_client
# test driver.
#
-# Copyright (c) 2021-2024, PostgreSQL Global Development Group
+# Copyright (c) 2021-2025, PostgreSQL Global Development Group
#
use strict;
diff --git a/src/test/modules/oauth_validator/t/OAuth/Server.pm b/src/test/modules/oauth_validator/t/OAuth/Server.pm
index f0f23d1d1a8..655b2870b0b 100644
--- a/src/test/modules/oauth_validator/t/OAuth/Server.pm
+++ b/src/test/modules/oauth_validator/t/OAuth/Server.pm
@@ -1,5 +1,5 @@
-# Copyright (c) 2024, PostgreSQL Global Development Group
+# Copyright (c) 2025, PostgreSQL Global Development Group
=pod
@@ -46,7 +46,7 @@ use Test::More;
=over
-=item SSL::Server->new()
+=item OAuth::Server->new()
Create a new OAuth Server object.
diff --git a/src/test/modules/oauth_validator/validator.c b/src/test/modules/oauth_validator/validator.c
index e218f5c8902..b2e5d182e1b 100644
--- a/src/test/modules/oauth_validator/validator.c
+++ b/src/test/modules/oauth_validator/validator.c
@@ -3,7 +3,7 @@
* validator.c
* Test module for serverside OAuth token validation callbacks
*
- * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/test/modules/oauth_validator/validator.c
--
2.39.3 (Apple Git-146)