Skip to content

Commit 7f49a67

Browse files
committed
Report pg_hba line number and contents when users fail to log in
Instead of just reporting which user failed to log in, log both the line number in the active pg_hba.conf file (which may not match reality in case the file has been edited and not reloaded) and the contents of the matching line (which will always be correct), to make it easier to debug incorrect pg_hba.conf files. The message to the client remains unchanged and does not include this information, to prevent leaking security sensitive information. Reviewed by Tom Lane and Dean Rasheed
1 parent 96443d1 commit 7f49a67

File tree

3 files changed

+81
-48
lines changed

3 files changed

+81
-48
lines changed

src/backend/libpq/auth.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,16 @@ auth_failed(Port *port, int status)
297297
break;
298298
}
299299

300-
ereport(FATAL,
301-
(errcode(errcode_return),
302-
errmsg(errstr, port->user_name)));
300+
if (port->hba)
301+
ereport(FATAL,
302+
(errcode(errcode_return),
303+
errmsg(errstr, port->user_name),
304+
errdetail_log("Connection matched pg_hba.conf line %d: \"%s\"", port->hba->linenumber, port->hba->rawline)));
305+
else
306+
ereport(FATAL,
307+
(errcode(errcode_return),
308+
errmsg(errstr, port->user_name)));
309+
303310
/* doesn't return */
304311
}
305312

src/backend/libpq/hba.c

Lines changed: 70 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
5151

5252
#define MAX_TOKEN 256
53+
#define MAX_LINE 8192
5354

5455
/* callback data for check_network_callback */
5556
typedef struct check_network_data
@@ -93,7 +94,7 @@ static MemoryContext parsed_ident_context = NULL;
9394

9495

9596
static MemoryContext tokenize_file(const char *filename, FILE *file,
96-
List **lines, List **line_nums);
97+
List **lines, List **line_nums, List **raw_lines);
9798
static List *tokenize_inc_file(List *tokens, const char *outer_filename,
9899
const char *inc_filename);
99100
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
@@ -111,7 +112,8 @@ pg_isblank(const char c)
111112

112113

113114
/*
114-
* Grab one token out of fp. Tokens are strings of non-blank
115+
* Grab one token out of the string pointed to by lineptr.
116+
* Tokens are strings of non-blank
115117
* characters bounded by blank characters, commas, beginning of line, and
116118
* end of line. Blank means space or tab. Tokens can be delimited by
117119
* double quotes (this allows the inclusion of blanks, but not newlines).
@@ -134,7 +136,7 @@ pg_isblank(const char c)
134136
* Handle comments.
135137
*/
136138
static bool
137-
next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
139+
next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
138140
bool *terminating_comma)
139141
{
140142
int c;
@@ -151,10 +153,10 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
151153
*terminating_comma = false;
152154

153155
/* Move over initial whitespace and commas */
154-
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
156+
while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
155157
;
156158

157-
if (c == EOF || c == '\n')
159+
if (c == '\0' || c == '\n')
158160
{
159161
*buf = '\0';
160162
return false;
@@ -164,17 +166,17 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
164166
* Build a token in buf of next characters up to EOF, EOL, unquoted comma,
165167
* or unquoted whitespace.
166168
*/
167-
while (c != EOF && c != '\n' &&
169+
while (c != '\0' && c != '\n' &&
168170
(!pg_isblank(c) || in_quote))
169171
{
170172
/* skip comments to EOL */
171173
if (c == '#' && !in_quote)
172174
{
173-
while ((c = getc(fp)) != EOF && c != '\n')
175+
while ((c = (*(*lineptr)++)) != '\0' && c != '\n')
174176
;
175177
/* If only comment, consume EOL too; return EOL */
176-
if (c != EOF && buf == start_buf)
177-
c = getc(fp);
178+
if (c != '\0' && buf == start_buf)
179+
(*lineptr)++;
178180
break;
179181
}
180182

@@ -186,7 +188,7 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
186188
errmsg("authentication file token too long, skipping: \"%s\"",
187189
start_buf)));
188190
/* Discard remainder of line */
189-
while ((c = getc(fp)) != EOF && c != '\n')
191+
while ((c = (*(*lineptr)++)) != '\0' && c != '\n')
190192
;
191193
break;
192194
}
@@ -215,15 +217,14 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
215217
*initial_quote = true;
216218
}
217219

218-
c = getc(fp);
220+
c = *(*lineptr)++;
219221
}
220222

221223
/*
222224
* Put back the char right after the token (critical in case it is EOL,
223225
* since we need to detect end-of-line at next call).
224226
*/
225-
if (c != EOF)
226-
ungetc(c, fp);
227+
(*lineptr)--;
227228

228229
*buf = '\0';
229230

@@ -258,13 +259,13 @@ copy_hba_token(HbaToken *in)
258259

259260

260261
/*
261-
* Tokenize one HBA field from a file, handling file inclusion and comma lists.
262+
* Tokenize one HBA field from a line, handling file inclusion and comma lists.
262263
*
263264
* The result is a List of HbaToken structs for each individual token,
264265
* or NIL if we reached EOL.
265266
*/
266267
static List *
267-
next_field_expand(const char *filename, FILE *file)
268+
next_field_expand(const char *filename, char **lineptr)
268269
{
269270
char buf[MAX_TOKEN];
270271
bool trailing_comma;
@@ -273,7 +274,7 @@ next_field_expand(const char *filename, FILE *file)
273274

274275
do
275276
{
276-
if (!next_token(file, buf, sizeof(buf), &initial_quote, &trailing_comma))
277+
if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
277278
break;
278279

279280
/* Is this referencing a file? */
@@ -335,7 +336,7 @@ tokenize_inc_file(List *tokens,
335336
}
336337

337338
/* There is possible recursion here if the file contains @ */
338-
linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
339+
linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums, NULL);
339340

340341
FreeFile(inc_file);
341342
pfree(inc_fullname);
@@ -364,8 +365,8 @@ tokenize_inc_file(List *tokens,
364365
}
365366

366367
/*
367-
* Tokenize the given file, storing the resulting data into two Lists: a
368-
* List of lines, and a List of line numbers.
368+
* Tokenize the given file, storing the resulting data into three Lists: a
369+
* List of lines, a List of line numbers, and a List of raw line contents.
369370
*
370371
* The list of lines is a triple-nested List structure. Each line is a List of
371372
* fields, and each field is a List of HbaTokens.
@@ -377,7 +378,7 @@ tokenize_inc_file(List *tokens,
377378
*/
378379
static MemoryContext
379380
tokenize_file(const char *filename, FILE *file,
380-
List **lines, List **line_nums)
381+
List **lines, List **line_nums, List **raw_lines)
381382
{
382383
List *current_line = NIL;
383384
List *current_field = NIL;
@@ -396,30 +397,51 @@ tokenize_file(const char *filename, FILE *file,
396397

397398
while (!feof(file) && !ferror(file))
398399
{
399-
current_field = next_field_expand(filename, file);
400+
char rawline[MAX_LINE];
401+
char *lineptr;
400402

401-
/* add tokens to list, unless we are at EOL or comment start */
402-
if (list_length(current_field) > 0)
403+
if (!fgets(rawline, sizeof(rawline), file))
404+
break;
405+
if (strlen(rawline) == MAX_LINE-1)
406+
/* Line too long! */
407+
ereport(ERROR,
408+
(errcode(ERRCODE_CONFIG_FILE_ERROR),
409+
errmsg("authentication file line too long"),
410+
errcontext("line %d of configuration file \"%s\"",
411+
line_number, filename)));
412+
413+
/* Strip trailing linebreak from rawline */
414+
while (rawline[strlen(rawline)-1] == '\n' ||
415+
rawline[strlen(rawline)-1] == '\r')
416+
rawline[strlen(rawline)-1] = '\0';
417+
418+
lineptr = rawline;
419+
while (strlen(lineptr) > 0)
403420
{
404-
if (current_line == NIL)
405-
{
406-
/* make a new line List, record its line number */
407-
current_line = lappend(current_line, current_field);
408-
*lines = lappend(*lines, current_line);
409-
*line_nums = lappend_int(*line_nums, line_number);
410-
}
411-
else
421+
current_field = next_field_expand(filename, &lineptr);
422+
423+
/* add tokens to list, unless we are at EOL or comment start */
424+
if (list_length(current_field) > 0)
412425
{
413-
/* append tokens to current line's list */
414-
current_line = lappend(current_line, current_field);
426+
if (current_line == NIL)
427+
{
428+
/* make a new line List, record its line number */
429+
current_line = lappend(current_line, current_field);
430+
*lines = lappend(*lines, current_line);
431+
*line_nums = lappend_int(*line_nums, line_number);
432+
if (raw_lines)
433+
*raw_lines = lappend(*raw_lines, pstrdup(rawline));
434+
}
435+
else
436+
{
437+
/* append tokens to current line's list */
438+
current_line = lappend(current_line, current_field);
439+
}
415440
}
416441
}
417-
else
418-
{
419-
/* we are at real or logical EOL, so force a new line List */
420-
current_line = NIL;
421-
line_number++;
422-
}
442+
/* we are at real or logical EOL, so force a new line List */
443+
current_line = NIL;
444+
line_number++;
423445
}
424446

425447
MemoryContextSwitchTo(oldcxt);
@@ -815,7 +837,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
815837
* NULL.
816838
*/
817839
static HbaLine *
818-
parse_hba_line(List *line, int line_num)
840+
parse_hba_line(List *line, int line_num, char *raw_line)
819841
{
820842
char *str;
821843
struct addrinfo *gai_result;
@@ -831,6 +853,7 @@ parse_hba_line(List *line, int line_num)
831853

832854
parsedline = palloc0(sizeof(HbaLine));
833855
parsedline->linenumber = line_num;
856+
parsedline->rawline = pstrdup(raw_line);
834857

835858
/* Check the record type. */
836859
field = list_head(line);
@@ -1761,8 +1784,10 @@ load_hba(void)
17611784
FILE *file;
17621785
List *hba_lines = NIL;
17631786
List *hba_line_nums = NIL;
1787+
List *hba_raw_lines = NIL;
17641788
ListCell *line,
1765-
*line_num;
1789+
*line_num,
1790+
*raw_line;
17661791
List *new_parsed_lines = NIL;
17671792
bool ok = true;
17681793
MemoryContext linecxt;
@@ -1779,7 +1804,7 @@ load_hba(void)
17791804
return false;
17801805
}
17811806

1782-
linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
1807+
linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines);
17831808
FreeFile(file);
17841809

17851810
/* Now parse all the lines */
@@ -1789,11 +1814,11 @@ load_hba(void)
17891814
ALLOCSET_DEFAULT_MINSIZE,
17901815
ALLOCSET_DEFAULT_MAXSIZE);
17911816
oldcxt = MemoryContextSwitchTo(hbacxt);
1792-
forboth(line, hba_lines, line_num, hba_line_nums)
1817+
forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines)
17931818
{
17941819
HbaLine *newline;
17951820

1796-
if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num))) == NULL)
1821+
if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line))) == NULL)
17971822
{
17981823
/*
17991824
* Parse error in the file, so indicate there's a problem. NB: a
@@ -2153,7 +2178,7 @@ load_ident(void)
21532178
return false;
21542179
}
21552180

2156-
linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
2181+
linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums, NULL);
21572182
FreeFile(file);
21582183

21592184
/* Now parse all the lines */

src/include/libpq/hba.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ typedef enum ConnType
5353
typedef struct HbaLine
5454
{
5555
int linenumber;
56+
char *rawline;
5657
ConnType conntype;
5758
List *databases;
5859
List *roles;

0 commit comments

Comments
 (0)