1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

When reading pg_hba.conf and similar files, do not treat @file as an inclusion

unless (1) the @ isn't quoted and (2) the filename isn't empty.  This guards
against unexpectedly treating usernames or other strings in "flat files"
as inclusion requests, as seen in a recent trouble report from Ed L.
The empty-filename case would be guaranteed to misbehave anyway, because our
subsequent path-munging behavior results in trying to read the directory
containing the current input file.

I think this might finally explain the report at
http://archives.postgresql.org/pgsql-bugs/2004-05/msg00132.php
of a crash after printing "authentication file token too long, skipping",
since I was able to duplicate that message (though not a crash) on a
platform where stdio doesn't refuse to read directories.  We never got
far in investigating that problem, but now I'm suspicious that the trigger
condition was an @ in the flat password file.

Back-patch to all active branches since the problem can be demonstrated in all
branches except HEAD.  The test case, creating a user named "@", doesn't cause
a problem in HEAD since we got rid of the flat password file.  Nonetheless it
seems like a good idea to not consider quoted @ as a file inclusion spec,
so I changed HEAD too.
This commit is contained in:
Tom Lane
2010-03-06 00:46:13 +00:00
parent 14669da788
commit 0a32a06bc9

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.149.2.1 2010/03/03 20:31:34 tgl Exp $ * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.149.2.2 2010/03/06 00:46:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -102,6 +102,10 @@ pg_isblank(const char c)
* double quotes (and usually are, in current usage). * double quotes (and usually are, in current usage).
* *
* The token, if any, is returned at *buf (a buffer of size bufsz). * The token, if any, is returned at *buf (a buffer of size bufsz).
* Also, we set *initial_quote to indicate whether there was quoting before
* the first character. (We use that to prevent "@x" from being treated
* as a file inclusion request. Note that @"x" should be so treated;
* we want to allow that to support embedded spaces in file paths.)
* *
* If successful: store null-terminated token at *buf and return TRUE. * If successful: store null-terminated token at *buf and return TRUE.
* If no more tokens on line: set *buf = '\0' and return FALSE. * If no more tokens on line: set *buf = '\0' and return FALSE.
@ -114,7 +118,7 @@ pg_isblank(const char c)
* database names specially, by appending a newline to them. * database names specially, by appending a newline to them.
*/ */
static bool static bool
next_token(FILE *fp, char *buf, int bufsz) next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
{ {
int c; int c;
char *start_buf = buf; char *start_buf = buf;
@ -123,8 +127,11 @@ next_token(FILE *fp, char *buf, int bufsz)
bool was_quote = false; bool was_quote = false;
bool saw_quote = false; bool saw_quote = false;
/* end_buf reserves two bytes to ensure we can append \n and \0 */
Assert(end_buf > start_buf); Assert(end_buf > start_buf);
*initial_quote = false;
/* Move over initial whitespace and commas */ /* Move over initial whitespace and commas */
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ',')) while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
; ;
@ -183,6 +190,8 @@ next_token(FILE *fp, char *buf, int bufsz)
{ {
in_quote = !in_quote; in_quote = !in_quote;
saw_quote = true; saw_quote = true;
if (buf == start_buf)
*initial_quote = true;
} }
c = getc(fp); c = getc(fp);
@ -225,12 +234,13 @@ next_token_expand(const char *filename, FILE *file)
char *comma_str = pstrdup(""); char *comma_str = pstrdup("");
bool got_something = false; bool got_something = false;
bool trailing_comma; bool trailing_comma;
bool initial_quote;
char *incbuf; char *incbuf;
int needed; int needed;
do do
{ {
if (!next_token(file, buf, sizeof(buf))) if (!next_token(file, buf, sizeof(buf), &initial_quote))
break; break;
got_something = true; got_something = true;
@ -244,7 +254,7 @@ next_token_expand(const char *filename, FILE *file)
trailing_comma = false; trailing_comma = false;
/* Is this referencing a file? */ /* Is this referencing a file? */
if (buf[0] == '@') if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
incbuf = tokenize_inc_file(filename, buf + 1); incbuf = tokenize_inc_file(filename, buf + 1);
else else
incbuf = pstrdup(buf); incbuf = pstrdup(buf);
@ -1013,32 +1023,33 @@ read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
TransactionId *dbvacuumxid) TransactionId *dbvacuumxid)
{ {
char buf[MAX_TOKEN]; char buf[MAX_TOKEN];
bool initial_quote;
if (feof(fp)) if (feof(fp))
return false; return false;
if (!next_token(fp, buf, sizeof(buf))) if (!next_token(fp, buf, sizeof(buf), &initial_quote))
return false; return false;
if (strlen(buf) >= NAMEDATALEN) if (strlen(buf) >= NAMEDATALEN)
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
strcpy(dbname, buf); strcpy(dbname, buf);
next_token(fp, buf, sizeof(buf)); next_token(fp, buf, sizeof(buf), &initial_quote);
if (!isdigit((unsigned char) buf[0])) if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
*dboid = atooid(buf); *dboid = atooid(buf);
next_token(fp, buf, sizeof(buf)); next_token(fp, buf, sizeof(buf), &initial_quote);
if (!isdigit((unsigned char) buf[0])) if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
*dbtablespace = atooid(buf); *dbtablespace = atooid(buf);
next_token(fp, buf, sizeof(buf)); next_token(fp, buf, sizeof(buf), &initial_quote);
if (!isdigit((unsigned char) buf[0])) if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
*dbfrozenxid = atoxid(buf); *dbfrozenxid = atoxid(buf);
next_token(fp, buf, sizeof(buf)); next_token(fp, buf, sizeof(buf), &initial_quote);
if (!isdigit((unsigned char) buf[0])) if (!isdigit((unsigned char) buf[0]))
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
*dbvacuumxid = atoxid(buf); *dbvacuumxid = atoxid(buf);
/* expect EOL next */ /* expect EOL next */
if (next_token(fp, buf, sizeof(buf))) if (next_token(fp, buf, sizeof(buf), &initial_quote))
elog(FATAL, "bad data in flat pg_database file"); elog(FATAL, "bad data in flat pg_database file");
return true; return true;
} }