Skip to content

Commit 3e6701e

Browse files
committed
Merge branch 'master' into release_2_5
2 parents ba28354 + 002d7b5 commit 3e6701e

12 files changed

+255
-18
lines changed

src/backup.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ check_server_version(PGconn *conn, PGNodeInfo *nodeInfo)
888888
nodeInfo->server_version_str, "9.6");
889889

890890
if (nodeInfo->pgpro_support)
891-
res = pgut_execute(conn, "SELECT pgpro_edition()", 0, NULL);
891+
res = pgut_execute(conn, "SELECT pg_catalog.pgpro_edition()", 0, NULL);
892892

893893
/*
894894
* Check major version of connected PostgreSQL and major version of
@@ -1079,7 +1079,7 @@ pgpro_support(PGconn *conn)
10791079
PGresult *res;
10801080

10811081
res = pgut_execute(conn,
1082-
"SELECT proname FROM pg_proc WHERE proname='pgpro_edition'",
1082+
"SELECT proname FROM pg_catalog.pg_proc WHERE proname='pgpro_edition'::name AND pronamespace='pg_catalog'::regnamespace::oid",
10831083
0, NULL);
10841084

10851085
if (PQresultStatus(res) == PGRES_TUPLES_OK &&
@@ -1118,7 +1118,7 @@ get_database_map(PGconn *conn)
11181118
*/
11191119
res = pgut_execute_extended(conn,
11201120
"SELECT oid, datname FROM pg_catalog.pg_database "
1121-
"WHERE datname NOT IN ('template1', 'template0')",
1121+
"WHERE datname NOT IN ('template1'::name, 'template0'::name)",
11221122
0, NULL, true, true);
11231123

11241124
/* Don't error out, simply return NULL. See comment above. */

src/checkdb.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,10 @@ get_index_list(const char *dbname, bool first_db_with_amcheck,
357357

358358
res = pgut_execute(db_conn, "SELECT "
359359
"extname, nspname, extversion "
360-
"FROM pg_namespace n "
361-
"JOIN pg_extension e "
360+
"FROM pg_catalog.pg_namespace n "
361+
"JOIN pg_catalog.pg_extension e "
362362
"ON n.oid=e.extnamespace "
363-
"WHERE e.extname IN ('amcheck', 'amcheck_next') "
363+
"WHERE e.extname IN ('amcheck'::name, 'amcheck_next'::name) "
364364
"ORDER BY extversion DESC "
365365
"LIMIT 1",
366366
0, NULL);
@@ -556,8 +556,8 @@ do_amcheck(ConnectionOptions conn_opt, PGconn *conn)
556556

557557
res_db = pgut_execute(conn,
558558
"SELECT datname, oid, dattablespace "
559-
"FROM pg_database "
560-
"WHERE datname NOT IN ('template0', 'template1')",
559+
"FROM pg_catalog.pg_database "
560+
"WHERE datname NOT IN ('template0'::name, 'template1'::name)",
561561
0, NULL);
562562

563563
/* we don't need this connection anymore */

src/ptrack.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ get_ptrack_version(PGconn *backup_conn, PGNodeInfo *nodeInfo)
5151

5252
res_db = pgut_execute(backup_conn,
5353
"SELECT extnamespace::regnamespace, extversion "
54-
"FROM pg_catalog.pg_extension WHERE extname = 'ptrack'",
54+
"FROM pg_catalog.pg_extension WHERE extname = 'ptrack'::name",
5555
0, NULL);
5656

5757
if (PQntuples(res_db) > 0)
@@ -69,7 +69,7 @@ get_ptrack_version(PGconn *backup_conn, PGNodeInfo *nodeInfo)
6969
/* ptrack 1.x is supported, save version */
7070
PQclear(res_db);
7171
res_db = pgut_execute(backup_conn,
72-
"SELECT proname FROM pg_proc WHERE proname='ptrack_version'",
72+
"SELECT proname FROM pg_catalog.pg_proc WHERE proname='ptrack_version'::name",
7373
0, NULL);
7474

7575
if (PQntuples(res_db) == 0)

src/util.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ get_current_timeline(PGconn *conn)
167167
char *val;
168168

169169
res = pgut_execute_extended(conn,
170-
"SELECT timeline_id FROM pg_control_checkpoint()", 0, NULL, true, true);
170+
"SELECT timeline_id FROM pg_catalog.pg_control_checkpoint()", 0, NULL, true, true);
171171

172172
if (PQresultStatus(res) == PGRES_TUPLES_OK)
173173
val = PQgetvalue(res, 0, 0);

src/utils/pgut.c

+29-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
#include "common/string.h"
2121
#endif
2222

23+
#if PG_VERSION_NUM >= 100000
24+
#include "common/connect.h"
25+
#else
26+
#include "fe_utils/connect.h"
27+
#endif
28+
2329
#include <time.h>
2430

2531
#include "pgut.h"
@@ -257,7 +263,7 @@ pgut_connect(const char *host, const char *port,
257263
pthread_lock(&atexit_callback_disconnect_mutex);
258264
pgut_atexit_push(pgut_disconnect_callback, conn);
259265
pthread_mutex_unlock(&atexit_callback_disconnect_mutex);
260-
return conn;
266+
break;
261267
}
262268

263269
if (conn && PQconnectionNeedsPassword(conn) && prompt_password)
@@ -279,6 +285,28 @@ pgut_connect(const char *host, const char *port,
279285
PQfinish(conn);
280286
return NULL;
281287
}
288+
289+
/*
290+
* Fix for CVE-2018-1058. This code was taken with small modification from
291+
* src/bin/pg_basebackup/streamutil.c:GetConnection()
292+
*/
293+
if (dbname != NULL)
294+
{
295+
PGresult *res;
296+
297+
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
298+
if (PQresultStatus(res) != PGRES_TUPLES_OK)
299+
{
300+
elog(ERROR, "could not clear search_path: %s",
301+
PQerrorMessage(conn));
302+
PQclear(res);
303+
PQfinish(conn);
304+
return NULL;
305+
}
306+
PQclear(res);
307+
}
308+
309+
return conn;
282310
}
283311

284312
PGconn *

tests/CVE_2018_1058.py

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import os
2+
import unittest
3+
from .helpers.ptrack_helpers import ProbackupTest, ProbackupException
4+
5+
module_name = 'CVE-2018-1058'
6+
7+
class CVE_2018_1058(ProbackupTest, unittest.TestCase):
8+
9+
# @unittest.skip("skip")
10+
def test_basic_default_search_path(self):
11+
""""""
12+
fname = self.id().split('.')[3]
13+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
14+
node = self.make_simple_node(
15+
base_dir=os.path.join(module_name, fname, 'node'),
16+
set_replication=True)
17+
18+
self.init_pb(backup_dir)
19+
self.add_instance(backup_dir, 'node', node)
20+
node.slow_start()
21+
22+
node.safe_psql(
23+
'postgres',
24+
"CREATE FUNCTION public.pgpro_edition() "
25+
"RETURNS text "
26+
"AS $$ "
27+
"BEGIN "
28+
" RAISE 'pg_probackup vulnerable!'; "
29+
"END "
30+
"$$ LANGUAGE plpgsql")
31+
32+
self.backup_node(backup_dir, 'node', node, backup_type='full', options=['--stream'])
33+
34+
# Clean after yourself
35+
self.del_test_dir(module_name, fname)
36+
37+
# @unittest.skip("skip")
38+
def test_basic_backup_modified_search_path(self):
39+
""""""
40+
fname = self.id().split('.')[3]
41+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
42+
node = self.make_simple_node(
43+
base_dir=os.path.join(module_name, fname, 'node'),
44+
set_replication=True)
45+
self.set_auto_conf(node, options={'search_path': 'public,pg_catalog'})
46+
47+
self.init_pb(backup_dir)
48+
self.add_instance(backup_dir, 'node', node)
49+
node.slow_start()
50+
51+
node.safe_psql(
52+
'postgres',
53+
"CREATE FUNCTION public.pg_control_checkpoint(OUT timeline_id integer, OUT dummy integer) "
54+
"RETURNS record "
55+
"AS $$ "
56+
"BEGIN "
57+
" RAISE '% vulnerable!', 'pg_probackup'; "
58+
"END "
59+
"$$ LANGUAGE plpgsql")
60+
61+
node.safe_psql(
62+
'postgres',
63+
"CREATE FUNCTION public.pg_proc(OUT proname name, OUT dummy integer) "
64+
"RETURNS record "
65+
"AS $$ "
66+
"BEGIN "
67+
" RAISE '% vulnerable!', 'pg_probackup'; "
68+
"END "
69+
"$$ LANGUAGE plpgsql; "
70+
"CREATE VIEW public.pg_proc AS SELECT proname FROM public.pg_proc()")
71+
72+
self.backup_node(backup_dir, 'node', node, backup_type='full', options=['--stream'])
73+
74+
log_file = os.path.join(node.logs_dir, 'postgresql.log')
75+
with open(log_file, 'r') as f:
76+
log_content = f.read()
77+
self.assertFalse(
78+
'pg_probackup vulnerable!' in log_content)
79+
80+
# Clean after yourself
81+
self.del_test_dir(module_name, fname)
82+
83+
# @unittest.skip("skip")
84+
def test_basic_checkdb_modified_search_path(self):
85+
""""""
86+
fname = self.id().split('.')[3]
87+
node = self.make_simple_node(
88+
base_dir=os.path.join(module_name, fname, 'node'),
89+
initdb_params=['--data-checksums'])
90+
self.set_auto_conf(node, options={'search_path': 'public,pg_catalog'})
91+
node.slow_start()
92+
93+
node.safe_psql(
94+
'postgres',
95+
"CREATE FUNCTION public.pg_database(OUT datname name, OUT oid oid, OUT dattablespace oid) "
96+
"RETURNS record "
97+
"AS $$ "
98+
"BEGIN "
99+
" RAISE 'pg_probackup vulnerable!'; "
100+
"END "
101+
"$$ LANGUAGE plpgsql; "
102+
"CREATE VIEW public.pg_database AS SELECT * FROM public.pg_database()")
103+
104+
node.safe_psql(
105+
'postgres',
106+
"CREATE FUNCTION public.pg_extension(OUT extname name, OUT extnamespace oid, OUT extversion text) "
107+
"RETURNS record "
108+
"AS $$ "
109+
"BEGIN "
110+
" RAISE 'pg_probackup vulnerable!'; "
111+
"END "
112+
"$$ LANGUAGE plpgsql; "
113+
"CREATE FUNCTION public.pg_namespace(OUT oid oid, OUT nspname name) "
114+
"RETURNS record "
115+
"AS $$ "
116+
"BEGIN "
117+
" RAISE 'pg_probackup vulnerable!'; "
118+
"END "
119+
"$$ LANGUAGE plpgsql; "
120+
"CREATE VIEW public.pg_extension AS SELECT * FROM public.pg_extension();"
121+
"CREATE VIEW public.pg_namespace AS SELECT * FROM public.pg_namespace();"
122+
)
123+
124+
try:
125+
self.checkdb_node(
126+
options=[
127+
'--amcheck',
128+
'--skip-block-validation',
129+
'-d', 'postgres', '-p', str(node.port)])
130+
self.assertEqual(
131+
1, 0,
132+
"Expecting Error because amcheck{,_next} not installed\n"
133+
" Output: {0} \n CMD: {1}".format(
134+
repr(self.output), self.cmd))
135+
except ProbackupException as e:
136+
self.assertIn(
137+
"WARNING: Extension 'amcheck' or 'amcheck_next' are not installed in database postgres",
138+
e.message,
139+
"\n Unexpected Error Message: {0}\n CMD: {1}".format(
140+
repr(e.message), self.cmd))
141+
142+
# Clean after yourself
143+
self.del_test_dir(module_name, fname)

tests/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
compression, page, ptrack, archive, exclude, cfs_backup, cfs_restore, \
88
cfs_validate_backup, auth_test, time_stamp, snapfs, logging, \
99
locking, remote, external, config, checkdb, set_backup, incr_restore, \
10-
catchup
10+
catchup, CVE_2018_1058
1111

1212

1313
def load_tests(loader, tests, pattern):
@@ -57,6 +57,7 @@ def load_tests(loader, tests, pattern):
5757
suite.addTests(loader.loadTestsFromModule(snapfs))
5858
suite.addTests(loader.loadTestsFromModule(time_stamp))
5959
suite.addTests(loader.loadTestsFromModule(validate))
60+
suite.addTests(loader.loadTestsFromModule(CVE_2018_1058))
6061

6162
return suite
6263

0 commit comments

Comments
 (0)