Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit fdf218f

Browse files
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
1 parent 91099bb commit fdf218f

File tree

15 files changed

+255
-1
lines changed

15 files changed

+255
-1
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,17 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1;
637637
Remote SQL: SELECT c1, c2 FROM public.loct_empty ORDER BY c1 ASC NULLS LAST
638638
(3 rows)
639639

640+
-- test restriction on non-system foreign tables.
641+
SET restrict_nonsystem_relation_kind TO 'foreign-table';
642+
SELECT * from ft1 where c1 < 1; -- ERROR
643+
ERROR: access to non-system foreign table is restricted
644+
INSERT INTO ft1 (c1) VALUES (1); -- ERROR
645+
ERROR: access to non-system foreign table is restricted
646+
DELETE FROM ft1 WHERE c1 = 1; -- ERROR
647+
ERROR: access to non-system foreign table is restricted
648+
TRUNCATE ft1; -- ERROR
649+
ERROR: access to non-system foreign table is restricted
650+
RESET restrict_nonsystem_relation_kind;
640651
-- ===================================================================
641652
-- WHERE with remotely-executable conditions
642653
-- ===================================================================

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,14 @@ DELETE FROM loct_empty;
327327
ANALYZE ft_empty;
328328
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft_empty ORDER BY c1;
329329

330+
-- test restriction on non-system foreign tables.
331+
SET restrict_nonsystem_relation_kind TO 'foreign-table';
332+
SELECT * from ft1 where c1 < 1; -- ERROR
333+
INSERT INTO ft1 (c1) VALUES (1); -- ERROR
334+
DELETE FROM ft1 WHERE c1 = 1; -- ERROR
335+
TRUNCATE ft1; -- ERROR
336+
RESET restrict_nonsystem_relation_kind;
337+
330338
-- ===================================================================
331339
-- WHERE with remotely-executable conditions
332340
-- ===================================================================

doc/src/sgml/config.sgml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9813,6 +9813,23 @@ SET XML OPTION { DOCUMENT | CONTENT };
98139813
</listitem>
98149814
</varlistentry>
98159815

9816+
<varlistentry id="guc-restrict-nonsystem-relation-kind" xreflabel="restrict_nonsystem_relation_kind">
9817+
<term><varname>restrict_nonsystem_relation_kind</varname> (<type>string</type>)
9818+
<indexterm>
9819+
<primary><varname>restrict_nonsystem_relation_kind</varname></primary>
9820+
<secondary>configuration parameter</secondary>
9821+
</indexterm>
9822+
</term>
9823+
<listitem>
9824+
<para>
9825+
This variable specifies relation kind to which access is restricted.
9826+
It contains a comma-separated list of relation kind. Currently, the
9827+
supported relation kinds are <literal>view</literal> and
9828+
<literal>foreign-table</literal>.
9829+
</para>
9830+
</listitem>
9831+
</varlistentry>
9832+
98169833
</variablelist>
98179834
</sect2>
98189835
<sect2 id="runtime-config-client-format">

doc/src/sgml/ref/pg_dump.sgml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,14 @@ PostgreSQL documentation
997997
The only exception is that an empty pattern is disallowed.
998998
</para>
999999

1000+
<note>
1001+
<para>
1002+
Using wildcards in <option>--include-foreign-data</option> may result
1003+
in access to unexpected foreign servers. Also, to use this option securely,
1004+
make sure that the named server must have a trusted owner.
1005+
</para>
1006+
</note>
1007+
10001008
<note>
10011009
<para>
10021010
When <option>--include-foreign-data</option> is specified,

src/backend/foreign/foreign.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "foreign/foreign.h"
2323
#include "funcapi.h"
2424
#include "miscadmin.h"
25+
#include "tcop/tcopprot.h"
2526
#include "utils/builtins.h"
2627
#include "utils/memutils.h"
2728
#include "utils/rel.h"
@@ -326,6 +327,15 @@ GetFdwRoutine(Oid fdwhandler)
326327
Datum datum;
327328
FdwRoutine *routine;
328329

330+
/* Check if the access to foreign tables is restricted */
331+
if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
332+
{
333+
/* there must not be built-in FDW handler */
334+
ereport(ERROR,
335+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
336+
errmsg("access to non-system foreign table is restricted")));
337+
}
338+
329339
datum = OidFunctionCall0(fdwhandler);
330340
routine = (FdwRoutine *) DatumGetPointer(datum);
331341

src/backend/optimizer/plan/createplan.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "parser/parse_clause.h"
4242
#include "parser/parsetree.h"
4343
#include "partitioning/partprune.h"
44+
#include "tcop/tcopprot.h"
4445
#include "utils/lsyscache.h"
4546

4647

@@ -7141,7 +7142,19 @@ make_modifytable(PlannerInfo *root, Plan *subplan,
71417142

71427143
if (rte->rtekind == RTE_RELATION &&
71437144
rte->relkind == RELKIND_FOREIGN_TABLE)
7145+
{
7146+
/* Check if the access to foreign tables is restricted */
7147+
if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
7148+
{
7149+
/* there must not be built-in foreign tables */
7150+
Assert(rte->relid >= FirstNormalObjectId);
7151+
ereport(ERROR,
7152+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7153+
errmsg("access to non-system foreign table is restricted")));
7154+
}
7155+
71447156
fdwroutine = GetFdwRoutineByRelId(rte->relid);
7157+
}
71457158
else
71467159
fdwroutine = NULL;
71477160
}

src/backend/optimizer/util/plancat.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "rewrite/rewriteManip.h"
4646
#include "statistics/statistics.h"
4747
#include "storage/bufmgr.h"
48+
#include "tcop/tcopprot.h"
4849
#include "utils/builtins.h"
4950
#include "utils/lsyscache.h"
5051
#include "utils/partcache.h"
@@ -528,6 +529,17 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
528529
/* Grab foreign-table info using the relcache, while we have it */
529530
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
530531
{
532+
/* Check if the access to foreign tables is restricted */
533+
if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
534+
{
535+
/* there must not be built-in foreign tables */
536+
Assert(RelationGetRelid(relation) >= FirstNormalObjectId);
537+
538+
ereport(ERROR,
539+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
540+
errmsg("access to non-system foreign table is restricted")));
541+
}
542+
531543
rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
532544
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
533545
}

src/backend/rewrite/rewriteHandler.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "rewrite/rewriteManip.h"
4141
#include "rewrite/rewriteSearchCycle.h"
4242
#include "rewrite/rowsecurity.h"
43+
#include "tcop/tcopprot.h"
4344
#include "utils/builtins.h"
4445
#include "utils/lsyscache.h"
4546
#include "utils/rel.h"
@@ -1729,6 +1730,14 @@ ApplyRetrieveRule(Query *parsetree,
17291730
if (rule->qual != NULL)
17301731
elog(ERROR, "cannot handle qualified ON SELECT rule");
17311732

1733+
/* Check if the expansion of non-system views are restricted */
1734+
if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
1735+
RelationGetRelid(relation) >= FirstNormalObjectId))
1736+
ereport(ERROR,
1737+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1738+
errmsg("access to non-system view \"%s\" is restricted",
1739+
RelationGetRelationName(relation))));
1740+
17321741
if (rt_index == parsetree->resultRelation)
17331742
{
17341743
/*
@@ -3212,6 +3221,14 @@ rewriteTargetView(Query *parsetree, Relation view)
32123221
}
32133222
}
32143223

3224+
/* Check if the expansion of non-system views are restricted */
3225+
if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
3226+
RelationGetRelid(view) >= FirstNormalObjectId))
3227+
ereport(ERROR,
3228+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3229+
errmsg("access to non-system view \"%s\" is restricted",
3230+
RelationGetRelationName(view))));
3231+
32153232
/*
32163233
* The view must be updatable, else fail.
32173234
*

src/backend/tcop/postgres.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include "utils/snapmgr.h"
8080
#include "utils/timeout.h"
8181
#include "utils/timestamp.h"
82+
#include "utils/varlena.h"
8283

8384
/* ----------------
8485
* global variables
@@ -103,6 +104,9 @@ int PostAuthDelay = 0;
103104
/* Time between checks that the client is still connected. */
104105
int client_connection_check_interval = 0;
105106

107+
/* flags for non-system relation kinds to restrict use */
108+
int restrict_nonsystem_relation_kind;
109+
106110
/* ----------------
107111
* private typedefs etc
108112
* ----------------
@@ -3667,6 +3671,66 @@ assign_transaction_timeout(int newval, void *extra)
36673671
}
36683672
}
36693673

3674+
/*
3675+
* GUC check_hook for restrict_nonsystem_relation_kind
3676+
*/
3677+
bool
3678+
check_restrict_nonsystem_relation_kind(char **newval, void **extra, GucSource source)
3679+
{
3680+
char *rawstring;
3681+
List *elemlist;
3682+
ListCell *l;
3683+
int flags = 0;
3684+
3685+
/* Need a modifiable copy of string */
3686+
rawstring = pstrdup(*newval);
3687+
3688+
if (!SplitIdentifierString(rawstring, ',', &elemlist))
3689+
{
3690+
/* syntax error in list */
3691+
GUC_check_errdetail("List syntax is invalid.");
3692+
pfree(rawstring);
3693+
list_free(elemlist);
3694+
return false;
3695+
}
3696+
3697+
foreach(l, elemlist)
3698+
{
3699+
char *tok = (char *) lfirst(l);
3700+
3701+
if (pg_strcasecmp(tok, "view") == 0)
3702+
flags |= RESTRICT_RELKIND_VIEW;
3703+
else if (pg_strcasecmp(tok, "foreign-table") == 0)
3704+
flags |= RESTRICT_RELKIND_FOREIGN_TABLE;
3705+
else
3706+
{
3707+
GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
3708+
pfree(rawstring);
3709+
list_free(elemlist);
3710+
return false;
3711+
}
3712+
}
3713+
3714+
pfree(rawstring);
3715+
list_free(elemlist);
3716+
3717+
/* Save the flags in *extra, for use by the assign function */
3718+
*extra = guc_malloc(ERROR, sizeof(int));
3719+
*((int *) *extra) = flags;
3720+
3721+
return true;
3722+
}
3723+
3724+
/*
3725+
* GUC assign_hook for restrict_nonsystem_relation_kind
3726+
*/
3727+
void
3728+
assign_restrict_nonsystem_relation_kind(const char *newval, void *extra)
3729+
{
3730+
int *flags = (int *) extra;
3731+
3732+
restrict_nonsystem_relation_kind = *flags;
3733+
}
36703734

36713735
/*
36723736
* set_debug_options --- apply "-d N" command line option

src/backend/utils/misc/guc_tables.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ static char *server_encoding_string;
578578
static char *server_version_string;
579579
static int server_version_num;
580580
static char *debug_io_direct_string;
581+
static char *restrict_nonsystem_relation_kind_string;
581582

582583
#ifdef HAVE_SYSLOG
583584
#define DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -4705,6 +4706,17 @@ struct config_string ConfigureNamesString[] =
47054706
check_synchronized_standby_slots, assign_synchronized_standby_slots, NULL
47064707
},
47074708

4709+
{
4710+
{"restrict_nonsystem_relation_kind", PGC_USERSET, CLIENT_CONN_STATEMENT,
4711+
gettext_noop("Sets relation kinds of non-system relation to restrict use"),
4712+
NULL,
4713+
GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE
4714+
},
4715+
&restrict_nonsystem_relation_kind_string,
4716+
"",
4717+
check_restrict_nonsystem_relation_kind, assign_restrict_nonsystem_relation_kind, NULL
4718+
},
4719+
47084720
/* End-of-list marker */
47094721
{
47104722
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL

src/bin/pg_dump/pg_dump.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ static bool nonemptyReloptions(const char *reloptions);
336336
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
337337
const char *prefix, Archive *fout);
338338
static char *get_synchronized_snapshot(Archive *fout);
339+
static void set_restrict_relation_kind(Archive *AH, const char *value);
339340
static void setupDumpWorker(Archive *AH);
340341
static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
341342
static bool forcePartitionRootLoad(const TableInfo *tbinfo);
@@ -1293,6 +1294,13 @@ setup_connection(Archive *AH, const char *dumpencoding,
12931294
ExecuteSqlStatement(AH, "SET row_security = off");
12941295
}
12951296

1297+
/*
1298+
* For security reasons, we restrict the expansion of non-system views and
1299+
* access to foreign tables during the pg_dump process. This restriction
1300+
* is adjusted when dumping foreign table data.
1301+
*/
1302+
set_restrict_relation_kind(AH, "view, foreign-table");
1303+
12961304
/*
12971305
* Initialize prepared-query state to "nothing prepared". We do this here
12981306
* so that a parallel dump worker will have its own state.
@@ -2161,6 +2169,10 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
21612169
*/
21622170
if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
21632171
{
2172+
/* Temporary allows to access to foreign tables to dump data */
2173+
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2174+
set_restrict_relation_kind(fout, "view");
2175+
21642176
appendPQExpBufferStr(q, "COPY (SELECT ");
21652177
/* klugery to get rid of parens in column list */
21662178
if (strlen(column_list) > 2)
@@ -2272,6 +2284,11 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
22722284
classname);
22732285

22742286
destroyPQExpBuffer(q);
2287+
2288+
/* Revert back the setting */
2289+
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2290+
set_restrict_relation_kind(fout, "view, foreign-table");
2291+
22752292
return 1;
22762293
}
22772294

@@ -2298,6 +2315,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
22982315
int rows_per_statement = dopt->dump_inserts;
22992316
int rows_this_statement = 0;
23002317

2318+
/* Temporary allows to access to foreign tables to dump data */
2319+
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2320+
set_restrict_relation_kind(fout, "view");
2321+
23012322
/*
23022323
* If we're going to emit INSERTs with column names, the most efficient
23032324
* way to deal with generated columns is to exclude them entirely. For
@@ -2537,6 +2558,10 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
25372558
destroyPQExpBuffer(insertStmt);
25382559
free(attgenerated);
25392560

2561+
/* Revert back the setting */
2562+
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2563+
set_restrict_relation_kind(fout, "view, foreign-table");
2564+
25402565
return 1;
25412566
}
25422567

@@ -4710,6 +4735,28 @@ is_superuser(Archive *fout)
47104735
return false;
47114736
}
47124737

4738+
/*
4739+
* Set the given value to restrict_nonsystem_relation_kind value. Since
4740+
* restrict_nonsystem_relation_kind is introduced in minor version releases,
4741+
* the setting query is effective only where available.
4742+
*/
4743+
static void
4744+
set_restrict_relation_kind(Archive *AH, const char *value)
4745+
{
4746+
PQExpBuffer query = createPQExpBuffer();
4747+
PGresult *res;
4748+
4749+
appendPQExpBuffer(query,
4750+
"SELECT set_config(name, '%s', false) "
4751+
"FROM pg_settings "
4752+
"WHERE name = 'restrict_nonsystem_relation_kind'",
4753+
value);
4754+
res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4755+
4756+
PQclear(res);
4757+
destroyPQExpBuffer(query);
4758+
}
4759+
47134760
/*
47144761
* getSubscriptions
47154762
* get information about subscriptions

0 commit comments

Comments
 (0)