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