summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorMasahiko Sawada2024-08-05 13:05:30 +0000
committerMasahiko Sawada2024-08-05 13:05:30 +0000
commitfdf218f1d52b2c93145f16b28b8374703ae4ef19 (patch)
tree83ae4de4ae5f92e4f1c0255da88e23948263de13 /src/backend
parent91099bb287ff71c970c72b81e6a190d80a92e760 (diff)
Restrict accesses to non-system views and foreign tables during pg_dump.
When pg_dump retrieves the list of database objects and performs the data dump, there was possibility that objects are replaced with others of the same name, such as views, and access them. This vulnerability could result in code execution with superuser privileges during the pg_dump process. This issue can arise when dumping data of sequences, foreign tables (only 13 or later), or tables registered with a WHERE clause in the extension configuration table. To address this, pg_dump now utilizes the newly introduced restrict_nonsystem_relation_kind GUC parameter to restrict the accesses to non-system views and foreign tables during the dump process. This new GUC parameter is added to back branches too, but these changes do not require cluster recreation. Back-patch to all supported branches. Reviewed-by: Noah Misch Security: CVE-2024-7348 Backpatch-through: 12
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/foreign/foreign.c10
-rw-r--r--src/backend/optimizer/plan/createplan.c13
-rw-r--r--src/backend/optimizer/util/plancat.c12
-rw-r--r--src/backend/rewrite/rewriteHandler.c17
-rw-r--r--src/backend/tcop/postgres.c64
-rw-r--r--src/backend/utils/misc/guc_tables.c12
6 files changed, 128 insertions, 0 deletions
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index f4f35728b40..bb67d9f92b8 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -22,6 +22,7 @@
#include "foreign/foreign.h"
#include "funcapi.h"
#include "miscadmin.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -326,6 +327,15 @@ GetFdwRoutine(Oid fdwhandler)
Datum datum;
FdwRoutine *routine;
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in FDW handler */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
datum = OidFunctionCall0(fdwhandler);
routine = (FdwRoutine *) DatumGetPointer(datum);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 6b64c4a362d..a5c32b91cca 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -41,6 +41,7 @@
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "partitioning/partprune.h"
+#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
@@ -7141,7 +7142,19 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
if (rte->rtekind == RTE_RELATION &&
rte->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in foreign tables */
+ Assert(rte->relid >= FirstNormalObjectId);
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
fdwroutine = GetFdwRoutineByRelId(rte->relid);
+ }
else
fdwroutine = NULL;
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 775955363ef..86655f05dc8 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -45,6 +45,7 @@
#include "rewrite/rewriteManip.h"
#include "statistics/statistics.h"
#include "storage/bufmgr.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
@@ -528,6 +529,17 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* Grab foreign-table info using the relcache, while we have it */
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{
+ /* Check if the access to foreign tables is restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
+ {
+ /* there must not be built-in foreign tables */
+ Assert(RelationGetRelid(relation) >= FirstNormalObjectId);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system foreign table is restricted")));
+ }
+
rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index e1d805d113e..c223a2c50af 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -40,6 +40,7 @@
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSearchCycle.h"
#include "rewrite/rowsecurity.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -1729,6 +1730,14 @@ ApplyRetrieveRule(Query *parsetree,
if (rule->qual != NULL)
elog(ERROR, "cannot handle qualified ON SELECT rule");
+ /* Check if the expansion of non-system views are restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
+ RelationGetRelid(relation) >= FirstNormalObjectId))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system view \"%s\" is restricted",
+ RelationGetRelationName(relation))));
+
if (rt_index == parsetree->resultRelation)
{
/*
@@ -3212,6 +3221,14 @@ rewriteTargetView(Query *parsetree, Relation view)
}
}
+ /* Check if the expansion of non-system views are restricted */
+ if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
+ RelationGetRelid(view) >= FirstNormalObjectId))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access to non-system view \"%s\" is restricted",
+ RelationGetRelationName(view))));
+
/*
* The view must be updatable, else fail.
*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 034e0100bf0..7fe14c5402c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -79,6 +79,7 @@
#include "utils/snapmgr.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
+#include "utils/varlena.h"
/* ----------------
* global variables
@@ -103,6 +104,9 @@ int PostAuthDelay = 0;
/* Time between checks that the client is still connected. */
int client_connection_check_interval = 0;
+/* flags for non-system relation kinds to restrict use */
+int restrict_nonsystem_relation_kind;
+
/* ----------------
* private typedefs etc
* ----------------
@@ -3667,6 +3671,66 @@ assign_transaction_timeout(int newval, void *extra)
}
}
+/*
+ * GUC check_hook for restrict_nonsystem_relation_kind
+ */
+bool
+check_restrict_nonsystem_relation_kind(char **newval, void **extra, GucSource source)
+{
+ char *rawstring;
+ List *elemlist;
+ ListCell *l;
+ int flags = 0;
+
+ /* Need a modifiable copy of string */
+ rawstring = pstrdup(*newval);
+
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
+ {
+ /* syntax error in list */
+ GUC_check_errdetail("List syntax is invalid.");
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+
+ foreach(l, elemlist)
+ {
+ char *tok = (char *) lfirst(l);
+
+ if (pg_strcasecmp(tok, "view") == 0)
+ flags |= RESTRICT_RELKIND_VIEW;
+ else if (pg_strcasecmp(tok, "foreign-table") == 0)
+ flags |= RESTRICT_RELKIND_FOREIGN_TABLE;
+ else
+ {
+ GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+ pfree(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+ }
+
+ pfree(rawstring);
+ list_free(elemlist);
+
+ /* Save the flags in *extra, for use by the assign function */
+ *extra = guc_malloc(ERROR, sizeof(int));
+ *((int *) *extra) = flags;
+
+ return true;
+}
+
+/*
+ * GUC assign_hook for restrict_nonsystem_relation_kind
+ */
+void
+assign_restrict_nonsystem_relation_kind(const char *newval, void *extra)
+{
+ int *flags = (int *) extra;
+
+ restrict_nonsystem_relation_kind = *flags;
+}
/*
* set_debug_options --- apply "-d N" command line option
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 6f4188599b1..319f6d7cc02 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -578,6 +578,7 @@ static char *server_encoding_string;
static char *server_version_string;
static int server_version_num;
static char *debug_io_direct_string;
+static char *restrict_nonsystem_relation_kind_string;
#ifdef HAVE_SYSLOG
#define DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -4705,6 +4706,17 @@ struct config_string ConfigureNamesString[] =
check_synchronized_standby_slots, assign_synchronized_standby_slots, NULL
},
+ {
+ {"restrict_nonsystem_relation_kind", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets relation kinds of non-system relation to restrict use"),
+ NULL,
+ GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE
+ },
+ &restrict_nonsystem_relation_kind_string,
+ "",
+ check_restrict_nonsystem_relation_kind, assign_restrict_nonsystem_relation_kind, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL