Skip to content

Commit dfb2d80

Browse files
committed
Introduce a psql "\connect -reuse-previous=on|off" option.
The decision to reuse values of parameters from a previous connection has been based on whether the new target is a conninfo string. Add this means of overriding that default. This feature arose as one component of a fix for security vulnerabilities in pg_dump, pg_dumpall, and pg_upgrade, so back-patch to 9.1 (all supported versions). In 9.3 and later, comment paragraphs that required update had already-incorrect claims about behavior when no connection is open; fix those problems. Security: CVE-2016-5424
1 parent a44d713 commit dfb2d80

File tree

3 files changed

+88
-44
lines changed

3 files changed

+88
-44
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ testdb=>
775775
</varlistentry>
776776

777777
<varlistentry>
778-
<term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> </literal></term>
778+
<term><literal>\c</literal> or <literal>\connect [ -reuse-previous=<replaceable class="parameter">on|off</replaceable> ] [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] | <replaceable class="parameter">conninfo</replaceable> ]</literal></term>
779779
<listitem>
780780
<para>
781781
Establishes a new connection to a <productname>PostgreSQL</>
@@ -785,16 +785,19 @@ testdb=&gt;
785785
</para>
786786

787787
<para>
788-
When using positional parameters, if any of
789-
<replaceable class="parameter">dbname</replaceable>,
788+
Where the command omits database name, user, host, or port, the new
789+
connection can reuse values from the previous connection. By default,
790+
values from the previous connection are reused except when processing
791+
a <literal>conninfo</> string. Passing a first argument
792+
of <literal>-reuse-previous=on</>
793+
or <literal>-reuse-previous=off</literal> overrides that default.
794+
When the command neither specifies nor reuses a particular parameter,
795+
the <application>libpq</application> default is used. Specifying any
796+
of <replaceable class="parameter">dbname</replaceable>,
790797
<replaceable class="parameter">username</replaceable>,
791798
<replaceable class="parameter">host</replaceable> or
792-
<replaceable class="parameter">port</replaceable> are omitted or
793-
specified as <literal>-</literal>, the value of that parameter from
794-
the previous connection is used; if there is no previous connection,
795-
the <application>libpq</application> default for the parameter's value
796-
is used. When using <literal>conninfo</> strings, no values from the
797-
previous connection are used for the new connection.
799+
<replaceable class="parameter">port</replaceable>
800+
as <literal>-</literal> is equivalent to omitting that parameter.
798801
</para>
799802

800803
<para>

src/bin/psql/command.c

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ static backslashResult exec_command(const char *cmd,
5959
PQExpBuffer query_buf);
6060
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
6161
int lineno, bool *edited);
62-
static bool do_connect(char *dbname, char *user, char *host, char *port);
62+
static bool do_connect(enum trivalue reuse_previous_specification,
63+
char *dbname, char *user, char *host, char *port);
6364
static bool do_shell(const char *command);
6465
static bool do_watch(PQExpBuffer query_buf, long sleep);
6566
static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
@@ -218,12 +219,9 @@ exec_command(const char *cmd,
218219
/*
219220
* \c or \connect -- connect to database using the specified parameters.
220221
*
221-
* \c dbname user host port
222+
* \c [-reuse-previous=BOOL] dbname user host port
222223
*
223-
* If any of these parameters are omitted or specified as '-', the current
224-
* value of the parameter will be used instead. If the parameter has no
225-
* current value, the default value for that parameter will be used. Some
226-
* examples:
224+
* Specifying a parameter as '-' is equivalent to omitting it. Examples:
227225
*
228226
* \c - - hst Connect to current database on current port of host
229227
* "hst" as current user. \c - usr - prt Connect to current database on
@@ -232,17 +230,31 @@ exec_command(const char *cmd,
232230
*/
233231
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
234232
{
233+
static const char prefix[] = "-reuse-previous=";
235234
char *opt1,
236235
*opt2,
237236
*opt3,
238237
*opt4;
238+
enum trivalue reuse_previous;
239239

240240
opt1 = read_connect_arg(scan_state);
241+
if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
242+
{
243+
reuse_previous =
244+
ParseVariableBool(opt1 + sizeof(prefix) - 1, prefix) ?
245+
TRI_YES : TRI_NO;
246+
247+
free(opt1);
248+
opt1 = read_connect_arg(scan_state);
249+
}
250+
else
251+
reuse_previous = TRI_DEFAULT;
252+
241253
opt2 = read_connect_arg(scan_state);
242254
opt3 = read_connect_arg(scan_state);
243255
opt4 = read_connect_arg(scan_state);
244256

245-
success = do_connect(opt1, opt2, opt3, opt4);
257+
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
246258

247259
free(opt1);
248260
free(opt2);
@@ -1545,22 +1557,25 @@ param_is_newly_set(const char *old_val, const char *new_val)
15451557
/*
15461558
* do_connect -- handler for \connect
15471559
*
1548-
* Connects to a database with given parameters. If there exists an
1549-
* established connection, NULL values will be replaced with the ones
1550-
* in the current connection. Otherwise NULL will be passed for that
1551-
* parameter to PQconnectdbParams(), so the libpq defaults will be used.
1560+
* Connects to a database with given parameters. Absent an established
1561+
* connection, all parameters are required. Given -reuse-previous=off or a
1562+
* connection string without -reuse-previous=on, NULL values will pass through
1563+
* to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL
1564+
* values will be replaced with the ones in the current connection.
15521565
*
15531566
* In interactive mode, if connection fails with the given parameters,
15541567
* the old connection will be kept.
15551568
*/
15561569
static bool
1557-
do_connect(char *dbname, char *user, char *host, char *port)
1570+
do_connect(enum trivalue reuse_previous_specification,
1571+
char *dbname, char *user, char *host, char *port)
15581572
{
15591573
PGconn *o_conn = pset.db,
15601574
*n_conn;
15611575
char *password = NULL;
15621576
bool keep_password;
15631577
bool has_connection_string;
1578+
bool reuse_previous;
15641579

15651580
if (!o_conn && (!dbname || !user || !host || !port))
15661581
{
@@ -1574,17 +1589,36 @@ do_connect(char *dbname, char *user, char *host, char *port)
15741589
return false;
15751590
}
15761591

1577-
/* grab values from the old connection, unless supplied by caller */
1578-
if (!user)
1592+
has_connection_string = dbname ?
1593+
recognized_connection_string(dbname) : false;
1594+
switch (reuse_previous_specification)
1595+
{
1596+
case TRI_YES:
1597+
reuse_previous = true;
1598+
break;
1599+
case TRI_NO:
1600+
reuse_previous = false;
1601+
break;
1602+
default:
1603+
reuse_previous = !has_connection_string;
1604+
break;
1605+
}
1606+
/* Silently ignore arguments subsequent to a connection string. */
1607+
if (has_connection_string)
1608+
{
1609+
user = NULL;
1610+
host = NULL;
1611+
port = NULL;
1612+
}
1613+
1614+
/* grab missing values from the old connection */
1615+
if (!user && reuse_previous)
15791616
user = PQuser(o_conn);
1580-
if (!host)
1617+
if (!host && reuse_previous)
15811618
host = PQhost(o_conn);
1582-
if (!port)
1619+
if (!port && reuse_previous)
15831620
port = PQport(o_conn);
15841621

1585-
has_connection_string =
1586-
dbname ? recognized_connection_string(dbname) : false;
1587-
15881622
/*
15891623
* Any change in the parameters read above makes us discard the password.
15901624
* We also discard it if we're to use a conninfo rather than the
@@ -1601,10 +1635,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
16011635
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
16021636

16031637
/*
1604-
* Grab dbname from old connection unless supplied by caller. No password
1605-
* discard if this changes: passwords aren't (usually) database-specific.
1638+
* Grab missing dbname from old connection. No password discard if this
1639+
* changes: passwords aren't (usually) database-specific.
16061640
*/
1607-
if (!dbname)
1641+
if (!dbname && reuse_previous)
16081642
dbname = PQdb(o_conn);
16091643

16101644
/*
@@ -1635,20 +1669,27 @@ do_connect(char *dbname, char *user, char *host, char *port)
16351669
#define PARAMS_ARRAY_SIZE 8
16361670
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
16371671
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
1638-
int paramnum = 0;
1672+
int paramnum = -1;
16391673

1640-
keywords[0] = "dbname";
1641-
values[0] = dbname;
1674+
keywords[++paramnum] = "host";
1675+
values[paramnum] = host;
1676+
keywords[++paramnum] = "port";
1677+
values[paramnum] = port;
1678+
keywords[++paramnum] = "user";
1679+
values[paramnum] = user;
16421680

1643-
if (!has_connection_string)
1644-
{
1645-
keywords[++paramnum] = "host";
1646-
values[paramnum] = host;
1647-
keywords[++paramnum] = "port";
1648-
values[paramnum] = port;
1649-
keywords[++paramnum] = "user";
1650-
values[paramnum] = user;
1651-
}
1681+
/*
1682+
* Position in the array matters when the dbname is a connection
1683+
* string, because settings in a connection string override earlier
1684+
* array entries only. Thus, user= in the connection string always
1685+
* takes effect, but client_encoding= often will not.
1686+
*
1687+
* If you change this code, also change the initial-connection code in
1688+
* main(). For no good reason, a connection string password= takes
1689+
* precedence in main() but not here.
1690+
*/
1691+
keywords[++paramnum] = "dbname";
1692+
values[paramnum] = dbname;
16521693
keywords[++paramnum] = "password";
16531694
values[paramnum] = password;
16541695
keywords[++paramnum] = "fallback_application_name";

src/bin/psql/startup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ main(int argc, char *argv[])
210210
values[2] = options.username;
211211
keywords[3] = "password";
212212
values[3] = password;
213-
keywords[4] = "dbname";
213+
keywords[4] = "dbname"; /* see do_connect() */
214214
values[4] = (options.action == ACT_LIST_DB &&
215215
options.dbname == NULL) ?
216216
"postgres" : options.dbname;

0 commit comments

Comments
 (0)