Fix up the PQconnectionUsedPassword mess: create a separate

PQconnectionNeedsPassword function that tells the right thing for whether to
prompt for a password, and improve PQconnectionUsedPassword so that it checks
whether the password used by the connection was actually supplied as a
connection argument, instead of coming from environment or a password file.
Per bug report from Mark Cave-Ayland and subsequent discussion.
This commit is contained in:
Tom Lane 2007-12-09 19:01:40 +00:00
parent cb1ab30fdc
commit 4f9bf7fc5a
13 changed files with 102 additions and 41 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.247 2007/11/28 15:42:31 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.248 2007/12/09 19:01:40 tgl Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
@ -1145,12 +1145,33 @@ typedef struct
</listitem>
</varlistentry>
<varlistentry>
<term><function>PQconnectionNeedsPassword</function><indexterm><primary>PQconnectionNeedsPassword</></></term>
<listitem>
<para>
Returns true (1) if the connection authentication method
required a password, but none was available.
Returns false (0) if not.
<synopsis>
int PQconnectionNeedsPassword(const PGconn *conn);
</synopsis>
</para>
<para>
This function can be applied after a failed connection attempt
to decide whether to prompt the user for a password.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PQconnectionUsedPassword</function><indexterm><primary>PQconnectionUsedPassword</></></term>
<listitem>
<para>
Returns true (1) if the connection authentication method
required a password to be supplied. Returns false (0) if not.
used a caller-supplied password. Returns false (0) if not.
<synopsis>
int PQconnectionUsedPassword(const PGconn *conn);
@ -1159,9 +1180,10 @@ typedef struct
</para>
<para>
This function can be applied after either successful or failed
connection attempts. In the case of failure, it can for example
be used to decide whether to prompt the user for a password.
This function detects whether a password supplied to the connection
function was actually used. Passwords obtained from other
sources (such as the <filename>.pgpass</> file) are not considered
caller-supplied.
</para>
</listitem>
</varlistentry>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.563 2007/12/08 17:24:03 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.564 2007/12/09 19:01:40 tgl Exp $ -->
<!--
Typical markup:
@ -2184,8 +2184,9 @@ current_date &lt; 2017-11-17
<listitem>
<para>
Add <function>PQconnectionUsedPassword()</function> that returns
true if the server required a password (Joe Conway)
Add <function>PQconnectionNeedsPassword()</function> that returns
true if the server required a password but none was supplied
(Joe Conway, Tom)
</para>
<para>
@ -2197,6 +2198,19 @@ current_date &lt; 2017-11-17
</para>
</listitem>
<listitem>
<para>
Add <function>PQconnectionUsedPassword()</function> that returns
true if the supplied password was actually used
(Joe Conway, Tom)
</para>
<para>
This is useful in some security contexts where it is important
to know whether a user-supplied password is actually valid.
</para>
</listitem>
</itemizedlist>
</sect3>

View File

@ -4,7 +4,7 @@
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.90 2007/11/20 19:24:26 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.91 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -492,7 +492,7 @@ test_postmaster_connection(bool do_checkpoint)
{
if ((conn = PQconnectdb(connstr)) != NULL &&
(PQstatus(conn) == CONNECTION_OK ||
PQconnectionUsedPassword(conn)))
PQconnectionNeedsPassword(conn)))
{
PQfinish(conn);
success = true;

View File

@ -5,7 +5,7 @@
* Implements the basic DB functions used by the archiver.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.76 2007/07/08 19:07:38 tgl Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.77 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -159,7 +159,7 @@ _connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
if (PQstatus(newConn) == CONNECTION_BAD)
{
if (!PQconnectionUsedPassword(newConn))
if (!PQconnectionNeedsPassword(newConn))
die_horribly(AH, modulename, "could not reconnect to database: %s",
PQerrorMessage(newConn));
PQfinish(newConn);
@ -234,7 +234,7 @@ ConnectDatabase(Archive *AHX,
die_horribly(AH, modulename, "failed to connect to database\n");
if (PQstatus(AH->connection) == CONNECTION_BAD &&
PQconnectionUsedPassword(AH->connection) &&
PQconnectionNeedsPassword(AH->connection) &&
password == NULL &&
!feof(stdin))
{

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.98 2007/11/15 21:14:42 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.99 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1336,7 +1336,7 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
}
if (PQstatus(conn) == CONNECTION_BAD &&
PQconnectionUsedPassword(conn) &&
PQconnectionNeedsPassword(conn) &&
password == NULL &&
!feof(stdin))
{

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.183 2007/11/15 21:14:42 momjian Exp $
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.184 2007/12/09 19:01:40 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@ -1164,7 +1164,7 @@ do_connect(char *dbname, char *user, char *host, char *port)
* Connection attempt failed; either retry the connection attempt with
* a new password, or give up.
*/
if (!password && PQconnectionUsedPassword(n_conn))
if (!password && PQconnectionNeedsPassword(n_conn))
{
PQfinish(n_conn);
password = prompt_for_password(user);

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.141 2007/07/08 19:07:38 tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.142 2007/12/09 19:01:40 tgl Exp $
*/
#include "postgres_fe.h"
@ -211,7 +211,7 @@ main(int argc, char *argv[])
username, password);
if (PQstatus(pset.db) == CONNECTION_BAD &&
PQconnectionUsedPassword(pset.db) &&
PQconnectionNeedsPassword(pset.db) &&
password == NULL &&
!feof(stdin))
{

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.29 2007/11/15 21:14:42 momjian Exp $
* $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.30 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -123,7 +123,7 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
}
if (PQstatus(conn) == CONNECTION_BAD &&
PQconnectionUsedPassword(conn) &&
PQconnectionNeedsPassword(conn) &&
password == NULL &&
!feof(stdin))
{

View File

@ -1,4 +1,4 @@
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.17 2007/10/13 20:18:41 tgl Exp $
# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.18 2007/12/09 19:01:40 tgl Exp $
# Functions to be exported by libpq DLLs
PQconnectdb 1
PQsetdbLogin 2
@ -139,3 +139,4 @@ PQsendDescribePortal 136
lo_truncate 137
PQconnectionUsedPassword 138
pg_valid_server_encoding_id 139
PQconnectionNeedsPassword 140

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.134 2007/12/04 13:02:53 mha Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.135 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -954,7 +954,8 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
case AUTH_REQ_MD5:
case AUTH_REQ_CRYPT:
case AUTH_REQ_PASSWORD:
if (conn->pgpass == NULL || *conn->pgpass == '\0')
conn->password_needed = true;
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
{
printfPQExpBuffer(&conn->errorMessage,
PQnoPasswordSupplied);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.353 2007/11/15 21:14:46 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.354 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -232,7 +232,7 @@ static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage);
PQExpBuffer errorMessage, bool *password_from_string);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
static void defaultNoticeReceiver(void *arg, const PGresult *res);
@ -376,7 +376,8 @@ connectOptions1(PGconn *conn, const char *conninfo)
/*
* Parse the conninfo string
*/
connOptions = conninfo_parse(conninfo, &conn->errorMessage);
connOptions = conninfo_parse(conninfo, &conn->errorMessage,
&conn->pgpass_from_client);
if (connOptions == NULL)
{
conn->status = CONNECTION_BAD;
@ -472,6 +473,7 @@ connectOptions2(PGconn *conn)
conn->dbName, conn->pguser);
if (conn->pgpass == NULL)
conn->pgpass = strdup(DefaultPassword);
conn->pgpass_from_client = false;
}
/*
@ -555,10 +557,11 @@ PQconninfoOption *
PQconndefaults(void)
{
PQExpBufferData errorBuf;
bool password_from_string;
PQconninfoOption *connOptions;
initPQExpBuffer(&errorBuf);
connOptions = conninfo_parse("", &errorBuf);
connOptions = conninfo_parse("", &errorBuf, &password_from_string);
termPQExpBuffer(&errorBuf);
return connOptions;
}
@ -659,6 +662,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
if (conn->pgpass)
free(conn->pgpass);
conn->pgpass = strdup(pwd);
conn->pgpass_from_client = true;
}
/*
@ -1718,10 +1722,6 @@ keep_going: /* We will come back to here until there is
*/
conn->inStart = conn->inCursor;
/* Save the authentication request type, if first one. */
if (conn->areq == AUTH_REQ_OK)
conn->areq = areq;
/* Respond to the request if necessary. */
/*
@ -1924,7 +1924,7 @@ makeEmptyPGconn(void)
conn->std_strings = false; /* unless server says differently */
conn->verbosity = PQERRORS_DEFAULT;
conn->sock = -1;
conn->areq = AUTH_REQ_OK; /* until we receive something else */
conn->password_needed = false;
#ifdef USE_SSL
conn->allow_ssl_try = true;
conn->wait_ssl_try = false;
@ -3064,9 +3064,12 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
* If successful, a malloc'd PQconninfoOption array is returned.
* If not successful, NULL is returned and an error message is
* left in errorMessage.
* *password_from_string is set TRUE if we got a password from the
* conninfo string, otherwise FALSE.
*/
static PQconninfoOption *
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
bool *password_from_string)
{
char *pname;
char *pval;
@ -3077,6 +3080,8 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
PQconninfoOption *options;
PQconninfoOption *option;
*password_from_string = false; /* default result */
/* Make a working copy of PQconninfoOptions */
options = malloc(sizeof(PQconninfoOptions));
if (options == NULL)
@ -3233,6 +3238,12 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
free(buf);
return NULL;
}
/*
* Special handling for password
*/
if (strcmp(option->keyword, "password") == 0)
*password_from_string = (option->val[0] != '\0');
}
/* Done with the modifiable input string */
@ -3475,14 +3486,24 @@ PQbackendPID(const PGconn *conn)
return conn->be_pid;
}
int
PQconnectionNeedsPassword(const PGconn *conn)
{
if (!conn)
return false;
if (conn->password_needed &&
(conn->pgpass == NULL || conn->pgpass[0] == '\0'))
return true;
else
return false;
}
int
PQconnectionUsedPassword(const PGconn *conn)
{
if (!conn)
return false;
if (conn->areq == AUTH_REQ_MD5 ||
conn->areq == AUTH_REQ_CRYPT ||
conn->areq == AUTH_REQ_PASSWORD)
if (conn->password_needed && conn->pgpass_from_client)
return true;
else
return false;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.139 2007/10/13 20:18:42 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.140 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -263,6 +263,7 @@ extern int PQserverVersion(const PGconn *conn);
extern char *PQerrorMessage(const PGconn *conn);
extern int PQsocket(const PGconn *conn);
extern int PQbackendPID(const PGconn *conn);
extern int PQconnectionNeedsPassword(const PGconn *conn);
extern int PQconnectionUsedPassword(const PGconn *conn);
extern int PQclientEncoding(const PGconn *conn);
extern int PQsetClientEncoding(PGconn *conn, const char *encoding);
@ -426,7 +427,7 @@ extern void PQfreemem(void *ptr);
#define PQfreeNotify(ptr) PQfreemem(ptr)
/* Error when no password was given. */
/* Note: depending on this is deprecated; use PQconnectionUsedPassword(). */
/* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */
#define PQnoPasswordSupplied "fe_sendauth: no password supplied\n"
/*

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.127 2007/11/15 21:14:46 momjian Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.128 2007/12/09 19:01:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -291,6 +291,7 @@ struct pg_conn
char *dbName; /* database name */
char *pguser; /* Postgres username and password, if any */
char *pgpass;
bool pgpass_from_client; /* did password come from connect args? */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
char *krbsrvname; /* Kerberos service name */
@ -323,7 +324,7 @@ struct pg_conn
SockAddr raddr; /* Remote address */
ProtocolVersion pversion; /* FE/BE protocol version in use */
int sversion; /* server version, e.g. 70401 for 7.4.1 */
AuthRequest areq; /* auth type demanded by server */
bool password_needed; /* true if server demanded a password */
/* Transient state needed while establishing connection */
struct addrinfo *addrlist; /* list of possible backend addresses */