PostgreSQL Source Code git master
hba.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * hba.c
4 * Routines to handle host based authentication (that's the scheme
5 * wherein you authenticate a user by seeing what IP address the system
6 * says he comes from and choosing authentication method based on it).
7 *
8 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
10 *
11 *
12 * IDENTIFICATION
13 * src/backend/libpq/hba.c
14 *
15 *-------------------------------------------------------------------------
16 */
17#include "postgres.h"
18
19#include <ctype.h>
20#include <pwd.h>
21#include <fcntl.h>
22#include <sys/param.h>
23#include <sys/socket.h>
24#include <netdb.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <unistd.h>
28
30#include "common/ip.h"
31#include "common/string.h"
32#include "libpq/hba.h"
33#include "libpq/ifaddr.h"
34#include "libpq/libpq-be.h"
35#include "libpq/oauth.h"
37#include "regex/regex.h"
39#include "storage/fd.h"
40#include "utils/acl.h"
41#include "utils/conffiles.h"
42#include "utils/guc.h"
43#include "utils/memutils.h"
44#include "utils/varlena.h"
45
46#ifdef USE_LDAP
47#ifdef WIN32
48#include <winldap.h>
49#else
50#include <ldap.h>
51#endif
52#endif
53
54
55/* callback data for check_network_callback */
56typedef struct check_network_data
57{
58 IPCompareMethod method; /* test method */
59 SockAddr *raddr; /* client's actual address */
60 bool result; /* set to true if match */
62
63typedef struct
64{
65 const char *filename;
68
69#define token_has_regexp(t) (t->regex != NULL)
70#define token_is_member_check(t) (!t->quoted && t->string[0] == '+')
71#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
72#define token_matches(t, k) (strcmp(t->string, k) == 0)
73#define token_matches_insensitive(t,k) (pg_strcasecmp(t->string, k) == 0)
74
75/*
76 * Memory context holding the list of TokenizedAuthLines when parsing
77 * HBA or ident configuration files. This is created when opening the first
78 * file (depth of CONF_FILE_START_DEPTH).
79 */
81
82/*
83 * pre-parsed content of HBA config file: list of HbaLine structs.
84 * parsed_hba_context is the memory context where it lives.
85 */
88
89/*
90 * pre-parsed content of ident mapping file: list of IdentLine structs.
91 * parsed_ident_context is the memory context where it lives.
92 */
95
96/*
97 * The following character array represents the names of the authentication
98 * methods that are supported by PostgreSQL.
99 *
100 * Note: keep this in sync with the UserAuth enum in hba.h.
101 */
102static const char *const UserAuthName[] =
103{
104 "reject",
105 "implicit reject", /* Not a user-visible option */
106 "trust",
107 "ident",
108 "password",
109 "md5",
110 "scram-sha-256",
111 "gss",
112 "sspi",
113 "pam",
114 "bsd",
115 "ldap",
116 "cert",
117 "radius",
118 "peer",
119 "oauth",
120};
121
122/*
123 * Make sure UserAuthName[] tracks additions to the UserAuth enum
124 */
126 "UserAuthName[] must match the UserAuth enum");
127
128
129static List *tokenize_expand_file(List *tokens, const char *outer_filename,
130 const char *inc_filename, int elevel,
131 int depth, char **err_msg);
132static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
133 int elevel, char **err_msg);
134static int regcomp_auth_token(AuthToken *token, char *filename, int line_num,
135 char **err_msg, int elevel);
136static int regexec_auth_token(const char *match, AuthToken *token,
137 size_t nmatch, regmatch_t pmatch[]);
138static void tokenize_error_callback(void *arg);
139
140
141static bool
142pg_isblank(const char c)
143{
144 /* don't accept non-ASCII data */
145 return (!IS_HIGHBIT_SET(c) && isblank(c));
146}
147
148
149/*
150 * Grab one token out of the string pointed to by *lineptr.
151 *
152 * Tokens are strings of non-blank characters bounded by blank characters,
153 * commas, beginning of line, and end of line. Blank means space or tab.
154 *
155 * Tokens can be delimited by double quotes (this allows the inclusion of
156 * commas, blanks, and '#', but not newlines). As in SQL, write two
157 * double-quotes to represent a double quote.
158 *
159 * Comments (started by an unquoted '#') are skipped, i.e. the remainder
160 * of the line is ignored.
161 *
162 * (Note that line continuation processing happens before tokenization.
163 * Thus, if a continuation occurs within quoted text or a comment, the
164 * quoted text or comment is considered to continue to the next line.)
165 *
166 * The token, if any, is returned into buf (replacing any previous
167 * contents), and *lineptr is advanced past the token.
168 *
169 * Also, we set *initial_quote to indicate whether there was quoting before
170 * the first character. (We use that to prevent "@x" from being treated
171 * as a file inclusion request. Note that @"x" should be so treated;
172 * we want to allow that to support embedded spaces in file paths.)
173 *
174 * We set *terminating_comma to indicate whether the token is terminated by a
175 * comma (which is not returned, nor advanced over).
176 *
177 * The only possible error condition is lack of terminating quote, but we
178 * currently do not detect that, but just return the rest of the line.
179 *
180 * If successful: store dequoted token in buf and return true.
181 * If no more tokens on line: set buf to empty and return false.
182 */
183static bool
185 bool *initial_quote, bool *terminating_comma)
186{
187 int c;
188 bool in_quote = false;
189 bool was_quote = false;
190 bool saw_quote = false;
191
192 /* Initialize output parameters */
194 *initial_quote = false;
195 *terminating_comma = false;
196
197 /* Move over any whitespace and commas preceding the next token */
198 while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
199 ;
200
201 /*
202 * Build a token in buf of next characters up to EOL, unquoted comma, or
203 * unquoted whitespace.
204 */
205 while (c != '\0' &&
206 (!pg_isblank(c) || in_quote))
207 {
208 /* skip comments to EOL */
209 if (c == '#' && !in_quote)
210 {
211 while ((c = (*(*lineptr)++)) != '\0')
212 ;
213 break;
214 }
215
216 /* we do not pass back a terminating comma in the token */
217 if (c == ',' && !in_quote)
218 {
219 *terminating_comma = true;
220 break;
221 }
222
223 if (c != '"' || was_quote)
225
226 /* Literal double-quote is two double-quotes */
227 if (in_quote && c == '"')
228 was_quote = !was_quote;
229 else
230 was_quote = false;
231
232 if (c == '"')
233 {
234 in_quote = !in_quote;
235 saw_quote = true;
236 if (buf->len == 0)
237 *initial_quote = true;
238 }
239
240 c = *(*lineptr)++;
241 }
242
243 /*
244 * Un-eat the char right after the token (critical in case it is '\0',
245 * else next call will read past end of string).
246 */
247 (*lineptr)--;
248
249 return (saw_quote || buf->len > 0);
250}
251
252/*
253 * Construct a palloc'd AuthToken struct, copying the given string.
254 */
255static AuthToken *
256make_auth_token(const char *token, bool quoted)
257{
258 AuthToken *authtoken;
259 int toklen;
260
261 toklen = strlen(token);
262 /* we copy string into same palloc block as the struct */
263 authtoken = (AuthToken *) palloc0(sizeof(AuthToken) + toklen + 1);
264 authtoken->string = (char *) authtoken + sizeof(AuthToken);
265 authtoken->quoted = quoted;
266 authtoken->regex = NULL;
267 memcpy(authtoken->string, token, toklen + 1);
268
269 return authtoken;
270}
271
272/*
273 * Free an AuthToken, that may include a regular expression that needs
274 * to be cleaned up explicitly.
275 */
276static void
278{
280 pg_regfree(token->regex);
281}
282
283/*
284 * Copy a AuthToken struct into freshly palloc'd memory.
285 */
286static AuthToken *
288{
289 AuthToken *out = make_auth_token(in->string, in->quoted);
290
291 return out;
292}
293
294/*
295 * Compile the regular expression and store it in the AuthToken given in
296 * input. Returns the result of pg_regcomp(). On error, the details are
297 * stored in "err_msg".
298 */
299static int
301 char **err_msg, int elevel)
302{
303 pg_wchar *wstr;
304 int wlen;
305 int rc;
306
307 Assert(token->regex == NULL);
308
309 if (token->string[0] != '/')
310 return 0; /* nothing to compile */
311
312 token->regex = (regex_t *) palloc0(sizeof(regex_t));
313 wstr = palloc((strlen(token->string + 1) + 1) * sizeof(pg_wchar));
314 wlen = pg_mb2wchar_with_len(token->string + 1,
315 wstr, strlen(token->string + 1));
316
317 rc = pg_regcomp(token->regex, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
318
319 if (rc)
320 {
321 char errstr[100];
322
323 pg_regerror(rc, token->regex, errstr, sizeof(errstr));
324 ereport(elevel,
325 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
326 errmsg("invalid regular expression \"%s\": %s",
327 token->string + 1, errstr),
328 errcontext("line %d of configuration file \"%s\"",
329 line_num, filename)));
330
331 *err_msg = psprintf("invalid regular expression \"%s\": %s",
332 token->string + 1, errstr);
333 }
334
335 pfree(wstr);
336 return rc;
337}
338
339/*
340 * Execute a regular expression computed in an AuthToken, checking for a match
341 * with the string specified in "match". The caller may optionally give an
342 * array to store the matches. Returns the result of pg_regexec().
343 */
344static int
345regexec_auth_token(const char *match, AuthToken *token, size_t nmatch,
346 regmatch_t pmatch[])
347{
348 pg_wchar *wmatchstr;
349 int wmatchlen;
350 int r;
351
352 Assert(token->string[0] == '/' && token->regex);
353
354 wmatchstr = palloc((strlen(match) + 1) * sizeof(pg_wchar));
355 wmatchlen = pg_mb2wchar_with_len(match, wmatchstr, strlen(match));
356
357 r = pg_regexec(token->regex, wmatchstr, wmatchlen, 0, NULL, nmatch, pmatch, 0);
358
359 pfree(wmatchstr);
360 return r;
361}
362
363/*
364 * Tokenize one HBA field from a line, handling file inclusion and comma lists.
365 *
366 * filename: current file's pathname (needed to resolve relative pathnames)
367 * *lineptr: current line pointer, which will be advanced past field
368 *
369 * In event of an error, log a message at ereport level elevel, and also
370 * set *err_msg to a string describing the error. Note that the result
371 * may be non-NIL anyway, so *err_msg must be tested to determine whether
372 * there was an error.
373 *
374 * The result is a List of AuthToken structs, one for each token in the field,
375 * or NIL if we reached EOL.
376 */
377static List *
379 int elevel, int depth, char **err_msg)
380{
382 bool trailing_comma;
383 bool initial_quote;
384 List *tokens = NIL;
385
387
388 do
389 {
390 if (!next_token(lineptr, &buf,
391 &initial_quote, &trailing_comma))
392 break;
393
394 /* Is this referencing a file? */
395 if (!initial_quote && buf.len > 1 && buf.data[0] == '@')
396 tokens = tokenize_expand_file(tokens, filename, buf.data + 1,
397 elevel, depth + 1, err_msg);
398 else
399 {
400 MemoryContext oldcxt;
401
402 /*
403 * lappend() may do its own allocations, so move to the context
404 * for the list of tokens.
405 */
407 tokens = lappend(tokens, make_auth_token(buf.data, initial_quote));
408 MemoryContextSwitchTo(oldcxt);
409 }
410 } while (trailing_comma && (*err_msg == NULL));
411
412 pfree(buf.data);
413
414 return tokens;
415}
416
417/*
418 * tokenize_include_file
419 * Include a file from another file into an hba "field".
420 *
421 * Opens and tokenises a file included from another authentication file
422 * with one of the include records ("include", "include_if_exists" or
423 * "include_dir"), and assign all values found to an existing list of
424 * list of AuthTokens.
425 *
426 * All new tokens are allocated in the memory context dedicated to the
427 * tokenization, aka tokenize_context.
428 *
429 * If missing_ok is true, ignore a missing file.
430 *
431 * In event of an error, log a message at ereport level elevel, and also
432 * set *err_msg to a string describing the error. Note that the result
433 * may be non-NIL anyway, so *err_msg must be tested to determine whether
434 * there was an error.
435 */
436static void
437tokenize_include_file(const char *outer_filename,
438 const char *inc_filename,
439 List **tok_lines,
440 int elevel,
441 int depth,
442 bool missing_ok,
443 char **err_msg)
444{
445 char *inc_fullname;
446 FILE *inc_file;
447
448 inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
449 inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
450
451 if (!inc_file)
452 {
453 if (errno == ENOENT && missing_ok)
454 {
455 ereport(elevel,
456 (errmsg("skipping missing authentication file \"%s\"",
457 inc_fullname)));
458 *err_msg = NULL;
459 pfree(inc_fullname);
460 return;
461 }
462
463 /* error in err_msg, so leave and report */
464 pfree(inc_fullname);
465 Assert(err_msg);
466 return;
467 }
468
469 tokenize_auth_file(inc_fullname, inc_file, tok_lines, elevel,
470 depth);
471 free_auth_file(inc_file, depth);
472 pfree(inc_fullname);
473}
474
475/*
476 * tokenize_expand_file
477 * Expand a file included from another file into an hba "field"
478 *
479 * Opens and tokenises a file included from another HBA config file with @,
480 * and returns all values found therein as a flat list of AuthTokens. If a
481 * @-token or include record is found, recursively expand it. The newly
482 * read tokens are appended to "tokens" (so that foo,bar,@baz does what you
483 * expect). All new tokens are allocated in the memory context dedicated
484 * to the list of TokenizedAuthLines, aka tokenize_context.
485 *
486 * In event of an error, log a message at ereport level elevel, and also
487 * set *err_msg to a string describing the error. Note that the result
488 * may be non-NIL anyway, so *err_msg must be tested to determine whether
489 * there was an error.
490 */
491static List *
493 const char *outer_filename,
494 const char *inc_filename,
495 int elevel,
496 int depth,
497 char **err_msg)
498{
499 char *inc_fullname;
500 FILE *inc_file;
501 List *inc_lines = NIL;
502 ListCell *inc_line;
503
504 inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
505 inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
506
507 if (inc_file == NULL)
508 {
509 /* error already logged */
510 pfree(inc_fullname);
511 return tokens;
512 }
513
514 /*
515 * There is possible recursion here if the file contains @ or an include
516 * record.
517 */
518 tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel,
519 depth);
520
521 pfree(inc_fullname);
522
523 /*
524 * Move all the tokens found in the file to the tokens list. These are
525 * already saved in tokenize_context.
526 */
527 foreach(inc_line, inc_lines)
528 {
529 TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(inc_line);
530 ListCell *inc_field;
531
532 /* If any line has an error, propagate that up to caller */
533 if (tok_line->err_msg)
534 {
535 *err_msg = pstrdup(tok_line->err_msg);
536 break;
537 }
538
539 foreach(inc_field, tok_line->fields)
540 {
541 List *inc_tokens = lfirst(inc_field);
542 ListCell *inc_token;
543
544 foreach(inc_token, inc_tokens)
545 {
546 AuthToken *token = lfirst(inc_token);
547 MemoryContext oldcxt;
548
549 /*
550 * lappend() may do its own allocations, so move to the
551 * context for the list of tokens.
552 */
554 tokens = lappend(tokens, token);
555 MemoryContextSwitchTo(oldcxt);
556 }
557 }
558 }
559
560 free_auth_file(inc_file, depth);
561 return tokens;
562}
563
564/*
565 * free_auth_file
566 * Free a file opened by open_auth_file().
567 */
568void
569free_auth_file(FILE *file, int depth)
570{
571 FreeFile(file);
572
573 /* If this is the last cleanup, remove the tokenization context */
574 if (depth == CONF_FILE_START_DEPTH)
575 {
577 tokenize_context = NULL;
578 }
579}
580
581/*
582 * open_auth_file
583 * Open the given file.
584 *
585 * filename: the absolute path to the target file
586 * elevel: message logging level
587 * depth: recursion level when opening the file
588 * err_msg: details about the error
589 *
590 * Return value is the opened file. On error, returns NULL with details
591 * about the error stored in "err_msg".
592 */
593FILE *
594open_auth_file(const char *filename, int elevel, int depth,
595 char **err_msg)
596{
597 FILE *file;
598
599 /*
600 * Reject too-deep include nesting depth. This is just a safety check to
601 * avoid dumping core due to stack overflow if an include file loops back
602 * to itself. The maximum nesting depth is pretty arbitrary.
603 */
604 if (depth > CONF_FILE_MAX_DEPTH)
605 {
606 ereport(elevel,
608 errmsg("could not open file \"%s\": maximum nesting depth exceeded",
609 filename)));
610 if (err_msg)
611 *err_msg = psprintf("could not open file \"%s\": maximum nesting depth exceeded",
612 filename);
613 return NULL;
614 }
615
616 file = AllocateFile(filename, "r");
617 if (file == NULL)
618 {
619 int save_errno = errno;
620
621 ereport(elevel,
623 errmsg("could not open file \"%s\": %m",
624 filename)));
625 if (err_msg)
626 {
627 errno = save_errno;
628 *err_msg = psprintf("could not open file \"%s\": %m",
629 filename);
630 }
631 /* the caller may care about some specific errno */
632 errno = save_errno;
633 return NULL;
634 }
635
636 /*
637 * When opening the top-level file, create the memory context used for the
638 * tokenization. This will be closed with this file when coming back to
639 * this level of cleanup.
640 */
641 if (depth == CONF_FILE_START_DEPTH)
642 {
643 /*
644 * A context may be present, but assume that it has been eliminated
645 * already.
646 */
648 "tokenize_context",
650 }
651
652 return file;
653}
654
655/*
656 * error context callback for tokenize_auth_file()
657 */
658static void
660{
662
663 errcontext("line %d of configuration file \"%s\"",
664 callback_arg->linenum, callback_arg->filename);
665}
666
667/*
668 * tokenize_auth_file
669 * Tokenize the given file.
670 *
671 * The output is a list of TokenizedAuthLine structs; see the struct definition
672 * in libpq/hba.h. This is the central piece in charge of parsing the
673 * authentication files. All the operations of this function happen in its own
674 * local memory context, easing the cleanup of anything allocated here. This
675 * matters a lot when reloading authentication files in the postmaster.
676 *
677 * filename: the absolute path to the target file
678 * file: the already-opened target file
679 * tok_lines: receives output list, saved into tokenize_context
680 * elevel: message logging level
681 * depth: level of recursion when tokenizing the target file
682 *
683 * Errors are reported by logging messages at ereport level elevel and by
684 * adding TokenizedAuthLine structs containing non-null err_msg fields to the
685 * output list.
686 */
687void
688tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
689 int elevel, int depth)
690{
691 int line_number = 1;
693 MemoryContext linecxt;
694 MemoryContext funccxt; /* context of this function's caller */
695 ErrorContextCallback tokenerrcontext;
696 tokenize_error_callback_arg callback_arg;
697
699
700 callback_arg.filename = filename;
701 callback_arg.linenum = line_number;
702
703 tokenerrcontext.callback = tokenize_error_callback;
704 tokenerrcontext.arg = &callback_arg;
705 tokenerrcontext.previous = error_context_stack;
706 error_context_stack = &tokenerrcontext;
707
708 /*
709 * Do all the local tokenization in its own context, to ease the cleanup
710 * of any memory allocated while tokenizing.
711 */
713 "tokenize_auth_file",
715 funccxt = MemoryContextSwitchTo(linecxt);
716
718
719 if (depth == CONF_FILE_START_DEPTH)
720 *tok_lines = NIL;
721
722 while (!feof(file) && !ferror(file))
723 {
724 TokenizedAuthLine *tok_line;
725 MemoryContext oldcxt;
726 char *lineptr;
727 List *current_line = NIL;
728 char *err_msg = NULL;
729 int last_backslash_buflen = 0;
730 int continuations = 0;
731
732 /* Collect the next input line, handling backslash continuations */
734
735 while (pg_get_line_append(file, &buf, NULL))
736 {
737 /* Strip trailing newline, including \r in case we're on Windows */
738 buf.len = pg_strip_crlf(buf.data);
739
740 /*
741 * Check for backslash continuation. The backslash must be after
742 * the last place we found a continuation, else two backslashes
743 * followed by two \n's would behave surprisingly.
744 */
745 if (buf.len > last_backslash_buflen &&
746 buf.data[buf.len - 1] == '\\')
747 {
748 /* Continuation, so strip it and keep reading */
749 buf.data[--buf.len] = '\0';
750 last_backslash_buflen = buf.len;
751 continuations++;
752 continue;
753 }
754
755 /* Nope, so we have the whole line */
756 break;
757 }
758
759 if (ferror(file))
760 {
761 /* I/O error! */
762 int save_errno = errno;
763
764 ereport(elevel,
766 errmsg("could not read file \"%s\": %m", filename)));
767 errno = save_errno;
768 err_msg = psprintf("could not read file \"%s\": %m",
769 filename);
770 break;
771 }
772
773 /* Parse fields */
774 lineptr = buf.data;
775 while (*lineptr && err_msg == NULL)
776 {
777 List *current_field;
778
779 current_field = next_field_expand(filename, &lineptr,
780 elevel, depth, &err_msg);
781 /* add field to line, unless we are at EOL or comment start */
782 if (current_field != NIL)
783 {
784 /*
785 * lappend() may do its own allocations, so move to the
786 * context for the list of tokens.
787 */
789 current_line = lappend(current_line, current_field);
790 MemoryContextSwitchTo(oldcxt);
791 }
792 }
793
794 /*
795 * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
796 * boring.
797 */
798 if (current_line == NIL && err_msg == NULL)
799 goto next_line;
800
801 /* If the line is valid, check if that's an include directive */
802 if (err_msg == NULL && list_length(current_line) == 2)
803 {
804 AuthToken *first,
805 *second;
806
807 first = linitial(linitial_node(List, current_line));
808 second = linitial(lsecond_node(List, current_line));
809
810 if (strcmp(first->string, "include") == 0)
811 {
812 tokenize_include_file(filename, second->string, tok_lines,
813 elevel, depth + 1, false, &err_msg);
814
815 if (err_msg)
816 goto process_line;
817
818 /*
819 * tokenize_auth_file() has taken care of creating the
820 * TokenizedAuthLines.
821 */
822 goto next_line;
823 }
824 else if (strcmp(first->string, "include_dir") == 0)
825 {
826 char **filenames;
827 char *dir_name = second->string;
828 int num_filenames;
829 StringInfoData err_buf;
830
831 filenames = GetConfFilesInDir(dir_name, filename, elevel,
832 &num_filenames, &err_msg);
833
834 if (!filenames)
835 {
836 /* the error is in err_msg, so create an entry */
837 goto process_line;
838 }
839
840 initStringInfo(&err_buf);
841 for (int i = 0; i < num_filenames; i++)
842 {
843 tokenize_include_file(filename, filenames[i], tok_lines,
844 elevel, depth + 1, false, &err_msg);
845 /* cumulate errors if any */
846 if (err_msg)
847 {
848 if (err_buf.len > 0)
849 appendStringInfoChar(&err_buf, '\n');
850 appendStringInfoString(&err_buf, err_msg);
851 }
852 }
853
854 /* clean up things */
855 for (int i = 0; i < num_filenames; i++)
856 pfree(filenames[i]);
857 pfree(filenames);
858
859 /*
860 * If there were no errors, the line is fully processed,
861 * bypass the general TokenizedAuthLine processing.
862 */
863 if (err_buf.len == 0)
864 goto next_line;
865
866 /* Otherwise, process the cumulated errors, if any. */
867 err_msg = err_buf.data;
868 goto process_line;
869 }
870 else if (strcmp(first->string, "include_if_exists") == 0)
871 {
872
873 tokenize_include_file(filename, second->string, tok_lines,
874 elevel, depth + 1, true, &err_msg);
875 if (err_msg)
876 goto process_line;
877
878 /*
879 * tokenize_auth_file() has taken care of creating the
880 * TokenizedAuthLines.
881 */
882 goto next_line;
883 }
884 }
885
886process_line:
887
888 /*
889 * General processing: report the error if any and emit line to the
890 * TokenizedAuthLine. This is saved in the memory context dedicated
891 * to this list.
892 */
894 tok_line = (TokenizedAuthLine *) palloc0(sizeof(TokenizedAuthLine));
895 tok_line->fields = current_line;
896 tok_line->file_name = pstrdup(filename);
897 tok_line->line_num = line_number;
898 tok_line->raw_line = pstrdup(buf.data);
899 tok_line->err_msg = err_msg ? pstrdup(err_msg) : NULL;
900 *tok_lines = lappend(*tok_lines, tok_line);
901 MemoryContextSwitchTo(oldcxt);
902
903next_line:
904 line_number += continuations + 1;
905 callback_arg.linenum = line_number;
906 }
907
908 MemoryContextSwitchTo(funccxt);
909 MemoryContextDelete(linecxt);
910
911 error_context_stack = tokenerrcontext.previous;
912}
913
914
915/*
916 * Does user belong to role?
917 *
918 * userid is the OID of the role given as the attempted login identifier.
919 * We check to see if it is a member of the specified role name.
920 */
921static bool
922is_member(Oid userid, const char *role)
923{
924 Oid roleid;
925
926 if (!OidIsValid(userid))
927 return false; /* if user not exist, say "no" */
928
929 roleid = get_role_oid(role, true);
930
931 if (!OidIsValid(roleid))
932 return false; /* if target role not exist, say "no" */
933
934 /*
935 * See if user is directly or indirectly a member of role. For this
936 * purpose, a superuser is not considered to be automatically a member of
937 * the role, so group auth only applies to explicit membership.
938 */
939 return is_member_of_role_nosuper(userid, roleid);
940}
941
942/*
943 * Check AuthToken list for a match to role, allowing group names.
944 *
945 * Each AuthToken listed is checked one-by-one. Keywords are processed
946 * first (these cannot have regular expressions), followed by regular
947 * expressions (if any), the case-insensitive match (if requested) and
948 * the exact match.
949 */
950static bool
951check_role(const char *role, Oid roleid, List *tokens, bool case_insensitive)
952{
953 ListCell *cell;
954 AuthToken *tok;
955
956 foreach(cell, tokens)
957 {
958 tok = lfirst(cell);
959 if (token_is_member_check(tok))
960 {
961 if (is_member(roleid, tok->string + 1))
962 return true;
963 }
964 else if (token_is_keyword(tok, "all"))
965 return true;
966 else if (token_has_regexp(tok))
967 {
968 if (regexec_auth_token(role, tok, 0, NULL) == REG_OKAY)
969 return true;
970 }
971 else if (case_insensitive)
972 {
973 if (token_matches_insensitive(tok, role))
974 return true;
975 }
976 else if (token_matches(tok, role))
977 return true;
978 }
979 return false;
980}
981
982/*
983 * Check to see if db/role combination matches AuthToken list.
984 *
985 * Each AuthToken listed is checked one-by-one. Keywords are checked
986 * first (these cannot have regular expressions), followed by regular
987 * expressions (if any) and the exact match.
988 */
989static bool
990check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
991{
992 ListCell *cell;
993 AuthToken *tok;
994
995 foreach(cell, tokens)
996 {
997 tok = lfirst(cell);
999 {
1000 /*
1001 * physical replication walsender connections can only match
1002 * replication keyword
1003 */
1004 if (token_is_keyword(tok, "replication"))
1005 return true;
1006 }
1007 else if (token_is_keyword(tok, "all"))
1008 return true;
1009 else if (token_is_keyword(tok, "sameuser"))
1010 {
1011 if (strcmp(dbname, role) == 0)
1012 return true;
1013 }
1014 else if (token_is_keyword(tok, "samegroup") ||
1015 token_is_keyword(tok, "samerole"))
1016 {
1017 if (is_member(roleid, dbname))
1018 return true;
1019 }
1020 else if (token_is_keyword(tok, "replication"))
1021 continue; /* never match this if not walsender */
1022 else if (token_has_regexp(tok))
1023 {
1024 if (regexec_auth_token(dbname, tok, 0, NULL) == REG_OKAY)
1025 return true;
1026 }
1027 else if (token_matches(tok, dbname))
1028 return true;
1029 }
1030 return false;
1031}
1032
1033static bool
1034ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
1035{
1036 return (a->sin_addr.s_addr == b->sin_addr.s_addr);
1037}
1038
1039static bool
1040ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
1041{
1042 int i;
1043
1044 for (i = 0; i < 16; i++)
1045 if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
1046 return false;
1047
1048 return true;
1049}
1050
1051/*
1052 * Check whether host name matches pattern.
1053 */
1054static bool
1055hostname_match(const char *pattern, const char *actual_hostname)
1056{
1057 if (pattern[0] == '.') /* suffix match */
1058 {
1059 size_t plen = strlen(pattern);
1060 size_t hlen = strlen(actual_hostname);
1061
1062 if (hlen < plen)
1063 return false;
1064
1065 return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
1066 }
1067 else
1068 return (pg_strcasecmp(pattern, actual_hostname) == 0);
1069}
1070
1071/*
1072 * Check to see if a connecting IP matches a given host name.
1073 */
1074static bool
1076{
1077 struct addrinfo *gai_result,
1078 *gai;
1079 int ret;
1080 bool found;
1081
1082 /* Quick out if remote host name already known bad */
1083 if (port->remote_hostname_resolv < 0)
1084 return false;
1085
1086 /* Lookup remote host name if not already done */
1087 if (!port->remote_hostname)
1088 {
1089 char remote_hostname[NI_MAXHOST];
1090
1091 ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
1092 remote_hostname, sizeof(remote_hostname),
1093 NULL, 0,
1094 NI_NAMEREQD);
1095 if (ret != 0)
1096 {
1097 /* remember failure; don't complain in the postmaster log yet */
1098 port->remote_hostname_resolv = -2;
1099 port->remote_hostname_errcode = ret;
1100 return false;
1101 }
1102
1103 port->remote_hostname = pstrdup(remote_hostname);
1104 }
1105
1106 /* Now see if remote host name matches this pg_hba line */
1107 if (!hostname_match(hostname, port->remote_hostname))
1108 return false;
1109
1110 /* If we already verified the forward lookup, we're done */
1111 if (port->remote_hostname_resolv == +1)
1112 return true;
1113
1114 /* Lookup IP from host name and check against original IP */
1115 ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
1116 if (ret != 0)
1117 {
1118 /* remember failure; don't complain in the postmaster log yet */
1119 port->remote_hostname_resolv = -2;
1120 port->remote_hostname_errcode = ret;
1121 return false;
1122 }
1123
1124 found = false;
1125 for (gai = gai_result; gai; gai = gai->ai_next)
1126 {
1127 if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
1128 {
1129 if (gai->ai_addr->sa_family == AF_INET)
1130 {
1131 if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
1132 (struct sockaddr_in *) &port->raddr.addr))
1133 {
1134 found = true;
1135 break;
1136 }
1137 }
1138 else if (gai->ai_addr->sa_family == AF_INET6)
1139 {
1140 if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
1141 (struct sockaddr_in6 *) &port->raddr.addr))
1142 {
1143 found = true;
1144 break;
1145 }
1146 }
1147 }
1148 }
1149
1150 if (gai_result)
1151 freeaddrinfo(gai_result);
1152
1153 if (!found)
1154 elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
1155 hostname);
1156
1157 port->remote_hostname_resolv = found ? +1 : -1;
1158
1159 return found;
1160}
1161
1162/*
1163 * Check to see if a connecting IP matches the given address and netmask.
1164 */
1165static bool
1166check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
1167{
1168 if (raddr->addr.ss_family == addr->sa_family &&
1169 pg_range_sockaddr(&raddr->addr,
1170 (struct sockaddr_storage *) addr,
1171 (struct sockaddr_storage *) mask))
1172 return true;
1173 return false;
1174}
1175
1176/*
1177 * pg_foreach_ifaddr callback: does client addr match this machine interface?
1178 */
1179static void
1180check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
1181 void *cb_data)
1182{
1183 check_network_data *cn = (check_network_data *) cb_data;
1184 struct sockaddr_storage mask;
1185
1186 /* Already found a match? */
1187 if (cn->result)
1188 return;
1189
1190 if (cn->method == ipCmpSameHost)
1191 {
1192 /* Make an all-ones netmask of appropriate length for family */
1193 pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
1194 cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
1195 }
1196 else
1197 {
1198 /* Use the netmask of the interface itself */
1199 cn->result = check_ip(cn->raddr, addr, netmask);
1200 }
1201}
1202
1203/*
1204 * Use pg_foreach_ifaddr to check a samehost or samenet match
1205 */
1206static bool
1208{
1210
1211 cn.method = method;
1212 cn.raddr = raddr;
1213 cn.result = false;
1214
1215 errno = 0;
1217 {
1218 ereport(LOG,
1219 (errmsg("error enumerating network interfaces: %m")));
1220 return false;
1221 }
1222
1223 return cn.result;
1224}
1225
1226
1227/*
1228 * Macros used to check and report on invalid configuration options.
1229 * On error: log a message at level elevel, set *err_msg, and exit the function.
1230 * These macros are not as general-purpose as they look, because they know
1231 * what the calling function's error-exit value is.
1232 *
1233 * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
1234 * not supported.
1235 * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
1236 * method is actually the one specified. Used as a shortcut when
1237 * the option is only valid for one authentication method.
1238 * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
1239 * reporting error if it's not.
1240 */
1241#define INVALID_AUTH_OPTION(optname, validmethods) \
1242do { \
1243 ereport(elevel, \
1244 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1245 /* translator: the second %s is a list of auth methods */ \
1246 errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
1247 optname, _(validmethods)), \
1248 errcontext("line %d of configuration file \"%s\"", \
1249 line_num, file_name))); \
1250 *err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \
1251 optname, validmethods); \
1252 return false; \
1253} while (0)
1254
1255#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \
1256do { \
1257 if (hbaline->auth_method != methodval) \
1258 INVALID_AUTH_OPTION(optname, validmethods); \
1259} while (0)
1260
1261#define MANDATORY_AUTH_ARG(argvar, argname, authname) \
1262do { \
1263 if (argvar == NULL) { \
1264 ereport(elevel, \
1265 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1266 errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
1267 authname, argname), \
1268 errcontext("line %d of configuration file \"%s\"", \
1269 line_num, file_name))); \
1270 *err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \
1271 authname, argname); \
1272 return NULL; \
1273 } \
1274} while (0)
1275
1276/*
1277 * Macros for handling pg_ident problems, similar as above.
1278 *
1279 * IDENT_FIELD_ABSENT:
1280 * Reports when the given ident field ListCell is not populated.
1281 *
1282 * IDENT_MULTI_VALUE:
1283 * Reports when the given ident token List has more than one element.
1284 */
1285#define IDENT_FIELD_ABSENT(field) \
1286do { \
1287 if (!field) { \
1288 ereport(elevel, \
1289 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1290 errmsg("missing entry at end of line"), \
1291 errcontext("line %d of configuration file \"%s\"", \
1292 line_num, file_name))); \
1293 *err_msg = pstrdup("missing entry at end of line"); \
1294 return NULL; \
1295 } \
1296} while (0)
1297
1298#define IDENT_MULTI_VALUE(tokens) \
1299do { \
1300 if (tokens->length > 1) { \
1301 ereport(elevel, \
1302 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1303 errmsg("multiple values in ident field"), \
1304 errcontext("line %d of configuration file \"%s\"", \
1305 line_num, file_name))); \
1306 *err_msg = pstrdup("multiple values in ident field"); \
1307 return NULL; \
1308 } \
1309} while (0)
1310
1311
1312/*
1313 * Parse one tokenised line from the hba config file and store the result in a
1314 * HbaLine structure.
1315 *
1316 * If parsing fails, log a message at ereport level elevel, store an error
1317 * string in tok_line->err_msg, and return NULL. (Some non-error conditions
1318 * can also result in such messages.)
1319 *
1320 * Note: this function leaks memory when an error occurs. Caller is expected
1321 * to have set a memory context that will be reset if this function returns
1322 * NULL.
1323 */
1324HbaLine *
1325parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
1326{
1327 int line_num = tok_line->line_num;
1328 char *file_name = tok_line->file_name;
1329 char **err_msg = &tok_line->err_msg;
1330 char *str;
1331 struct addrinfo *gai_result;
1332 struct addrinfo hints;
1333 int ret;
1334 char *cidr_slash;
1335 char *unsupauth;
1336 ListCell *field;
1337 List *tokens;
1338 ListCell *tokencell;
1340 HbaLine *parsedline;
1341
1342 parsedline = palloc0(sizeof(HbaLine));
1343 parsedline->sourcefile = pstrdup(file_name);
1344 parsedline->linenumber = line_num;
1345 parsedline->rawline = pstrdup(tok_line->raw_line);
1346
1347 /* Check the record type. */
1348 Assert(tok_line->fields != NIL);
1349 field = list_head(tok_line->fields);
1350 tokens = lfirst(field);
1351 if (tokens->length > 1)
1352 {
1353 ereport(elevel,
1354 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1355 errmsg("multiple values specified for connection type"),
1356 errhint("Specify exactly one connection type per line."),
1357 errcontext("line %d of configuration file \"%s\"",
1358 line_num, file_name)));
1359 *err_msg = "multiple values specified for connection type";
1360 return NULL;
1361 }
1362 token = linitial(tokens);
1363 if (strcmp(token->string, "local") == 0)
1364 {
1365 parsedline->conntype = ctLocal;
1366 }
1367 else if (strcmp(token->string, "host") == 0 ||
1368 strcmp(token->string, "hostssl") == 0 ||
1369 strcmp(token->string, "hostnossl") == 0 ||
1370 strcmp(token->string, "hostgssenc") == 0 ||
1371 strcmp(token->string, "hostnogssenc") == 0)
1372 {
1373
1374 if (token->string[4] == 's') /* "hostssl" */
1375 {
1376 parsedline->conntype = ctHostSSL;
1377 /* Log a warning if SSL support is not active */
1378#ifdef USE_SSL
1379 if (!EnableSSL)
1380 {
1381 ereport(elevel,
1382 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1383 errmsg("hostssl record cannot match because SSL is disabled"),
1384 errhint("Set \"ssl = on\" in postgresql.conf."),
1385 errcontext("line %d of configuration file \"%s\"",
1386 line_num, file_name)));
1387 *err_msg = "hostssl record cannot match because SSL is disabled";
1388 }
1389#else
1390 ereport(elevel,
1391 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1392 errmsg("hostssl record cannot match because SSL is not supported by this build"),
1393 errcontext("line %d of configuration file \"%s\"",
1394 line_num, file_name)));
1395 *err_msg = "hostssl record cannot match because SSL is not supported by this build";
1396#endif
1397 }
1398 else if (token->string[4] == 'g') /* "hostgssenc" */
1399 {
1400 parsedline->conntype = ctHostGSS;
1401#ifndef ENABLE_GSS
1402 ereport(elevel,
1403 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1404 errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
1405 errcontext("line %d of configuration file \"%s\"",
1406 line_num, file_name)));
1407 *err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
1408#endif
1409 }
1410 else if (token->string[4] == 'n' && token->string[6] == 's')
1411 parsedline->conntype = ctHostNoSSL;
1412 else if (token->string[4] == 'n' && token->string[6] == 'g')
1413 parsedline->conntype = ctHostNoGSS;
1414 else
1415 {
1416 /* "host" */
1417 parsedline->conntype = ctHost;
1418 }
1419 } /* record type */
1420 else
1421 {
1422 ereport(elevel,
1423 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1424 errmsg("invalid connection type \"%s\"",
1425 token->string),
1426 errcontext("line %d of configuration file \"%s\"",
1427 line_num, file_name)));
1428 *err_msg = psprintf("invalid connection type \"%s\"", token->string);
1429 return NULL;
1430 }
1431
1432 /* Get the databases. */
1433 field = lnext(tok_line->fields, field);
1434 if (!field)
1435 {
1436 ereport(elevel,
1437 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1438 errmsg("end-of-line before database specification"),
1439 errcontext("line %d of configuration file \"%s\"",
1440 line_num, file_name)));
1441 *err_msg = "end-of-line before database specification";
1442 return NULL;
1443 }
1444 parsedline->databases = NIL;
1445 tokens = lfirst(field);
1446 foreach(tokencell, tokens)
1447 {
1448 AuthToken *tok = copy_auth_token(lfirst(tokencell));
1449
1450 /* Compile a regexp for the database token, if necessary */
1451 if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1452 return NULL;
1453
1454 parsedline->databases = lappend(parsedline->databases, tok);
1455 }
1456
1457 /* Get the roles. */
1458 field = lnext(tok_line->fields, field);
1459 if (!field)
1460 {
1461 ereport(elevel,
1462 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1463 errmsg("end-of-line before role specification"),
1464 errcontext("line %d of configuration file \"%s\"",
1465 line_num, file_name)));
1466 *err_msg = "end-of-line before role specification";
1467 return NULL;
1468 }
1469 parsedline->roles = NIL;
1470 tokens = lfirst(field);
1471 foreach(tokencell, tokens)
1472 {
1473 AuthToken *tok = copy_auth_token(lfirst(tokencell));
1474
1475 /* Compile a regexp from the role token, if necessary */
1476 if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1477 return NULL;
1478
1479 parsedline->roles = lappend(parsedline->roles, tok);
1480 }
1481
1482 if (parsedline->conntype != ctLocal)
1483 {
1484 /* Read the IP address field. (with or without CIDR netmask) */
1485 field = lnext(tok_line->fields, field);
1486 if (!field)
1487 {
1488 ereport(elevel,
1489 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1490 errmsg("end-of-line before IP address specification"),
1491 errcontext("line %d of configuration file \"%s\"",
1492 line_num, file_name)));
1493 *err_msg = "end-of-line before IP address specification";
1494 return NULL;
1495 }
1496 tokens = lfirst(field);
1497 if (tokens->length > 1)
1498 {
1499 ereport(elevel,
1500 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1501 errmsg("multiple values specified for host address"),
1502 errhint("Specify one address range per line."),
1503 errcontext("line %d of configuration file \"%s\"",
1504 line_num, file_name)));
1505 *err_msg = "multiple values specified for host address";
1506 return NULL;
1507 }
1508 token = linitial(tokens);
1509
1510 if (token_is_keyword(token, "all"))
1511 {
1512 parsedline->ip_cmp_method = ipCmpAll;
1513 }
1514 else if (token_is_keyword(token, "samehost"))
1515 {
1516 /* Any IP on this host is allowed to connect */
1517 parsedline->ip_cmp_method = ipCmpSameHost;
1518 }
1519 else if (token_is_keyword(token, "samenet"))
1520 {
1521 /* Any IP on the host's subnets is allowed to connect */
1522 parsedline->ip_cmp_method = ipCmpSameNet;
1523 }
1524 else
1525 {
1526 /* IP and netmask are specified */
1527 parsedline->ip_cmp_method = ipCmpMask;
1528
1529 /* need a modifiable copy of token */
1530 str = pstrdup(token->string);
1531
1532 /* Check if it has a CIDR suffix and if so isolate it */
1533 cidr_slash = strchr(str, '/');
1534 if (cidr_slash)
1535 *cidr_slash = '\0';
1536
1537 /* Get the IP address either way */
1538 hints.ai_flags = AI_NUMERICHOST;
1539 hints.ai_family = AF_UNSPEC;
1540 hints.ai_socktype = 0;
1541 hints.ai_protocol = 0;
1542 hints.ai_addrlen = 0;
1543 hints.ai_canonname = NULL;
1544 hints.ai_addr = NULL;
1545 hints.ai_next = NULL;
1546
1547 ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
1548 if (ret == 0 && gai_result)
1549 {
1550 memcpy(&parsedline->addr, gai_result->ai_addr,
1551 gai_result->ai_addrlen);
1552 parsedline->addrlen = gai_result->ai_addrlen;
1553 }
1554 else if (ret == EAI_NONAME)
1555 parsedline->hostname = str;
1556 else
1557 {
1558 ereport(elevel,
1559 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1560 errmsg("invalid IP address \"%s\": %s",
1561 str, gai_strerror(ret)),
1562 errcontext("line %d of configuration file \"%s\"",
1563 line_num, file_name)));
1564 *err_msg = psprintf("invalid IP address \"%s\": %s",
1565 str, gai_strerror(ret));
1566 if (gai_result)
1567 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1568 return NULL;
1569 }
1570
1571 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1572
1573 /* Get the netmask */
1574 if (cidr_slash)
1575 {
1576 if (parsedline->hostname)
1577 {
1578 ereport(elevel,
1579 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1580 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
1581 token->string),
1582 errcontext("line %d of configuration file \"%s\"",
1583 line_num, file_name)));
1584 *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
1585 token->string);
1586 return NULL;
1587 }
1588
1589 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
1590 parsedline->addr.ss_family) < 0)
1591 {
1592 ereport(elevel,
1593 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1594 errmsg("invalid CIDR mask in address \"%s\"",
1595 token->string),
1596 errcontext("line %d of configuration file \"%s\"",
1597 line_num, file_name)));
1598 *err_msg = psprintf("invalid CIDR mask in address \"%s\"",
1599 token->string);
1600 return NULL;
1601 }
1602 parsedline->masklen = parsedline->addrlen;
1603 pfree(str);
1604 }
1605 else if (!parsedline->hostname)
1606 {
1607 /* Read the mask field. */
1608 pfree(str);
1609 field = lnext(tok_line->fields, field);
1610 if (!field)
1611 {
1612 ereport(elevel,
1613 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1614 errmsg("end-of-line before netmask specification"),
1615 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
1616 errcontext("line %d of configuration file \"%s\"",
1617 line_num, file_name)));
1618 *err_msg = "end-of-line before netmask specification";
1619 return NULL;
1620 }
1621 tokens = lfirst(field);
1622 if (tokens->length > 1)
1623 {
1624 ereport(elevel,
1625 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1626 errmsg("multiple values specified for netmask"),
1627 errcontext("line %d of configuration file \"%s\"",
1628 line_num, file_name)));
1629 *err_msg = "multiple values specified for netmask";
1630 return NULL;
1631 }
1632 token = linitial(tokens);
1633
1634 ret = pg_getaddrinfo_all(token->string, NULL,
1635 &hints, &gai_result);
1636 if (ret || !gai_result)
1637 {
1638 ereport(elevel,
1639 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1640 errmsg("invalid IP mask \"%s\": %s",
1641 token->string, gai_strerror(ret)),
1642 errcontext("line %d of configuration file \"%s\"",
1643 line_num, file_name)));
1644 *err_msg = psprintf("invalid IP mask \"%s\": %s",
1645 token->string, gai_strerror(ret));
1646 if (gai_result)
1647 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1648 return NULL;
1649 }
1650
1651 memcpy(&parsedline->mask, gai_result->ai_addr,
1652 gai_result->ai_addrlen);
1653 parsedline->masklen = gai_result->ai_addrlen;
1654 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1655
1656 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
1657 {
1658 ereport(elevel,
1659 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1660 errmsg("IP address and mask do not match"),
1661 errcontext("line %d of configuration file \"%s\"",
1662 line_num, file_name)));
1663 *err_msg = "IP address and mask do not match";
1664 return NULL;
1665 }
1666 }
1667 }
1668 } /* != ctLocal */
1669
1670 /* Get the authentication method */
1671 field = lnext(tok_line->fields, field);
1672 if (!field)
1673 {
1674 ereport(elevel,
1675 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1676 errmsg("end-of-line before authentication method"),
1677 errcontext("line %d of configuration file \"%s\"",
1678 line_num, file_name)));
1679 *err_msg = "end-of-line before authentication method";
1680 return NULL;
1681 }
1682 tokens = lfirst(field);
1683 if (tokens->length > 1)
1684 {
1685 ereport(elevel,
1686 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1687 errmsg("multiple values specified for authentication type"),
1688 errhint("Specify exactly one authentication type per line."),
1689 errcontext("line %d of configuration file \"%s\"",
1690 line_num, file_name)));
1691 *err_msg = "multiple values specified for authentication type";
1692 return NULL;
1693 }
1694 token = linitial(tokens);
1695
1696 unsupauth = NULL;
1697 if (strcmp(token->string, "trust") == 0)
1698 parsedline->auth_method = uaTrust;
1699 else if (strcmp(token->string, "ident") == 0)
1700 parsedline->auth_method = uaIdent;
1701 else if (strcmp(token->string, "peer") == 0)
1702 parsedline->auth_method = uaPeer;
1703 else if (strcmp(token->string, "password") == 0)
1704 parsedline->auth_method = uaPassword;
1705 else if (strcmp(token->string, "gss") == 0)
1706#ifdef ENABLE_GSS
1707 parsedline->auth_method = uaGSS;
1708#else
1709 unsupauth = "gss";
1710#endif
1711 else if (strcmp(token->string, "sspi") == 0)
1712#ifdef ENABLE_SSPI
1713 parsedline->auth_method = uaSSPI;
1714#else
1715 unsupauth = "sspi";
1716#endif
1717 else if (strcmp(token->string, "reject") == 0)
1718 parsedline->auth_method = uaReject;
1719 else if (strcmp(token->string, "md5") == 0)
1720 parsedline->auth_method = uaMD5;
1721 else if (strcmp(token->string, "scram-sha-256") == 0)
1722 parsedline->auth_method = uaSCRAM;
1723 else if (strcmp(token->string, "pam") == 0)
1724#ifdef USE_PAM
1725 parsedline->auth_method = uaPAM;
1726#else
1727 unsupauth = "pam";
1728#endif
1729 else if (strcmp(token->string, "bsd") == 0)
1730#ifdef USE_BSD_AUTH
1731 parsedline->auth_method = uaBSD;
1732#else
1733 unsupauth = "bsd";
1734#endif
1735 else if (strcmp(token->string, "ldap") == 0)
1736#ifdef USE_LDAP
1737 parsedline->auth_method = uaLDAP;
1738#else
1739 unsupauth = "ldap";
1740#endif
1741 else if (strcmp(token->string, "cert") == 0)
1742#ifdef USE_SSL
1743 parsedline->auth_method = uaCert;
1744#else
1745 unsupauth = "cert";
1746#endif
1747 else if (strcmp(token->string, "radius") == 0)
1748 parsedline->auth_method = uaRADIUS;
1749 else if (strcmp(token->string, "oauth") == 0)
1750 parsedline->auth_method = uaOAuth;
1751 else
1752 {
1753 ereport(elevel,
1754 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1755 errmsg("invalid authentication method \"%s\"",
1756 token->string),
1757 errcontext("line %d of configuration file \"%s\"",
1758 line_num, file_name)));
1759 *err_msg = psprintf("invalid authentication method \"%s\"",
1760 token->string);
1761 return NULL;
1762 }
1763
1764 if (unsupauth)
1765 {
1766 ereport(elevel,
1767 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1768 errmsg("invalid authentication method \"%s\": not supported by this build",
1769 token->string),
1770 errcontext("line %d of configuration file \"%s\"",
1771 line_num, file_name)));
1772 *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
1773 token->string);
1774 return NULL;
1775 }
1776
1777 /*
1778 * XXX: When using ident on local connections, change it to peer, for
1779 * backwards compatibility.
1780 */
1781 if (parsedline->conntype == ctLocal &&
1782 parsedline->auth_method == uaIdent)
1783 parsedline->auth_method = uaPeer;
1784
1785 /* Invalid authentication combinations */
1786 if (parsedline->conntype == ctLocal &&
1787 parsedline->auth_method == uaGSS)
1788 {
1789 ereport(elevel,
1790 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1791 errmsg("gssapi authentication is not supported on local sockets"),
1792 errcontext("line %d of configuration file \"%s\"",
1793 line_num, file_name)));
1794 *err_msg = "gssapi authentication is not supported on local sockets";
1795 return NULL;
1796 }
1797
1798 if (parsedline->conntype != ctLocal &&
1799 parsedline->auth_method == uaPeer)
1800 {
1801 ereport(elevel,
1802 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1803 errmsg("peer authentication is only supported on local sockets"),
1804 errcontext("line %d of configuration file \"%s\"",
1805 line_num, file_name)));
1806 *err_msg = "peer authentication is only supported on local sockets";
1807 return NULL;
1808 }
1809
1810 /*
1811 * SSPI authentication can never be enabled on ctLocal connections,
1812 * because it's only supported on Windows, where ctLocal isn't supported.
1813 */
1814
1815
1816 if (parsedline->conntype != ctHostSSL &&
1817 parsedline->auth_method == uaCert)
1818 {
1819 ereport(elevel,
1820 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1821 errmsg("cert authentication is only supported on hostssl connections"),
1822 errcontext("line %d of configuration file \"%s\"",
1823 line_num, file_name)));
1824 *err_msg = "cert authentication is only supported on hostssl connections";
1825 return NULL;
1826 }
1827
1828 /*
1829 * For GSS and SSPI, set the default value of include_realm to true.
1830 * Having include_realm set to false is dangerous in multi-realm
1831 * situations and is generally considered bad practice. We keep the
1832 * capability around for backwards compatibility, but we might want to
1833 * remove it at some point in the future. Users who still need to strip
1834 * the realm off would be better served by using an appropriate regex in a
1835 * pg_ident.conf mapping.
1836 */
1837 if (parsedline->auth_method == uaGSS ||
1838 parsedline->auth_method == uaSSPI)
1839 parsedline->include_realm = true;
1840
1841 /*
1842 * For SSPI, include_realm defaults to the SAM-compatible domain (aka
1843 * NetBIOS name) and user names instead of the Kerberos principal name for
1844 * compatibility.
1845 */
1846 if (parsedline->auth_method == uaSSPI)
1847 {
1848 parsedline->compat_realm = true;
1849 parsedline->upn_username = false;
1850 }
1851
1852 /* Parse remaining arguments */
1853 while ((field = lnext(tok_line->fields, field)) != NULL)
1854 {
1855 tokens = lfirst(field);
1856 foreach(tokencell, tokens)
1857 {
1858 char *val;
1859
1860 token = lfirst(tokencell);
1861
1862 str = pstrdup(token->string);
1863 val = strchr(str, '=');
1864 if (val == NULL)
1865 {
1866 /*
1867 * Got something that's not a name=value pair.
1868 */
1869 ereport(elevel,
1870 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1871 errmsg("authentication option not in name=value format: %s", token->string),
1872 errcontext("line %d of configuration file \"%s\"",
1873 line_num, file_name)));
1874 *err_msg = psprintf("authentication option not in name=value format: %s",
1875 token->string);
1876 return NULL;
1877 }
1878
1879 *val++ = '\0'; /* str now holds "name", val holds "value" */
1880 if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
1881 /* parse_hba_auth_opt already logged the error message */
1882 return NULL;
1883 pfree(str);
1884 }
1885 }
1886
1887 /*
1888 * Check if the selected authentication method has any mandatory arguments
1889 * that are not set.
1890 */
1891 if (parsedline->auth_method == uaLDAP)
1892 {
1893#ifndef HAVE_LDAP_INITIALIZE
1894 /* Not mandatory for OpenLDAP, because it can use DNS SRV records */
1895 MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1896#endif
1897
1898 /*
1899 * LDAP can operate in two modes: either with a direct bind, using
1900 * ldapprefix and ldapsuffix, or using a search+bind, using
1901 * ldapbasedn, ldapbinddn, ldapbindpasswd and one of
1902 * ldapsearchattribute or ldapsearchfilter. Disallow mixing these
1903 * parameters.
1904 */
1905 if (parsedline->ldapprefix || parsedline->ldapsuffix)
1906 {
1907 if (parsedline->ldapbasedn ||
1908 parsedline->ldapbinddn ||
1909 parsedline->ldapbindpasswd ||
1910 parsedline->ldapsearchattribute ||
1911 parsedline->ldapsearchfilter)
1912 {
1913 ereport(elevel,
1914 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1915 errmsg("cannot mix options for simple bind and search+bind modes"),
1916 errcontext("line %d of configuration file \"%s\"",
1917 line_num, file_name)));
1918 *err_msg = "cannot mix options for simple bind and search+bind modes";
1919 return NULL;
1920 }
1921 }
1922 else if (!parsedline->ldapbasedn)
1923 {
1924 ereport(elevel,
1925 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1926 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1927 errcontext("line %d of configuration file \"%s\"",
1928 line_num, file_name)));
1929 *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
1930 return NULL;
1931 }
1932
1933 /*
1934 * When using search+bind, you can either use a simple attribute
1935 * (defaulting to "uid") or a fully custom search filter. You can't
1936 * do both.
1937 */
1938 if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter)
1939 {
1940 ereport(elevel,
1941 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1942 errmsg("cannot use ldapsearchattribute together with ldapsearchfilter"),
1943 errcontext("line %d of configuration file \"%s\"",
1944 line_num, file_name)));
1945 *err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter";
1946 return NULL;
1947 }
1948 }
1949
1950 if (parsedline->auth_method == uaRADIUS)
1951 {
1952 MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
1953 MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
1954
1955 if (parsedline->radiusservers == NIL)
1956 {
1957 ereport(elevel,
1958 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1959 errmsg("list of RADIUS servers cannot be empty"),
1960 errcontext("line %d of configuration file \"%s\"",
1961 line_num, file_name)));
1962 *err_msg = "list of RADIUS servers cannot be empty";
1963 return NULL;
1964 }
1965
1966 if (parsedline->radiussecrets == NIL)
1967 {
1968 ereport(elevel,
1969 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1970 errmsg("list of RADIUS secrets cannot be empty"),
1971 errcontext("line %d of configuration file \"%s\"",
1972 line_num, file_name)));
1973 *err_msg = "list of RADIUS secrets cannot be empty";
1974 return NULL;
1975 }
1976
1977 /*
1978 * Verify length of option lists - each can be 0 (except for secrets,
1979 * but that's already checked above), 1 (use the same value
1980 * everywhere) or the same as the number of servers.
1981 */
1982 if (!(list_length(parsedline->radiussecrets) == 1 ||
1983 list_length(parsedline->radiussecrets) == list_length(parsedline->radiusservers)))
1984 {
1985 ereport(elevel,
1986 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1987 errmsg("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
1988 list_length(parsedline->radiussecrets),
1989 list_length(parsedline->radiusservers)),
1990 errcontext("line %d of configuration file \"%s\"",
1991 line_num, file_name)));
1992 *err_msg = psprintf("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
1993 list_length(parsedline->radiussecrets),
1994 list_length(parsedline->radiusservers));
1995 return NULL;
1996 }
1997 if (!(list_length(parsedline->radiusports) == 0 ||
1998 list_length(parsedline->radiusports) == 1 ||
1999 list_length(parsedline->radiusports) == list_length(parsedline->radiusservers)))
2000 {
2001 ereport(elevel,
2002 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2003 errmsg("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2004 list_length(parsedline->radiusports),
2005 list_length(parsedline->radiusservers)),
2006 errcontext("line %d of configuration file \"%s\"",
2007 line_num, file_name)));
2008 *err_msg = psprintf("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2009 list_length(parsedline->radiusports),
2010 list_length(parsedline->radiusservers));
2011 return NULL;
2012 }
2013 if (!(list_length(parsedline->radiusidentifiers) == 0 ||
2014 list_length(parsedline->radiusidentifiers) == 1 ||
2015 list_length(parsedline->radiusidentifiers) == list_length(parsedline->radiusservers)))
2016 {
2017 ereport(elevel,
2018 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2019 errmsg("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2020 list_length(parsedline->radiusidentifiers),
2021 list_length(parsedline->radiusservers)),
2022 errcontext("line %d of configuration file \"%s\"",
2023 line_num, file_name)));
2024 *err_msg = psprintf("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2025 list_length(parsedline->radiusidentifiers),
2026 list_length(parsedline->radiusservers));
2027 return NULL;
2028 }
2029 }
2030
2031 /*
2032 * Enforce any parameters implied by other settings.
2033 */
2034 if (parsedline->auth_method == uaCert)
2035 {
2036 /*
2037 * For auth method cert, client certificate validation is mandatory,
2038 * and it implies the level of verify-full.
2039 */
2040 parsedline->clientcert = clientCertFull;
2041 }
2042
2043 /*
2044 * Enforce proper configuration of OAuth authentication.
2045 */
2046 if (parsedline->auth_method == uaOAuth)
2047 {
2048 MANDATORY_AUTH_ARG(parsedline->oauth_scope, "scope", "oauth");
2049 MANDATORY_AUTH_ARG(parsedline->oauth_issuer, "issuer", "oauth");
2050
2051 /* Ensure a validator library is set and permitted by the config. */
2052 if (!check_oauth_validator(parsedline, elevel, err_msg))
2053 return NULL;
2054
2055 /*
2056 * Supplying a usermap combined with the option to skip usermapping is
2057 * nonsensical and indicates a configuration error.
2058 */
2059 if (parsedline->oauth_skip_usermap && parsedline->usermap != NULL)
2060 {
2061 ereport(elevel,
2062 errcode(ERRCODE_CONFIG_FILE_ERROR),
2063 /* translator: strings are replaced with hba options */
2064 errmsg("%s cannot be used in combination with %s",
2065 "map", "delegate_ident_mapping"),
2066 errcontext("line %d of configuration file \"%s\"",
2067 line_num, file_name));
2068 *err_msg = "map cannot be used in combination with delegate_ident_mapping";
2069 return NULL;
2070 }
2071 }
2072
2073 return parsedline;
2074}
2075
2076
2077/*
2078 * Parse one name-value pair as an authentication option into the given
2079 * HbaLine. Return true if we successfully parse the option, false if we
2080 * encounter an error. In the event of an error, also log a message at
2081 * ereport level elevel, and store a message string into *err_msg.
2082 */
2083static bool
2084parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
2085 int elevel, char **err_msg)
2086{
2087 int line_num = hbaline->linenumber;
2088 char *file_name = hbaline->sourcefile;
2089
2090#ifdef USE_LDAP
2091 hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
2092#endif
2093
2094 if (strcmp(name, "map") == 0)
2095 {
2096 if (hbaline->auth_method != uaIdent &&
2097 hbaline->auth_method != uaPeer &&
2098 hbaline->auth_method != uaGSS &&
2099 hbaline->auth_method != uaSSPI &&
2100 hbaline->auth_method != uaCert &&
2101 hbaline->auth_method != uaOAuth)
2102 INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, cert, and oauth"));
2103 hbaline->usermap = pstrdup(val);
2104 }
2105 else if (strcmp(name, "clientcert") == 0)
2106 {
2107 if (hbaline->conntype != ctHostSSL)
2108 {
2109 ereport(elevel,
2110 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2111 errmsg("clientcert can only be configured for \"hostssl\" rows"),
2112 errcontext("line %d of configuration file \"%s\"",
2113 line_num, file_name)));
2114 *err_msg = "clientcert can only be configured for \"hostssl\" rows";
2115 return false;
2116 }
2117
2118 if (strcmp(val, "verify-full") == 0)
2119 {
2120 hbaline->clientcert = clientCertFull;
2121 }
2122 else if (strcmp(val, "verify-ca") == 0)
2123 {
2124 if (hbaline->auth_method == uaCert)
2125 {
2126 ereport(elevel,
2127 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2128 errmsg("clientcert only accepts \"verify-full\" when using \"cert\" authentication"),
2129 errcontext("line %d of configuration file \"%s\"",
2130 line_num, file_name)));
2131 *err_msg = "clientcert can only be set to \"verify-full\" when using \"cert\" authentication";
2132 return false;
2133 }
2134
2135 hbaline->clientcert = clientCertCA;
2136 }
2137 else
2138 {
2139 ereport(elevel,
2140 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2141 errmsg("invalid value for clientcert: \"%s\"", val),
2142 errcontext("line %d of configuration file \"%s\"",
2143 line_num, file_name)));
2144 return false;
2145 }
2146 }
2147 else if (strcmp(name, "clientname") == 0)
2148 {
2149 if (hbaline->conntype != ctHostSSL)
2150 {
2151 ereport(elevel,
2152 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2153 errmsg("clientname can only be configured for \"hostssl\" rows"),
2154 errcontext("line %d of configuration file \"%s\"",
2155 line_num, file_name)));
2156 *err_msg = "clientname can only be configured for \"hostssl\" rows";
2157 return false;
2158 }
2159
2160 if (strcmp(val, "CN") == 0)
2161 {
2162 hbaline->clientcertname = clientCertCN;
2163 }
2164 else if (strcmp(val, "DN") == 0)
2165 {
2166 hbaline->clientcertname = clientCertDN;
2167 }
2168 else
2169 {
2170 ereport(elevel,
2171 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2172 errmsg("invalid value for clientname: \"%s\"", val),
2173 errcontext("line %d of configuration file \"%s\"",
2174 line_num, file_name)));
2175 return false;
2176 }
2177 }
2178 else if (strcmp(name, "pamservice") == 0)
2179 {
2180 REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
2181 hbaline->pamservice = pstrdup(val);
2182 }
2183 else if (strcmp(name, "pam_use_hostname") == 0)
2184 {
2185 REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
2186 if (strcmp(val, "1") == 0)
2187 hbaline->pam_use_hostname = true;
2188 else
2189 hbaline->pam_use_hostname = false;
2190 }
2191 else if (strcmp(name, "ldapurl") == 0)
2192 {
2193#ifdef LDAP_API_FEATURE_X_OPENLDAP
2194 LDAPURLDesc *urldata;
2195 int rc;
2196#endif
2197
2198 REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
2199#ifdef LDAP_API_FEATURE_X_OPENLDAP
2200 rc = ldap_url_parse(val, &urldata);
2201 if (rc != LDAP_SUCCESS)
2202 {
2203 ereport(elevel,
2204 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2205 errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
2206 *err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
2207 val, ldap_err2string(rc));
2208 return false;
2209 }
2210
2211 if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
2212 strcmp(urldata->lud_scheme, "ldaps") != 0)
2213 {
2214 ereport(elevel,
2215 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2216 errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
2217 *err_msg = psprintf("unsupported LDAP URL scheme: %s",
2218 urldata->lud_scheme);
2219 ldap_free_urldesc(urldata);
2220 return false;
2221 }
2222
2223 if (urldata->lud_scheme)
2224 hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
2225 if (urldata->lud_host)
2226 hbaline->ldapserver = pstrdup(urldata->lud_host);
2227 hbaline->ldapport = urldata->lud_port;
2228 if (urldata->lud_dn)
2229 hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
2230
2231 if (urldata->lud_attrs)
2232 hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]); /* only use first one */
2233 hbaline->ldapscope = urldata->lud_scope;
2234 if (urldata->lud_filter)
2235 hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter);
2236 ldap_free_urldesc(urldata);
2237#else /* not OpenLDAP */
2238 ereport(elevel,
2239 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2240 errmsg("LDAP URLs not supported on this platform")));
2241 *err_msg = "LDAP URLs not supported on this platform";
2242#endif /* not OpenLDAP */
2243 }
2244 else if (strcmp(name, "ldaptls") == 0)
2245 {
2246 REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
2247 if (strcmp(val, "1") == 0)
2248 hbaline->ldaptls = true;
2249 else
2250 hbaline->ldaptls = false;
2251 }
2252 else if (strcmp(name, "ldapscheme") == 0)
2253 {
2254 REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
2255 if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
2256 ereport(elevel,
2257 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2258 errmsg("invalid ldapscheme value: \"%s\"", val),
2259 errcontext("line %d of configuration file \"%s\"",
2260 line_num, file_name)));
2261 hbaline->ldapscheme = pstrdup(val);
2262 }
2263 else if (strcmp(name, "ldapserver") == 0)
2264 {
2265 REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
2266 hbaline->ldapserver = pstrdup(val);
2267 }
2268 else if (strcmp(name, "ldapport") == 0)
2269 {
2270 REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
2271 hbaline->ldapport = atoi(val);
2272 if (hbaline->ldapport == 0)
2273 {
2274 ereport(elevel,
2275 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2276 errmsg("invalid LDAP port number: \"%s\"", val),
2277 errcontext("line %d of configuration file \"%s\"",
2278 line_num, file_name)));
2279 *err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
2280 return false;
2281 }
2282 }
2283 else if (strcmp(name, "ldapbinddn") == 0)
2284 {
2285 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
2286 hbaline->ldapbinddn = pstrdup(val);
2287 }
2288 else if (strcmp(name, "ldapbindpasswd") == 0)
2289 {
2290 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
2291 hbaline->ldapbindpasswd = pstrdup(val);
2292 }
2293 else if (strcmp(name, "ldapsearchattribute") == 0)
2294 {
2295 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
2296 hbaline->ldapsearchattribute = pstrdup(val);
2297 }
2298 else if (strcmp(name, "ldapsearchfilter") == 0)
2299 {
2300 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter", "ldap");
2301 hbaline->ldapsearchfilter = pstrdup(val);
2302 }
2303 else if (strcmp(name, "ldapbasedn") == 0)
2304 {
2305 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
2306 hbaline->ldapbasedn = pstrdup(val);
2307 }
2308 else if (strcmp(name, "ldapprefix") == 0)
2309 {
2310 REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
2311 hbaline->ldapprefix = pstrdup(val);
2312 }
2313 else if (strcmp(name, "ldapsuffix") == 0)
2314 {
2315 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
2316 hbaline->ldapsuffix = pstrdup(val);
2317 }
2318 else if (strcmp(name, "krb_realm") == 0)
2319 {
2320 if (hbaline->auth_method != uaGSS &&
2321 hbaline->auth_method != uaSSPI)
2322 INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
2323 hbaline->krb_realm = pstrdup(val);
2324 }
2325 else if (strcmp(name, "include_realm") == 0)
2326 {
2327 if (hbaline->auth_method != uaGSS &&
2328 hbaline->auth_method != uaSSPI)
2329 INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
2330 if (strcmp(val, "1") == 0)
2331 hbaline->include_realm = true;
2332 else
2333 hbaline->include_realm = false;
2334 }
2335 else if (strcmp(name, "compat_realm") == 0)
2336 {
2337 if (hbaline->auth_method != uaSSPI)
2338 INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
2339 if (strcmp(val, "1") == 0)
2340 hbaline->compat_realm = true;
2341 else
2342 hbaline->compat_realm = false;
2343 }
2344 else if (strcmp(name, "upn_username") == 0)
2345 {
2346 if (hbaline->auth_method != uaSSPI)
2347 INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
2348 if (strcmp(val, "1") == 0)
2349 hbaline->upn_username = true;
2350 else
2351 hbaline->upn_username = false;
2352 }
2353 else if (strcmp(name, "radiusservers") == 0)
2354 {
2355 struct addrinfo *gai_result;
2356 struct addrinfo hints;
2357 int ret;
2358 List *parsed_servers;
2359 ListCell *l;
2360 char *dupval = pstrdup(val);
2361
2362 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
2363
2364 if (!SplitGUCList(dupval, ',', &parsed_servers))
2365 {
2366 /* syntax error in list */
2367 ereport(elevel,
2368 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2369 errmsg("could not parse RADIUS server list \"%s\"",
2370 val),
2371 errcontext("line %d of configuration file \"%s\"",
2372 line_num, file_name)));
2373 return false;
2374 }
2375
2376 /* For each entry in the list, translate it */
2377 foreach(l, parsed_servers)
2378 {
2379 MemSet(&hints, 0, sizeof(hints));
2380 hints.ai_socktype = SOCK_DGRAM;
2381 hints.ai_family = AF_UNSPEC;
2382
2383 ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
2384 if (ret || !gai_result)
2385 {
2386 ereport(elevel,
2387 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2388 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
2389 (char *) lfirst(l), gai_strerror(ret)),
2390 errcontext("line %d of configuration file \"%s\"",
2391 line_num, file_name)));
2392 if (gai_result)
2393 pg_freeaddrinfo_all(hints.ai_family, gai_result);
2394
2395 list_free(parsed_servers);
2396 return false;
2397 }
2398 pg_freeaddrinfo_all(hints.ai_family, gai_result);
2399 }
2400
2401 /* All entries are OK, so store them */
2402 hbaline->radiusservers = parsed_servers;
2403 hbaline->radiusservers_s = pstrdup(val);
2404 }
2405 else if (strcmp(name, "radiusports") == 0)
2406 {
2407 List *parsed_ports;
2408 ListCell *l;
2409 char *dupval = pstrdup(val);
2410
2411 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
2412
2413 if (!SplitGUCList(dupval, ',', &parsed_ports))
2414 {
2415 ereport(elevel,
2416 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2417 errmsg("could not parse RADIUS port list \"%s\"",
2418 val),
2419 errcontext("line %d of configuration file \"%s\"",
2420 line_num, file_name)));
2421 *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
2422 return false;
2423 }
2424
2425 foreach(l, parsed_ports)
2426 {
2427 if (atoi(lfirst(l)) == 0)
2428 {
2429 ereport(elevel,
2430 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2431 errmsg("invalid RADIUS port number: \"%s\"", val),
2432 errcontext("line %d of configuration file \"%s\"",
2433 line_num, file_name)));
2434
2435 return false;
2436 }
2437 }
2438 hbaline->radiusports = parsed_ports;
2439 hbaline->radiusports_s = pstrdup(val);
2440 }
2441 else if (strcmp(name, "radiussecrets") == 0)
2442 {
2443 List *parsed_secrets;
2444 char *dupval = pstrdup(val);
2445
2446 REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
2447
2448 if (!SplitGUCList(dupval, ',', &parsed_secrets))
2449 {
2450 /* syntax error in list */
2451 ereport(elevel,
2452 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2453 errmsg("could not parse RADIUS secret list \"%s\"",
2454 val),
2455 errcontext("line %d of configuration file \"%s\"",
2456 line_num, file_name)));
2457 return false;
2458 }
2459
2460 hbaline->radiussecrets = parsed_secrets;
2461 hbaline->radiussecrets_s = pstrdup(val);
2462 }
2463 else if (strcmp(name, "radiusidentifiers") == 0)
2464 {
2465 List *parsed_identifiers;
2466 char *dupval = pstrdup(val);
2467
2468 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
2469
2470 if (!SplitGUCList(dupval, ',', &parsed_identifiers))
2471 {
2472 /* syntax error in list */
2473 ereport(elevel,
2474 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2475 errmsg("could not parse RADIUS identifiers list \"%s\"",
2476 val),
2477 errcontext("line %d of configuration file \"%s\"",
2478 line_num, file_name)));
2479 return false;
2480 }
2481
2482 hbaline->radiusidentifiers = parsed_identifiers;
2483 hbaline->radiusidentifiers_s = pstrdup(val);
2484 }
2485 else if (strcmp(name, "issuer") == 0)
2486 {
2487 REQUIRE_AUTH_OPTION(uaOAuth, "issuer", "oauth");
2488 hbaline->oauth_issuer = pstrdup(val);
2489 }
2490 else if (strcmp(name, "scope") == 0)
2491 {
2492 REQUIRE_AUTH_OPTION(uaOAuth, "scope", "oauth");
2493 hbaline->oauth_scope = pstrdup(val);
2494 }
2495 else if (strcmp(name, "validator") == 0)
2496 {
2497 REQUIRE_AUTH_OPTION(uaOAuth, "validator", "oauth");
2498 hbaline->oauth_validator = pstrdup(val);
2499 }
2500 else if (strcmp(name, "delegate_ident_mapping") == 0)
2501 {
2502 REQUIRE_AUTH_OPTION(uaOAuth, "delegate_ident_mapping", "oauth");
2503 if (strcmp(val, "1") == 0)
2504 hbaline->oauth_skip_usermap = true;
2505 else
2506 hbaline->oauth_skip_usermap = false;
2507 }
2508 else
2509 {
2510 ereport(elevel,
2511 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2512 errmsg("unrecognized authentication option name: \"%s\"",
2513 name),
2514 errcontext("line %d of configuration file \"%s\"",
2515 line_num, file_name)));
2516 *err_msg = psprintf("unrecognized authentication option name: \"%s\"",
2517 name);
2518 return false;
2519 }
2520 return true;
2521}
2522
2523/*
2524 * Scan the pre-parsed hba file, looking for a match to the port's connection
2525 * request.
2526 */
2527static void
2529{
2530 Oid roleid;
2531 ListCell *line;
2532 HbaLine *hba;
2533
2534 /* Get the target role's OID. Note we do not error out for bad role. */
2535 roleid = get_role_oid(port->user_name, true);
2536
2537 foreach(line, parsed_hba_lines)
2538 {
2539 hba = (HbaLine *) lfirst(line);
2540
2541 /* Check connection type */
2542 if (hba->conntype == ctLocal)
2543 {
2544 if (port->raddr.addr.ss_family != AF_UNIX)
2545 continue;
2546 }
2547 else
2548 {
2549 if (port->raddr.addr.ss_family == AF_UNIX)
2550 continue;
2551
2552 /* Check SSL state */
2553 if (port->ssl_in_use)
2554 {
2555 /* Connection is SSL, match both "host" and "hostssl" */
2556 if (hba->conntype == ctHostNoSSL)
2557 continue;
2558 }
2559 else
2560 {
2561 /* Connection is not SSL, match both "host" and "hostnossl" */
2562 if (hba->conntype == ctHostSSL)
2563 continue;
2564 }
2565
2566 /* Check GSSAPI state */
2567#ifdef ENABLE_GSS
2568 if (port->gss && port->gss->enc &&
2569 hba->conntype == ctHostNoGSS)
2570 continue;
2571 else if (!(port->gss && port->gss->enc) &&
2572 hba->conntype == ctHostGSS)
2573 continue;
2574#else
2575 if (hba->conntype == ctHostGSS)
2576 continue;
2577#endif
2578
2579 /* Check IP address */
2580 switch (hba->ip_cmp_method)
2581 {
2582 case ipCmpMask:
2583 if (hba->hostname)
2584 {
2585 if (!check_hostname(port,
2586 hba->hostname))
2587 continue;
2588 }
2589 else
2590 {
2591 if (!check_ip(&port->raddr,
2592 (struct sockaddr *) &hba->addr,
2593 (struct sockaddr *) &hba->mask))
2594 continue;
2595 }
2596 break;
2597 case ipCmpAll:
2598 break;
2599 case ipCmpSameHost:
2600 case ipCmpSameNet:
2601 if (!check_same_host_or_net(&port->raddr,
2602 hba->ip_cmp_method))
2603 continue;
2604 break;
2605 default:
2606 /* shouldn't get here, but deem it no-match if so */
2607 continue;
2608 }
2609 } /* != ctLocal */
2610
2611 /* Check database and role */
2612 if (!check_db(port->database_name, port->user_name, roleid,
2613 hba->databases))
2614 continue;
2615
2616 if (!check_role(port->user_name, roleid, hba->roles, false))
2617 continue;
2618
2619 /* Found a record that matched! */
2620 port->hba = hba;
2621 return;
2622 }
2623
2624 /* If no matching entry was found, then implicitly reject. */
2625 hba = palloc0(sizeof(HbaLine));
2627 port->hba = hba;
2628}
2629
2630/*
2631 * Read the config file and create a List of HbaLine records for the contents.
2632 *
2633 * The configuration is read into a temporary list, and if any parse error
2634 * occurs the old list is kept in place and false is returned. Only if the
2635 * whole file parses OK is the list replaced, and the function returns true.
2636 *
2637 * On a false result, caller will take care of reporting a FATAL error in case
2638 * this is the initial startup. If it happens on reload, we just keep running
2639 * with the old data.
2640 */
2641bool
2643{
2644 FILE *file;
2645 List *hba_lines = NIL;
2646 ListCell *line;
2647 List *new_parsed_lines = NIL;
2648 bool ok = true;
2649 MemoryContext oldcxt;
2650 MemoryContext hbacxt;
2651
2652 file = open_auth_file(HbaFileName, LOG, 0, NULL);
2653 if (file == NULL)
2654 {
2655 /* error already logged */
2656 return false;
2657 }
2658
2659 tokenize_auth_file(HbaFileName, file, &hba_lines, LOG, 0);
2660
2661 /* Now parse all the lines */
2664 "hba parser context",
2666 oldcxt = MemoryContextSwitchTo(hbacxt);
2667 foreach(line, hba_lines)
2668 {
2669 TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
2671
2672 /* don't parse lines that already have errors */
2673 if (tok_line->err_msg != NULL)
2674 {
2675 ok = false;
2676 continue;
2677 }
2678
2679 if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
2680 {
2681 /* Parse error; remember there's trouble */
2682 ok = false;
2683
2684 /*
2685 * Keep parsing the rest of the file so we can report errors on
2686 * more than the first line. Error has already been logged, no
2687 * need for more chatter here.
2688 */
2689 continue;
2690 }
2691
2692 new_parsed_lines = lappend(new_parsed_lines, newline);
2693 }
2694
2695 /*
2696 * A valid HBA file must have at least one entry; else there's no way to
2697 * connect to the postmaster. But only complain about this if we didn't
2698 * already have parsing errors.
2699 */
2700 if (ok && new_parsed_lines == NIL)
2701 {
2702 ereport(LOG,
2703 (errcode(ERRCODE_CONFIG_FILE_ERROR),
2704 errmsg("configuration file \"%s\" contains no entries",
2705 HbaFileName)));
2706 ok = false;
2707 }
2708
2709 /* Free tokenizer memory */
2710 free_auth_file(file, 0);
2711 MemoryContextSwitchTo(oldcxt);
2712
2713 if (!ok)
2714 {
2715 /*
2716 * File contained one or more errors, so bail out. MemoryContextDelete
2717 * is enough to clean up everything, including regexes.
2718 */
2719 MemoryContextDelete(hbacxt);
2720 return false;
2721 }
2722
2723 /* Loaded new file successfully, replace the one we use */
2724 if (parsed_hba_context != NULL)
2726 parsed_hba_context = hbacxt;
2727 parsed_hba_lines = new_parsed_lines;
2728
2729 return true;
2730}
2731
2732
2733/*
2734 * Parse one tokenised line from the ident config file and store the result in
2735 * an IdentLine structure.
2736 *
2737 * If parsing fails, log a message at ereport level elevel, store an error
2738 * string in tok_line->err_msg and return NULL.
2739 *
2740 * If ident_user is a regular expression (ie. begins with a slash), it is
2741 * compiled and stored in IdentLine structure.
2742 *
2743 * Note: this function leaks memory when an error occurs. Caller is expected
2744 * to have set a memory context that will be reset if this function returns
2745 * NULL.
2746 */
2747IdentLine *
2749{
2750 int line_num = tok_line->line_num;
2751 char *file_name = tok_line->file_name;
2752 char **err_msg = &tok_line->err_msg;
2753 ListCell *field;
2754 List *tokens;
2756 IdentLine *parsedline;
2757
2758 Assert(tok_line->fields != NIL);
2759 field = list_head(tok_line->fields);
2760
2761 parsedline = palloc0(sizeof(IdentLine));
2762 parsedline->linenumber = line_num;
2763
2764 /* Get the map token (must exist) */
2765 tokens = lfirst(field);
2766 IDENT_MULTI_VALUE(tokens);
2767 token = linitial(tokens);
2768 parsedline->usermap = pstrdup(token->string);
2769
2770 /* Get the ident user token */
2771 field = lnext(tok_line->fields, field);
2772 IDENT_FIELD_ABSENT(field);
2773 tokens = lfirst(field);
2774 IDENT_MULTI_VALUE(tokens);
2775 token = linitial(tokens);
2776
2777 /* Copy the ident user token */
2778 parsedline->system_user = copy_auth_token(token);
2779
2780 /* Get the PG rolename token */
2781 field = lnext(tok_line->fields, field);
2782 IDENT_FIELD_ABSENT(field);
2783 tokens = lfirst(field);
2784 IDENT_MULTI_VALUE(tokens);
2785 token = linitial(tokens);
2786 parsedline->pg_user = copy_auth_token(token);
2787
2788 /*
2789 * Now that the field validation is done, compile a regex from the user
2790 * tokens, if necessary.
2791 */
2792 if (regcomp_auth_token(parsedline->system_user, file_name, line_num,
2793 err_msg, elevel))
2794 {
2795 /* err_msg includes the error to report */
2796 return NULL;
2797 }
2798
2799 if (regcomp_auth_token(parsedline->pg_user, file_name, line_num,
2800 err_msg, elevel))
2801 {
2802 /* err_msg includes the error to report */
2803 return NULL;
2804 }
2805
2806 return parsedline;
2807}
2808
2809/*
2810 * Process one line from the parsed ident config lines.
2811 *
2812 * Compare input parsed ident line to the needed map, pg_user and system_user.
2813 * *found_p and *error_p are set according to our results.
2814 */
2815static void
2816check_ident_usermap(IdentLine *identLine, const char *usermap_name,
2817 const char *pg_user, const char *system_user,
2818 bool case_insensitive, bool *found_p, bool *error_p)
2819{
2820 Oid roleid;
2821
2822 *found_p = false;
2823 *error_p = false;
2824
2825 if (strcmp(identLine->usermap, usermap_name) != 0)
2826 /* Line does not match the map name we're looking for, so just abort */
2827 return;
2828
2829 /* Get the target role's OID. Note we do not error out for bad role. */
2830 roleid = get_role_oid(pg_user, true);
2831
2832 /* Match? */
2833 if (token_has_regexp(identLine->system_user))
2834 {
2835 /*
2836 * Process the system username as a regular expression that returns
2837 * exactly one match. This is replaced for \1 in the database username
2838 * string, if present.
2839 */
2840 int r;
2841 regmatch_t matches[2];
2842 char *ofs;
2843 AuthToken *expanded_pg_user_token;
2844 bool created_temporary_token = false;
2845
2846 r = regexec_auth_token(system_user, identLine->system_user, 2, matches);
2847 if (r)
2848 {
2849 char errstr[100];
2850
2851 if (r != REG_NOMATCH)
2852 {
2853 /* REG_NOMATCH is not an error, everything else is */
2854 pg_regerror(r, identLine->system_user->regex, errstr, sizeof(errstr));
2855 ereport(LOG,
2856 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2857 errmsg("regular expression match for \"%s\" failed: %s",
2858 identLine->system_user->string + 1, errstr)));
2859 *error_p = true;
2860 }
2861 return;
2862 }
2863
2864 /*
2865 * Replace \1 with the first captured group unless the field already
2866 * has some special meaning, like a group membership or a regexp-based
2867 * check.
2868 */
2869 if (!token_is_member_check(identLine->pg_user) &&
2870 !token_has_regexp(identLine->pg_user) &&
2871 (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL)
2872 {
2873 const char *repl_str;
2874 size_t repl_len;
2875 char *old_pg_user;
2876 char *expanded_pg_user;
2877 size_t offset;
2878
2879 /* substitution of the first argument requested */
2880 if (matches[1].rm_so < 0)
2881 {
2882 ereport(LOG,
2883 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2884 errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
2885 identLine->system_user->string + 1, identLine->pg_user->string)));
2886 *error_p = true;
2887 return;
2888 }
2889 repl_str = system_user + matches[1].rm_so;
2890 repl_len = matches[1].rm_eo - matches[1].rm_so;
2891
2892 /*
2893 * It's allowed to have more than one \1 in the string, and we'll
2894 * replace them all. But that's pretty unusual so we optimize on
2895 * the assumption of only one occurrence, which motivates doing
2896 * repeated replacements instead of making two passes over the
2897 * string to determine the final length right away.
2898 */
2899 old_pg_user = identLine->pg_user->string;
2900 do
2901 {
2902 /*
2903 * length: current length minus length of \1 plus length of
2904 * replacement plus null terminator
2905 */
2906 expanded_pg_user = palloc(strlen(old_pg_user) - 2 + repl_len + 1);
2907 /* ofs points into the old_pg_user string at this point */
2908 offset = ofs - old_pg_user;
2909 memcpy(expanded_pg_user, old_pg_user, offset);
2910 memcpy(expanded_pg_user + offset, repl_str, repl_len);
2911 strcpy(expanded_pg_user + offset + repl_len, ofs + 2);
2912 if (old_pg_user != identLine->pg_user->string)
2913 pfree(old_pg_user);
2914 old_pg_user = expanded_pg_user;
2915 } while ((ofs = strstr(old_pg_user + offset + repl_len, "\\1")) != NULL);
2916
2917 /*
2918 * Mark the token as quoted, so it will only be compared literally
2919 * and not for some special meaning, such as "all" or a group
2920 * membership check.
2921 */
2922 expanded_pg_user_token = make_auth_token(expanded_pg_user, true);
2923 created_temporary_token = true;
2924 pfree(expanded_pg_user);
2925 }
2926 else
2927 {
2928 expanded_pg_user_token = identLine->pg_user;
2929 }
2930
2931 /* check the Postgres user */
2932 *found_p = check_role(pg_user, roleid,
2933 list_make1(expanded_pg_user_token),
2934 case_insensitive);
2935
2936 if (created_temporary_token)
2937 free_auth_token(expanded_pg_user_token);
2938
2939 return;
2940 }
2941 else
2942 {
2943 /*
2944 * Not a regular expression, so make a complete match. If the system
2945 * user does not match, just leave.
2946 */
2947 if (case_insensitive)
2948 {
2949 if (!token_matches_insensitive(identLine->system_user,
2950 system_user))
2951 return;
2952 }
2953 else
2954 {
2955 if (!token_matches(identLine->system_user, system_user))
2956 return;
2957 }
2958
2959 /* check the Postgres user */
2960 *found_p = check_role(pg_user, roleid,
2961 list_make1(identLine->pg_user),
2962 case_insensitive);
2963 }
2964}
2965
2966
2967/*
2968 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
2969 *
2970 * See if the system user with ident username "system_user" is allowed to act as
2971 * Postgres user "pg_user" according to usermap "usermap_name".
2972 *
2973 * Special case: Usermap NULL, equivalent to what was previously called
2974 * "sameuser" or "samerole", means don't look in the usermap file.
2975 * That's an implied map wherein "pg_user" must be identical to
2976 * "system_user" in order to be authorized.
2977 *
2978 * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
2979 */
2980int
2981check_usermap(const char *usermap_name,
2982 const char *pg_user,
2983 const char *system_user,
2984 bool case_insensitive)
2985{
2986 bool found_entry = false,
2987 error = false;
2988
2989 if (usermap_name == NULL || usermap_name[0] == '\0')
2990 {
2991 if (case_insensitive)
2992 {
2993 if (pg_strcasecmp(pg_user, system_user) == 0)
2994 return STATUS_OK;
2995 }
2996 else
2997 {
2998 if (strcmp(pg_user, system_user) == 0)
2999 return STATUS_OK;
3000 }
3001 ereport(LOG,
3002 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
3003 pg_user, system_user)));
3004 return STATUS_ERROR;
3005 }
3006 else
3007 {
3008 ListCell *line_cell;
3009
3010 foreach(line_cell, parsed_ident_lines)
3011 {
3012 check_ident_usermap(lfirst(line_cell), usermap_name,
3013 pg_user, system_user, case_insensitive,
3014 &found_entry, &error);
3015 if (found_entry || error)
3016 break;
3017 }
3018 }
3019 if (!found_entry && !error)
3020 {
3021 ereport(LOG,
3022 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
3023 usermap_name, pg_user, system_user)));
3024 }
3025 return found_entry ? STATUS_OK : STATUS_ERROR;
3026}
3027
3028
3029/*
3030 * Read the ident config file and create a List of IdentLine records for
3031 * the contents.
3032 *
3033 * This works the same as load_hba(), but for the user config file.
3034 */
3035bool
3037{
3038 FILE *file;
3039 List *ident_lines = NIL;
3040 ListCell *line_cell;
3041 List *new_parsed_lines = NIL;
3042 bool ok = true;
3043 MemoryContext oldcxt;
3044 MemoryContext ident_context;
3046
3047 /* not FATAL ... we just won't do any special ident maps */
3048 file = open_auth_file(IdentFileName, LOG, 0, NULL);
3049 if (file == NULL)
3050 {
3051 /* error already logged */
3052 return false;
3053 }
3054
3055 tokenize_auth_file(IdentFileName, file, &ident_lines, LOG, 0);
3056
3057 /* Now parse all the lines */
3060 "ident parser context",
3062 oldcxt = MemoryContextSwitchTo(ident_context);
3063 foreach(line_cell, ident_lines)
3064 {
3065 TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line_cell);
3066
3067 /* don't parse lines that already have errors */
3068 if (tok_line->err_msg != NULL)
3069 {
3070 ok = false;
3071 continue;
3072 }
3073
3074 if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
3075 {
3076 /* Parse error; remember there's trouble */
3077 ok = false;
3078
3079 /*
3080 * Keep parsing the rest of the file so we can report errors on
3081 * more than the first line. Error has already been logged, no
3082 * need for more chatter here.
3083 */
3084 continue;
3085 }
3086
3087 new_parsed_lines = lappend(new_parsed_lines, newline);
3088 }
3089
3090 /* Free tokenizer memory */
3091 free_auth_file(file, 0);
3092 MemoryContextSwitchTo(oldcxt);
3093
3094 if (!ok)
3095 {
3096 /*
3097 * File contained one or more errors, so bail out. MemoryContextDelete
3098 * is enough to clean up everything, including regexes.
3099 */
3100 MemoryContextDelete(ident_context);
3101 return false;
3102 }
3103
3104 /* Loaded new file successfully, replace the one we use */
3105 if (parsed_ident_context != NULL)
3107
3108 parsed_ident_context = ident_context;
3109 parsed_ident_lines = new_parsed_lines;
3110
3111 return true;
3112}
3113
3114
3115
3116/*
3117 * Determine what authentication method should be used when accessing database
3118 * "database" from frontend "raddr", user "user". Return the method and
3119 * an optional argument (stored in fields of *port), and STATUS_OK.
3120 *
3121 * If the file does not contain any entry matching the request, we return
3122 * method = uaImplicitReject.
3123 */
3124void
3126{
3127 check_hba(port);
3128}
3129
3130
3131/*
3132 * Return the name of the auth method in use ("gss", "md5", "trust", etc.).
3133 *
3134 * The return value is statically allocated (see the UserAuthName array) and
3135 * should not be freed.
3136 */
3137const char *
3139{
3140 return UserAuthName[auth_method];
3141}
bool is_member_of_role_nosuper(Oid member, Oid role)
Definition: acl.c:5392
Oid get_role_oid(const char *rolname, bool missing_ok)
Definition: acl.c:5552
bool check_oauth_validator(HbaLine *hbaline, int elevel, char **err_msg)
Definition: auth-oauth.c:820
#define STATUS_OK
Definition: c.h:1157
#define IS_HIGHBIT_SET(ch)
Definition: c.h:1143
#define gettext_noop(x)
Definition: c.h:1184
#define lengthof(array)
Definition: c.h:790
#define MemSet(start, val, len)
Definition: c.h:1022
#define STATUS_ERROR
Definition: c.h:1158
#define OidIsValid(objectId)
Definition: c.h:777
char * AbsoluteConfigLocation(const char *location, const char *calling_file)
Definition: conffiles.c:36
char ** GetConfFilesInDir(const char *includedir, const char *calling_file, int elevel, int *num_filenames, char **err_msg)
Definition: conffiles.c:70
#define CONF_FILE_MAX_DEPTH
Definition: conffiles.h:18
#define CONF_FILE_START_DEPTH
Definition: conffiles.h:17
int errcode_for_file_access(void)
Definition: elog.c:886
ErrorContextCallback * error_context_stack
Definition: elog.c:95
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define LOG
Definition: elog.h:31
#define errcontext
Definition: elog.h:198
#define DEBUG2
Definition: elog.h:29
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
int FreeFile(FILE *file)
Definition: fd.c:2840
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2641
char * HbaFileName
Definition: guc_tables.c:557
char * IdentFileName
Definition: guc_tables.c:558
Assert(PointerIsAligned(start, uint64))
const char * str
static AuthToken * make_auth_token(const char *token, bool quoted)
Definition: hba.c:256
#define MANDATORY_AUTH_ARG(argvar, argname, authname)
Definition: hba.c:1261
static bool is_member(Oid userid, const char *role)
Definition: hba.c:922
static bool check_role(const char *role, Oid roleid, List *tokens, bool case_insensitive)
Definition: hba.c:951
static bool check_hostname(Port *port, const char *hostname)
Definition: hba.c:1075
static bool pg_isblank(const char c)
Definition: hba.c:142
HbaLine * parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
Definition: hba.c:1325
static bool hostname_match(const char *pattern, const char *actual_hostname)
Definition: hba.c:1055
StaticAssertDecl(lengthof(UserAuthName)==USER_AUTH_LAST+1, "UserAuthName[] must match the UserAuth enum")
static bool check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
Definition: hba.c:1166
#define token_is_member_check(t)
Definition: hba.c:70
#define IDENT_FIELD_ABSENT(field)
Definition: hba.c:1285
#define token_is_keyword(t, k)
Definition: hba.c:71
static bool ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
Definition: hba.c:1034
static List * next_field_expand(const char *filename, char **lineptr, int elevel, int depth, char **err_msg)
Definition: hba.c:378
static MemoryContext parsed_ident_context
Definition: hba.c:94
#define token_matches_insensitive(t, k)
Definition: hba.c:73
bool load_ident(void)
Definition: hba.c:3036
struct check_network_data check_network_data
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int elevel, char **err_msg)
Definition: hba.c:2084
static bool check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
Definition: hba.c:1207
static int regcomp_auth_token(AuthToken *token, char *filename, int line_num, char **err_msg, int elevel)
Definition: hba.c:300
static MemoryContext tokenize_context
Definition: hba.c:80
static int regexec_auth_token(const char *match, AuthToken *token, size_t nmatch, regmatch_t pmatch[])
Definition: hba.c:345
static void tokenize_include_file(const char *outer_filename, const char *inc_filename, List **tok_lines, int elevel, int depth, bool missing_ok, char **err_msg)
Definition: hba.c:437
static void tokenize_error_callback(void *arg)
Definition: hba.c:659
static void check_network_callback(struct sockaddr *addr, struct sockaddr *netmask, void *cb_data)
Definition: hba.c:1180
#define token_has_regexp(t)
Definition: hba.c:69
static MemoryContext parsed_hba_context
Definition: hba.c:87
static AuthToken * copy_auth_token(AuthToken *in)
Definition: hba.c:287
#define IDENT_MULTI_VALUE(tokens)
Definition: hba.c:1298
bool load_hba(void)
Definition: hba.c:2642
IdentLine * parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
Definition: hba.c:2748
int check_usermap(const char *usermap_name, const char *pg_user, const char *system_user, bool case_insensitive)
Definition: hba.c:2981
void free_auth_file(FILE *file, int depth)
Definition: hba.c:569
static bool ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
Definition: hba.c:1040
static List * tokenize_expand_file(List *tokens, const char *outer_filename, const char *inc_filename, int elevel, int depth, char **err_msg)
Definition: hba.c:492
static void free_auth_token(AuthToken *token)
Definition: hba.c:277
static List * parsed_ident_lines
Definition: hba.c:93
static const char *const UserAuthName[]
Definition: hba.c:102
#define token_matches(t, k)
Definition: hba.c:72
static bool next_token(char **lineptr, StringInfo buf, bool *initial_quote, bool *terminating_comma)
Definition: hba.c:184
static List * parsed_hba_lines
Definition: hba.c:86
#define INVALID_AUTH_OPTION(optname, validmethods)
Definition: hba.c:1241
void hba_getauthmethod(Port *port)
Definition: hba.c:3125
static void check_hba(Port *port)
Definition: hba.c:2528
void tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, int elevel, int depth)
Definition: hba.c:688
static void check_ident_usermap(IdentLine *identLine, const char *usermap_name, const char *pg_user, const char *system_user, bool case_insensitive, bool *found_p, bool *error_p)
Definition: hba.c:2816
static bool check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
Definition: hba.c:990
const char * hba_authname(UserAuth auth_method)
Definition: hba.c:3138
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods)
Definition: hba.c:1255
FILE * open_auth_file(const char *filename, int elevel, int depth, char **err_msg)
Definition: hba.c:594
IPCompareMethod
Definition: hba.h:51
@ ipCmpAll
Definition: hba.h:55
@ ipCmpSameNet
Definition: hba.h:54
@ ipCmpMask
Definition: hba.h:52
@ ipCmpSameHost
Definition: hba.h:53
@ ctHostNoGSS
Definition: hba.h:65
@ ctHostSSL
Definition: hba.h:62
@ ctHostNoSSL
Definition: hba.h:63
@ ctHost
Definition: hba.h:61
@ ctHostGSS
Definition: hba.h:64
@ ctLocal
Definition: hba.h:60
#define USER_AUTH_LAST
Definition: hba.h:43
UserAuth
Definition: hba.h:26
@ uaBSD
Definition: hba.h:37
@ uaLDAP
Definition: hba.h:38
@ uaPeer
Definition: hba.h:41
@ uaPAM
Definition: hba.h:36
@ uaPassword
Definition: hba.h:31
@ uaCert
Definition: hba.h:39
@ uaMD5
Definition: hba.h:32
@ uaReject
Definition: hba.h:27
@ uaGSS
Definition: hba.h:34
@ uaSCRAM
Definition: hba.h:33
@ uaImplicitReject
Definition: hba.h:28
@ uaRADIUS
Definition: hba.h:40
@ uaIdent
Definition: hba.h:30
@ uaOAuth
Definition: hba.h:42
@ uaTrust
Definition: hba.h:29
@ uaSSPI
Definition: hba.h:35
@ clientCertDN
Definition: hba.h:78
@ clientCertCN
Definition: hba.h:77
@ clientCertFull
Definition: hba.h:72
@ clientCertCA
Definition: hba.h:71
int pg_sockaddr_cidr_mask(struct sockaddr_storage *mask, char *numbits, int family)
Definition: ifaddr.c:105
int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
Definition: ifaddr.c:425
int pg_range_sockaddr(const struct sockaddr_storage *addr, const struct sockaddr_storage *netaddr, const struct sockaddr_storage *netmask)
Definition: ifaddr.c:49
#define newline
Definition: indent_codes.h:35
#define token
Definition: indent_globs.h:126
long val
Definition: informix.c:689
void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
Definition: ip.c:85
int pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen, char *node, int nodelen, char *service, int servicelen, int flags)
Definition: ip.c:117
int pg_getaddrinfo_all(const char *hostname, const char *servname, const struct addrinfo *hintp, struct addrinfo **result)
Definition: ip.c:56
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int i
Definition: isn.c:77
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
unsigned int pg_wchar
Definition: mbprint.c:31
int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len)
Definition: mbutils.c:987
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
void * palloc(Size size)
Definition: mcxt.c:1365
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
MemoryContext PostmasterContext
Definition: mcxt.c:168
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_START_SMALL_SIZES
Definition: memutils.h:177
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
Datum system_user(PG_FUNCTION_ARGS)
Definition: miscinit.c:898
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
static char * filename
Definition: pg_dumpall.c:120
bool pg_get_line_append(FILE *stream, StringInfo buf, PromptInterruptContext *prompt_ctx)
Definition: pg_get_line.c:124
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial_node(type, l)
Definition: pg_list.h:181
#define NIL
Definition: pg_list.h:68
#define lsecond_node(type, l)
Definition: pg_list.h:186
#define list_make1(x1)
Definition: pg_list.h:212
#define linitial(l)
Definition: pg_list.h:178
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
static int port
Definition: pg_regress.c:115
static char * hostname
Definition: pg_regress.c:114
static char * buf
Definition: pg_test_fsync.c:72
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:32
unsigned int Oid
Definition: postgres_ext.h:32
bool EnableSSL
Definition: postmaster.c:238
char * c
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
int pg_regcomp(regex_t *re, const chr *string, size_t len, int flags, Oid collation)
Definition: regcomp.c:372
size_t pg_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
Definition: regerror.c:60
#define REG_NOMATCH
Definition: regex.h:216
#define REG_ADVANCED
Definition: regex.h:181
#define regmatch_t
Definition: regex.h:246
#define REG_OKAY
Definition: regex.h:215
#define regex_t
Definition: regex.h:245
int pg_regexec(regex_t *re, const chr *string, size_t len, size_t search_start, rm_detail_t *details, size_t nmatch, regmatch_t pmatch[], int flags)
Definition: regexec.c:185
void pg_regfree(regex_t *re)
Definition: regfree.c:49
const char * gai_strerror(int ecode)
static void error(void)
Definition: sql-dyntest.c:147
char * dbname
Definition: streamutil.c:49
int pg_strip_crlf(char *str)
Definition: string.c:154
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:126
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97
Definition: hba.h:89
regex_t * regex
Definition: hba.h:92
char * string
Definition: hba.h:90
bool quoted
Definition: hba.h:91
struct ErrorContextCallback * previous
Definition: elog.h:297
void(* callback)(void *arg)
Definition: elog.h:298
Definition: hba.h:96
UserAuth auth_method
Definition: hba.h:109
bool upn_username
Definition: hba.h:130
struct sockaddr_storage mask
Definition: hba.h:105
char * sourcefile
Definition: hba.h:97
ClientCertName clientcertname
Definition: hba.h:126
int addrlen
Definition: hba.h:104
List * radiusservers
Definition: hba.h:131
bool oauth_skip_usermap
Definition: hba.h:142
char * ldapserver
Definition: hba.h:115
bool include_realm
Definition: hba.h:128
int masklen
Definition: hba.h:106
ClientCertMode clientcert
Definition: hba.h:125
char * ldapsearchfilter
Definition: hba.h:120
char * ldapscheme
Definition: hba.h:114
char * rawline
Definition: hba.h:99
char * oauth_issuer
Definition: hba.h:139
char * ldapprefix
Definition: hba.h:123
List * radiussecrets
Definition: hba.h:133
char * ldapsearchattribute
Definition: hba.h:119
char * krb_realm
Definition: hba.h:127
char * ldapbasedn
Definition: hba.h:121
bool pam_use_hostname
Definition: hba.h:112
int linenumber
Definition: hba.h:98
char * radiussecrets_s
Definition: hba.h:134
char * oauth_scope
Definition: hba.h:140
List * radiusports
Definition: hba.h:137
List * radiusidentifiers
Definition: hba.h:135
char * oauth_validator
Definition: hba.h:141
char * hostname
Definition: hba.h:108
char * pamservice
Definition: hba.h:111
List * databases
Definition: hba.h:101
ConnType conntype
Definition: hba.h:100
char * usermap
Definition: hba.h:110
char * ldapsuffix
Definition: hba.h:124
int ldapport
Definition: hba.h:116
struct sockaddr_storage addr
Definition: hba.h:103
char * ldapbindpasswd
Definition: hba.h:118
List * roles
Definition: hba.h:102
char * radiusports_s
Definition: hba.h:138
char * ldapbinddn
Definition: hba.h:117
bool compat_realm
Definition: hba.h:129
int ldapscope
Definition: hba.h:122
IPCompareMethod ip_cmp_method
Definition: hba.h:107
bool ldaptls
Definition: hba.h:113
char * radiusservers_s
Definition: hba.h:132
char * radiusidentifiers_s
Definition: hba.h:136
Definition: hba.h:146
AuthToken * pg_user
Definition: hba.h:151
AuthToken * system_user
Definition: hba.h:150
char * usermap
Definition: hba.h:149
int linenumber
Definition: hba.h:147
Definition: pg_list.h:54
int length
Definition: pg_list.h:56
Definition: libpq-be.h:129
struct sockaddr_storage addr
Definition: pqcomm.h:32
char * raw_line
Definition: hba.h:168
int line_num
Definition: hba.h:167
char * file_name
Definition: hba.h:166
char * err_msg
Definition: hba.h:169
List * fields
Definition: hba.h:165
bool result
Definition: hba.c:60
SockAddr * raddr
Definition: hba.c:59
IPCompareMethod method
Definition: hba.c:58
const char * filename
Definition: hba.c:65
bool SplitGUCList(char *rawstring, char separator, List **namelist)
Definition: varlena.c:2992
const char * name
bool am_walsender
Definition: walsender.c:123
bool am_db_walsender
Definition: walsender.c:126