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