Thread: libpq 9.4 requires /etc/passwd?

libpq 9.4 requires /etc/passwd?

From
Christoph Berg
Date:
Hi,

I've got several reports that postfix's pgsql lookup tables are broken
with 9.4's libpq5, while 9.3's libpq5 works just fine. The error
message looks like this:

Jan 10 00:11:40 lehmann postfix/trivial-rewrite[29960]: warning: connect to pgsql server localhost:5432: out of
memory?
Jan 10 00:11:40 lehmann postfix/trivial-rewrite[29960]: warning: pgsql:/etc/postfix/pgsqltest lookup error for "*"

The "out of memory?" message comes from PQsetdbLogin and
PQerrorMessage in postfix-2.11.3/src/global/dict_pgsql.c:
   if ((host->db = PQsetdbLogin(host->name, host->port, NULL, NULL,                                dbname, username,
password))== NULL       || PQstatus(host->db) != CONNECTION_OK) {       msg_warn("connect to pgsql server %s: %s",
         host->hostname, PQerrorMessage(host->db));
 

There are two ways around the problem on the postfix side: not running
the postfix service chrooted, or using a "proxy" map which effectively
does the lookup also from outside the chroot.

Debian bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=756627
mentions another solution: creation of an /etc/passwd file inside the
postfix chroot.

libpq wants the user home directory to find .pgpass and
.pg_service.conf files, but apparently the behavior to require the
existence of the passwd file (or nss equivalent) is new in 9.4.

I've tried to make sense of the fe-connect.c code to find the issue
but couldn't spot it. Can someone explain what's going on there? Is
this a bug in libpq? (It's certainly a regression.)

Christoph
-- 
cb@df7cb.de | http://www.df7cb.de/



Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
Christoph Berg <cb@df7cb.de> writes:
> libpq wants the user home directory to find .pgpass and
> .pg_service.conf files, but apparently the behavior to require the
> existence of the passwd file (or nss equivalent) is new in 9.4.

There is demonstrably no direct reference to /etc/passwd in the PG code.
It's possible that we've added some new expectation that $HOME can be
identified, but a quick look through the code shows no such checks that
don't look like they've been there for some time.

Are the complainants doing anything that would result in SSL certificate
checking?  More generally, it'd be useful to see an exact example of
what are the connection parameters and environment that result in a
failure.
        regards, tom lane



Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
I wrote:
> Christoph Berg <cb@df7cb.de> writes:
>> libpq wants the user home directory to find .pgpass and
>> .pg_service.conf files, but apparently the behavior to require the
>> existence of the passwd file (or nss equivalent) is new in 9.4.

> There is demonstrably no direct reference to /etc/passwd in the PG code.
> It's possible that we've added some new expectation that $HOME can be
> identified, but a quick look through the code shows no such checks that
> don't look like they've been there for some time.

Hmm ... actually, I'll bet it's not $HOME that's at issue, but the name
of the user.  Commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 turned
failure of pg_fe_getauthname() into a hard connection failure, whereas
previously it was harmless as long as the caller provided a username.

I wonder if we shouldn't just revert that commit in toto.  Yeah,
identifying an out-of-memory error might be useful, but this cure
seems a lot worse than the disease.  What's more, this coding reports
*any* pg_fe_getauthname failure as "out of memory", which is much worse
than useless.

Alternatively, maybe don't try to resolve username this way until
we've found that the caller isn't providing any username.
        regards, tom lane



Re: libpq 9.4 requires /etc/passwd?

From
Bruce Momjian
Date:
On Fri, Jan  9, 2015 at 06:42:19PM -0500, Tom Lane wrote:
> Christoph Berg <cb@df7cb.de> writes:
> > libpq wants the user home directory to find .pgpass and
> > .pg_service.conf files, but apparently the behavior to require the
> > existence of the passwd file (or nss equivalent) is new in 9.4.
> 
> There is demonstrably no direct reference to /etc/passwd in the PG code.
> It's possible that we've added some new expectation that $HOME can be
> identified, but a quick look through the code shows no such checks that
> don't look like they've been there for some time.
> 
> Are the complainants doing anything that would result in SSL certificate
> checking?  More generally, it'd be useful to see an exact example of
> what are the connection parameters and environment that result in a
> failure.

I see similar error strings, but nothing that ends with a question mark:
"out of memory?".  However, I wonder if the crux of the problem is that
they are running in a chroot environment where the user id can't be
looked up.

Looking at libpq's pg_fe_getauthname(), I see that if it can't get the
OS user name of the effective user, it assumes it failed and returns
NULL:
    /*     * We document PQconndefaults() to return NULL for a memory allocation     * failure.  We don't have an API
toreturn a user name lookup failure, so     * we just assume it always succeeds.     */#ifdef WIN32    if
(GetUserName(username,&namesize))        name = username;#else    if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf,
sizeof(pwdbuf),&pw) == 0)        name = pw->pw_name;#endif    authn = name ? strdup(name) : NULL;
 

and conninfo_add_defaults() assumes a pg_fe_getauthname() failure is a
memory allocation failure:
option->val = pg_fe_getauthname();if (!option->val){    if (errorMessage)        printfPQExpBuffer(errorMessage,
                 libpq_gettext("out of memory\n"));    return false;
 

It was my 9.4 commit that changed this:
commit a4c8f14364c27508233f8a31ac4b10a4c90235a9Author: Bruce Momjian <bruce@momjian.us>Date:   Thu Mar 20 11:48:31 2014
-0400   libpq:  pass a memory allocation failure error up to PQconndefaults()    Previously user name memory allocation
failureswere ignored and the    default user name set to NULL.
 

I am thinking we have to now assume that user name lookups can fail for
other reasons.  I am unclear how this worked in the past though.

--  Bruce Momjian  <bruce@momjian.us>        http://momjian.us EnterpriseDB
http://enterprisedb.com
 + Everyone has their own god. +



Re: libpq 9.4 requires /etc/passwd?

From
Bruce Momjian
Date:
On Fri, Jan  9, 2015 at 06:57:02PM -0500, Tom Lane wrote:
> I wrote:
> > Christoph Berg <cb@df7cb.de> writes:
> >> libpq wants the user home directory to find .pgpass and
> >> .pg_service.conf files, but apparently the behavior to require the
> >> existence of the passwd file (or nss equivalent) is new in 9.4.
>
> > There is demonstrably no direct reference to /etc/passwd in the PG code.
> > It's possible that we've added some new expectation that $HOME can be
> > identified, but a quick look through the code shows no such checks that
> > don't look like they've been there for some time.
>
> Hmm ... actually, I'll bet it's not $HOME that's at issue, but the name
> of the user.  Commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 turned
> failure of pg_fe_getauthname() into a hard connection failure, whereas
> previously it was harmless as long as the caller provided a username.
>
> I wonder if we shouldn't just revert that commit in toto.  Yeah,
> identifying an out-of-memory error might be useful, but this cure
> seems a lot worse than the disease.  What's more, this coding reports
> *any* pg_fe_getauthname failure as "out of memory", which is much worse
> than useless.
>
> Alternatively, maybe don't try to resolve username this way until
> we've found that the caller isn't providing any username.

Seems we both found it at the same time.  Here is the thread were we
discussed it, and you were concerned about the memory allocation failure
too:

    http://www.postgresql.org/message-id/flat/20140320154905.GC7711@momjian.us#20140320154905.GC7711@momjian.us

    In particular, it appears to me that if the strdup in pg_fe_getauthname
    fails, we'll just let that pass without comment, which is inconsistent
    with all the other out-of-memory cases in conninfo_add_defaults.
    (I wonder whether any code downstream of this supposes that we always
    have a value for the "user" option.  It's a pretty safe bet that the
    case is hardly ever tested.)

I have developed the attached patch which documents why the user name
lookup might fail, and sets the failure string to "".  It preserves the
memory allocation failure behavior.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +

Attachment

Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
Bruce Momjian <bruce@momjian.us> writes:
> On Fri, Jan  9, 2015 at 06:57:02PM -0500, Tom Lane wrote:
>> Hmm ... actually, I'll bet it's not $HOME that's at issue, but the name
>> of the user.  Commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 turned
>> failure of pg_fe_getauthname() into a hard connection failure, whereas
>> previously it was harmless as long as the caller provided a username.
>>
>> I wonder if we shouldn't just revert that commit in toto.  Yeah,
>> identifying an out-of-memory error might be useful, but this cure
>> seems a lot worse than the disease.  What's more, this coding reports
>> *any* pg_fe_getauthname failure as "out of memory", which is much worse
>> than useless.
>>
>> Alternatively, maybe don't try to resolve username this way until
>> we've found that the caller isn't providing any username.

> I have developed the attached patch which documents why the user name
> lookup might fail, and sets the failure string to "".  It preserves the
> memory allocation failure behavior.

I'm unimpressed with that patch.  It does nothing to fix the fundamental
problem, which is that error handling in this area is many bricks shy of
a load.  And to the extent that it addresses Christoph's complaint, it
does so only in a roundabout and utterly undocumented way.

I think we need to suck it up and fix pg_fe_getauthname to have proper
error reporting, which in turns means fixing pqGetpwuid (since the API
for that is incapable of distinguishing "no such user" from "error during
lookup").  Accordingly, I propose the attached patch instead.  Some
notes:

* According to Microsoft's documentation, Windows' GetUserName() does not
set errno (it would be mildly astonishing if it did...).  The existing
error-handling code in src/common/username.c is therefore wrong too.

* port.h seems to think it should declare pqGetpwuid() if __CYGWIN__,
but noplace else agrees with that.

* I made pg_fe_getauthname's Windows username[] array 256 bytes for
consistency with username.c, where some actual research seems to have
been expended on how large it ought to be.

* pqGetpwuid thinks there was once a four-argument spec for getpwuid_r.
That must have been in the late bronze age, because even in 1992's Single
Unix Spec it's five arguments.  And the SUS is our standard benchmark
for Unix portability.  So I think we could rip that out, along with the
corresponding configure test, at least in HEAD.  I've not done so here
though.

In principle, changing the API specs for pg_fe_getauthname and pqGetpwuid
should be safe because we don't officially export those functions.  In
practice, on platforms that don't honor the export restriction list it's
theoretically possible that somebody is calling those from third-party
code.  So what I propose we do with this is patch HEAD and 9.4 only.
We need to do *something* in 9.4 to address Christoph's complaint, and
that branch is new enough that we can probably get away with changing
officially-unsupported APIs.  The lack of other field complaints makes
me okay with not trying to fix these issues further back.

Comments?

            regards, tom lane

diff --git a/src/common/username.c b/src/common/username.c
index ee5ef1c..ac9a898 100644
*** a/src/common/username.c
--- b/src/common/username.c
*************** get_user_name(char **errstr)
*** 58,64 ****

      if (!GetUserName(username, &len))
      {
!         *errstr = psprintf(_("user name lookup failure: %s"), strerror(errno));
          return NULL;
      }

--- 58,65 ----

      if (!GetUserName(username, &len))
      {
!         *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 1d53e4e..26d7fcd 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void srandom(unsigned int seed);
*** 433,439 ****
  /* thread.h */
  extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen);

! #if !defined(WIN32) || defined(__CYGWIN__)
  extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer,
             size_t buflen, struct passwd ** result);
  #endif
--- 433,439 ----
  /* thread.h */
  extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen);

! #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 179793e..5a6a1e4 100644
*** a/src/interfaces/libpq/fe-auth.c
--- b/src/interfaces/libpq/fe-auth.c
*************** pg_fe_sendauth(AuthRequest areq, PGconn
*** 714,735 ****
  /*
   * 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.
   */
  char *
! pg_fe_getauthname(void)
  {
      const char *name = NULL;
-     char       *authn;

  #ifdef WIN32
!     char        username[128];
      DWORD        namesize = sizeof(username) - 1;
  #else
      char        pwdbuf[BUFSIZ];
      struct passwd pwdstr;
      struct passwd *pw = NULL;
  #endif

      /*
--- 714,738 ----
  /*
   * pg_fe_getauthname
   *
!  * 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(PQExpBuffer errorMessage)
  {
+     char       *result = NULL;
      const char *name = NULL;

  #ifdef WIN32
!     char        username[256];
      DWORD        namesize = sizeof(username) - 1;
  #else
+     uid_t        user_id = geteuid();
      char        pwdbuf[BUFSIZ];
      struct passwd pwdstr;
      struct passwd *pw = NULL;
+     int            pwerr;
  #endif

      /*
*************** pg_fe_getauthname(void)
*** 741,764 ****
       */
      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 (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
          name = pw->pw_name;
  #endif

!     authn = name ? strdup(name) : NULL;

      pgunlock_thread();

!     return authn;
  }


--- 744,785 ----
       */
      pglock_thread();

  #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
!     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

!     if (name)
!     {
!         result = strdup(name);
!         if (result == NULL && errorMessage)
!             printfPQExpBuffer(errorMessage,
!                               libpq_gettext("out of memory\n"));
!     }

      pgunlock_thread();

!     return result;
  }


diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 59b6c16..8d35767 100644
*** a/src/interfaces/libpq/fe-auth.h
--- b/src/interfaces/libpq/fe-auth.h
***************
*** 19,24 ****


  extern int    pg_fe_sendauth(AuthRequest areq, PGconn *conn);
! extern char *pg_fe_getauthname(void);

  #endif   /* FE_AUTH_H */
--- 19,24 ----


  extern int    pg_fe_sendauth(AuthRequest areq, PGconn *conn);
! 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 b2f556c..25961b1 100644
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
*************** static bool
*** 765,774 ****
  connectOptions2(PGconn *conn)
  {
      /*
       * 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)
              free(conn->dbName);
--- 765,790 ----
  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')
      {
          if (conn->dbName)
              free(conn->dbName);
*************** keep_going:                        /* We will come back to
*** 1967,1972 ****
--- 1983,1989 ----
                      char        pwdbuf[BUFSIZ];
                      struct passwd pass_buf;
                      struct passwd *pass;
+                     int            passerr;
                      uid_t        uid;
                      gid_t        gid;

*************** keep_going:                        /* We will come back to
*** 1987,1999 ****
                          goto error_return;
                      }

!                     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);
                          goto error_return;
                      }

--- 2004,2021 ----
                          goto error_return;
                      }

!                     passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
                      if (pass == NULL)
                      {
!                         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;
                      }

*************** conninfo_add_defaults(PQconninfoOption *
*** 4605,4622 ****
          }

          /*
!          * Special handling for "user" option
           */
          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;
!             }
              continue;
          }
      }
--- 4627,4641 ----
          }

          /*
!          * 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(NULL);
              continue;
          }
      }
*************** pqGetHomeDirectory(char *buf, int bufsiz
*** 5843,5849 ****
      struct passwd pwdstr;
      struct passwd *pwd = NULL;

!     if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
          return false;
      strlcpy(buf, pwd->pw_dir, bufsize);
      return true;
--- 5862,5869 ----
      struct passwd pwdstr;
      struct passwd *pwd = NULL;

!     (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 e8faac3..d0f72df 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** get_home_path(char *ret_path)
*** 777,783 ****
      struct passwd pwdstr;
      struct passwd *pwd = NULL;

!     if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
          return false;
      strlcpy(ret_path, pwd->pw_dir, MAXPGPATH);
      return true;
--- 777,784 ----
      struct passwd pwdstr;
      struct passwd *pwd = NULL;

!     (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 1568803..aab7451 100644
*** a/src/port/thread.c
--- b/src/port/thread.c
*************** pqStrerror(int errnum, char *strerrbuf,
*** 83,88 ****
--- 83,94 ----
  /*
   * 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
*************** pqGetpwuid(uid_t uid, struct passwd * re
*** 93,114 ****

  #ifdef GETPWUID_R_5ARG
      /* POSIX version */
!     getpwuid_r(uid, resultbuf, buffer, buflen, result);
  #else

      /*
       * Early POSIX draft of getpwuid_r() returns 'struct passwd *'.
       * getpwuid_r(uid, resultbuf, buffer, buflen)
       */
      *result = getpwuid_r(uid, resultbuf, buffer, buflen);
  #endif
  #else
-
      /* no getpwuid_r() available, just use getpwuid() */
      *result = getpwuid(uid);
  #endif
-
-     return (*result == NULL) ? -1 : 0;
  }
  #endif

--- 99,123 ----

  #ifdef GETPWUID_R_5ARG
      /* POSIX version */
!     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
  }
  #endif


Re: libpq 9.4 requires /etc/passwd?

From
Noah Misch
Date:
On Fri, Jan 09, 2015 at 06:57:02PM -0500, Tom Lane wrote:
> Commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 turned
> failure of pg_fe_getauthname() into a hard connection failure, whereas
> previously it was harmless as long as the caller provided a username.
> 
> I wonder if we shouldn't just revert that commit in toto.  Yeah,
> identifying an out-of-memory error might be useful, but this cure
> seems a lot worse than the disease.  What's more, this coding reports
> *any* pg_fe_getauthname failure as "out of memory", which is much worse
> than useless.

+1 for reverting it as the next step

> Alternatively, maybe don't try to resolve username this way until
> we've found that the caller isn't providing any username.

and for subsequently pursuing this.



Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
One other point here: I realized while testing my patch that it's actually
impossible to provoke the failure mode Christoph is unhappy about in psql.
You can only see it in an application that uses PQsetdb/PQsetdbLogin,
which of course psql doesn't anymore.  The reason is that in the PQconnect
family of functions, conninfo_add_defaults() is only applied after
filling in all available caller-supplied parameters, so if the user has
in one way or another specified the role name to use, we never invoke
pg_fe_getauthname() at all.  It's only called if we have to use the
default role name, and in that context of course a hard failure is
appropriate.  But in the PQsetdbLogin code path, we do pg_fe_getauthname
first and then overwrite (some) values from the parameters, so a failure
during conninfo_add_defaults() is premature.

This is a tad disturbing, because we are not using and therefore not
testing PQsetdb/PQsetdbLogin anywhere, which means that any failure modes
that path acquires that don't exist in the PQconnect code path are
guaranteed to go undetected in our testing.  Now, I rather doubt that
we'd have found the problem with doesn't-handle-lack-of-/etc/passwd-well
even if we had been testing that code path, but we certainly won't find
problems in paths we don't test.

Not entirely sure what to do about this, but I predict this won't be
the last complaint unless we find some way to improve test coverage
in this area.  Or perhaps we could turn PQsetdbLogin into a ***very***
thin wrapper around PQconnectdbParams?
        regards, tom lane



Re: libpq 9.4 requires /etc/passwd?

From
Christoph Berg
Date:
Re: Tom Lane 2015-01-10 <22432.1420915326@sss.pgh.pa.us>
> So what I propose we do with this is patch HEAD and 9.4 only.
> We need to do *something* in 9.4 to address Christoph's complaint, and
> that branch is new enough that we can probably get away with changing
> officially-unsupported APIs.  The lack of other field complaints makes
> me okay with not trying to fix these issues further back.

The problem isn't present in 9.3 and earlier (at least with
postfix-pgsql), so there's no need to go back further.

As for the number of complaints, I've received two independent reports
on IRC, and upon googling the problem had been seen as early as in
July [1] and August [2]. All four reports are for postfix-pgsql on
Debian, but that's probably just because we pushed 9.4 into the
next-release branch very early. (And I wish someone had told me about
the problem, instead of only reporting it for postfix...)

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=756627
[2] https://workaround.org/comment/3415#comment-3415

Christoph
-- 
cb@df7cb.de | http://www.df7cb.de/



Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
Christoph Berg <cb@df7cb.de> writes:
> Re: Tom Lane 2015-01-10 <22432.1420915326@sss.pgh.pa.us>
>> So what I propose we do with this is patch HEAD and 9.4 only.
>> We need to do *something* in 9.4 to address Christoph's complaint, and
>> that branch is new enough that we can probably get away with changing
>> officially-unsupported APIs.  The lack of other field complaints makes
>> me okay with not trying to fix these issues further back.

> The problem isn't present in 9.3 and earlier (at least with
> postfix-pgsql), so there's no need to go back further.

Right, the specific issue you're complaining of is new in 9.4.  The
general issue that failures in /etc/passwd lookups aren't well reported
has been there more or less forever, but your immediate point is that no
such failure should be reported to the user at all if he's supplied a
login name to use.

It looks like the bad handling of Windows' GetUserName() failures has
been there a long time too, but given the lack of field reports I don't
feel a need to back-patch that very far either.
        regards, tom lane



Re: libpq 9.4 requires /etc/passwd?

From
David Fetter
Date:
On Sat, Jan 10, 2015 at 02:02:54PM -0500, Tom Lane wrote:
> Not entirely sure what to do about this, but I predict this won't be
> the last complaint unless we find some way to improve test coverage
> in this area.  Or perhaps we could turn PQsetdbLogin into a
> ***very*** thin wrapper around PQconnectdbParams?

+1 for this.  Having a single point of truth here would be a big win.

Cheers,
David.
-- 
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778  AIM: dfetter666  Yahoo!: dfetter
Skype: davidfetter      XMPP: david.fetter@gmail.com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate



Re: libpq 9.4 requires /etc/passwd?

From
Tom Lane
Date:
Christoph Berg <cb@df7cb.de> writes:
> Re: Tom Lane 2015-01-10 <22432.1420915326@sss.pgh.pa.us>
>> So what I propose we do with this is patch HEAD and 9.4 only.
>> We need to do *something* in 9.4 to address Christoph's complaint, and
>> that branch is new enough that we can probably get away with changing
>> officially-unsupported APIs.  The lack of other field complaints makes
>> me okay with not trying to fix these issues further back.

> The problem isn't present in 9.3 and earlier (at least with
> postfix-pgsql), so there's no need to go back further.

I've committed a fix for this in HEAD and 9.4.
        regards, tom lane



Re: libpq 9.4 requires /etc/passwd?

From
Christoph Berg
Date:
Re: Tom Lane 2015-01-11 <13609.1420998872@sss.pgh.pa.us>
> > The problem isn't present in 9.3 and earlier (at least with
> > postfix-pgsql), so there's no need to go back further.
> 
> I've committed a fix for this in HEAD and 9.4.

I've just tested with the HEAD libpq and the issue is fixed. Thanks!

In the first iteration of the test the database was still down and I
got this error message:

Jan 11 19:08:53 lehmann postfix/trivial-rewrite[3453]: warning: connect to pgsql server localhost:5432: could not
connectto server: Connection refused??Is the server running on host "localhost" (::1) and accepting??TCP/IP connections
onport 5432??could not connect to server: Connection refused??Is the server running on host "localhost" (127.0.0.1) and
accepting??TCP/IPconnections on port 5432??
 

The ?? are probably postfix and/or syslog messing with newlines or the
like. At first I was confused that the PG part of the error message is
duplicated, but then I figured that's two different "localhost"
addresses, so all is fine.

Christoph
-- 
cb@df7cb.de | http://www.df7cb.de/