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