mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-05 08:19:20 +02:00
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
This commit is contained in:
parent
db951dd195
commit
6655c07574
@ -799,7 +799,7 @@ testdb=>
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<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>
|
<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>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Establishes a new connection to a <productname>PostgreSQL</>
|
Establishes a new connection to a <productname>PostgreSQL</>
|
||||||
@ -809,16 +809,19 @@ testdb=>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
When using positional parameters, if any of
|
Where the command omits database name, user, host, or port, the new
|
||||||
<replaceable class="parameter">dbname</replaceable>,
|
connection can reuse values from the previous connection. By default,
|
||||||
|
values from the previous connection are reused except when processing
|
||||||
|
a <literal>conninfo</> string. Passing a first argument
|
||||||
|
of <literal>-reuse-previous=on</>
|
||||||
|
or <literal>-reuse-previous=off</literal> overrides that default.
|
||||||
|
When the command neither specifies nor reuses a particular parameter,
|
||||||
|
the <application>libpq</application> default is used. Specifying any
|
||||||
|
of <replaceable class="parameter">dbname</replaceable>,
|
||||||
<replaceable class="parameter">username</replaceable>,
|
<replaceable class="parameter">username</replaceable>,
|
||||||
<replaceable class="parameter">host</replaceable> or
|
<replaceable class="parameter">host</replaceable> or
|
||||||
<replaceable class="parameter">port</replaceable> are omitted or
|
<replaceable class="parameter">port</replaceable>
|
||||||
specified as <literal>-</literal>, the value of that parameter from
|
as <literal>-</literal> is equivalent to omitting that parameter.
|
||||||
the previous connection is used; if there is no previous connection,
|
|
||||||
the <application>libpq</application> default for the parameter's value
|
|
||||||
is used. When using <literal>conninfo</> strings, no values from the
|
|
||||||
previous connection are used for the new connection.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -56,7 +56,8 @@ static backslashResult exec_command(const char *cmd,
|
|||||||
PQExpBuffer query_buf);
|
PQExpBuffer query_buf);
|
||||||
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
||||||
int lineno, bool *edited);
|
int lineno, bool *edited);
|
||||||
static bool do_connect(char *dbname, char *user, char *host, char *port);
|
static bool do_connect(enum trivalue reuse_previous_specification,
|
||||||
|
char *dbname, char *user, char *host, char *port);
|
||||||
static bool do_shell(const char *command);
|
static bool do_shell(const char *command);
|
||||||
static bool do_watch(PQExpBuffer query_buf, long sleep);
|
static bool do_watch(PQExpBuffer query_buf, long sleep);
|
||||||
static bool lookup_function_oid(const char *desc, Oid *foid);
|
static bool lookup_function_oid(const char *desc, Oid *foid);
|
||||||
@ -217,12 +218,9 @@ exec_command(const char *cmd,
|
|||||||
/*
|
/*
|
||||||
* \c or \connect -- connect to database using the specified parameters.
|
* \c or \connect -- connect to database using the specified parameters.
|
||||||
*
|
*
|
||||||
* \c dbname user host port
|
* \c [-reuse-previous=BOOL] dbname user host port
|
||||||
*
|
*
|
||||||
* If any of these parameters are omitted or specified as '-', the current
|
* Specifying a parameter as '-' is equivalent to omitting it. Examples:
|
||||||
* value of the parameter will be used instead. If the parameter has no
|
|
||||||
* current value, the default value for that parameter will be used. Some
|
|
||||||
* examples:
|
|
||||||
*
|
*
|
||||||
* \c - - hst Connect to current database on current port of host
|
* \c - - hst Connect to current database on current port of host
|
||||||
* "hst" as current user. \c - usr - prt Connect to current database on
|
* "hst" as current user. \c - usr - prt Connect to current database on
|
||||||
@ -231,17 +229,31 @@ exec_command(const char *cmd,
|
|||||||
*/
|
*/
|
||||||
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
||||||
{
|
{
|
||||||
|
static const char prefix[] = "-reuse-previous=";
|
||||||
char *opt1,
|
char *opt1,
|
||||||
*opt2,
|
*opt2,
|
||||||
*opt3,
|
*opt3,
|
||||||
*opt4;
|
*opt4;
|
||||||
|
enum trivalue reuse_previous;
|
||||||
|
|
||||||
opt1 = read_connect_arg(scan_state);
|
opt1 = read_connect_arg(scan_state);
|
||||||
|
if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0)
|
||||||
|
{
|
||||||
|
reuse_previous =
|
||||||
|
ParseVariableBool(opt1 + sizeof(prefix) - 1, prefix) ?
|
||||||
|
TRI_YES : TRI_NO;
|
||||||
|
|
||||||
|
free(opt1);
|
||||||
|
opt1 = read_connect_arg(scan_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
reuse_previous = TRI_DEFAULT;
|
||||||
|
|
||||||
opt2 = read_connect_arg(scan_state);
|
opt2 = read_connect_arg(scan_state);
|
||||||
opt3 = read_connect_arg(scan_state);
|
opt3 = read_connect_arg(scan_state);
|
||||||
opt4 = read_connect_arg(scan_state);
|
opt4 = read_connect_arg(scan_state);
|
||||||
|
|
||||||
success = do_connect(opt1, opt2, opt3, opt4);
|
success = do_connect(reuse_previous, opt1, opt2, opt3, opt4);
|
||||||
|
|
||||||
free(opt1);
|
free(opt1);
|
||||||
free(opt2);
|
free(opt2);
|
||||||
@ -1599,22 +1611,25 @@ param_is_newly_set(const char *old_val, const char *new_val)
|
|||||||
/*
|
/*
|
||||||
* do_connect -- handler for \connect
|
* do_connect -- handler for \connect
|
||||||
*
|
*
|
||||||
* Connects to a database with given parameters. If there exists an
|
* Connects to a database with given parameters. Absent an established
|
||||||
* established connection, NULL values will be replaced with the ones
|
* connection, all parameters are required. Given -reuse-previous=off or a
|
||||||
* in the current connection. Otherwise NULL will be passed for that
|
* connection string without -reuse-previous=on, NULL values will pass through
|
||||||
* parameter to PQconnectdbParams(), so the libpq defaults will be used.
|
* to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL
|
||||||
|
* values will be replaced with the ones in the current connection.
|
||||||
*
|
*
|
||||||
* In interactive mode, if connection fails with the given parameters,
|
* In interactive mode, if connection fails with the given parameters,
|
||||||
* the old connection will be kept.
|
* the old connection will be kept.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
do_connect(char *dbname, char *user, char *host, char *port)
|
do_connect(enum trivalue reuse_previous_specification,
|
||||||
|
char *dbname, char *user, char *host, char *port)
|
||||||
{
|
{
|
||||||
PGconn *o_conn = pset.db,
|
PGconn *o_conn = pset.db,
|
||||||
*n_conn;
|
*n_conn;
|
||||||
char *password = NULL;
|
char *password = NULL;
|
||||||
bool keep_password;
|
bool keep_password;
|
||||||
bool has_connection_string;
|
bool has_connection_string;
|
||||||
|
bool reuse_previous;
|
||||||
|
|
||||||
if (!o_conn && (!dbname || !user || !host || !port))
|
if (!o_conn && (!dbname || !user || !host || !port))
|
||||||
{
|
{
|
||||||
@ -1628,16 +1643,35 @@ do_connect(char *dbname, char *user, char *host, char *port)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* grab values from the old connection, unless supplied by caller */
|
has_connection_string = dbname ?
|
||||||
if (!user)
|
recognized_connection_string(dbname) : false;
|
||||||
user = PQuser(o_conn);
|
switch (reuse_previous_specification)
|
||||||
if (!host)
|
{
|
||||||
host = PQhost(o_conn);
|
case TRI_YES:
|
||||||
if (!port)
|
reuse_previous = true;
|
||||||
port = PQport(o_conn);
|
break;
|
||||||
|
case TRI_NO:
|
||||||
|
reuse_previous = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reuse_previous = !has_connection_string;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Silently ignore arguments subsequent to a connection string. */
|
||||||
|
if (has_connection_string)
|
||||||
|
{
|
||||||
|
user = NULL;
|
||||||
|
host = NULL;
|
||||||
|
port = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
has_connection_string =
|
/* grab missing values from the old connection */
|
||||||
dbname ? recognized_connection_string(dbname) : false;
|
if (!user && reuse_previous)
|
||||||
|
user = PQuser(o_conn);
|
||||||
|
if (!host && reuse_previous)
|
||||||
|
host = PQhost(o_conn);
|
||||||
|
if (!port && reuse_previous)
|
||||||
|
port = PQport(o_conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Any change in the parameters read above makes us discard the password.
|
* Any change in the parameters read above makes us discard the password.
|
||||||
@ -1655,10 +1689,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
|
|||||||
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
|
(port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grab dbname from old connection unless supplied by caller. No password
|
* Grab missing dbname from old connection. No password discard if this
|
||||||
* discard if this changes: passwords aren't (usually) database-specific.
|
* changes: passwords aren't (usually) database-specific.
|
||||||
*/
|
*/
|
||||||
if (!dbname)
|
if (!dbname && reuse_previous)
|
||||||
dbname = PQdb(o_conn);
|
dbname = PQdb(o_conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1689,20 +1723,27 @@ do_connect(char *dbname, char *user, char *host, char *port)
|
|||||||
#define PARAMS_ARRAY_SIZE 8
|
#define PARAMS_ARRAY_SIZE 8
|
||||||
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
|
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
|
||||||
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
|
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
|
||||||
int paramnum = 0;
|
int paramnum = -1;
|
||||||
|
|
||||||
keywords[0] = "dbname";
|
keywords[++paramnum] = "host";
|
||||||
values[0] = dbname;
|
values[paramnum] = host;
|
||||||
|
keywords[++paramnum] = "port";
|
||||||
|
values[paramnum] = port;
|
||||||
|
keywords[++paramnum] = "user";
|
||||||
|
values[paramnum] = user;
|
||||||
|
|
||||||
if (!has_connection_string)
|
/*
|
||||||
{
|
* Position in the array matters when the dbname is a connection
|
||||||
keywords[++paramnum] = "host";
|
* string, because settings in a connection string override earlier
|
||||||
values[paramnum] = host;
|
* array entries only. Thus, user= in the connection string always
|
||||||
keywords[++paramnum] = "port";
|
* takes effect, but client_encoding= often will not.
|
||||||
values[paramnum] = port;
|
*
|
||||||
keywords[++paramnum] = "user";
|
* If you change this code, also change the initial-connection code in
|
||||||
values[paramnum] = user;
|
* main(). For no good reason, a connection string password= takes
|
||||||
}
|
* precedence in main() but not here.
|
||||||
|
*/
|
||||||
|
keywords[++paramnum] = "dbname";
|
||||||
|
values[paramnum] = dbname;
|
||||||
keywords[++paramnum] = "password";
|
keywords[++paramnum] = "password";
|
||||||
values[paramnum] = password;
|
values[paramnum] = password;
|
||||||
keywords[++paramnum] = "fallback_application_name";
|
keywords[++paramnum] = "fallback_application_name";
|
||||||
|
@ -215,7 +215,7 @@ main(int argc, char *argv[])
|
|||||||
values[2] = options.username;
|
values[2] = options.username;
|
||||||
keywords[3] = "password";
|
keywords[3] = "password";
|
||||||
values[3] = password;
|
values[3] = password;
|
||||||
keywords[4] = "dbname";
|
keywords[4] = "dbname"; /* see do_connect() */
|
||||||
values[4] = (options.action == ACT_LIST_DB &&
|
values[4] = (options.action == ACT_LIST_DB &&
|
||||||
options.dbname == NULL) ?
|
options.dbname == NULL) ?
|
||||||
"postgres" : options.dbname;
|
"postgres" : options.dbname;
|
||||||
|
Loading…
Reference in New Issue
Block a user