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-2016, 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 "catalog/pg_collation.h"
29 #include "libpq/ip.h"
30 #include "libpq/libpq.h"
31 #include "postmaster/postmaster.h"
32 #include "regex/regex.h"
33 #include "replication/walsender.h"
34 #include "storage/fd.h"
35 #include "utils/acl.h"
36 #include "utils/guc.h"
37 #include "utils/lsyscache.h"
38 #include "utils/memutils.h"
39
40 #ifdef USE_LDAP
41 #ifdef WIN32
42 #include <winldap.h>
43 #else
44 #include <ldap.h>
45 #endif
46 #endif
47
48
49 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
50 #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
51
52 #define MAX_TOKEN 256
53 #define MAX_LINE 8192
54
55 /* callback data for check_network_callback */
56 typedef struct check_network_data
57 {
58 IPCompareMethod method; /* test method */
59 SockAddr *raddr; /* client's actual address */
60 bool result; /* set to true if match */
61 } check_network_data;
62
63
64 #define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
65 #define token_matches(t, k) (strcmp(t->string, k) == 0)
66
67 /*
68 * A single string token lexed from the HBA config file, together with whether
69 * the token had been quoted.
70 */
71 typedef struct HbaToken
72 {
73 char *string;
74 bool quoted;
75 } HbaToken;
76
77 /*
78 * pre-parsed content of HBA config file: list of HbaLine structs.
79 * parsed_hba_context is the memory context where it lives.
80 */
81 static List *parsed_hba_lines = NIL;
82 static MemoryContext parsed_hba_context = NULL;
83
84 /*
85 * pre-parsed content of ident mapping file: list of IdentLine structs.
86 * parsed_ident_context is the memory context where it lives.
87 *
88 * NOTE: the IdentLine structs can contain pre-compiled regular expressions
89 * that live outside the memory context. Before destroying or resetting the
90 * memory context, they need to be explicitly free'd.
91 */
92 static List *parsed_ident_lines = NIL;
93 static MemoryContext parsed_ident_context = NULL;
94
95
96 static MemoryContext tokenize_file(const char *filename, FILE *file,
97 List **lines, List **line_nums, List **raw_lines);
98 static List *tokenize_inc_file(List *tokens, const char *outer_filename,
99 const char *inc_filename);
100 static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
101 int line_num);
102
103 /*
104 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
105 * so provide our own version.
106 */
107 bool
pg_isblank(const char c)108 pg_isblank(const char c)
109 {
110 return c == ' ' || c == '\t' || c == '\r';
111 }
112
113
114 /*
115 * Grab one token out of the string pointed to by lineptr.
116 * Tokens are strings of non-blank
117 * characters bounded by blank characters, commas, beginning of line, and
118 * end of line. Blank means space or tab. Tokens can be delimited by
119 * double quotes (this allows the inclusion of blanks, but not newlines).
120 *
121 * The token, if any, is returned at *buf (a buffer of size bufsz).
122 * Also, we set *initial_quote to indicate whether there was quoting before
123 * the first character. (We use that to prevent "@x" from being treated
124 * as a file inclusion request. Note that @"x" should be so treated;
125 * we want to allow that to support embedded spaces in file paths.)
126 * We set *terminating_comma to indicate whether the token is terminated by a
127 * comma (which is not returned.)
128 *
129 * If successful: store null-terminated token at *buf and return TRUE.
130 * If no more tokens on line: set *buf = '\0' and return FALSE.
131 *
132 * Leave file positioned at the character immediately after the token or EOF,
133 * whichever comes first. If no more tokens on line, position the file to the
134 * beginning of the next line or EOF, whichever comes first.
135 *
136 * Handle comments.
137 */
138 static bool
next_token(char ** lineptr,char * buf,int bufsz,bool * initial_quote,bool * terminating_comma)139 next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
140 bool *terminating_comma)
141 {
142 int c;
143 char *start_buf = buf;
144 char *end_buf = buf + (bufsz - 1);
145 bool in_quote = false;
146 bool was_quote = false;
147 bool saw_quote = false;
148
149 Assert(end_buf > start_buf);
150
151 *initial_quote = false;
152 *terminating_comma = false;
153
154 /* Move over any whitespace and commas preceding the next token */
155 while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
156 ;
157
158 /*
159 * Build a token in buf of next characters up to EOL, unquoted comma, or
160 * unquoted whitespace.
161 */
162 while (c != '\0' &&
163 (!pg_isblank(c) || in_quote))
164 {
165 /* skip comments to EOL */
166 if (c == '#' && !in_quote)
167 {
168 while ((c = (*(*lineptr)++)) != '\0')
169 ;
170 break;
171 }
172
173 if (buf >= end_buf)
174 {
175 *buf = '\0';
176 ereport(LOG,
177 (errcode(ERRCODE_CONFIG_FILE_ERROR),
178 errmsg("authentication file token too long, skipping: \"%s\"",
179 start_buf)));
180 /* Discard remainder of line */
181 while ((c = (*(*lineptr)++)) != '\0')
182 ;
183 break;
184 }
185
186 /* we do not pass back a terminating comma in the token */
187 if (c == ',' && !in_quote)
188 {
189 *terminating_comma = true;
190 break;
191 }
192
193 if (c != '"' || was_quote)
194 *buf++ = c;
195
196 /* Literal double-quote is two double-quotes */
197 if (in_quote && c == '"')
198 was_quote = !was_quote;
199 else
200 was_quote = false;
201
202 if (c == '"')
203 {
204 in_quote = !in_quote;
205 saw_quote = true;
206 if (buf == start_buf)
207 *initial_quote = true;
208 }
209
210 c = *(*lineptr)++;
211 }
212
213 /*
214 * Un-eat the char right after the token (critical in case it is '\0',
215 * else next call will read past end of string).
216 */
217 (*lineptr)--;
218
219 *buf = '\0';
220
221 return (saw_quote || buf > start_buf);
222 }
223
224 static HbaToken *
make_hba_token(char * token,bool quoted)225 make_hba_token(char *token, bool quoted)
226 {
227 HbaToken *hbatoken;
228 int toklen;
229
230 toklen = strlen(token);
231 hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
232 hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
233 hbatoken->quoted = quoted;
234 memcpy(hbatoken->string, token, toklen + 1);
235
236 return hbatoken;
237 }
238
239 /*
240 * Copy a HbaToken struct into freshly palloc'd memory.
241 */
242 static HbaToken *
copy_hba_token(HbaToken * in)243 copy_hba_token(HbaToken *in)
244 {
245 HbaToken *out = make_hba_token(in->string, in->quoted);
246
247 return out;
248 }
249
250
251 /*
252 * Tokenize one HBA field from a line, handling file inclusion and comma lists.
253 *
254 * The result is a List of HbaToken structs for each individual token,
255 * or NIL if we reached EOL.
256 */
257 static List *
next_field_expand(const char * filename,char ** lineptr)258 next_field_expand(const char *filename, char **lineptr)
259 {
260 char buf[MAX_TOKEN];
261 bool trailing_comma;
262 bool initial_quote;
263 List *tokens = NIL;
264
265 do
266 {
267 if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
268 break;
269
270 /* Is this referencing a file? */
271 if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
272 tokens = tokenize_inc_file(tokens, filename, buf + 1);
273 else
274 tokens = lappend(tokens, make_hba_token(buf, initial_quote));
275 } while (trailing_comma);
276
277 return tokens;
278 }
279
280 /*
281 * tokenize_inc_file
282 * Expand a file included from another file into an hba "field"
283 *
284 * Opens and tokenises a file included from another HBA config file with @,
285 * and returns all values found therein as a flat list of HbaTokens. If a
286 * @-token is found, recursively expand it. The given token list is used as
287 * initial contents of list (so foo,bar,@baz does what you expect).
288 */
289 static List *
tokenize_inc_file(List * tokens,const char * outer_filename,const char * inc_filename)290 tokenize_inc_file(List *tokens,
291 const char *outer_filename,
292 const char *inc_filename)
293 {
294 char *inc_fullname;
295 FILE *inc_file;
296 List *inc_lines;
297 List *inc_line_nums;
298 ListCell *inc_line;
299 MemoryContext linecxt;
300
301 if (is_absolute_path(inc_filename))
302 {
303 /* absolute path is taken as-is */
304 inc_fullname = pstrdup(inc_filename);
305 }
306 else
307 {
308 /* relative path is relative to dir of calling file */
309 inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
310 strlen(inc_filename) + 1);
311 strcpy(inc_fullname, outer_filename);
312 get_parent_directory(inc_fullname);
313 join_path_components(inc_fullname, inc_fullname, inc_filename);
314 canonicalize_path(inc_fullname);
315 }
316
317 inc_file = AllocateFile(inc_fullname, "r");
318 if (inc_file == NULL)
319 {
320 ereport(LOG,
321 (errcode_for_file_access(),
322 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
323 inc_filename, inc_fullname)));
324 pfree(inc_fullname);
325 return tokens;
326 }
327
328 /* There is possible recursion here if the file contains @ */
329 linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums, NULL);
330
331 FreeFile(inc_file);
332 pfree(inc_fullname);
333
334 foreach(inc_line, inc_lines)
335 {
336 List *inc_fields = lfirst(inc_line);
337 ListCell *inc_field;
338
339 foreach(inc_field, inc_fields)
340 {
341 List *inc_tokens = lfirst(inc_field);
342 ListCell *inc_token;
343
344 foreach(inc_token, inc_tokens)
345 {
346 HbaToken *token = lfirst(inc_token);
347
348 tokens = lappend(tokens, copy_hba_token(token));
349 }
350 }
351 }
352
353 MemoryContextDelete(linecxt);
354 return tokens;
355 }
356
357 /*
358 * Tokenize the given file, storing the resulting data into three Lists: a
359 * List of lines, a List of line numbers, and a List of raw line contents.
360 *
361 * The list of lines is a triple-nested List structure. Each line is a List of
362 * fields, and each field is a List of HbaTokens.
363 *
364 * filename must be the absolute path to the target file.
365 *
366 * Return value is a memory context which contains all memory allocated by
367 * this function.
368 */
369 static MemoryContext
tokenize_file(const char * filename,FILE * file,List ** lines,List ** line_nums,List ** raw_lines)370 tokenize_file(const char *filename, FILE *file,
371 List **lines, List **line_nums, List **raw_lines)
372 {
373 List *current_line = NIL;
374 List *current_field = NIL;
375 int line_number = 1;
376 MemoryContext linecxt;
377 MemoryContext oldcxt;
378
379 linecxt = AllocSetContextCreate(CurrentMemoryContext,
380 "tokenize_file",
381 ALLOCSET_SMALL_SIZES);
382 oldcxt = MemoryContextSwitchTo(linecxt);
383
384 *lines = *line_nums = NIL;
385
386 while (!feof(file) && !ferror(file))
387 {
388 char rawline[MAX_LINE];
389 char *lineptr;
390
391 if (!fgets(rawline, sizeof(rawline), file))
392 break;
393 if (strlen(rawline) == MAX_LINE - 1)
394 /* Line too long! */
395 ereport(ERROR,
396 (errcode(ERRCODE_CONFIG_FILE_ERROR),
397 errmsg("authentication file line too long"),
398 errcontext("line %d of configuration file \"%s\"",
399 line_number, filename)));
400
401 /* Strip trailing linebreak from rawline */
402 lineptr = rawline + strlen(rawline) - 1;
403 while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r'))
404 *lineptr-- = '\0';
405
406 lineptr = rawline;
407 while (strlen(lineptr) > 0)
408 {
409 current_field = next_field_expand(filename, &lineptr);
410
411 /* add tokens to list, unless we are at EOL or comment start */
412 if (list_length(current_field) > 0)
413 {
414 if (current_line == NIL)
415 {
416 /* make a new line List, record its line number */
417 current_line = lappend(current_line, current_field);
418 *lines = lappend(*lines, current_line);
419 *line_nums = lappend_int(*line_nums, line_number);
420 if (raw_lines)
421 *raw_lines = lappend(*raw_lines, pstrdup(rawline));
422 }
423 else
424 {
425 /* append tokens to current line's list */
426 current_line = lappend(current_line, current_field);
427 }
428 }
429 }
430 /* we are at real or logical EOL, so force a new line List */
431 current_line = NIL;
432 line_number++;
433 }
434
435 MemoryContextSwitchTo(oldcxt);
436
437 return linecxt;
438 }
439
440
441 /*
442 * Does user belong to role?
443 *
444 * userid is the OID of the role given as the attempted login identifier.
445 * We check to see if it is a member of the specified role name.
446 */
447 static bool
is_member(Oid userid,const char * role)448 is_member(Oid userid, const char *role)
449 {
450 Oid roleid;
451
452 if (!OidIsValid(userid))
453 return false; /* if user not exist, say "no" */
454
455 roleid = get_role_oid(role, true);
456
457 if (!OidIsValid(roleid))
458 return false; /* if target role not exist, say "no" */
459
460 /*
461 * See if user is directly or indirectly a member of role. For this
462 * purpose, a superuser is not considered to be automatically a member of
463 * the role, so group auth only applies to explicit membership.
464 */
465 return is_member_of_role_nosuper(userid, roleid);
466 }
467
468 /*
469 * Check HbaToken list for a match to role, allowing group names.
470 */
471 static bool
check_role(const char * role,Oid roleid,List * tokens)472 check_role(const char *role, Oid roleid, List *tokens)
473 {
474 ListCell *cell;
475 HbaToken *tok;
476
477 foreach(cell, tokens)
478 {
479 tok = lfirst(cell);
480 if (!tok->quoted && tok->string[0] == '+')
481 {
482 if (is_member(roleid, tok->string + 1))
483 return true;
484 }
485 else if (token_matches(tok, role) ||
486 token_is_keyword(tok, "all"))
487 return true;
488 }
489 return false;
490 }
491
492 /*
493 * Check to see if db/role combination matches HbaToken list.
494 */
495 static bool
check_db(const char * dbname,const char * role,Oid roleid,List * tokens)496 check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
497 {
498 ListCell *cell;
499 HbaToken *tok;
500
501 foreach(cell, tokens)
502 {
503 tok = lfirst(cell);
504 if (am_walsender)
505 {
506 /* walsender connections can only match replication keyword */
507 if (token_is_keyword(tok, "replication"))
508 return true;
509 }
510 else if (token_is_keyword(tok, "all"))
511 return true;
512 else if (token_is_keyword(tok, "sameuser"))
513 {
514 if (strcmp(dbname, role) == 0)
515 return true;
516 }
517 else if (token_is_keyword(tok, "samegroup") ||
518 token_is_keyword(tok, "samerole"))
519 {
520 if (is_member(roleid, dbname))
521 return true;
522 }
523 else if (token_is_keyword(tok, "replication"))
524 continue; /* never match this if not walsender */
525 else if (token_matches(tok, dbname))
526 return true;
527 }
528 return false;
529 }
530
531 static bool
ipv4eq(struct sockaddr_in * a,struct sockaddr_in * b)532 ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
533 {
534 return (a->sin_addr.s_addr == b->sin_addr.s_addr);
535 }
536
537 #ifdef HAVE_IPV6
538
539 static bool
ipv6eq(struct sockaddr_in6 * a,struct sockaddr_in6 * b)540 ipv6eq(struct sockaddr_in6 * a, struct sockaddr_in6 * b)
541 {
542 int i;
543
544 for (i = 0; i < 16; i++)
545 if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
546 return false;
547
548 return true;
549 }
550 #endif /* HAVE_IPV6 */
551
552 /*
553 * Check whether host name matches pattern.
554 */
555 static bool
hostname_match(const char * pattern,const char * actual_hostname)556 hostname_match(const char *pattern, const char *actual_hostname)
557 {
558 if (pattern[0] == '.') /* suffix match */
559 {
560 size_t plen = strlen(pattern);
561 size_t hlen = strlen(actual_hostname);
562
563 if (hlen < plen)
564 return false;
565
566 return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
567 }
568 else
569 return (pg_strcasecmp(pattern, actual_hostname) == 0);
570 }
571
572 /*
573 * Check to see if a connecting IP matches a given host name.
574 */
575 static bool
check_hostname(hbaPort * port,const char * hostname)576 check_hostname(hbaPort *port, const char *hostname)
577 {
578 struct addrinfo *gai_result,
579 *gai;
580 int ret;
581 bool found;
582
583 /* Quick out if remote host name already known bad */
584 if (port->remote_hostname_resolv < 0)
585 return false;
586
587 /* Lookup remote host name if not already done */
588 if (!port->remote_hostname)
589 {
590 char remote_hostname[NI_MAXHOST];
591
592 ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
593 remote_hostname, sizeof(remote_hostname),
594 NULL, 0,
595 NI_NAMEREQD);
596 if (ret != 0)
597 {
598 /* remember failure; don't complain in the postmaster log yet */
599 port->remote_hostname_resolv = -2;
600 port->remote_hostname_errcode = ret;
601 return false;
602 }
603
604 port->remote_hostname = pstrdup(remote_hostname);
605 }
606
607 /* Now see if remote host name matches this pg_hba line */
608 if (!hostname_match(hostname, port->remote_hostname))
609 return false;
610
611 /* If we already verified the forward lookup, we're done */
612 if (port->remote_hostname_resolv == +1)
613 return true;
614
615 /* Lookup IP from host name and check against original IP */
616 ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
617 if (ret != 0)
618 {
619 /* remember failure; don't complain in the postmaster log yet */
620 port->remote_hostname_resolv = -2;
621 port->remote_hostname_errcode = ret;
622 return false;
623 }
624
625 found = false;
626 for (gai = gai_result; gai; gai = gai->ai_next)
627 {
628 if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
629 {
630 if (gai->ai_addr->sa_family == AF_INET)
631 {
632 if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
633 (struct sockaddr_in *) & port->raddr.addr))
634 {
635 found = true;
636 break;
637 }
638 }
639 #ifdef HAVE_IPV6
640 else if (gai->ai_addr->sa_family == AF_INET6)
641 {
642 if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
643 (struct sockaddr_in6 *) & port->raddr.addr))
644 {
645 found = true;
646 break;
647 }
648 }
649 #endif
650 }
651 }
652
653 if (gai_result)
654 freeaddrinfo(gai_result);
655
656 if (!found)
657 elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
658 hostname);
659
660 port->remote_hostname_resolv = found ? +1 : -1;
661
662 return found;
663 }
664
665 /*
666 * Check to see if a connecting IP matches the given address and netmask.
667 */
668 static bool
check_ip(SockAddr * raddr,struct sockaddr * addr,struct sockaddr * mask)669 check_ip(SockAddr *raddr, struct sockaddr * addr, struct sockaddr * mask)
670 {
671 if (raddr->addr.ss_family == addr->sa_family &&
672 pg_range_sockaddr(&raddr->addr,
673 (struct sockaddr_storage *) addr,
674 (struct sockaddr_storage *) mask))
675 return true;
676 return false;
677 }
678
679 /*
680 * pg_foreach_ifaddr callback: does client addr match this machine interface?
681 */
682 static void
check_network_callback(struct sockaddr * addr,struct sockaddr * netmask,void * cb_data)683 check_network_callback(struct sockaddr * addr, struct sockaddr * netmask,
684 void *cb_data)
685 {
686 check_network_data *cn = (check_network_data *) cb_data;
687 struct sockaddr_storage mask;
688
689 /* Already found a match? */
690 if (cn->result)
691 return;
692
693 if (cn->method == ipCmpSameHost)
694 {
695 /* Make an all-ones netmask of appropriate length for family */
696 pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
697 cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) & mask);
698 }
699 else
700 {
701 /* Use the netmask of the interface itself */
702 cn->result = check_ip(cn->raddr, addr, netmask);
703 }
704 }
705
706 /*
707 * Use pg_foreach_ifaddr to check a samehost or samenet match
708 */
709 static bool
check_same_host_or_net(SockAddr * raddr,IPCompareMethod method)710 check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
711 {
712 check_network_data cn;
713
714 cn.method = method;
715 cn.raddr = raddr;
716 cn.result = false;
717
718 errno = 0;
719 if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
720 {
721 elog(LOG, "error enumerating network interfaces: %m");
722 return false;
723 }
724
725 return cn.result;
726 }
727
728
729 /*
730 * Macros used to check and report on invalid configuration options.
731 * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
732 * not supported.
733 * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
734 * method is actually the one specified. Used as a shortcut when
735 * the option is only valid for one authentication method.
736 * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
737 * reporting error if it's not.
738 */
739 #define INVALID_AUTH_OPTION(optname, validmethods) do {\
740 ereport(LOG, \
741 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
742 /* translator: the second %s is a list of auth methods */ \
743 errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
744 optname, _(validmethods)), \
745 errcontext("line %d of configuration file \"%s\"", \
746 line_num, HbaFileName))); \
747 return false; \
748 } while (0);
749
750 #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
751 if (hbaline->auth_method != methodval) \
752 INVALID_AUTH_OPTION(optname, validmethods); \
753 } while (0);
754
755 #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
756 if (argvar == NULL) {\
757 ereport(LOG, \
758 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
759 errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
760 authname, argname), \
761 errcontext("line %d of configuration file \"%s\"", \
762 line_num, HbaFileName))); \
763 return NULL; \
764 } \
765 } while (0);
766
767 /*
768 * IDENT_FIELD_ABSENT:
769 * Throw an error and exit the function if the given ident field ListCell is
770 * not populated.
771 *
772 * IDENT_MULTI_VALUE:
773 * Throw an error and exit the function if the given ident token List has more
774 * than one element.
775 */
776 #define IDENT_FIELD_ABSENT(field) do {\
777 if (!field) { \
778 ereport(LOG, \
779 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
780 errmsg("missing entry in file \"%s\" at end of line %d", \
781 IdentFileName, line_number))); \
782 return NULL; \
783 } \
784 } while (0);
785
786 #define IDENT_MULTI_VALUE(tokens) do {\
787 if (tokens->length > 1) { \
788 ereport(LOG, \
789 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
790 errmsg("multiple values in ident field"), \
791 errcontext("line %d of configuration file \"%s\"", \
792 line_number, IdentFileName))); \
793 return NULL; \
794 } \
795 } while (0);
796
797
798 /*
799 * Parse one tokenised line from the hba config file and store the result in a
800 * HbaLine structure, or NULL if parsing fails.
801 *
802 * The tokenised line is a List of fields, each field being a List of
803 * HbaTokens.
804 *
805 * Note: this function leaks memory when an error occurs. Caller is expected
806 * to have set a memory context that will be reset if this function returns
807 * NULL.
808 */
809 static HbaLine *
parse_hba_line(List * line,int line_num,char * raw_line)810 parse_hba_line(List *line, int line_num, char *raw_line)
811 {
812 char *str;
813 struct addrinfo *gai_result;
814 struct addrinfo hints;
815 int ret;
816 char *cidr_slash;
817 char *unsupauth;
818 ListCell *field;
819 List *tokens;
820 ListCell *tokencell;
821 HbaToken *token;
822 HbaLine *parsedline;
823
824 parsedline = palloc0(sizeof(HbaLine));
825 parsedline->linenumber = line_num;
826 parsedline->rawline = pstrdup(raw_line);
827
828 /* Check the record type. */
829 field = list_head(line);
830 tokens = lfirst(field);
831 if (tokens->length > 1)
832 {
833 ereport(LOG,
834 (errcode(ERRCODE_CONFIG_FILE_ERROR),
835 errmsg("multiple values specified for connection type"),
836 errhint("Specify exactly one connection type per line."),
837 errcontext("line %d of configuration file \"%s\"",
838 line_num, HbaFileName)));
839 return NULL;
840 }
841 token = linitial(tokens);
842 if (strcmp(token->string, "local") == 0)
843 {
844 #ifdef HAVE_UNIX_SOCKETS
845 parsedline->conntype = ctLocal;
846 #else
847 ereport(LOG,
848 (errcode(ERRCODE_CONFIG_FILE_ERROR),
849 errmsg("local connections are not supported by this build"),
850 errcontext("line %d of configuration file \"%s\"",
851 line_num, HbaFileName)));
852 return NULL;
853 #endif
854 }
855 else if (strcmp(token->string, "host") == 0 ||
856 strcmp(token->string, "hostssl") == 0 ||
857 strcmp(token->string, "hostnossl") == 0)
858 {
859
860 if (token->string[4] == 's') /* "hostssl" */
861 {
862 /* SSL support must be actually active, else complain */
863 #ifdef USE_SSL
864 if (EnableSSL)
865 parsedline->conntype = ctHostSSL;
866 else
867 {
868 ereport(LOG,
869 (errcode(ERRCODE_CONFIG_FILE_ERROR),
870 errmsg("hostssl requires SSL to be turned on"),
871 errhint("Set ssl = on in postgresql.conf."),
872 errcontext("line %d of configuration file \"%s\"",
873 line_num, HbaFileName)));
874 return NULL;
875 }
876 #else
877 ereport(LOG,
878 (errcode(ERRCODE_CONFIG_FILE_ERROR),
879 errmsg("hostssl is not supported by this build"),
880 errhint("Compile with --with-openssl to use SSL connections."),
881 errcontext("line %d of configuration file \"%s\"",
882 line_num, HbaFileName)));
883 return NULL;
884 #endif
885 }
886 else if (token->string[4] == 'n') /* "hostnossl" */
887 {
888 parsedline->conntype = ctHostNoSSL;
889 }
890 else
891 {
892 /* "host" */
893 parsedline->conntype = ctHost;
894 }
895 } /* record type */
896 else
897 {
898 ereport(LOG,
899 (errcode(ERRCODE_CONFIG_FILE_ERROR),
900 errmsg("invalid connection type \"%s\"",
901 token->string),
902 errcontext("line %d of configuration file \"%s\"",
903 line_num, HbaFileName)));
904 return NULL;
905 }
906
907 /* Get the databases. */
908 field = lnext(field);
909 if (!field)
910 {
911 ereport(LOG,
912 (errcode(ERRCODE_CONFIG_FILE_ERROR),
913 errmsg("end-of-line before database specification"),
914 errcontext("line %d of configuration file \"%s\"",
915 line_num, HbaFileName)));
916 return NULL;
917 }
918 parsedline->databases = NIL;
919 tokens = lfirst(field);
920 foreach(tokencell, tokens)
921 {
922 parsedline->databases = lappend(parsedline->databases,
923 copy_hba_token(lfirst(tokencell)));
924 }
925
926 /* Get the roles. */
927 field = lnext(field);
928 if (!field)
929 {
930 ereport(LOG,
931 (errcode(ERRCODE_CONFIG_FILE_ERROR),
932 errmsg("end-of-line before role specification"),
933 errcontext("line %d of configuration file \"%s\"",
934 line_num, HbaFileName)));
935 return NULL;
936 }
937 parsedline->roles = NIL;
938 tokens = lfirst(field);
939 foreach(tokencell, tokens)
940 {
941 parsedline->roles = lappend(parsedline->roles,
942 copy_hba_token(lfirst(tokencell)));
943 }
944
945 if (parsedline->conntype != ctLocal)
946 {
947 /* Read the IP address field. (with or without CIDR netmask) */
948 field = lnext(field);
949 if (!field)
950 {
951 ereport(LOG,
952 (errcode(ERRCODE_CONFIG_FILE_ERROR),
953 errmsg("end-of-line before IP address specification"),
954 errcontext("line %d of configuration file \"%s\"",
955 line_num, HbaFileName)));
956 return NULL;
957 }
958 tokens = lfirst(field);
959 if (tokens->length > 1)
960 {
961 ereport(LOG,
962 (errcode(ERRCODE_CONFIG_FILE_ERROR),
963 errmsg("multiple values specified for host address"),
964 errhint("Specify one address range per line."),
965 errcontext("line %d of configuration file \"%s\"",
966 line_num, HbaFileName)));
967 return NULL;
968 }
969 token = linitial(tokens);
970
971 if (token_is_keyword(token, "all"))
972 {
973 parsedline->ip_cmp_method = ipCmpAll;
974 }
975 else if (token_is_keyword(token, "samehost"))
976 {
977 /* Any IP on this host is allowed to connect */
978 parsedline->ip_cmp_method = ipCmpSameHost;
979 }
980 else if (token_is_keyword(token, "samenet"))
981 {
982 /* Any IP on the host's subnets is allowed to connect */
983 parsedline->ip_cmp_method = ipCmpSameNet;
984 }
985 else
986 {
987 /* IP and netmask are specified */
988 parsedline->ip_cmp_method = ipCmpMask;
989
990 /* need a modifiable copy of token */
991 str = pstrdup(token->string);
992
993 /* Check if it has a CIDR suffix and if so isolate it */
994 cidr_slash = strchr(str, '/');
995 if (cidr_slash)
996 *cidr_slash = '\0';
997
998 /* Get the IP address either way */
999 hints.ai_flags = AI_NUMERICHOST;
1000 hints.ai_family = AF_UNSPEC;
1001 hints.ai_socktype = 0;
1002 hints.ai_protocol = 0;
1003 hints.ai_addrlen = 0;
1004 hints.ai_canonname = NULL;
1005 hints.ai_addr = NULL;
1006 hints.ai_next = NULL;
1007
1008 ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
1009 if (ret == 0 && gai_result)
1010 memcpy(&parsedline->addr, gai_result->ai_addr,
1011 gai_result->ai_addrlen);
1012 else if (ret == EAI_NONAME)
1013 parsedline->hostname = str;
1014 else
1015 {
1016 ereport(LOG,
1017 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1018 errmsg("invalid IP address \"%s\": %s",
1019 str, gai_strerror(ret)),
1020 errcontext("line %d of configuration file \"%s\"",
1021 line_num, HbaFileName)));
1022 if (gai_result)
1023 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1024 return NULL;
1025 }
1026
1027 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1028
1029 /* Get the netmask */
1030 if (cidr_slash)
1031 {
1032 if (parsedline->hostname)
1033 {
1034 ereport(LOG,
1035 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1036 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
1037 token->string),
1038 errcontext("line %d of configuration file \"%s\"",
1039 line_num, HbaFileName)));
1040 return NULL;
1041 }
1042
1043 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
1044 parsedline->addr.ss_family) < 0)
1045 {
1046 ereport(LOG,
1047 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1048 errmsg("invalid CIDR mask in address \"%s\"",
1049 token->string),
1050 errcontext("line %d of configuration file \"%s\"",
1051 line_num, HbaFileName)));
1052 return NULL;
1053 }
1054 pfree(str);
1055 }
1056 else if (!parsedline->hostname)
1057 {
1058 /* Read the mask field. */
1059 pfree(str);
1060 field = lnext(field);
1061 if (!field)
1062 {
1063 ereport(LOG,
1064 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1065 errmsg("end-of-line before netmask specification"),
1066 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
1067 errcontext("line %d of configuration file \"%s\"",
1068 line_num, HbaFileName)));
1069 return NULL;
1070 }
1071 tokens = lfirst(field);
1072 if (tokens->length > 1)
1073 {
1074 ereport(LOG,
1075 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1076 errmsg("multiple values specified for netmask"),
1077 errcontext("line %d of configuration file \"%s\"",
1078 line_num, HbaFileName)));
1079 return NULL;
1080 }
1081 token = linitial(tokens);
1082
1083 ret = pg_getaddrinfo_all(token->string, NULL,
1084 &hints, &gai_result);
1085 if (ret || !gai_result)
1086 {
1087 ereport(LOG,
1088 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1089 errmsg("invalid IP mask \"%s\": %s",
1090 token->string, gai_strerror(ret)),
1091 errcontext("line %d of configuration file \"%s\"",
1092 line_num, HbaFileName)));
1093 if (gai_result)
1094 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1095 return NULL;
1096 }
1097
1098 memcpy(&parsedline->mask, gai_result->ai_addr,
1099 gai_result->ai_addrlen);
1100 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1101
1102 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
1103 {
1104 ereport(LOG,
1105 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1106 errmsg("IP address and mask do not match"),
1107 errcontext("line %d of configuration file \"%s\"",
1108 line_num, HbaFileName)));
1109 return NULL;
1110 }
1111 }
1112 }
1113 } /* != ctLocal */
1114
1115 /* Get the authentication method */
1116 field = lnext(field);
1117 if (!field)
1118 {
1119 ereport(LOG,
1120 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1121 errmsg("end-of-line before authentication method"),
1122 errcontext("line %d of configuration file \"%s\"",
1123 line_num, HbaFileName)));
1124 return NULL;
1125 }
1126 tokens = lfirst(field);
1127 if (tokens->length > 1)
1128 {
1129 ereport(LOG,
1130 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1131 errmsg("multiple values specified for authentication type"),
1132 errhint("Specify exactly one authentication type per line."),
1133 errcontext("line %d of configuration file \"%s\"",
1134 line_num, HbaFileName)));
1135 return NULL;
1136 }
1137 token = linitial(tokens);
1138
1139 unsupauth = NULL;
1140 if (strcmp(token->string, "trust") == 0)
1141 parsedline->auth_method = uaTrust;
1142 else if (strcmp(token->string, "ident") == 0)
1143 parsedline->auth_method = uaIdent;
1144 else if (strcmp(token->string, "peer") == 0)
1145 parsedline->auth_method = uaPeer;
1146 else if (strcmp(token->string, "password") == 0)
1147 parsedline->auth_method = uaPassword;
1148 else if (strcmp(token->string, "gss") == 0)
1149 #ifdef ENABLE_GSS
1150 parsedline->auth_method = uaGSS;
1151 #else
1152 unsupauth = "gss";
1153 #endif
1154 else if (strcmp(token->string, "sspi") == 0)
1155 #ifdef ENABLE_SSPI
1156 parsedline->auth_method = uaSSPI;
1157 #else
1158 unsupauth = "sspi";
1159 #endif
1160 else if (strcmp(token->string, "reject") == 0)
1161 parsedline->auth_method = uaReject;
1162 else if (strcmp(token->string, "md5") == 0)
1163 {
1164 if (Db_user_namespace)
1165 {
1166 ereport(LOG,
1167 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1168 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
1169 errcontext("line %d of configuration file \"%s\"",
1170 line_num, HbaFileName)));
1171 return NULL;
1172 }
1173 parsedline->auth_method = uaMD5;
1174 }
1175 else if (strcmp(token->string, "pam") == 0)
1176 #ifdef USE_PAM
1177 parsedline->auth_method = uaPAM;
1178 #else
1179 unsupauth = "pam";
1180 #endif
1181 else if (strcmp(token->string, "bsd") == 0)
1182 #ifdef USE_BSD_AUTH
1183 parsedline->auth_method = uaBSD;
1184 #else
1185 unsupauth = "bsd";
1186 #endif
1187 else if (strcmp(token->string, "ldap") == 0)
1188 #ifdef USE_LDAP
1189 parsedline->auth_method = uaLDAP;
1190 #else
1191 unsupauth = "ldap";
1192 #endif
1193 else if (strcmp(token->string, "cert") == 0)
1194 #ifdef USE_SSL
1195 parsedline->auth_method = uaCert;
1196 #else
1197 unsupauth = "cert";
1198 #endif
1199 else if (strcmp(token->string, "radius") == 0)
1200 parsedline->auth_method = uaRADIUS;
1201 else
1202 {
1203 ereport(LOG,
1204 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1205 errmsg("invalid authentication method \"%s\"",
1206 token->string),
1207 errcontext("line %d of configuration file \"%s\"",
1208 line_num, HbaFileName)));
1209 return NULL;
1210 }
1211
1212 if (unsupauth)
1213 {
1214 ereport(LOG,
1215 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1216 errmsg("invalid authentication method \"%s\": not supported by this build",
1217 token->string),
1218 errcontext("line %d of configuration file \"%s\"",
1219 line_num, HbaFileName)));
1220 return NULL;
1221 }
1222
1223 /*
1224 * XXX: When using ident on local connections, change it to peer, for
1225 * backwards compatibility.
1226 */
1227 if (parsedline->conntype == ctLocal &&
1228 parsedline->auth_method == uaIdent)
1229 parsedline->auth_method = uaPeer;
1230
1231 /* Invalid authentication combinations */
1232 if (parsedline->conntype == ctLocal &&
1233 parsedline->auth_method == uaGSS)
1234 {
1235 ereport(LOG,
1236 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1237 errmsg("gssapi authentication is not supported on local sockets"),
1238 errcontext("line %d of configuration file \"%s\"",
1239 line_num, HbaFileName)));
1240 return NULL;
1241 }
1242
1243 if (parsedline->conntype != ctLocal &&
1244 parsedline->auth_method == uaPeer)
1245 {
1246 ereport(LOG,
1247 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1248 errmsg("peer authentication is only supported on local sockets"),
1249 errcontext("line %d of configuration file \"%s\"",
1250 line_num, HbaFileName)));
1251 return NULL;
1252 }
1253
1254 /*
1255 * SSPI authentication can never be enabled on ctLocal connections,
1256 * because it's only supported on Windows, where ctLocal isn't supported.
1257 */
1258
1259
1260 if (parsedline->conntype != ctHostSSL &&
1261 parsedline->auth_method == uaCert)
1262 {
1263 ereport(LOG,
1264 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1265 errmsg("cert authentication is only supported on hostssl connections"),
1266 errcontext("line %d of configuration file \"%s\"",
1267 line_num, HbaFileName)));
1268 return NULL;
1269 }
1270
1271 /*
1272 * For GSS and SSPI, set the default value of include_realm to true.
1273 * Having include_realm set to false is dangerous in multi-realm
1274 * situations and is generally considered bad practice. We keep the
1275 * capability around for backwards compatibility, but we might want to
1276 * remove it at some point in the future. Users who still need to strip
1277 * the realm off would be better served by using an appropriate regex in a
1278 * pg_ident.conf mapping.
1279 */
1280 if (parsedline->auth_method == uaGSS ||
1281 parsedline->auth_method == uaSSPI)
1282 parsedline->include_realm = true;
1283
1284 /*
1285 * For SSPI, include_realm defaults to the SAM-compatible domain (aka
1286 * NetBIOS name) and user names instead of the Kerberos principal name for
1287 * compatibility.
1288 */
1289 if (parsedline->auth_method == uaSSPI)
1290 {
1291 parsedline->compat_realm = true;
1292 parsedline->upn_username = false;
1293 }
1294
1295 /* Parse remaining arguments */
1296 while ((field = lnext(field)) != NULL)
1297 {
1298 tokens = lfirst(field);
1299 foreach(tokencell, tokens)
1300 {
1301 char *val;
1302
1303 token = lfirst(tokencell);
1304
1305 str = pstrdup(token->string);
1306 val = strchr(str, '=');
1307 if (val == NULL)
1308 {
1309 /*
1310 * Got something that's not a name=value pair.
1311 */
1312 ereport(LOG,
1313 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1314 errmsg("authentication option not in name=value format: %s", token->string),
1315 errcontext("line %d of configuration file \"%s\"",
1316 line_num, HbaFileName)));
1317 return NULL;
1318 }
1319
1320 *val++ = '\0'; /* str now holds "name", val holds "value" */
1321 if (!parse_hba_auth_opt(str, val, parsedline, line_num))
1322 /* parse_hba_auth_opt already logged the error message */
1323 return NULL;
1324 pfree(str);
1325 }
1326 }
1327
1328 /*
1329 * Check if the selected authentication method has any mandatory arguments
1330 * that are not set.
1331 */
1332 if (parsedline->auth_method == uaLDAP)
1333 {
1334 MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1335
1336 /*
1337 * LDAP can operate in two modes: either with a direct bind, using
1338 * ldapprefix and ldapsuffix, or using a search+bind, using
1339 * ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
1340 * Disallow mixing these parameters.
1341 */
1342 if (parsedline->ldapprefix || parsedline->ldapsuffix)
1343 {
1344 if (parsedline->ldapbasedn ||
1345 parsedline->ldapbinddn ||
1346 parsedline->ldapbindpasswd ||
1347 parsedline->ldapsearchattribute)
1348 {
1349 ereport(LOG,
1350 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1351 errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"),
1352 errcontext("line %d of configuration file \"%s\"",
1353 line_num, HbaFileName)));
1354 return NULL;
1355 }
1356 }
1357 else if (!parsedline->ldapbasedn)
1358 {
1359 ereport(LOG,
1360 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1361 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1362 errcontext("line %d of configuration file \"%s\"",
1363 line_num, HbaFileName)));
1364 return NULL;
1365 }
1366 }
1367
1368 if (parsedline->auth_method == uaRADIUS)
1369 {
1370 MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
1371 MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
1372 }
1373
1374 /*
1375 * Enforce any parameters implied by other settings.
1376 */
1377 if (parsedline->auth_method == uaCert)
1378 {
1379 parsedline->clientcert = true;
1380 }
1381
1382 return parsedline;
1383 }
1384
1385 /*
1386 * Parse one name-value pair as an authentication option into the given
1387 * HbaLine. Return true if we successfully parse the option, false if we
1388 * encounter an error.
1389 */
1390 static bool
parse_hba_auth_opt(char * name,char * val,HbaLine * hbaline,int line_num)1391 parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
1392 {
1393 #ifdef USE_LDAP
1394 hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
1395 #endif
1396
1397 if (strcmp(name, "map") == 0)
1398 {
1399 if (hbaline->auth_method != uaIdent &&
1400 hbaline->auth_method != uaPeer &&
1401 hbaline->auth_method != uaGSS &&
1402 hbaline->auth_method != uaSSPI &&
1403 hbaline->auth_method != uaCert)
1404 INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
1405 hbaline->usermap = pstrdup(val);
1406 }
1407 else if (strcmp(name, "clientcert") == 0)
1408 {
1409 /*
1410 * Since we require ctHostSSL, this really can never happen on
1411 * non-SSL-enabled builds, so don't bother checking for USE_SSL.
1412 */
1413 if (hbaline->conntype != ctHostSSL)
1414 {
1415 ereport(LOG,
1416 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1417 errmsg("clientcert can only be configured for \"hostssl\" rows"),
1418 errcontext("line %d of configuration file \"%s\"",
1419 line_num, HbaFileName)));
1420 return false;
1421 }
1422 if (strcmp(val, "1") == 0)
1423 {
1424 if (!secure_loaded_verify_locations())
1425 {
1426 ereport(LOG,
1427 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1428 errmsg("client certificates can only be checked if a root certificate store is available"),
1429 errhint("Make sure the configuration parameter \"%s\" is set.", "ssl_ca_file"),
1430 errcontext("line %d of configuration file \"%s\"",
1431 line_num, HbaFileName)));
1432 return false;
1433 }
1434 hbaline->clientcert = true;
1435 }
1436 else
1437 {
1438 if (hbaline->auth_method == uaCert)
1439 {
1440 ereport(LOG,
1441 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1442 errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
1443 errcontext("line %d of configuration file \"%s\"",
1444 line_num, HbaFileName)));
1445 return false;
1446 }
1447 hbaline->clientcert = false;
1448 }
1449 }
1450 else if (strcmp(name, "pamservice") == 0)
1451 {
1452 REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
1453 hbaline->pamservice = pstrdup(val);
1454 }
1455 else if (strcmp(name, "pam_use_hostname") == 0)
1456 {
1457 REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
1458 if (strcmp(val, "1") == 0)
1459 hbaline->pam_use_hostname = true;
1460 else
1461 hbaline->pam_use_hostname = false;
1462
1463 }
1464 else if (strcmp(name, "ldapurl") == 0)
1465 {
1466 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1467 LDAPURLDesc *urldata;
1468 int rc;
1469 #endif
1470
1471 REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
1472 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1473 rc = ldap_url_parse(val, &urldata);
1474 if (rc != LDAP_SUCCESS)
1475 {
1476 ereport(LOG,
1477 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1478 errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
1479 return false;
1480 }
1481
1482 if (strcmp(urldata->lud_scheme, "ldap") != 0)
1483 {
1484 ereport(LOG,
1485 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1486 errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
1487 ldap_free_urldesc(urldata);
1488 return false;
1489 }
1490
1491 if (urldata->lud_host)
1492 hbaline->ldapserver = pstrdup(urldata->lud_host);
1493 hbaline->ldapport = urldata->lud_port;
1494 if (urldata->lud_dn)
1495 hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
1496
1497 if (urldata->lud_attrs)
1498 hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]); /* only use first one */
1499 hbaline->ldapscope = urldata->lud_scope;
1500 if (urldata->lud_filter)
1501 {
1502 ereport(LOG,
1503 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1504 errmsg("filters not supported in LDAP URLs")));
1505 ldap_free_urldesc(urldata);
1506 return false;
1507 }
1508 ldap_free_urldesc(urldata);
1509 #else /* not OpenLDAP */
1510 ereport(LOG,
1511 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1512 errmsg("LDAP URLs not supported on this platform")));
1513 #endif /* not OpenLDAP */
1514 }
1515 else if (strcmp(name, "ldaptls") == 0)
1516 {
1517 REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
1518 if (strcmp(val, "1") == 0)
1519 hbaline->ldaptls = true;
1520 else
1521 hbaline->ldaptls = false;
1522 }
1523 else if (strcmp(name, "ldapserver") == 0)
1524 {
1525 REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
1526 hbaline->ldapserver = pstrdup(val);
1527 }
1528 else if (strcmp(name, "ldapport") == 0)
1529 {
1530 REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
1531 hbaline->ldapport = atoi(val);
1532 if (hbaline->ldapport == 0)
1533 {
1534 ereport(LOG,
1535 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1536 errmsg("invalid LDAP port number: \"%s\"", val),
1537 errcontext("line %d of configuration file \"%s\"",
1538 line_num, HbaFileName)));
1539 return false;
1540 }
1541 }
1542 else if (strcmp(name, "ldapbinddn") == 0)
1543 {
1544 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
1545 hbaline->ldapbinddn = pstrdup(val);
1546 }
1547 else if (strcmp(name, "ldapbindpasswd") == 0)
1548 {
1549 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
1550 hbaline->ldapbindpasswd = pstrdup(val);
1551 }
1552 else if (strcmp(name, "ldapsearchattribute") == 0)
1553 {
1554 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
1555 hbaline->ldapsearchattribute = pstrdup(val);
1556 }
1557 else if (strcmp(name, "ldapbasedn") == 0)
1558 {
1559 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
1560 hbaline->ldapbasedn = pstrdup(val);
1561 }
1562 else if (strcmp(name, "ldapprefix") == 0)
1563 {
1564 REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
1565 hbaline->ldapprefix = pstrdup(val);
1566 }
1567 else if (strcmp(name, "ldapsuffix") == 0)
1568 {
1569 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
1570 hbaline->ldapsuffix = pstrdup(val);
1571 }
1572 else if (strcmp(name, "krb_realm") == 0)
1573 {
1574 if (hbaline->auth_method != uaGSS &&
1575 hbaline->auth_method != uaSSPI)
1576 INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
1577 hbaline->krb_realm = pstrdup(val);
1578 }
1579 else if (strcmp(name, "include_realm") == 0)
1580 {
1581 if (hbaline->auth_method != uaGSS &&
1582 hbaline->auth_method != uaSSPI)
1583 INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
1584 if (strcmp(val, "1") == 0)
1585 hbaline->include_realm = true;
1586 else
1587 hbaline->include_realm = false;
1588 }
1589 else if (strcmp(name, "compat_realm") == 0)
1590 {
1591 if (hbaline->auth_method != uaSSPI)
1592 INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
1593 if (strcmp(val, "1") == 0)
1594 hbaline->compat_realm = true;
1595 else
1596 hbaline->compat_realm = false;
1597 }
1598 else if (strcmp(name, "upn_username") == 0)
1599 {
1600 if (hbaline->auth_method != uaSSPI)
1601 INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
1602 if (strcmp(val, "1") == 0)
1603 hbaline->upn_username = true;
1604 else
1605 hbaline->upn_username = false;
1606 }
1607 else if (strcmp(name, "radiusserver") == 0)
1608 {
1609 struct addrinfo *gai_result;
1610 struct addrinfo hints;
1611 int ret;
1612
1613 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
1614
1615 MemSet(&hints, 0, sizeof(hints));
1616 hints.ai_socktype = SOCK_DGRAM;
1617 hints.ai_family = AF_UNSPEC;
1618
1619 ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
1620 if (ret || !gai_result)
1621 {
1622 ereport(LOG,
1623 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1624 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
1625 val, gai_strerror(ret)),
1626 errcontext("line %d of configuration file \"%s\"",
1627 line_num, HbaFileName)));
1628 if (gai_result)
1629 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1630 return false;
1631 }
1632 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1633 hbaline->radiusserver = pstrdup(val);
1634 }
1635 else if (strcmp(name, "radiusport") == 0)
1636 {
1637 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
1638 hbaline->radiusport = atoi(val);
1639 if (hbaline->radiusport == 0)
1640 {
1641 ereport(LOG,
1642 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1643 errmsg("invalid RADIUS port number: \"%s\"", val),
1644 errcontext("line %d of configuration file \"%s\"",
1645 line_num, HbaFileName)));
1646 return false;
1647 }
1648 }
1649 else if (strcmp(name, "radiussecret") == 0)
1650 {
1651 REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
1652 hbaline->radiussecret = pstrdup(val);
1653 }
1654 else if (strcmp(name, "radiusidentifier") == 0)
1655 {
1656 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
1657 hbaline->radiusidentifier = pstrdup(val);
1658 }
1659 else
1660 {
1661 ereport(LOG,
1662 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1663 errmsg("unrecognized authentication option name: \"%s\"",
1664 name),
1665 errcontext("line %d of configuration file \"%s\"",
1666 line_num, HbaFileName)));
1667 return false;
1668 }
1669 return true;
1670 }
1671
1672 /*
1673 * Scan the pre-parsed hba file, looking for a match to the port's connection
1674 * request.
1675 */
1676 static void
check_hba(hbaPort * port)1677 check_hba(hbaPort *port)
1678 {
1679 Oid roleid;
1680 ListCell *line;
1681 HbaLine *hba;
1682
1683 /* Get the target role's OID. Note we do not error out for bad role. */
1684 roleid = get_role_oid(port->user_name, true);
1685
1686 foreach(line, parsed_hba_lines)
1687 {
1688 hba = (HbaLine *) lfirst(line);
1689
1690 /* Check connection type */
1691 if (hba->conntype == ctLocal)
1692 {
1693 if (!IS_AF_UNIX(port->raddr.addr.ss_family))
1694 continue;
1695 }
1696 else
1697 {
1698 if (IS_AF_UNIX(port->raddr.addr.ss_family))
1699 continue;
1700
1701 /* Check SSL state */
1702 if (port->ssl_in_use)
1703 {
1704 /* Connection is SSL, match both "host" and "hostssl" */
1705 if (hba->conntype == ctHostNoSSL)
1706 continue;
1707 }
1708 else
1709 {
1710 /* Connection is not SSL, match both "host" and "hostnossl" */
1711 if (hba->conntype == ctHostSSL)
1712 continue;
1713 }
1714
1715 /* Check IP address */
1716 switch (hba->ip_cmp_method)
1717 {
1718 case ipCmpMask:
1719 if (hba->hostname)
1720 {
1721 if (!check_hostname(port,
1722 hba->hostname))
1723 continue;
1724 }
1725 else
1726 {
1727 if (!check_ip(&port->raddr,
1728 (struct sockaddr *) & hba->addr,
1729 (struct sockaddr *) & hba->mask))
1730 continue;
1731 }
1732 break;
1733 case ipCmpAll:
1734 break;
1735 case ipCmpSameHost:
1736 case ipCmpSameNet:
1737 if (!check_same_host_or_net(&port->raddr,
1738 hba->ip_cmp_method))
1739 continue;
1740 break;
1741 default:
1742 /* shouldn't get here, but deem it no-match if so */
1743 continue;
1744 }
1745 } /* != ctLocal */
1746
1747 /* Check database and role */
1748 if (!check_db(port->database_name, port->user_name, roleid,
1749 hba->databases))
1750 continue;
1751
1752 if (!check_role(port->user_name, roleid, hba->roles))
1753 continue;
1754
1755 /* Found a record that matched! */
1756 port->hba = hba;
1757 return;
1758 }
1759
1760 /* If no matching entry was found, then implicitly reject. */
1761 hba = palloc0(sizeof(HbaLine));
1762 hba->auth_method = uaImplicitReject;
1763 port->hba = hba;
1764 }
1765
1766 /*
1767 * Read the config file and create a List of HbaLine records for the contents.
1768 *
1769 * The configuration is read into a temporary list, and if any parse error
1770 * occurs the old list is kept in place and false is returned. Only if the
1771 * whole file parses OK is the list replaced, and the function returns true.
1772 *
1773 * On a false result, caller will take care of reporting a FATAL error in case
1774 * this is the initial startup. If it happens on reload, we just keep running
1775 * with the old data.
1776 */
1777 bool
load_hba(void)1778 load_hba(void)
1779 {
1780 FILE *file;
1781 List *hba_lines = NIL;
1782 List *hba_line_nums = NIL;
1783 List *hba_raw_lines = NIL;
1784 ListCell *line,
1785 *line_num,
1786 *raw_line;
1787 List *new_parsed_lines = NIL;
1788 bool ok = true;
1789 MemoryContext linecxt;
1790 MemoryContext oldcxt;
1791 MemoryContext hbacxt;
1792
1793 file = AllocateFile(HbaFileName, "r");
1794 if (file == NULL)
1795 {
1796 ereport(LOG,
1797 (errcode_for_file_access(),
1798 errmsg("could not open configuration file \"%s\": %m",
1799 HbaFileName)));
1800 return false;
1801 }
1802
1803 linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines);
1804 FreeFile(file);
1805
1806 /* Now parse all the lines */
1807 Assert(PostmasterContext);
1808 hbacxt = AllocSetContextCreate(PostmasterContext,
1809 "hba parser context",
1810 ALLOCSET_SMALL_SIZES);
1811 oldcxt = MemoryContextSwitchTo(hbacxt);
1812 forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines)
1813 {
1814 HbaLine *newline;
1815
1816 if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line))) == NULL)
1817 {
1818 /*
1819 * Parse error in the file, so indicate there's a problem. NB: a
1820 * problem in a line will free the memory for all previous lines
1821 * as well!
1822 */
1823 MemoryContextReset(hbacxt);
1824 new_parsed_lines = NIL;
1825 ok = false;
1826
1827 /*
1828 * Keep parsing the rest of the file so we can report errors on
1829 * more than the first row. Error has already been reported in the
1830 * parsing function, so no need to log it here.
1831 */
1832 continue;
1833 }
1834
1835 new_parsed_lines = lappend(new_parsed_lines, newline);
1836 }
1837
1838 /*
1839 * A valid HBA file must have at least one entry; else there's no way to
1840 * connect to the postmaster. But only complain about this if we didn't
1841 * already have parsing errors.
1842 */
1843 if (ok && new_parsed_lines == NIL)
1844 {
1845 ereport(LOG,
1846 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1847 errmsg("configuration file \"%s\" contains no entries",
1848 HbaFileName)));
1849 ok = false;
1850 }
1851
1852 /* Free tokenizer memory */
1853 MemoryContextDelete(linecxt);
1854 MemoryContextSwitchTo(oldcxt);
1855
1856 if (!ok)
1857 {
1858 /* File contained one or more errors, so bail out */
1859 MemoryContextDelete(hbacxt);
1860 return false;
1861 }
1862
1863 /* Loaded new file successfully, replace the one we use */
1864 if (parsed_hba_context != NULL)
1865 MemoryContextDelete(parsed_hba_context);
1866 parsed_hba_context = hbacxt;
1867 parsed_hba_lines = new_parsed_lines;
1868
1869 return true;
1870 }
1871
1872 /*
1873 * Parse one tokenised line from the ident config file and store the result in
1874 * an IdentLine structure, or NULL if parsing fails.
1875 *
1876 * The tokenised line is a nested List of fields and tokens.
1877 *
1878 * If ident_user is a regular expression (ie. begins with a slash), it is
1879 * compiled and stored in IdentLine structure.
1880 *
1881 * Note: this function leaks memory when an error occurs. Caller is expected
1882 * to have set a memory context that will be reset if this function returns
1883 * NULL.
1884 */
1885 static IdentLine *
parse_ident_line(List * line,int line_number)1886 parse_ident_line(List *line, int line_number)
1887 {
1888 ListCell *field;
1889 List *tokens;
1890 HbaToken *token;
1891 IdentLine *parsedline;
1892
1893 Assert(line != NIL);
1894 field = list_head(line);
1895
1896 parsedline = palloc0(sizeof(IdentLine));
1897 parsedline->linenumber = line_number;
1898
1899 /* Get the map token (must exist) */
1900 tokens = lfirst(field);
1901 IDENT_MULTI_VALUE(tokens);
1902 token = linitial(tokens);
1903 parsedline->usermap = pstrdup(token->string);
1904
1905 /* Get the ident user token */
1906 field = lnext(field);
1907 IDENT_FIELD_ABSENT(field);
1908 tokens = lfirst(field);
1909 IDENT_MULTI_VALUE(tokens);
1910 token = linitial(tokens);
1911 parsedline->ident_user = pstrdup(token->string);
1912
1913 /* Get the PG rolename token */
1914 field = lnext(field);
1915 IDENT_FIELD_ABSENT(field);
1916 tokens = lfirst(field);
1917 IDENT_MULTI_VALUE(tokens);
1918 token = linitial(tokens);
1919 parsedline->pg_role = pstrdup(token->string);
1920
1921 if (parsedline->ident_user[0] == '/')
1922 {
1923 /*
1924 * When system username starts with a slash, treat it as a regular
1925 * expression. Pre-compile it.
1926 */
1927 int r;
1928 pg_wchar *wstr;
1929 int wlen;
1930
1931 wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar));
1932 wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1,
1933 wstr, strlen(parsedline->ident_user + 1));
1934
1935 r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
1936 if (r)
1937 {
1938 char errstr[100];
1939
1940 pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
1941 ereport(LOG,
1942 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1943 errmsg("invalid regular expression \"%s\": %s",
1944 parsedline->ident_user + 1, errstr)));
1945
1946 pfree(wstr);
1947 return NULL;
1948 }
1949 pfree(wstr);
1950 }
1951
1952 return parsedline;
1953 }
1954
1955 /*
1956 * Process one line from the parsed ident config lines.
1957 *
1958 * Compare input parsed ident line to the needed map, pg_role and ident_user.
1959 * *found_p and *error_p are set according to our results.
1960 */
1961 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)1962 check_ident_usermap(IdentLine *identLine, const char *usermap_name,
1963 const char *pg_role, const char *ident_user,
1964 bool case_insensitive, bool *found_p, bool *error_p)
1965 {
1966 *found_p = false;
1967 *error_p = false;
1968
1969 if (strcmp(identLine->usermap, usermap_name) != 0)
1970 /* Line does not match the map name we're looking for, so just abort */
1971 return;
1972
1973 /* Match? */
1974 if (identLine->ident_user[0] == '/')
1975 {
1976 /*
1977 * When system username starts with a slash, treat it as a regular
1978 * expression. In this case, we process the system username as a
1979 * regular expression that returns exactly one match. This is replaced
1980 * for \1 in the database username string, if present.
1981 */
1982 int r;
1983 regmatch_t matches[2];
1984 pg_wchar *wstr;
1985 int wlen;
1986 char *ofs;
1987 char *regexp_pgrole;
1988
1989 wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
1990 wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
1991
1992 r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0);
1993 if (r)
1994 {
1995 char errstr[100];
1996
1997 if (r != REG_NOMATCH)
1998 {
1999 /* REG_NOMATCH is not an error, everything else is */
2000 pg_regerror(r, &identLine->re, errstr, sizeof(errstr));
2001 ereport(LOG,
2002 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2003 errmsg("regular expression match for \"%s\" failed: %s",
2004 identLine->ident_user + 1, errstr)));
2005 *error_p = true;
2006 }
2007
2008 pfree(wstr);
2009 return;
2010 }
2011 pfree(wstr);
2012
2013 if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL)
2014 {
2015 int offset;
2016
2017 /* substitution of the first argument requested */
2018 if (matches[1].rm_so < 0)
2019 {
2020 ereport(LOG,
2021 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2022 errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
2023 identLine->ident_user + 1, identLine->pg_role)));
2024 *error_p = true;
2025 return;
2026 }
2027
2028 /*
2029 * length: original length minus length of \1 plus length of match
2030 * plus null terminator
2031 */
2032 regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
2033 offset = ofs - identLine->pg_role;
2034 memcpy(regexp_pgrole, identLine->pg_role, offset);
2035 memcpy(regexp_pgrole + offset,
2036 ident_user + matches[1].rm_so,
2037 matches[1].rm_eo - matches[1].rm_so);
2038 strcat(regexp_pgrole, ofs + 2);
2039 }
2040 else
2041 {
2042 /* no substitution, so copy the match */
2043 regexp_pgrole = pstrdup(identLine->pg_role);
2044 }
2045
2046 /*
2047 * now check if the username actually matched what the user is trying
2048 * to connect as
2049 */
2050 if (case_insensitive)
2051 {
2052 if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
2053 *found_p = true;
2054 }
2055 else
2056 {
2057 if (strcmp(regexp_pgrole, pg_role) == 0)
2058 *found_p = true;
2059 }
2060 pfree(regexp_pgrole);
2061
2062 return;
2063 }
2064 else
2065 {
2066 /* Not regular expression, so make complete match */
2067 if (case_insensitive)
2068 {
2069 if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 &&
2070 pg_strcasecmp(identLine->ident_user, ident_user) == 0)
2071 *found_p = true;
2072 }
2073 else
2074 {
2075 if (strcmp(identLine->pg_role, pg_role) == 0 &&
2076 strcmp(identLine->ident_user, ident_user) == 0)
2077 *found_p = true;
2078 }
2079 }
2080 return;
2081 }
2082
2083
2084 /*
2085 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
2086 *
2087 * See if the user with ident username "auth_user" is allowed to act
2088 * as Postgres user "pg_role" according to usermap "usermap_name".
2089 *
2090 * Special case: Usermap NULL, equivalent to what was previously called
2091 * "sameuser" or "samerole", means don't look in the usermap file.
2092 * That's an implied map wherein "pg_role" must be identical to
2093 * "auth_user" in order to be authorized.
2094 *
2095 * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
2096 */
2097 int
check_usermap(const char * usermap_name,const char * pg_role,const char * auth_user,bool case_insensitive)2098 check_usermap(const char *usermap_name,
2099 const char *pg_role,
2100 const char *auth_user,
2101 bool case_insensitive)
2102 {
2103 bool found_entry = false,
2104 error = false;
2105
2106 if (usermap_name == NULL || usermap_name[0] == '\0')
2107 {
2108 if (case_insensitive)
2109 {
2110 if (pg_strcasecmp(pg_role, auth_user) == 0)
2111 return STATUS_OK;
2112 }
2113 else
2114 {
2115 if (strcmp(pg_role, auth_user) == 0)
2116 return STATUS_OK;
2117 }
2118 ereport(LOG,
2119 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
2120 pg_role, auth_user)));
2121 return STATUS_ERROR;
2122 }
2123 else
2124 {
2125 ListCell *line_cell;
2126
2127 foreach(line_cell, parsed_ident_lines)
2128 {
2129 check_ident_usermap(lfirst(line_cell), usermap_name,
2130 pg_role, auth_user, case_insensitive,
2131 &found_entry, &error);
2132 if (found_entry || error)
2133 break;
2134 }
2135 }
2136 if (!found_entry && !error)
2137 {
2138 ereport(LOG,
2139 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
2140 usermap_name, pg_role, auth_user)));
2141 }
2142 return found_entry ? STATUS_OK : STATUS_ERROR;
2143 }
2144
2145
2146 /*
2147 * Read the ident config file and create a List of IdentLine records for
2148 * the contents.
2149 *
2150 * This works the same as load_hba(), but for the user config file.
2151 */
2152 bool
load_ident(void)2153 load_ident(void)
2154 {
2155 FILE *file;
2156 List *ident_lines = NIL;
2157 List *ident_line_nums = NIL;
2158 ListCell *line_cell,
2159 *num_cell,
2160 *parsed_line_cell;
2161 List *new_parsed_lines = NIL;
2162 bool ok = true;
2163 MemoryContext linecxt;
2164 MemoryContext oldcxt;
2165 MemoryContext ident_context;
2166 IdentLine *newline;
2167
2168 file = AllocateFile(IdentFileName, "r");
2169 if (file == NULL)
2170 {
2171 /* not fatal ... we just won't do any special ident maps */
2172 ereport(LOG,
2173 (errcode_for_file_access(),
2174 errmsg("could not open usermap file \"%s\": %m",
2175 IdentFileName)));
2176 return false;
2177 }
2178
2179 linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums, NULL);
2180 FreeFile(file);
2181
2182 /* Now parse all the lines */
2183 Assert(PostmasterContext);
2184 ident_context = AllocSetContextCreate(PostmasterContext,
2185 "ident parser context",
2186 ALLOCSET_SMALL_SIZES);
2187 oldcxt = MemoryContextSwitchTo(ident_context);
2188 forboth(line_cell, ident_lines, num_cell, ident_line_nums)
2189 {
2190 if ((newline = parse_ident_line(lfirst(line_cell), lfirst_int(num_cell))) == NULL)
2191 {
2192 /*
2193 * Parse error in the file, so indicate there's a problem. Free
2194 * all the memory and regular expressions of lines parsed so far.
2195 */
2196 foreach(parsed_line_cell, new_parsed_lines)
2197 {
2198 newline = (IdentLine *) lfirst(parsed_line_cell);
2199 if (newline->ident_user[0] == '/')
2200 pg_regfree(&newline->re);
2201 }
2202 MemoryContextReset(ident_context);
2203 new_parsed_lines = NIL;
2204 ok = false;
2205
2206 /*
2207 * Keep parsing the rest of the file so we can report errors on
2208 * more than the first row. Error has already been reported in the
2209 * parsing function, so no need to log it here.
2210 */
2211 continue;
2212 }
2213
2214 new_parsed_lines = lappend(new_parsed_lines, newline);
2215 }
2216
2217 /* Free tokenizer memory */
2218 MemoryContextDelete(linecxt);
2219 MemoryContextSwitchTo(oldcxt);
2220
2221 if (!ok)
2222 {
2223 /* File contained one or more errors, so bail out */
2224 foreach(parsed_line_cell, new_parsed_lines)
2225 {
2226 newline = (IdentLine *) lfirst(parsed_line_cell);
2227 if (newline->ident_user[0] == '/')
2228 pg_regfree(&newline->re);
2229 }
2230 MemoryContextDelete(ident_context);
2231 return false;
2232 }
2233
2234 /* Loaded new file successfully, replace the one we use */
2235 if (parsed_ident_lines != NIL)
2236 {
2237 foreach(parsed_line_cell, parsed_ident_lines)
2238 {
2239 newline = (IdentLine *) lfirst(parsed_line_cell);
2240 if (newline->ident_user[0] == '/')
2241 pg_regfree(&newline->re);
2242 }
2243 }
2244 if (parsed_ident_context != NULL)
2245 MemoryContextDelete(parsed_ident_context);
2246
2247 parsed_ident_context = ident_context;
2248 parsed_ident_lines = new_parsed_lines;
2249
2250 return true;
2251 }
2252
2253
2254
2255 /*
2256 * Determine what authentication method should be used when accessing database
2257 * "database" from frontend "raddr", user "user". Return the method and
2258 * an optional argument (stored in fields of *port), and STATUS_OK.
2259 *
2260 * If the file does not contain any entry matching the request, we return
2261 * method = uaImplicitReject.
2262 */
2263 void
hba_getauthmethod(hbaPort * port)2264 hba_getauthmethod(hbaPort *port)
2265 {
2266 check_hba(port);
2267 }
2268