aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/parallel.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
commit5a2fed911a85ed6d8a015a6bafe3a0d9a69334ae (patch)
treecfefb92673eb97a03fd6c960abd313447dda19f4 /src/backend/access/transam/parallel.c
parentcd7ab57532bb4fbf2e636b1f7d132e6e2d9ac5fc (diff)
downloadpostgresql-5a2fed911a85ed6d8a015a6bafe3a0d9a69334ae.tar.gz
postgresql-5a2fed911a85ed6d8a015a6bafe3a0d9a69334ae.zip
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/access/transam/parallel.c')
-rw-r--r--src/backend/access/transam/parallel.c37
1 files changed, 27 insertions, 10 deletions
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index a10bf02ccff..0a1e089ec1d 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -83,12 +83,14 @@ typedef struct FixedParallelState
/* Fixed-size state that workers must restore. */
Oid database_id;
Oid authenticated_user_id;
- Oid current_user_id;
+ Oid session_user_id;
Oid outer_user_id;
+ Oid current_user_id;
Oid temp_namespace_id;
Oid temp_toast_namespace_id;
int sec_context;
- bool is_superuser;
+ bool session_user_is_superuser;
+ bool role_is_superuser;
PGPROC *parallel_leader_pgproc;
pid_t parallel_leader_pid;
ProcNumber parallel_leader_proc_number;
@@ -336,9 +338,11 @@ InitializeParallelDSM(ParallelContext *pcxt)
shm_toc_allocate(pcxt->toc, sizeof(FixedParallelState));
fps->database_id = MyDatabaseId;
fps->authenticated_user_id = GetAuthenticatedUserId();
+ fps->session_user_id = GetSessionUserId();
fps->outer_user_id = GetCurrentRoleId();
- fps->is_superuser = current_role_is_superuser;
GetUserIdAndSecContext(&fps->current_user_id, &fps->sec_context);
+ fps->session_user_is_superuser = GetSessionUserIsSuperuser();
+ fps->role_is_superuser = current_role_is_superuser;
GetTempNamespaceState(&fps->temp_namespace_id,
&fps->temp_toast_namespace_id);
fps->parallel_leader_pgproc = MyProc;
@@ -1404,6 +1408,17 @@ ParallelWorkerMain(Datum main_arg)
entrypt = LookupParallelWorkerFunction(library_name, function_name);
+ /*
+ * Restore current session authorization and role id. No verification
+ * happens here, we just blindly adopt the leader's state. Note that this
+ * has to happen before InitPostgres, since InitializeSessionUserId will
+ * not set these variables.
+ */
+ SetAuthenticatedUserId(fps->authenticated_user_id);
+ SetSessionAuthorization(fps->session_user_id,
+ fps->session_user_is_superuser);
+ SetCurrentRoleId(fps->outer_user_id, fps->role_is_superuser);
+
/* Restore database connection. */
BackgroundWorkerInitializeConnectionByOid(fps->database_id,
fps->authenticated_user_id,
@@ -1483,19 +1498,21 @@ ParallelWorkerMain(Datum main_arg)
/*
* Restore GUC values from launching backend. We can't do this earlier,
* because GUC check hooks that do catalog lookups need to see the same
- * database state as the leader.
+ * database state as the leader. Also, the check hooks for
+ * session_authorization and role assume we already set the correct role
+ * OIDs.
*/
gucspace = shm_toc_lookup(toc, PARALLEL_KEY_GUC, false);
RestoreGUCState(gucspace);
/*
- * Restore current role id. Skip verifying whether session user is
- * allowed to become this role and blindly restore the leader's state for
- * current role.
+ * Restore current user ID and security context. No verification happens
+ * here, we just blindly adopt the leader's state. We can't do this till
+ * after restoring GUCs, else we'll get complaints about restoring
+ * session_authorization and role. (In effect, we're assuming that all
+ * the restored values are okay to set, even if we are now inside a
+ * restricted context.)
*/
- SetCurrentRoleId(fps->outer_user_id, fps->is_superuser);
-
- /* Restore user ID and security context. */
SetUserIdAndSecContext(fps->current_user_id, fps->sec_context);
/* Restore temp-namespace state to ensure search path matches leader's. */