diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/init/miscinit.c | 165 | ||||
-rwxr-xr-x | src/backend/utils/misc/check_guc | 2 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 87 |
3 files changed, 193 insertions, 61 deletions
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 389ad06f2fe..66d6d1725e0 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.146 2005/07/14 05:13:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.147 2005/07/25 22:12:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -270,24 +270,44 @@ make_absolute_path(const char *path) /* ---------------------------------------------------------------- - * Role ID things + * User ID state * - * The authenticated user is determined at connection start and never - * changes. The session user can be changed only by SET SESSION - * AUTHORIZATION. The current user may change when "setuid" functions - * are implemented. Conceptually there is a stack, whose bottom - * is the session user. You are yourself responsible to save and - * restore the current user id if you need to change it. + * We have to track several different values associated with the concept + * of "user ID". + * + * AuthenticatedUserId is determined at connection start and never changes. + * + * SessionUserId is initially the same as AuthenticatedUserId, but can be + * changed by SET SESSION AUTHORIZATION (if AuthenticatedUserIsSuperuser). + * This is the ID reported by the SESSION_USER SQL function. + * + * OuterUserId is the current user ID in effect at the "outer level" (outside + * any transaction or function). This is initially the same as SessionUserId, + * but can be changed by SET ROLE to any role that SessionUserId is a + * member of. We store this mainly so that AbortTransaction knows what to + * reset CurrentUserId to. + * + * CurrentUserId is the current effective user ID; this is the one to use + * for all normal permissions-checking purposes. At outer level this will + * be the same as OuterUserId, but it changes during calls to SECURITY + * DEFINER functions, as well as locally in some specialized commands. * ---------------------------------------------------------------- */ static Oid AuthenticatedUserId = InvalidOid; static Oid SessionUserId = InvalidOid; +static Oid OuterUserId = InvalidOid; static Oid CurrentUserId = InvalidOid; +/* We also have to remember the superuser state of some of these levels */ static bool AuthenticatedUserIsSuperuser = false; +static bool SessionUserIsSuperuser = false; + +/* We also remember if a SET ROLE is currently active */ +static bool SetRoleIsActive = false; + /* - * This function is relevant for all privilege checks. + * GetUserId/SetUserId - get/set the current effective user ID. */ Oid GetUserId(void) @@ -298,15 +318,37 @@ GetUserId(void) void -SetUserId(Oid roleid) +SetUserId(Oid userid) { - AssertArg(OidIsValid(roleid)); - CurrentUserId = roleid; + AssertArg(OidIsValid(userid)); + CurrentUserId = userid; } /* - * This value is only relevant for informational purposes. + * GetOuterUserId/SetOuterUserId - get/set the outer-level user ID. + */ +Oid +GetOuterUserId(void) +{ + AssertState(OidIsValid(OuterUserId)); + return OuterUserId; +} + + +static void +SetOuterUserId(Oid userid) +{ + AssertArg(OidIsValid(userid)); + OuterUserId = userid; + + /* We force the effective user ID to match, too */ + CurrentUserId = userid; +} + + +/* + * GetSessionUserId/SetSessionUserId - get/set the session user ID. */ Oid GetSessionUserId(void) @@ -316,17 +358,23 @@ GetSessionUserId(void) } -void -SetSessionUserId(Oid roleid) +static void +SetSessionUserId(Oid userid, bool is_superuser) { - AssertArg(OidIsValid(roleid)); - SessionUserId = roleid; - /* Current user defaults to session user. */ - if (!OidIsValid(CurrentUserId)) - CurrentUserId = roleid; + AssertArg(OidIsValid(userid)); + SessionUserId = userid; + SessionUserIsSuperuser = is_superuser; + SetRoleIsActive = false; + + /* We force the effective user IDs to match, too */ + OuterUserId = userid; + CurrentUserId = userid; } +/* + * Initialize user identity during normal backend startup + */ void InitializeSessionUserId(const char *rolename) { @@ -364,7 +412,8 @@ InitializeSessionUserId(const char *rolename) AuthenticatedUserId = roleid; AuthenticatedUserIsSuperuser = rform->rolsuper; - SetSessionUserId(roleid); /* sets CurrentUserId too */ + /* This sets OuterUserId/CurrentUserId too */ + SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); /* Record username and superuser status as GUC settings too */ SetConfigOption("session_authorization", rolename, @@ -391,6 +440,9 @@ InitializeSessionUserId(const char *rolename) } +/* + * Initialize user identity during special backend startup + */ void InitializeSessionUserIdStandalone(void) { @@ -403,7 +455,7 @@ InitializeSessionUserIdStandalone(void) AuthenticatedUserId = BOOTSTRAP_SUPERUSERID; AuthenticatedUserIsSuperuser = true; - SetSessionUserId(BOOTSTRAP_SUPERUSERID); + SetSessionUserId(BOOTSTRAP_SUPERUSERID, true); } @@ -414,21 +466,82 @@ InitializeSessionUserIdStandalone(void) * 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. */ void -SetSessionAuthorization(Oid roleid, bool is_superuser) +SetSessionAuthorization(Oid userid, bool is_superuser) { /* Must have authenticated already, else can't make permission check */ AssertState(OidIsValid(AuthenticatedUserId)); - if (roleid != AuthenticatedUserId && + if (userid != AuthenticatedUserId && !AuthenticatedUserIsSuperuser) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set session authorization"))); - SetSessionUserId(roleid); - SetUserId(roleid); + SetSessionUserId(userid, is_superuser); + + SetConfigOption("is_superuser", + is_superuser ? "on" : "off", + PGC_INTERNAL, PGC_S_OVERRIDE); +} + +/* + * Report current role id + * This follows the semantics of SET ROLE, ie return the outer-level ID + * not the current effective ID, and return InvalidOid when the setting + * is logically SET ROLE NONE. + */ +Oid +GetCurrentRoleId(void) +{ + if (SetRoleIsActive) + return OuterUserId; + else + return InvalidOid; +} + +/* + * Change Role ID while running (SET ROLE) + * + * If roleid is InvalidOid, we are doing SET ROLE NONE: revert to the + * session user authorization. In this case the is_superuser argument + * is ignored. + * + * When roleid is not InvalidOid, the caller must have checked whether + * the session user has permission to become that role. (We cannot check + * here because this routine must be able to execute in a failed transaction + * to restore a prior value of the ROLE GUC variable.) + */ +void +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 (!OidIsValid(roleid)) + { + if (!OidIsValid(SessionUserId)) + return; + + roleid = SessionUserId; + is_superuser = SessionUserIsSuperuser; + + SetRoleIsActive = false; + } + else + SetRoleIsActive = true; + + SetOuterUserId(roleid); SetConfigOption("is_superuser", is_superuser ? "on" : "off", diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc index 5b545d5f436..3332e636427 100755 --- a/src/backend/utils/misc/check_guc +++ b/src/backend/utils/misc/check_guc @@ -18,7 +18,7 @@ ## can be ignored INTENTIONALLY_NOT_INCLUDED="autocommit debug_deadlocks exit_on_error \ is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \ -pre_auth_delay seed server_encoding server_version session_authorization \ +pre_auth_delay role seed server_encoding server_version session_authorization \ trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks trace_notify \ trace_userlocks transaction_isolation transaction_read_only \ zero_damaged_pages" diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 6400ef566b2..726a093d0d7 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.277 2005/07/23 21:05:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.278 2005/07/25 22:12:33 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -195,7 +195,8 @@ static int block_size; static bool integer_datetimes; static bool standard_compliant_strings; -/* should be static, but commands/variable.c needs to get at it */ +/* should be static, but commands/variable.c needs to get at these */ +char *role_string; char *session_authorization_string; @@ -1829,6 +1830,17 @@ static struct config_string ConfigureNamesString[] = }, { + /* Not for general use --- used by SET ROLE */ + {"role", PGC_USERSET, UNGROUPED, + gettext_noop("Sets the current role."), + NULL, + GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &role_string, + "none", assign_role, show_role + }, + + { /* Not for general use --- used by SET SESSION AUTHORIZATION */ {"session_authorization", PGC_USERSET, UNGROUPED, gettext_noop("Sets the session user name."), @@ -2048,8 +2060,6 @@ static bool guc_dirty; /* TRUE if need to do commit/abort work */ static bool reporting_enabled; /* TRUE to enable GUC_REPORT */ -static char *guc_string_workspace; /* for avoiding memory leaks */ - static int guc_var_compare(const void *a, const void *b); static int guc_name_compare(const char *namea, const char *nameb); @@ -2576,8 +2586,6 @@ InitializeGUCOptions(void) reporting_enabled = false; - guc_string_workspace = NULL; - /* * Prevent any attempt to override the transaction modes from * non-interactive sources. @@ -2976,13 +2984,6 @@ AtEOXact_GUC(bool isCommit, bool isSubXact) if (!guc_dirty) return; - /* Prevent memory leak if ereport during an assign_hook */ - if (guc_string_workspace) - { - free(guc_string_workspace); - guc_string_workspace = NULL; - } - my_level = GetCurrentTransactionNestLevel(); Assert(isSubXact ? (my_level > 1) : (my_level == 1)); @@ -3389,6 +3390,33 @@ parse_real(const char *value, double *result) } +/* + * Call a GucStringAssignHook function, being careful to free the + * "newval" string if the hook ereports. + * + * This is split out of set_config_option just to avoid the "volatile" + * qualifiers that would otherwise have to be plastered all over. + */ +static const char * +call_string_assign_hook(GucStringAssignHook assign_hook, + char *newval, bool doit, GucSource source) +{ + const char *result; + + PG_TRY(); + { + result = (*assign_hook) (newval, doit, source); + } + PG_CATCH(); + { + free(newval); + PG_RE_THROW(); + } + PG_END_TRY(); + + return result; +} + /* * Sets option `name' to given value. The value should be a string @@ -3833,21 +3861,18 @@ set_config_option(const char *name, const char *value, break; } - /* - * Remember string in workspace, so that we can free it - * and avoid a permanent memory leak if hook ereports. - */ - if (guc_string_workspace) - free(guc_string_workspace); - guc_string_workspace = newval; - if (conf->assign_hook) { const char *hookresult; - hookresult = (*conf->assign_hook) (newval, - changeVal, source); - guc_string_workspace = NULL; + /* + * If the hook ereports, we have to make sure we free + * newval, else it will be a permanent memory leak. + */ + hookresult = call_string_assign_hook(conf->assign_hook, + newval, + changeVal, + source); if (hookresult == NULL) { free(newval); @@ -3874,8 +3899,6 @@ set_config_option(const char *name, const char *value, } } - guc_string_workspace = NULL; - if (changeVal || makeDefault) { /* Save old value to support transaction abort */ @@ -4305,8 +4328,7 @@ init_custom_variable(struct config_generic * gen, } void -DefineCustomBoolVariable( - const char *name, +DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, @@ -4328,8 +4350,7 @@ DefineCustomBoolVariable( } void -DefineCustomIntVariable( - const char *name, +DefineCustomIntVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, @@ -4355,8 +4376,7 @@ DefineCustomIntVariable( } void -DefineCustomRealVariable( - const char *name, +DefineCustomRealVariable(const char *name, const char *short_desc, const char *long_desc, double *valueAddr, @@ -4382,8 +4402,7 @@ DefineCustomRealVariable( } void -DefineCustomStringVariable( - const char *name, +DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, |