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