Skip to content

Commit 71e3b28

Browse files
committed
Fix libpq's code for searching .pgpass; rationalize empty-list-item cases.
Before v10, we always searched ~/.pgpass using the host parameter, and nothing else, to match to the "hostname" field of ~/.pgpass. (However, null host or host matching DEFAULT_PGSOCKET_DIR was replaced by "localhost".) In v10, this got broken by commit 274bb2b, repaired by commit bdac983, and broken again by commit 7b02ba6; in the code actually shipped, we'd search with hostaddr if both that and host were specified --- though oddly, *not* if only hostaddr were specified. Since this is directly contrary to the documentation, and not backwards-compatible, it's clearly a bug. However, the change wasn't totally without justification, even though it wasn't done quite right, because the pre-v10 behavior has arguably been buggy since we added hostaddr. If hostaddr is specified and host isn't, the pre-v10 code will search ~/.pgpass for "localhost", and ship that password off to a server that most likely isn't local at all. That's unhelpful at best, and could be a security breach at worst. Therefore, rather than just revert to that old behavior, let's define the behavior as "search with host if provided, else with hostaddr if provided, else search for localhost". (As before, a host name matching DEFAULT_PGSOCKET_DIR is replaced by localhost.) This matches the behavior of the actual connection code, so that we don't pick up an inappropriate password; and it allows useful searches to happen when only hostaddr is given. While we're messing around here, ensure that empty elements within a host or hostaddr list select the same behavior as a totally-empty field would; for instance "host=a,,b" is equivalent to "host=a,/tmp,b" if DEFAULT_PGSOCKET_DIR is /tmp. Things worked that way in some cases already, but not consistently so, which contributed to the confusion about what key ~/.pgpass would get searched with. Update documentation accordingly, and also clarify some nearby text. Back-patch to v10 where the host/hostaddr list functionality was introduced. Discussion: https://postgr.es/m/30805.1532749137@sss.pgh.pa.us
1 parent d8dd8d2 commit 71e3b28

File tree

2 files changed

+83
-55
lines changed

2 files changed

+83
-55
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -938,8 +938,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
938938
<para>
939939
If a password file is used, you can have different passwords for
940940
different hosts. All the other connection options are the same for every
941-
host, it is not possible to e.g. specify a different username for
942-
different hosts.
941+
host in the list; it is not possible to e.g. specify different
942+
usernames for different hosts.
943943
</para>
944944
</sect3>
945945
</sect2>
@@ -961,15 +961,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
961961
name of the directory in which the socket file is stored. If
962962
multiple host names are specified, each will be tried in turn in
963963
the order given. The default behavior when <literal>host</literal> is
964-
not specified is to connect to a Unix-domain
964+
not specified, or is empty, is to connect to a Unix-domain
965965
socket<indexterm><primary>Unix domain socket</></> in
966966
<filename>/tmp</filename> (or whatever socket directory was specified
967967
when <productname>PostgreSQL</> was built). On machines without
968968
Unix-domain sockets, the default is to connect to <literal>localhost</>.
969969
</para>
970970
<para>
971971
A comma-separated list of host names is also accepted, in which case
972-
each host name in the list is tried in order. See
972+
each host name in the list is tried in order; an empty item in the
973+
list selects the default behavior as explained above. See
973974
<xref linkend="libpq-multiple-hosts"> for details.
974975
</para>
975976
</listitem>
@@ -1020,14 +1021,17 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
10201021
</itemizedlist>
10211022
Note that authentication is likely to fail if <literal>host</>
10221023
is not the name of the server at network address <literal>hostaddr</>.
1023-
Also, note that <literal>host</> rather than <literal>hostaddr</>
1024+
Also, when both <literal>host</literal> and <literal>hostaddr</literal>
1025+
are specified, <literal>host</literal>
10241026
is used to identify the connection in a password file (see
10251027
<xref linkend="libpq-pgpass">).
10261028
</para>
10271029

10281030
<para>
10291031
A comma-separated list of <literal>hostaddr</literal> values is also
1030-
accepted, in which case each host in the list is tried in order. See
1032+
accepted, in which case each host in the list is tried in order.
1033+
An empty item in the list causes the corresponding host name to be
1034+
used, or the default host name if that is empty as well. See
10311035
<xref linkend="libpq-multiple-hosts"> for details.
10321036
</para>
10331037
<para>
@@ -1047,9 +1051,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
10471051
name extension for Unix-domain
10481052
connections.<indexterm><primary>port</></>
10491053
If multiple hosts were given in the <literal>host</literal> or
1050-
<literal>hostaddr</> parameters, this parameter may specify a list
1051-
of ports of equal length, or it may specify a single port number to
1052-
be used for all hosts.
1054+
<literal>hostaddr</literal> parameters, this parameter may specify a
1055+
comma-separated list of ports of the same length as the host list, or
1056+
it may specify a single port number to be used for all hosts.
1057+
An empty string, or an empty item in a comma-separated list,
1058+
specifies the default port number established
1059+
when <productname>PostgreSQL</productname> was built.
10531060
</para>
10541061
</listitem>
10551062
</varlistentry>
@@ -1588,6 +1595,17 @@ char *PQuser(const PGconn *conn);
15881595
char *PQpass(const PGconn *conn);
15891596
</synopsis>
15901597
</para>
1598+
1599+
<para>
1600+
<function>PQpass</function> will return either the password specified
1601+
in the connection parameters, or if there was none and the password
1602+
was obtained from the <link linkend="libpq-pgpass">password
1603+
file</link>, it will return that. In the latter case,
1604+
if multiple hosts were specified in the connection parameters, it is
1605+
not possible to rely on the result of <function>PQpass</function> until
1606+
the connection is established. The status of the connection can be
1607+
checked using the function <function>PQstatus</function>.
1608+
</para>
15911609
</listitem>
15921610
</varlistentry>
15931611

@@ -7380,13 +7398,18 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
73807398
used. (Therefore, put more-specific entries first when you are using
73817399
wildcards.) If an entry needs to contain <literal>:</literal> or
73827400
<literal>\</literal>, escape this character with <literal>\</literal>.
7383-
A host name of <literal>localhost</> matches both TCP (host name
7384-
<literal>localhost</>) and Unix domain socket (<literal>pghost</> empty
7385-
or the default socket directory) connections coming from the local
7386-
machine. In a standby server, a database name of <literal>replication</>
7401+
The host name field is matched to the <literal>host</literal> connection
7402+
parameter if that is specified, otherwise to
7403+
the <literal>hostaddr</literal> parameter if that is specified; if neither
7404+
are given then the host name <literal>localhost</literal> is searched for.
7405+
The host name <literal>localhost</literal> is also searched for when
7406+
the connection is a Unix-domain socket connection and
7407+
the <literal>host</literal> parameter
7408+
matches <application>libpq</application>'s default socket directory path.
7409+
In a standby server, a database field of <literal>replication</literal>
73877410
matches streaming replication connections made to the master server.
7388-
The <literal>database</> field is of limited usefulness because
7389-
users have the same password for all databases in the same cluster.
7411+
The database field is of limited usefulness otherwise, because users have
7412+
the same password for all databases in the same cluster.
73907413
</para>
73917414

73927415
<para>

src/interfaces/libpq/fe-connect.c

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,8 @@ parse_comma_separated_list(char **startptr, bool *more)
894894
static bool
895895
connectOptions2(PGconn *conn)
896896
{
897+
int i;
898+
897899
/*
898900
* Allocate memory for details about each host to which we might possibly
899901
* try to connect. For that, count the number of elements in the hostaddr
@@ -913,11 +915,10 @@ connectOptions2(PGconn *conn)
913915

914916
/*
915917
* We now have one pg_conn_host structure per possible host. Fill in the
916-
* host details for each one.
918+
* host and hostaddr fields for each, by splitting the parameter strings.
917919
*/
918920
if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
919921
{
920-
int i;
921922
char *s = conn->pghostaddr;
922923
bool more = true;
923924

@@ -926,8 +927,6 @@ connectOptions2(PGconn *conn)
926927
conn->connhost[i].hostaddr = parse_comma_separated_list(&s, &more);
927928
if (conn->connhost[i].hostaddr == NULL)
928929
goto oom_error;
929-
930-
conn->connhost[i].type = CHT_HOST_ADDRESS;
931930
}
932931

933932
/*
@@ -941,7 +940,6 @@ connectOptions2(PGconn *conn)
941940

942941
if (conn->pghost != NULL && conn->pghost[0] != '\0')
943942
{
944-
int i;
945943
char *s = conn->pghost;
946944
bool more = true;
947945

@@ -950,17 +948,9 @@ connectOptions2(PGconn *conn)
950948
conn->connhost[i].host = parse_comma_separated_list(&s, &more);
951949
if (conn->connhost[i].host == NULL)
952950
goto oom_error;
953-
954-
/* Identify the type of host. */
955-
if (conn->pghostaddr == NULL || conn->pghostaddr[0] == '\0')
956-
{
957-
conn->connhost[i].type = CHT_HOST_NAME;
958-
#ifdef HAVE_UNIX_SOCKETS
959-
if (is_absolute_path(conn->connhost[i].host))
960-
conn->connhost[i].type = CHT_UNIX_SOCKET;
961-
#endif
962-
}
963951
}
952+
953+
/* Check for wrong number of host items. */
964954
if (more || i != conn->nconnhost)
965955
{
966956
conn->status = CONNECTION_BAD;
@@ -972,29 +962,48 @@ connectOptions2(PGconn *conn)
972962
}
973963

974964
/*
975-
* If neither host or hostaddr options was given, connect to default host.
965+
* Now, for each host slot, identify the type of address spec, and fill in
966+
* the default address if nothing was given.
976967
*/
977-
if ((conn->pghostaddr == NULL || conn->pghostaddr[0] == '\0') &&
978-
(conn->pghost == NULL || conn->pghost[0] == '\0'))
968+
for (i = 0; i < conn->nconnhost; i++)
979969
{
980-
Assert(conn->nconnhost == 1);
970+
pg_conn_host *ch = &conn->connhost[i];
971+
972+
if (ch->hostaddr != NULL && ch->hostaddr[0] != '\0')
973+
ch->type = CHT_HOST_ADDRESS;
974+
else if (ch->host != NULL && ch->host[0] != '\0')
975+
{
976+
ch->type = CHT_HOST_NAME;
981977
#ifdef HAVE_UNIX_SOCKETS
982-
conn->connhost[0].host = strdup(DEFAULT_PGSOCKET_DIR);
983-
conn->connhost[0].type = CHT_UNIX_SOCKET;
978+
if (is_absolute_path(ch->host))
979+
ch->type = CHT_UNIX_SOCKET;
980+
#endif
981+
}
982+
else
983+
{
984+
if (ch->host)
985+
free(ch->host);
986+
#ifdef HAVE_UNIX_SOCKETS
987+
ch->host = strdup(DEFAULT_PGSOCKET_DIR);
988+
ch->type = CHT_UNIX_SOCKET;
984989
#else
985-
conn->connhost[0].host = strdup(DefaultHost);
986-
conn->connhost[0].type = CHT_HOST_NAME;
990+
ch->host = strdup(DefaultHost);
991+
ch->type = CHT_HOST_NAME;
987992
#endif
988-
if (conn->connhost[0].host == NULL)
989-
goto oom_error;
993+
if (ch->host == NULL)
994+
goto oom_error;
995+
}
990996
}
991997

992998
/*
993999
* Next, work out the port number corresponding to each host name.
1000+
*
1001+
* Note: unlike the above for host names, this could leave the port fields
1002+
* as null or empty strings. We will substitute DEF_PGPORT whenever we
1003+
* read such a port field.
9941004
*/
9951005
if (conn->pgport != NULL && conn->pgport[0] != '\0')
9961006
{
997-
int i;
9981007
char *s = conn->pgport;
9991008
bool more = true;
10001009

@@ -1058,8 +1067,8 @@ connectOptions2(PGconn *conn)
10581067
}
10591068

10601069
/*
1061-
* Supply default password if none given. Note that the password might be
1062-
* different for each host/port pair.
1070+
* If password was not given, try to look it up in password file. Note
1071+
* that the result might be different for each host/port pair.
10631072
*/
10641073
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
10651074
{
@@ -1082,20 +1091,16 @@ connectOptions2(PGconn *conn)
10821091

10831092
if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
10841093
{
1085-
int i;
1086-
10871094
for (i = 0; i < conn->nconnhost; i++)
10881095
{
10891096
/*
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().
1097+
* Try to get a password for this host from file. We use host
1098+
* for the hostname search key if given, else hostaddr (at
1099+
* least one of them is guaranteed nonempty by now).
10931100
*/
10941101
char *pwhost = conn->connhost[i].host;
10951102

1096-
if (conn->connhost[i].type == CHT_HOST_ADDRESS &&
1097-
conn->connhost[i].host != NULL &&
1098-
conn->connhost[i].host[0] != '\0')
1103+
if (pwhost == NULL || pwhost[0] == '\0')
10991104
pwhost = conn->connhost[i].hostaddr;
11001105

11011106
conn->connhost[i].password =
@@ -6375,14 +6380,14 @@ passwordFromFile(char *hostname, char *port, char *dbname,
63756380
#define LINELEN NAMEDATALEN*5
63766381
char buf[LINELEN];
63776382

6378-
if (dbname == NULL || strlen(dbname) == 0)
6383+
if (dbname == NULL || dbname[0] == '\0')
63796384
return NULL;
63806385

6381-
if (username == NULL || strlen(username) == 0)
6386+
if (username == NULL || username[0] == '\0')
63826387
return NULL;
63836388

63846389
/* 'localhost' matches pghost of '' or the default socket directory */
6385-
if (hostname == NULL)
6390+
if (hostname == NULL || hostname[0] == '\0')
63866391
hostname = DefaultHost;
63876392
else if (is_absolute_path(hostname))
63886393

@@ -6393,7 +6398,7 @@ passwordFromFile(char *hostname, char *port, char *dbname,
63936398
if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0)
63946399
hostname = DefaultHost;
63956400

6396-
if (port == NULL)
6401+
if (port == NULL || port[0] == '\0')
63976402
port = DEF_PGPORT_STR;
63986403

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

0 commit comments

Comments
 (0)