1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-14 02:22:38 +03:00

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
This commit is contained in:
Tom Lane
2008-01-03 21:25:00 +00:00
parent 69abe8294b
commit 108b19d860
11 changed files with 184 additions and 93 deletions

View File

@@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.12 2003/11/29 19:51:39 pgsql Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.12.4.1 2008/01/03 21:25:00 tgl Exp $ -->
<refentry id="SQL-SET-SESSION-AUTHORIZATION"> <refentry id="SQL-SET-SESSION-AUTHORIZATION">
<refmeta> <refmeta>
<refentrytitle id="sql-set-session-authorization-title">SET SESSION AUTHORIZATION</refentrytitle> <refentrytitle id="sql-set-session-authorization-title">SET SESSION AUTHORIZATION</refentrytitle>
@@ -27,7 +27,7 @@ RESET SESSION AUTHORIZATION
<para> <para>
This command sets the session user identifier and the current user This command sets the session user identifier and the current user
identifier of the current SQL-session context to be <replaceable identifier of the current SQL session to be <replaceable
class="parameter">username</replaceable>. The user name may be class="parameter">username</replaceable>. The user name may be
written as either an identifier or a string literal. Using this written as either an identifier or a string literal. Using this
command, it is possible, for example, to temporarily become an 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 The session user identifier is initially set to be the (possibly
authenticated) user name provided by the client. The current user authenticated) user name provided by the client. The current user
identifier is normally equal to the session user identifier, but identifier is normally equal to the session user identifier, but
may change temporarily in the context of <quote>setuid</quote> might change temporarily in the context of <literal>SECURITY DEFINER</>
functions and similar mechanisms. The current user identifier is functions and similar mechanisms. The current user identifier is
relevant for permission checking. relevant for permission checking.
</para> </para>
@@ -63,6 +63,15 @@ RESET SESSION AUTHORIZATION
</para> </para>
</refsect1> </refsect1>
<refsect1>
<title>Notes</title>
<para>
<command>SET SESSION AUTHORIZATION</> cannot be used within a
<literal>SECURITY DEFINER</> function.
</para>
</refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Examples</title>

View File

@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.195.4.3 2007/05/30 21:02:02 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.195.4.4 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -117,7 +117,8 @@ typedef struct TransactionStateData
* context */ * context */
ResourceOwner curTransactionOwner; /* my query resources */ ResourceOwner curTransactionOwner; /* my query resources */
List *childXids; /* subcommitted child XIDs */ List *childXids; /* subcommitted child XIDs */
AclId currentUser; /* subxact start current_user */ AclId prevUser; /* previous CurrentUserId setting */
bool prevSecDefCxt; /* previous SecurityDefinerContext setting */
bool prevXactReadOnly; /* entry-time xact r/o state */ bool prevXactReadOnly; /* entry-time xact r/o state */
struct TransactionStateData *parent; /* back link to parent */ struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData; } TransactionStateData;
@@ -149,7 +150,8 @@ static TransactionStateData TopTransactionStateData = {
NULL, /* cur transaction context */ NULL, /* cur transaction context */
NULL, /* cur transaction resource owner */ NULL, /* cur transaction resource owner */
NIL, /* subcommitted child Xids */ NIL, /* subcommitted child Xids */
0, /* entry-time current userid */ 0, /* previous CurrentUserId setting */
false, /* previous SecurityDefinerContext setting */
false, /* entry-time xact r/o state */ false, /* entry-time xact r/o state */
NULL /* link to parent state block */ NULL /* link to parent state block */
}; };
@@ -1390,18 +1392,14 @@ StartTransaction(void)
/* /*
* initialize current transaction state fields * initialize current transaction state fields
*
* note: prevXactReadOnly is not used at the outermost level
*/ */
s->nestingLevel = 1; s->nestingLevel = 1;
s->childXids = NIL; s->childXids = NIL;
GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
/* /* SecurityDefinerContext should never be set outside a transaction */
* You might expect to see "s->currentUser = GetUserId();" here, but Assert(!s->prevSecDefCxt);
* 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.
*/
/* /*
* initialize other subsystems for new transaction * initialize other subsystems for new transaction
@@ -1652,17 +1650,16 @@ AbortTransaction(void)
AtAbort_ResourceOwner(); AtAbort_ResourceOwner();
/* /*
* Reset user id which might have been changed transiently. We cannot * Reset user ID which might have been changed transiently. We need this
* use s->currentUser, but must get the session userid from * to clean up in case control escaped out of a SECURITY DEFINER function
* miscinit.c. * 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 * (Note: it is not necessary to restore session authorization
* because that can only be changed via GUC, and GUC will take care of * setting here because that can only be changed via GUC, and GUC will
* rolling it back if need be. However, an error within a SECURITY * take care of rolling it back if need be.)
* DEFINER function could send control here with the wrong current
* userid.)
*/ */
SetUserId(GetSessionUserId()); SetUserIdAndContext(s->prevUser, s->prevSecDefCxt);
/* /*
* do abort processing * do abort processing
@@ -3394,6 +3391,12 @@ AbortSubTransaction(void)
AtSubAbort_Memory(); AtSubAbort_Memory();
AtSubAbort_ResourceOwner(); AtSubAbort_ResourceOwner();
/*
* 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 * We can skip all this stuff if the subxact failed before creating
* a ResourceOwner... * a ResourceOwner...
@@ -3446,22 +3449,6 @@ AbortSubTransaction(void)
AtEOSubXact_HashTables(false, s->nestingLevel); 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 * 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 * be redundant with GUC's cleanup but we may as well do it for
@@ -3516,13 +3503,6 @@ PushTransaction(void)
{ {
TransactionState p = CurrentTransactionState; TransactionState p = CurrentTransactionState;
TransactionState s; TransactionState s;
AclId 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. * We keep subtransaction state nodes in TopTransactionContext.
@@ -3553,7 +3533,7 @@ PushTransaction(void)
s->savepointLevel = p->savepointLevel; s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT; s->state = TRANS_DEFAULT;
s->blockState = TBLOCK_SUBBEGIN; s->blockState = TBLOCK_SUBBEGIN;
s->currentUser = currentUser; GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
s->prevXactReadOnly = XactReadOnly; s->prevXactReadOnly = XactReadOnly;
CurrentTransactionState = s; CurrentTransactionState = s;

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.244.4.2 2007/11/08 23:23:14 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.244.4.3 2008/01/03 21:25:00 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
@@ -1323,6 +1323,8 @@ index_build(Relation heapRelation,
IndexInfo *indexInfo) IndexInfo *indexInfo)
{ {
RegProcedure procedure; RegProcedure procedure;
AclId save_userid;
bool save_secdefcxt;
/* /*
* sanity checks * sanity checks
@@ -1333,6 +1335,13 @@ index_build(Relation heapRelation,
procedure = indexRelation->rd_am->ambuild; procedure = indexRelation->rd_am->ambuild;
Assert(RegProcedureIsValid(procedure)); 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 * Call the access method's build procedure
*/ */
@@ -1340,6 +1349,9 @@ index_build(Relation heapRelation,
PointerGetDatum(heapRelation), PointerGetDatum(heapRelation),
PointerGetDatum(indexRelation), PointerGetDatum(indexRelation),
PointerGetDatum(indexInfo)); PointerGetDatum(indexInfo));
/* Restore userid */
SetUserIdAndContext(save_userid, save_secdefcxt);
} }

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.80 2004/12/31 21:59:41 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.80.4.1 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -110,6 +110,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
numrows; numrows;
double totalrows; double totalrows;
HeapTuple *rows; HeapTuple *rows;
AclId save_userid;
bool save_secdefcxt;
if (vacstmt->verbose) if (vacstmt->verbose)
elevel = INFO; elevel = INFO;
@@ -199,6 +201,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
get_namespace_name(RelationGetNamespace(onerel)), get_namespace_name(RelationGetNamespace(onerel)),
RelationGetRelationName(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 * Determine which columns to analyze
* *
@@ -309,11 +318,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
* Quit if no analyzable columns * Quit if no analyzable columns
*/ */
if (attr_cnt <= 0 && !analyzableindex) if (attr_cnt <= 0 && !analyzableindex)
{ goto cleanup;
vac_close_indexes(nindexes, Irel, AccessShareLock);
relation_close(onerel, AccessShareLock);
return;
}
/* /*
* Determine how many rows we need to sample, using the worst case * Determine how many rows we need to sample, using the worst case
@@ -426,6 +431,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
} }
} }
/* We skip to here if there were no analyzable columns */
cleanup:
/* Done with indexes */ /* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock); vac_close_indexes(nindexes, Irel, NoLock);
@@ -435,6 +443,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
* entries we made in pg_statistic.) * entries we made in pg_statistic.)
*/ */
relation_close(onerel, NoLock); relation_close(onerel, NoLock);
/* Restore userid */
SetUserIdAndContext(save_userid, save_secdefcxt);
} }
/* /*

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.27 2004/12/31 21:59:41 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.27.4.1 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -46,9 +46,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
const char *owner_name; const char *owner_name;
AclId owner_userid; AclId owner_userid;
AclId saved_userid; AclId saved_userid;
bool saved_secdefcxt;
AclResult aclresult; AclResult aclresult;
saved_userid = GetUserId(); GetUserIdAndContext(&saved_userid, &saved_secdefcxt);
/* /*
* Figure out user identities. * Figure out user identities.
@@ -71,7 +72,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
* (This will revert to session user on error or at the end of * (This will revert to session user on error or at the end of
* this routine.) * this routine.)
*/ */
SetUserId(owner_userid); SetUserIdAndContext(owner_userid, true);
} }
else else
{ {
@@ -151,7 +152,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
PopSpecialNamespace(namespaceId); PopSpecialNamespace(namespaceId);
/* Reset current user */ /* Reset current user */
SetUserId(saved_userid); SetUserIdAndContext(saved_userid, saved_secdefcxt);
} }

View File

@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.299.4.2 2007/03/14 18:49:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.299.4.3 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -883,6 +883,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
LockRelId onerelid; LockRelId onerelid;
Oid toast_relid; Oid toast_relid;
bool result; bool result;
AclId save_userid;
bool save_secdefcxt;
/* Begin a transaction for vacuuming this relation */ /* Begin a transaction for vacuuming this relation */
StartTransactionCommand(); StartTransactionCommand();
@@ -997,6 +999,14 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
*/ */
toast_relid = onerel->rd_rel->reltoastrelid; 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 * Do the actual work --- either FULL or "lazy" vacuum
*/ */
@@ -1007,6 +1017,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
result = true; /* did the vacuum */ result = true; /* did the vacuum */
/* Restore userid */
SetUserIdAndContext(save_userid, save_secdefcxt);
/* all done with this class, but hold lock until commit */ /* all done with this class, but hold lock until commit */
relation_close(onerel, NoLock); relation_close(onerel, NoLock);

View File

@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.105.4.3 2006/02/12 22:33:14 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.105.4.4 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -656,6 +656,22 @@ assign_session_authorization(const char *value, bool doit, GucSource source)
/* not a saved ID, so look it up */ /* not a saved ID, so look it up */
HeapTuple userTup; 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 (source >= PGC_S_INTERACTIVE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set session authorization within security-definer function")));
return NULL;
}
if (!IsTransactionState()) if (!IsTransactionState())
{ {
/* /*

View File

@@ -17,7 +17,7 @@
* *
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.76.4.2 2007/08/15 19:16:12 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.76.4.3 2008/01/03 21:25:00 tgl Exp $
* *
* ---------- * ----------
*/ */
@@ -3000,7 +3000,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
{ {
void *qplan; void *qplan;
Relation query_rel; Relation query_rel;
AclId save_uid; AclId save_userid;
bool save_secdefcxt;
/* /*
* The query is always run against the FK table except when this is an * The query is always run against the FK table except when this is an
@@ -3014,8 +3015,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
query_rel = fk_rel; query_rel = fk_rel;
/* Switch to proper UID to perform check as */ /* Switch to proper UID to perform check as */
save_uid = GetUserId(); GetUserIdAndContext(&save_userid, &save_secdefcxt);
SetUserId(RelationGetForm(query_rel)->relowner); SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);
/* Create the plan */ /* Create the plan */
qplan = SPI_prepare(querystr, nargs, argtypes); qplan = SPI_prepare(querystr, nargs, argtypes);
@@ -3024,7 +3025,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr); elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
/* Restore UID */ /* Restore UID */
SetUserId(save_uid); SetUserIdAndContext(save_userid, save_secdefcxt);
/* Save the plan if requested */ /* Save the plan if requested */
if (cache_plan) if (cache_plan)
@@ -3053,7 +3054,8 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
Snapshot crosscheck_snapshot; Snapshot crosscheck_snapshot;
int limit; int limit;
int spi_result; int spi_result;
AclId save_uid; AclId save_userid;
bool save_secdefcxt;
Datum vals[RI_MAX_NUMKEYS * 2]; Datum vals[RI_MAX_NUMKEYS * 2];
char nulls[RI_MAX_NUMKEYS * 2]; char nulls[RI_MAX_NUMKEYS * 2];
@@ -3132,8 +3134,8 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0; limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
/* Switch to proper UID to perform check as */ /* Switch to proper UID to perform check as */
save_uid = GetUserId(); GetUserIdAndContext(&save_userid, &save_secdefcxt);
SetUserId(RelationGetForm(query_rel)->relowner); SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);
/* Finally we can run the query. */ /* Finally we can run the query. */
spi_result = SPI_execute_snapshot(qplan, spi_result = SPI_execute_snapshot(qplan,
@@ -3142,7 +3144,7 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
false, false, limit); false, false, limit);
/* Restore UID */ /* Restore UID */
SetUserId(save_uid); SetUserIdAndContext(save_userid, save_secdefcxt);
/* Check result */ /* Check result */
if (spi_result < 0) if (spi_result < 0)

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.88.4.2 2007/07/31 15:50:07 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.88.4.3 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -791,6 +791,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
FmgrInfo *save_flinfo; FmgrInfo *save_flinfo;
struct fmgr_security_definer_cache * volatile fcache; struct fmgr_security_definer_cache * volatile fcache;
AclId save_userid; AclId save_userid;
bool save_secdefcxt;
HeapTuple tuple; HeapTuple tuple;
if (!fcinfo->flinfo->fn_extra) if (!fcinfo->flinfo->fn_extra)
@@ -816,26 +817,32 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
else else
fcache = fcinfo->flinfo->fn_extra; 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_flinfo = fcinfo->flinfo;
save_userid = GetUserId();
PG_TRY(); PG_TRY();
{ {
fcinfo->flinfo = &fcache->flinfo; fcinfo->flinfo = &fcache->flinfo;
SetUserId(fcache->userid);
result = FunctionCallInvoke(fcinfo); result = FunctionCallInvoke(fcinfo);
} }
PG_CATCH(); PG_CATCH();
{ {
fcinfo->flinfo = save_flinfo; fcinfo->flinfo = save_flinfo;
SetUserId(save_userid);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
fcinfo->flinfo = save_flinfo; fcinfo->flinfo = save_flinfo;
SetUserId(save_userid);
SetUserIdAndContext(save_userid, save_secdefcxt);
return result; return result;
} }

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.137.4.1 2005/03/18 03:49:19 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.137.4.2 2008/01/03 21:25:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -260,6 +260,9 @@ make_absolute_path(const char *path)
* are implemented. Conceptually there is a stack, whose bottom * are implemented. Conceptually there is a stack, whose bottom
* is the session user. You are yourself responsible to save and * is the session user. You are yourself responsible to save and
* restore the current user id if you need to change it. * 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 AclId AuthenticatedUserId = 0; static AclId AuthenticatedUserId = 0;
@@ -268,8 +271,13 @@ static AclId CurrentUserId = 0;
static bool AuthenticatedUserIsSuperuser = false; 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().
*/ */
AclId AclId
GetUserId(void) GetUserId(void)
@@ -279,14 +287,6 @@ GetUserId(void)
} }
void
SetUserId(AclId newid)
{
AssertArg(AclIdIsValid(newid));
CurrentUserId = newid;
}
/* /*
* This value is only relevant for informational purposes. * This value is only relevant for informational purposes.
*/ */
@@ -298,17 +298,57 @@ GetSessionUserId(void)
} }
void static void
SetSessionUserId(AclId newid) SetSessionUserId(AclId newid)
{ {
AssertState(!SecurityDefinerContext);
AssertArg(AclIdIsValid(newid)); AssertArg(AclIdIsValid(newid));
SessionUserId = newid; SessionUserId = newid;
/* Current user defaults to session user. */
if (!AclIdIsValid(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(AclId *userid, bool *sec_def_context)
{
*userid = CurrentUserId;
*sec_def_context = SecurityDefinerContext;
}
void
SetUserIdAndContext(AclId 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 void
InitializeSessionUserId(const char *username) InitializeSessionUserId(const char *username)
{ {
@@ -403,7 +443,6 @@ SetSessionAuthorization(AclId userid, bool is_superuser)
errmsg("permission denied to set session authorization"))); errmsg("permission denied to set session authorization")));
SetSessionUserId(userid); SetSessionUserId(userid);
SetUserId(userid);
SetConfigOption("is_superuser", SetConfigOption("is_superuser",
is_superuser ? "on" : "off", is_superuser ? "on" : "off",

View File

@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.174 2004/12/31 22:03:19 pgsql Exp $ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.174.4.1 2008/01/03 21:25:00 tgl Exp $
* *
* NOTES * NOTES
* some of the information in this file should be moved to other files. * some of the information in this file should be moved to other files.
@@ -233,9 +233,10 @@ extern void SetDatabasePath(const char *path);
extern char *GetUserNameFromId(AclId userid); extern char *GetUserNameFromId(AclId userid);
extern AclId GetUserId(void); extern AclId GetUserId(void);
extern void SetUserId(AclId userid);
extern AclId GetSessionUserId(void); extern AclId GetSessionUserId(void);
extern void SetSessionUserId(AclId userid); extern void GetUserIdAndContext(AclId *userid, bool *sec_def_context);
extern void SetUserIdAndContext(AclId userid, bool sec_def_context);
extern bool InSecurityDefinerContext(void);
extern void InitializeSessionUserId(const char *username); extern void InitializeSessionUserId(const char *username);
extern void InitializeSessionUserIdStandalone(void); extern void InitializeSessionUserIdStandalone(void);
extern void SetSessionAuthorization(AclId userid, bool is_superuser); extern void SetSessionAuthorization(AclId userid, bool is_superuser);