Line data Source code
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 :
29 : #include "catalog/pg_collation.h"
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"
36 : #include "postmaster/postmaster.h"
37 : #include "regex/regex.h"
38 : #include "replication/walsender.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 */
56 : typedef 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 */
61 : } check_network_data;
62 :
63 : typedef struct
64 : {
65 : const char *filename;
66 : int linenum;
67 : } tokenize_error_callback_arg;
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 : */
80 : static MemoryContext tokenize_context = NULL;
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 : */
86 : static List *parsed_hba_lines = NIL;
87 : static MemoryContext parsed_hba_context = NULL;
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 : */
93 : static List *parsed_ident_lines = NIL;
94 : static MemoryContext parsed_ident_context = NULL;
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 : */
102 : static 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 : */
125 : StaticAssertDecl(lengthof(UserAuthName) == USER_AUTH_LAST + 1,
126 : "UserAuthName[] must match the UserAuth enum");
127 :
128 :
129 : static List *tokenize_expand_file(List *tokens, const char *outer_filename,
130 : const char *inc_filename, int elevel,
131 : int depth, char **err_msg);
132 : static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
133 : int elevel, char **err_msg);
134 : static int regcomp_auth_token(AuthToken *token, char *filename, int line_num,
135 : char **err_msg, int elevel);
136 : static int regexec_auth_token(const char *match, AuthToken *token,
137 : size_t nmatch, regmatch_t pmatch[]);
138 : static void tokenize_error_callback(void *arg);
139 :
140 :
141 : static bool
142 1631282 : pg_isblank(const char c)
143 : {
144 : /* don't accept non-ASCII data */
145 1631282 : 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 : */
183 : static bool
184 409014 : next_token(char **lineptr, StringInfo buf,
185 : bool *initial_quote, bool *terminating_comma)
186 : {
187 : int c;
188 409014 : bool in_quote = false;
189 409014 : bool was_quote = false;
190 409014 : bool saw_quote = false;
191 :
192 : /* Initialize output parameters */
193 409014 : resetStringInfo(buf);
194 409014 : *initial_quote = false;
195 409014 : *terminating_comma = false;
196 :
197 : /* Move over any whitespace and commas preceding the next token */
198 908816 : 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 734460 : while (c != '\0' &&
206 722470 : (!pg_isblank(c) || in_quote))
207 : {
208 : /* skip comments to EOL */
209 678286 : if (c == '#' && !in_quote)
210 : {
211 14764656 : while ((c = (*(*lineptr)++)) != '\0')
212 : ;
213 352824 : break;
214 : }
215 :
216 : /* we do not pass back a terminating comma in the token */
217 325462 : if (c == ',' && !in_quote)
218 : {
219 16 : *terminating_comma = true;
220 16 : break;
221 : }
222 :
223 325446 : if (c != '"' || was_quote)
224 325096 : appendStringInfoChar(buf, c);
225 :
226 : /* Literal double-quote is two double-quotes */
227 325446 : if (in_quote && c == '"')
228 174 : was_quote = !was_quote;
229 : else
230 325272 : was_quote = false;
231 :
232 325446 : if (c == '"')
233 : {
234 350 : in_quote = !in_quote;
235 350 : saw_quote = true;
236 350 : if (buf->len == 0)
237 106 : *initial_quote = true;
238 : }
239 :
240 325446 : 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 409014 : (*lineptr)--;
248 :
249 409014 : return (saw_quote || buf->len > 0);
250 : }
251 :
252 : /*
253 : * Construct a palloc'd AuthToken struct, copying the given string.
254 : */
255 : static AuthToken *
256 80122 : make_auth_token(const char *token, bool quoted)
257 : {
258 : AuthToken *authtoken;
259 : int toklen;
260 :
261 80122 : toklen = strlen(token);
262 : /* we copy string into same palloc block as the struct */
263 80122 : authtoken = (AuthToken *) palloc0(sizeof(AuthToken) + toklen + 1);
264 80122 : authtoken->string = (char *) authtoken + sizeof(AuthToken);
265 80122 : authtoken->quoted = quoted;
266 80122 : authtoken->regex = NULL;
267 80122 : memcpy(authtoken->string, token, toklen + 1);
268 :
269 80122 : return authtoken;
270 : }
271 :
272 : /*
273 : * Free an AuthToken, that may include a regular expression that needs
274 : * to be cleaned up explicitly.
275 : */
276 : static void
277 4 : free_auth_token(AuthToken *token)
278 : {
279 4 : if (token_has_regexp(token))
280 0 : pg_regfree(token->regex);
281 4 : }
282 :
283 : /*
284 : * Copy a AuthToken struct into freshly palloc'd memory.
285 : */
286 : static AuthToken *
287 23934 : copy_auth_token(AuthToken *in)
288 : {
289 23934 : AuthToken *out = make_auth_token(in->string, in->quoted);
290 :
291 23934 : 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 : */
299 : static int
300 23934 : regcomp_auth_token(AuthToken *token, char *filename, int line_num,
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 23934 : if (token->string[0] != '/')
310 23840 : return 0; /* nothing to compile */
311 :
312 94 : token->regex = (regex_t *) palloc0(sizeof(regex_t));
313 94 : wstr = palloc((strlen(token->string + 1) + 1) * sizeof(pg_wchar));
314 94 : wlen = pg_mb2wchar_with_len(token->string + 1,
315 94 : wstr, strlen(token->string + 1));
316 :
317 94 : rc = pg_regcomp(token->regex, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
318 :
319 94 : if (rc)
320 : {
321 : char errstr[100];
322 :
323 0 : pg_regerror(rc, token->regex, errstr, sizeof(errstr));
324 0 : 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 0 : *err_msg = psprintf("invalid regular expression \"%s\": %s",
332 0 : token->string + 1, errstr);
333 : }
334 :
335 94 : pfree(wstr);
336 94 : 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 : */
344 : static int
345 48 : regexec_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 48 : wmatchstr = palloc((strlen(match) + 1) * sizeof(pg_wchar));
355 48 : wmatchlen = pg_mb2wchar_with_len(match, wmatchstr, strlen(match));
356 :
357 48 : r = pg_regexec(token->regex, wmatchstr, wmatchlen, 0, NULL, nmatch, pmatch, 0);
358 :
359 48 : pfree(wmatchstr);
360 48 : 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 : */
377 : static List *
378 408998 : next_field_expand(const char *filename, char **lineptr,
379 : int elevel, int depth, char **err_msg)
380 : {
381 : StringInfoData buf;
382 : bool trailing_comma;
383 : bool initial_quote;
384 408998 : List *tokens = NIL;
385 :
386 408998 : initStringInfo(&buf);
387 :
388 : do
389 : {
390 409014 : if (!next_token(lineptr, &buf,
391 : &initial_quote, &trailing_comma))
392 352828 : break;
393 :
394 : /* Is this referencing a file? */
395 56186 : if (!initial_quote && buf.len > 1 && buf.data[0] == '@')
396 2 : 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 : */
406 56184 : oldcxt = MemoryContextSwitchTo(tokenize_context);
407 56184 : tokens = lappend(tokens, make_auth_token(buf.data, initial_quote));
408 56184 : MemoryContextSwitchTo(oldcxt);
409 : }
410 56186 : } while (trailing_comma && (*err_msg == NULL));
411 :
412 408998 : pfree(buf.data);
413 :
414 408998 : 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 : */
436 : static void
437 28 : tokenize_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 28 : inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
449 28 : inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
450 :
451 28 : if (!inc_file)
452 : {
453 4 : if (errno == ENOENT && missing_ok)
454 : {
455 4 : ereport(elevel,
456 : (errmsg("skipping missing authentication file \"%s\"",
457 : inc_fullname)));
458 4 : *err_msg = NULL;
459 4 : pfree(inc_fullname);
460 4 : return;
461 : }
462 :
463 : /* error in err_msg, so leave and report */
464 0 : pfree(inc_fullname);
465 : Assert(err_msg);
466 0 : return;
467 : }
468 :
469 24 : tokenize_auth_file(inc_fullname, inc_file, tok_lines, elevel,
470 : depth);
471 24 : free_auth_file(inc_file, depth);
472 24 : 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 : */
491 : static List *
492 2 : tokenize_expand_file(List *tokens,
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 2 : List *inc_lines = NIL;
502 : ListCell *inc_line;
503 :
504 2 : inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
505 2 : inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
506 :
507 2 : if (inc_file == NULL)
508 : {
509 : /* error already logged */
510 0 : pfree(inc_fullname);
511 0 : return tokens;
512 : }
513 :
514 : /*
515 : * There is possible recursion here if the file contains @ or an include
516 : * record.
517 : */
518 2 : tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel,
519 : depth);
520 :
521 2 : 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 6 : foreach(inc_line, inc_lines)
528 : {
529 4 : 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 4 : if (tok_line->err_msg)
534 : {
535 0 : *err_msg = pstrdup(tok_line->err_msg);
536 0 : break;
537 : }
538 :
539 8 : foreach(inc_field, tok_line->fields)
540 : {
541 4 : List *inc_tokens = lfirst(inc_field);
542 : ListCell *inc_token;
543 :
544 8 : foreach(inc_token, inc_tokens)
545 : {
546 4 : 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 : */
553 4 : oldcxt = MemoryContextSwitchTo(tokenize_context);
554 4 : tokens = lappend(tokens, token);
555 4 : MemoryContextSwitchTo(oldcxt);
556 : }
557 : }
558 : }
559 :
560 2 : free_auth_file(inc_file, depth);
561 2 : return tokens;
562 : }
563 :
564 : /*
565 : * free_auth_file
566 : * Free a file opened by open_auth_file().
567 : */
568 : void
569 4110 : free_auth_file(FILE *file, int depth)
570 : {
571 4110 : FreeFile(file);
572 :
573 : /* If this is the last cleanup, remove the tokenization context */
574 4110 : if (depth == CONF_FILE_START_DEPTH)
575 : {
576 4084 : MemoryContextDelete(tokenize_context);
577 4084 : tokenize_context = NULL;
578 : }
579 4110 : }
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 : */
593 : FILE *
594 4114 : open_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 4114 : if (depth > CONF_FILE_MAX_DEPTH)
605 : {
606 0 : ereport(elevel,
607 : (errcode_for_file_access(),
608 : errmsg("could not open file \"%s\": maximum nesting depth exceeded",
609 : filename)));
610 0 : if (err_msg)
611 0 : *err_msg = psprintf("could not open file \"%s\": maximum nesting depth exceeded",
612 : filename);
613 0 : return NULL;
614 : }
615 :
616 4114 : file = AllocateFile(filename, "r");
617 4114 : if (file == NULL)
618 : {
619 4 : int save_errno = errno;
620 :
621 4 : ereport(elevel,
622 : (errcode_for_file_access(),
623 : errmsg("could not open file \"%s\": %m",
624 : filename)));
625 4 : if (err_msg)
626 : {
627 4 : errno = save_errno;
628 4 : *err_msg = psprintf("could not open file \"%s\": %m",
629 : filename);
630 : }
631 : /* the caller may care about some specific errno */
632 4 : errno = save_errno;
633 4 : 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 4110 : if (depth == CONF_FILE_START_DEPTH)
642 : {
643 : /*
644 : * A context may be present, but assume that it has been eliminated
645 : * already.
646 : */
647 4084 : tokenize_context = AllocSetContextCreate(CurrentMemoryContext,
648 : "tokenize_context",
649 : ALLOCSET_START_SMALL_SIZES);
650 : }
651 :
652 4110 : return file;
653 : }
654 :
655 : /*
656 : * error context callback for tokenize_auth_file()
657 : */
658 : static void
659 8 : tokenize_error_callback(void *arg)
660 : {
661 8 : tokenize_error_callback_arg *callback_arg = (tokenize_error_callback_arg *) arg;
662 :
663 8 : errcontext("line %d of configuration file \"%s\"",
664 : callback_arg->linenum, callback_arg->filename);
665 8 : }
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 : */
687 : void
688 4110 : tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
689 : int elevel, int depth)
690 : {
691 4110 : int line_number = 1;
692 : StringInfoData buf;
693 : MemoryContext linecxt;
694 : MemoryContext funccxt; /* context of this function's caller */
695 : ErrorContextCallback tokenerrcontext;
696 : tokenize_error_callback_arg callback_arg;
697 :
698 : Assert(tokenize_context);
699 :
700 4110 : callback_arg.filename = filename;
701 4110 : callback_arg.linenum = line_number;
702 :
703 4110 : tokenerrcontext.callback = tokenize_error_callback;
704 4110 : tokenerrcontext.arg = &callback_arg;
705 4110 : tokenerrcontext.previous = error_context_stack;
706 4110 : 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 : */
712 4110 : linecxt = AllocSetContextCreate(CurrentMemoryContext,
713 : "tokenize_auth_file",
714 : ALLOCSET_SMALL_SIZES);
715 4110 : funccxt = MemoryContextSwitchTo(linecxt);
716 :
717 4110 : initStringInfo(&buf);
718 :
719 4110 : if (depth == CONF_FILE_START_DEPTH)
720 4084 : *tok_lines = NIL;
721 :
722 385466 : while (!feof(file) && !ferror(file))
723 : {
724 : TokenizedAuthLine *tok_line;
725 : MemoryContext oldcxt;
726 : char *lineptr;
727 381356 : List *current_line = NIL;
728 381356 : char *err_msg = NULL;
729 381356 : int last_backslash_buflen = 0;
730 381356 : int continuations = 0;
731 :
732 : /* Collect the next input line, handling backslash continuations */
733 381356 : resetStringInfo(&buf);
734 :
735 381390 : while (pg_get_line_append(file, &buf, NULL))
736 : {
737 : /* Strip trailing newline, including \r in case we're on Windows */
738 377280 : 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 377280 : if (buf.len > last_backslash_buflen &&
746 364848 : buf.data[buf.len - 1] == '\\')
747 : {
748 : /* Continuation, so strip it and keep reading */
749 34 : buf.data[--buf.len] = '\0';
750 34 : last_backslash_buflen = buf.len;
751 34 : continuations++;
752 34 : continue;
753 : }
754 :
755 : /* Nope, so we have the whole line */
756 377246 : break;
757 : }
758 :
759 381356 : if (ferror(file))
760 : {
761 : /* I/O error! */
762 0 : int save_errno = errno;
763 :
764 0 : ereport(elevel,
765 : (errcode_for_file_access(),
766 : errmsg("could not read file \"%s\": %m", filename)));
767 0 : errno = save_errno;
768 0 : err_msg = psprintf("could not read file \"%s\": %m",
769 : filename);
770 0 : break;
771 : }
772 :
773 : /* Parse fields */
774 381356 : lineptr = buf.data;
775 1171710 : while (*lineptr && err_msg == NULL)
776 : {
777 : List *current_field;
778 :
779 408998 : 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 408998 : 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 : */
788 56170 : oldcxt = MemoryContextSwitchTo(tokenize_context);
789 56170 : current_line = lappend(current_line, current_field);
790 56170 : MemoryContextSwitchTo(oldcxt);
791 : }
792 : }
793 :
794 : /*
795 : * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
796 : * boring.
797 : */
798 381356 : if (current_line == NIL && err_msg == NULL)
799 369370 : goto next_line;
800 :
801 : /* If the line is valid, check if that's an include directive */
802 11986 : if (err_msg == NULL && list_length(current_line) == 2)
803 : {
804 : AuthToken *first,
805 : *second;
806 :
807 24 : first = linitial(linitial_node(List, current_line));
808 24 : second = linitial(lsecond_node(List, current_line));
809 :
810 24 : if (strcmp(first->string, "include") == 0)
811 : {
812 12 : tokenize_include_file(filename, second->string, tok_lines,
813 : elevel, depth + 1, false, &err_msg);
814 :
815 12 : if (err_msg)
816 0 : goto process_line;
817 :
818 : /*
819 : * tokenize_auth_file() has taken care of creating the
820 : * TokenizedAuthLines.
821 : */
822 12 : goto next_line;
823 : }
824 12 : else if (strcmp(first->string, "include_dir") == 0)
825 : {
826 : char **filenames;
827 4 : char *dir_name = second->string;
828 : int num_filenames;
829 : StringInfoData err_buf;
830 :
831 4 : filenames = GetConfFilesInDir(dir_name, filename, elevel,
832 : &num_filenames, &err_msg);
833 :
834 4 : if (!filenames)
835 : {
836 : /* the error is in err_msg, so create an entry */
837 0 : goto process_line;
838 : }
839 :
840 4 : initStringInfo(&err_buf);
841 12 : for (int i = 0; i < num_filenames; i++)
842 : {
843 8 : tokenize_include_file(filename, filenames[i], tok_lines,
844 : elevel, depth + 1, false, &err_msg);
845 : /* cumulate errors if any */
846 8 : if (err_msg)
847 : {
848 0 : if (err_buf.len > 0)
849 0 : appendStringInfoChar(&err_buf, '\n');
850 0 : appendStringInfoString(&err_buf, err_msg);
851 : }
852 : }
853 :
854 : /* clean up things */
855 12 : for (int i = 0; i < num_filenames; i++)
856 8 : pfree(filenames[i]);
857 4 : pfree(filenames);
858 :
859 : /*
860 : * If there were no errors, the line is fully processed,
861 : * bypass the general TokenizedAuthLine processing.
862 : */
863 4 : if (err_buf.len == 0)
864 4 : goto next_line;
865 :
866 : /* Otherwise, process the cumulated errors, if any. */
867 0 : err_msg = err_buf.data;
868 0 : goto process_line;
869 : }
870 8 : else if (strcmp(first->string, "include_if_exists") == 0)
871 : {
872 :
873 8 : tokenize_include_file(filename, second->string, tok_lines,
874 : elevel, depth + 1, true, &err_msg);
875 8 : if (err_msg)
876 0 : goto process_line;
877 :
878 : /*
879 : * tokenize_auth_file() has taken care of creating the
880 : * TokenizedAuthLines.
881 : */
882 8 : goto next_line;
883 : }
884 : }
885 :
886 11962 : process_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 : */
893 11962 : oldcxt = MemoryContextSwitchTo(tokenize_context);
894 11962 : tok_line = (TokenizedAuthLine *) palloc0(sizeof(TokenizedAuthLine));
895 11962 : tok_line->fields = current_line;
896 11962 : tok_line->file_name = pstrdup(filename);
897 11962 : tok_line->line_num = line_number;
898 11962 : tok_line->raw_line = pstrdup(buf.data);
899 11962 : tok_line->err_msg = err_msg ? pstrdup(err_msg) : NULL;
900 11962 : *tok_lines = lappend(*tok_lines, tok_line);
901 11962 : MemoryContextSwitchTo(oldcxt);
902 :
903 381356 : next_line:
904 381356 : line_number += continuations + 1;
905 381356 : callback_arg.linenum = line_number;
906 : }
907 :
908 4110 : MemoryContextSwitchTo(funccxt);
909 4110 : MemoryContextDelete(linecxt);
910 :
911 4110 : error_context_stack = tokenerrcontext.previous;
912 4110 : }
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 : */
921 : static bool
922 28 : is_member(Oid userid, const char *role)
923 : {
924 : Oid roleid;
925 :
926 28 : if (!OidIsValid(userid))
927 0 : return false; /* if user not exist, say "no" */
928 :
929 28 : roleid = get_role_oid(role, true);
930 :
931 28 : if (!OidIsValid(roleid))
932 0 : 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 28 : 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 : */
950 : static bool
951 26370 : check_role(const char *role, Oid roleid, List *tokens, bool case_insensitive)
952 : {
953 : ListCell *cell;
954 : AuthToken *tok;
955 :
956 26558 : foreach(cell, tokens)
957 : {
958 26378 : tok = lfirst(cell);
959 26378 : if (token_is_member_check(tok))
960 : {
961 16 : if (is_member(roleid, tok->string + 1))
962 26190 : return true;
963 : }
964 26362 : else if (token_is_keyword(tok, "all"))
965 26094 : return true;
966 268 : else if (token_has_regexp(tok))
967 : {
968 16 : if (regexec_auth_token(role, tok, 0, NULL) == REG_OKAY)
969 8 : return true;
970 : }
971 252 : else if (case_insensitive)
972 : {
973 0 : if (token_matches_insensitive(tok, role))
974 0 : return true;
975 : }
976 252 : else if (token_matches(tok, role))
977 74 : return true;
978 : }
979 180 : 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 : */
989 : static bool
990 27516 : check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
991 : {
992 : ListCell *cell;
993 : AuthToken *tok;
994 :
995 28710 : foreach(cell, tokens)
996 : {
997 27524 : tok = lfirst(cell);
998 27524 : if (am_walsender && !am_db_walsender)
999 : {
1000 : /*
1001 : * physical replication walsender connections can only match
1002 : * replication keyword
1003 : */
1004 1776 : if (token_is_keyword(tok, "replication"))
1005 26330 : return true;
1006 : }
1007 25748 : else if (token_is_keyword(tok, "all"))
1008 25032 : return true;
1009 716 : else if (token_is_keyword(tok, "sameuser"))
1010 : {
1011 0 : if (strcmp(dbname, role) == 0)
1012 0 : return true;
1013 : }
1014 716 : else if (token_is_keyword(tok, "samegroup") ||
1015 710 : token_is_keyword(tok, "samerole"))
1016 : {
1017 12 : if (is_member(roleid, dbname))
1018 8 : return true;
1019 : }
1020 704 : else if (token_is_keyword(tok, "replication"))
1021 0 : continue; /* never match this if not walsender */
1022 704 : else if (token_has_regexp(tok))
1023 : {
1024 8 : if (regexec_auth_token(dbname, tok, 0, NULL) == REG_OKAY)
1025 2 : return true;
1026 : }
1027 696 : else if (token_matches(tok, dbname))
1028 400 : return true;
1029 : }
1030 1186 : return false;
1031 : }
1032 :
1033 : static bool
1034 0 : ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
1035 : {
1036 0 : return (a->sin_addr.s_addr == b->sin_addr.s_addr);
1037 : }
1038 :
1039 : static bool
1040 0 : ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
1041 : {
1042 : int i;
1043 :
1044 0 : for (i = 0; i < 16; i++)
1045 0 : if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
1046 0 : return false;
1047 :
1048 0 : return true;
1049 : }
1050 :
1051 : /*
1052 : * Check whether host name matches pattern.
1053 : */
1054 : static bool
1055 0 : hostname_match(const char *pattern, const char *actual_hostname)
1056 : {
1057 0 : if (pattern[0] == '.') /* suffix match */
1058 : {
1059 0 : size_t plen = strlen(pattern);
1060 0 : size_t hlen = strlen(actual_hostname);
1061 :
1062 0 : if (hlen < plen)
1063 0 : return false;
1064 :
1065 0 : return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
1066 : }
1067 : else
1068 0 : return (pg_strcasecmp(pattern, actual_hostname) == 0);
1069 : }
1070 :
1071 : /*
1072 : * Check to see if a connecting IP matches a given host name.
1073 : */
1074 : static bool
1075 0 : check_hostname(Port *port, const char *hostname)
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 0 : if (port->remote_hostname_resolv < 0)
1084 0 : return false;
1085 :
1086 : /* Lookup remote host name if not already done */
1087 0 : if (!port->remote_hostname)
1088 : {
1089 : char remote_hostname[NI_MAXHOST];
1090 :
1091 0 : ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
1092 : remote_hostname, sizeof(remote_hostname),
1093 : NULL, 0,
1094 : NI_NAMEREQD);
1095 0 : if (ret != 0)
1096 : {
1097 : /* remember failure; don't complain in the postmaster log yet */
1098 0 : port->remote_hostname_resolv = -2;
1099 0 : port->remote_hostname_errcode = ret;
1100 0 : return false;
1101 : }
1102 :
1103 0 : port->remote_hostname = pstrdup(remote_hostname);
1104 : }
1105 :
1106 : /* Now see if remote host name matches this pg_hba line */
1107 0 : if (!hostname_match(hostname, port->remote_hostname))
1108 0 : return false;
1109 :
1110 : /* If we already verified the forward lookup, we're done */
1111 0 : if (port->remote_hostname_resolv == +1)
1112 0 : return true;
1113 :
1114 : /* Lookup IP from host name and check against original IP */
1115 0 : ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
1116 0 : if (ret != 0)
1117 : {
1118 : /* remember failure; don't complain in the postmaster log yet */
1119 0 : port->remote_hostname_resolv = -2;
1120 0 : port->remote_hostname_errcode = ret;
1121 0 : return false;
1122 : }
1123 :
1124 0 : found = false;
1125 0 : for (gai = gai_result; gai; gai = gai->ai_next)
1126 : {
1127 0 : if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
1128 : {
1129 0 : if (gai->ai_addr->sa_family == AF_INET)
1130 : {
1131 0 : if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
1132 0 : (struct sockaddr_in *) &port->raddr.addr))
1133 : {
1134 0 : found = true;
1135 0 : break;
1136 : }
1137 : }
1138 0 : else if (gai->ai_addr->sa_family == AF_INET6)
1139 : {
1140 0 : if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
1141 0 : (struct sockaddr_in6 *) &port->raddr.addr))
1142 : {
1143 0 : found = true;
1144 0 : break;
1145 : }
1146 : }
1147 : }
1148 : }
1149 :
1150 0 : if (gai_result)
1151 0 : freeaddrinfo(gai_result);
1152 :
1153 0 : if (!found)
1154 0 : 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 0 : port->remote_hostname_resolv = found ? +1 : -1;
1158 :
1159 0 : return found;
1160 : }
1161 :
1162 : /*
1163 : * Check to see if a connecting IP matches the given address and netmask.
1164 : */
1165 : static bool
1166 1236 : check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
1167 : {
1168 2186 : if (raddr->addr.ss_family == addr->sa_family &&
1169 950 : pg_range_sockaddr(&raddr->addr,
1170 : (struct sockaddr_storage *) addr,
1171 : (struct sockaddr_storage *) mask))
1172 950 : return true;
1173 286 : return false;
1174 : }
1175 :
1176 : /*
1177 : * pg_foreach_ifaddr callback: does client addr match this machine interface?
1178 : */
1179 : static void
1180 0 : check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
1181 : void *cb_data)
1182 : {
1183 0 : check_network_data *cn = (check_network_data *) cb_data;
1184 : struct sockaddr_storage mask;
1185 :
1186 : /* Already found a match? */
1187 0 : if (cn->result)
1188 0 : return;
1189 :
1190 0 : if (cn->method == ipCmpSameHost)
1191 : {
1192 : /* Make an all-ones netmask of appropriate length for family */
1193 0 : pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
1194 0 : cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
1195 : }
1196 : else
1197 : {
1198 : /* Use the netmask of the interface itself */
1199 0 : 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 : */
1206 : static bool
1207 0 : check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
1208 : {
1209 : check_network_data cn;
1210 :
1211 0 : cn.method = method;
1212 0 : cn.raddr = raddr;
1213 0 : cn.result = false;
1214 :
1215 0 : errno = 0;
1216 0 : if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
1217 : {
1218 0 : ereport(LOG,
1219 : (errmsg("error enumerating network interfaces: %m")));
1220 0 : return false;
1221 : }
1222 :
1223 0 : 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) \
1242 : do { \
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) \
1256 : do { \
1257 : if (hbaline->auth_method != methodval) \
1258 : INVALID_AUTH_OPTION(optname, validmethods); \
1259 : } while (0)
1260 :
1261 : #define MANDATORY_AUTH_ARG(argvar, argname, authname) \
1262 : do { \
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) \
1286 : do { \
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) \
1299 : do { \
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 : */
1324 : HbaLine *
1325 11762 : parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
1326 : {
1327 11762 : int line_num = tok_line->line_num;
1328 11762 : char *file_name = tok_line->file_name;
1329 11762 : 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;
1339 : AuthToken *token;
1340 : HbaLine *parsedline;
1341 :
1342 11762 : parsedline = palloc0(sizeof(HbaLine));
1343 11762 : parsedline->sourcefile = pstrdup(file_name);
1344 11762 : parsedline->linenumber = line_num;
1345 11762 : parsedline->rawline = pstrdup(tok_line->raw_line);
1346 :
1347 : /* Check the record type. */
1348 : Assert(tok_line->fields != NIL);
1349 11762 : field = list_head(tok_line->fields);
1350 11762 : tokens = lfirst(field);
1351 11762 : if (tokens->length > 1)
1352 : {
1353 0 : 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 0 : *err_msg = "multiple values specified for connection type";
1360 0 : return NULL;
1361 : }
1362 11762 : token = linitial(tokens);
1363 11762 : if (strcmp(token->string, "local") == 0)
1364 : {
1365 3870 : parsedline->conntype = ctLocal;
1366 : }
1367 7892 : else if (strcmp(token->string, "host") == 0 ||
1368 468 : strcmp(token->string, "hostssl") == 0 ||
1369 24 : strcmp(token->string, "hostnossl") == 0 ||
1370 12 : strcmp(token->string, "hostgssenc") == 0 ||
1371 12 : strcmp(token->string, "hostnogssenc") == 0)
1372 : {
1373 :
1374 7892 : if (token->string[4] == 's') /* "hostssl" */
1375 : {
1376 444 : parsedline->conntype = ctHostSSL;
1377 : /* Log a warning if SSL support is not active */
1378 : #ifdef USE_SSL
1379 444 : if (!EnableSSL)
1380 : {
1381 4 : 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 4 : *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 7448 : else if (token->string[4] == 'g') /* "hostgssenc" */
1399 : {
1400 0 : parsedline->conntype = ctHostGSS;
1401 : #ifndef ENABLE_GSS
1402 0 : 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 0 : *err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
1408 : #endif
1409 : }
1410 7448 : else if (token->string[4] == 'n' && token->string[6] == 's')
1411 12 : parsedline->conntype = ctHostNoSSL;
1412 7436 : else if (token->string[4] == 'n' && token->string[6] == 'g')
1413 12 : parsedline->conntype = ctHostNoGSS;
1414 : else
1415 : {
1416 : /* "host" */
1417 7424 : parsedline->conntype = ctHost;
1418 : }
1419 : } /* record type */
1420 : else
1421 : {
1422 0 : 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 0 : *err_msg = psprintf("invalid connection type \"%s\"", token->string);
1429 0 : return NULL;
1430 : }
1431 :
1432 : /* Get the databases. */
1433 11762 : field = lnext(tok_line->fields, field);
1434 11762 : if (!field)
1435 : {
1436 0 : 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 0 : *err_msg = "end-of-line before database specification";
1442 0 : return NULL;
1443 : }
1444 11762 : parsedline->databases = NIL;
1445 11762 : tokens = lfirst(field);
1446 23534 : foreach(tokencell, tokens)
1447 : {
1448 11772 : AuthToken *tok = copy_auth_token(lfirst(tokencell));
1449 :
1450 : /* Compile a regexp for the database token, if necessary */
1451 11772 : if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1452 0 : return NULL;
1453 :
1454 11772 : parsedline->databases = lappend(parsedline->databases, tok);
1455 : }
1456 :
1457 : /* Get the roles. */
1458 11762 : field = lnext(tok_line->fields, field);
1459 11762 : if (!field)
1460 : {
1461 0 : 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 0 : *err_msg = "end-of-line before role specification";
1467 0 : return NULL;
1468 : }
1469 11762 : parsedline->roles = NIL;
1470 11762 : tokens = lfirst(field);
1471 23532 : foreach(tokencell, tokens)
1472 : {
1473 11770 : AuthToken *tok = copy_auth_token(lfirst(tokencell));
1474 :
1475 : /* Compile a regexp from the role token, if necessary */
1476 11770 : if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1477 0 : return NULL;
1478 :
1479 11770 : parsedline->roles = lappend(parsedline->roles, tok);
1480 : }
1481 :
1482 11762 : if (parsedline->conntype != ctLocal)
1483 : {
1484 : /* Read the IP address field. (with or without CIDR netmask) */
1485 7892 : field = lnext(tok_line->fields, field);
1486 7892 : if (!field)
1487 : {
1488 0 : 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 0 : *err_msg = "end-of-line before IP address specification";
1494 0 : return NULL;
1495 : }
1496 7892 : tokens = lfirst(field);
1497 7892 : if (tokens->length > 1)
1498 : {
1499 0 : 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 0 : *err_msg = "multiple values specified for host address";
1506 0 : return NULL;
1507 : }
1508 7892 : token = linitial(tokens);
1509 :
1510 7892 : if (token_is_keyword(token, "all"))
1511 : {
1512 0 : parsedline->ip_cmp_method = ipCmpAll;
1513 : }
1514 7892 : else if (token_is_keyword(token, "samehost"))
1515 : {
1516 : /* Any IP on this host is allowed to connect */
1517 0 : parsedline->ip_cmp_method = ipCmpSameHost;
1518 : }
1519 7892 : else if (token_is_keyword(token, "samenet"))
1520 : {
1521 : /* Any IP on the host's subnets is allowed to connect */
1522 0 : parsedline->ip_cmp_method = ipCmpSameNet;
1523 : }
1524 : else
1525 : {
1526 : /* IP and netmask are specified */
1527 7892 : parsedline->ip_cmp_method = ipCmpMask;
1528 :
1529 : /* need a modifiable copy of token */
1530 7892 : str = pstrdup(token->string);
1531 :
1532 : /* Check if it has a CIDR suffix and if so isolate it */
1533 7892 : cidr_slash = strchr(str, '/');
1534 7892 : if (cidr_slash)
1535 7892 : *cidr_slash = '\0';
1536 :
1537 : /* Get the IP address either way */
1538 7892 : hints.ai_flags = AI_NUMERICHOST;
1539 7892 : hints.ai_family = AF_UNSPEC;
1540 7892 : hints.ai_socktype = 0;
1541 7892 : hints.ai_protocol = 0;
1542 7892 : hints.ai_addrlen = 0;
1543 7892 : hints.ai_canonname = NULL;
1544 7892 : hints.ai_addr = NULL;
1545 7892 : hints.ai_next = NULL;
1546 :
1547 7892 : ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
1548 7892 : if (ret == 0 && gai_result)
1549 : {
1550 7892 : memcpy(&parsedline->addr, gai_result->ai_addr,
1551 7892 : gai_result->ai_addrlen);
1552 7892 : parsedline->addrlen = gai_result->ai_addrlen;
1553 : }
1554 0 : else if (ret == EAI_NONAME)
1555 0 : parsedline->hostname = str;
1556 : else
1557 : {
1558 0 : 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 0 : *err_msg = psprintf("invalid IP address \"%s\": %s",
1565 : str, gai_strerror(ret));
1566 0 : if (gai_result)
1567 0 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
1568 0 : return NULL;
1569 : }
1570 :
1571 7892 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
1572 :
1573 : /* Get the netmask */
1574 7892 : if (cidr_slash)
1575 : {
1576 7892 : if (parsedline->hostname)
1577 : {
1578 0 : 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 0 : *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
1585 : token->string);
1586 0 : return NULL;
1587 : }
1588 :
1589 7892 : if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
1590 7892 : parsedline->addr.ss_family) < 0)
1591 : {
1592 0 : 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 0 : *err_msg = psprintf("invalid CIDR mask in address \"%s\"",
1599 : token->string);
1600 0 : return NULL;
1601 : }
1602 7892 : parsedline->masklen = parsedline->addrlen;
1603 7892 : pfree(str);
1604 : }
1605 0 : else if (!parsedline->hostname)
1606 : {
1607 : /* Read the mask field. */
1608 0 : pfree(str);
1609 0 : field = lnext(tok_line->fields, field);
1610 0 : if (!field)
1611 : {
1612 0 : 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 0 : *err_msg = "end-of-line before netmask specification";
1619 0 : return NULL;
1620 : }
1621 0 : tokens = lfirst(field);
1622 0 : if (tokens->length > 1)
1623 : {
1624 0 : 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 0 : *err_msg = "multiple values specified for netmask";
1630 0 : return NULL;
1631 : }
1632 0 : token = linitial(tokens);
1633 :
1634 0 : ret = pg_getaddrinfo_all(token->string, NULL,
1635 : &hints, &gai_result);
1636 0 : if (ret || !gai_result)
1637 : {
1638 0 : 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 0 : *err_msg = psprintf("invalid IP mask \"%s\": %s",
1645 : token->string, gai_strerror(ret));
1646 0 : if (gai_result)
1647 0 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
1648 0 : return NULL;
1649 : }
1650 :
1651 0 : memcpy(&parsedline->mask, gai_result->ai_addr,
1652 0 : gai_result->ai_addrlen);
1653 0 : parsedline->masklen = gai_result->ai_addrlen;
1654 0 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
1655 :
1656 0 : if (parsedline->addr.ss_family != parsedline->mask.ss_family)
1657 : {
1658 0 : 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 0 : *err_msg = "IP address and mask do not match";
1664 0 : return NULL;
1665 : }
1666 : }
1667 : }
1668 : } /* != ctLocal */
1669 :
1670 : /* Get the authentication method */
1671 11762 : field = lnext(tok_line->fields, field);
1672 11762 : if (!field)
1673 : {
1674 0 : 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 0 : *err_msg = "end-of-line before authentication method";
1680 0 : return NULL;
1681 : }
1682 11762 : tokens = lfirst(field);
1683 11762 : if (tokens->length > 1)
1684 : {
1685 0 : 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 0 : *err_msg = "multiple values specified for authentication type";
1692 0 : return NULL;
1693 : }
1694 11762 : token = linitial(tokens);
1695 :
1696 11762 : unsupauth = NULL;
1697 11762 : if (strcmp(token->string, "trust") == 0)
1698 11358 : parsedline->auth_method = uaTrust;
1699 404 : else if (strcmp(token->string, "ident") == 0)
1700 0 : parsedline->auth_method = uaIdent;
1701 404 : else if (strcmp(token->string, "peer") == 0)
1702 38 : parsedline->auth_method = uaPeer;
1703 366 : else if (strcmp(token->string, "password") == 0)
1704 20 : parsedline->auth_method = uaPassword;
1705 346 : else if (strcmp(token->string, "gss") == 0)
1706 : #ifdef ENABLE_GSS
1707 : parsedline->auth_method = uaGSS;
1708 : #else
1709 0 : unsupauth = "gss";
1710 : #endif
1711 346 : else if (strcmp(token->string, "sspi") == 0)
1712 : #ifdef ENABLE_SSPI
1713 : parsedline->auth_method = uaSSPI;
1714 : #else
1715 0 : unsupauth = "sspi";
1716 : #endif
1717 346 : else if (strcmp(token->string, "reject") == 0)
1718 18 : parsedline->auth_method = uaReject;
1719 328 : else if (strcmp(token->string, "md5") == 0)
1720 52 : parsedline->auth_method = uaMD5;
1721 276 : else if (strcmp(token->string, "scram-sha-256") == 0)
1722 48 : parsedline->auth_method = uaSCRAM;
1723 228 : else if (strcmp(token->string, "pam") == 0)
1724 : #ifdef USE_PAM
1725 0 : parsedline->auth_method = uaPAM;
1726 : #else
1727 : unsupauth = "pam";
1728 : #endif
1729 228 : else if (strcmp(token->string, "bsd") == 0)
1730 : #ifdef USE_BSD_AUTH
1731 : parsedline->auth_method = uaBSD;
1732 : #else
1733 0 : unsupauth = "bsd";
1734 : #endif
1735 228 : else if (strcmp(token->string, "ldap") == 0)
1736 : #ifdef USE_LDAP
1737 36 : parsedline->auth_method = uaLDAP;
1738 : #else
1739 : unsupauth = "ldap";
1740 : #endif
1741 192 : else if (strcmp(token->string, "cert") == 0)
1742 : #ifdef USE_SSL
1743 192 : parsedline->auth_method = uaCert;
1744 : #else
1745 : unsupauth = "cert";
1746 : #endif
1747 0 : else if (strcmp(token->string, "radius") == 0)
1748 0 : parsedline->auth_method = uaRADIUS;
1749 0 : else if (strcmp(token->string, "oauth") == 0)
1750 0 : parsedline->auth_method = uaOAuth;
1751 : else
1752 : {
1753 0 : 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 0 : *err_msg = psprintf("invalid authentication method \"%s\"",
1760 : token->string);
1761 0 : return NULL;
1762 : }
1763 :
1764 11762 : if (unsupauth)
1765 : {
1766 0 : 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 0 : *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
1773 : token->string);
1774 0 : return NULL;
1775 : }
1776 :
1777 : /*
1778 : * XXX: When using ident on local connections, change it to peer, for
1779 : * backwards compatibility.
1780 : */
1781 11762 : if (parsedline->conntype == ctLocal &&
1782 3870 : parsedline->auth_method == uaIdent)
1783 0 : parsedline->auth_method = uaPeer;
1784 :
1785 : /* Invalid authentication combinations */
1786 11762 : if (parsedline->conntype == ctLocal &&
1787 3870 : parsedline->auth_method == uaGSS)
1788 : {
1789 0 : 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 0 : *err_msg = "gssapi authentication is not supported on local sockets";
1795 0 : return NULL;
1796 : }
1797 :
1798 11762 : if (parsedline->conntype != ctLocal &&
1799 7892 : parsedline->auth_method == uaPeer)
1800 : {
1801 0 : 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 0 : *err_msg = "peer authentication is only supported on local sockets";
1807 0 : 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 11762 : if (parsedline->conntype != ctHostSSL &&
1817 11318 : parsedline->auth_method == uaCert)
1818 : {
1819 0 : 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 0 : *err_msg = "cert authentication is only supported on hostssl connections";
1825 0 : 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 11762 : if (parsedline->auth_method == uaGSS ||
1838 11762 : parsedline->auth_method == uaSSPI)
1839 0 : 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 11762 : if (parsedline->auth_method == uaSSPI)
1847 : {
1848 0 : parsedline->compat_realm = true;
1849 0 : parsedline->upn_username = false;
1850 : }
1851 :
1852 : /* Parse remaining arguments */
1853 12352 : while ((field = lnext(tok_line->fields, field)) != NULL)
1854 : {
1855 590 : tokens = lfirst(field);
1856 1180 : foreach(tokencell, tokens)
1857 : {
1858 : char *val;
1859 :
1860 590 : token = lfirst(tokencell);
1861 :
1862 590 : str = pstrdup(token->string);
1863 590 : val = strchr(str, '=');
1864 590 : if (val == NULL)
1865 : {
1866 : /*
1867 : * Got something that's not a name=value pair.
1868 : */
1869 0 : 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 0 : *err_msg = psprintf("authentication option not in name=value format: %s",
1875 : token->string);
1876 0 : return NULL;
1877 : }
1878 :
1879 590 : *val++ = '\0'; /* str now holds "name", val holds "value" */
1880 590 : if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
1881 : /* parse_hba_auth_opt already logged the error message */
1882 0 : return NULL;
1883 590 : pfree(str);
1884 : }
1885 : }
1886 :
1887 : /*
1888 : * Check if the selected authentication method has any mandatory arguments
1889 : * that are not set.
1890 : */
1891 11762 : 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 36 : if (parsedline->ldapprefix || parsedline->ldapsuffix)
1906 : {
1907 6 : if (parsedline->ldapbasedn ||
1908 6 : parsedline->ldapbinddn ||
1909 6 : parsedline->ldapbindpasswd ||
1910 6 : parsedline->ldapsearchattribute ||
1911 6 : parsedline->ldapsearchfilter)
1912 : {
1913 0 : 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 0 : *err_msg = "cannot mix options for simple bind and search+bind modes";
1919 0 : return NULL;
1920 : }
1921 : }
1922 30 : else if (!parsedline->ldapbasedn)
1923 : {
1924 0 : 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 0 : *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
1930 0 : 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 36 : if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter)
1939 : {
1940 0 : 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 0 : *err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter";
1946 0 : return NULL;
1947 : }
1948 : }
1949 :
1950 11762 : if (parsedline->auth_method == uaRADIUS)
1951 : {
1952 0 : MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
1953 0 : MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
1954 :
1955 0 : if (parsedline->radiusservers == NIL)
1956 : {
1957 0 : 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 0 : *err_msg = "list of RADIUS servers cannot be empty";
1963 0 : return NULL;
1964 : }
1965 :
1966 0 : if (parsedline->radiussecrets == NIL)
1967 : {
1968 0 : 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 0 : *err_msg = "list of RADIUS secrets cannot be empty";
1974 0 : 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 0 : if (!(list_length(parsedline->radiussecrets) == 1 ||
1983 0 : list_length(parsedline->radiussecrets) == list_length(parsedline->radiusservers)))
1984 : {
1985 0 : 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 0 : *err_msg = psprintf("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
1993 0 : list_length(parsedline->radiussecrets),
1994 0 : list_length(parsedline->radiusservers));
1995 0 : return NULL;
1996 : }
1997 0 : if (!(list_length(parsedline->radiusports) == 0 ||
1998 0 : list_length(parsedline->radiusports) == 1 ||
1999 0 : list_length(parsedline->radiusports) == list_length(parsedline->radiusservers)))
2000 : {
2001 0 : 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 0 : *err_msg = psprintf("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2009 0 : list_length(parsedline->radiusports),
2010 0 : list_length(parsedline->radiusservers));
2011 0 : return NULL;
2012 : }
2013 0 : if (!(list_length(parsedline->radiusidentifiers) == 0 ||
2014 0 : list_length(parsedline->radiusidentifiers) == 1 ||
2015 0 : list_length(parsedline->radiusidentifiers) == list_length(parsedline->radiusservers)))
2016 : {
2017 0 : 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 0 : *err_msg = psprintf("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
2025 0 : list_length(parsedline->radiusidentifiers),
2026 0 : list_length(parsedline->radiusservers));
2027 0 : return NULL;
2028 : }
2029 : }
2030 :
2031 : /*
2032 : * Enforce any parameters implied by other settings.
2033 : */
2034 11762 : 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 192 : parsedline->clientcert = clientCertFull;
2041 : }
2042 :
2043 : /*
2044 : * Enforce proper configuration of OAuth authentication.
2045 : */
2046 11762 : if (parsedline->auth_method == uaOAuth)
2047 : {
2048 0 : MANDATORY_AUTH_ARG(parsedline->oauth_scope, "scope", "oauth");
2049 0 : MANDATORY_AUTH_ARG(parsedline->oauth_issuer, "issuer", "oauth");
2050 :
2051 : /* Ensure a validator library is set and permitted by the config. */
2052 0 : if (!check_oauth_validator(parsedline, elevel, err_msg))
2053 0 : 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 0 : if (parsedline->oauth_skip_usermap && parsedline->usermap != NULL)
2060 : {
2061 0 : 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 0 : *err_msg = "map cannot be used in combination with delegate_ident_mapping";
2069 0 : return NULL;
2070 : }
2071 : }
2072 :
2073 11762 : 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 : */
2083 : static bool
2084 590 : parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
2085 : int elevel, char **err_msg)
2086 : {
2087 590 : int line_num = hbaline->linenumber;
2088 590 : char *file_name = hbaline->sourcefile;
2089 :
2090 : #ifdef USE_LDAP
2091 590 : hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
2092 : #endif
2093 :
2094 590 : if (strcmp(name, "map") == 0)
2095 : {
2096 178 : if (hbaline->auth_method != uaIdent &&
2097 178 : hbaline->auth_method != uaPeer &&
2098 144 : hbaline->auth_method != uaGSS &&
2099 144 : hbaline->auth_method != uaSSPI &&
2100 144 : hbaline->auth_method != uaCert &&
2101 0 : hbaline->auth_method != uaOAuth)
2102 0 : INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, cert, and oauth"));
2103 178 : hbaline->usermap = pstrdup(val);
2104 : }
2105 412 : else if (strcmp(name, "clientcert") == 0)
2106 : {
2107 144 : if (hbaline->conntype != ctHostSSL)
2108 : {
2109 0 : 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 0 : *err_msg = "clientcert can only be configured for \"hostssl\" rows";
2115 0 : return false;
2116 : }
2117 :
2118 144 : if (strcmp(val, "verify-full") == 0)
2119 : {
2120 96 : hbaline->clientcert = clientCertFull;
2121 : }
2122 48 : else if (strcmp(val, "verify-ca") == 0)
2123 : {
2124 48 : if (hbaline->auth_method == uaCert)
2125 : {
2126 0 : 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 0 : *err_msg = "clientcert can only be set to \"verify-full\" when using \"cert\" authentication";
2132 0 : return false;
2133 : }
2134 :
2135 48 : hbaline->clientcert = clientCertCA;
2136 : }
2137 : else
2138 : {
2139 0 : 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 0 : return false;
2145 : }
2146 : }
2147 268 : else if (strcmp(name, "clientname") == 0)
2148 : {
2149 144 : if (hbaline->conntype != ctHostSSL)
2150 : {
2151 0 : 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 0 : *err_msg = "clientname can only be configured for \"hostssl\" rows";
2157 0 : return false;
2158 : }
2159 :
2160 144 : if (strcmp(val, "CN") == 0)
2161 : {
2162 48 : hbaline->clientcertname = clientCertCN;
2163 : }
2164 96 : else if (strcmp(val, "DN") == 0)
2165 : {
2166 96 : hbaline->clientcertname = clientCertDN;
2167 : }
2168 : else
2169 : {
2170 0 : 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 0 : return false;
2176 : }
2177 : }
2178 124 : else if (strcmp(name, "pamservice") == 0)
2179 : {
2180 0 : REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
2181 0 : hbaline->pamservice = pstrdup(val);
2182 : }
2183 124 : else if (strcmp(name, "pam_use_hostname") == 0)
2184 : {
2185 0 : REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
2186 0 : if (strcmp(val, "1") == 0)
2187 0 : hbaline->pam_use_hostname = true;
2188 : else
2189 0 : hbaline->pam_use_hostname = false;
2190 : }
2191 124 : else if (strcmp(name, "ldapurl") == 0)
2192 : {
2193 : #ifdef LDAP_API_FEATURE_X_OPENLDAP
2194 : LDAPURLDesc *urldata;
2195 : int rc;
2196 : #endif
2197 :
2198 12 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
2199 : #ifdef LDAP_API_FEATURE_X_OPENLDAP
2200 12 : rc = ldap_url_parse(val, &urldata);
2201 12 : if (rc != LDAP_SUCCESS)
2202 : {
2203 0 : ereport(elevel,
2204 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
2205 : errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
2206 0 : *err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
2207 : val, ldap_err2string(rc));
2208 0 : return false;
2209 : }
2210 :
2211 12 : if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
2212 4 : strcmp(urldata->lud_scheme, "ldaps") != 0)
2213 : {
2214 0 : ereport(elevel,
2215 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
2216 : errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
2217 0 : *err_msg = psprintf("unsupported LDAP URL scheme: %s",
2218 0 : urldata->lud_scheme);
2219 0 : ldap_free_urldesc(urldata);
2220 0 : return false;
2221 : }
2222 :
2223 12 : if (urldata->lud_scheme)
2224 12 : hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
2225 12 : if (urldata->lud_host)
2226 12 : hbaline->ldapserver = pstrdup(urldata->lud_host);
2227 12 : hbaline->ldapport = urldata->lud_port;
2228 12 : if (urldata->lud_dn)
2229 10 : hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
2230 :
2231 12 : if (urldata->lud_attrs)
2232 2 : hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]); /* only use first one */
2233 12 : hbaline->ldapscope = urldata->lud_scope;
2234 12 : if (urldata->lud_filter)
2235 6 : hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter);
2236 12 : 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 112 : else if (strcmp(name, "ldaptls") == 0)
2245 : {
2246 4 : REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
2247 4 : if (strcmp(val, "1") == 0)
2248 4 : hbaline->ldaptls = true;
2249 : else
2250 0 : hbaline->ldaptls = false;
2251 : }
2252 108 : else if (strcmp(name, "ldapscheme") == 0)
2253 : {
2254 2 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
2255 2 : if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
2256 0 : 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 2 : hbaline->ldapscheme = pstrdup(val);
2262 : }
2263 106 : else if (strcmp(name, "ldapserver") == 0)
2264 : {
2265 24 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
2266 24 : hbaline->ldapserver = pstrdup(val);
2267 : }
2268 82 : else if (strcmp(name, "ldapport") == 0)
2269 : {
2270 24 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
2271 24 : hbaline->ldapport = atoi(val);
2272 24 : if (hbaline->ldapport == 0)
2273 : {
2274 0 : 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 0 : *err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
2280 0 : return false;
2281 : }
2282 : }
2283 58 : else if (strcmp(name, "ldapbinddn") == 0)
2284 : {
2285 10 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
2286 10 : hbaline->ldapbinddn = pstrdup(val);
2287 : }
2288 48 : else if (strcmp(name, "ldapbindpasswd") == 0)
2289 : {
2290 8 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
2291 8 : hbaline->ldapbindpasswd = pstrdup(val);
2292 : }
2293 40 : else if (strcmp(name, "ldapsearchattribute") == 0)
2294 : {
2295 0 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
2296 0 : hbaline->ldapsearchattribute = pstrdup(val);
2297 : }
2298 40 : else if (strcmp(name, "ldapsearchfilter") == 0)
2299 : {
2300 8 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter", "ldap");
2301 8 : hbaline->ldapsearchfilter = pstrdup(val);
2302 : }
2303 32 : else if (strcmp(name, "ldapbasedn") == 0)
2304 : {
2305 20 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
2306 20 : hbaline->ldapbasedn = pstrdup(val);
2307 : }
2308 12 : else if (strcmp(name, "ldapprefix") == 0)
2309 : {
2310 6 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
2311 6 : hbaline->ldapprefix = pstrdup(val);
2312 : }
2313 6 : else if (strcmp(name, "ldapsuffix") == 0)
2314 : {
2315 6 : REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
2316 6 : hbaline->ldapsuffix = pstrdup(val);
2317 : }
2318 0 : else if (strcmp(name, "krb_realm") == 0)
2319 : {
2320 0 : if (hbaline->auth_method != uaGSS &&
2321 0 : hbaline->auth_method != uaSSPI)
2322 0 : INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
2323 0 : hbaline->krb_realm = pstrdup(val);
2324 : }
2325 0 : else if (strcmp(name, "include_realm") == 0)
2326 : {
2327 0 : if (hbaline->auth_method != uaGSS &&
2328 0 : hbaline->auth_method != uaSSPI)
2329 0 : INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
2330 0 : if (strcmp(val, "1") == 0)
2331 0 : hbaline->include_realm = true;
2332 : else
2333 0 : hbaline->include_realm = false;
2334 : }
2335 0 : else if (strcmp(name, "compat_realm") == 0)
2336 : {
2337 0 : if (hbaline->auth_method != uaSSPI)
2338 0 : INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
2339 0 : if (strcmp(val, "1") == 0)
2340 0 : hbaline->compat_realm = true;
2341 : else
2342 0 : hbaline->compat_realm = false;
2343 : }
2344 0 : else if (strcmp(name, "upn_username") == 0)
2345 : {
2346 0 : if (hbaline->auth_method != uaSSPI)
2347 0 : INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
2348 0 : if (strcmp(val, "1") == 0)
2349 0 : hbaline->upn_username = true;
2350 : else
2351 0 : hbaline->upn_username = false;
2352 : }
2353 0 : 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 0 : char *dupval = pstrdup(val);
2361 :
2362 0 : REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
2363 :
2364 0 : if (!SplitGUCList(dupval, ',', &parsed_servers))
2365 : {
2366 : /* syntax error in list */
2367 0 : 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 0 : return false;
2374 : }
2375 :
2376 : /* For each entry in the list, translate it */
2377 0 : foreach(l, parsed_servers)
2378 : {
2379 0 : MemSet(&hints, 0, sizeof(hints));
2380 0 : hints.ai_socktype = SOCK_DGRAM;
2381 0 : hints.ai_family = AF_UNSPEC;
2382 :
2383 0 : ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
2384 0 : if (ret || !gai_result)
2385 : {
2386 0 : 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 0 : if (gai_result)
2393 0 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
2394 :
2395 0 : list_free(parsed_servers);
2396 0 : return false;
2397 : }
2398 0 : pg_freeaddrinfo_all(hints.ai_family, gai_result);
2399 : }
2400 :
2401 : /* All entries are OK, so store them */
2402 0 : hbaline->radiusservers = parsed_servers;
2403 0 : hbaline->radiusservers_s = pstrdup(val);
2404 : }
2405 0 : else if (strcmp(name, "radiusports") == 0)
2406 : {
2407 : List *parsed_ports;
2408 : ListCell *l;
2409 0 : char *dupval = pstrdup(val);
2410 :
2411 0 : REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
2412 :
2413 0 : if (!SplitGUCList(dupval, ',', &parsed_ports))
2414 : {
2415 0 : 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 0 : *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
2422 0 : return false;
2423 : }
2424 :
2425 0 : foreach(l, parsed_ports)
2426 : {
2427 0 : if (atoi(lfirst(l)) == 0)
2428 : {
2429 0 : 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 0 : return false;
2436 : }
2437 : }
2438 0 : hbaline->radiusports = parsed_ports;
2439 0 : hbaline->radiusports_s = pstrdup(val);
2440 : }
2441 0 : else if (strcmp(name, "radiussecrets") == 0)
2442 : {
2443 : List *parsed_secrets;
2444 0 : char *dupval = pstrdup(val);
2445 :
2446 0 : REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
2447 :
2448 0 : if (!SplitGUCList(dupval, ',', &parsed_secrets))
2449 : {
2450 : /* syntax error in list */
2451 0 : 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 0 : return false;
2458 : }
2459 :
2460 0 : hbaline->radiussecrets = parsed_secrets;
2461 0 : hbaline->radiussecrets_s = pstrdup(val);
2462 : }
2463 0 : else if (strcmp(name, "radiusidentifiers") == 0)
2464 : {
2465 : List *parsed_identifiers;
2466 0 : char *dupval = pstrdup(val);
2467 :
2468 0 : REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
2469 :
2470 0 : if (!SplitGUCList(dupval, ',', &parsed_identifiers))
2471 : {
2472 : /* syntax error in list */
2473 0 : 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 0 : return false;
2480 : }
2481 :
2482 0 : hbaline->radiusidentifiers = parsed_identifiers;
2483 0 : hbaline->radiusidentifiers_s = pstrdup(val);
2484 : }
2485 0 : else if (strcmp(name, "issuer") == 0)
2486 : {
2487 0 : REQUIRE_AUTH_OPTION(uaOAuth, "issuer", "oauth");
2488 0 : hbaline->oauth_issuer = pstrdup(val);
2489 : }
2490 0 : else if (strcmp(name, "scope") == 0)
2491 : {
2492 0 : REQUIRE_AUTH_OPTION(uaOAuth, "scope", "oauth");
2493 0 : hbaline->oauth_scope = pstrdup(val);
2494 : }
2495 0 : else if (strcmp(name, "validator") == 0)
2496 : {
2497 0 : REQUIRE_AUTH_OPTION(uaOAuth, "validator", "oauth");
2498 0 : hbaline->oauth_validator = pstrdup(val);
2499 : }
2500 0 : else if (strcmp(name, "delegate_ident_mapping") == 0)
2501 : {
2502 0 : REQUIRE_AUTH_OPTION(uaOAuth, "delegate_ident_mapping", "oauth");
2503 0 : if (strcmp(val, "1") == 0)
2504 0 : hbaline->oauth_skip_usermap = true;
2505 : else
2506 0 : hbaline->oauth_skip_usermap = false;
2507 : }
2508 : else
2509 : {
2510 0 : 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 0 : *err_msg = psprintf("unrecognized authentication option name: \"%s\"",
2517 : name);
2518 0 : return false;
2519 : }
2520 590 : return true;
2521 : }
2522 :
2523 : /*
2524 : * Scan the pre-parsed hba file, looking for a match to the port's connection
2525 : * request.
2526 : */
2527 : static void
2528 26182 : check_hba(Port *port)
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 26182 : roleid = get_role_oid(port->user_name, true);
2536 :
2537 29974 : foreach(line, parsed_hba_lines)
2538 : {
2539 29948 : hba = (HbaLine *) lfirst(line);
2540 :
2541 : /* Check connection type */
2542 29948 : if (hba->conntype == ctLocal)
2543 : {
2544 26900 : if (port->raddr.addr.ss_family != AF_UNIX)
2545 334 : continue;
2546 : }
2547 : else
2548 : {
2549 3048 : if (port->raddr.addr.ss_family == AF_UNIX)
2550 1776 : continue;
2551 :
2552 : /* Check SSL state */
2553 1272 : if (port->ssl_in_use)
2554 : {
2555 : /* Connection is SSL, match both "host" and "hostssl" */
2556 636 : if (hba->conntype == ctHostNoSSL)
2557 14 : continue;
2558 : }
2559 : else
2560 : {
2561 : /* Connection is not SSL, match both "host" and "hostnossl" */
2562 636 : if (hba->conntype == ctHostSSL)
2563 22 : 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 1236 : if (hba->conntype == ctHostGSS)
2576 0 : continue;
2577 : #endif
2578 :
2579 : /* Check IP address */
2580 1236 : switch (hba->ip_cmp_method)
2581 : {
2582 1236 : case ipCmpMask:
2583 1236 : if (hba->hostname)
2584 : {
2585 0 : if (!check_hostname(port,
2586 0 : hba->hostname))
2587 0 : continue;
2588 : }
2589 : else
2590 : {
2591 1236 : if (!check_ip(&port->raddr,
2592 1236 : (struct sockaddr *) &hba->addr,
2593 1236 : (struct sockaddr *) &hba->mask))
2594 286 : continue;
2595 : }
2596 950 : break;
2597 0 : case ipCmpAll:
2598 0 : break;
2599 0 : case ipCmpSameHost:
2600 : case ipCmpSameNet:
2601 0 : if (!check_same_host_or_net(&port->raddr,
2602 : hba->ip_cmp_method))
2603 0 : continue;
2604 0 : break;
2605 0 : default:
2606 : /* shouldn't get here, but deem it no-match if so */
2607 0 : continue;
2608 : }
2609 : } /* != ctLocal */
2610 :
2611 : /* Check database and role */
2612 27516 : if (!check_db(port->database_name, port->user_name, roleid,
2613 : hba->databases))
2614 1186 : continue;
2615 :
2616 26330 : if (!check_role(port->user_name, roleid, hba->roles, false))
2617 174 : continue;
2618 :
2619 : /* Found a record that matched! */
2620 26156 : port->hba = hba;
2621 26156 : return;
2622 : }
2623 :
2624 : /* If no matching entry was found, then implicitly reject. */
2625 26 : hba = palloc0(sizeof(HbaLine));
2626 26 : hba->auth_method = uaImplicitReject;
2627 26 : 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 : */
2641 : bool
2642 2036 : load_hba(void)
2643 : {
2644 : FILE *file;
2645 2036 : List *hba_lines = NIL;
2646 : ListCell *line;
2647 2036 : List *new_parsed_lines = NIL;
2648 2036 : bool ok = true;
2649 : MemoryContext oldcxt;
2650 : MemoryContext hbacxt;
2651 :
2652 2036 : file = open_auth_file(HbaFileName, LOG, 0, NULL);
2653 2036 : if (file == NULL)
2654 : {
2655 : /* error already logged */
2656 0 : return false;
2657 : }
2658 :
2659 2036 : tokenize_auth_file(HbaFileName, file, &hba_lines, LOG, 0);
2660 :
2661 : /* Now parse all the lines */
2662 : Assert(PostmasterContext);
2663 2036 : hbacxt = AllocSetContextCreate(PostmasterContext,
2664 : "hba parser context",
2665 : ALLOCSET_SMALL_SIZES);
2666 2036 : oldcxt = MemoryContextSwitchTo(hbacxt);
2667 13762 : foreach(line, hba_lines)
2668 : {
2669 11726 : TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
2670 : HbaLine *newline;
2671 :
2672 : /* don't parse lines that already have errors */
2673 11726 : if (tok_line->err_msg != NULL)
2674 : {
2675 0 : ok = false;
2676 0 : continue;
2677 : }
2678 :
2679 11726 : if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
2680 : {
2681 : /* Parse error; remember there's trouble */
2682 0 : 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 0 : continue;
2690 : }
2691 :
2692 11726 : 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 2036 : if (ok && new_parsed_lines == NIL)
2701 : {
2702 0 : ereport(LOG,
2703 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
2704 : errmsg("configuration file \"%s\" contains no entries",
2705 : HbaFileName)));
2706 0 : ok = false;
2707 : }
2708 :
2709 : /* Free tokenizer memory */
2710 2036 : free_auth_file(file, 0);
2711 2036 : MemoryContextSwitchTo(oldcxt);
2712 :
2713 2036 : 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 0 : MemoryContextDelete(hbacxt);
2720 0 : return false;
2721 : }
2722 :
2723 : /* Loaded new file successfully, replace the one we use */
2724 2036 : if (parsed_hba_context != NULL)
2725 280 : MemoryContextDelete(parsed_hba_context);
2726 2036 : parsed_hba_context = hbacxt;
2727 2036 : parsed_hba_lines = new_parsed_lines;
2728 :
2729 2036 : 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 : */
2747 : IdentLine *
2748 196 : parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
2749 : {
2750 196 : int line_num = tok_line->line_num;
2751 196 : char *file_name = tok_line->file_name;
2752 196 : char **err_msg = &tok_line->err_msg;
2753 : ListCell *field;
2754 : List *tokens;
2755 : AuthToken *token;
2756 : IdentLine *parsedline;
2757 :
2758 : Assert(tok_line->fields != NIL);
2759 196 : field = list_head(tok_line->fields);
2760 :
2761 196 : parsedline = palloc0(sizeof(IdentLine));
2762 196 : parsedline->linenumber = line_num;
2763 :
2764 : /* Get the map token (must exist) */
2765 196 : tokens = lfirst(field);
2766 196 : IDENT_MULTI_VALUE(tokens);
2767 196 : token = linitial(tokens);
2768 196 : parsedline->usermap = pstrdup(token->string);
2769 :
2770 : /* Get the ident user token */
2771 196 : field = lnext(tok_line->fields, field);
2772 196 : IDENT_FIELD_ABSENT(field);
2773 196 : tokens = lfirst(field);
2774 196 : IDENT_MULTI_VALUE(tokens);
2775 196 : token = linitial(tokens);
2776 :
2777 : /* Copy the ident user token */
2778 196 : parsedline->system_user = copy_auth_token(token);
2779 :
2780 : /* Get the PG rolename token */
2781 196 : field = lnext(tok_line->fields, field);
2782 196 : IDENT_FIELD_ABSENT(field);
2783 196 : tokens = lfirst(field);
2784 196 : IDENT_MULTI_VALUE(tokens);
2785 196 : token = linitial(tokens);
2786 196 : 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 196 : 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 0 : return NULL;
2797 : }
2798 :
2799 196 : 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 0 : return NULL;
2804 : }
2805 :
2806 196 : 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 : */
2815 : static void
2816 50 : check_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 50 : *found_p = false;
2823 50 : *error_p = false;
2824 :
2825 50 : if (strcmp(identLine->usermap, usermap_name) != 0)
2826 : /* Line does not match the map name we're looking for, so just abort */
2827 6 : return;
2828 :
2829 : /* Get the target role's OID. Note we do not error out for bad role. */
2830 44 : roleid = get_role_oid(pg_user, true);
2831 :
2832 : /* Match? */
2833 44 : 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 24 : bool created_temporary_token = false;
2845 :
2846 24 : r = regexec_auth_token(system_user, identLine->system_user, 2, matches);
2847 24 : if (r)
2848 : {
2849 : char errstr[100];
2850 :
2851 2 : if (r != REG_NOMATCH)
2852 : {
2853 : /* REG_NOMATCH is not an error, everything else is */
2854 0 : pg_regerror(r, identLine->system_user->regex, errstr, sizeof(errstr));
2855 0 : 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 0 : *error_p = true;
2860 : }
2861 2 : 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 22 : if (!token_is_member_check(identLine->pg_user) &&
2870 16 : !token_has_regexp(identLine->pg_user) &&
2871 14 : (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL)
2872 4 : {
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 6 : if (matches[1].rm_so < 0)
2881 : {
2882 2 : 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 2 : *error_p = true;
2887 2 : return;
2888 : }
2889 4 : repl_str = system_user + matches[1].rm_so;
2890 4 : 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 4 : 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 8 : 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 8 : offset = ofs - old_pg_user;
2909 8 : memcpy(expanded_pg_user, old_pg_user, offset);
2910 8 : memcpy(expanded_pg_user + offset, repl_str, repl_len);
2911 8 : strcpy(expanded_pg_user + offset + repl_len, ofs + 2);
2912 8 : if (old_pg_user != identLine->pg_user->string)
2913 4 : pfree(old_pg_user);
2914 8 : old_pg_user = expanded_pg_user;
2915 8 : } 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 4 : expanded_pg_user_token = make_auth_token(expanded_pg_user, true);
2923 4 : created_temporary_token = true;
2924 4 : pfree(expanded_pg_user);
2925 : }
2926 : else
2927 : {
2928 16 : expanded_pg_user_token = identLine->pg_user;
2929 : }
2930 :
2931 : /* check the Postgres user */
2932 20 : *found_p = check_role(pg_user, roleid,
2933 20 : list_make1(expanded_pg_user_token),
2934 : case_insensitive);
2935 :
2936 20 : if (created_temporary_token)
2937 4 : free_auth_token(expanded_pg_user_token);
2938 :
2939 20 : 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 20 : if (case_insensitive)
2948 : {
2949 0 : if (!token_matches_insensitive(identLine->system_user,
2950 : system_user))
2951 0 : return;
2952 : }
2953 : else
2954 : {
2955 20 : if (!token_matches(identLine->system_user, system_user))
2956 0 : return;
2957 : }
2958 :
2959 : /* check the Postgres user */
2960 20 : *found_p = check_role(pg_user, roleid,
2961 20 : 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 : */
2980 : int
2981 110 : check_usermap(const char *usermap_name,
2982 : const char *pg_user,
2983 : const char *system_user,
2984 : bool case_insensitive)
2985 : {
2986 110 : bool found_entry = false,
2987 110 : error = false;
2988 :
2989 110 : if (usermap_name == NULL || usermap_name[0] == '\0')
2990 : {
2991 66 : if (case_insensitive)
2992 : {
2993 0 : if (pg_strcasecmp(pg_user, system_user) == 0)
2994 0 : return STATUS_OK;
2995 : }
2996 : else
2997 : {
2998 66 : if (strcmp(pg_user, system_user) == 0)
2999 60 : return STATUS_OK;
3000 : }
3001 6 : ereport(LOG,
3002 : (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
3003 : pg_user, system_user)));
3004 6 : return STATUS_ERROR;
3005 : }
3006 : else
3007 : {
3008 : ListCell *line_cell;
3009 :
3010 58 : foreach(line_cell, parsed_ident_lines)
3011 : {
3012 50 : check_ident_usermap(lfirst(line_cell), usermap_name,
3013 : pg_user, system_user, case_insensitive,
3014 : &found_entry, &error);
3015 50 : if (found_entry || error)
3016 : break;
3017 : }
3018 : }
3019 44 : if (!found_entry && !error)
3020 : {
3021 8 : ereport(LOG,
3022 : (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
3023 : usermap_name, pg_user, system_user)));
3024 : }
3025 44 : 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 : */
3035 : bool
3036 2036 : load_ident(void)
3037 : {
3038 : FILE *file;
3039 2036 : List *ident_lines = NIL;
3040 : ListCell *line_cell;
3041 2036 : List *new_parsed_lines = NIL;
3042 2036 : bool ok = true;
3043 : MemoryContext oldcxt;
3044 : MemoryContext ident_context;
3045 : IdentLine *newline;
3046 :
3047 : /* not FATAL ... we just won't do any special ident maps */
3048 2036 : file = open_auth_file(IdentFileName, LOG, 0, NULL);
3049 2036 : if (file == NULL)
3050 : {
3051 : /* error already logged */
3052 0 : return false;
3053 : }
3054 :
3055 2036 : tokenize_auth_file(IdentFileName, file, &ident_lines, LOG, 0);
3056 :
3057 : /* Now parse all the lines */
3058 : Assert(PostmasterContext);
3059 2036 : ident_context = AllocSetContextCreate(PostmasterContext,
3060 : "ident parser context",
3061 : ALLOCSET_SMALL_SIZES);
3062 2036 : oldcxt = MemoryContextSwitchTo(ident_context);
3063 2232 : foreach(line_cell, ident_lines)
3064 : {
3065 196 : TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line_cell);
3066 :
3067 : /* don't parse lines that already have errors */
3068 196 : if (tok_line->err_msg != NULL)
3069 : {
3070 0 : ok = false;
3071 0 : continue;
3072 : }
3073 :
3074 196 : if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
3075 : {
3076 : /* Parse error; remember there's trouble */
3077 0 : 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 0 : continue;
3085 : }
3086 :
3087 196 : new_parsed_lines = lappend(new_parsed_lines, newline);
3088 : }
3089 :
3090 : /* Free tokenizer memory */
3091 2036 : free_auth_file(file, 0);
3092 2036 : MemoryContextSwitchTo(oldcxt);
3093 :
3094 2036 : 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 0 : MemoryContextDelete(ident_context);
3101 0 : return false;
3102 : }
3103 :
3104 : /* Loaded new file successfully, replace the one we use */
3105 2036 : if (parsed_ident_context != NULL)
3106 280 : MemoryContextDelete(parsed_ident_context);
3107 :
3108 2036 : parsed_ident_context = ident_context;
3109 2036 : parsed_ident_lines = new_parsed_lines;
3110 :
3111 2036 : 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 : */
3124 : void
3125 26182 : hba_getauthmethod(Port *port)
3126 : {
3127 26182 : check_hba(port);
3128 26182 : }
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 : */
3137 : const char *
3138 742 : hba_authname(UserAuth auth_method)
3139 : {
3140 742 : return UserAuthName[auth_method];
3141 : }
|