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

Commit 9d924e9

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 984e5be commit 9d924e9

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
@@ -825,7 +825,7 @@ testdb=>
825825
</varlistentry>
826826

827827
<varlistentry>
828-
<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>
828+
<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>
829829
<listitem>
830830
<para>
831831
Establishes a new connection to a <productname>PostgreSQL</>
@@ -835,16 +835,19 @@ testdb=&gt;
835835
</para>
836836

837837
<para>
838-
When using positional parameters, if any of
839-
<replaceable class="parameter">dbname</replaceable>,
838+
Where the command omits database name, user, host, or port, the new
839+
connection can reuse values from the previous connection. By default,
840+
values from the previous connection are reused except when processing
841+
a <literal>conninfo</> string. Passing a first argument
842+
of <literal>-reuse-previous=on</>
843+
or <literal>-reuse-previous=off</literal> overrides that default.
844+
When the command neither specifies nor reuses a particular parameter,
845+
the <application>libpq</application> default is used. Specifying any
846+
of <replaceable class="parameter">dbname</replaceable>,
840847
<replaceable class="parameter">username</replaceable>,
841848
<replaceable class="parameter">host</replaceable> or
842-
<replaceable class="parameter">port</replaceable> are omitted or
843-
specified as <literal>-</literal>, the value of that parameter from
844-
the previous connection is used; if there is no previous connection,
845-
the <application>libpq</application> default for the parameter's value
846-
is used. When using <literal>conninfo</> strings, no values from the
847-
previous connection are used for the new connection.
849+
<replaceable class="parameter">port</replaceable>
850+
as <literal>-</literal> is equivalent to omitting that parameter.
848851
</para>
849852

850853
<para>

src/bin/psql/command.c

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ static backslashResult exec_command(const char *cmd,
6565
PQExpBuffer query_buf);
6666
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
6767
int lineno, bool *edited);
68-
static bool do_connect(char *dbname, char *user, char *host, char *port);
68+
static bool do_connect(enum trivalue reuse_previous_specification,
69+
char *dbname, char *user, char *host, char *port);
6970
static bool do_shell(const char *command);
7071
static bool do_watch(PQExpBuffer query_buf, double sleep);
7172
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
@@ -231,12 +232,9 @@ exec_command(const char *cmd,
231232
/*
232233
* \c or \connect -- connect to database using the specified parameters.
233234
*
234-
* \c dbname user host port
235+
* \c [-reuse-previous=BOOL] dbname user host port
235236
*
236-
* If any of these parameters are omitted or specified as '-', the current
237-
* value of the parameter will be used instead. If the parameter has no
238-
* current value, the default value for that parameter will be used. Some
239-
* examples:
237+
* Specifying a parameter as '-' is equivalent to omitting it. Examples:
240238
*
241239
* \c - - hst Connect to current database on current port of host
242240
* "hst" as current user. \c - usr - prt Connect to current database on
@@ -245,17 +243,31 @@ exec_command(const char *cmd,
245243
*/
246244
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
247245
{
246+
static const char prefix[] = "-reuse-previous=";
248247
char *opt1,
249248
*opt2,
250249
*opt3,
251250
*opt4;
251+
enum trivalue reuse_previous;
252252

253253
opt1 = read_connect_arg(scan_state);
254+
if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
255+
{
256+
reuse_previous =
257+
ParseVariableBool(opt1 + sizeof(prefix) - 1, prefix) ?
258+
TRI_YES : TRI_NO;
259+
260+
free(opt1);
261+
opt1 = read_connect_arg(scan_state);
262+
}
263+
else
264+
reuse_previous = TRI_DEFAULT;
265+
254266
opt2 = read_connect_arg(scan_state);
255267
opt3 = read_connect_arg(scan_state);
256268
opt4 = read_connect_arg(scan_state);
257269

258-
success = do_connect(opt1, opt2, opt3, opt4);
270+
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
259271

260272
free(opt1);
261273
free(opt2);
@@ -1754,22 +1766,25 @@ param_is_newly_set(const char *old_val, const char *new_val)
17541766
/*
17551767
* do_connect -- handler for \connect
17561768
*
1757-
* Connects to a database with given parameters. If there exists an
1758-
* established connection, NULL values will be replaced with the ones
1759-
* in the current connection. Otherwise NULL will be passed for that
1760-
* parameter to PQconnectdbParams(), so the libpq defaults will be used.
1769+
* Connects to a database with given parameters. Absent an established
1770+
* connection, all parameters are required. Given -reuse-previous=off or a
1771+
* connection string without -reuse-previous=on, NULL values will pass through
1772+
* to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL
1773+
* values will be replaced with the ones in the current connection.
17611774
*
17621775
* In interactive mode, if connection fails with the given parameters,
17631776
* the old connection will be kept.
17641777
*/
17651778
static bool
1766-
do_connect(char *dbname, char *user, char *host, char *port)
1779+
do_connect(enum trivalue reuse_previous_specification,
1780+
char *dbname, char *user, char *host, char *port)
17671781
{
17681782
PGconn *o_conn = pset.db,
17691783
*n_conn;
17701784
char *password = NULL;
17711785
bool keep_password;
17721786
bool has_connection_string;
1787+
bool reuse_previous;
17731788

17741789
if (!o_conn && (!dbname || !user || !host || !port))
17751790
{
@@ -1783,17 +1798,36 @@ do_connect(char *dbname, char *user, char *host, char *port)
17831798
return false;
17841799
}
17851800

1786-
/* grab values from the old connection, unless supplied by caller */
1787-
if (!user)
1801+
has_connection_string = dbname ?
1802+
recognized_connection_string(dbname) : false;
1803+
switch (reuse_previous_specification)
1804+
{
1805+
case TRI_YES:
1806+
reuse_previous = true;
1807+
break;
1808+
case TRI_NO:
1809+
reuse_previous = false;
1810+
break;
1811+
default:
1812+
reuse_previous = !has_connection_string;
1813+
break;
1814+
}
1815+
/* Silently ignore arguments subsequent to a connection string. */
1816+
if (has_connection_string)
1817+
{
1818+
user = NULL;
1819+
host = NULL;
1820+
port = NULL;
1821+
}
1822+
1823+
/* grab missing values from the old connection */
1824+
if (!user && reuse_previous)
17881825
user = PQuser(o_conn);
1789-
if (!host)
1826+
if (!host && reuse_previous)
17901827
host = PQhost(o_conn);
1791-
if (!port)
1828+
if (!port && reuse_previous)
17921829
port = PQport(o_conn);
17931830

1794-
has_connection_string =
1795-
dbname ? recognized_connection_string(dbname) : false;
1796-
17971831
/*
17981832
* Any change in the parameters read above makes us discard the password.
17991833
* We also discard it if we're to use a conninfo rather than the
@@ -1808,10 +1842,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
18081842
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
18091843

18101844
/*
1811-
* Grab dbname from old connection unless supplied by caller. No password
1812-
* discard if this changes: passwords aren't (usually) database-specific.
1845+
* Grab missing dbname from old connection. No password discard if this
1846+
* changes: passwords aren't (usually) database-specific.
18131847
*/
1814-
if (!dbname)
1848+
if (!dbname && reuse_previous)
18151849
dbname = PQdb(o_conn);
18161850

18171851
/*
@@ -1842,20 +1876,27 @@ do_connect(char *dbname, char *user, char *host, char *port)
18421876
#define PARAMS_ARRAY_SIZE 8
18431877
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
18441878
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
1845-
int paramnum = 0;
1879+
int paramnum = -1;
18461880

1847-
keywords[0] = "dbname";
1848-
values[0] = dbname;
1881+
keywords[++paramnum] = "host";
1882+
values[paramnum] = host;
1883+
keywords[++paramnum] = "port";
1884+
values[paramnum] = port;
1885+
keywords[++paramnum] = "user";
1886+
values[paramnum] = user;
18491887

1850-
if (!has_connection_string)
1851-
{
1852-
keywords[++paramnum] = "host";
1853-
values[paramnum] = host;
1854-
keywords[++paramnum] = "port";
1855-
values[paramnum] = port;
1856-
keywords[++paramnum] = "user";
1857-
values[paramnum] = user;
1858-
}
1888+
/*
1889+
* Position in the array matters when the dbname is a connection
1890+
* string, because settings in a connection string override earlier
1891+
* array entries only. Thus, user= in the connection string always
1892+
* takes effect, but client_encoding= often will not.
1893+
*
1894+
* If you change this code, also change the initial-connection code in
1895+
* main(). For no good reason, a connection string password= takes
1896+
* precedence in main() but not here.
1897+
*/
1898+
keywords[++paramnum] = "dbname";
1899+
values[paramnum] = dbname;
18591900
keywords[++paramnum] = "password";
18601901
values[paramnum] = password;
18611902
keywords[++paramnum] = "fallback_application_name";

src/bin/psql/startup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ main(int argc, char *argv[])
227227
values[2] = options.username;
228228
keywords[3] = "password";
229229
values[3] = password;
230-
keywords[4] = "dbname";
230+
keywords[4] = "dbname"; /* see do_connect() */
231231
values[4] = (options.list_dbs && options.dbname == NULL) ?
232232
"postgres" : options.dbname;
233233
keywords[5] = "fallback_application_name";

0 commit comments

Comments
 (0)