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

Commit 4c03ac7

Browse files
committed
Re-validate connection string in libpqrcv_connect().
A superuser may create a subscription with password_required=true, but which uses a connection string without a password. Previously, if the owner of such a subscription was changed to a non-superuser, the non-superuser was able to utilize a password from another source (like a password file or the PGPASSWORD environment variable), which should not have been allowed. This commit adds a step to re-validate the connection string before connecting. Reported-by: Jeff Davis Author: Vignesh C Reviewed-by: Peter Smith, Robert Haas, Amit Kapila Discussion: https://www.postgresql.org/message-id/flat/e5892973ae2a80a1a3e0266806640dae3c428100.camel%40j-davis.com Backpatch-through: 16
1 parent 9c00e4c commit 4c03ac7

File tree

3 files changed

+95
-5
lines changed

3 files changed

+95
-5
lines changed

doc/src/sgml/ref/create_subscription.sgml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,12 @@ CREATE SUBSCRIPTION <replaceable class="parameter">subscription_name</replaceabl
357357
<term><literal>password_required</literal> (<type>boolean</type>)</term>
358358
<listitem>
359359
<para>
360-
Specifies whether connections to the publisher made as a result
361-
of this subscription must use password authentication. This setting
362-
is ignored when the subscription is owned by a superuser.
363-
The default is <literal>true</literal>. Only superusers can set
364-
this value to <literal>false</literal>.
360+
If set to <literal>true</literal>, connections to the publisher made
361+
as a result of this subscription must use password authentication
362+
and the password must be specified as a part of the connection
363+
string. This setting is ignored when the subscription is owned by a
364+
superuser. The default is <literal>true</literal>. Only superusers
365+
can set this value to <literal>false</literal>.
365366
</para>
366367
</listitem>
367368
</varlistentry>

src/backend/replication/libpqwalreceiver/libpqwalreceiver.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ libpqrcv_connect(const char *conninfo, bool logical, bool must_use_password,
137137
const char *vals[6];
138138
int i = 0;
139139

140+
/*
141+
* Re-validate connection string. The validation already happened at DDL
142+
* time, but the subscription owner may have changed. If we don't recheck
143+
* with the correct must_use_password, it's possible that the connection
144+
* will obtain the password from a different source, such as PGPASSFILE or
145+
* PGPASSWORD.
146+
*/
147+
libpqrcv_check_conninfo(conninfo, must_use_password);
148+
140149
/*
141150
* We use the expand_dbname parameter to process the connection string (or
142151
* URI), and pass some extra options.

src/test/subscription/t/027_nosuperuser.pl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,84 @@ sub grant_superuser
303303
expect_replication("alice.unpartitioned", 3, 17, 21,
304304
"restoring SELECT permission permits replication to continue");
305305

306+
# If the subscription connection requires a password ('password_required'
307+
# is true) then a non-superuser must specify that password in the connection
308+
# string.
309+
$ENV{"PGPASSWORD"} = 'secret';
310+
311+
my $node_publisher1 = PostgreSQL::Test::Cluster->new('publisher1');
312+
my $node_subscriber1 = PostgreSQL::Test::Cluster->new('subscriber1');
313+
$node_publisher1->init(allows_streaming => 'logical');
314+
$node_subscriber1->init;
315+
$node_publisher1->start;
316+
$node_subscriber1->start;
317+
my $publisher_connstr1 =
318+
$node_publisher1->connstr . ' user=regress_test_user dbname=postgres';
319+
my $publisher_connstr2 =
320+
$node_publisher1->connstr
321+
. ' user=regress_test_user dbname=postgres password=secret';
322+
323+
for my $node ($node_publisher1, $node_subscriber1)
324+
{
325+
$node->safe_psql(
326+
'postgres', qq(
327+
CREATE ROLE regress_test_user PASSWORD 'secret' LOGIN REPLICATION;
328+
GRANT CREATE ON DATABASE postgres TO regress_test_user;
329+
GRANT PG_CREATE_SUBSCRIPTION TO regress_test_user;
330+
));
331+
}
332+
333+
$node_publisher1->safe_psql(
334+
'postgres', qq(
335+
SET SESSION AUTHORIZATION regress_test_user;
336+
CREATE PUBLICATION regress_test_pub;
337+
));
338+
$node_subscriber1->safe_psql(
339+
'postgres', qq(
340+
CREATE SUBSCRIPTION regress_test_sub CONNECTION '$publisher_connstr1' PUBLICATION regress_test_pub;
341+
));
342+
343+
# Wait for initial sync to finish
344+
$node_subscriber1->wait_for_subscription_sync($node_publisher1,
345+
'regress_test_sub');
346+
347+
# Setup pg_hba configuration so that logical replication connection without
348+
# password is not allowed.
349+
unlink($node_publisher1->data_dir . '/pg_hba.conf');
350+
$node_publisher1->append_conf('pg_hba.conf',
351+
qq{local all regress_test_user md5});
352+
$node_publisher1->reload;
353+
354+
# Change the subscription owner to a non-superuser
355+
$node_subscriber1->safe_psql(
356+
'postgres', qq(
357+
ALTER SUBSCRIPTION regress_test_sub OWNER TO regress_test_user;
358+
));
359+
360+
# Non-superuser must specify password in the connection string
361+
my ($ret, $stdout, $stderr) = $node_subscriber1->psql(
362+
'postgres', qq(
363+
SET SESSION AUTHORIZATION regress_test_user;
364+
ALTER SUBSCRIPTION regress_test_sub REFRESH PUBLICATION;
365+
));
366+
isnt($ret, 0,
367+
"non zero exit for subscription whose owner is a non-superuser must specify password parameter of the connection string"
368+
);
369+
ok( $stderr =~ m/DETAIL: Non-superusers must provide a password in the connection string./,
370+
'subscription whose owner is a non-superuser must specify password parameter of the connection string'
371+
);
372+
373+
delete $ENV{"PGPASSWORD"};
374+
375+
# It should succeed after including the password parameter of the connection
376+
# string.
377+
($ret, $stdout, $stderr) = $node_subscriber1->psql(
378+
'postgres', qq(
379+
SET SESSION AUTHORIZATION regress_test_user;
380+
ALTER SUBSCRIPTION regress_test_sub CONNECTION '$publisher_connstr2';
381+
ALTER SUBSCRIPTION regress_test_sub REFRESH PUBLICATION;
382+
));
383+
is($ret, 0,
384+
"Non-superuser will be able to refresh the publication after specifying the password parameter of the connection string"
385+
);
306386
done_testing();

0 commit comments

Comments
 (0)