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

Commit91f3ffc

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 parenta8fc37a commit91f3ffc

28 files changed

+360
-89
lines changed

‎contrib/oid2name/oid2name.c

Lines changed: 13 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"libpq-fe.h"
1314
#include"pg_getopt.h"
1415

@@ -263,6 +264,7 @@ sql_conn(struct options * my_opts)
263264
PGconn*conn;
264265
char*password=NULL;
265266
boolnew_pass;
267+
PGresult*res;
266268

267269
/*
268270
* Start the connection. Loop until we have a password if requested by
@@ -322,6 +324,17 @@ sql_conn(struct options * my_opts)
322324
exit(1);
323325
}
324326

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

‎contrib/vacuumlo/vacuumlo.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include<termios.h>
2222
#endif
2323

24+
#include"fe_utils/connect.h"
2425
#include"libpq-fe.h"
2526
#include"pg_getopt.h"
2627

@@ -135,11 +136,8 @@ vacuumlo(const char *database, const struct _param * param)
135136
fprintf(stdout,"Test run: no large objects will be removed!\n");
136137
}
137138

138-
/*
139-
* Don't get fooled by any non-system catalogs
140-
*/
141-
res=PQexec(conn,"SET search_path = pg_catalog");
142-
if (PQresultStatus(res)!=PGRES_COMMAND_OK)
139+
res=PQexec(conn,ALWAYS_SECURE_SEARCH_PATH_SQL);
140+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
143141
{
144142
fprintf(stderr,"Failed to set search_path:\n");
145143
fprintf(stderr,"%s",PQerrorMessage(conn));

‎src/backend/postmaster/autovacuum.c

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

533+
/*
534+
* Set always-secure search path. Launcher doesn't connect to a database,
535+
* so this has no effect.
536+
*/
537+
SetConfigOption("search_path","",PGC_SUSET,PGC_S_OVERRIDE);
538+
533539
/*
534540
* Force zero_damaged_pages OFF in the autovac process, even if it is set
535541
* in postgresql.conf. We don't really want such a dangerous option being
@@ -1543,6 +1549,14 @@ AutoVacWorkerMain(int argc, char *argv[])
15431549

15441550
PG_SETMASK(&UnBlockSig);
15451551

1552+
/*
1553+
* Set always-secure search path, so malicious users can't redirect user
1554+
* code (e.g. pg_index.indexprs). (That code runs in a
1555+
* SECURITY_RESTRICTED_OPERATION sandbox, so malicious users could not
1556+
* take control of the entire autovacuum worker in any case.)
1557+
*/
1558+
SetConfigOption("search_path","",PGC_SUSET,PGC_S_OVERRIDE);
1559+
15461560
/*
15471561
* Force zero_damaged_pages OFF in the autovac process, even if it is set
15481562
* 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
@@ -30,6 +30,7 @@
3030
#include"pqexpbuffer.h"
3131
#include"common/fe_memutils.h"
3232
#include"datatype/timestamp.h"
33+
#include"fe_utils/connect.h"
3334

3435
#defineERRCODE_DUPLICATE_OBJECT "42710"
3536

@@ -208,6 +209,23 @@ GetConnection(void)
208209
if (conn_opts)
209210
PQconninfoFree(conn_opts);
210211

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

‎src/bin/pg_dump/dumputils.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,8 +1376,9 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
13761376
}
13771377

13781378
/*
1379-
* Now decide what we need to emit. Note there will be a leading "^(" in
1380-
* the patterns in any case.
1379+
* Now decide what we need to emit. We may run under a hostile
1380+
* search_path, so qualify EVERY name. Note there will be a leading "^("
1381+
* in the patterns in any case.
13811382
*/
13821383
if (namebuf.len>2)
13831384
{
@@ -1390,15 +1391,18 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
13901391
WHEREAND();
13911392
if (altnamevar)
13921393
{
1393-
appendPQExpBuffer(buf,"(%s ~ ",namevar);
1394+
appendPQExpBuffer(buf,
1395+
"(%s OPERATOR(pg_catalog.~) ",namevar);
13941396
appendStringLiteralConn(buf,namebuf.data,conn);
1395-
appendPQExpBuffer(buf,"\n OR %s ~ ",altnamevar);
1397+
appendPQExpBuffer(buf,
1398+
"\n OR %s OPERATOR(pg_catalog.~) ",
1399+
altnamevar);
13961400
appendStringLiteralConn(buf,namebuf.data,conn);
13971401
appendPQExpBufferStr(buf,")\n");
13981402
}
13991403
else
14001404
{
1401-
appendPQExpBuffer(buf,"%s~ ",namevar);
1405+
appendPQExpBuffer(buf,"%sOPERATOR(pg_catalog.~) ",namevar);
14021406
appendStringLiteralConn(buf,namebuf.data,conn);
14031407
appendPQExpBufferChar(buf,'\n');
14041408
}
@@ -1414,7 +1418,7 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
14141418
if (strcmp(schemabuf.data,"^(.*)$")!=0&&schemavar)
14151419
{
14161420
WHEREAND();
1417-
appendPQExpBuffer(buf,"%s~ ",schemavar);
1421+
appendPQExpBuffer(buf,"%sOPERATOR(pg_catalog.~) ",schemavar);
14181422
appendStringLiteralConn(buf,schemabuf.data,conn);
14191423
appendPQExpBufferChar(buf,'\n');
14201424
}

‎src/bin/pg_dump/pg_backup_db.c

Lines changed: 11 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"parallel.h"
1617
#include"pg_backup_archiver.h"
1718
#include"pg_backup_db.h"
@@ -113,6 +114,11 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username)
113114
PQfinish(AH->connection);
114115
AH->connection=newConn;
115116

117+
/* Start strict; later phases may override this. */
118+
if (PQserverVersion(AH->connection) >=70300)
119+
PQclear(ExecuteSqlQueryForSingleRow((Archive*)AH,
120+
ALWAYS_SECURE_SEARCH_PATH_SQL));
121+
116122
return1;
117123
}
118124

@@ -321,6 +327,11 @@ ConnectDatabase(Archive *AHX,
321327
PQdb(AH->connection) ?PQdb(AH->connection) :"",
322328
PQerrorMessage(AH->connection));
323329

330+
/* Start strict; later phases may override this. */
331+
if (PQserverVersion(AH->connection) >=70300)
332+
PQclear(ExecuteSqlQueryForSingleRow((Archive*)AH,
333+
ALWAYS_SECURE_SEARCH_PATH_SQL));
334+
324335
/*
325336
* We want to remember connection's actual password, whether or not we got
326337
* 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
@@ -60,6 +60,7 @@
6060
#include"pg_backup_db.h"
6161
#include"pg_backup_utils.h"
6262
#include"pg_dump.h"
63+
#include"fe_utils/connect.h"
6364

6465

6566
typedefstruct
@@ -965,6 +966,9 @@ setup_connection(Archive *AH, const char *dumpencoding,
965966
PGconn*conn=GetConnection(AH);
966967
constchar*std_strings;
967968

969+
if (AH->remoteVersion >=70300)
970+
PQclear(ExecuteSqlQueryForSingleRow(AH,ALWAYS_SECURE_SEARCH_PATH_SQL));
971+
968972
/*
969973
* Set the client encoding if requested.
970974
*/
@@ -1257,21 +1261,30 @@ expand_table_name_patterns(Archive *fout,
12571261

12581262
for (cell=patterns->head;cell;cell=cell->next)
12591263
{
1264+
/*
1265+
* Query must remain ABSOLUTELY devoid of unqualified names. This
1266+
* would be unnecessary given a pg_table_is_visible() variant taking a
1267+
* search_path argument.
1268+
*/
12601269
if (cell!=patterns->head)
12611270
appendPQExpBufferStr(query,"UNION ALL\n");
12621271
appendPQExpBuffer(query,
12631272
"SELECT c.oid"
12641273
"\nFROM pg_catalog.pg_class c"
1265-
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
1266-
"\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n",
1274+
"\n LEFT JOIN pg_catalog.pg_namespace n"
1275+
"\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1276+
"\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1277+
"\n (array['%c', '%c', '%c', '%c', '%c'])\n",
12671278
RELKIND_RELATION,RELKIND_SEQUENCE,RELKIND_VIEW,
12681279
RELKIND_MATVIEW,RELKIND_FOREIGN_TABLE);
12691280
processSQLNamePattern(GetConnection(fout),query,cell->val, true,
12701281
false,"n.nspname","c.relname",NULL,
12711282
"pg_catalog.pg_table_is_visible(c.oid)");
12721283
}
12731284

1285+
ExecuteSqlStatement(fout,"RESET search_path");
12741286
res=ExecuteSqlQuery(fout,query->data,PGRES_TUPLES_OK);
1287+
PQclear(ExecuteSqlQueryForSingleRow(fout,ALWAYS_SECURE_SEARCH_PATH_SQL));
12751288

12761289
for (i=0;i<PQntuples(res);i++)
12771290
{

‎src/bin/pg_dump/pg_dumpall.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include"dumputils.h"
2828
#include"pg_backup.h"
29+
#include"fe_utils/connect.h"
2930

3031
/* version string we expect back from pg_dump */
3132
#definePGDUMP_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n"
@@ -1983,12 +1984,8 @@ connectDatabase(const char *dbname, const char *connection_string,
19831984
exit_nicely(1);
19841985
}
19851986

1986-
/*
1987-
* On 7.3 and later, make sure we are not fooled by non-system schemas in
1988-
* the search path.
1989-
*/
19901987
if (server_version >=70300)
1991-
executeCommand(conn,"SET search_path = pg_catalog");
1988+
PQclear(executeQuery(conn,ALWAYS_SECURE_SEARCH_PATH_SQL));
19921989

19931990
returnconn;
19941991
}

‎src/bin/pg_rewind/libpq_fetch.c

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

3334
staticPGconn*conn=NULL;
3435

@@ -58,6 +59,12 @@ libpqConnect(const char *connstr)
5859

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

62+
res=PQexec(conn,ALWAYS_SECURE_SEARCH_PATH_SQL);
63+
if (PQresultStatus(res)!=PGRES_TUPLES_OK)
64+
pg_fatal("could not clear search_path: %s",
65+
PQresultErrorMessage(res));
66+
PQclear(res);
67+
6168
/*
6269
* Check that the server is not in hot standby mode. There is no
6370
* 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"pg_upgrade.h"
1314

1415

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

43+
PQclear(executeQueryOrDie(conn,ALWAYS_SECURE_SEARCH_PATH_SQL));
44+
4245
returnconn;
4346
}
4447

‎src/bin/scripts/Makefile

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ all: $(PROGRAMS)
2525
%:%.o$(WIN32RES)
2626
$(CC)$(CFLAGS)$^$(libpq_pgport)$(LDFLAGS)$(LDFLAGS_EX)$(LIBS) -o$@$(X)
2727

28-
createdb: createdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
29-
createlang: createlang.o common.o print.o mbprint.o | submake-libpq submake-libpgport
30-
createuser: createuser.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
31-
dropdb: dropdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
32-
droplang: droplang.o common.o print.o mbprint.o | submake-libpq submake-libpgport
33-
dropuser: dropuser.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
34-
clusterdb: clusterdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
35-
vacuumdb: vacuumdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
36-
reindexdb: reindexdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq submake-libpgport
37-
pg_isready: pg_isready.o common.o | submake-libpq submake-libpgport
28+
SCRIPTS_COMMON = common.o dumputils.o kwlookup.o keywords.o
29+
createdb: createdb.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
30+
createlang: createlang.o$(SCRIPTS_COMMON) print.o mbprint.o | submake-libpq submake-libpgport
31+
createuser: createuser.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
32+
dropdb: dropdb.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
33+
droplang: droplang.o$(SCRIPTS_COMMON) print.o mbprint.o | submake-libpq submake-libpgport
34+
dropuser: dropuser.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
35+
clusterdb: clusterdb.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
36+
vacuumdb: vacuumdb.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
37+
reindexdb: reindexdb.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
38+
pg_isready: pg_isready.o$(SCRIPTS_COMMON) | submake-libpq submake-libpgport
3839

3940
dumputils.ckeywords.c:% :$(top_srcdir)/src/bin/pg_dump/%
4041
rm -f$@&&$(LN_S)$<.
@@ -65,7 +66,7 @@ uninstall:
6566

6667
cleandistcleanmaintainer-clean:
6768
rm -f$(addsuffix$(X),$(PROGRAMS))$(addsuffix .o,$(PROGRAMS))
68-
rm -fcommon.o dumputils.o kwlookup.o keywords.o print.o mbprint.o$(WIN32RES)
69+
rm -f$(SCRIPTS_COMMON) print.o mbprint.o$(WIN32RES)
6970
rm -f dumputils.c print.c mbprint.c kwlookup.c keywords.c
7071
rm -rf tmp_check
7172

‎src/bin/scripts/clusterdb.c

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

195195
PGconn*conn;
196196

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

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

206-
conn=connectDatabase(dbname,host,port,username,prompt_password,
207-
progname, false, false);
208212
if (!executeMaintenanceCommand(conn,sql.data,echo))
209213
{
210214
if (table)
@@ -233,7 +237,7 @@ cluster_all_databases(bool verbose, const char *maintenance_db,
233237
inti;
234238

235239
conn=connectMaintenanceDatabase(maintenance_db,host,port,username,
236-
prompt_password,progname);
240+
prompt_password,progname,echo);
237241
result=executeQuery(conn,"SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;",progname,echo);
238242
PQfinish(conn);
239243

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp