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