Re: Bizarre behavior in libpq's searching of ~/.pgpass - Mailing list pgsql-hackers

From Tom Lane
Subject Re: Bizarre behavior in libpq's searching of ~/.pgpass
Date
Msg-id 11620.1532898945@sss.pgh.pa.us
Whole thread Raw
In response to Bizarre behavior in libpq's searching of ~/.pgpass  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Bizarre behavior in libpq's searching of ~/.pgpass
List pgsql-hackers
I wrote
> I noticed that there's some strange coding in libpq's choice of
> what hostname to use for searching ~/.pgpass for a password.
> ...

> So my first thought was that we should go back to the pre-v10 behavior
> of considering only the host parameter, which it looks like would only
> require removing the "if" bit above.

> But on second thought, I'm not clear that the pre-v10 behavior is really
> all that sane either.  What it means is that if you specify only hostaddr,
> the code will happily grab your localhost password and send it off to
> whatever server hostaddr references.  This is unlikely to be helpful,
> and it could even be painted as a security breach --- the remote server
> could ask for your password in plaintext and then capture it.

> What seems like a saner definition is "use host if it's specified
> (nonempty), else use hostaddr if it's specified (nonempty), else
> fall back to localhost".  That avoids sending a password somewhere
> it doesn't belong, and allows a useful ~/.pgpass lookup in cases
> where only hostaddr is given -- you just need to make an entry
> with the numeric IP address in the host column.

> I think it's not too late to make v11 work that way, but I wonder
> what we ought to do in v10.  Comments?

Here's a proposed patch to adopt that behavior.  I'm still of mixed
mind whether to push this into v10 ... but we definitely need some
change in v10, because it's not acting as per its docs.

            regards, tom lane

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d67212b..0bcaeb9 100644
*** a/doc/src/sgml/libpq.sgml
--- b/doc/src/sgml/libpq.sgml
*************** postgresql://%2Fvar%2Flib%2Fpostgresql/d
*** 1020,1026 ****
          </itemizedlist>
          Note that authentication is likely to fail if <literal>host</literal>
          is not the name of the server at network address <literal>hostaddr</literal>.
!         Also, note that <literal>host</literal> rather than <literal>hostaddr</literal>
          is used to identify the connection in a password file (see
          <xref linkend="libpq-pgpass"/>).
         </para>
--- 1020,1027 ----
          </itemizedlist>
          Note that authentication is likely to fail if <literal>host</literal>
          is not the name of the server at network address <literal>hostaddr</literal>.
!         Also, when both <literal>host</literal> and <literal>hostaddr</literal>
!         are specified, <literal>host</literal>
          is used to identify the connection in a password file (see
          <xref linkend="libpq-pgpass"/>).
         </para>
*************** myEventProc(PGEventId evtId, void *evtIn
*** 7521,7533 ****
     used.  (Therefore, put more-specific entries first when you are using
     wildcards.) If an entry needs to contain <literal>:</literal> or
     <literal>\</literal>, escape this character with <literal>\</literal>.
!    A host name of <literal>localhost</literal> matches both TCP (host name
!    <literal>localhost</literal>) and Unix domain socket (<literal>pghost</literal> empty
!    or the default socket directory) connections coming from the local
!    machine. In a standby server, a database name of <literal>replication</literal>
     matches streaming replication connections made to the master server.
!    The <literal>database</literal> field is of limited usefulness because
!    users have the same password for all databases in the same cluster.
    </para>

    <para>
--- 7522,7538 ----
     used.  (Therefore, put more-specific entries first when you are using
     wildcards.) If an entry needs to contain <literal>:</literal> or
     <literal>\</literal>, escape this character with <literal>\</literal>.
!    The host name field is matched to the <literal>host</literal> connection
!    parameter if that is specified, otherwise to
!    the <literal>hostaddr</literal> parameter if that is specified; if neither
!    are given then the host name <literal>localhost</literal> is searched for.
!    A host name field of <literal>localhost</literal> will also match
!    Unix-domain socket connections when the <literal>host</literal> parameter
!    equals the installation's default socket directory path.
!    In a standby server, a database name of <literal>replication</literal>
     matches streaming replication connections made to the master server.
!    The <literal>database</literal> field is of limited usefulness otherwise,
!    because users have the same password for all databases in the same cluster.
    </para>

    <para>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bd7dac1..c4d27c4 100644
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
*************** connectOptions2(PGconn *conn)
*** 1065,1072 ****
      }

      /*
!      * Supply default password if none given.  Note that the password might be
!      * different for each host/port pair.
       */
      if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
      {
--- 1065,1072 ----
      }

      /*
!      * If password was not given, try to look it up in password file.  Note
!      * that the result might be different for each host/port pair.
       */
      if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
      {
*************** connectOptions2(PGconn *conn)
*** 1094,1108 ****
              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 =
--- 1094,1107 ----
              for (i = 0; i < conn->nconnhost; i++)
              {
                  /*
!                  * Try to get a password for this host from file.  We use host
!                  * if given, else hostaddr if given; if both are omitted then
!                  * the search will be for "localhost".  (That's handled inside
!                  * passwordFromFile.)
                   */
!                 const char *pwhost = conn->connhost[i].host;

!                 if (pwhost == NULL || pwhost[0] == '\0')
                      pwhost = conn->connhost[i].hostaddr;

                  conn->connhost[i].password =
*************** passwordFromFile(const char *hostname, c
*** 6385,6398 ****
  #define LINELEN NAMEDATALEN*5
      char        buf[LINELEN];

!     if (dbname == NULL || strlen(dbname) == 0)
          return NULL;

!     if (username == NULL || strlen(username) == 0)
          return NULL;

      /* 'localhost' matches pghost of '' or the default socket directory */
!     if (hostname == NULL)
          hostname = DefaultHost;
      else if (is_absolute_path(hostname))

--- 6384,6397 ----
  #define LINELEN NAMEDATALEN*5
      char        buf[LINELEN];

!     if (dbname == NULL || dbname[0] == '\0')
          return NULL;

!     if (username == NULL || username[0] == '\0')
          return NULL;

      /* 'localhost' matches pghost of '' or the default socket directory */
!     if (hostname == NULL || hostname[0] == '\0')
          hostname = DefaultHost;
      else if (is_absolute_path(hostname))

*************** passwordFromFile(const char *hostname, c
*** 6403,6409 ****
          if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0)
              hostname = DefaultHost;

!     if (port == NULL)
          port = DEF_PGPORT_STR;

      /* If password file cannot be opened, ignore it. */
--- 6402,6408 ----
          if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0)
              hostname = DefaultHost;

!     if (port == NULL || port[0] == '\0')
          port = DEF_PGPORT_STR;

      /* If password file cannot be opened, ignore it. */

pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: [PATCH] Improve geometric types
Next
From: Tomas Vondra
Date:
Subject: Re: [PATCH] Improve geometric types