aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-01-11 12:35:44 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2015-01-11 12:35:44 -0500
commit080eabe2e8a184ff40b7380aaaa9418714acace9 (patch)
treef203be9f71fbc124f6b1bb95d98326c8c5bdb138 /src/interfaces/libpq/fe-connect.c
parentde6429a8fdd3538e977b482d90389785d733e373 (diff)
downloadpostgresql-080eabe2e8a184ff40b7380aaaa9418714acace9.tar.gz
postgresql-080eabe2e8a184ff40b7380aaaa9418714acace9.zip
Fix libpq's behavior when /etc/passwd isn't readable.
Some users run their applications in chroot environments that lack an /etc/passwd file. This means that the current UID's user name and home directory are not obtainable. libpq used to be all right with that, so long as the database role name to use was specified explicitly. But commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 broke such cases by causing any failure of pg_fe_getauthname() to be treated as a hard error. In any case it did little to advance its nominal goal of causing errors in pg_fe_getauthname() to be reported better. So revert that and instead put some real error-reporting code in place. This requires changes to the APIs of pg_fe_getauthname() and pqGetpwuid(), since the latter had departed from the POSIX-specified API of getpwuid_r() in a way that made it impossible to distinguish actual lookup errors from "no such user". To allow such failures to be reported, while not failing if the caller supplies a role name, add a second call of pg_fe_getauthname() in connectOptions2(). This is a tad ugly, and could perhaps be avoided with some refactoring of PQsetdbLogin(), but I'll leave that idea for later. (Note that the complained-of misbehavior only occurs in PQsetdbLogin, not when using the PQconnect functions, because in the latter we will never bother to call pg_fe_getauthname() if the user gives a role name.) In passing also clean up the Windows-side usage of GetUserName(): the recommended buffer size is 257 bytes, the passed buffer length should be the buffer size not buffer size less 1, and any error is reported by GetLastError() not errno. Per report from Christoph Berg. Back-patch to 9.4 where the chroot failure case was introduced. The generally poor reporting of errors here is of very long standing, of course, but given the lack of field complaints about it we won't risk changing these APIs further back (even though they're theoretically internal to libpq).
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c54
1 files changed, 37 insertions, 17 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index b2f556c1c30..25961b1f10d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -765,10 +765,26 @@ static bool
connectOptions2(PGconn *conn)
{
/*
+ * If user name was not given, fetch it. (Most likely, the fetch will
+ * fail, since the only way we get here is if pg_fe_getauthname() failed
+ * during conninfo_add_defaults(). But now we want an error message.)
+ */
+ if (conn->pguser == NULL || conn->pguser[0] == '\0')
+ {
+ if (conn->pguser)
+ free(conn->pguser);
+ conn->pguser = pg_fe_getauthname(&conn->errorMessage);
+ if (!conn->pguser)
+ {
+ conn->status = CONNECTION_BAD;
+ return false;
+ }
+ }
+
+ /*
* If database name was not given, default it to equal user name
*/
- if ((conn->dbName == NULL || conn->dbName[0] == '\0')
- && conn->pguser != NULL)
+ if (conn->dbName == NULL || conn->dbName[0] == '\0')
{
if (conn->dbName)
free(conn->dbName);
@@ -1967,6 +1983,7 @@ keep_going: /* We will come back to here until there is
char pwdbuf[BUFSIZ];
struct passwd pass_buf;
struct passwd *pass;
+ int passerr;
uid_t uid;
gid_t gid;
@@ -1987,13 +2004,18 @@ keep_going: /* We will come back to here until there is
goto error_return;
}
- pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
-
+ passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
if (pass == NULL)
{
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("local user with ID %d does not exist\n"),
- (int) uid);
+ if (passerr != 0)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not look up local user ID %d: %s\n"),
+ (int) uid,
+ pqStrerror(passerr, sebuf, sizeof(sebuf)));
+ else
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("local user with ID %d does not exist\n"),
+ (int) uid);
goto error_return;
}
@@ -4605,18 +4627,15 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
}
/*
- * Special handling for "user" option
+ * Special handling for "user" option. Note that if pg_fe_getauthname
+ * fails, we just leave the value as NULL; there's no need for this to
+ * be an error condition if the caller provides a user name. The only
+ * reason we do this now at all is so that callers of PQconndefaults
+ * will see a correct default (barring error, of course).
*/
if (strcmp(option->keyword, "user") == 0)
{
- option->val = pg_fe_getauthname();
- if (!option->val)
- {
- if (errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
+ option->val = pg_fe_getauthname(NULL);
continue;
}
}
@@ -5843,7 +5862,8 @@ pqGetHomeDirectory(char *buf, int bufsize)
struct passwd pwdstr;
struct passwd *pwd = NULL;
- if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
+ (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd);
+ if (pwd == NULL)
return false;
strlcpy(buf, pwd->pw_dir, bufsize);
return true;