diff --git a/src/common/username.c b/src/common/username.c index ee5ef1c072..686a6a43c5 100644 --- a/src/common/username.c +++ b/src/common/username.c @@ -26,8 +26,8 @@ #include "common/username.h" /* - * Returns the current user name in a static buffer, or NULL on error and - * sets errstr + * Returns the current user name in a static buffer + * On error, returns NULL and sets *errstr to point to a palloc'd message */ const char * get_user_name(char **errstr) @@ -50,15 +50,17 @@ get_user_name(char **errstr) return pw->pw_name; #else - /* UNLEN = 256, 'static' variable remains after function exit */ + /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ + /* "static" variable remains after function exit */ static char username[256 + 1]; - DWORD len = sizeof(username) - 1; + DWORD len = sizeof(username); *errstr = NULL; if (!GetUserName(username, &len)) { - *errstr = psprintf(_("user name lookup failure: %s"), strerror(errno)); + *errstr = psprintf(_("user name lookup failure: error code %lu"), + GetLastError()); return NULL; } diff --git a/src/include/port.h b/src/include/port.h index 1d53e4ec0b..26d7fcd672 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -433,7 +433,7 @@ extern void srandom(unsigned int seed); /* thread.h */ extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen); -#if !defined(WIN32) || defined(__CYGWIN__) +#ifndef WIN32 extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, size_t buflen, struct passwd ** result); #endif diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 179793e2a0..8927df4f06 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -714,22 +714,26 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn) /* * pg_fe_getauthname * - * Returns a pointer to dynamic space containing whatever name the user - * has authenticated to the system. If there is an error, return NULL. + * Returns a pointer to malloc'd space containing whatever name the user + * has authenticated to the system. If there is an error, return NULL, + * and put a suitable error message in *errorMessage if that's not NULL. */ char * -pg_fe_getauthname(void) +pg_fe_getauthname(PQExpBuffer errorMessage) { + char *result = NULL; const char *name = NULL; - char *authn; #ifdef WIN32 - char username[128]; - DWORD namesize = sizeof(username) - 1; + /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ + char username[256 + 1]; + DWORD namesize = sizeof(username); #else + uid_t user_id = geteuid(); char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pw = NULL; + int pwerr; #endif /* @@ -741,24 +745,42 @@ pg_fe_getauthname(void) */ pglock_thread(); - /* - * We document PQconndefaults() to return NULL for a memory allocation - * failure. We don't have an API to return a user name lookup failure, so - * we just assume it always succeeds. - */ #ifdef WIN32 if (GetUserName(username, &namesize)) name = username; + else if (errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("user name lookup failure: error code %lu\n"), + GetLastError()); #else - if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0) + pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); + if (pw != NULL) name = pw->pw_name; + else if (errorMessage) + { + if (pwerr != 0) + printfPQExpBuffer(errorMessage, + libpq_gettext("could not look up local user ID %d: %s\n"), + (int) user_id, + pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf))); + else + printfPQExpBuffer(errorMessage, + libpq_gettext("local user with ID %d does not exist\n"), + (int) user_id); + } #endif - authn = name ? strdup(name) : NULL; + if (name) + { + result = strdup(name); + if (result == NULL && errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + } pgunlock_thread(); - return authn; + return result; } diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h index 59b6c16a6d..8d35767f7a 100644 --- a/src/interfaces/libpq/fe-auth.h +++ b/src/interfaces/libpq/fe-auth.h @@ -19,6 +19,6 @@ extern int pg_fe_sendauth(AuthRequest areq, PGconn *conn); -extern char *pg_fe_getauthname(void); +extern char *pg_fe_getauthname(PQExpBuffer errorMessage); #endif /* FE_AUTH_H */ diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index b2f556c1c3..25961b1f10 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -764,11 +764,27 @@ connectOptions1(PGconn *conn, const char *conninfo) 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; diff --git a/src/port/path.c b/src/port/path.c index e8faac3a26..d0f72df29c 100644 --- a/src/port/path.c +++ b/src/port/path.c @@ -777,7 +777,8 @@ get_home_path(char *ret_path) 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(ret_path, pwd->pw_dir, MAXPGPATH); return true; diff --git a/src/port/thread.c b/src/port/thread.c index 1568803d62..aab74516ac 100644 --- a/src/port/thread.c +++ b/src/port/thread.c @@ -83,6 +83,12 @@ pqStrerror(int errnum, char *strerrbuf, size_t buflen) /* * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() * behaviour, if it is not available or required. + * + * Per POSIX, the possible cases are: + * success: returns zero, *result is non-NULL + * uid not found: returns zero, *result is NULL + * error during lookup: returns an errno code, *result is NULL + * (caller should *not* assume that the errno variable is set) */ #ifndef WIN32 int @@ -93,22 +99,25 @@ pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, #ifdef GETPWUID_R_5ARG /* POSIX version */ - getpwuid_r(uid, resultbuf, buffer, buflen, result); + return getpwuid_r(uid, resultbuf, buffer, buflen, result); #else /* * Early POSIX draft of getpwuid_r() returns 'struct passwd *'. * getpwuid_r(uid, resultbuf, buffer, buflen) */ + errno = 0; *result = getpwuid_r(uid, resultbuf, buffer, buflen); + /* paranoia: ensure we return zero on success */ + return (*result == NULL) ? errno : 0; #endif #else - /* no getpwuid_r() available, just use getpwuid() */ + errno = 0; *result = getpwuid(uid); + /* paranoia: ensure we return zero on success */ + return (*result == NULL) ? errno : 0; #endif - - return (*result == NULL) ? -1 : 0; } #endif