From: Tom Lane Date: Thu, 3 Jan 2008 21:25:58 +0000 (+0000) Subject: Make standard maintenance operations (including VACUUM, ANALYZE, REINDEX, X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=50be46a28eb1686d52f094c43e75197da1a76fd2;p=users%2Fbernd%2Fpostgres.git Make standard maintenance operations (including VACUUM, ANALYZE, REINDEX, and CLUSTER) execute as the table owner rather than the calling user, using the same privilege-switching mechanism already used for SECURITY DEFINER functions. The purpose of this change is to ensure that user-defined functions used in index definitions cannot acquire the privileges of a superuser account that is performing routine maintenance. While a function used in an index is supposed to be IMMUTABLE and thus not able to do anything very interesting, there are several easy ways around that restriction; and even if we could plug them all, there would remain a risk of reading sensitive information and broadcasting it through a covert channel such as CPU usage. To prevent bypassing this security measure, execution of SET SESSION AUTHORIZATION and SET ROLE is now forbidden within a SECURITY DEFINER context. Thanks to Itagaki Takahiro for reporting this vulnerability. Security: CVE-2007-6600 --- diff --git a/doc/src/sgml/ref/set_session_auth.sgml b/doc/src/sgml/ref/set_session_auth.sgml index ba516c7bd0..9a0beb96ad 100644 --- a/doc/src/sgml/ref/set_session_auth.sgml +++ b/doc/src/sgml/ref/set_session_auth.sgml @@ -27,7 +27,7 @@ RESET SESSION AUTHORIZATION This command sets the session user identifier and the current user - identifier of the current SQL-session context to be + identifier of the current SQL session to be username. The user name may be written as either an identifier or a string literal. The session user identifier is valid for the duration of a @@ -39,7 +39,7 @@ RESET SESSION AUTHORIZATION The session user identifier is initially set to be the (possibly authenticated) user name provided by the client. The current user identifier is normally equal to the session user identifier, but - may change temporarily in the context of setuid + might change temporarily in the context of SECURITY DEFINER functions and similar mechanisms. The current user identifier is relevant for permission checking. @@ -65,6 +65,15 @@ RESET SESSION AUTHORIZATION + + Notes + + + SET SESSION AUTHORIZATION cannot be used within a + SECURITY DEFINER function. + + + Examples diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index a7ced751ab..8e7d436b3f 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -215,6 +215,8 @@ static TransactionStateData CurrentTransactionStateData = { TransactionState CurrentTransactionState = &CurrentTransactionStateData; +static Oid prevUser; /* CurrentUserId at transaction start */ + /* * User-tweakable parameters */ @@ -960,6 +962,7 @@ static void CommitTransaction(void) { TransactionState s = CurrentTransactionState; + bool prevSecDefCxt; /* * check the current transaction state @@ -983,6 +986,10 @@ CommitTransaction(void) */ s->state = TRANS_COMMIT; + GetUserIdAndContext(&prevUser, &prevSecDefCxt); + /* SecurityDefinerContext should never be set outside a transaction */ + Assert(!prevSecDefCxt); + /* * Do pre-commit processing (most of this stuff requires database * access, and in fact could still cause an error...) @@ -1118,9 +1125,16 @@ AbortTransaction(void) AtAbort_Memory(); /* - * Reset user id which might have been changed transiently + * Reset user ID which might have been changed transiently. We need this + * to clean up in case control escaped out of a SECURITY DEFINER function + * or other local change of CurrentUserId; therefore, the prior value + * of SecurityDefinerContext also needs to be restored. + * + * (Note: it is not necessary to restore session authorization + * setting here because that can only be changed via GUC, and GUC will + * take care of rolling it back if need be.) */ - SetUserId(GetSessionUserId()); + SetUserIdAndContext(prevUser, false); /* * do abort processing diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 697cd05b8f..24df242a78 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1439,6 +1439,8 @@ index_build(Relation heapRelation, IndexInfo *indexInfo) { RegProcedure procedure; + Oid save_userid; + bool save_secdefcxt; /* * sanity checks @@ -1449,6 +1451,13 @@ index_build(Relation heapRelation, procedure = indexRelation->rd_am->ambuild; Assert(RegProcedureIsValid(procedure)); + /* + * Switch to the table owner's userid, so that any index functions are + * run as that user. + */ + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(heapRelation->rd_rel->relowner, true); + /* * Call the access method's build procedure */ @@ -1456,6 +1465,9 @@ index_build(Relation heapRelation, PointerGetDatum(heapRelation), PointerGetDatum(indexRelation), PointerGetDatum(indexInfo)); + + /* Restore userid */ + SetUserIdAndContext(save_userid, save_secdefcxt); } diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index 620eae5f07..12c6407752 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -43,9 +43,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) const char *owner_name; Oid owner_userid; Oid saved_userid; + bool saved_secdefcxt; AclResult aclresult; - saved_userid = GetUserId(); + GetUserIdAndContext(&saved_userid, &saved_secdefcxt); /* * Figure out user identities. @@ -68,7 +69,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) * (This will revert to session user on error or at the end of * this routine.) */ - SetUserId(owner_userid); + SetUserIdAndContext(owner_userid, true); } else /* not superuser */ @@ -143,7 +144,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) PopSpecialNamespace(namespaceId); /* Reset current user */ - SetUserId(saved_userid); + SetUserIdAndContext(saved_userid, saved_secdefcxt); } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 868b3de66e..0be479c4ea 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -720,6 +720,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) LockRelId onerelid; Oid toast_relid; bool result; + Oid save_userid; + bool save_secdefcxt; /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(true); @@ -820,6 +822,14 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) */ toast_relid = onerel->rd_rel->reltoastrelid; + /* + * Switch to the table owner's userid, so that any index functions are + * run as that user. (This is unnecessary, but harmless, for lazy + * VACUUM.) + */ + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(onerel->rd_rel->relowner, true); + /* * Do the actual work --- either FULL or "lazy" vacuum */ @@ -830,6 +840,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) result = true; /* did the vacuum */ + /* Restore userid */ + SetUserIdAndContext(save_userid, save_secdefcxt); + /* all done with this class, but hold lock until commit */ relation_close(onerel, NoLock); diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 156fd5fdf2..6bba275e36 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -561,6 +561,20 @@ assign_session_authorization(const char *value, bool doit, bool interactive) /* not a saved ID, so look it up */ HeapTuple userTup; + if (InSecurityDefinerContext()) + { + /* + * Disallow SET SESSION AUTHORIZATION inside a security definer + * context. We need to do this because when we exit the context, + * GUC won't be notified, leaving things out of sync. Note that + * this test is positioned so that restoring a previously saved + * setting isn't prevented. + */ + if (interactive) + elog(ERROR, "cannot set session authorization within security-definer function"); + return NULL; + } + if (! IsTransactionState()) { /* diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index fa593a6852..0e1f8d660f 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -178,9 +178,10 @@ RI_FKey_check(PG_FUNCTION_ARGS) bool isnull; int i; int match_type; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; - save_uid = GetUserId(); + GetUserIdAndContext(&save_userid, &save_secdefcxt); ReferentialIntegritySnapshotOverride = true; @@ -297,12 +298,12 @@ RI_FKey_check(PG_FUNCTION_ARGS) /* * Execute the plan */ - SetUserId(RelationGetForm(pk_rel)->relowner); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed == 0) elog(ERROR, "%s referential integrity violation - " @@ -486,12 +487,12 @@ RI_FKey_check(PG_FUNCTION_ARGS) * Now check that foreign key exists in PK table */ - SetUserId(RelationGetForm(pk_rel)->relowner); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed == 0) elog(ERROR, "%s referential integrity violation - " @@ -558,10 +559,11 @@ ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, Datum check_values[RI_MAX_NUMKEYS]; char check_nulls[RI_MAX_NUMKEYS + 1]; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; bool result; - save_uid = GetUserId(); + GetUserIdAndContext(&save_userid, &save_secdefcxt); ri_BuildQueryKeyPkCheck(&qkey, tgoid, RI_PLAN_CHECK_LOOKUPPK, pk_rel, @@ -681,12 +683,12 @@ ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, * Now check that foreign key exists in PK table */ - SetUserId(RelationGetForm(pk_rel)->relowner); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in ri_Check_Pk_Match()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); result = (SPI_processed != 0); @@ -721,9 +723,10 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) bool isnull; int i; int match_type; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; - save_uid = GetUserId(); + GetUserIdAndContext(&save_userid, &save_secdefcxt); ReferentialIntegritySnapshotOverride = true; @@ -886,12 +889,12 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) /* * Now check for existing references */ - SetUserId(RelationGetForm(pk_rel)->relowner); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed > 0) elog(ERROR, "%s referential integrity violation - " @@ -948,9 +951,10 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) bool isnull; int i; int match_type; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; - save_uid = GetUserId(); + GetUserIdAndContext(&save_userid, &save_secdefcxt); ReferentialIntegritySnapshotOverride = true; @@ -1124,12 +1128,12 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) /* * Now check for existing references */ - SetUserId(RelationGetForm(pk_rel)->relowner); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed > 0) elog(ERROR, "%s referential integrity violation - " @@ -1182,7 +1186,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) char del_nulls[RI_MAX_NUMKEYS + 1]; bool isnull; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -1333,13 +1338,13 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) /* * Now delete constraint */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, del_values, del_nulls, 0) != SPI_OK_DELETE) elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_del()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()"); @@ -1387,7 +1392,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) bool isnull; int i; int j; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -1569,13 +1575,13 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) /* * Now update the existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_upd()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()"); @@ -1628,7 +1634,8 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) char del_nulls[RI_MAX_NUMKEYS + 1]; bool isnull; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -1781,13 +1788,13 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) /* * Now check for existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed > 0) elog(ERROR, "%s referential integrity violation - " @@ -1848,7 +1855,8 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) char upd_nulls[RI_MAX_NUMKEYS + 1]; bool isnull; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -2012,15 +2020,13 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) /* * Now check for existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); - - SetUserId(RelationGetForm(pk_rel)->relowner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(RelationGetForm(pk_rel)->relowner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_processed > 0) elog(ERROR, "%s referential integrity violation - " @@ -2073,7 +2079,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) char upd_nulls[RI_MAX_NUMKEYS + 1]; bool isnull; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -2234,13 +2241,13 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) /* * Now update the existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_del()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()"); @@ -2289,7 +2296,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) int i; int match_type; bool use_cached_query; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -2495,13 +2503,13 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) /* * Now update the existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_upd()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()"); @@ -2547,7 +2555,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) char upd_nulls[RI_MAX_NUMKEYS + 1]; bool isnull; int i; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -2739,13 +2748,13 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) /* * Now update the existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_del()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()"); @@ -2803,7 +2812,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) bool isnull; int i; int match_type; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; Oid fk_owner; ReferentialIntegritySnapshotOverride = true; @@ -3018,13 +3028,13 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) /* * Now update the existing references */ - save_uid = GetUserId(); - SetUserId(fk_owner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fk_owner, true); if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_upd()"); - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()"); @@ -3171,7 +3181,8 @@ ri_PlanCheck(char *querystr, int nargs, Oid *argtypes, { void *qplan; Relation query_rel; - Oid save_uid; + Oid save_userid; + bool save_secdefcxt; /* * The query is always run against the FK table except @@ -3185,14 +3196,14 @@ ri_PlanCheck(char *querystr, int nargs, Oid *argtypes, query_rel = fk_rel; /* Switch to proper UID to perform check as */ - save_uid = GetUserId(); - SetUserId(RelationGetForm(query_rel)->relowner); + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); /* Create the plan */ qplan = SPI_prepare(querystr, nargs, argtypes); /* Restore UID */ - SetUserId(save_uid); + SetUserIdAndContext(save_userid, save_secdefcxt); /* Save the plan if requested */ if (cache_plan) diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 04490562e6..7508b104fe 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -651,6 +651,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) FmgrInfo *save_flinfo; struct fmgr_security_definer_cache *fcache; Oid save_userid; + bool save_secdefcxt; HeapTuple tuple; if (!fcinfo->flinfo->fn_extra) @@ -673,16 +674,18 @@ fmgr_security_definer(PG_FUNCTION_ARGS) else fcache = fcinfo->flinfo->fn_extra; + GetUserIdAndContext(&save_userid, &save_secdefcxt); + SetUserIdAndContext(fcache->userid, true); + save_flinfo = fcinfo->flinfo; fcinfo->flinfo = &fcache->flinfo; - save_userid = GetUserId(); - SetUserId(fcache->userid); result = FunctionCallInvoke(fcinfo); - SetUserId(save_userid); fcinfo->flinfo = save_flinfo; + SetUserIdAndContext(save_userid, save_secdefcxt); + return result; } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 7f7a18814f..1735243bff 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -483,6 +483,9 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) * 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. + * + * SecurityDefinerContext is TRUE if we are within a SECURITY DEFINER function + * or another context that temporarily changes CurrentUserId. * ---------------------------------------------------------------- */ static Oid AuthenticatedUserId = InvalidOid; @@ -491,8 +494,13 @@ static Oid CurrentUserId = InvalidOid; static bool AuthenticatedUserIsSuperuser = false; +static bool SecurityDefinerContext = false; + + /* - * This function is relevant for all privilege checks. + * GetUserId - get the current effective user ID. + * + * Note: there's no SetUserId() anymore; use SetUserIdAndContext(). */ Oid GetUserId(void) @@ -502,14 +510,6 @@ GetUserId(void) } -void -SetUserId(Oid newid) -{ - AssertArg(OidIsValid(newid)); - CurrentUserId = newid; -} - - /* * This value is only relevant for informational purposes. */ @@ -521,17 +521,57 @@ GetSessionUserId(void) } -void +static void SetSessionUserId(Oid newid) { + AssertState(!SecurityDefinerContext); AssertArg(OidIsValid(newid)); SessionUserId = newid; - /* Current user defaults to session user. */ - if (!OidIsValid(CurrentUserId)) - CurrentUserId = newid; + CurrentUserId = newid; } +/* + * GetUserIdAndContext/SetUserIdAndContext - get/set the current user ID + * and the SecurityDefinerContext flag. + * + * Unlike GetUserId, GetUserIdAndContext does *not* Assert that the current + * value of CurrentUserId is valid; nor does SetUserIdAndContext require + * the new value to be valid. In fact, these routines had better not + * ever throw any kind of error. This is because they are used by + * StartTransaction and AbortTransaction to save/restore the settings, + * and during the first transaction within a backend, the value to be saved + * and perhaps restored is indeed invalid. We have to be able to get + * through AbortTransaction without asserting in case InitPostgres fails. + */ +void +GetUserIdAndContext(Oid *userid, bool *sec_def_context) +{ + *userid = CurrentUserId; + *sec_def_context = SecurityDefinerContext; +} + +void +SetUserIdAndContext(Oid userid, bool sec_def_context) +{ + CurrentUserId = userid; + SecurityDefinerContext = sec_def_context; +} + + +/* + * InSecurityDefinerContext - are we inside a SECURITY DEFINER context? + */ +bool +InSecurityDefinerContext(void) +{ + return SecurityDefinerContext; +} + + +/* + * Initialize user identity during normal backend startup + */ void InitializeSessionUserId(const char *username) { @@ -616,7 +656,6 @@ SetSessionAuthorization(Oid userid) elog(ERROR, "permission denied"); SetSessionUserId(userid); - SetUserId(userid); } diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index be081e1d73..614b4c9c9c 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -203,9 +203,10 @@ extern void SetDatabasePath(const char *path); extern char *GetUserNameFromId(Oid userid); extern Oid GetUserId(void); -extern void SetUserId(Oid userid); extern Oid GetSessionUserId(void); -extern void SetSessionUserId(Oid userid); +extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context); +extern void SetUserIdAndContext(Oid userid, bool sec_def_context); +extern bool InSecurityDefinerContext(void); extern void InitializeSessionUserId(const char *username); extern void InitializeSessionUserIdStandalone(void); extern void SetSessionAuthorization(Oid userid);