Commit db6986f4 authored by Tom Lane's avatar Tom Lane

Fix libpq to not require user's home directory to exist.

Some people like to run libpq-using applications in environments where
there's no home directory.  We've broken that scenario before (cf commits
5b406779 and bd58d9d8), and commit ba005f19 broke it again, by making
it a hard error if we fail to get the home directory name while looking
for ~/.pgpass.  The previous precedent is that if we can't get the home
directory name, we should just silently act as though the file we hoped
to find there doesn't exist.  Rearrange the new code to honor that.

Looking around, the service-file code added by commit 41a4e459 had the
same disease.  Apparently, that escaped notice because it only runs when
a service name has been specified, which I guess the people who use this
scenario don't do.  Nonetheless, it's wrong too, so fix that case as well.

Add a comment about this policy to pqGetHomeDirectory, in the probably
vain hope of forestalling the same error in future.  And upgrade the
rather miserable commenting in parseServiceInfo, too.

In passing, also back off parseServiceInfo's assumption that only ENOENT
is an ignorable error from stat() when checking a service file.  We would
need to ignore at least ENOTDIR as well (cf 5b406779), and seeing that
the far-better-tested code for ~/.pgpass treats all stat() failures alike,
I think this code ought to as well.

Per bug #14872 from Dan Watson.  Back-patch the .pgpass change to v10
where ba005f19 came in.  The service-file bugs are far older, so
back-patch the other changes to all supported branches.

Discussion: https://postgr.es/m/20171025200457.1471.34504@wrigleys.postgresql.org
parent 18fc4ecf
......@@ -1063,52 +1063,51 @@ connectOptions2(PGconn *conn)
*/
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
{
int i;
/* If password file wasn't specified, use ~/PGPASSFILE */
if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0')
{
/* Identify password file to use; fail if we can't */
char homedir[MAXPGPATH];
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
if (pqGetHomeDirectory(homedir, sizeof(homedir)))
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not get home directory to locate password file\n"));
return false;
if (conn->pgpassfile)
free(conn->pgpassfile);
conn->pgpassfile = malloc(MAXPGPATH);
if (!conn->pgpassfile)
goto oom_error;
snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s",
homedir, PGPASSFILE);
}
if (conn->pgpassfile)
free(conn->pgpassfile);
conn->pgpassfile = malloc(MAXPGPATH);
if (!conn->pgpassfile)
goto oom_error;
snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE);
}
for (i = 0; i < conn->nconnhost; i++)
if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
{
/*
* Try to get a password for this host from pgpassfile. We use
* host name rather than host address in the same manner to
* PQhost().
*/
char *pwhost = conn->connhost[i].host;
if (conn->connhost[i].type == CHT_HOST_ADDRESS &&
conn->connhost[i].host != NULL && conn->connhost[i].host[0] != '\0')
pwhost = conn->connhost[i].hostaddr;
conn->connhost[i].password =
passwordFromFile(pwhost,
conn->connhost[i].port,
conn->dbName,
conn->pguser,
conn->pgpassfile);
/* If we got one, set pgpassfile_used */
if (conn->connhost[i].password != NULL)
conn->pgpassfile_used = true;
int i;
for (i = 0; i < conn->nconnhost; i++)
{
/*
* Try to get a password for this host from pgpassfile. We use
* host name rather than host address in the same manner as
* PQhost().
*/
char *pwhost = conn->connhost[i].host;
if (conn->connhost[i].type == CHT_HOST_ADDRESS &&
conn->connhost[i].host != NULL &&
conn->connhost[i].host[0] != '\0')
pwhost = conn->connhost[i].hostaddr;
conn->connhost[i].password =
passwordFromFile(pwhost,
conn->connhost[i].port,
conn->dbName,
conn->pguser,
conn->pgpassfile);
/* If we got one, set pgpassfile_used */
if (conn->connhost[i].password != NULL)
conn->pgpassfile_used = true;
}
}
}
......@@ -4469,6 +4468,16 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
#define MAXBUFSIZE 256
/*
* parseServiceInfo: if a service name has been given, look it up and absorb
* connection options from it into *options.
*
* Returns 0 on success, nonzero on failure. On failure, if errorMessage
* isn't null, also store an error message there. (Note: the only reason
* this function and related ones don't dump core on errorMessage == NULL
* is the undocumented fact that printfPQExpBuffer does nothing when passed
* a null PQExpBuffer pointer.)
*/
static int
parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
{
......@@ -4487,9 +4496,14 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
if (service == NULL)
service = getenv("PGSERVICE");
/* If no service name given, nothing to do */
if (service == NULL)
return 0;
/*
* Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that
* exists).
*/
if ((env = getenv("PGSERVICEFILE")) != NULL)
strlcpy(serviceFile, env, sizeof(serviceFile));
else
......@@ -4497,13 +4511,9 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
char homedir[MAXPGPATH];
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
{
printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file"));
return 1;
}
goto next_file;
snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
errno = 0;
if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
if (stat(serviceFile, &stat_buf) != 0)
goto next_file;
}
......@@ -4519,8 +4529,7 @@ next_file:
*/
snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
errno = 0;
if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
if (stat(serviceFile, &stat_buf) != 0)
goto last_file;
status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
......@@ -6510,7 +6519,15 @@ pgpassfileWarning(PGconn *conn)
*
* This is essentially the same as get_home_path(), but we don't use that
* because we don't want to pull path.c into libpq (it pollutes application
* namespace)
* namespace).
*
* Returns true on success, false on failure to obtain the directory name.
*
* CAUTION: although in most situations failure is unexpected, there are users
* who like to run applications in a home-directory-less environment. On
* failure, you almost certainly DO NOT want to report an error. Just act as
* though whatever file you were hoping to find in the home directory isn't
* there (which it isn't).
*/
bool
pqGetHomeDirectory(char *buf, int bufsize)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment