]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make standard maintenance operations (including VACUUM, ANALYZE, REINDEX,
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 3 Jan 2008 21:25:58 +0000 (21:25 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 3 Jan 2008 21:25:58 +0000 (21:25 +0000)
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

doc/src/sgml/ref/set_session_auth.sgml
src/backend/access/transam/xact.c
src/backend/catalog/index.c
src/backend/commands/schemacmds.c
src/backend/commands/vacuum.c
src/backend/commands/variable.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/fmgr/fmgr.c
src/backend/utils/init/miscinit.c
src/include/miscadmin.h

index eb9d760f10e1554f7efa317133ab45c95884aae0..ecb7623e55a2787afd228ccf6409879a5348153a 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.7 2002/09/21 18:32:54 petere Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.7.2.1 2008/01/03 21:25:58 tgl Exp $ -->
 <refentry id="SQL-SET-SESSION-AUTHORIZATION">
  <docinfo>
   <date>2001-04-21</date>
@@ -27,7 +27,7 @@ RESET SESSION AUTHORIZATION
 
   <para>
    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
    <parameter>username</parameter>.  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 <quote>setuid</quote>
+   might change temporarily in the context of <literal>SECURITY DEFINER</>
    functions and similar mechanisms.  The current user identifier is
    relevant for permission checking.
   </para>
@@ -65,6 +65,15 @@ RESET SESSION AUTHORIZATION
 
  </refsect1>
 
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   <command>SET SESSION AUTHORIZATION</> cannot be used within a
+   <literal>SECURITY DEFINER</> function.
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Examples</title>
 
index 1c5ffb1192d99fb2db26cfcbd34bd50def118bf9..24a50e5eb8f22ca898502ffd037399e1b2b37ac9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.135.2.4 2007/04/26 23:25:48 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.135.2.5 2008/01/03 21:25:58 tgl Exp $
  *
  * NOTES
  *             Transaction aborts can now occur two ways:
@@ -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
index debc3945eecd4b74c3d7c961fcaaa4595986d4ce..506fd43dcae3c0bb01e4f711d93d35e756bb1009 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.202.2.1 2005/06/25 16:54:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.202.2.2 2008/01/03 21:25:58 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -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);
 }
 
 
index 09165d47bd32979b89a91e6e5a724247d973c5d3..ba62a54223f2de6548434683830e6f8d6c6562cf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.6 2002/09/04 20:31:15 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.6.2.1 2008/01/03 21:25:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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);
 }
 
 
index 31a4be47853dfe34f1de228e8010c2562c8e9201..9befea59e0a1b8166dafff18866f3b5ed1b4b9b0 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.244.2.2 2007/03/14 18:49:32 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.244.2.3 2008/01/03 21:25:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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);
 
index 87f8953ee2663ef169a63684a2b31044b83b8eb5..1a351b62876d18ac4835cf65dc5790343bbb60a4 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.71.2.4 2006/02/12 22:33:46 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.71.2.5 2008/01/03 21:25:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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())
                {
                        /*
index 6f85e6fc4a50bdb1f877c347f162a8335752a6ab..acb17219da848db47e935f60e8550182ff41a774 100644 (file)
@@ -17,7 +17,7 @@
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.43.2.5 2004/10/13 22:22:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.43.2.6 2008/01/03 21:25:58 tgl Exp $
  *
  * ----------
  */
@@ -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)
index 470d4a11dd6ef481e7ade6bf3be2b671835ad75d..5cebe0595b53c955373c5fe5d7437df553a47c10 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.63 2002/10/04 17:19:55 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.63.2.1 2008/01/03 21:25:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;
 }
 
index f41706862fa0ae435fadf71cb61949c5a0be3e68..e772d6a8ec571788926435f239ec97ce3bfb8f70 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.96 2002/09/04 20:31:31 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.96.2.1 2008/01/03 21:25:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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);
 }
 
 
index 1dbd737db3bb4b98cfca45af8e87c181f35b61fd..7f7894b597a3a8ff2d6ededb8bcc207f719c7c83 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: miscadmin.h,v 1.111 2002/10/03 17:07:53 momjian Exp $
+ * $Id: miscadmin.h,v 1.111.2.1 2008/01/03 21:25:58 tgl Exp $
  *
  * NOTES
  *       some of the information in this file should be moved to
@@ -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);