Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Empty search_path in logical replication apply worker and walsender.
authorNoah Misch <noah@leadboat.com>
Mon, 10 Aug 2020 16:22:54 +0000 (09:22 -0700)
committerNoah Misch <noah@leadboat.com>
Mon, 10 Aug 2020 16:22:59 +0000 (09:22 -0700)
This is like CVE-2018-1058 commit
582edc369cdbd348d68441fc50fa26a84afd0c1a.  Today, a malicious user of a
publisher or subscriber database can invoke arbitrary SQL functions
under an identity running replication, often a superuser.  This fix may
cause "does not exist" or "no schema has been selected to create in"
errors in a replication process.  After upgrading, consider watching
server logs for these errors.  Objects accruing schema qualification in
the wake of the earlier commit are unlikely to need further correction.
Back-patch to v10, which introduced logical replication.

Security: CVE-2020-14349

src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
src/backend/replication/logical/worker.c
src/test/subscription/t/001_rep_changes.pl

index cf7b146311712e55a42995bee832efc6a19f48da..1873ff130660da5fb7bdec1309b9f2d43051f936 100644 (file)
@@ -23,6 +23,7 @@
 #include "pqexpbuffer.h"
 #include "access/xlog.h"
 #include "catalog/pg_type.h"
+#include "common/connect.h"
 #include "funcapi.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -215,6 +216,22 @@ libpqrcv_connect(const char *conninfo, bool logical, const char *appname,
        return NULL;
    }
 
+   if (logical)
+   {
+       PGresult   *res;
+
+       res = libpqrcv_PQexec(conn->streamConn,
+                             ALWAYS_SECURE_SEARCH_PATH_SQL);
+       if (PQresultStatus(res) != PGRES_TUPLES_OK)
+       {
+           PQclear(res);
+           ereport(ERROR,
+                   (errmsg("could not clear search path: %s",
+                           pchomp(PQerrorMessage(conn->streamConn)))));
+       }
+       PQclear(res);
+   }
+
    conn->logical = logical;
 
    return conn;
index ef65cb9922476cbc001ff36be9f07b5a06b60730..ced1a59ac18a5180d77f77538474299ff9c2acbf 100644 (file)
@@ -1659,6 +1659,12 @@ ApplyWorkerMain(Datum main_arg)
                                              MyLogicalRepWorker->userid,
                                              0);
 
+   /*
+    * Set always-secure search path, so malicious users can't redirect user
+    * code (e.g. pg_index.indexprs).
+    */
+   SetConfigOption("search_path", "", PGC_SUSET, PGC_S_OVERRIDE);
+
    /* Load the subscription into persistent memory context. */
    ApplyContext = AllocSetContextCreate(TopMemoryContext,
                                         "ApplyContext",
index 587bcb50ed0cfce1265dc8b3cbdf32f77ffd297f..9ca0acb6cbc530c281abb992e04bfa14c06f5c0e 100644 (file)
@@ -16,6 +16,10 @@ $node_subscriber->init(allows_streaming => 'logical');
 $node_subscriber->start;
 
 # Create some preexisting content on publisher
+$node_publisher->safe_psql(
+   'postgres',
+   "CREATE FUNCTION public.pg_get_replica_identity_index(int)
+    RETURNS regclass LANGUAGE sql AS 'SELECT 1/0'");    # shall not call
 $node_publisher->safe_psql('postgres',
    "CREATE TABLE tab_notrep AS SELECT generate_series(1,10) AS a");
 $node_publisher->safe_psql('postgres',