Skip to content

Commit db6986f

Browse files
committed
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 5b40677 and bd58d9d), and commit ba005f1 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 41a4e45 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 5b40677), 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 ba005f1 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
1 parent 18fc4ec commit db6986f

File tree

1 file changed

+63
-46
lines changed

1 file changed

+63
-46
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,52 +1063,51 @@ connectOptions2(PGconn *conn)
10631063
*/
10641064
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
10651065
{
1066-
int i;
1067-
1066+
/* If password file wasn't specified, use ~/PGPASSFILE */
10681067
if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0')
10691068
{
1070-
/* Identify password file to use; fail if we can't */
10711069
char homedir[MAXPGPATH];
10721070

1073-
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
1071+
if (pqGetHomeDirectory(homedir, sizeof(homedir)))
10741072
{
1075-
conn->status = CONNECTION_BAD;
1076-
printfPQExpBuffer(&conn->errorMessage,
1077-
libpq_gettext("could not get home directory to locate password file\n"));
1078-
return false;
1073+
if (conn->pgpassfile)
1074+
free(conn->pgpassfile);
1075+
conn->pgpassfile = malloc(MAXPGPATH);
1076+
if (!conn->pgpassfile)
1077+
goto oom_error;
1078+
snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s",
1079+
homedir, PGPASSFILE);
10791080
}
1080-
1081-
if (conn->pgpassfile)
1082-
free(conn->pgpassfile);
1083-
conn->pgpassfile = malloc(MAXPGPATH);
1084-
if (!conn->pgpassfile)
1085-
goto oom_error;
1086-
1087-
snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE);
10881081
}
10891082

1090-
for (i = 0; i < conn->nconnhost; i++)
1083+
if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
10911084
{
1092-
/*
1093-
* Try to get a password for this host from pgpassfile. We use
1094-
* host name rather than host address in the same manner to
1095-
* PQhost().
1096-
*/
1097-
char *pwhost = conn->connhost[i].host;
1098-
1099-
if (conn->connhost[i].type == CHT_HOST_ADDRESS &&
1100-
conn->connhost[i].host != NULL && conn->connhost[i].host[0] != '\0')
1101-
pwhost = conn->connhost[i].hostaddr;
1102-
1103-
conn->connhost[i].password =
1104-
passwordFromFile(pwhost,
1105-
conn->connhost[i].port,
1106-
conn->dbName,
1107-
conn->pguser,
1108-
conn->pgpassfile);
1109-
/* If we got one, set pgpassfile_used */
1110-
if (conn->connhost[i].password != NULL)
1111-
conn->pgpassfile_used = true;
1085+
int i;
1086+
1087+
for (i = 0; i < conn->nconnhost; i++)
1088+
{
1089+
/*
1090+
* Try to get a password for this host from pgpassfile. We use
1091+
* host name rather than host address in the same manner as
1092+
* PQhost().
1093+
*/
1094+
char *pwhost = conn->connhost[i].host;
1095+
1096+
if (conn->connhost[i].type == CHT_HOST_ADDRESS &&
1097+
conn->connhost[i].host != NULL &&
1098+
conn->connhost[i].host[0] != '\0')
1099+
pwhost = conn->connhost[i].hostaddr;
1100+
1101+
conn->connhost[i].password =
1102+
passwordFromFile(pwhost,
1103+
conn->connhost[i].port,
1104+
conn->dbName,
1105+
conn->pguser,
1106+
conn->pgpassfile);
1107+
/* If we got one, set pgpassfile_used */
1108+
if (conn->connhost[i].password != NULL)
1109+
conn->pgpassfile_used = true;
1110+
}
11121111
}
11131112
}
11141113

@@ -4469,6 +4468,16 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
44694468

44704469
#define MAXBUFSIZE 256
44714470

4471+
/*
4472+
* parseServiceInfo: if a service name has been given, look it up and absorb
4473+
* connection options from it into *options.
4474+
*
4475+
* Returns 0 on success, nonzero on failure. On failure, if errorMessage
4476+
* isn't null, also store an error message there. (Note: the only reason
4477+
* this function and related ones don't dump core on errorMessage == NULL
4478+
* is the undocumented fact that printfPQExpBuffer does nothing when passed
4479+
* a null PQExpBuffer pointer.)
4480+
*/
44724481
static int
44734482
parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
44744483
{
@@ -4487,23 +4496,24 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
44874496
if (service == NULL)
44884497
service = getenv("PGSERVICE");
44894498

4499+
/* If no service name given, nothing to do */
44904500
if (service == NULL)
44914501
return 0;
44924502

4503+
/*
4504+
* Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that
4505+
* exists).
4506+
*/
44934507
if ((env = getenv("PGSERVICEFILE")) != NULL)
44944508
strlcpy(serviceFile, env, sizeof(serviceFile));
44954509
else
44964510
{
44974511
char homedir[MAXPGPATH];
44984512

44994513
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
4500-
{
4501-
printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file"));
4502-
return 1;
4503-
}
4514+
goto next_file;
45044515
snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
4505-
errno = 0;
4506-
if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
4516+
if (stat(serviceFile, &stat_buf) != 0)
45074517
goto next_file;
45084518
}
45094519

@@ -4519,8 +4529,7 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
45194529
*/
45204530
snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
45214531
getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
4522-
errno = 0;
4523-
if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
4532+
if (stat(serviceFile, &stat_buf) != 0)
45244533
goto last_file;
45254534

45264535
status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
@@ -6510,7 +6519,15 @@ pgpassfileWarning(PGconn *conn)
65106519
*
65116520
* This is essentially the same as get_home_path(), but we don't use that
65126521
* because we don't want to pull path.c into libpq (it pollutes application
6513-
* namespace)
6522+
* namespace).
6523+
*
6524+
* Returns true on success, false on failure to obtain the directory name.
6525+
*
6526+
* CAUTION: although in most situations failure is unexpected, there are users
6527+
* who like to run applications in a home-directory-less environment. On
6528+
* failure, you almost certainly DO NOT want to report an error. Just act as
6529+
* though whatever file you were hoping to find in the home directory isn't
6530+
* there (which it isn't).
65146531
*/
65156532
bool
65166533
pqGetHomeDirectory(char *buf, int bufsize)

0 commit comments

Comments
 (0)