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

Skip to content

Commit afabfcc

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 fed83cd commit afabfcc

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
@@ -781,7 +781,7 @@ testdb=>
781781
</varlistentry>
782782

783783
<varlistentry>
784-
<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>
784+
<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>
785785
<listitem>
786786
<para>
787787
Establishes a new connection to a <productname>PostgreSQL</>
@@ -791,16 +791,19 @@ testdb=&gt;
791791
</para>
792792

793793
<para>
794-
When using positional parameters, if any of
795-
<replaceable class="parameter">dbname</replaceable>,
794+
Where the command omits database name, user, host, or port, the new
795+
connection can reuse values from the previous connection. By default,
796+
values from the previous connection are reused except when processing
797+
a <literal>conninfo</> string. Passing a first argument
798+
of <literal>-reuse-previous=on</>
799+
or <literal>-reuse-previous=off</literal> overrides that default.
800+
When the command neither specifies nor reuses a particular parameter,
801+
the <application>libpq</application> default is used. Specifying any
802+
of <replaceable class="parameter">dbname</replaceable>,
796803
<replaceable class="parameter">username</replaceable>,
797804
<replaceable class="parameter">host</replaceable> or
798-
<replaceable class="parameter">port</replaceable> are omitted or
799-
specified as <literal>-</literal>, the value of that parameter from
800-
the previous connection is used; if there is no previous connection,
801-
the <application>libpq</application> default for the parameter's value
802-
is used. When using <literal>conninfo</> strings, no values from the
803-
previous connection are used for the new connection.
805+
<replaceable class="parameter">port</replaceable>
806+
as <literal>-</literal> is equivalent to omitting that parameter.
804807
</para>
805808

806809
<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);
@@ -220,12 +221,9 @@ exec_command(const char *cmd,
220221
/*
221222
* \c or \connect -- connect to database using the specified parameters.
222223
*
223-
* \c dbname user host port
224+
* \c [-reuse-previous=BOOL] dbname user host port
224225
*
225-
* If any of these parameters are omitted or specified as '-', the current
226-
* value of the parameter will be used instead. If the parameter has no
227-
* current value, the default value for that parameter will be used. Some
228-
* examples:
226+
* Specifying a parameter as '-' is equivalent to omitting it. Examples:
229227
*
230228
* \c - - hst Connect to current database on current port of host
231229
* "hst" as current user. \c - usr - prt Connect to current database on
@@ -234,17 +232,31 @@ exec_command(const char *cmd,
234232
*/
235233
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
236234
{
235+
static const char prefix[] = "-reuse-previous=";
237236
char *opt1,
238237
*opt2,
239238
*opt3,
240239
*opt4;
240+
enum trivalue reuse_previous;
241241

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

247-
success = do_connect(opt1, opt2, opt3, opt4);
259+
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
248260

249261
free(opt1);
250262
free(opt2);
@@ -1581,22 +1593,25 @@ param_is_newly_set(const char *old_val, const char *new_val)
15811593
/*
15821594
* do_connect -- handler for \connect
15831595
*
1584-
* Connects to a database with given parameters. If there exists an
1585-
* established connection, NULL values will be replaced with the ones
1586-
* in the current connection. Otherwise NULL will be passed for that
1587-
* parameter to PQconnectdbParams(), so the libpq defaults will be used.
1596+
* Connects to a database with given parameters. Absent an established
1597+
* connection, all parameters are required. Given -reuse-previous=off or a
1598+
* connection string without -reuse-previous=on, NULL values will pass through
1599+
* to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL
1600+
* values will be replaced with the ones in the current connection.
15881601
*
15891602
* In interactive mode, if connection fails with the given parameters,
15901603
* the old connection will be kept.
15911604
*/
15921605
static bool
1593-
do_connect(char *dbname, char *user, char *host, char *port)
1606+
do_connect(enum trivalue reuse_previous_specification,
1607+
char *dbname, char *user, char *host, char *port)
15941608
{
15951609
PGconn *o_conn = pset.db,
15961610
*n_conn;
15971611
char *password = NULL;
15981612
bool keep_password;
15991613
bool has_connection_string;
1614+
bool reuse_previous;
16001615

16011616
if (!o_conn && (!dbname || !user || !host || !port))
16021617
{
@@ -1610,17 +1625,36 @@ do_connect(char *dbname, char *user, char *host, char *port)
16101625
return false;
16111626
}
16121627

1613-
/* grab values from the old connection, unless supplied by caller */
1614-
if (!user)
1628+
has_connection_string = dbname ?
1629+
recognized_connection_string(dbname) : false;
1630+
switch (reuse_previous_specification)
1631+
{
1632+
case TRI_YES:
1633+
reuse_previous = true;
1634+
break;
1635+
case TRI_NO:
1636+
reuse_previous = false;
1637+
break;
1638+
default:
1639+
reuse_previous = !has_connection_string;
1640+
break;
1641+
}
1642+
/* Silently ignore arguments subsequent to a connection string. */
1643+
if (has_connection_string)
1644+
{
1645+
user = NULL;
1646+
host = NULL;
1647+
port = NULL;
1648+
}
1649+
1650+
/* grab missing values from the old connection */
1651+
if (!user && reuse_previous)
16151652
user = PQuser(o_conn);
1616-
if (!host)
1653+
if (!host && reuse_previous)
16171654
host = PQhost(o_conn);
1618-
if (!port)
1655+
if (!port && reuse_previous)
16191656
port = PQport(o_conn);
16201657

1621-
has_connection_string =
1622-
dbname ? recognized_connection_string(dbname) : false;
1623-
16241658
/*
16251659
* Any change in the parameters read above makes us discard the password.
16261660
* We also discard it if we're to use a conninfo rather than the
@@ -1637,10 +1671,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
16371671
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
16381672

16391673
/*
1640-
* Grab dbname from old connection unless supplied by caller. No password
1641-
* discard if this changes: passwords aren't (usually) database-specific.
1674+
* Grab missing dbname from old connection. No password discard if this
1675+
* changes: passwords aren't (usually) database-specific.
16421676
*/
1643-
if (!dbname)
1677+
if (!dbname && reuse_previous)
16441678
dbname = PQdb(o_conn);
16451679

16461680
/*
@@ -1671,20 +1705,27 @@ do_connect(char *dbname, char *user, char *host, char *port)
16711705
#define PARAMS_ARRAY_SIZE 8
16721706
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
16731707
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
1674-
int paramnum = 0;
1708+
int paramnum = -1;
16751709

1676-
keywords[0] = "dbname";
1677-
values[0] = dbname;
1710+
keywords[++paramnum] = "host";
1711+
values[paramnum] = host;
1712+
keywords[++paramnum] = "port";
1713+
values[paramnum] = port;
1714+
keywords[++paramnum] = "user";
1715+
values[paramnum] = user;
16781716

1679-
if (!has_connection_string)
1680-
{
1681-
keywords[++paramnum] = "host";
1682-
values[paramnum] = host;
1683-
keywords[++paramnum] = "port";
1684-
values[paramnum] = port;
1685-
keywords[++paramnum] = "user";
1686-
values[paramnum] = user;
1687-
}
1717+
/*
1718+
* Position in the array matters when the dbname is a connection
1719+
* string, because settings in a connection string override earlier
1720+
* array entries only. Thus, user= in the connection string always
1721+
* takes effect, but client_encoding= often will not.
1722+
*
1723+
* If you change this code, also change the initial-connection code in
1724+
* main(). For no good reason, a connection string password= takes
1725+
* precedence in main() but not here.
1726+
*/
1727+
keywords[++paramnum] = "dbname";
1728+
values[paramnum] = dbname;
16881729
keywords[++paramnum] = "password";
16891730
values[paramnum] = password;
16901731
keywords[++paramnum] = "fallback_application_name";

src/bin/psql/startup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ main(int argc, char *argv[])
204204
values[2] = options.username;
205205
keywords[3] = "password";
206206
values[3] = password;
207-
keywords[4] = "dbname";
207+
keywords[4] = "dbname"; /* see do_connect() */
208208
values[4] = (options.action == ACT_LIST_DB &&
209209
options.dbname == NULL) ?
210210
"postgres" : options.dbname;

0 commit comments

Comments
 (0)
0