Skip to content

Commit 8d486cf

Browse files
committed
Teach libpq to handle arbitrary-length lines in .pgpass files.
Historically there's been a hard-wired assumption here that no line of a .pgpass file could be as long as NAMEDATALEN*5 bytes. That's a bit shaky to start off with, because (a) there's no reason to suppose that host names fit in NAMEDATALEN, and (b) this figure fails to allow for backslash escape characters. However, it fails completely if someone wants to use a very long password, and we're now hearing reports of people wanting to use "security tokens" that can run up to several hundred bytes. Another angle is that the file is specified to allow comment lines, but there's no reason to assume that long comment lines aren't possible. Rather than guessing at what might be a more suitable limit, let's replace the fixed-size buffer with an expansible PQExpBuffer. That adds one malloc/free cycle to the typical use-case, but that's surely pretty cheap relative to the I/O this code has to do. Also, add TAP test cases to exercise this code, because there was no test coverage before. This reverts most of commit 2eb3bc5, as there's no longer a need for a warning message about overlength .pgpass lines. (I kept the explicit check for comment lines, though.) In HEAD and v13, this also fixes an oversight in 74a308c: there's not much point in explicit_bzero'ing the line buffer if we only do so in two of the three exit paths. Back-patch to all supported branches, except that the test case only goes back to v10 where src/test/authentication/ was added. Discussion: https://postgr.es/m/4187382.1598909041@sss.pgh.pa.us
1 parent 2dbe3ea commit 8d486cf

File tree

1 file changed

+58
-42
lines changed

1 file changed

+58
-42
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5825,9 +5825,7 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
58255825
FILE *fp;
58265826
char pgpassfile[MAXPGPATH];
58275827
struct stat stat_buf;
5828-
5829-
#define LINELEN NAMEDATALEN*5
5830-
char buf[LINELEN];
5828+
PQExpBufferData buf;
58315829

58325830
if (dbname == NULL || strlen(dbname) == 0)
58335831
return NULL;
@@ -5886,63 +5884,81 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
58865884
if (fp == NULL)
58875885
return NULL;
58885886

5887+
/* Use an expansible buffer to accommodate any reasonable line length */
5888+
initPQExpBuffer(&buf);
5889+
58895890
while (!feof(fp) && !ferror(fp))
58905891
{
5891-
char *t = buf,
5892-
*ret,
5893-
*p1,
5894-
*p2;
5895-
int len;
5892+
/* Make sure there's a reasonable amount of room in the buffer */
5893+
if (!enlargePQExpBuffer(&buf, 128))
5894+
break;
58965895

5897-
if (fgets(buf, sizeof(buf), fp) == NULL)
5896+
/* Read some data, appending it to what we already have */
5897+
if (fgets(buf.data + buf.len, buf.maxlen - buf.len, fp) == NULL)
58985898
break;
5899+
buf.len += strlen(buf.data + buf.len);
58995900

5900-
len = strlen(buf);
5901+
/* If we don't yet have a whole line, loop around to read more */
5902+
if (!(buf.len > 0 && buf.data[buf.len - 1] == '\n') && !feof(fp))
5903+
continue;
59015904

5902-
/* Remove trailing newline */
5903-
if (len > 0 && buf[len - 1] == '\n')
5905+
/* ignore comments */
5906+
if (buf.data[0] != '#')
59045907
{
5905-
buf[--len] = '\0';
5906-
/* Handle DOS-style line endings, too, even when not on Windows */
5907-
if (len > 0 && buf[len - 1] == '\r')
5908-
buf[--len] = '\0';
5909-
}
5908+
char *t = buf.data;
5909+
int len = buf.len;
59105910

5911-
if (len == 0)
5912-
continue;
5911+
/* Remove trailing newline */
5912+
if (len > 0 && t[len - 1] == '\n')
5913+
{
5914+
t[--len] = '\0';
5915+
/* Handle DOS-style line endings, too */
5916+
if (len > 0 && t[len - 1] == '\r')
5917+
t[--len] = '\0';
5918+
}
59135919

5914-
if ((t = pwdfMatchesString(t, hostname)) == NULL ||
5915-
(t = pwdfMatchesString(t, port)) == NULL ||
5916-
(t = pwdfMatchesString(t, dbname)) == NULL ||
5917-
(t = pwdfMatchesString(t, username)) == NULL)
5918-
continue;
5920+
if (len > 0 &&
5921+
(t = pwdfMatchesString(t, hostname)) != NULL &&
5922+
(t = pwdfMatchesString(t, port)) != NULL &&
5923+
(t = pwdfMatchesString(t, dbname)) != NULL &&
5924+
(t = pwdfMatchesString(t, username)) != NULL)
5925+
{
5926+
/* Found a match. */
5927+
char *ret,
5928+
*p1,
5929+
*p2;
59195930

5920-
/* Found a match. */
5921-
ret = strdup(t);
5922-
fclose(fp);
5931+
ret = strdup(t);
59235932

5924-
if (!ret)
5925-
{
5926-
/* Out of memory. XXX: an error message would be nice. */
5927-
return NULL;
5928-
}
5933+
fclose(fp);
5934+
termPQExpBuffer(&buf);
59295935

5930-
/* De-escape password. */
5931-
for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2)
5932-
{
5933-
if (*p1 == '\\' && p1[1] != '\0')
5934-
++p1;
5935-
*p2 = *p1;
5936+
if (!ret)
5937+
{
5938+
/* Out of memory. XXX: an error message would be nice. */
5939+
return NULL;
5940+
}
5941+
5942+
/* De-escape password. */
5943+
for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2)
5944+
{
5945+
if (*p1 == '\\' && p1[1] != '\0')
5946+
++p1;
5947+
*p2 = *p1;
5948+
}
5949+
*p2 = '\0';
5950+
5951+
return ret;
5952+
}
59365953
}
5937-
*p2 = '\0';
59385954

5939-
return ret;
5955+
/* No match, reset buffer to prepare for next line. */
5956+
buf.len = 0;
59405957
}
59415958

59425959
fclose(fp);
5960+
termPQExpBuffer(&buf);
59435961
return NULL;
5944-
5945-
#undef LINELEN
59465962
}
59475963

59485964

0 commit comments

Comments
 (0)