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