diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index d04dba7..72e1341 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -943,7 +943,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname Note that authentication is likely to fail if host is not the name of the server at network address hostaddr. Also, note that host rather than hostaddr - is used to identify the connection in ~/.pgpass (see + is used to identify the connection in a password file (see ). @@ -1002,6 +1002,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + + pgpassfile + + + Specifies the name of the file used to lookup passwords. + Defaults to the password file (see ). + + + + connect_timeout @@ -6882,9 +6892,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) PGPASSFILE - PGPASSFILE specifies the name of the password file to - use for lookups. If not set, it defaults to ~/.pgpass - (see ). + PGPASSFILE behaves the same as the connection parameter. @@ -7156,13 +7165,15 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) - The file .pgpass in a user's home directory or the - file referenced by PGPASSFILE can contain passwords to + The file .pgpass in a user's home directory can contain passwords to be used if the connection requires a password (and no password has been specified otherwise). On Microsoft Windows the file is named %APPDATA%\postgresql\pgpass.conf (where %APPDATA% refers to the Application Data subdirectory in the user's profile). + Alternatively, a password file can be specified + using the connection parameter + or the environment variable PGPASSFILE. diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index b4f9ad7..abcf1ae 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -184,6 +184,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Database-Password", "*", 20, offsetof(struct pg_conn, pgpass)}, + {"pgpassfile", "PGPASSFILE", NULL, NULL, + "Database-Password-File", "", 64, + offsetof(struct pg_conn, pgpassfile)}, + {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL, "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */ offsetof(struct pg_conn, connect_timeout)}, @@ -375,9 +379,9 @@ static int parseServiceFile(const char *serviceFile, bool *group_found); static char *pwdfMatchesString(char *buf, char *token); static char *PasswordFromFile(char *hostname, char *port, char *dbname, - char *username); -static bool getPgPassFilename(char *pgpassfile); -static void dot_pg_pass_warning(PGconn *conn); + char *username, char *pgpassfile); +static bool fillDefaultPGPassFile(PGconn *conn); +static void PGPassFileWarning(PGconn *conn); static void default_threadlock(int acquire); @@ -954,14 +958,23 @@ connectOptions2(PGconn *conn) conn->pgpass = strdup(DefaultPassword); if (!conn->pgpass) goto oom_error; + + if(!conn->pgpassfile || conn->pgpassfile[0] =='\0') + { + fillDefaultPGPassFile(conn); + } + for (i = 0; i < conn->nconnhost; ++i) { + /* We'll pass conn->pgpassfile regardless of it's contents - checks happen in PasswordFromFile() */ conn->connhost[i].password = PasswordFromFile(conn->connhost[i].host, conn->connhost[i].port, - conn->dbName, conn->pguser); - if (conn->connhost[i].password != NULL) - conn->dot_pgpass_used = true; + conn->dbName, conn->pguser, + conn->pgpassfile); + /* Check if conn->pgpassfile_used is already true so we can skip this each following iteration */ + if (!conn->pgpassfile_used && conn->connhost[i].password != NULL) + conn->pgpassfile_used = true; } } @@ -2830,7 +2843,7 @@ keep_going: /* We will come back to here until there is error_return: - dot_pg_pass_warning(conn); + PGPassFileWarning(conn); /* * We used to close the socket at this point, but that makes it awkward @@ -2961,7 +2974,7 @@ makeEmptyPGconn(void) conn->sock = PGINVALID_SOCKET; conn->auth_req_received = false; conn->password_needed = false; - conn->dot_pgpass_used = false; + conn->pgpassfile_used = false; #ifdef USE_SSL conn->allow_ssl_try = true; conn->wait_ssl_try = false; @@ -3070,6 +3083,8 @@ freePGconn(PGconn *conn) free(conn->pguser); if (conn->pgpass) free(conn->pgpass); + if(conn->pgpassfile) + free(conn->pgpassfile); if (conn->keepalives) free(conn->keepalives); if (conn->keepalives_idle) @@ -5946,12 +5961,12 @@ pwdfMatchesString(char *buf, char *token) return NULL; } -/* Get a password from the password file. Return value is malloc'd. */ +/* Get a password from the password file or the user-specified pgpassfile. + Return value is malloc'd. */ static char * -PasswordFromFile(char *hostname, char *port, char *dbname, char *username) +PasswordFromFile(char *hostname, char *port, char *dbname, char *username, char *pgpassfile) { FILE *fp; - char pgpassfile[MAXPGPATH]; struct stat stat_buf; #define LINELEN NAMEDATALEN*5 @@ -5978,9 +5993,6 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username) if (port == NULL) port = DEF_PGPORT_STR; - if (!getPgPassFilename(pgpassfile)) - return NULL; - /* If password file cannot be opened, ignore it. */ if (stat(pgpassfile, &stat_buf) != 0) return NULL; @@ -6067,21 +6079,19 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username) static bool -getPgPassFilename(char *pgpassfile) +fillDefaultPGPassFile(PGconn *conn) { - char *passfile_env; - - if ((passfile_env = getenv("PGPASSFILE")) != NULL) - /* use the literal path from the environment, if set */ - strlcpy(pgpassfile, passfile_env, MAXPGPATH); - else - { - char homedir[MAXPGPATH]; - - if (!pqGetHomeDirectory(homedir, sizeof(homedir))) - return false; - snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); - } + char homedir[MAXPGPATH]; + + if(conn->pgpassfile) + free(conn->pgpassfile); + + conn->pgpassfile = malloc(MAXPGPATH); + + if (!pqGetHomeDirectory(homedir, sizeof(homedir))) + return false; + snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); + return true; } @@ -6091,21 +6101,18 @@ getPgPassFilename(char *pgpassfile) * password is wrong. */ static void -dot_pg_pass_warning(PGconn *conn) +PGPassFileWarning(PGconn *conn) { - /* If it was 'invalid authorization', add .pgpass mention */ + /* If it was 'invalid authorization', add pgpassfile mention */ /* only works with >= 9.0 servers */ - if (conn->dot_pgpass_used && conn->password_needed && conn->result && + if (conn->pgpassfile_used && conn->password_needed && conn->result && + conn->pgpassfile && conn->pgpassfile[0]!='\0' && strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE), ERRCODE_INVALID_PASSWORD) == 0) { - char pgpassfile[MAXPGPATH]; - - if (!getPgPassFilename(pgpassfile)) - return; appendPQExpBuffer(&conn->errorMessage, libpq_gettext("password retrieved from file \"%s\"\n"), - pgpassfile); + conn->pgpassfile); } } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 854ec89..a60ebea 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -343,6 +343,7 @@ struct pg_conn char *replication; /* connect as the replication standby? */ char *pguser; /* Postgres username and password, if any */ char *pgpass; + char *pgpassfile; /* path to a file containing the password */ char *keepalives; /* use TCP keepalives? */ char *keepalives_idle; /* time between TCP keepalives */ char *keepalives_interval; /* time between TCP keepalive @@ -404,7 +405,7 @@ struct pg_conn bool auth_req_received; /* true if any type of auth req * received */ bool password_needed; /* true if server demanded a password */ - bool dot_pgpass_used; /* true if used .pgpass */ + bool pgpassfile_used; /* true if a password from a pgpassfile was used */ bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */