diff --git a/doc/src/sgml/ref/set_role.sgml b/doc/src/sgml/ref/set_role.sgml
index 96be6e9fbf7..54fd03ff2a6 100644
--- a/doc/src/sgml/ref/set_role.sgml
+++ b/doc/src/sgml/ref/set_role.sgml
@@ -1,5 +1,5 @@
@@ -31,7 +31,7 @@ RESET ROLE
This command sets the current user
- identifier of the current SQL-session context to be rolename. The role name may be
written as either an identifier or a string literal.
After SET ROLE>, permissions checking for SQL commands
@@ -89,6 +89,11 @@ RESET ROLE
roles with SET ROLE> does not change the set of roles
allowed to a later SET ROLE>.
+
+
+ SET ROLE> cannot be used within a
+ SECURITY DEFINER> function.
+
diff --git a/doc/src/sgml/ref/set_session_auth.sgml b/doc/src/sgml/ref/set_session_auth.sgml
index dc64a6c24ff..47b0a233961 100644
--- a/doc/src/sgml/ref/set_session_auth.sgml
+++ b/doc/src/sgml/ref/set_session_auth.sgml
@@ -1,4 +1,4 @@
-
+
SET SESSION AUTHORIZATION
@@ -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 username. The user name may be
written as either an identifier or a string literal. Using this
command, it is possible, for example, to temporarily become an
@@ -38,7 +38,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; it can also be changed by
.
The current user identifier is relevant for permission checking.
@@ -64,6 +64,15 @@ RESET SESSION AUTHORIZATION
+
+ Notes
+
+
+ SET SESSION AUTHORIZATION> cannot be used within a
+ SECURITY DEFINER> function.
+
+
+
Examples
diff --git a/doc/src/sgml/ref/show.sgml b/doc/src/sgml/ref/show.sgml
index f43a0a50bd8..56ccd09c9a3 100644
--- a/doc/src/sgml/ref/show.sgml
+++ b/doc/src/sgml/ref/show.sgml
@@ -1,5 +1,5 @@
@@ -104,8 +104,7 @@ SHOW ALL
IS_SUPERUSER
- True if the current session authorization identifier has
- superuser privileges.
+ True if the current role has superuser privileges.
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index f0442f5f9fa..e000547b327 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.229.2.2 2007/05/30 21:01:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.229.2.3 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -118,7 +118,8 @@ typedef struct TransactionStateData
MemoryContext curTransactionContext; /* my xact-lifetime context */
ResourceOwner curTransactionOwner; /* my query resources */
List *childXids; /* subcommitted child XIDs */
- Oid currentUser; /* subxact start current_user */
+ Oid prevUser; /* previous CurrentUserId setting */
+ bool prevSecDefCxt; /* previous SecurityDefinerContext setting */
bool prevXactReadOnly; /* entry-time xact r/o state */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
@@ -142,7 +143,8 @@ static TransactionStateData TopTransactionStateData = {
NULL, /* cur transaction context */
NULL, /* cur transaction resource owner */
NIL, /* subcommitted child Xids */
- 0, /* entry-time current userid */
+ InvalidOid, /* previous CurrentUserId setting */
+ false, /* previous SecurityDefinerContext setting */
false, /* entry-time xact r/o state */
NULL /* link to parent state block */
};
@@ -1419,18 +1421,14 @@ StartTransaction(void)
/*
* initialize current transaction state fields
+ *
+ * note: prevXactReadOnly is not used at the outermost level
*/
s->nestingLevel = 1;
s->childXids = NIL;
-
- /*
- * You might expect to see "s->currentUser = GetUserId();" here, but you
- * won't because it doesn't work during startup; the userid isn't set yet
- * during a backend's first transaction start. We only use the
- * currentUser field in sub-transaction state structs.
- *
- * prevXactReadOnly is also valid only in sub-transactions.
- */
+ GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
+ /* SecurityDefinerContext should never be set outside a transaction */
+ Assert(!s->prevSecDefCxt);
/*
* initialize other subsystems for new transaction
@@ -1916,17 +1914,16 @@ AbortTransaction(void)
s->state = TRANS_ABORT;
/*
- * Reset user id which might have been changed transiently. We cannot use
- * s->currentUser, since it may not be set yet; instead rely on internal
- * state of miscinit.c.
+ * 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 here
- * because that can only be changed via GUC, and GUC will take care of
- * rolling it back if need be. However, an error within a SECURITY
- * DEFINER function could send control here with the wrong current
- * userid.)
+ * (Note: it is not necessary to restore session authorization or role
+ * settings here because those can only be changed via GUC, and GUC will
+ * take care of rolling them back if need be.)
*/
- AtAbort_UserId();
+ SetUserIdAndContext(s->prevUser, s->prevSecDefCxt);
/*
* do abort processing
@@ -3771,6 +3768,12 @@ AbortSubTransaction(void)
s->state = TRANS_ABORT;
+ /*
+ * Reset user ID which might have been changed transiently. (See notes
+ * in AbortTransaction.)
+ */
+ SetUserIdAndContext(s->prevUser, s->prevSecDefCxt);
+
/*
* We can skip all this stuff if the subxact failed before creating a
* ResourceOwner...
@@ -3823,20 +3826,6 @@ AbortSubTransaction(void)
AtEOSubXact_HashTables(false, s->nestingLevel);
}
- /*
- * Reset user id which might have been changed transiently. Here we want
- * to restore to the userid that was current at subxact entry. (As in
- * AbortTransaction, we need not worry about the session userid.)
- *
- * Must do this after AtEOXact_GUC to handle the case where we entered the
- * subxact inside a SECURITY DEFINER function (hence current and session
- * userids were different) and then session auth was changed inside the
- * subxact. GUC will reset both current and session userids to the
- * entry-time session userid. This is right in every other scenario so it
- * seems simplest to let GUC do that and fix it here.
- */
- SetUserId(s->currentUser);
-
/*
* Restore the upper transaction's read-only state, too. This should be
* redundant with GUC's cleanup but we may as well do it for consistency
@@ -3891,13 +3880,6 @@ PushTransaction(void)
{
TransactionState p = CurrentTransactionState;
TransactionState s;
- Oid currentUser;
-
- /*
- * At present, GetUserId cannot fail, but let's not assume that. Get the
- * ID before entering the critical code sequence.
- */
- currentUser = GetUserId();
/*
* We keep subtransaction state nodes in TopTransactionContext.
@@ -3930,7 +3912,7 @@ PushTransaction(void)
s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT;
s->blockState = TBLOCK_SUBBEGIN;
- s->currentUser = currentUser;
+ GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
s->prevXactReadOnly = XactReadOnly;
CurrentTransactionState = s;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index ad934d57ea9..dcc4aa0a98c 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.274.2.1 2007/11/08 23:23:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.274.2.2 2008/01/03 21:23:45 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1291,6 +1291,8 @@ index_build(Relation heapRelation,
{
RegProcedure procedure;
IndexBuildResult *stats;
+ Oid save_userid;
+ bool save_secdefcxt;
/*
* sanity checks
@@ -1301,6 +1303,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
*/
@@ -1311,6 +1320,9 @@ index_build(Relation heapRelation,
PointerGetDatum(indexInfo)));
Assert(PointerIsValid(stats));
+ /* Restore userid */
+ SetUserIdAndContext(save_userid, save_secdefcxt);
+
/*
* Update heap and index pg_class rows
*/
@@ -1638,6 +1650,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
IndexInfo *indexInfo;
IndexVacuumInfo ivinfo;
v_i_state state;
+ Oid save_userid;
+ bool save_secdefcxt;
/* Open and lock the parent heap relation */
heapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
@@ -1654,6 +1668,13 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
/* mark build is concurrent just for consistency */
indexInfo->ii_Concurrent = true;
+ /*
+ * 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);
+
/*
* Scan the index and gather up all the TIDs into a tuplesort object.
*/
@@ -1690,6 +1711,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
"validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples",
state.htups, state.itups, state.tups_inserted);
+ /* Restore userid */
+ SetUserIdAndContext(save_userid, save_secdefcxt);
+
/* Close rels, but keep locks */
index_close(indexRelation, NoLock);
heap_close(heapRelation, NoLock);
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 95410258d34..233345c1c47 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.101 2006/11/05 22:42:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.101.2.1 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -109,6 +109,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
double totalrows,
totaldeadrows;
HeapTuple *rows;
+ Oid save_userid;
+ bool save_secdefcxt;
if (vacstmt->verbose)
elevel = INFO;
@@ -195,6 +197,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
get_namespace_name(RelationGetNamespace(onerel)),
RelationGetRelationName(onerel))));
+ /*
+ * Switch to the table owner's userid, so that any index functions are
+ * run as that user.
+ */
+ GetUserIdAndContext(&save_userid, &save_secdefcxt);
+ SetUserIdAndContext(onerel->rd_rel->relowner, true);
+
/*
* Determine which columns to analyze
*
@@ -319,9 +328,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
onerel->rd_rel->relisshared,
0, 0);
- vac_close_indexes(nindexes, Irel, AccessShareLock);
- relation_close(onerel, ShareUpdateExclusiveLock);
- return;
+ goto cleanup;
}
/*
@@ -441,6 +448,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
totalrows, totaldeadrows);
}
+ /* We skip to here if there were no analyzable columns */
+cleanup:
+
/* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock);
@@ -451,6 +461,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
* expose us to concurrent-update failures in update_attstats.)
*/
relation_close(onerel, NoLock);
+
+ /* Restore userid */
+ SetUserIdAndContext(save_userid, save_secdefcxt);
}
/*
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 36804e53c99..881f45fa2e8 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.41 2006/07/13 16:49:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.41.2.1 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,9 +47,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
ListCell *parsetree_item;
Oid owner_uid;
Oid saved_uid;
+ bool saved_secdefcxt;
AclResult aclresult;
- saved_uid = GetUserId();
+ GetUserIdAndContext(&saved_uid, &saved_secdefcxt);
/*
* Who is supposed to own the new schema?
@@ -85,11 +86,11 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
* temporarily set the current user so that the object(s) will be created
* with the correct ownership.
*
- * (The setting will revert to session user on error or at the end of this
- * routine.)
+ * (The setting will be restored at the end of this routine, or in case
+ * of error, transaction abort will clean things up.)
*/
if (saved_uid != owner_uid)
- SetUserId(owner_uid);
+ SetUserIdAndContext(owner_uid, true);
/* Create the schema's namespace */
namespaceId = NamespaceCreate(schemaName, owner_uid);
@@ -141,7 +142,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
PopSpecialNamespace(namespaceId);
/* Reset current user */
- SetUserId(saved_uid);
+ SetUserIdAndContext(saved_uid, saved_secdefcxt);
}
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 304cc325d38..e3f9367f028 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.342.2.3 2007/06/14 13:54:28 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.342.2.4 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -959,6 +959,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
+ Oid save_userid;
+ bool save_secdefcxt;
/* Begin a transaction for vacuuming this relation */
StartTransactionCommand();
@@ -1086,6 +1088,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);
+
/*
* Tell the cache replacement strategy that vacuum is causing all
* following IO
@@ -1102,6 +1112,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
StrategyHintVacuum(false);
+ /* 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 c910f6376fd..730a3ac4289 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.119 2006/10/04 00:29:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.119.2.1 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -620,6 +620,22 @@ assign_session_authorization(const char *value, bool doit, GucSource source)
/* not a saved ID, so look it up */
HeapTuple roleTup;
+ 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 (source >= PGC_S_INTERACTIVE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set session authorization within security-definer function")));
+ return NULL;
+ }
+
if (!IsTransactionState())
{
/*
@@ -727,6 +743,25 @@ assign_role(const char *value, bool doit, GucSource source)
}
}
+ if (roleid == InvalidOid && InSecurityDefinerContext())
+ {
+ /*
+ * Disallow SET ROLE 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 arranged so
+ * that restoring a previously saved setting isn't prevented.
+ *
+ * XXX it would be nice to allow this case in future, with the
+ * behavior being that the SET ROLE's effects end when the security
+ * definer context is exited.
+ */
+ if (source >= PGC_S_INTERACTIVE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set role within security-definer function")));
+ return NULL;
+ }
+
if (roleid == InvalidOid &&
strcmp(actual_rolename, "none") != 0)
{
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index af2955183cd..8dae05d4046 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -17,7 +17,7 @@
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.89.2.1 2007/08/15 19:15:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.89.2.2 2008/01/03 21:23:45 tgl Exp $
*
* ----------
*/
@@ -3008,7 +3008,8 @@ ri_PlanCheck(const 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 when this is an
@@ -3022,8 +3023,8 @@ ri_PlanCheck(const 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);
@@ -3032,7 +3033,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
/* Restore UID */
- SetUserId(save_uid);
+ SetUserIdAndContext(save_userid, save_secdefcxt);
/* Save the plan if requested */
if (cache_plan)
@@ -3061,7 +3062,8 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
Snapshot crosscheck_snapshot;
int limit;
int spi_result;
- Oid save_uid;
+ Oid save_userid;
+ bool save_secdefcxt;
Datum vals[RI_MAX_NUMKEYS * 2];
char nulls[RI_MAX_NUMKEYS * 2];
@@ -3139,8 +3141,8 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
/* 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);
/* Finally we can run the query. */
spi_result = SPI_execute_snapshot(qplan,
@@ -3149,7 +3151,7 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
false, false, limit);
/* Restore UID */
- SetUserId(save_uid);
+ SetUserIdAndContext(save_userid, save_secdefcxt);
/* Check result */
if (spi_result < 0)
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index e493aba7877..a7d6765862f 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.102.2.1 2007/07/31 15:49:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.102.2.2 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -784,6 +784,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
FmgrInfo *save_flinfo;
struct fmgr_security_definer_cache *volatile fcache;
Oid save_userid;
+ bool save_secdefcxt;
HeapTuple tuple;
if (!fcinfo->flinfo->fn_extra)
@@ -809,26 +810,32 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
else
fcache = fcinfo->flinfo->fn_extra;
+ GetUserIdAndContext(&save_userid, &save_secdefcxt);
+ SetUserIdAndContext(fcache->userid, true);
+
+ /*
+ * We don't need to restore the userid settings on error, because the
+ * ensuing xact or subxact abort will do that. The PG_TRY block is only
+ * needed to clean up the flinfo link.
+ */
save_flinfo = fcinfo->flinfo;
- save_userid = GetUserId();
PG_TRY();
{
fcinfo->flinfo = &fcache->flinfo;
- SetUserId(fcache->userid);
result = FunctionCallInvoke(fcinfo);
}
PG_CATCH();
{
fcinfo->flinfo = save_flinfo;
- SetUserId(save_userid);
PG_RE_THROW();
}
PG_END_TRY();
fcinfo->flinfo = save_flinfo;
- SetUserId(save_userid);
+
+ SetUserIdAndContext(save_userid, save_secdefcxt);
return result;
}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 5ddb0b2e44c..fbd31df49d1 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.159 2006/10/04 00:30:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.159.2.1 2008/01/03 21:23:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -264,13 +264,15 @@ make_absolute_path(const char *path)
* 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 AtAbort_UserId knows what to
- * reset CurrentUserId to.
+ * member of. (XXX rename to something like CurrentRoleId?)
*
* 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.
+ *
+ * SecurityDefinerContext is TRUE if we are within a SECURITY DEFINER function
+ * or another context that temporarily changes CurrentUserId.
* ----------------------------------------------------------------
*/
static Oid AuthenticatedUserId = InvalidOid;
@@ -282,12 +284,16 @@ static Oid CurrentUserId = InvalidOid;
static bool AuthenticatedUserIsSuperuser = false;
static bool SessionUserIsSuperuser = false;
+static bool SecurityDefinerContext = false;
+
/* We also remember if a SET ROLE is currently active */
static bool SetRoleIsActive = false;
/*
- * GetUserId/SetUserId - get/set the current effective user ID.
+ * GetUserId - get the current effective user ID.
+ *
+ * Note: there's no SetUserId() anymore; use SetUserIdAndContext().
*/
Oid
GetUserId(void)
@@ -297,14 +303,6 @@ GetUserId(void)
}
-void
-SetUserId(Oid userid)
-{
- AssertArg(OidIsValid(userid));
- CurrentUserId = userid;
-}
-
-
/*
* GetOuterUserId/SetOuterUserId - get/set the outer-level user ID.
*/
@@ -319,6 +317,7 @@ GetOuterUserId(void)
static void
SetOuterUserId(Oid userid)
{
+ AssertState(!SecurityDefinerContext);
AssertArg(OidIsValid(userid));
OuterUserId = userid;
@@ -341,6 +340,7 @@ GetSessionUserId(void)
static void
SetSessionUserId(Oid userid, bool is_superuser)
{
+ AssertState(!SecurityDefinerContext);
AssertArg(OidIsValid(userid));
SessionUserId = userid;
SessionUserIsSuperuser = is_superuser;
@@ -352,6 +352,44 @@ SetSessionUserId(Oid userid, bool is_superuser)
}
+/*
+ * 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
*/
@@ -474,21 +512,6 @@ InitializeSessionUserIdStandalone(void)
}
-/*
- * Reset effective userid during AbortTransaction
- *
- * This is essentially SetUserId(GetOuterUserId()), but without the Asserts.
- * The reason is that if a backend's InitPostgres transaction fails (eg,
- * because an invalid user name was given), we have to be able to get through
- * AbortTransaction without asserting.
- */
-void
-AtAbort_UserId(void)
-{
- CurrentUserId = OuterUserId;
-}
-
-
/*
* Change session auth ID while running
*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f21717e7b69..6d49072a993 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.190 2006/10/19 18:32:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.190.2.1 2008/01/03 21:23:45 tgl Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
@@ -229,12 +229,13 @@ extern void SetDatabasePath(const char *path);
extern char *GetUserNameFromId(Oid roleid);
extern Oid GetUserId(void);
-extern void SetUserId(Oid userid);
extern Oid GetOuterUserId(void);
extern Oid GetSessionUserId(void);
+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 *rolename);
extern void InitializeSessionUserIdStandalone(void);
-extern void AtAbort_UserId(void);
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
extern Oid GetCurrentRoleId(void);
extern void SetCurrentRoleId(Oid roleid, bool is_superuser);