v48-0006-fixup-Add-OAUTHBEARER-SASL-mechanism.patch
application/x-patch
Filename: v48-0006-fixup-Add-OAUTHBEARER-SASL-mechanism.patch
Type: application/x-patch
Part: 6
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 v48-0006
Subject: fixup! Add OAUTHBEARER SASL mechanism
| File | + | − |
|---|---|---|
| doc/src/sgml/oauth-validators.sgml | 13 | 9 |
| src/backend/libpq/auth-oauth.c | 17 | 3 |
| src/include/libpq/oauth.h | 47 | 1 |
| src/test/modules/oauth_validator/fail_validator.c | 10 | 5 |
| src/test/modules/oauth_validator/validator.c | 18 | 10 |
From 954341052b44ab0f89e679926c9b66a085d2f064 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Thu, 6 Feb 2025 20:53:33 -0800
Subject: [PATCH v48 6/8] fixup! Add OAUTHBEARER SASL mechanism
---
doc/src/sgml/oauth-validators.sgml | 22 +++++----
src/backend/libpq/auth-oauth.c | 20 ++++++--
src/include/libpq/oauth.h | 48 ++++++++++++++++++-
.../modules/oauth_validator/fail_validator.c | 15 ++++--
src/test/modules/oauth_validator/validator.c | 28 +++++++----
5 files changed, 105 insertions(+), 28 deletions(-)
diff --git a/doc/src/sgml/oauth-validators.sgml b/doc/src/sgml/oauth-validators.sgml
index c8bbac7b462..eb8c4431c2d 100644
--- a/doc/src/sgml/oauth-validators.sgml
+++ b/doc/src/sgml/oauth-validators.sgml
@@ -297,13 +297,15 @@
validator module a function named
<function>_PG_oauth_validator_module_init</function> must be provided. The
return value of the function must be a pointer to a struct of type
- <structname>OAuthValidatorCallbacks</structname>, which contains pointers to
- the module's token validation functions. The returned
+ <structname>OAuthValidatorCallbacks</structname>, which contains a magic
+ number and pointers to the module's token validation functions. The returned
pointer must be of server lifetime, which is typically achieved by defining
it as a <literal>static const</literal> variable in global scope.
<programlisting>
typedef struct OAuthValidatorCallbacks
{
+ uint32 magic; /* must be set to PG_OAUTH_VALIDATOR_MAGIC */
+
ValidatorStartupCB startup_cb;
ValidatorShutdownCB shutdown_cb;
ValidatorValidateCB validate_cb;
@@ -348,14 +350,16 @@ typedef void (*ValidatorStartupCB) (ValidatorModuleState *state);
previous calls will be available in <structfield>state->private_data</structfield>.
<programlisting>
-typedef ValidatorModuleResult *(*ValidatorValidateCB) (ValidatorModuleState *state, const char *token, const char *role);
+typedef bool (*ValidatorValidateCB) (const ValidatorModuleState *state,
+ const char *token, const char *role,
+ ValidatorModuleResult *result);
</programlisting>
<replaceable>token</replaceable> will contain the bearer token to validate.
The server 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
- return a palloc'd <literal>ValidatorModuleResult</literal> struct, which is
+ set output parameters in the <literal>result</literal> struct, which is
defined as below:
<programlisting>
@@ -375,17 +379,17 @@ typedef struct ValidatorModuleResult
determined.
</para>
<para>
- The caller assumes ownership of the returned memory allocation, the
- validator module should not in any way access the memory after it has been
- returned. A validator may instead return NULL to signal an internal
- error.
+ A validator may return <literal>false</literal> to signal an internal error,
+ in which case any result parameters are ignored and the connection fails.
+ Otherwise the validator should return <literal>true</literal> to indicate
+ that it has processed the token and made an authorization decision.
</para>
<para>
The behavior after <function>validate_cb</function> returns depends on the
specific HBA setup. Normally, the <structfield>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>trust_validator_authz</literal> turned on, the
+ 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
validator to ensure that the token carries enough privileges for the user to
diff --git a/src/backend/libpq/auth-oauth.c b/src/backend/libpq/auth-oauth.c
index aa16977c643..e2b5d1ed913 100644
--- a/src/backend/libpq/auth-oauth.c
+++ b/src/backend/libpq/auth-oauth.c
@@ -656,9 +656,9 @@ validate(Port *port, const char *auth)
errmsg("validation of OAuth token requested without a validator loaded"));
/* Call the validation function from the validator module */
- ret = ValidatorCallbacks->validate_cb(validator_module_state,
- token, port->user_name);
- if (ret == NULL)
+ ret = palloc0(sizeof(ValidatorModuleResult));
+ if (!ValidatorCallbacks->validate_cb(validator_module_state, token,
+ port->user_name, ret))
{
ereport(LOG, errmsg("internal error in OAuth validator module"));
return false;
@@ -756,8 +756,22 @@ load_validator_library(const char *libname)
ValidatorCallbacks = (*validator_init) ();
Assert(ValidatorCallbacks);
+ /*
+ * Check the magic number, to protect against break-glass scenarios where
+ * the ABI must change within a major version. load_external_function()
+ * already checks for compatibility across major versions.
+ */
+ if (ValidatorCallbacks->magic != PG_OAUTH_VALIDATOR_MAGIC)
+ ereport(ERROR,
+ errmsg("%s module \"%s\": magic number mismatch",
+ "OAuth validator", libname),
+ errdetail("Server has magic number 0x%08X, module has 0x%08X.",
+ PG_OAUTH_VALIDATOR_MAGIC, ValidatorCallbacks->magic));
+
/* Allocate memory for validator library private state data */
validator_module_state = (ValidatorModuleState *) palloc0(sizeof(ValidatorModuleState));
+ validator_module_state->sversion = PG_VERSION_NUM;
+
if (ValidatorCallbacks->startup_cb != NULL)
ValidatorCallbacks->startup_cb(validator_module_state);
diff --git a/src/include/libpq/oauth.h b/src/include/libpq/oauth.h
index 4fcdda74305..7e249613e10 100644
--- a/src/include/libpq/oauth.h
+++ b/src/include/libpq/oauth.h
@@ -20,26 +20,72 @@ extern PGDLLIMPORT char *oauth_validator_libraries_string;
typedef struct ValidatorModuleState
{
+ /* Holds the server's PG_VERSION_NUM. Reserved for future extensibility. */
+ int sversion;
+
+ /*
+ * Private data pointer for use by a validator module. This can be used to
+ * store state for the module that will be passed to each of its
+ * callbacks.
+ */
void *private_data;
} ValidatorModuleState;
typedef struct ValidatorModuleResult
{
+ /*
+ * Should be set to true if the token carries sufficient permissions for
+ * the bearer to connect.
+ */
bool authorized;
+
+ /*
+ * If the token authenticates the user, this should be set to a palloc'd
+ * string containing the SYSTEM_USER to use for HBA mapping. Consider
+ * setting this even if result->authorized is false so that DBAs may use
+ * the logs to match end users to token failures.
+ *
+ * This is required if the module is not configured for ident mapping
+ * delegation. See the validator module documentation for details.
+ */
char *authn_id;
} ValidatorModuleResult;
+/*
+ * Validator module callbacks
+ *
+ * These callback functions should be defined by validator modules and returned
+ * via _PG_oauth_validator_module_init(). ValidatorValidateCB is the only
+ * required callback. For more information about the purpose of each callback,
+ * refer to the OAuth validator modules documentation.
+ */
typedef void (*ValidatorStartupCB) (ValidatorModuleState *state);
typedef void (*ValidatorShutdownCB) (ValidatorModuleState *state);
-typedef ValidatorModuleResult *(*ValidatorValidateCB) (ValidatorModuleState *state, const char *token, const char *role);
+typedef bool (*ValidatorValidateCB) (const ValidatorModuleState *state,
+ const char *token, const char *role,
+ ValidatorModuleResult *result);
+
+/*
+ * Identifies the compiled ABI version of the validator module. Since the server
+ * already enforces the PG_MODULE_MAGIC number for modules across major
+ * versions, this is reserved for emergency use within a stable release line.
+ * May it never need to change.
+ */
+#define PG_OAUTH_VALIDATOR_MAGIC 0x20250207
typedef struct OAuthValidatorCallbacks
{
+ uint32 magic; /* must be set to PG_OAUTH_VALIDATOR_MAGIC */
+
ValidatorStartupCB startup_cb;
ValidatorShutdownCB shutdown_cb;
ValidatorValidateCB validate_cb;
} OAuthValidatorCallbacks;
+/*
+ * Type of the shared library symbol _PG_oauth_validator_module_init that is
+ * looked up when loading a validator module.
+ */
typedef const OAuthValidatorCallbacks *(*OAuthValidatorModuleInit) (void);
extern PGDLLEXPORT const OAuthValidatorCallbacks *_PG_oauth_validator_module_init(void);
diff --git a/src/test/modules/oauth_validator/fail_validator.c b/src/test/modules/oauth_validator/fail_validator.c
index f77a3e115c6..7b1e69518d9 100644
--- a/src/test/modules/oauth_validator/fail_validator.c
+++ b/src/test/modules/oauth_validator/fail_validator.c
@@ -19,12 +19,15 @@
PG_MODULE_MAGIC;
-static ValidatorModuleResult *fail_token(ValidatorModuleState *state,
- const char *token,
- const char *role);
+static bool fail_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 = {
+ PG_OAUTH_VALIDATOR_MAGIC,
+
.validate_cb = fail_token,
};
@@ -34,8 +37,10 @@ _PG_oauth_validator_module_init(void)
return &validator_callbacks;
}
-static ValidatorModuleResult *
-fail_token(ValidatorModuleState *state, const char *token, const char *role)
+static bool
+fail_token(const ValidatorModuleState *state,
+ const char *token, const char *role,
+ ValidatorModuleResult *res)
{
elog(FATAL, "fail_validator: sentinel error");
pg_unreachable();
diff --git a/src/test/modules/oauth_validator/validator.c b/src/test/modules/oauth_validator/validator.c
index ef9bbb2866f..e218f5c8902 100644
--- a/src/test/modules/oauth_validator/validator.c
+++ b/src/test/modules/oauth_validator/validator.c
@@ -23,12 +23,15 @@ PG_MODULE_MAGIC;
static void validator_startup(ValidatorModuleState *state);
static void validator_shutdown(ValidatorModuleState *state);
-static ValidatorModuleResult *validate_token(ValidatorModuleState *state,
- const char *token,
- const char *role);
+static bool validate_token(const ValidatorModuleState *state,
+ const char *token,
+ const char *role,
+ ValidatorModuleResult *result);
/* Callback implementations (exercise all three) */
static const OAuthValidatorCallbacks validator_callbacks = {
+ PG_OAUTH_VALIDATOR_MAGIC,
+
.startup_cb = validator_startup,
.shutdown_cb = validator_shutdown,
.validate_cb = validate_token
@@ -89,6 +92,13 @@ _PG_oauth_validator_module_init(void)
static void
validator_startup(ValidatorModuleState *state)
{
+ /*
+ * Make sure the server is correctly setting sversion. (Real modules
+ * should not do this; it would defeat upgrade compatibility.)
+ */
+ if (state->sversion != PG_VERSION_NUM)
+ elog(ERROR, "oauth_validator: sversion set to %d", state->sversion);
+
state->private_data = PRIVATE_COOKIE;
}
@@ -108,18 +118,16 @@ validator_shutdown(ValidatorModuleState *state)
* Validator implementation. Logs the incoming data and authorizes the token by
* default; the behavior can be modified via the module's GUC settings.
*/
-static ValidatorModuleResult *
-validate_token(ValidatorModuleState *state, const char *token, const char *role)
+static bool
+validate_token(const ValidatorModuleState *state,
+ const char *token, const char *role,
+ ValidatorModuleResult *res)
{
- ValidatorModuleResult *res;
-
/* Check to make sure our private state still exists. */
if (state->private_data != PRIVATE_COOKIE)
elog(ERROR, "oauth_validator: private state cookie changed to %p in validate",
state->private_data);
- res = palloc(sizeof(ValidatorModuleResult));
-
elog(LOG, "oauth_validator: token=\"%s\", role=\"%s\"", token, role);
elog(LOG, "oauth_validator: issuer=\"%s\", scope=\"%s\"",
MyProcPort->hba->oauth_issuer,
@@ -131,5 +139,5 @@ validate_token(ValidatorModuleState *state, const char *token, const char *role)
else
res->authn_id = pstrdup(role);
- return res;
+ return true;
}
--
2.34.1