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-2020, 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/guc.h"
43 #include "utils/lsyscache.h"
44 #include "utils/memutils.h"
45 #include "utils/varlena.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 			 strcmp(token->string, "hostgssenc") == 0 ||
999 			 strcmp(token->string, "hostnogssenc") == 0)
1000 	{
1001 
1002 		if (token->string[4] == 's')	/* "hostssl" */
1003 		{
1004 			parsedline->conntype = ctHostSSL;
1005 			/* Log a warning if SSL support is not active */
1006 #ifdef USE_SSL
1007 			if (!EnableSSL)
1008 			{
1009 				ereport(elevel,
1010 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1011 						 errmsg("hostssl record cannot match because SSL is disabled"),
1012 						 errhint("Set ssl = on in postgresql.conf."),
1013 						 errcontext("line %d of configuration file \"%s\"",
1014 									line_num, HbaFileName)));
1015 				*err_msg = "hostssl record cannot match because SSL is disabled";
1016 			}
1017 #else
1018 			ereport(elevel,
1019 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1020 					 errmsg("hostssl record cannot match because SSL is not supported by this build"),
1021 					 errhint("Compile with --with-openssl to use SSL connections."),
1022 					 errcontext("line %d of configuration file \"%s\"",
1023 								line_num, HbaFileName)));
1024 			*err_msg = "hostssl record cannot match because SSL is not supported by this build";
1025 #endif
1026 		}
1027 		else if (token->string[4] == 'g')	/* "hostgssenc" */
1028 		{
1029 			parsedline->conntype = ctHostGSS;
1030 #ifndef ENABLE_GSS
1031 			ereport(elevel,
1032 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1033 					 errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
1034 					 errhint("Compile with --with-gssapi to use GSSAPI connections."),
1035 					 errcontext("line %d of configuration file \"%s\"",
1036 								line_num, HbaFileName)));
1037 			*err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
1038 #endif
1039 		}
1040 		else if (token->string[4] == 'n' && token->string[6] == 's')
1041 			parsedline->conntype = ctHostNoSSL;
1042 		else if (token->string[4] == 'n' && token->string[6] == 'g')
1043 			parsedline->conntype = ctHostNoGSS;
1044 		else
1045 		{
1046 			/* "host" */
1047 			parsedline->conntype = ctHost;
1048 		}
1049 	}							/* record type */
1050 	else
1051 	{
1052 		ereport(elevel,
1053 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1054 				 errmsg("invalid connection type \"%s\"",
1055 						token->string),
1056 				 errcontext("line %d of configuration file \"%s\"",
1057 							line_num, HbaFileName)));
1058 		*err_msg = psprintf("invalid connection type \"%s\"", token->string);
1059 		return NULL;
1060 	}
1061 
1062 	/* Get the databases. */
1063 	field = lnext(tok_line->fields, field);
1064 	if (!field)
1065 	{
1066 		ereport(elevel,
1067 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1068 				 errmsg("end-of-line before database specification"),
1069 				 errcontext("line %d of configuration file \"%s\"",
1070 							line_num, HbaFileName)));
1071 		*err_msg = "end-of-line before database specification";
1072 		return NULL;
1073 	}
1074 	parsedline->databases = NIL;
1075 	tokens = lfirst(field);
1076 	foreach(tokencell, tokens)
1077 	{
1078 		parsedline->databases = lappend(parsedline->databases,
1079 										copy_hba_token(lfirst(tokencell)));
1080 	}
1081 
1082 	/* Get the roles. */
1083 	field = lnext(tok_line->fields, field);
1084 	if (!field)
1085 	{
1086 		ereport(elevel,
1087 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1088 				 errmsg("end-of-line before role specification"),
1089 				 errcontext("line %d of configuration file \"%s\"",
1090 							line_num, HbaFileName)));
1091 		*err_msg = "end-of-line before role specification";
1092 		return NULL;
1093 	}
1094 	parsedline->roles = NIL;
1095 	tokens = lfirst(field);
1096 	foreach(tokencell, tokens)
1097 	{
1098 		parsedline->roles = lappend(parsedline->roles,
1099 									copy_hba_token(lfirst(tokencell)));
1100 	}
1101 
1102 	if (parsedline->conntype != ctLocal)
1103 	{
1104 		/* Read the IP address field. (with or without CIDR netmask) */
1105 		field = lnext(tok_line->fields, field);
1106 		if (!field)
1107 		{
1108 			ereport(elevel,
1109 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1110 					 errmsg("end-of-line before IP address specification"),
1111 					 errcontext("line %d of configuration file \"%s\"",
1112 								line_num, HbaFileName)));
1113 			*err_msg = "end-of-line before IP address specification";
1114 			return NULL;
1115 		}
1116 		tokens = lfirst(field);
1117 		if (tokens->length > 1)
1118 		{
1119 			ereport(elevel,
1120 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1121 					 errmsg("multiple values specified for host address"),
1122 					 errhint("Specify one address range per line."),
1123 					 errcontext("line %d of configuration file \"%s\"",
1124 								line_num, HbaFileName)));
1125 			*err_msg = "multiple values specified for host address";
1126 			return NULL;
1127 		}
1128 		token = linitial(tokens);
1129 
1130 		if (token_is_keyword(token, "all"))
1131 		{
1132 			parsedline->ip_cmp_method = ipCmpAll;
1133 		}
1134 		else if (token_is_keyword(token, "samehost"))
1135 		{
1136 			/* Any IP on this host is allowed to connect */
1137 			parsedline->ip_cmp_method = ipCmpSameHost;
1138 		}
1139 		else if (token_is_keyword(token, "samenet"))
1140 		{
1141 			/* Any IP on the host's subnets is allowed to connect */
1142 			parsedline->ip_cmp_method = ipCmpSameNet;
1143 		}
1144 		else
1145 		{
1146 			/* IP and netmask are specified */
1147 			parsedline->ip_cmp_method = ipCmpMask;
1148 
1149 			/* need a modifiable copy of token */
1150 			str = pstrdup(token->string);
1151 
1152 			/* Check if it has a CIDR suffix and if so isolate it */
1153 			cidr_slash = strchr(str, '/');
1154 			if (cidr_slash)
1155 				*cidr_slash = '\0';
1156 
1157 			/* Get the IP address either way */
1158 			hints.ai_flags = AI_NUMERICHOST;
1159 			hints.ai_family = AF_UNSPEC;
1160 			hints.ai_socktype = 0;
1161 			hints.ai_protocol = 0;
1162 			hints.ai_addrlen = 0;
1163 			hints.ai_canonname = NULL;
1164 			hints.ai_addr = NULL;
1165 			hints.ai_next = NULL;
1166 
1167 			ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
1168 			if (ret == 0 && gai_result)
1169 			{
1170 				memcpy(&parsedline->addr, gai_result->ai_addr,
1171 					   gai_result->ai_addrlen);
1172 				parsedline->addrlen = gai_result->ai_addrlen;
1173 			}
1174 			else if (ret == EAI_NONAME)
1175 				parsedline->hostname = str;
1176 			else
1177 			{
1178 				ereport(elevel,
1179 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1180 						 errmsg("invalid IP address \"%s\": %s",
1181 								str, gai_strerror(ret)),
1182 						 errcontext("line %d of configuration file \"%s\"",
1183 									line_num, HbaFileName)));
1184 				*err_msg = psprintf("invalid IP address \"%s\": %s",
1185 									str, gai_strerror(ret));
1186 				if (gai_result)
1187 					pg_freeaddrinfo_all(hints.ai_family, gai_result);
1188 				return NULL;
1189 			}
1190 
1191 			pg_freeaddrinfo_all(hints.ai_family, gai_result);
1192 
1193 			/* Get the netmask */
1194 			if (cidr_slash)
1195 			{
1196 				if (parsedline->hostname)
1197 				{
1198 					ereport(elevel,
1199 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1200 							 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
1201 									token->string),
1202 							 errcontext("line %d of configuration file \"%s\"",
1203 										line_num, HbaFileName)));
1204 					*err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
1205 										token->string);
1206 					return NULL;
1207 				}
1208 
1209 				if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
1210 										  parsedline->addr.ss_family) < 0)
1211 				{
1212 					ereport(elevel,
1213 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1214 							 errmsg("invalid CIDR mask in address \"%s\"",
1215 									token->string),
1216 							 errcontext("line %d of configuration file \"%s\"",
1217 										line_num, HbaFileName)));
1218 					*err_msg = psprintf("invalid CIDR mask in address \"%s\"",
1219 										token->string);
1220 					return NULL;
1221 				}
1222 				parsedline->masklen = parsedline->addrlen;
1223 				pfree(str);
1224 			}
1225 			else if (!parsedline->hostname)
1226 			{
1227 				/* Read the mask field. */
1228 				pfree(str);
1229 				field = lnext(tok_line->fields, field);
1230 				if (!field)
1231 				{
1232 					ereport(elevel,
1233 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1234 							 errmsg("end-of-line before netmask specification"),
1235 							 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
1236 							 errcontext("line %d of configuration file \"%s\"",
1237 										line_num, HbaFileName)));
1238 					*err_msg = "end-of-line before netmask specification";
1239 					return NULL;
1240 				}
1241 				tokens = lfirst(field);
1242 				if (tokens->length > 1)
1243 				{
1244 					ereport(elevel,
1245 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1246 							 errmsg("multiple values specified for netmask"),
1247 							 errcontext("line %d of configuration file \"%s\"",
1248 										line_num, HbaFileName)));
1249 					*err_msg = "multiple values specified for netmask";
1250 					return NULL;
1251 				}
1252 				token = linitial(tokens);
1253 
1254 				ret = pg_getaddrinfo_all(token->string, NULL,
1255 										 &hints, &gai_result);
1256 				if (ret || !gai_result)
1257 				{
1258 					ereport(elevel,
1259 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1260 							 errmsg("invalid IP mask \"%s\": %s",
1261 									token->string, gai_strerror(ret)),
1262 							 errcontext("line %d of configuration file \"%s\"",
1263 										line_num, HbaFileName)));
1264 					*err_msg = psprintf("invalid IP mask \"%s\": %s",
1265 										token->string, gai_strerror(ret));
1266 					if (gai_result)
1267 						pg_freeaddrinfo_all(hints.ai_family, gai_result);
1268 					return NULL;
1269 				}
1270 
1271 				memcpy(&parsedline->mask, gai_result->ai_addr,
1272 					   gai_result->ai_addrlen);
1273 				parsedline->masklen = gai_result->ai_addrlen;
1274 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
1275 
1276 				if (parsedline->addr.ss_family != parsedline->mask.ss_family)
1277 				{
1278 					ereport(elevel,
1279 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
1280 							 errmsg("IP address and mask do not match"),
1281 							 errcontext("line %d of configuration file \"%s\"",
1282 										line_num, HbaFileName)));
1283 					*err_msg = "IP address and mask do not match";
1284 					return NULL;
1285 				}
1286 			}
1287 		}
1288 	}							/* != ctLocal */
1289 
1290 	/* Get the authentication method */
1291 	field = lnext(tok_line->fields, field);
1292 	if (!field)
1293 	{
1294 		ereport(elevel,
1295 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1296 				 errmsg("end-of-line before authentication method"),
1297 				 errcontext("line %d of configuration file \"%s\"",
1298 							line_num, HbaFileName)));
1299 		*err_msg = "end-of-line before authentication method";
1300 		return NULL;
1301 	}
1302 	tokens = lfirst(field);
1303 	if (tokens->length > 1)
1304 	{
1305 		ereport(elevel,
1306 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1307 				 errmsg("multiple values specified for authentication type"),
1308 				 errhint("Specify exactly one authentication type per line."),
1309 				 errcontext("line %d of configuration file \"%s\"",
1310 							line_num, HbaFileName)));
1311 		*err_msg = "multiple values specified for authentication type";
1312 		return NULL;
1313 	}
1314 	token = linitial(tokens);
1315 
1316 	unsupauth = NULL;
1317 	if (strcmp(token->string, "trust") == 0)
1318 		parsedline->auth_method = uaTrust;
1319 	else if (strcmp(token->string, "ident") == 0)
1320 		parsedline->auth_method = uaIdent;
1321 	else if (strcmp(token->string, "peer") == 0)
1322 		parsedline->auth_method = uaPeer;
1323 	else if (strcmp(token->string, "password") == 0)
1324 		parsedline->auth_method = uaPassword;
1325 	else if (strcmp(token->string, "gss") == 0)
1326 #ifdef ENABLE_GSS
1327 		parsedline->auth_method = uaGSS;
1328 #else
1329 		unsupauth = "gss";
1330 #endif
1331 	else if (strcmp(token->string, "sspi") == 0)
1332 #ifdef ENABLE_SSPI
1333 		parsedline->auth_method = uaSSPI;
1334 #else
1335 		unsupauth = "sspi";
1336 #endif
1337 	else if (strcmp(token->string, "reject") == 0)
1338 		parsedline->auth_method = uaReject;
1339 	else if (strcmp(token->string, "md5") == 0)
1340 	{
1341 		if (Db_user_namespace)
1342 		{
1343 			ereport(elevel,
1344 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1345 					 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
1346 					 errcontext("line %d of configuration file \"%s\"",
1347 								line_num, HbaFileName)));
1348 			*err_msg = "MD5 authentication is not supported when \"db_user_namespace\" is enabled";
1349 			return NULL;
1350 		}
1351 		parsedline->auth_method = uaMD5;
1352 	}
1353 	else if (strcmp(token->string, "scram-sha-256") == 0)
1354 		parsedline->auth_method = uaSCRAM;
1355 	else if (strcmp(token->string, "pam") == 0)
1356 #ifdef USE_PAM
1357 		parsedline->auth_method = uaPAM;
1358 #else
1359 		unsupauth = "pam";
1360 #endif
1361 	else if (strcmp(token->string, "bsd") == 0)
1362 #ifdef USE_BSD_AUTH
1363 		parsedline->auth_method = uaBSD;
1364 #else
1365 		unsupauth = "bsd";
1366 #endif
1367 	else if (strcmp(token->string, "ldap") == 0)
1368 #ifdef USE_LDAP
1369 		parsedline->auth_method = uaLDAP;
1370 #else
1371 		unsupauth = "ldap";
1372 #endif
1373 	else if (strcmp(token->string, "cert") == 0)
1374 #ifdef USE_SSL
1375 		parsedline->auth_method = uaCert;
1376 #else
1377 		unsupauth = "cert";
1378 #endif
1379 	else if (strcmp(token->string, "radius") == 0)
1380 		parsedline->auth_method = uaRADIUS;
1381 	else
1382 	{
1383 		ereport(elevel,
1384 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1385 				 errmsg("invalid authentication method \"%s\"",
1386 						token->string),
1387 				 errcontext("line %d of configuration file \"%s\"",
1388 							line_num, HbaFileName)));
1389 		*err_msg = psprintf("invalid authentication method \"%s\"",
1390 							token->string);
1391 		return NULL;
1392 	}
1393 
1394 	if (unsupauth)
1395 	{
1396 		ereport(elevel,
1397 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1398 				 errmsg("invalid authentication method \"%s\": not supported by this build",
1399 						token->string),
1400 				 errcontext("line %d of configuration file \"%s\"",
1401 							line_num, HbaFileName)));
1402 		*err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
1403 							token->string);
1404 		return NULL;
1405 	}
1406 
1407 	/*
1408 	 * XXX: When using ident on local connections, change it to peer, for
1409 	 * backwards compatibility.
1410 	 */
1411 	if (parsedline->conntype == ctLocal &&
1412 		parsedline->auth_method == uaIdent)
1413 		parsedline->auth_method = uaPeer;
1414 
1415 	/* Invalid authentication combinations */
1416 	if (parsedline->conntype == ctLocal &&
1417 		parsedline->auth_method == uaGSS)
1418 	{
1419 		ereport(elevel,
1420 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1421 				 errmsg("gssapi authentication is not supported on local sockets"),
1422 				 errcontext("line %d of configuration file \"%s\"",
1423 							line_num, HbaFileName)));
1424 		*err_msg = "gssapi authentication is not supported on local sockets";
1425 		return NULL;
1426 	}
1427 
1428 	if (parsedline->conntype != ctLocal &&
1429 		parsedline->auth_method == uaPeer)
1430 	{
1431 		ereport(elevel,
1432 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1433 				 errmsg("peer authentication is only supported on local sockets"),
1434 				 errcontext("line %d of configuration file \"%s\"",
1435 							line_num, HbaFileName)));
1436 		*err_msg = "peer authentication is only supported on local sockets";
1437 		return NULL;
1438 	}
1439 
1440 	/*
1441 	 * SSPI authentication can never be enabled on ctLocal connections,
1442 	 * because it's only supported on Windows, where ctLocal isn't supported.
1443 	 */
1444 
1445 
1446 	if (parsedline->conntype != ctHostSSL &&
1447 		parsedline->auth_method == uaCert)
1448 	{
1449 		ereport(elevel,
1450 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
1451 				 errmsg("cert authentication is only supported on hostssl connections"),
1452 				 errcontext("line %d of configuration file \"%s\"",
1453 							line_num, HbaFileName)));
1454 		*err_msg = "cert authentication is only supported on hostssl connections";
1455 		return NULL;
1456 	}
1457 
1458 	/*
1459 	 * For GSS and SSPI, set the default value of include_realm to true.
1460 	 * Having include_realm set to false is dangerous in multi-realm
1461 	 * situations and is generally considered bad practice.  We keep the
1462 	 * capability around for backwards compatibility, but we might want to
1463 	 * remove it at some point in the future.  Users who still need to strip
1464 	 * the realm off would be better served by using an appropriate regex in a
1465 	 * pg_ident.conf mapping.
1466 	 */
1467 	if (parsedline->auth_method == uaGSS ||
1468 		parsedline->auth_method == uaSSPI)
1469 		parsedline->include_realm = true;
1470 
1471 	/*
1472 	 * For SSPI, include_realm defaults to the SAM-compatible domain (aka
1473 	 * NetBIOS name) and user names instead of the Kerberos principal name for
1474 	 * compatibility.
1475 	 */
1476 	if (parsedline->auth_method == uaSSPI)
1477 	{
1478 		parsedline->compat_realm = true;
1479 		parsedline->upn_username = false;
1480 	}
1481 
1482 	/* Parse remaining arguments */
1483 	while ((field = lnext(tok_line->fields, field)) != NULL)
1484 	{
1485 		tokens = lfirst(field);
1486 		foreach(tokencell, tokens)
1487 		{
1488 			char	   *val;
1489 
1490 			token = lfirst(tokencell);
1491 
1492 			str = pstrdup(token->string);
1493 			val = strchr(str, '=');
1494 			if (val == NULL)
1495 			{
1496 				/*
1497 				 * Got something that's not a name=value pair.
1498 				 */
1499 				ereport(elevel,
1500 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1501 						 errmsg("authentication option not in name=value format: %s", token->string),
1502 						 errcontext("line %d of configuration file \"%s\"",
1503 									line_num, HbaFileName)));
1504 				*err_msg = psprintf("authentication option not in name=value format: %s",
1505 									token->string);
1506 				return NULL;
1507 			}
1508 
1509 			*val++ = '\0';		/* str now holds "name", val holds "value" */
1510 			if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
1511 				/* parse_hba_auth_opt already logged the error message */
1512 				return NULL;
1513 			pfree(str);
1514 		}
1515 	}
1516 
1517 	/*
1518 	 * Check if the selected authentication method has any mandatory arguments
1519 	 * that are not set.
1520 	 */
1521 	if (parsedline->auth_method == uaLDAP)
1522 	{
1523 #ifndef HAVE_LDAP_INITIALIZE
1524 		/* Not mandatory for OpenLDAP, because it can use DNS SRV records */
1525 		MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1526 #endif
1527 
1528 		/*
1529 		 * LDAP can operate in two modes: either with a direct bind, using
1530 		 * ldapprefix and ldapsuffix, or using a search+bind, using
1531 		 * ldapbasedn, ldapbinddn, ldapbindpasswd and one of
1532 		 * ldapsearchattribute or ldapsearchfilter.  Disallow mixing these
1533 		 * parameters.
1534 		 */
1535 		if (parsedline->ldapprefix || parsedline->ldapsuffix)
1536 		{
1537 			if (parsedline->ldapbasedn ||
1538 				parsedline->ldapbinddn ||
1539 				parsedline->ldapbindpasswd ||
1540 				parsedline->ldapsearchattribute ||
1541 				parsedline->ldapsearchfilter)
1542 			{
1543 				ereport(elevel,
1544 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1545 						 errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix"),
1546 						 errcontext("line %d of configuration file \"%s\"",
1547 									line_num, HbaFileName)));
1548 				*err_msg = "cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix";
1549 				return NULL;
1550 			}
1551 		}
1552 		else if (!parsedline->ldapbasedn)
1553 		{
1554 			ereport(elevel,
1555 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1556 					 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1557 					 errcontext("line %d of configuration file \"%s\"",
1558 								line_num, HbaFileName)));
1559 			*err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
1560 			return NULL;
1561 		}
1562 
1563 		/*
1564 		 * When using search+bind, you can either use a simple attribute
1565 		 * (defaulting to "uid") or a fully custom search filter.  You can't
1566 		 * do both.
1567 		 */
1568 		if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter)
1569 		{
1570 			ereport(elevel,
1571 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1572 					 errmsg("cannot use ldapsearchattribute together with ldapsearchfilter"),
1573 					 errcontext("line %d of configuration file \"%s\"",
1574 								line_num, HbaFileName)));
1575 			*err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter";
1576 			return NULL;
1577 		}
1578 	}
1579 
1580 	if (parsedline->auth_method == uaRADIUS)
1581 	{
1582 		MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
1583 		MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
1584 
1585 		if (list_length(parsedline->radiusservers) < 1)
1586 		{
1587 			ereport(LOG,
1588 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1589 					 errmsg("list of RADIUS servers cannot be empty"),
1590 					 errcontext("line %d of configuration file \"%s\"",
1591 								line_num, HbaFileName)));
1592 			return NULL;
1593 		}
1594 
1595 		if (list_length(parsedline->radiussecrets) < 1)
1596 		{
1597 			ereport(LOG,
1598 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1599 					 errmsg("list of RADIUS secrets cannot be empty"),
1600 					 errcontext("line %d of configuration file \"%s\"",
1601 								line_num, HbaFileName)));
1602 			return NULL;
1603 		}
1604 
1605 		/*
1606 		 * Verify length of option lists - each can be 0 (except for secrets,
1607 		 * but that's already checked above), 1 (use the same value
1608 		 * everywhere) or the same as the number of servers.
1609 		 */
1610 		if (!verify_option_list_length(parsedline->radiussecrets,
1611 									   "RADIUS secrets",
1612 									   parsedline->radiusservers,
1613 									   "RADIUS servers",
1614 									   line_num))
1615 			return NULL;
1616 		if (!verify_option_list_length(parsedline->radiusports,
1617 									   "RADIUS ports",
1618 									   parsedline->radiusservers,
1619 									   "RADIUS servers",
1620 									   line_num))
1621 			return NULL;
1622 		if (!verify_option_list_length(parsedline->radiusidentifiers,
1623 									   "RADIUS identifiers",
1624 									   parsedline->radiusservers,
1625 									   "RADIUS servers",
1626 									   line_num))
1627 			return NULL;
1628 	}
1629 
1630 	/*
1631 	 * Enforce any parameters implied by other settings.
1632 	 */
1633 	if (parsedline->auth_method == uaCert)
1634 	{
1635 		parsedline->clientcert = clientCertCA;
1636 	}
1637 
1638 	return parsedline;
1639 }
1640 
1641 
1642 static bool
verify_option_list_length(List * options,const char * optionname,List * masters,const char * mastername,int line_num)1643 verify_option_list_length(List *options, const char *optionname, List *masters, const char *mastername, int line_num)
1644 {
1645 	if (list_length(options) == 0 ||
1646 		list_length(options) == 1 ||
1647 		list_length(options) == list_length(masters))
1648 		return true;
1649 
1650 	ereport(LOG,
1651 			(errcode(ERRCODE_CONFIG_FILE_ERROR),
1652 			 errmsg("the number of %s (%d) must be 1 or the same as the number of %s (%d)",
1653 					optionname,
1654 					list_length(options),
1655 					mastername,
1656 					list_length(masters)
1657 					),
1658 			 errcontext("line %d of configuration file \"%s\"",
1659 						line_num, HbaFileName)));
1660 	return false;
1661 }
1662 
1663 /*
1664  * Parse one name-value pair as an authentication option into the given
1665  * HbaLine.  Return true if we successfully parse the option, false if we
1666  * encounter an error.  In the event of an error, also log a message at
1667  * ereport level elevel, and store a message string into *err_msg.
1668  */
1669 static bool
parse_hba_auth_opt(char * name,char * val,HbaLine * hbaline,int elevel,char ** err_msg)1670 parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
1671 				   int elevel, char **err_msg)
1672 {
1673 	int			line_num = hbaline->linenumber;
1674 
1675 #ifdef USE_LDAP
1676 	hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
1677 #endif
1678 
1679 	if (strcmp(name, "map") == 0)
1680 	{
1681 		if (hbaline->auth_method != uaIdent &&
1682 			hbaline->auth_method != uaPeer &&
1683 			hbaline->auth_method != uaGSS &&
1684 			hbaline->auth_method != uaSSPI &&
1685 			hbaline->auth_method != uaCert)
1686 			INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
1687 		hbaline->usermap = pstrdup(val);
1688 	}
1689 	else if (strcmp(name, "clientcert") == 0)
1690 	{
1691 		if (hbaline->conntype != ctHostSSL)
1692 		{
1693 			ereport(elevel,
1694 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1695 					 errmsg("clientcert can only be configured for \"hostssl\" rows"),
1696 					 errcontext("line %d of configuration file \"%s\"",
1697 								line_num, HbaFileName)));
1698 			*err_msg = "clientcert can only be configured for \"hostssl\" rows";
1699 			return false;
1700 		}
1701 		if (strcmp(val, "1") == 0
1702 			|| strcmp(val, "verify-ca") == 0)
1703 		{
1704 			hbaline->clientcert = clientCertCA;
1705 		}
1706 		else if (strcmp(val, "verify-full") == 0)
1707 		{
1708 			hbaline->clientcert = clientCertFull;
1709 		}
1710 		else if (strcmp(val, "0") == 0
1711 				 || strcmp(val, "no-verify") == 0)
1712 		{
1713 			if (hbaline->auth_method == uaCert)
1714 			{
1715 				ereport(elevel,
1716 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1717 						 errmsg("clientcert cannot be set to \"no-verify\" when using \"cert\" authentication"),
1718 						 errcontext("line %d of configuration file \"%s\"",
1719 									line_num, HbaFileName)));
1720 				*err_msg = "clientcert cannot be set to \"no-verify\" when using \"cert\" authentication";
1721 				return false;
1722 			}
1723 			hbaline->clientcert = clientCertOff;
1724 		}
1725 		else
1726 		{
1727 			ereport(elevel,
1728 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1729 					 errmsg("invalid value for clientcert: \"%s\"", val),
1730 					 errcontext("line %d of configuration file \"%s\"",
1731 								line_num, HbaFileName)));
1732 			return false;
1733 		}
1734 	}
1735 	else if (strcmp(name, "pamservice") == 0)
1736 	{
1737 		REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
1738 		hbaline->pamservice = pstrdup(val);
1739 	}
1740 	else if (strcmp(name, "pam_use_hostname") == 0)
1741 	{
1742 		REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
1743 		if (strcmp(val, "1") == 0)
1744 			hbaline->pam_use_hostname = true;
1745 		else
1746 			hbaline->pam_use_hostname = false;
1747 
1748 	}
1749 	else if (strcmp(name, "ldapurl") == 0)
1750 	{
1751 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1752 		LDAPURLDesc *urldata;
1753 		int			rc;
1754 #endif
1755 
1756 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
1757 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1758 		rc = ldap_url_parse(val, &urldata);
1759 		if (rc != LDAP_SUCCESS)
1760 		{
1761 			ereport(elevel,
1762 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1763 					 errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
1764 			*err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
1765 								val, ldap_err2string(rc));
1766 			return false;
1767 		}
1768 
1769 		if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
1770 			strcmp(urldata->lud_scheme, "ldaps") != 0)
1771 		{
1772 			ereport(elevel,
1773 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1774 					 errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
1775 			*err_msg = psprintf("unsupported LDAP URL scheme: %s",
1776 								urldata->lud_scheme);
1777 			ldap_free_urldesc(urldata);
1778 			return false;
1779 		}
1780 
1781 		if (urldata->lud_scheme)
1782 			hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
1783 		if (urldata->lud_host)
1784 			hbaline->ldapserver = pstrdup(urldata->lud_host);
1785 		hbaline->ldapport = urldata->lud_port;
1786 		if (urldata->lud_dn)
1787 			hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
1788 
1789 		if (urldata->lud_attrs)
1790 			hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]);	/* only use first one */
1791 		hbaline->ldapscope = urldata->lud_scope;
1792 		if (urldata->lud_filter)
1793 			hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter);
1794 		ldap_free_urldesc(urldata);
1795 #else							/* not OpenLDAP */
1796 		ereport(elevel,
1797 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1798 				 errmsg("LDAP URLs not supported on this platform")));
1799 		*err_msg = "LDAP URLs not supported on this platform";
1800 #endif							/* not OpenLDAP */
1801 	}
1802 	else if (strcmp(name, "ldaptls") == 0)
1803 	{
1804 		REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
1805 		if (strcmp(val, "1") == 0)
1806 			hbaline->ldaptls = true;
1807 		else
1808 			hbaline->ldaptls = false;
1809 	}
1810 	else if (strcmp(name, "ldapscheme") == 0)
1811 	{
1812 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
1813 		if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
1814 			ereport(elevel,
1815 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1816 					 errmsg("invalid ldapscheme value: \"%s\"", val),
1817 					 errcontext("line %d of configuration file \"%s\"",
1818 								line_num, HbaFileName)));
1819 		hbaline->ldapscheme = pstrdup(val);
1820 	}
1821 	else if (strcmp(name, "ldapserver") == 0)
1822 	{
1823 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
1824 		hbaline->ldapserver = pstrdup(val);
1825 	}
1826 	else if (strcmp(name, "ldapport") == 0)
1827 	{
1828 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
1829 		hbaline->ldapport = atoi(val);
1830 		if (hbaline->ldapport == 0)
1831 		{
1832 			ereport(elevel,
1833 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1834 					 errmsg("invalid LDAP port number: \"%s\"", val),
1835 					 errcontext("line %d of configuration file \"%s\"",
1836 								line_num, HbaFileName)));
1837 			*err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
1838 			return false;
1839 		}
1840 	}
1841 	else if (strcmp(name, "ldapbinddn") == 0)
1842 	{
1843 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
1844 		hbaline->ldapbinddn = pstrdup(val);
1845 	}
1846 	else if (strcmp(name, "ldapbindpasswd") == 0)
1847 	{
1848 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
1849 		hbaline->ldapbindpasswd = pstrdup(val);
1850 	}
1851 	else if (strcmp(name, "ldapsearchattribute") == 0)
1852 	{
1853 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
1854 		hbaline->ldapsearchattribute = pstrdup(val);
1855 	}
1856 	else if (strcmp(name, "ldapsearchfilter") == 0)
1857 	{
1858 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter", "ldap");
1859 		hbaline->ldapsearchfilter = pstrdup(val);
1860 	}
1861 	else if (strcmp(name, "ldapbasedn") == 0)
1862 	{
1863 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
1864 		hbaline->ldapbasedn = pstrdup(val);
1865 	}
1866 	else if (strcmp(name, "ldapprefix") == 0)
1867 	{
1868 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
1869 		hbaline->ldapprefix = pstrdup(val);
1870 	}
1871 	else if (strcmp(name, "ldapsuffix") == 0)
1872 	{
1873 		REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
1874 		hbaline->ldapsuffix = pstrdup(val);
1875 	}
1876 	else if (strcmp(name, "krb_realm") == 0)
1877 	{
1878 		if (hbaline->auth_method != uaGSS &&
1879 			hbaline->auth_method != uaSSPI)
1880 			INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
1881 		hbaline->krb_realm = pstrdup(val);
1882 	}
1883 	else if (strcmp(name, "include_realm") == 0)
1884 	{
1885 		if (hbaline->auth_method != uaGSS &&
1886 			hbaline->auth_method != uaSSPI)
1887 			INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
1888 		if (strcmp(val, "1") == 0)
1889 			hbaline->include_realm = true;
1890 		else
1891 			hbaline->include_realm = false;
1892 	}
1893 	else if (strcmp(name, "compat_realm") == 0)
1894 	{
1895 		if (hbaline->auth_method != uaSSPI)
1896 			INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
1897 		if (strcmp(val, "1") == 0)
1898 			hbaline->compat_realm = true;
1899 		else
1900 			hbaline->compat_realm = false;
1901 	}
1902 	else if (strcmp(name, "upn_username") == 0)
1903 	{
1904 		if (hbaline->auth_method != uaSSPI)
1905 			INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
1906 		if (strcmp(val, "1") == 0)
1907 			hbaline->upn_username = true;
1908 		else
1909 			hbaline->upn_username = false;
1910 	}
1911 	else if (strcmp(name, "radiusservers") == 0)
1912 	{
1913 		struct addrinfo *gai_result;
1914 		struct addrinfo hints;
1915 		int			ret;
1916 		List	   *parsed_servers;
1917 		ListCell   *l;
1918 		char	   *dupval = pstrdup(val);
1919 
1920 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
1921 
1922 		if (!SplitGUCList(dupval, ',', &parsed_servers))
1923 		{
1924 			/* syntax error in list */
1925 			ereport(elevel,
1926 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1927 					 errmsg("could not parse RADIUS server list \"%s\"",
1928 							val),
1929 					 errcontext("line %d of configuration file \"%s\"",
1930 								line_num, HbaFileName)));
1931 			return false;
1932 		}
1933 
1934 		/* For each entry in the list, translate it */
1935 		foreach(l, parsed_servers)
1936 		{
1937 			MemSet(&hints, 0, sizeof(hints));
1938 			hints.ai_socktype = SOCK_DGRAM;
1939 			hints.ai_family = AF_UNSPEC;
1940 
1941 			ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
1942 			if (ret || !gai_result)
1943 			{
1944 				ereport(elevel,
1945 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1946 						 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
1947 								(char *) lfirst(l), gai_strerror(ret)),
1948 						 errcontext("line %d of configuration file \"%s\"",
1949 									line_num, HbaFileName)));
1950 				if (gai_result)
1951 					pg_freeaddrinfo_all(hints.ai_family, gai_result);
1952 
1953 				list_free(parsed_servers);
1954 				return false;
1955 			}
1956 			pg_freeaddrinfo_all(hints.ai_family, gai_result);
1957 		}
1958 
1959 		/* All entries are OK, so store them */
1960 		hbaline->radiusservers = parsed_servers;
1961 		hbaline->radiusservers_s = pstrdup(val);
1962 	}
1963 	else if (strcmp(name, "radiusports") == 0)
1964 	{
1965 		List	   *parsed_ports;
1966 		ListCell   *l;
1967 		char	   *dupval = pstrdup(val);
1968 
1969 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
1970 
1971 		if (!SplitGUCList(dupval, ',', &parsed_ports))
1972 		{
1973 			ereport(elevel,
1974 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1975 					 errmsg("could not parse RADIUS port list \"%s\"",
1976 							val),
1977 					 errcontext("line %d of configuration file \"%s\"",
1978 								line_num, HbaFileName)));
1979 			*err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
1980 			return false;
1981 		}
1982 
1983 		foreach(l, parsed_ports)
1984 		{
1985 			if (atoi(lfirst(l)) == 0)
1986 			{
1987 				ereport(elevel,
1988 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
1989 						 errmsg("invalid RADIUS port number: \"%s\"", val),
1990 						 errcontext("line %d of configuration file \"%s\"",
1991 									line_num, HbaFileName)));
1992 
1993 				return false;
1994 			}
1995 		}
1996 		hbaline->radiusports = parsed_ports;
1997 		hbaline->radiusports_s = pstrdup(val);
1998 	}
1999 	else if (strcmp(name, "radiussecrets") == 0)
2000 	{
2001 		List	   *parsed_secrets;
2002 		char	   *dupval = pstrdup(val);
2003 
2004 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
2005 
2006 		if (!SplitGUCList(dupval, ',', &parsed_secrets))
2007 		{
2008 			/* syntax error in list */
2009 			ereport(elevel,
2010 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
2011 					 errmsg("could not parse RADIUS secret list \"%s\"",
2012 							val),
2013 					 errcontext("line %d of configuration file \"%s\"",
2014 								line_num, HbaFileName)));
2015 			return false;
2016 		}
2017 
2018 		hbaline->radiussecrets = parsed_secrets;
2019 		hbaline->radiussecrets_s = pstrdup(val);
2020 	}
2021 	else if (strcmp(name, "radiusidentifiers") == 0)
2022 	{
2023 		List	   *parsed_identifiers;
2024 		char	   *dupval = pstrdup(val);
2025 
2026 		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
2027 
2028 		if (!SplitGUCList(dupval, ',', &parsed_identifiers))
2029 		{
2030 			/* syntax error in list */
2031 			ereport(elevel,
2032 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
2033 					 errmsg("could not parse RADIUS identifiers list \"%s\"",
2034 							val),
2035 					 errcontext("line %d of configuration file \"%s\"",
2036 								line_num, HbaFileName)));
2037 			return false;
2038 		}
2039 
2040 		hbaline->radiusidentifiers = parsed_identifiers;
2041 		hbaline->radiusidentifiers_s = pstrdup(val);
2042 	}
2043 	else
2044 	{
2045 		ereport(elevel,
2046 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
2047 				 errmsg("unrecognized authentication option name: \"%s\"",
2048 						name),
2049 				 errcontext("line %d of configuration file \"%s\"",
2050 							line_num, HbaFileName)));
2051 		*err_msg = psprintf("unrecognized authentication option name: \"%s\"",
2052 							name);
2053 		return false;
2054 	}
2055 	return true;
2056 }
2057 
2058 /*
2059  *	Scan the pre-parsed hba file, looking for a match to the port's connection
2060  *	request.
2061  */
2062 static void
check_hba(hbaPort * port)2063 check_hba(hbaPort *port)
2064 {
2065 	Oid			roleid;
2066 	ListCell   *line;
2067 	HbaLine    *hba;
2068 
2069 	/* Get the target role's OID.  Note we do not error out for bad role. */
2070 	roleid = get_role_oid(port->user_name, true);
2071 
2072 	foreach(line, parsed_hba_lines)
2073 	{
2074 		hba = (HbaLine *) lfirst(line);
2075 
2076 		/* Check connection type */
2077 		if (hba->conntype == ctLocal)
2078 		{
2079 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
2080 				continue;
2081 		}
2082 		else
2083 		{
2084 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
2085 				continue;
2086 
2087 			/* Check SSL state */
2088 			if (port->ssl_in_use)
2089 			{
2090 				/* Connection is SSL, match both "host" and "hostssl" */
2091 				if (hba->conntype == ctHostNoSSL)
2092 					continue;
2093 			}
2094 			else
2095 			{
2096 				/* Connection is not SSL, match both "host" and "hostnossl" */
2097 				if (hba->conntype == ctHostSSL)
2098 					continue;
2099 			}
2100 
2101 			/* Check GSSAPI state */
2102 #ifdef ENABLE_GSS
2103 			if (port->gss && port->gss->enc &&
2104 				hba->conntype == ctHostNoGSS)
2105 				continue;
2106 			else if (!(port->gss && port->gss->enc) &&
2107 					 hba->conntype == ctHostGSS)
2108 				continue;
2109 #else
2110 			if (hba->conntype == ctHostGSS)
2111 				continue;
2112 #endif
2113 
2114 			/* Check IP address */
2115 			switch (hba->ip_cmp_method)
2116 			{
2117 				case ipCmpMask:
2118 					if (hba->hostname)
2119 					{
2120 						if (!check_hostname(port,
2121 											hba->hostname))
2122 							continue;
2123 					}
2124 					else
2125 					{
2126 						if (!check_ip(&port->raddr,
2127 									  (struct sockaddr *) &hba->addr,
2128 									  (struct sockaddr *) &hba->mask))
2129 							continue;
2130 					}
2131 					break;
2132 				case ipCmpAll:
2133 					break;
2134 				case ipCmpSameHost:
2135 				case ipCmpSameNet:
2136 					if (!check_same_host_or_net(&port->raddr,
2137 												hba->ip_cmp_method))
2138 						continue;
2139 					break;
2140 				default:
2141 					/* shouldn't get here, but deem it no-match if so */
2142 					continue;
2143 			}
2144 		}						/* != ctLocal */
2145 
2146 		/* Check database and role */
2147 		if (!check_db(port->database_name, port->user_name, roleid,
2148 					  hba->databases))
2149 			continue;
2150 
2151 		if (!check_role(port->user_name, roleid, hba->roles))
2152 			continue;
2153 
2154 		/* Found a record that matched! */
2155 		port->hba = hba;
2156 		return;
2157 	}
2158 
2159 	/* If no matching entry was found, then implicitly reject. */
2160 	hba = palloc0(sizeof(HbaLine));
2161 	hba->auth_method = uaImplicitReject;
2162 	port->hba = hba;
2163 }
2164 
2165 /*
2166  * Read the config file and create a List of HbaLine records for the contents.
2167  *
2168  * The configuration is read into a temporary list, and if any parse error
2169  * occurs the old list is kept in place and false is returned.  Only if the
2170  * whole file parses OK is the list replaced, and the function returns true.
2171  *
2172  * On a false result, caller will take care of reporting a FATAL error in case
2173  * this is the initial startup.  If it happens on reload, we just keep running
2174  * with the old data.
2175  */
2176 bool
load_hba(void)2177 load_hba(void)
2178 {
2179 	FILE	   *file;
2180 	List	   *hba_lines = NIL;
2181 	ListCell   *line;
2182 	List	   *new_parsed_lines = NIL;
2183 	bool		ok = true;
2184 	MemoryContext linecxt;
2185 	MemoryContext oldcxt;
2186 	MemoryContext hbacxt;
2187 
2188 	file = AllocateFile(HbaFileName, "r");
2189 	if (file == NULL)
2190 	{
2191 		ereport(LOG,
2192 				(errcode_for_file_access(),
2193 				 errmsg("could not open configuration file \"%s\": %m",
2194 						HbaFileName)));
2195 		return false;
2196 	}
2197 
2198 	linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG);
2199 	FreeFile(file);
2200 
2201 	/* Now parse all the lines */
2202 	Assert(PostmasterContext);
2203 	hbacxt = AllocSetContextCreate(PostmasterContext,
2204 								   "hba parser context",
2205 								   ALLOCSET_SMALL_SIZES);
2206 	oldcxt = MemoryContextSwitchTo(hbacxt);
2207 	foreach(line, hba_lines)
2208 	{
2209 		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
2210 		HbaLine    *newline;
2211 
2212 		/* don't parse lines that already have errors */
2213 		if (tok_line->err_msg != NULL)
2214 		{
2215 			ok = false;
2216 			continue;
2217 		}
2218 
2219 		if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
2220 		{
2221 			/* Parse error; remember there's trouble */
2222 			ok = false;
2223 
2224 			/*
2225 			 * Keep parsing the rest of the file so we can report errors on
2226 			 * more than the first line.  Error has already been logged, no
2227 			 * need for more chatter here.
2228 			 */
2229 			continue;
2230 		}
2231 
2232 		new_parsed_lines = lappend(new_parsed_lines, newline);
2233 	}
2234 
2235 	/*
2236 	 * A valid HBA file must have at least one entry; else there's no way to
2237 	 * connect to the postmaster.  But only complain about this if we didn't
2238 	 * already have parsing errors.
2239 	 */
2240 	if (ok && new_parsed_lines == NIL)
2241 	{
2242 		ereport(LOG,
2243 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
2244 				 errmsg("configuration file \"%s\" contains no entries",
2245 						HbaFileName)));
2246 		ok = false;
2247 	}
2248 
2249 	/* Free tokenizer memory */
2250 	MemoryContextDelete(linecxt);
2251 	MemoryContextSwitchTo(oldcxt);
2252 
2253 	if (!ok)
2254 	{
2255 		/* File contained one or more errors, so bail out */
2256 		MemoryContextDelete(hbacxt);
2257 		return false;
2258 	}
2259 
2260 	/* Loaded new file successfully, replace the one we use */
2261 	if (parsed_hba_context != NULL)
2262 		MemoryContextDelete(parsed_hba_context);
2263 	parsed_hba_context = hbacxt;
2264 	parsed_hba_lines = new_parsed_lines;
2265 
2266 	return true;
2267 }
2268 
2269 /*
2270  * This macro specifies the maximum number of authentication options
2271  * that are possible with any given authentication method that is supported.
2272  * Currently LDAP supports 11, and there are 3 that are not dependent on
2273  * the auth method here.  It may not actually be possible to set all of them
2274  * at the same time, but we'll set the macro value high enough to be
2275  * conservative and avoid warnings from static analysis tools.
2276  */
2277 #define MAX_HBA_OPTIONS 14
2278 
2279 /*
2280  * Create a text array listing the options specified in the HBA line.
2281  * Return NULL if no options are specified.
2282  */
2283 static ArrayType *
gethba_options(HbaLine * hba)2284 gethba_options(HbaLine *hba)
2285 {
2286 	int			noptions;
2287 	Datum		options[MAX_HBA_OPTIONS];
2288 
2289 	noptions = 0;
2290 
2291 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
2292 	{
2293 		if (hba->include_realm)
2294 			options[noptions++] =
2295 				CStringGetTextDatum("include_realm=true");
2296 
2297 		if (hba->krb_realm)
2298 			options[noptions++] =
2299 				CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm));
2300 	}
2301 
2302 	if (hba->usermap)
2303 		options[noptions++] =
2304 			CStringGetTextDatum(psprintf("map=%s", hba->usermap));
2305 
2306 	if (hba->clientcert != clientCertOff)
2307 		options[noptions++] =
2308 			CStringGetTextDatum(psprintf("clientcert=%s", (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full"));
2309 
2310 	if (hba->pamservice)
2311 		options[noptions++] =
2312 			CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice));
2313 
2314 	if (hba->auth_method == uaLDAP)
2315 	{
2316 		if (hba->ldapserver)
2317 			options[noptions++] =
2318 				CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver));
2319 
2320 		if (hba->ldapport)
2321 			options[noptions++] =
2322 				CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport));
2323 
2324 		if (hba->ldaptls)
2325 			options[noptions++] =
2326 				CStringGetTextDatum("ldaptls=true");
2327 
2328 		if (hba->ldapprefix)
2329 			options[noptions++] =
2330 				CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix));
2331 
2332 		if (hba->ldapsuffix)
2333 			options[noptions++] =
2334 				CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix));
2335 
2336 		if (hba->ldapbasedn)
2337 			options[noptions++] =
2338 				CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn));
2339 
2340 		if (hba->ldapbinddn)
2341 			options[noptions++] =
2342 				CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn));
2343 
2344 		if (hba->ldapbindpasswd)
2345 			options[noptions++] =
2346 				CStringGetTextDatum(psprintf("ldapbindpasswd=%s",
2347 											 hba->ldapbindpasswd));
2348 
2349 		if (hba->ldapsearchattribute)
2350 			options[noptions++] =
2351 				CStringGetTextDatum(psprintf("ldapsearchattribute=%s",
2352 											 hba->ldapsearchattribute));
2353 
2354 		if (hba->ldapsearchfilter)
2355 			options[noptions++] =
2356 				CStringGetTextDatum(psprintf("ldapsearchfilter=%s",
2357 											 hba->ldapsearchfilter));
2358 
2359 		if (hba->ldapscope)
2360 			options[noptions++] =
2361 				CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
2362 	}
2363 
2364 	if (hba->auth_method == uaRADIUS)
2365 	{
2366 		if (hba->radiusservers_s)
2367 			options[noptions++] =
2368 				CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
2369 
2370 		if (hba->radiussecrets_s)
2371 			options[noptions++] =
2372 				CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
2373 
2374 		if (hba->radiusidentifiers_s)
2375 			options[noptions++] =
2376 				CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
2377 
2378 		if (hba->radiusports_s)
2379 			options[noptions++] =
2380 				CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
2381 	}
2382 
2383 	/* If you add more options, consider increasing MAX_HBA_OPTIONS. */
2384 	Assert(noptions <= MAX_HBA_OPTIONS);
2385 
2386 	if (noptions > 0)
2387 		return construct_array(options, noptions, TEXTOID, -1, false, TYPALIGN_INT);
2388 	else
2389 		return NULL;
2390 }
2391 
2392 /* Number of columns in pg_hba_file_rules view */
2393 #define NUM_PG_HBA_FILE_RULES_ATTS	 9
2394 
2395 /*
2396  * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore
2397  *
2398  * tuple_store: where to store data
2399  * tupdesc: tuple descriptor for the view
2400  * lineno: pg_hba.conf line number (must always be valid)
2401  * hba: parsed line data (can be NULL, in which case err_msg should be set)
2402  * err_msg: error message (NULL if none)
2403  *
2404  * Note: leaks memory, but we don't care since this is run in a short-lived
2405  * memory context.
2406  */
2407 static void
fill_hba_line(Tuplestorestate * tuple_store,TupleDesc tupdesc,int lineno,HbaLine * hba,const char * err_msg)2408 fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
2409 			  int lineno, HbaLine *hba, const char *err_msg)
2410 {
2411 	Datum		values[NUM_PG_HBA_FILE_RULES_ATTS];
2412 	bool		nulls[NUM_PG_HBA_FILE_RULES_ATTS];
2413 	char		buffer[NI_MAXHOST];
2414 	HeapTuple	tuple;
2415 	int			index;
2416 	ListCell   *lc;
2417 	const char *typestr;
2418 	const char *addrstr;
2419 	const char *maskstr;
2420 	ArrayType  *options;
2421 
2422 	Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS);
2423 
2424 	memset(values, 0, sizeof(values));
2425 	memset(nulls, 0, sizeof(nulls));
2426 	index = 0;
2427 
2428 	/* line_number */
2429 	values[index++] = Int32GetDatum(lineno);
2430 
2431 	if (hba != NULL)
2432 	{
2433 		/* type */
2434 		/* Avoid a default: case so compiler will warn about missing cases */
2435 		typestr = NULL;
2436 		switch (hba->conntype)
2437 		{
2438 			case ctLocal:
2439 				typestr = "local";
2440 				break;
2441 			case ctHost:
2442 				typestr = "host";
2443 				break;
2444 			case ctHostSSL:
2445 				typestr = "hostssl";
2446 				break;
2447 			case ctHostNoSSL:
2448 				typestr = "hostnossl";
2449 				break;
2450 			case ctHostGSS:
2451 				typestr = "hostgssenc";
2452 				break;
2453 			case ctHostNoGSS:
2454 				typestr = "hostnogssenc";
2455 				break;
2456 		}
2457 		if (typestr)
2458 			values[index++] = CStringGetTextDatum(typestr);
2459 		else
2460 			nulls[index++] = true;
2461 
2462 		/* database */
2463 		if (hba->databases)
2464 		{
2465 			/*
2466 			 * Flatten HbaToken list to string list.  It might seem that we
2467 			 * should re-quote any quoted tokens, but that has been rejected
2468 			 * on the grounds that it makes it harder to compare the array
2469 			 * elements to other system catalogs.  That makes entries like
2470 			 * "all" or "samerole" formally ambiguous ... but users who name
2471 			 * databases/roles that way are inflicting their own pain.
2472 			 */
2473 			List	   *names = NIL;
2474 
2475 			foreach(lc, hba->databases)
2476 			{
2477 				HbaToken   *tok = lfirst(lc);
2478 
2479 				names = lappend(names, tok->string);
2480 			}
2481 			values[index++] = PointerGetDatum(strlist_to_textarray(names));
2482 		}
2483 		else
2484 			nulls[index++] = true;
2485 
2486 		/* user */
2487 		if (hba->roles)
2488 		{
2489 			/* Flatten HbaToken list to string list; see comment above */
2490 			List	   *roles = NIL;
2491 
2492 			foreach(lc, hba->roles)
2493 			{
2494 				HbaToken   *tok = lfirst(lc);
2495 
2496 				roles = lappend(roles, tok->string);
2497 			}
2498 			values[index++] = PointerGetDatum(strlist_to_textarray(roles));
2499 		}
2500 		else
2501 			nulls[index++] = true;
2502 
2503 		/* address and netmask */
2504 		/* Avoid a default: case so compiler will warn about missing cases */
2505 		addrstr = maskstr = NULL;
2506 		switch (hba->ip_cmp_method)
2507 		{
2508 			case ipCmpMask:
2509 				if (hba->hostname)
2510 				{
2511 					addrstr = hba->hostname;
2512 				}
2513 				else
2514 				{
2515 					/*
2516 					 * Note: if pg_getnameinfo_all fails, it'll set buffer to
2517 					 * "???", which we want to return.
2518 					 */
2519 					if (hba->addrlen > 0)
2520 					{
2521 						if (pg_getnameinfo_all(&hba->addr, hba->addrlen,
2522 											   buffer, sizeof(buffer),
2523 											   NULL, 0,
2524 											   NI_NUMERICHOST) == 0)
2525 							clean_ipv6_addr(hba->addr.ss_family, buffer);
2526 						addrstr = pstrdup(buffer);
2527 					}
2528 					if (hba->masklen > 0)
2529 					{
2530 						if (pg_getnameinfo_all(&hba->mask, hba->masklen,
2531 											   buffer, sizeof(buffer),
2532 											   NULL, 0,
2533 											   NI_NUMERICHOST) == 0)
2534 							clean_ipv6_addr(hba->mask.ss_family, buffer);
2535 						maskstr = pstrdup(buffer);
2536 					}
2537 				}
2538 				break;
2539 			case ipCmpAll:
2540 				addrstr = "all";
2541 				break;
2542 			case ipCmpSameHost:
2543 				addrstr = "samehost";
2544 				break;
2545 			case ipCmpSameNet:
2546 				addrstr = "samenet";
2547 				break;
2548 		}
2549 		if (addrstr)
2550 			values[index++] = CStringGetTextDatum(addrstr);
2551 		else
2552 			nulls[index++] = true;
2553 		if (maskstr)
2554 			values[index++] = CStringGetTextDatum(maskstr);
2555 		else
2556 			nulls[index++] = true;
2557 
2558 		/*
2559 		 * Make sure UserAuthName[] tracks additions to the UserAuth enum
2560 		 */
2561 		StaticAssertStmt(lengthof(UserAuthName) == USER_AUTH_LAST + 1,
2562 						 "UserAuthName[] must match the UserAuth enum");
2563 
2564 		/* auth_method */
2565 		values[index++] = CStringGetTextDatum(UserAuthName[hba->auth_method]);
2566 
2567 		/* options */
2568 		options = gethba_options(hba);
2569 		if (options)
2570 			values[index++] = PointerGetDatum(options);
2571 		else
2572 			nulls[index++] = true;
2573 	}
2574 	else
2575 	{
2576 		/* no parsing result, so set relevant fields to nulls */
2577 		memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool));
2578 	}
2579 
2580 	/* error */
2581 	if (err_msg)
2582 		values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg);
2583 	else
2584 		nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true;
2585 
2586 	tuple = heap_form_tuple(tupdesc, values, nulls);
2587 	tuplestore_puttuple(tuple_store, tuple);
2588 }
2589 
2590 /*
2591  * Read the pg_hba.conf file and fill the tuplestore with view records.
2592  */
2593 static void
fill_hba_view(Tuplestorestate * tuple_store,TupleDesc tupdesc)2594 fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
2595 {
2596 	FILE	   *file;
2597 	List	   *hba_lines = NIL;
2598 	ListCell   *line;
2599 	MemoryContext linecxt;
2600 	MemoryContext hbacxt;
2601 	MemoryContext oldcxt;
2602 
2603 	/*
2604 	 * In the unlikely event that we can't open pg_hba.conf, we throw an
2605 	 * error, rather than trying to report it via some sort of view entry.
2606 	 * (Most other error conditions should result in a message in a view
2607 	 * entry.)
2608 	 */
2609 	file = AllocateFile(HbaFileName, "r");
2610 	if (file == NULL)
2611 		ereport(ERROR,
2612 				(errcode_for_file_access(),
2613 				 errmsg("could not open configuration file \"%s\": %m",
2614 						HbaFileName)));
2615 
2616 	linecxt = tokenize_file(HbaFileName, file, &hba_lines, DEBUG3);
2617 	FreeFile(file);
2618 
2619 	/* Now parse all the lines */
2620 	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
2621 								   "hba parser context",
2622 								   ALLOCSET_SMALL_SIZES);
2623 	oldcxt = MemoryContextSwitchTo(hbacxt);
2624 	foreach(line, hba_lines)
2625 	{
2626 		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
2627 		HbaLine    *hbaline = NULL;
2628 
2629 		/* don't parse lines that already have errors */
2630 		if (tok_line->err_msg == NULL)
2631 			hbaline = parse_hba_line(tok_line, DEBUG3);
2632 
2633 		fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
2634 					  hbaline, tok_line->err_msg);
2635 	}
2636 
2637 	/* Free tokenizer memory */
2638 	MemoryContextDelete(linecxt);
2639 	/* Free parse_hba_line memory */
2640 	MemoryContextSwitchTo(oldcxt);
2641 	MemoryContextDelete(hbacxt);
2642 }
2643 
2644 /*
2645  * SQL-accessible SRF to return all the entries in the pg_hba.conf file.
2646  */
2647 Datum
pg_hba_file_rules(PG_FUNCTION_ARGS)2648 pg_hba_file_rules(PG_FUNCTION_ARGS)
2649 {
2650 	Tuplestorestate *tuple_store;
2651 	TupleDesc	tupdesc;
2652 	MemoryContext old_cxt;
2653 	ReturnSetInfo *rsi;
2654 
2655 	/*
2656 	 * We must use the Materialize mode to be safe against HBA file changes
2657 	 * while the cursor is open. It's also more efficient than having to look
2658 	 * up our current position in the parsed list every time.
2659 	 */
2660 	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2661 
2662 	/* Check to see if caller supports us returning a tuplestore */
2663 	if (rsi == NULL || !IsA(rsi, ReturnSetInfo))
2664 		ereport(ERROR,
2665 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2666 				 errmsg("set-valued function called in context that cannot accept a set")));
2667 	if (!(rsi->allowedModes & SFRM_Materialize))
2668 		ereport(ERROR,
2669 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2670 				 errmsg("materialize mode required, but it is not allowed in this context")));
2671 
2672 	rsi->returnMode = SFRM_Materialize;
2673 
2674 	/* Build a tuple descriptor for our result type */
2675 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2676 		elog(ERROR, "return type must be a row type");
2677 
2678 	/* Build tuplestore to hold the result rows */
2679 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
2680 
2681 	tuple_store =
2682 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
2683 							  false, work_mem);
2684 	rsi->setDesc = tupdesc;
2685 	rsi->setResult = tuple_store;
2686 
2687 	MemoryContextSwitchTo(old_cxt);
2688 
2689 	/* Fill the tuplestore */
2690 	fill_hba_view(tuple_store, tupdesc);
2691 
2692 	PG_RETURN_NULL();
2693 }
2694 
2695 
2696 /*
2697  * Parse one tokenised line from the ident config file and store the result in
2698  * an IdentLine structure.
2699  *
2700  * If parsing fails, log a message and return NULL.
2701  *
2702  * If ident_user is a regular expression (ie. begins with a slash), it is
2703  * compiled and stored in IdentLine structure.
2704  *
2705  * Note: this function leaks memory when an error occurs.  Caller is expected
2706  * to have set a memory context that will be reset if this function returns
2707  * NULL.
2708  */
2709 static IdentLine *
parse_ident_line(TokenizedLine * tok_line)2710 parse_ident_line(TokenizedLine *tok_line)
2711 {
2712 	int			line_num = tok_line->line_num;
2713 	ListCell   *field;
2714 	List	   *tokens;
2715 	HbaToken   *token;
2716 	IdentLine  *parsedline;
2717 
2718 	Assert(tok_line->fields != NIL);
2719 	field = list_head(tok_line->fields);
2720 
2721 	parsedline = palloc0(sizeof(IdentLine));
2722 	parsedline->linenumber = line_num;
2723 
2724 	/* Get the map token (must exist) */
2725 	tokens = lfirst(field);
2726 	IDENT_MULTI_VALUE(tokens);
2727 	token = linitial(tokens);
2728 	parsedline->usermap = pstrdup(token->string);
2729 
2730 	/* Get the ident user token */
2731 	field = lnext(tok_line->fields, field);
2732 	IDENT_FIELD_ABSENT(field);
2733 	tokens = lfirst(field);
2734 	IDENT_MULTI_VALUE(tokens);
2735 	token = linitial(tokens);
2736 	parsedline->ident_user = pstrdup(token->string);
2737 
2738 	/* Get the PG rolename token */
2739 	field = lnext(tok_line->fields, field);
2740 	IDENT_FIELD_ABSENT(field);
2741 	tokens = lfirst(field);
2742 	IDENT_MULTI_VALUE(tokens);
2743 	token = linitial(tokens);
2744 	parsedline->pg_role = pstrdup(token->string);
2745 
2746 	if (parsedline->ident_user[0] == '/')
2747 	{
2748 		/*
2749 		 * When system username starts with a slash, treat it as a regular
2750 		 * expression. Pre-compile it.
2751 		 */
2752 		int			r;
2753 		pg_wchar   *wstr;
2754 		int			wlen;
2755 
2756 		wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar));
2757 		wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1,
2758 									wstr, strlen(parsedline->ident_user + 1));
2759 
2760 		r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
2761 		if (r)
2762 		{
2763 			char		errstr[100];
2764 
2765 			pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
2766 			ereport(LOG,
2767 					(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2768 					 errmsg("invalid regular expression \"%s\": %s",
2769 							parsedline->ident_user + 1, errstr)));
2770 
2771 			pfree(wstr);
2772 			return NULL;
2773 		}
2774 		pfree(wstr);
2775 	}
2776 
2777 	return parsedline;
2778 }
2779 
2780 /*
2781  *	Process one line from the parsed ident config lines.
2782  *
2783  *	Compare input parsed ident line to the needed map, pg_role and ident_user.
2784  *	*found_p and *error_p are set according to our results.
2785  */
2786 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)2787 check_ident_usermap(IdentLine *identLine, const char *usermap_name,
2788 					const char *pg_role, const char *ident_user,
2789 					bool case_insensitive, bool *found_p, bool *error_p)
2790 {
2791 	*found_p = false;
2792 	*error_p = false;
2793 
2794 	if (strcmp(identLine->usermap, usermap_name) != 0)
2795 		/* Line does not match the map name we're looking for, so just abort */
2796 		return;
2797 
2798 	/* Match? */
2799 	if (identLine->ident_user[0] == '/')
2800 	{
2801 		/*
2802 		 * When system username starts with a slash, treat it as a regular
2803 		 * expression. In this case, we process the system username as a
2804 		 * regular expression that returns exactly one match. This is replaced
2805 		 * for \1 in the database username string, if present.
2806 		 */
2807 		int			r;
2808 		regmatch_t	matches[2];
2809 		pg_wchar   *wstr;
2810 		int			wlen;
2811 		char	   *ofs;
2812 		char	   *regexp_pgrole;
2813 
2814 		wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
2815 		wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
2816 
2817 		r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0);
2818 		if (r)
2819 		{
2820 			char		errstr[100];
2821 
2822 			if (r != REG_NOMATCH)
2823 			{
2824 				/* REG_NOMATCH is not an error, everything else is */
2825 				pg_regerror(r, &identLine->re, errstr, sizeof(errstr));
2826 				ereport(LOG,
2827 						(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2828 						 errmsg("regular expression match for \"%s\" failed: %s",
2829 								identLine->ident_user + 1, errstr)));
2830 				*error_p = true;
2831 			}
2832 
2833 			pfree(wstr);
2834 			return;
2835 		}
2836 		pfree(wstr);
2837 
2838 		if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL)
2839 		{
2840 			int			offset;
2841 
2842 			/* substitution of the first argument requested */
2843 			if (matches[1].rm_so < 0)
2844 			{
2845 				ereport(LOG,
2846 						(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2847 						 errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
2848 								identLine->ident_user + 1, identLine->pg_role)));
2849 				*error_p = true;
2850 				return;
2851 			}
2852 
2853 			/*
2854 			 * length: original length minus length of \1 plus length of match
2855 			 * plus null terminator
2856 			 */
2857 			regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
2858 			offset = ofs - identLine->pg_role;
2859 			memcpy(regexp_pgrole, identLine->pg_role, offset);
2860 			memcpy(regexp_pgrole + offset,
2861 				   ident_user + matches[1].rm_so,
2862 				   matches[1].rm_eo - matches[1].rm_so);
2863 			strcat(regexp_pgrole, ofs + 2);
2864 		}
2865 		else
2866 		{
2867 			/* no substitution, so copy the match */
2868 			regexp_pgrole = pstrdup(identLine->pg_role);
2869 		}
2870 
2871 		/*
2872 		 * now check if the username actually matched what the user is trying
2873 		 * to connect as
2874 		 */
2875 		if (case_insensitive)
2876 		{
2877 			if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
2878 				*found_p = true;
2879 		}
2880 		else
2881 		{
2882 			if (strcmp(regexp_pgrole, pg_role) == 0)
2883 				*found_p = true;
2884 		}
2885 		pfree(regexp_pgrole);
2886 
2887 		return;
2888 	}
2889 	else
2890 	{
2891 		/* Not regular expression, so make complete match */
2892 		if (case_insensitive)
2893 		{
2894 			if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 &&
2895 				pg_strcasecmp(identLine->ident_user, ident_user) == 0)
2896 				*found_p = true;
2897 		}
2898 		else
2899 		{
2900 			if (strcmp(identLine->pg_role, pg_role) == 0 &&
2901 				strcmp(identLine->ident_user, ident_user) == 0)
2902 				*found_p = true;
2903 		}
2904 	}
2905 }
2906 
2907 
2908 /*
2909  *	Scan the (pre-parsed) ident usermap file line by line, looking for a match
2910  *
2911  *	See if the user with ident username "auth_user" is allowed to act
2912  *	as Postgres user "pg_role" according to usermap "usermap_name".
2913  *
2914  *	Special case: Usermap NULL, equivalent to what was previously called
2915  *	"sameuser" or "samerole", means don't look in the usermap file.
2916  *	That's an implied map wherein "pg_role" must be identical to
2917  *	"auth_user" in order to be authorized.
2918  *
2919  *	Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
2920  */
2921 int
check_usermap(const char * usermap_name,const char * pg_role,const char * auth_user,bool case_insensitive)2922 check_usermap(const char *usermap_name,
2923 			  const char *pg_role,
2924 			  const char *auth_user,
2925 			  bool case_insensitive)
2926 {
2927 	bool		found_entry = false,
2928 				error = false;
2929 
2930 	if (usermap_name == NULL || usermap_name[0] == '\0')
2931 	{
2932 		if (case_insensitive)
2933 		{
2934 			if (pg_strcasecmp(pg_role, auth_user) == 0)
2935 				return STATUS_OK;
2936 		}
2937 		else
2938 		{
2939 			if (strcmp(pg_role, auth_user) == 0)
2940 				return STATUS_OK;
2941 		}
2942 		ereport(LOG,
2943 				(errmsg("provided user name (%s) and authenticated user name (%s) do not match",
2944 						pg_role, auth_user)));
2945 		return STATUS_ERROR;
2946 	}
2947 	else
2948 	{
2949 		ListCell   *line_cell;
2950 
2951 		foreach(line_cell, parsed_ident_lines)
2952 		{
2953 			check_ident_usermap(lfirst(line_cell), usermap_name,
2954 								pg_role, auth_user, case_insensitive,
2955 								&found_entry, &error);
2956 			if (found_entry || error)
2957 				break;
2958 		}
2959 	}
2960 	if (!found_entry && !error)
2961 	{
2962 		ereport(LOG,
2963 				(errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
2964 						usermap_name, pg_role, auth_user)));
2965 	}
2966 	return found_entry ? STATUS_OK : STATUS_ERROR;
2967 }
2968 
2969 
2970 /*
2971  * Read the ident config file and create a List of IdentLine records for
2972  * the contents.
2973  *
2974  * This works the same as load_hba(), but for the user config file.
2975  */
2976 bool
load_ident(void)2977 load_ident(void)
2978 {
2979 	FILE	   *file;
2980 	List	   *ident_lines = NIL;
2981 	ListCell   *line_cell,
2982 			   *parsed_line_cell;
2983 	List	   *new_parsed_lines = NIL;
2984 	bool		ok = true;
2985 	MemoryContext linecxt;
2986 	MemoryContext oldcxt;
2987 	MemoryContext ident_context;
2988 	IdentLine  *newline;
2989 
2990 	file = AllocateFile(IdentFileName, "r");
2991 	if (file == NULL)
2992 	{
2993 		/* not fatal ... we just won't do any special ident maps */
2994 		ereport(LOG,
2995 				(errcode_for_file_access(),
2996 				 errmsg("could not open usermap file \"%s\": %m",
2997 						IdentFileName)));
2998 		return false;
2999 	}
3000 
3001 	linecxt = tokenize_file(IdentFileName, file, &ident_lines, LOG);
3002 	FreeFile(file);
3003 
3004 	/* Now parse all the lines */
3005 	Assert(PostmasterContext);
3006 	ident_context = AllocSetContextCreate(PostmasterContext,
3007 										  "ident parser context",
3008 										  ALLOCSET_SMALL_SIZES);
3009 	oldcxt = MemoryContextSwitchTo(ident_context);
3010 	foreach(line_cell, ident_lines)
3011 	{
3012 		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell);
3013 
3014 		/* don't parse lines that already have errors */
3015 		if (tok_line->err_msg != NULL)
3016 		{
3017 			ok = false;
3018 			continue;
3019 		}
3020 
3021 		if ((newline = parse_ident_line(tok_line)) == NULL)
3022 		{
3023 			/* Parse error; remember there's trouble */
3024 			ok = false;
3025 
3026 			/*
3027 			 * Keep parsing the rest of the file so we can report errors on
3028 			 * more than the first line.  Error has already been logged, no
3029 			 * need for more chatter here.
3030 			 */
3031 			continue;
3032 		}
3033 
3034 		new_parsed_lines = lappend(new_parsed_lines, newline);
3035 	}
3036 
3037 	/* Free tokenizer memory */
3038 	MemoryContextDelete(linecxt);
3039 	MemoryContextSwitchTo(oldcxt);
3040 
3041 	if (!ok)
3042 	{
3043 		/*
3044 		 * File contained one or more errors, so bail out, first being careful
3045 		 * to clean up whatever we allocated.  Most stuff will go away via
3046 		 * MemoryContextDelete, but we have to clean up regexes explicitly.
3047 		 */
3048 		foreach(parsed_line_cell, new_parsed_lines)
3049 		{
3050 			newline = (IdentLine *) lfirst(parsed_line_cell);
3051 			if (newline->ident_user[0] == '/')
3052 				pg_regfree(&newline->re);
3053 		}
3054 		MemoryContextDelete(ident_context);
3055 		return false;
3056 	}
3057 
3058 	/* Loaded new file successfully, replace the one we use */
3059 	if (parsed_ident_lines != NIL)
3060 	{
3061 		foreach(parsed_line_cell, parsed_ident_lines)
3062 		{
3063 			newline = (IdentLine *) lfirst(parsed_line_cell);
3064 			if (newline->ident_user[0] == '/')
3065 				pg_regfree(&newline->re);
3066 		}
3067 	}
3068 	if (parsed_ident_context != NULL)
3069 		MemoryContextDelete(parsed_ident_context);
3070 
3071 	parsed_ident_context = ident_context;
3072 	parsed_ident_lines = new_parsed_lines;
3073 
3074 	return true;
3075 }
3076 
3077 
3078 
3079 /*
3080  *	Determine what authentication method should be used when accessing database
3081  *	"database" from frontend "raddr", user "user".  Return the method and
3082  *	an optional argument (stored in fields of *port), and STATUS_OK.
3083  *
3084  *	If the file does not contain any entry matching the request, we return
3085  *	method = uaImplicitReject.
3086  */
3087 void
hba_getauthmethod(hbaPort * port)3088 hba_getauthmethod(hbaPort *port)
3089 {
3090 	check_hba(port);
3091 }
3092