]> granicus.if.org Git - postgresql/commitdiff
When reading pg_hba.conf and similar files, do not treat @file as an inclusion
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Mar 2010 00:46:13 +0000 (00:46 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Mar 2010 00:46:13 +0000 (00:46 +0000)
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.

src/backend/libpq/hba.c

index a8f1bd36067dccfe3109ed3292cdfbf9f6bf3996..300faece07ded63886e213cb512183923b18b491 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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).
  *
  * 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 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.
  */
 static bool
-next_token(FILE *fp, char *buf, int bufsz)
+next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
 {
        int                     c;
        char       *start_buf = buf;
@@ -123,8 +127,11 @@ next_token(FILE *fp, char *buf, int bufsz)
        bool            was_quote = false;
        bool            saw_quote = false;
 
+       /* end_buf reserves two bytes to ensure we can append \n and \0 */
        Assert(end_buf > start_buf);
 
+       *initial_quote = false;
+
        /* Move over initial whitespace and commas */
        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;
                        saw_quote = true;
+                       if (buf == start_buf)
+                               *initial_quote = true;
                }
 
                c = getc(fp);
@@ -225,12 +234,13 @@ next_token_expand(const char *filename, FILE *file)
        char       *comma_str = pstrdup("");
        bool            got_something = false;
        bool            trailing_comma;
+       bool            initial_quote;
        char       *incbuf;
        int                     needed;
 
        do
        {
-               if (!next_token(file, buf, sizeof(buf)))
+               if (!next_token(file, buf, sizeof(buf), &initial_quote))
                        break;
 
                got_something = true;
@@ -244,7 +254,7 @@ next_token_expand(const char *filename, FILE *file)
                        trailing_comma = false;
 
                /* Is this referencing a file? */
-               if (buf[0] == '@')
+               if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
                        incbuf = tokenize_inc_file(filename, buf + 1);
                else
                        incbuf = pstrdup(buf);
@@ -1013,32 +1023,33 @@ read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
                                          TransactionId *dbvacuumxid)
 {
        char            buf[MAX_TOKEN];
+       bool            initial_quote;
 
        if (feof(fp))
                return false;
-       if (!next_token(fp, buf, sizeof(buf)))
+       if (!next_token(fp, buf, sizeof(buf), &initial_quote))
                return false;
        if (strlen(buf) >= NAMEDATALEN)
                elog(FATAL, "bad data in flat pg_database file");
        strcpy(dbname, buf);
-       next_token(fp, buf, sizeof(buf));
+       next_token(fp, buf, sizeof(buf), &initial_quote);
        if (!isdigit((unsigned char) buf[0]))
                elog(FATAL, "bad data in flat pg_database file");
        *dboid = atooid(buf);
-       next_token(fp, buf, sizeof(buf));
+       next_token(fp, buf, sizeof(buf), &initial_quote);
        if (!isdigit((unsigned char) buf[0]))
                elog(FATAL, "bad data in flat pg_database file");
        *dbtablespace = atooid(buf);
-       next_token(fp, buf, sizeof(buf));
+       next_token(fp, buf, sizeof(buf), &initial_quote);
        if (!isdigit((unsigned char) buf[0]))
                elog(FATAL, "bad data in flat pg_database file");
        *dbfrozenxid = atoxid(buf);
-       next_token(fp, buf, sizeof(buf));
+       next_token(fp, buf, sizeof(buf), &initial_quote);
        if (!isdigit((unsigned char) buf[0]))
                elog(FATAL, "bad data in flat pg_database file");
        *dbvacuumxid = atoxid(buf);
        /* 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");
        return true;
 }