summaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/guc.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-11-11 10:29:54 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2024-11-11 10:29:54 -0500
commita5d2e6205f716c79ecfb15eb1aae75bae3f8daa9 (patch)
tree5b8b25937265db184a5cdd983ea828d867a50409 /src/backend/utils/misc/guc.c
parent6db5ea8de8ce15897b706009aaf701d23bd65b23 (diff)
Fix improper interactions between session_authorization and role.
The SQL spec mandates that SET SESSION AUTHORIZATION implies SET ROLE NONE. We tried to implement that within the lowest-level functions that manipulate these settings, but that was a bad idea. In particular, guc.c assumes that it doesn't matter in what order it applies GUC variable updates, but that was not the case for these two variables. This problem, compounded by some hackish attempts to work around it, led to some security-grade issues: * Rolling back a transaction that had done SET SESSION AUTHORIZATION would revert to SET ROLE NONE, even if that had not been the previous state, so that the effective user ID might now be different from what it had been. * The same for SET SESSION AUTHORIZATION in a function SET clause. * If a parallel worker inspected current_setting('role'), it saw "none" even when it should see something else. Also, although the parallel worker startup code intended to cope with the current role's pg_authid row having disappeared, its implementation of that was incomplete so it would still fail. Fix by fully separating the miscinit.c functions that assign session_authorization from those that assign role. To implement the spec's requirement, teach set_config_option itself to perform "SET ROLE NONE" when it sets session_authorization. (This is undoubtedly ugly, but the alternatives seem worse. In particular, there's no way to do it within assign_session_authorization without incompatible changes in the API for GUC assign hooks.) Also, improve ParallelWorkerMain to directly set all the relevant user-ID variables instead of relying on some of them to get set indirectly. That allows us to survive not finding the pg_authid row during worker startup. In v16 and earlier, this includes back-patching 9987a7bf3 which fixed a violation of GUC coding rules: SetSessionAuthorization is not an appropriate place to be throwing errors from. Security: CVE-2024-10978
Diffstat (limited to 'src/backend/utils/misc/guc.c')
-rw-r--r--src/backend/utils/misc/guc.c43
1 files changed, 35 insertions, 8 deletions
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 835986288a2..79599a2c10f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8187,6 +8187,9 @@ set_config_option_ext(const char *name, const char *value,
case PGC_STRING:
{
struct config_string *conf = (struct config_string *) record;
+ GucContext orig_context = context;
+ GucSource orig_source = source;
+ Oid orig_srole = srole;
#define newval (newval_union.stringval)
@@ -8272,6 +8275,36 @@ set_config_option_ext(const char *name, const char *value,
conf->gen.source = source;
conf->gen.scontext = context;
conf->gen.srole = srole;
+
+ /*
+ * Ugly hack: during SET session_authorization, forcibly
+ * do SET ROLE NONE with the same context/source/etc, so
+ * that the effects will have identical lifespan. This is
+ * required by the SQL spec, and it's not possible to do
+ * it within the variable's check hook or assign hook
+ * because our APIs for those don't pass enough info.
+ * However, don't do it if is_reload: in that case we
+ * expect that if "role" isn't supposed to be default, it
+ * has been or will be set by a separate reload action.
+ *
+ * A fine point: for RESET session_authorization, we do
+ * "RESET role" not "SET ROLE NONE" (by passing down NULL
+ * rather than "none" for the value). This would have the
+ * same effects in typical cases, but if the reset value
+ * of "role" is not "none" it seems better to revert to
+ * that.
+ */
+ if (!is_reload &&
+ strcmp(conf->gen.name, "session_authorization") == 0)
+ (void) set_config_option_ext("role",
+ value ? "none" : NULL,
+ orig_context,
+ orig_source,
+ orig_srole,
+ action,
+ true,
+ elevel,
+ false);
}
if (makeDefault)
@@ -10927,12 +10960,6 @@ can_skip_gucvar(struct config_generic *gconf)
* mechanisms (if indeed they aren't compile-time constants). So we may
* always skip these.
*
- * Role must be handled specially because its current value can be an
- * invalid value (for instance, if someone dropped the role since we set
- * it). So if we tried to serialize it normally, we might get a failure.
- * We skip it here, and use another mechanism to ensure the worker has the
- * right value.
- *
* For all other GUCs, we skip if the GUC has its compiled-in default
* value (i.e., source == PGC_S_DEFAULT). On the leader side, this means
* we don't send GUCs that have their default values, which typically
@@ -10941,8 +10968,8 @@ can_skip_gucvar(struct config_generic *gconf)
* comments in RestoreGUCState for more info.
*/
return gconf->context == PGC_POSTMASTER ||
- gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT ||
- strcmp(gconf->name, "role") == 0;
+ gconf->context == PGC_INTERNAL ||
+ gconf->source == PGC_S_DEFAULT;
}
/*