diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index d67212b831..f5f2668f15 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -7231,6 +7231,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) + + + + PGPASSCOMMAND + + PGPASSCOMMAND behaves the same as the connection parameter. + + + diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a7e969d7c1..60382bc67b 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -204,6 +204,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Database-Password-File", "", 64, offsetof(struct pg_conn, pgpassfile)}, + {"passcommand", "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 +410,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); @@ -1068,7 +1072,8 @@ connectOptions2(PGconn *conn) * Supply default password if none given. Note that the password might be * different for each host/port pair. */ - if (conn->pgpass == NULL || conn->pgpass[0] == '\0') + if ( (conn->pgpass == NULL || conn->pgpass[0] == '\0') + || (conn->pgpasscommand != NULL && conn->pgpasscommand[0] != '\0') ) { /* If password file wasn't specified, use ~/PGPASSFILE */ if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0') @@ -1087,7 +1092,8 @@ connectOptions2(PGconn *conn) } } - if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0') + if ( (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0') + || (conn->pgpasscommand != NULL && conn->pgpasscommand[0] != '\0') ) { int i; @@ -1106,11 +1112,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; @@ -3468,6 +3475,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 +6385,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 +6417,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 +6431,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 +6445,22 @@ 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 + { + fprintf(stderr, + libpq_gettext("WARNING: password file and password command are not provided, please correct code\n") + ); + 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