Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit10d5983

Browse files
committed
Empty search_path in Autovacuum and non-psql/pgbench clients.
This makes the client programs behave as documented regardless of theconnect-time search_path and regardless of user-created objects. Today,a malicious user with CREATE permission on a search_path schema can takecontrol of certain of these clients' queries and invoke arbitrary SQLfunctions under the client identity, often a superuser. This isexploitable in the default configuration, where all users have CREATEprivilege on schema "public".This changes behavior of user-defined code stored in the database, likepg_index.indexprs and pg_extension_config_dump(). If they reach codebearing unqualified names, "does not exist" or "no schema has beenselected to create in" errors might appear. Users may fix such errorsby schema-qualifying affected names. After upgrading, consider watchingserver logs for these errors.The --table arguments of src/bin/scripts clients have been lax; forexample, "vacuumdb -Zt pg_am\;CHECKPOINT" performed a checkpoint. Thatnow fails, but for now, "vacuumdb -Zt 'pg_am(amname);CHECKPOINT'" stillperforms a checkpoint.Back-patch to 9.3 (all supported versions).Reviewed by Tom Lane, though this fix strategy was not his first choice.Reported by Arseniy Sharoglazov.Security:CVE-2018-1058
1 parentb8a2908 commit10d5983

File tree

24 files changed

+338
-66
lines changed

24 files changed

+338
-66
lines changed

‎contrib/oid2name/oid2name.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include"catalog/pg_class.h"
1313

14+
#include"fe_utils/connect.h"
1415
#include"libpq-fe.h"
1516
#include"pg_getopt.h"
1617

@@ -266,6 +267,7 @@ sql_conn(struct options *my_opts)
266267
boolhave_password= false;
267268
charpassword[100];
268269
boolnew_pass;
270+
PGresult*res;
269271

270272
/*
271273
* Start the connection. Loop until we have a password if requested by
@@ -323,6 +325,17 @@ sql_conn(struct options *my_opts)
323325
exit(1);
324326
}
325327

328+
res=PQexec(conn,ALWAYS_SECURE_SEARCH_PATH_SQL);
329+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
330+
{
331+
fprintf(stderr,"oid2name: could not clear search_path: %s\n",
332+
PQerrorMessage(conn));
333+
PQclear(res);
334+
PQfinish(conn);
335+
exit(-1);
336+
}
337+
PQclear(res);
338+
326339
/* return the conn if good */
327340
returnconn;
328341
}

‎contrib/vacuumlo/vacuumlo.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include"catalog/pg_class.h"
2525

26+
#include"fe_utils/connect.h"
2627
#include"libpq-fe.h"
2728
#include"pg_getopt.h"
2829

@@ -140,11 +141,8 @@ vacuumlo(const char *database, const struct _param *param)
140141
fprintf(stdout,"Test run: no large objects will be removed!\n");
141142
}
142143

143-
/*
144-
* Don't get fooled by any non-system catalogs
145-
*/
146-
res=PQexec(conn,"SET search_path = pg_catalog");
147-
if (PQresultStatus(res)!=PGRES_COMMAND_OK)
144+
res=PQexec(conn,ALWAYS_SECURE_SEARCH_PATH_SQL);
145+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
148146
{
149147
fprintf(stderr,"Failed to set search_path:\n");
150148
fprintf(stderr,"%s",PQerrorMessage(conn));

‎src/backend/postmaster/autovacuum.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,12 @@ AutoVacLauncherMain(int argc, char *argv[])
573573
/* must unblock signals before calling rebuild_database_list */
574574
PG_SETMASK(&UnBlockSig);
575575

576+
/*
577+
* Set always-secure search path. Launcher doesn't connect to a database,
578+
* so this has no effect.
579+
*/
580+
SetConfigOption("search_path","",PGC_SUSET,PGC_S_OVERRIDE);
581+
576582
/*
577583
* Force zero_damaged_pages OFF in the autovac process, even if it is set
578584
* in postgresql.conf. We don't really want such a dangerous option being
@@ -1583,6 +1589,14 @@ AutoVacWorkerMain(int argc, char *argv[])
15831589

15841590
PG_SETMASK(&UnBlockSig);
15851591

1592+
/*
1593+
* Set always-secure search path, so malicious users can't redirect user
1594+
* code (e.g. pg_index.indexprs). (That code runs in a
1595+
* SECURITY_RESTRICTED_OPERATION sandbox, so malicious users could not
1596+
* take control of the entire autovacuum worker in any case.)
1597+
*/
1598+
SetConfigOption("search_path","",PGC_SUSET,PGC_S_OVERRIDE);
1599+
15861600
/*
15871601
* Force zero_damaged_pages OFF in the autovac process, even if it is set
15881602
* in postgresql.conf. We don't really want such a dangerous option being

‎src/bin/pg_basebackup/streamutil.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include"pqexpbuffer.h"
2929
#include"common/fe_memutils.h"
3030
#include"datatype/timestamp.h"
31+
#include"fe_utils/connect.h"
3132

3233
#defineERRCODE_DUPLICATE_OBJECT "42710"
3334

@@ -205,6 +206,23 @@ GetConnection(void)
205206
if (conn_opts)
206207
PQconninfoFree(conn_opts);
207208

209+
/* Set always-secure search path, so malicious users can't get control. */
210+
if (dbname!=NULL)
211+
{
212+
PGresult*res;
213+
214+
res=PQexec(tmpconn,ALWAYS_SECURE_SEARCH_PATH_SQL);
215+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
216+
{
217+
fprintf(stderr,_("%s: could not clear search_path: %s\n"),
218+
progname,PQerrorMessage(tmpconn));
219+
PQclear(res);
220+
PQfinish(tmpconn);
221+
exit(1);
222+
}
223+
PQclear(res);
224+
}
225+
208226
/*
209227
* Ensure we have the same value of integer_datetimes (now always "on") as
210228
* the server we are connecting to.

‎src/bin/pg_dump/pg_backup_db.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include"postgres_fe.h"
1313

1414
#include"dumputils.h"
15+
#include"fe_utils/connect.h"
1516
#include"fe_utils/string_utils.h"
1617
#include"parallel.h"
1718
#include"pg_backup_archiver.h"
@@ -112,6 +113,10 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
112113
PQfinish(AH->connection);
113114
AH->connection=newConn;
114115

116+
/* Start strict; later phases may override this. */
117+
PQclear(ExecuteSqlQueryForSingleRow((Archive*)AH,
118+
ALWAYS_SECURE_SEARCH_PATH_SQL));
119+
115120
return1;
116121
}
117122

@@ -315,6 +320,10 @@ ConnectDatabase(Archive *AHX,
315320
PQdb(AH->connection) ?PQdb(AH->connection) :"",
316321
PQerrorMessage(AH->connection));
317322

323+
/* Start strict; later phases may override this. */
324+
PQclear(ExecuteSqlQueryForSingleRow((Archive*)AH,
325+
ALWAYS_SECURE_SEARCH_PATH_SQL));
326+
318327
/*
319328
* We want to remember connection's actual password, whether or not we got
320329
* it by prompting. So we don't just store the password variable.

‎src/bin/pg_dump/pg_dump.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "pg_backup_db.h"
6060
#include "pg_backup_utils.h"
6161
#include "pg_dump.h"
62+
#include "fe_utils/connect.h"
6263
#include "fe_utils/string_utils.h"
6364

6465

@@ -1002,6 +1003,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
10021003
PGconn *conn = GetConnection(AH);
10031004
const char *std_strings;
10041005

1006+
PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1007+
10051008
/*
10061009
* Set the client encoding if requested.
10071010
*/
@@ -1292,19 +1295,29 @@ expand_table_name_patterns(Archive *fout,
12921295

12931296
for (cell = patterns->head; cell; cell = cell->next)
12941297
{
1298+
/*
1299+
* Query must remain ABSOLUTELY devoid of unqualified names. This
1300+
* would be unnecessary given a pg_table_is_visible() variant taking a
1301+
* search_path argument.
1302+
*/
12951303
appendPQExpBuffer(query,
12961304
"SELECT c.oid"
12971305
"\nFROM pg_catalog.pg_class c"
1298-
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
1299-
"\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n",
1306+
"\n LEFT JOIN pg_catalog.pg_namespace n"
1307+
"\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1308+
"\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1309+
"\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
13001310
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
13011311
RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
13021312
RELKIND_PARTITIONED_TABLE);
13031313
processSQLNamePattern(GetConnection(fout), query, cell->val, true,
13041314
false, "n.nspname", "c.relname", NULL,
13051315
"pg_catalog.pg_table_is_visible(c.oid)");
13061316

1317+
ExecuteSqlStatement(fout, "RESET search_path");
13071318
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1319+
PQclear(ExecuteSqlQueryForSingleRow(fout,
1320+
ALWAYS_SECURE_SEARCH_PATH_SQL));
13081321
if (strict_names && PQntuples(res) == 0)
13091322
exit_horribly(NULL, "no matching tables were found for pattern \"%s\"\n", cell->val);
13101323

‎src/bin/pg_dump/pg_dumpall.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include"dumputils.h"
2424
#include"pg_backup.h"
2525
#include"common/file_utils.h"
26+
#include"fe_utils/connect.h"
2627
#include"fe_utils/string_utils.h"
2728

2829
/* version string we expect back from pg_dump */
@@ -2068,10 +2069,7 @@ connectDatabase(const char *dbname, const char *connection_string,
20682069
exit_nicely(1);
20692070
}
20702071

2071-
/*
2072-
* Make sure we are not fooled by non-system schemas in the search path.
2073-
*/
2074-
executeCommand(conn,"SET search_path = pg_catalog");
2072+
PQclear(executeQuery(conn,ALWAYS_SECURE_SEARCH_PATH_SQL));
20752073

20762074
returnconn;
20772075
}

‎src/bin/pg_rewind/libpq_fetch.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include"libpq-fe.h"
2929
#include"catalog/catalog.h"
3030
#include"catalog/pg_type.h"
31+
#include"fe_utils/connect.h"
3132

3233
staticPGconn*conn=NULL;
3334

@@ -57,6 +58,12 @@ libpqConnect(const char *connstr)
5758

5859
pg_log(PG_PROGRESS,"connected to server\n");
5960

61+
res=PQexec(conn,ALWAYS_SECURE_SEARCH_PATH_SQL);
62+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
63+
pg_fatal("could not clear search_path: %s",
64+
PQresultErrorMessage(res));
65+
PQclear(res);
66+
6067
/*
6168
* Check that the server is not in hot standby mode. There is no
6269
* fundamental reason that couldn't be made to work, but it doesn't

‎src/bin/pg_upgrade/server.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include"postgres_fe.h"
1111

12+
#include"fe_utils/connect.h"
1213
#include"fe_utils/string_utils.h"
1314
#include"pg_upgrade.h"
1415

@@ -40,6 +41,8 @@ connectToServer(ClusterInfo *cluster, const char *db_name)
4041
exit(1);
4142
}
4243

44+
PQclear(executeQueryOrDie(conn,ALWAYS_SECURE_SEARCH_PATH_SQL));
45+
4346
returnconn;
4447
}
4548

‎src/bin/scripts/clusterdb.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,21 @@ cluster_one_database(const char *dbname, bool verbose, const char *table,
195195

196196
PGconn*conn;
197197

198+
conn=connectDatabase(dbname,host,port,username,prompt_password,
199+
progname,echo, false, false);
200+
198201
initPQExpBuffer(&sql);
199202

200203
appendPQExpBufferStr(&sql,"CLUSTER");
201204
if (verbose)
202205
appendPQExpBufferStr(&sql," VERBOSE");
203206
if (table)
204-
appendPQExpBuffer(&sql," %s",table);
207+
{
208+
appendPQExpBufferChar(&sql,' ');
209+
appendQualifiedRelation(&sql,table,conn,progname,echo);
210+
}
205211
appendPQExpBufferChar(&sql,';');
206212

207-
conn=connectDatabase(dbname,host,port,username,prompt_password,
208-
progname, false, false);
209213
if (!executeMaintenanceCommand(conn,sql.data,echo))
210214
{
211215
if (table)
@@ -234,7 +238,7 @@ cluster_all_databases(bool verbose, const char *maintenance_db,
234238
inti;
235239

236240
conn=connectMaintenanceDatabase(maintenance_db,host,port,username,
237-
prompt_password,progname);
241+
prompt_password,progname,echo);
238242
result=executeQuery(conn,"SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;",progname,echo);
239243
PQfinish(conn);
240244

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp