diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d67212b831..ae526a35c8 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1099,6 +1099,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
+
+ passcommand
+
+
+ Specifies the command to run to get the contents of store passwords
+ (see ).
+
+
+
+
connect_timeout
@@ -7231,6 +7241,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
+
+
+
+ PGPASSCOMMAND
+
+ PGPASSCOMMAND behaves the same as the connection parameter.
+
+
+
@@ -7486,7 +7506,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
- The Password File
+ The Password File / Password Command
password file
@@ -7494,6 +7514,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
.pgpass
+
+ password command
+
The file .pgpass in a user's home directory can
@@ -7509,7 +7532,25 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
- This file should contain lines of the following format:
+ The command which produces passwords to connect if the connection needs a
+ password (and no password has been specified otherwise).
+ Alternatively, a password command can be specifiedin the environment
+ variable PGPASSCOMMAND. It is not possible to specify the
+ command as a connect parameter since it could be executed by another user.
+
+ examples:
+
+ PGPASSCOMMAND = "gpg -q -d pgpass.gpg"
+ PGPASSCOMMAND = "curl http://passwords/really-unsecure-pgpass"
+ PGPASSCOMMAND = "my-own-secure-pgpass-script"
+
+
+ NOTE: if a .pgpass is present the command will not be
+ issued
+
+
+
+ This file/command-output should contain lines of the following format:
hostname:port:database:username:password
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bd7dac120d..db0d5e6d81 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -204,6 +204,13 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Database-Password-File", "", 64,
offsetof(struct pg_conn, pgpassfile)},
+ /* PGPASSCOMMAND may only be passed as an environment variable to
+ since adding it to the connect_string could potentially allow the command
+ to run with the priviliges of the database-server */
+ {"", "PGPASSCOMMAND", NULL, NULL,
+ "Database-Password-Command", "", 64,
+ offsetof(struct pg_conn, pgpasscommand)},
+
{"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
"Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
offsetof(struct pg_conn, connect_timeout)},
@@ -406,8 +413,8 @@ static int parseServiceFile(const char *serviceFile,
PQExpBuffer errorMessage,
bool *group_found);
static char *pwdfMatchesString(char *buf, const char *token);
-static char *passwordFromFile(const char *hostname, const char *port, const char *dbname,
- const char *username, const char *pgpassfile);
+static char *passwordFromFileOrCommand(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile, const char *pgpasscommand);
static void pgpassfileWarning(PGconn *conn);
static void default_threadlock(int acquire);
@@ -1087,7 +1094,25 @@ connectOptions2(PGconn *conn)
}
}
- if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
+ // if (conn->pgpasscommand == NULL)
+ // {
+ // char *tmp;
+ // if ((tmp = getenv("PGPASSCOMMAND")) != NULL)
+ // {
+ // conn->pgpasscommand = strdup(tmp);
+ // if (!conn->pgpasscommand)
+ // {
+ // conn->status = CONNECTION_BAD;
+ // if (errorMessage)
+ // printfPQExpBuffer(errorMessage,
+ // libpq_gettext("out of memory\n"));
+ // return false;
+ // }
+ // }
+ // }
+
+ if ( (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
+ || (conn->pgpasscommand != NULL && conn->pgpasscommand[0] != '\0') )
{
int i;
@@ -1106,11 +1131,12 @@ connectOptions2(PGconn *conn)
pwhost = conn->connhost[i].hostaddr;
conn->connhost[i].password =
- passwordFromFile(pwhost,
+ passwordFromFileOrCommand(pwhost,
conn->connhost[i].port,
conn->dbName,
conn->pguser,
- conn->pgpassfile);
+ conn->pgpassfile,
+ conn->pgpasscommand);
/* If we got one, set pgpassfile_used */
if (conn->connhost[i].password != NULL)
conn->pgpassfile_used = true;
@@ -1751,7 +1777,7 @@ connectDBStart(PGconn *conn)
if (ret || !ch->addrlist)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not parse network address \"%s\": %s\n"),
- ch->hostaddr, gai_strerror(ret));
+ ch->host, gai_strerror(ret));
break;
case CHT_UNIX_SOCKET:
@@ -3468,6 +3494,8 @@ freePGconn(PGconn *conn)
free(conn->pgpass);
if (conn->pgpassfile)
free(conn->pgpassfile);
+ if (conn->pgpasscommand)
+ free(conn->pgpasscommand);
if (conn->keepalives)
free(conn->keepalives);
if (conn->keepalives_idle)
@@ -6376,11 +6404,12 @@ pwdfMatchesString(char *buf, const char *token)
/* Get a password from the password file. Return value is malloc'd. */
static char *
-passwordFromFile(const char *hostname, const char *port, const char *dbname,
- const char *username, const char *pgpassfile)
+passwordFromFileOrCommand(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile, const char *pgpasscommand)
{
FILE *fp;
struct stat stat_buf;
+ char has_pgpassfile=0;
#define LINELEN NAMEDATALEN*5
char buf[LINELEN];
@@ -6407,11 +6436,12 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
port = DEF_PGPORT_STR;
/* If password file cannot be opened, ignore it. */
- if (stat(pgpassfile, &stat_buf) != 0)
- return NULL;
+ if (stat(pgpassfile, &stat_buf) == 0)
+ has_pgpassfile=1;
+
#ifndef WIN32
- if (!S_ISREG(stat_buf.st_mode))
+ if (has_pgpassfile && !S_ISREG(stat_buf.st_mode))
{
fprintf(stderr,
libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"),
@@ -6420,7 +6450,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
/* If password file is insecure, alert the user and ignore it. */
- if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
+ if (has_pgpassfile && stat_buf.st_mode & (S_IRWXG | S_IRWXO))
{
fprintf(stderr,
libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
@@ -6434,8 +6464,19 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
* file.
*/
#endif
+ if (has_pgpassfile)
+ {
+ fp = fopen(pgpassfile, "r");
+ }
+ else if (pgpasscommand != NULL && pgpasscommand[0] != '\0')
+ {
+ fp = popen(pgpasscommand, "r");
+ }
+ else
+ {
+ return NULL;
+ }
- fp = fopen(pgpassfile, "r");
if (fp == NULL)
return NULL;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 9a586ff25a..77af2ab550 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -343,6 +343,7 @@ struct pg_conn
char *pguser; /* Postgres username and password, if any */
char *pgpass;
char *pgpassfile; /* path to a file containing password(s) */
+ char *pgpasscommand; /* path to command producing the password(s) */
char *keepalives; /* use TCP keepalives? */
char *keepalives_idle; /* time between TCP keepalives */
char *keepalives_interval; /* time between TCP keepalive