8000 Introduce a psql "\connect -reuse-previous=on|off" option. · linearregression/postgres@cf7e5f5 · GitHub
[go: up one dir, main page]

Skip to content

Commit cf7e5f5

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 aed766a commit cf7e5f5

File tree

3 files changed

+89
-44
lines changed

3 files changed

+89
-44
lines changed

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

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

750750
<varlistentry>
751-
<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>
751+
<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>
752752
<listitem>
753753
<para>
754754
Establishes a new connection to a <productname>PostgreSQL</>
@@ -758,16 +758,19 @@ testdb=&gt;
758758
</para>
759759

760760
<para>
761-
When using positional parameters, if any of
762-
<replaceable class="parameter">dbname</replaceable>,
761+
Where the command omits database name, user, host, or port, the new
762+
connection can reuse values from the previous connection. By default,
763+
values from the previous connection are reused except when processing
764+
a <literal>conninfo</> string. Passing a first argument
765+
of <literal>-reuse-previous=on</>
766+
or <literal>-reuse-previous=off</literal> overrides that default.
767+
When the command neither specifies nor reuses a particular parameter,
768+
the <application>libpq</application> default is used. Specifying any
769+
of <replaceable class="parameter">dbname</replaceable>,
763770
<replaceable class="parameter">username</replaceable>,
764771
<replaceable class="parameter">host</replaceable> or
765-
<replaceable class="parameter">port</replaceable> are omitted or
766-
specified as <literal>-</literal>, the value of that parameter from
767-
the previous connection is used; if there is no previous connection,
768-
the <application>libpq</application> default for the parameter's value
769-
is used. When using <literal>conninfo</> strings, no values from the
770-
previous connection are used for the new connection.
772+
<replaceable class="parameter">port</replaceable>
773+
as <literal>-</literal> is equivalent to omitting that parameter.
771774
</para>
772775

773776
<para>

src/bin/psql/command.c

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ static backslashResult exec_command(const char *cmd,
5858
PQExpBuffer query_buf);
5959
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
6060
int lineno, bool *edited);
61-
static bool do_connect(char *dbname, char *user, char *host, char *port);
61+
static bool do_connect(enum trivalue reuse_previous_specification,
62+
char *dbname, char *user, char *host, char *port);
6263
static bool do_shell(const char *command);
6364
static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
6465
static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf);
@@ -216,12 +217,9 @@ exec_command(const char *cmd,
216217
/*
217218
* \c or \connect -- connect to database using the specified parameters.
218219
*
219-
* \c dbname user host port
220+
* \c [-reuse-previous=BOOL] dbname user host port
220221
*
221-
* If any of these parameters are omitted or specified as '-', the current
222-
* value of the parameter will be used instead. If the parameter has no
223-
* current value, the default value for that parameter will be used. Some
224-
* examples:
222+
* Specifying a parameter as '-' is equivalent to omitting it. Examples:
225223
*
226224
* \c - - hst Connect to current database on current port of host
227225
* "hst" as current user. \c - usr - prt Connect to current database on
@@ -230,17 +228,31 @@ exec_command(const char *cmd,
230228
*/
231229
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
232230
{
231+
static const char prefix[] = "-reuse-previous=";
233232
char *opt1,
234233
*opt2,
235234
*opt3,
236235
*opt4;
236+
enum trivalue reuse_previous;
237237

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

243-
success = do_connect(opt1, opt2, opt3, opt4);
255+
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
244256

245257
free(opt1);
246258
free(opt2);
@@ -1453,34 +1465,57 @@ param_is_newly_set(const char *old_val, const char *new_val)
14531465
/*
14541466
* do_connect -- handler for \connect
14551467
*
1456-
* Connects to a database with given parameters. If there exists an
1457-
* established connection, NULL values will be replaced with the ones
1458-
* in the current connection. Otherwise NULL will be passed for that
1459-
* parameter to PQconnectdbParams(), so the libpq defaults will be used.
1468+
* Connects to a database with given parameters. Absent an established
1469+
* connection, all parameters are required. Given any of -reuse-previous=off,
1470+
* a connection string without -reuse-previous=on, or lack of an established
1471+
* connection, NULL values will pass through to PQconnectdbParams(), so the
1472+
* libpq defaults will be used. Otherwise, NULL values will be replaced with
1473+
* the ones in the current connection.
14601474
*
14611475
* In interactive mode, if connection fails with the given parameters,
14621476
* the old connection will be kept.
14631477
*/
14641478
static bool
1465-
do_connect(char *dbname, char *user, char *host, char *port)
1479+
do_connect(enum trivalue reuse_previous_specification,
1480+
char *dbname, char *user, char *host, char *port)
14661481
{
14671482
PGconn *o_conn = pset.db,
14681483
*n_conn;
14691484
char *password = NULL;
14701485
bool keep_password;
14711486
bool has_connection_string;
1487+
bool reuse_previous;
14721488

1473-
/* grab values from the old connection, unless supplied by caller */
1474-
if (!user)
1489+
has_connection_string = dbname ?
1490+
recognized_connection_string(dbname) : false;
1491+
switch (reuse_previous_specification)
1492+
{
1493+
case TRI_YES:
1494+
reuse_previous = true;
1495+
break;
1496+
case TRI_NO:
1497+
reuse_previous = false;
1498+
break;
1499+
default:
1500+
reuse_previous = !has_connection_string;
1501+
break;
1502+
}
1503+
/* Silently ignore arguments subsequent to a connection string. */
1504+
if (has_connection_string)
1505+
{
1506+
user = NULL;
1507+
host = NULL;
1508+
port = NULL;
1509+
}
1510+
1511+
/* grab missing values from the old connection */
1512+
if (!user && reuse_previous)
14751513
user = PQuser(o_conn);
1476-
if (!host)
1514+
if (!host && reuse_previous)
14771515
host = PQhost(o_conn);
1478-
if (!port)
1516+
if (!port && reuse_previous)
14791517
port = PQport(o_conn);
14801518

1481-
has_connection_string =
1482-
dbname ? recognized_connection_string(dbname) : false;
1483-
14841519
/*
14851520
* Any change in the parameters read above makes us discard the password.
14861521
* We also discard it if we're to use a conninfo rather than the
@@ -1497,10 +1532,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
14971532
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
14981533

14991534
/*
1500-
* Grab dbname from old connection unless supplied by caller. No password
1501-
* discard if this changes: passwords aren't (usually) database-specific.
1535+
* Grab missing dbname from old connection. No password discard if this
1536+
* changes: passwords aren't (usually) database-specific.
15021537
*/
1503-
if (!dbname)
1538+
if (!dbname && reuse_previous)
15041539
dbname = PQdb(o_conn);
15051540

15061541
/*
@@ -1527,20 +1562,27 @@ do_connect(char *dbname, char *user, char *host, char *port)
15271562
#define PARAMS_ARRAY_SIZE 8
15281563
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
15291564
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
1530-
int paramnum = 0;
1565+
int paramnum = -1;
15311566

1532-
keywords[0] = "dbname";
1533-
values[0] = dbname;
1567+
keywords[++paramnum] = "host";
1568+
values[paramnum] = host;
1569+
keywords[++paramnum] = "port";
1570+
values[paramnum] = port;
1571+
keywords[++paramnum] = "user";
1572+
values[paramnum] = user;
15341573

1535-
if (!has_connection_string)
1536-
{
1537-
keywords[++paramnum] = "host";
1538-
values[paramnum] = host;
1539-
keywords[++paramnum] = "port";
1540-
values[paramnum] = port;
1541-
keywords[++paramnum] = "user";
1542-
values[paramnum] = user;
1543-
}
1574+
/*
1575+
* Position in the array matters when the dbname is a connection
1576+
* string, because settings in a connection string override earlier
1577+
* array entries only. Thus, user= in the connection string always
1578+
* takes effect, but client_encoding= often will not.
1579+
*
1580+
* If you change this code, also change the initial-connection code in
1581+
* main(). For no good reason, a connection string password= takes
1582+
* precedence in main() but not here.
1583+
*/
1584+
keywords[++paramnum] = "dbname";
1585+
values[paramnum] = dbname;
15441586
keywords[++paramnum] = "password";
15451587
values[paramnum] = password;
15461588
keywords[++paramnum] = "fallback_application_name";

src/bin/psql/startup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ main(int argc, char *argv[])
183183
values[2] = options.username;
184184
keywords[3] = "password";
185185
values[3] = password;
186-
keywords[4] = "dbname";
186+
keywords[4] = "dbname"; /* see do_connect() */
187187
values[4] = (options.action == ACT_LIST_DB &&
188188
options.dbname == NULL) ?
189189
"postgres" : options.dbname;

0 commit comments

Comments
 (0)
0