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