diff options
Diffstat (limited to 'src/backend/utils/init/miscinit.c')
-rw-r--r-- | src/backend/utils/init/miscinit.c | 157 |
1 files changed, 93 insertions, 64 deletions
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 8997193ca1c..3fc27d7255d 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -29,6 +29,7 @@ #include <utime.h> #include "access/htup_details.h" +#include "access/parallel.h" #include "catalog/pg_authid.h" #include "common/file_perm.h" #include "libpq/libpq.h" @@ -508,7 +509,7 @@ GetOuterUserId(void) static void -SetOuterUserId(Oid userid) +SetOuterUserId(Oid userid, bool is_superuser) { AssertState(SecurityRestrictionContext == 0); AssertArg(OidIsValid(userid)); @@ -516,6 +517,11 @@ SetOuterUserId(Oid userid) /* We force the effective user ID to match, too */ CurrentUserId = userid; + + /* Also update the is_superuser GUC to match OuterUserId's property */ + SetConfigOption("is_superuser", + is_superuser ? "on" : "off", + PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); } @@ -529,6 +535,12 @@ GetSessionUserId(void) return SessionUserId; } +bool +GetSessionUserIsSuperuser(void) +{ + Assert(OidIsValid(SessionUserId)); + return SessionUserIsSuperuser; +} static void SetSessionUserId(Oid userid, bool is_superuser) @@ -537,15 +549,11 @@ SetSessionUserId(Oid userid, bool is_superuser) AssertArg(OidIsValid(userid)); SessionUserId = userid; SessionUserIsSuperuser = is_superuser; - SetRoleIsActive = false; - - /* We force the effective user IDs to match, too */ - OuterUserId = userid; - CurrentUserId = userid; } /* - * GetAuthenticatedUserId - get the authenticated user ID + * GetAuthenticatedUserId/SetAuthenticatedUserId - get/set the authenticated + * user ID */ Oid GetAuthenticatedUserId(void) @@ -554,6 +562,32 @@ GetAuthenticatedUserId(void) return AuthenticatedUserId; } +/* + * Return whether the authenticated user was superuser at connection start. + */ +bool +GetAuthenticatedUserIsSuperuser(void) +{ + Assert(OidIsValid(AuthenticatedUserId)); + return AuthenticatedUserIsSuperuser; +} + +void +SetAuthenticatedUserId(Oid userid, bool is_superuser) +{ + Assert(OidIsValid(userid)); + + /* call only once */ + Assert(!OidIsValid(AuthenticatedUserId)); + + AuthenticatedUserId = userid; + AuthenticatedUserIsSuperuser = is_superuser; + + /* Also mark our PGPROC entry with the authenticated user id */ + /* (We assume this is an atomic store so no lock is needed) */ + MyProc->roleId = userid; +} + /* * GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID @@ -699,6 +733,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid) HeapTuple roleTup; Form_pg_authid rform; char *rname; + bool is_superuser; /* * Don't do scans if we're bootstrapping, none of the system catalogs @@ -706,9 +741,6 @@ InitializeSessionUserId(const char *rolename, Oid roleid) */ AssertState(!IsBootstrapProcessingMode()); - /* call only once */ - AssertState(!OidIsValid(AuthenticatedUserId)); - /* * Make sure syscache entries are flushed for recent catalog changes. This * allows us to find roles that were created on-the-fly during @@ -716,36 +748,52 @@ InitializeSessionUserId(const char *rolename, Oid roleid) */ AcceptInvalidationMessages(); + /* + * Look up the role, either by name if that's given or by OID if not. + * Normally we have to fail if we don't find it, but in parallel workers + * just return without doing anything: all the critical work has been done + * already. The upshot of that is that if the role has been deleted, we + * will not enforce its rolconnlimit against parallel workers anymore. + */ if (rolename != NULL) { roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(rolename)); if (!HeapTupleIsValid(roleTup)) + { + if (InitializingParallelWorker) + return; ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role \"%s\" does not exist", rolename))); + } } else { roleTup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (!HeapTupleIsValid(roleTup)) + { + if (InitializingParallelWorker) + return; ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role with OID %u does not exist", roleid))); + } } rform = (Form_pg_authid) GETSTRUCT(roleTup); roleid = rform->oid; rname = NameStr(rform->rolname); + is_superuser = rform->rolsuper; - AuthenticatedUserId = roleid; - AuthenticatedUserIsSuperuser = rform->rolsuper; - - /* This sets OuterUserId/CurrentUserId too */ - SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); + /* In a parallel worker, ParallelWorkerMain already set these variables */ + if (!InitializingParallelWorker) + { + SetAuthenticatedUserId(roleid, is_superuser); - /* Also mark our PGPROC entry with the authenticated user id */ - /* (We assume this is an atomic store so no lock is needed) */ - MyProc->roleId = roleid; + /* Set SessionUserId and related variables via the GUC mechanisms */ + SetConfigOption("session_authorization", rname, + PGC_BACKEND, PGC_S_OVERRIDE); + } /* * These next checks are not enforced when in standalone mode, so that @@ -774,7 +822,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid) * just document that the connection limit is approximate. */ if (rform->rolconnlimit >= 0 && - !AuthenticatedUserIsSuperuser && + !is_superuser && CountUserBackends(roleid) > rform->rolconnlimit) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), @@ -782,13 +830,6 @@ InitializeSessionUserId(const char *rolename, Oid roleid) rname))); } - /* Record username and superuser status as GUC settings too */ - SetConfigOption("session_authorization", rname, - PGC_BACKEND, PGC_S_OVERRIDE); - SetConfigOption("is_superuser", - AuthenticatedUserIsSuperuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); - ReleaseSysCache(roleTup); } @@ -811,48 +852,39 @@ InitializeSessionUserIdStandalone(void) AuthenticatedUserId = BOOTSTRAP_SUPERUSERID; AuthenticatedUserIsSuperuser = true; - SetSessionUserId(BOOTSTRAP_SUPERUSERID, true); - /* - * XXX This should set SetConfigOption("session_authorization"), too. - * Since we don't, C code will get NULL, and current_setting() will get an - * empty string. + * XXX Ideally we'd do this via SetConfigOption("session_authorization"), + * but we lack the role name needed to do that, and we can't fetch it + * because one reason for this special case is to be able to start up even + * if something's happened to the BOOTSTRAP_SUPERUSERID's pg_authid row. + * Since we don't set the GUC itself, C code will see the value as NULL, + * and current_setting() will report an empty string within this session. */ - SetConfigOption("is_superuser", "on", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + SetSessionAuthorization(BOOTSTRAP_SUPERUSERID, true); + + /* We could do SetConfigOption("role"), but let's be consistent */ + SetCurrentRoleId(InvalidOid, false); } /* * Change session auth ID while running * - * Only a superuser may set auth ID to something other than himself. Note - * that in case of multiple SETs in a single session, the original userid's - * superuserness is what matters. But we set the GUC variable is_superuser - * to indicate whether the *current* session userid is a superuser. - * - * Note: this is not an especially clean place to do the permission check. - * It's OK because the check does not require catalog access and can't - * fail during an end-of-transaction GUC reversion, but we may someday - * have to push it up into assign_session_authorization. + * The SQL standard says that SET SESSION AUTHORIZATION implies SET ROLE NONE. + * We mechanize that at higher levels not here, because this is the GUC + * assign hook for "session_authorization", and it must be commutative with + * SetCurrentRoleId (the hook for "role") because guc.c provides no guarantees + * which will run first during cases such as transaction rollback. Therefore, + * we update derived state (OuterUserId/CurrentUserId/is_superuser) only if + * !SetRoleIsActive. */ void SetSessionAuthorization(Oid userid, bool is_superuser) { - /* Must have authenticated already, else can't make permission check */ - AssertState(OidIsValid(AuthenticatedUserId)); - - if (userid != AuthenticatedUserId && - !AuthenticatedUserIsSuperuser) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set session authorization"))); - SetSessionUserId(userid, is_superuser); - SetConfigOption("is_superuser", - is_superuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + if (!SetRoleIsActive) + SetOuterUserId(userid, is_superuser); } /* @@ -888,28 +920,25 @@ SetCurrentRoleId(Oid roleid, bool is_superuser) /* * Get correct info if it's SET ROLE NONE * - * If SessionUserId hasn't been set yet, just do nothing --- the eventual - * SetSessionUserId call will fix everything. This is needed since we - * will get called during GUC initialization. + * If SessionUserId hasn't been set yet, do nothing beyond updating + * SetRoleIsActive --- the eventual SetSessionAuthorization call will + * update the derived state. This is needed since we will get called + * during GUC initialization. */ if (!OidIsValid(roleid)) { + SetRoleIsActive = false; + if (!OidIsValid(SessionUserId)) return; roleid = SessionUserId; is_superuser = SessionUserIsSuperuser; - - SetRoleIsActive = false; } else SetRoleIsActive = true; - SetOuterUserId(roleid); - - SetConfigOption("is_superuser", - is_superuser ? "on" : "off", - PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + SetOuterUserId(roleid, is_superuser); } |