1 /* -*-pgsql-c-*- */
2 /*
3  *
4  * $Header$
5  *
6  * pgpool: a language independent connection pool server for PostgreSQL
7  * written by Tatsuo Ishii
8  *
9  * Portions Copyright (c) 2003-2018	PgPool Global Development Group
10  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  * Permission to use, copy, modify, and distribute this software and
13  * its documentation for any purpose and without fee is hereby
14  * granted, provided that the above copyright notice appear in all
15  * copies and that both that copyright notice and this permission
16  * notice appear in supporting documentation, and that the name of the
17  * author not be used in advertising or publicity pertaining to
18  * distribution of the software without specific, written prior
19  * permission. The author makes no representations about the
20  * suitability of this software for any purpose.  It is provided "as
21  * is" without express or implied warranty.
22  *
23  * pool_hba.c.: Routines to handle host based authentication.
24  *
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <netdb.h>
32 
33 #ifdef __FreeBSD__
34 #include <netinet/in.h>
35 #endif
36 
37 #include "pool.h"
38 #include "auth/pool_hba.h"
39 #include "utils/pool_path.h"
40 #include "utils/pool_ip.h"
41 #include "utils/pool_stream.h"
42 #include "pool_config.h"
43 #include "pool_type.h"
44 #include "utils/palloc.h"
45 #include "utils/memutils.h"
46 #include "utils/elog.h"
47 #include "parser/pg_list.h"
48 #include "auth/pool_passwd.h"
49 
50 #define MULTI_VALUE_SEP "\001"	/* delimiter for multi-valued column strings */
51 
52 #define MAX_TOKEN	256
53 #define MAX_LINE	8192
54 
55 static MemoryContext parsed_hba_context = NULL;
56 static List *parsed_hba_lines = NIL;
57 static char *HbaFileName;
58 
59 
60 #define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
61 #define token_matches(t, k)  (strcmp(t->string, k) == 0)
62 
63 
64 /*
65  * TokenizedLine represents one line lexed from a config file.
66  * Each item in the "fields" list is a sub-list of HbaTokens.
67  * We don't emit a TokenizedLine for empty or all-comment lines,
68  * so "fields" is never NIL (nor are any of its sub-lists).
69  * Exception: if an error occurs during tokenization, we might
70  * have fields == NIL, in which case err_msg != NULL.
71  */
72 typedef struct TokenizedLine
73 {
74 	List	   *fields;			/* List of lists of HbaTokens */
75 	int			line_num;		/* Line number */
76 	char	   *raw_line;		/* Raw line text */
77 	char	   *err_msg;		/* Error message if any */
78 } TokenizedLine;
79 
80 /*
81  * A single string token lexed from a config file, together with whether
82  * the token had been quoted.
83  */
84 typedef struct HbaToken
85 {
86 	char	   *string;
87 	bool		quoted;
88 } HbaToken;
89 
90 /* callback data for check_network_callback */
91 typedef struct check_network_data
92 {
93 	IPCompareMethod method;		/* test method */
94 	SockAddr   *raddr;			/* client's actual address */
95 	bool		result;			/* set to true if match */
96 } check_network_data;
97 
98 
99 static HbaToken *copy_hba_token(HbaToken *in);
100 static HbaToken *make_hba_token(const char *token, bool quoted);
101 
102 static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
103 				   int elevel, char **err_msg);
104 
105 static MemoryContext tokenize_file(const char *filename, FILE *file,
106 			  List **tok_lines, int elevel);
107 static void sendAuthRequest(POOL_CONNECTION * frontend, AuthRequest areq);
108 static void auth_failed(POOL_CONNECTION * frontend);
109 static void close_all_backend_connections(void);
110 static bool hba_getauthmethod(POOL_CONNECTION * frontend);
111 static bool check_hba(POOL_CONNECTION * frontend);
112 static bool check_user(char *user, List *tokens);
113 static bool check_db(const char *dbname, const char *user, List *tokens);
114 static List *tokenize_inc_file(List *tokens,
115 				  const char *outer_filename,
116 				  const char *inc_filename,
117 				  int elevel,
118 				  char **err_msg);
119 static bool
120 			check_hostname(POOL_CONNECTION * frontend, const char *hostname);
121 static bool
122 			check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask);
123 static bool
124 			check_same_host_or_net(SockAddr *raddr, IPCompareMethod method);
125 static void check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
126 					   void *cb_data);
127 
128 static HbaLine *parse_hba_line(TokenizedLine *tok_line, int elevel);
129 static bool pg_isblank(const char c);
130 static bool next_token(char **lineptr, char *buf, int bufsz,
131 		   bool *initial_quote, bool *terminating_comma,
132 		   int elevel, char **err_msg);
133 static List *next_field_expand(const char *filename, char **lineptr,
134 				  int elevel, char **err_msg);
135 #ifdef NOT_USED
136 static POOL_STATUS CheckUserExist(char *username);
137 #endif
138 
139 #ifdef USE_PAM
140 #ifdef HAVE_PAM_PAM_APPL_H
141 #include <pam/pam_appl.h>
142 #endif
143 #ifdef HAVE_SECURITY_PAM_APPL_H
144 #include <security/pam_appl.h>
145 #endif
146 
147 #define PGPOOL_PAM_SERVICE "pgpool" /* Service name passed to PAM */
148 
149 static POOL_STATUS CheckPAMAuth(POOL_CONNECTION * frontend, char *user, char *password);
150 static int	pam_passwd_conv_proc(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);
151 
152 /*
153  * recv_password_packet is usually used with authentications that require a client
154  * password. However, pgpool's hba function only uses it for PAM authentication,
155  * so declare a prototype here in "#ifdef USE_PAM" to avoid compilation warning.
156  */
157 static char *recv_password_packet(POOL_CONNECTION * frontend);
158 
159 static struct pam_conv pam_passw_conv = {
160 	&pam_passwd_conv_proc,
161 	NULL
162 };
163 
164 static char *pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
165 static POOL_CONNECTION * pam_frontend_kludge;	/* Workaround for passing
166 												 * POOL_CONNECTION *frontend
167 												 * into pam_passwd_conv_proc */
168 #endif							/* USE_PAM */
169 
170 
171 /*
172  * Read the config file and create a List of HbaLine records for the contents.
173  *
174  * The configuration is read into a temporary list, and if any parse error
175  * occurs the old list is kept in place and false is returned.  Only if the
176  * whole file parses OK is the list replaced, and the function returns true.
177  *
178  * On a false result, caller will take care of reporting a FATAL error in case
179  * this is the initial startup.  If it happens on reload, we just keep running
180  * with the old data.
181  */
182 bool
load_hba(char * hbapath)183 load_hba(char *hbapath)
184 {
185 	FILE	   *file;
186 	List	   *hba_lines = NIL;
187 	ListCell   *line;
188 	List	   *new_parsed_lines = NIL;
189 	bool		ok = true;
190 	MemoryContext linecxt;
191 	MemoryContext oldcxt;
192 	MemoryContext hbacxt;
193 
194 	HbaFileName = pstrdup(hbapath);
195 
196 	file = fopen(hbapath, "r");
197 	if (file == NULL)
198 	{
199 		ereport(LOG,
200 				(errmsg("could not open configuration file \"%s\": %m",
201 						HbaFileName)));
202 		return false;
203 	}
204 
205 	linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG);
206 	fclose(file);
207 
208 	/* Now parse all the lines */
209 	hbacxt = AllocSetContextCreate(TopMemoryContext,
210 								   "hba parser context",
211 								   ALLOCSET_SMALL_SIZES);
212 	oldcxt = MemoryContextSwitchTo(hbacxt);
213 	foreach(line, hba_lines)
214 	{
215 		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
216 		HbaLine    *newline;
217 
218 		/* don't parse lines that already have errors */
219 		if (tok_line->err_msg != NULL)
220 		{
221 			ok = false;
222 			continue;
223 		}
224 
225 		if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
226 		{
227 			/* Parse error; remember there's trouble */
228 			ok = false;
229 
230 			/*
231 			 * Keep parsing the rest of the file so we can report errors on
232 			 * more than the first line.  Error has already been logged, no
233 			 * need for more chatter here.
234 			 */
235 			continue;
236 		}
237 
238 		new_parsed_lines = lappend(new_parsed_lines, newline);
239 	}
240 
241 	/*
242 	 * A valid HBA file must have at least one entry; else there's no way to
243 	 * connect to the postmaster.  But only complain about this if we didn't
244 	 * already have parsing errors.
245 	 */
246 	if (ok && new_parsed_lines == NIL)
247 	{
248 		ereport(LOG,
249 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
250 				 errmsg("configuration file \"%s\" contains no entries",
251 						HbaFileName)));
252 		ok = false;
253 	}
254 
255 	/* Free tokenizer memory */
256 	MemoryContextDelete(linecxt);
257 	MemoryContextSwitchTo(oldcxt);
258 
259 	if (!ok)
260 	{
261 		/* File contained one or more errors, so bail out */
262 		MemoryContextDelete(hbacxt);
263 		return false;
264 	}
265 
266 	/* Loaded new file successfully, replace the one we use */
267 	if (parsed_hba_context != NULL)
268 		MemoryContextDelete(parsed_hba_context);
269 	parsed_hba_context = hbacxt;
270 	parsed_hba_lines = new_parsed_lines;
271 
272 	return true;
273 }
274 
275 static HbaLine *
parse_hba_line(TokenizedLine * tok_line,int elevel)276 parse_hba_line(TokenizedLine *tok_line, int elevel)
277 {
278 	int			line_num = tok_line->line_num;
279 	char	  **err_msg = &tok_line->err_msg;
280 	char	   *str;
281 	struct addrinfo *gai_result;
282 	struct addrinfo hints;
283 	int			ret;
284 	char	   *cidr_slash;
285 	ListCell   *field;
286 	List	   *tokens;
287 	ListCell   *tokencell;
288 	HbaToken   *token;
289 	HbaLine    *parsedline;
290 
291 	parsedline = palloc0(sizeof(HbaLine));
292 	parsedline->linenumber = line_num;
293 	parsedline->rawline = pstrdup(tok_line->raw_line);
294 
295 	/* Check the record type. */
296 	Assert(tok_line->fields != NIL);
297 	field = list_head(tok_line->fields);
298 	tokens = lfirst(field);
299 	if (tokens->length > 1)
300 	{
301 		ereport(elevel,
302 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
303 				 errmsg("multiple values specified for connection type"),
304 				 errhint("Specify exactly one connection type per line."),
305 				 errcontext("line %d of configuration file \"%s\"",
306 							line_num, HbaFileName)));
307 		*err_msg = "multiple values specified for connection type";
308 		return NULL;
309 	}
310 	token = linitial(tokens);
311 	if (strcmp(token->string, "local") == 0)
312 	{
313 		parsedline->conntype = ctLocal;
314 	}
315 	else if (strcmp(token->string, "host") == 0 ||
316 			 strcmp(token->string, "hostssl") == 0 ||
317 			 strcmp(token->string, "hostnossl") == 0)
318 	{
319 
320 		if (token->string[4] == 's')	/* "hostssl" */
321 		{
322 			parsedline->conntype = ctHostSSL;
323 			/* Log a warning if SSL support is not active */
324 #ifdef USE_SSL
325 			if (!pool_config->ssl)
326 			{
327 				ereport(elevel,
328 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
329 						 errmsg("hostssl record cannot match because SSL is disabled"),
330 						 errhint("Set ssl = on in pgpool.conf"),
331 						 errcontext("line %d of configuration file \"%s\"",
332 									line_num, HbaFileName)));
333 				*err_msg = "hostssl record cannot match because SSL is disabled";
334 			}
335 #else
336 			ereport(elevel,
337 					(errmsg("hostssl record cannot match because SSL is not supported by this build"),
338 					 errhint("Compile with --with-openssl to use SSL connections."),
339 					 errcontext("line %d of configuration file \"%s\"",
340 								line_num, HbaFileName)));
341 			*err_msg = "hostssl record cannot match because SSL is not supported by this build";
342 #endif
343 		}
344 		else if (token->string[4] == 'n')	/* "hostnossl" */
345 		{
346 			parsedline->conntype = ctHostNoSSL;
347 		}
348 		else
349 		{
350 			/* "host" */
351 			parsedline->conntype = ctHost;
352 		}
353 	}							/* record type */
354 	else
355 	{
356 		ereport(elevel,
357 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
358 				 errmsg("invalid connection type \"%s\"",
359 						token->string),
360 				 errcontext("line %d of configuration file \"%s\"",
361 							line_num, HbaFileName)));
362 		*err_msg = psprintf("invalid connection type \"%s\"", token->string);
363 		return NULL;
364 	}
365 
366 	/* Get the databases. */
367 	field = lnext(field);
368 	if (!field)
369 	{
370 		ereport(elevel,
371 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
372 				 errmsg("end-of-line before database specification"),
373 				 errcontext("line %d of configuration file \"%s\"",
374 							line_num, HbaFileName)));
375 		*err_msg = "end-of-line before database specification";
376 		return NULL;
377 	}
378 	parsedline->databases = NIL;
379 	tokens = lfirst(field);
380 	foreach(tokencell, tokens)
381 	{
382 		parsedline->databases = lappend(parsedline->databases,
383 										copy_hba_token(lfirst(tokencell)));
384 	}
385 
386 	/* Get the users. */
387 	field = lnext(field);
388 	if (!field)
389 	{
390 		ereport(elevel,
391 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
392 				 errmsg("end-of-line before role specification"),
393 				 errcontext("line %d of configuration file \"%s\"",
394 							line_num, HbaFileName)));
395 		*err_msg = "end-of-line before role specification";
396 		return NULL;
397 	}
398 	parsedline->users = NIL;
399 	tokens = lfirst(field);
400 	foreach(tokencell, tokens)
401 	{
402 		parsedline->users = lappend(parsedline->users,
403 									copy_hba_token(lfirst(tokencell)));
404 	}
405 
406 	if (parsedline->conntype != ctLocal)
407 	{
408 		/* Read the IP address field. (with or without CIDR netmask) */
409 		field = lnext(field);
410 		if (!field)
411 		{
412 			ereport(elevel,
413 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
414 					 errmsg("end-of-line before IP address specification"),
415 					 errcontext("line %d of configuration file \"%s\"",
416 								line_num, HbaFileName)));
417 			*err_msg = "end-of-line before IP address specification";
418 			return NULL;
419 		}
420 		tokens = lfirst(field);
421 		if (tokens->length > 1)
422 		{
423 			ereport(elevel,
424 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
425 					 errmsg("multiple values specified for host address"),
426 					 errhint("Specify one address range per line."),
427 					 errcontext("line %d of configuration file \"%s\"",
428 								line_num, HbaFileName)));
429 			*err_msg = "multiple values specified for host address";
430 			return NULL;
431 		}
432 		token = linitial(tokens);
433 
434 		if (token_is_keyword(token, "all"))
435 		{
436 			parsedline->ip_cmp_method = ipCmpAll;
437 		}
438 		else if (token_is_keyword(token, "samehost"))
439 		{
440 			/* Any IP on this host is allowed to connect */
441 			parsedline->ip_cmp_method = ipCmpSameHost;
442 		}
443 		else if (token_is_keyword(token, "samenet"))
444 		{
445 			/* Any IP on the host's subnets is allowed to connect */
446 			parsedline->ip_cmp_method = ipCmpSameNet;
447 		}
448 		else
449 		{
450 			/* IP and netmask are specified */
451 			parsedline->ip_cmp_method = ipCmpMask;
452 
453 			/* need a modifiable copy of token */
454 			str = pstrdup(token->string);
455 
456 			/* Check if it has a CIDR suffix and if so isolate it */
457 			cidr_slash = strchr(str, '/');
458 			if (cidr_slash)
459 				*cidr_slash = '\0';
460 
461 			/* Get the IP address either way */
462 			hints.ai_flags = AI_NUMERICHOST;
463 			hints.ai_family = AF_UNSPEC;
464 			hints.ai_socktype = 0;
465 			hints.ai_protocol = 0;
466 			hints.ai_addrlen = 0;
467 			hints.ai_canonname = NULL;
468 			hints.ai_addr = NULL;
469 			hints.ai_next = NULL;
470 
471 			ret = getaddrinfo_all(str, NULL, &hints, &gai_result);
472 			if (ret == 0 && gai_result)
473 				memcpy(&parsedline->addr, gai_result->ai_addr,
474 					   gai_result->ai_addrlen);
475 			else if (ret == EAI_NONAME)
476 				parsedline->hostname = str;
477 			else
478 			{
479 				ereport(elevel,
480 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
481 						 errmsg("invalid IP address \"%s\": %s",
482 								str, gai_strerror(ret)),
483 						 errcontext("line %d of configuration file \"%s\"",
484 									line_num, HbaFileName)));
485 				*err_msg = psprintf("invalid IP address \"%s\": %s",
486 									str, gai_strerror(ret));
487 				if (gai_result)
488 					freeaddrinfo_all(hints.ai_family, gai_result);
489 				pfree(str);
490 				return NULL;
491 			}
492 
493 			freeaddrinfo_all(hints.ai_family, gai_result);
494 
495 			/* Get the netmask */
496 			if (cidr_slash)
497 			{
498 				if (parsedline->hostname)
499 				{
500 					ereport(elevel,
501 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
502 							 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
503 									token->string),
504 							 errcontext("line %d of configuration file \"%s\"",
505 										line_num, HbaFileName)));
506 					*err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
507 										token->string);
508 					pfree(str);
509 					return NULL;
510 				}
511 
512 				if (SockAddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
513 									   parsedline->addr.ss_family) < 0)
514 				{
515 					ereport(elevel,
516 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
517 							 errmsg("invalid CIDR mask in address \"%s\"",
518 									token->string),
519 							 errcontext("line %d of configuration file \"%s\"",
520 										line_num, HbaFileName)));
521 					*err_msg = psprintf("invalid CIDR mask in address \"%s\"",
522 										token->string);
523 					pfree(str);
524 					return NULL;
525 				}
526 				pfree(str);
527 			}
528 			else if (!parsedline->hostname)
529 			{
530 				/* Read the mask field. */
531 				pfree(str);
532 				field = lnext(field);
533 				if (!field)
534 				{
535 					ereport(elevel,
536 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
537 							 errmsg("end-of-line before netmask specification"),
538 							 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
539 							 errcontext("line %d of configuration file \"%s\"",
540 										line_num, HbaFileName)));
541 					*err_msg = "end-of-line before netmask specification";
542 					return NULL;
543 				}
544 				tokens = lfirst(field);
545 				if (tokens->length > 1)
546 				{
547 					ereport(elevel,
548 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
549 							 errmsg("multiple values specified for netmask"),
550 							 errcontext("line %d of configuration file \"%s\"",
551 										line_num, HbaFileName)));
552 					*err_msg = "multiple values specified for netmask";
553 					return NULL;
554 				}
555 				token = linitial(tokens);
556 
557 				ret = getaddrinfo_all(token->string, NULL,
558 									  &hints, &gai_result);
559 				if (ret || !gai_result)
560 				{
561 					ereport(elevel,
562 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
563 							 errmsg("invalid IP mask \"%s\": %s",
564 									token->string, gai_strerror(ret)),
565 							 errcontext("line %d of configuration file \"%s\"",
566 										line_num, HbaFileName)));
567 					*err_msg = psprintf("invalid IP mask \"%s\": %s",
568 										token->string, gai_strerror(ret));
569 					if (gai_result)
570 						freeaddrinfo_all(hints.ai_family, gai_result);
571 					return NULL;
572 				}
573 
574 				memcpy(&parsedline->mask, gai_result->ai_addr,
575 					   gai_result->ai_addrlen);
576 				freeaddrinfo_all(hints.ai_family, gai_result);
577 
578 				if (parsedline->addr.ss_family != parsedline->mask.ss_family)
579 				{
580 					ereport(elevel,
581 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
582 							 errmsg("IP address and mask do not match"),
583 							 errcontext("line %d of configuration file \"%s\"",
584 										line_num, HbaFileName)));
585 					*err_msg = "IP address and mask do not match";
586 					return NULL;
587 				}
588 			}
589 		}
590 	}							/* != ctLocal */
591 
592 	/* Get the authentication method */
593 	field = lnext(field);
594 	if (!field)
595 	{
596 		ereport(elevel,
597 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
598 				 errmsg("end-of-line before authentication method"),
599 				 errcontext("line %d of configuration file \"%s\"",
600 							line_num, HbaFileName)));
601 		*err_msg = "end-of-line before authentication method";
602 		return NULL;
603 	}
604 	tokens = lfirst(field);
605 	if (tokens->length > 1)
606 	{
607 		ereport(elevel,
608 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
609 				 errmsg("multiple values specified for authentication type"),
610 				 errhint("Specify exactly one authentication type per line."),
611 				 errcontext("line %d of configuration file \"%s\"",
612 							line_num, HbaFileName)));
613 		*err_msg = "multiple values specified for authentication type";
614 		return NULL;
615 	}
616 	token = linitial(tokens);
617 
618 	if (strcmp(token->string, "trust") == 0)
619 		parsedline->auth_method = uaTrust;
620 	else if (strcmp(token->string, "reject") == 0)
621 		parsedline->auth_method = uaReject;
622 	else if (strcmp(token->string, "cert") == 0)
623 		parsedline->auth_method = uaCert;
624 	else if (strcmp(token->string, "password") == 0)
625 		parsedline->auth_method = uaPassword;
626 	else if (strcmp(token->string, "md5") == 0)
627 		parsedline->auth_method = uaMD5;
628 	else if (strcmp(token->string, "scram-sha-256") == 0)
629 		parsedline->auth_method = uaSCRAM;
630 #ifdef USE_PAM
631 	else if (strcmp(token->string, "pam") == 0)
632 		parsedline->auth_method = uaPAM;
633 #endif
634 	else
635 	{
636 		ereport(elevel,
637 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
638 				 errmsg("invalid authentication method \"%s\"",
639 						token->string),
640 				 errcontext("line %d of configuration file \"%s\"",
641 							line_num, HbaFileName)));
642 		*err_msg = psprintf("invalid authentication method \"%s\"",
643 							token->string);
644 		return NULL;
645 	}
646 	/* Parse remaining arguments */
647 	while ((field = lnext(field)) != NULL)
648 	{
649 		tokens = lfirst(field);
650 		foreach(tokencell, tokens)
651 		{
652 			char	   *val;
653 
654 			token = lfirst(tokencell);
655 
656 			str = pstrdup(token->string);
657 			val = strchr(str, '=');
658 			if (val == NULL)
659 			{
660 				/*
661 				 * Got something that's not a name=value pair.
662 				 */
663 				ereport(elevel,
664 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
665 						 errmsg("authentication option not in name=value format: %s", token->string),
666 						 errcontext("line %d of configuration file \"%s\"",
667 									line_num, HbaFileName)));
668 				*err_msg = psprintf("authentication option not in name=value format: %s",
669 									token->string);
670 				pfree(str);
671 				return NULL;
672 			}
673 
674 			*val++ = '\0';		/* str now holds "name", val holds "value" */
675 			if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
676 			{
677 				/* parse_hba_auth_opt already logged the error message */
678 				pfree(str);
679 				return NULL;
680 			}
681 			pfree(str);
682 		}
683 	}
684 
685 	return parsedline;
686 }
687 
688 /*
689  * Parse one name-value pair as an authentication option into the given
690  * HbaLine.  Return true if we successfully parse the option, false if we
691  * encounter an error.  In the event of an error, also log a message at
692  * ereport level elevel, and store a message string into *err_msg.
693  */
694 static bool
parse_hba_auth_opt(char * name,char * val,HbaLine * hbaline,int elevel,char ** err_msg)695 parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
696 				   int elevel, char **err_msg)
697 {
698 	int			line_num = hbaline->linenumber;
699 
700 	if (strcmp(name, "pamservice") == 0)
701 	{
702 #ifdef USE_PAM
703 		if (hbaline->auth_method != uaPAM)
704 		{
705 			ereport(elevel,
706 					(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
707 					 errcontext("line %d of configuration file \"%s\"",
708 								line_num, HbaFileName)));
709 			*err_msg = "pamservice authentication option can only be configured for authentication method \"pam\"";
710 		}
711 		else
712 			hbaline->pamservice = pstrdup(val);
713 #else
714 		ereport(elevel,
715 				(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
716 				 errhint("Compile with --with-pam to use PAM authentication."),
717 				 errcontext("line %d of configuration file \"%s\"",
718 							line_num, HbaFileName)));
719 		*err_msg = "pamservice authentication option cannot be used because PAM is not supported by this build";
720 
721 #endif
722 	}
723 	else if (strcmp(name, "pam_use_hostname") == 0)
724 	{
725 #ifdef USE_PAM
726 		if (hbaline->auth_method != uaPAM)
727 		{
728 			ereport(elevel,
729 					(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
730 					 errcontext("line %d of configuration file \"%s\"",
731 								line_num, HbaFileName)));
732 			*err_msg = "pamservice authentication option can only be configured for authentication method \"pam\"";
733 		}
734 		else
735 		{
736 			if (strcmp(val, "1") == 0)
737 				hbaline->pam_use_hostname = true;
738 			else
739 				hbaline->pam_use_hostname = false;
740 
741 		}
742 #else
743 		ereport(elevel,
744 				(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
745 				 errhint("Compile with --with-pam to use PAM authentication."),
746 				 errcontext("line %d of configuration file \"%s\"",
747 							line_num, HbaFileName)));
748 		*err_msg = "pamservice authentication option cannot be used because PAM is not supported by this build";
749 
750 #endif
751 	}
752 	else
753 	{
754 		ereport(elevel,
755 				(errmsg("unrecognized authentication option name: \"%s\"",
756 						name),
757 				 errcontext("line %d of configuration file \"%s\"",
758 							line_num, HbaFileName)));
759 		*err_msg = psprintf("unrecognized authentication option name: \"%s\"",
760 							name);
761 		return false;
762 	}
763 	return true;
764 }
765 
766 /*
767  * do frontend <-> pgpool authentication based on pool_hba.conf
768  */
769 void
ClientAuthentication(POOL_CONNECTION * frontend)770 ClientAuthentication(POOL_CONNECTION * frontend)
771 {
772 	POOL_STATUS status = POOL_END;
773 	MemoryContext oldContext;
774 
775 	PG_TRY();
776 	{
777 		if (!hba_getauthmethod(frontend))
778 			ereport(FATAL,
779 					(return_code(2),
780 					 errmsg("client authentication failed"),
781 					 errdetail("missing or erroneous pool_hba.conf file"),
782 					 errhint("see pgpool log for details")));
783 
784 		/*
785 		 * Get the password for the user if it is stored in the pool_password
786 		 * file
787 		 * authentication process is called in the temporary memory
788 		 * context, but password mappings has to live till the life time
789 		 * of frontend connection, so call the pool_get_user_credentials in
790 		 * ProcessLoopContext memory context
791 		 */
792 		oldContext = MemoryContextSwitchTo(ProcessLoopContext);
793 		frontend->passwordMapping = pool_get_user_credentials(frontend->username);
794 		MemoryContextSwitchTo(oldContext);
795 
796 		switch (frontend->pool_hba->auth_method)
797 		{
798 			case uaImplicitReject:
799 			case uaReject:
800 				{
801 					/*
802 					 * This could have come from an explicit "reject" entry in
803 					 * pool_hba.conf, but more likely it means there was no
804 					 * matching entry.  Take pity on the poor user and issue a
805 					 * helpful error message.  NOTE: this is not a security
806 					 * breach, because all the info reported here is known at
807 					 * the frontend and must be assumed known to bad guys.
808 					 * We're merely helping out the less clueful good guys.
809 					 */
810 					char		hostinfo[NI_MAXHOST];
811 
812 					getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen,
813 									hostinfo, sizeof(hostinfo),
814 									NULL, 0,
815 									NI_NUMERICHOST);
816 
817 #ifdef USE_SSL
818 					ereport(FATAL,
819 						(return_code(2),
820 							 errmsg("client authentication failed"),
821 							 errdetail("no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
822 									   hostinfo, frontend->username, frontend->database,
823 									   frontend->ssl ? "SSL on" : "SSL off"),
824 							 errhint("see pgpool log for details")));
825 #else
826 					ereport(FATAL,
827 							(return_code(2),
828 							 errmsg("client authentication failed"),
829 							 errdetail("no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
830 									   hostinfo, frontend->username, frontend->database),
831 							 errhint("see pgpool log for details")));
832 #endif
833 					break;
834 				}
835 
836 				/*
837 				 * case uaKrb4: case uaKrb5: case uaIdent: case uaCrypt:
838 				 */
839 			case uaPassword:
840 				ereport(DEBUG1,
841 						(errmsg("password authentication required")));
842 				status = POOL_CONTINUE;
843 				break;
844 			case uaCert:
845 				ereport(DEBUG1,
846 						(errmsg("SSL certificate authentication required")));
847 				status = POOL_CONTINUE;
848 				break;
849 
850 			case uaMD5:
851 				status = POOL_CONTINUE;
852 
853 				if (NUM_BACKENDS <= 1)
854 					break;
855 
856 				if (!frontend->passwordMapping)
857 					ereport(FATAL,
858 							(return_code(2),
859 							 errmsg("md5 authentication failed"),
860 							 errdetail("pool_passwd file does not contain an entry for \"%s\"", frontend->username)));
861 				if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
862 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_MD5 &&
863 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_TEXT_PREFIXED &&
864 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_AES)
865 					ereport(FATAL,
866 							(return_code(2),
867 							 errmsg("md5 authentication failed"),
868 							 errdetail("pool_passwd file does not contain valid md5 entry for \"%s\"", frontend->username)));
869 				break;
870 
871 			case uaSCRAM:
872 				if (!frontend->passwordMapping)
873 					ereport(FATAL,
874 							(return_code(2),
875 							 errmsg("SCRAM authentication failed"),
876 							 errdetail("pool_passwd file does not contain an entry for \"%s\"", frontend->username)));
877 				if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
878 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_TEXT_PREFIXED &&
879 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_SCRAM_SHA_256 &&
880 					frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_AES)
881 					ereport(FATAL,
882 							(return_code(2),
883 							 errmsg("SCRAM authentication failed"),
884 							 errdetail("pool_passwd file does not contain valid SCRAM entry for \"%s\"", frontend->username)));
885 
886 				status = POOL_CONTINUE;
887 				break;
888 
889 
890 #ifdef USE_PAM
891 			case uaPAM:
892 				pam_frontend_kludge = frontend;
893 				status = CheckPAMAuth(frontend, frontend->username, "");
894 				break;
895 #endif							/* USE_PAM */
896 
897 			case uaTrust:
898 				status = POOL_CONTINUE;
899 				break;
900 		}
901 	}
902 	PG_CATCH();
903 	{
904 		close_all_backend_connections();
905 		PG_RE_THROW();
906 	}
907 	PG_END_TRY();
908 
909 	if (status == POOL_CONTINUE)
910 	{
911 		sendAuthRequest(frontend, AUTH_REQ_OK);
912 		authenticate_frontend(frontend);
913 	}
914 	else
915 		auth_failed(frontend);
916 }
917 
918 
919 static void
sendAuthRequest(POOL_CONNECTION * frontend,AuthRequest areq)920 sendAuthRequest(POOL_CONNECTION * frontend, AuthRequest areq)
921 {
922 	int			wsize;			/* number of bytes to write */
923 	int			areq_nbo;		/* areq in network byte order */
924 
925 	/*
926 	 * If AUTH_REQ_OK, then frontend is OK to connect __with_pgpool__. Do not
927 	 * send 'R' to the frontend, he still needs to authenticate himself with
928 	 * the backend.
929 	 */
930 	if (areq == AUTH_REQ_OK)
931 		return;
932 
933 	/* request a password */
934 	pool_write(frontend, "R", 1);
935 
936 	if (frontend->protoVersion == PROTO_MAJOR_V3)
937 	{
938 /* 		if (areq == AUTH_REQ_MD5) */
939 /* 			wsize = htonl(sizeof(int)*2+4); */
940 /* 		else if (areq == AUTH_REQ_CRYPT) */
941 /* 			wsize = htonl(sizeof(int)*2+2); */
942 /* 		else */
943 		wsize = htonl(sizeof(int) * 2);
944 		pool_write(frontend, &wsize, sizeof(int));
945 	}
946 
947 	areq_nbo = htonl(areq);
948 	pool_write(frontend, &areq_nbo, sizeof(int));
949 
950 	/* Add the salt for encrypted passwords. */
951 /* 	if (areq == AUTH_REQ_MD5) */
952 /* 		pq_sendbytes(&buf, port->md5Salt, 4); */
953 /* 	else if (areq == AUTH_REQ_CRYPT) */
954 /* 		pq_sendbytes(&buf, port->cryptSalt, 2); */
955 
956 	pool_flush(frontend);
957 }
958 
959 
960 #ifdef USE_PAM					/* see the prototype comment  */
961 
962 /*
963  * Collect password response packet from frontend.
964  *
965  * Returns NULL if couldn't get password, else palloc'd string.
966  */
967 static char *
recv_password_packet(POOL_CONNECTION * frontend)968 recv_password_packet(POOL_CONNECTION * frontend)
969 {
970 	int			rsize;
971 	char	   *passwd;
972 	char	   *returnVal;
973 
974 	if (frontend->protoVersion == PROTO_MAJOR_V3)
975 	{
976 		/* Expect 'p' message type */
977 		char		kind;
978 
979 		if (pool_read(frontend, &kind, 1) < 0)
980 			return NULL;
981 
982 		if (kind != 'p')
983 		{
984 			ereport(LOG,
985 					(errmsg("unexpected password response received. expected 'p' received '%c'", kind)));
986 			return NULL;		/* bad message type */
987 		}
988 	}
989 	/* pre-3.0 protocol does not send a message type */
990 
991 	if (pool_read(frontend, &rsize, sizeof(int)) < 0)
992 		return NULL;
993 
994 	rsize = ntohl(rsize) - 4;
995 	passwd = pool_read2(frontend, rsize);	/* retrieve password */
996 	if (passwd == NULL)
997 		return NULL;
998 
999 	/* Do not echo password to logs, for security. */
1000 	ereport(DEBUG1,
1001 			(errmsg("received password packet from frontend for pgpool's HBA")));
1002 
1003 	/*
1004 	 * Return the received string.  Note we do not attempt to do any
1005 	 * character-set conversion on it; since we don't yet know the client's
1006 	 * encoding, there wouldn't be much point.
1007 	 */
1008 	returnVal = pstrdup(passwd);
1009 	return returnVal;
1010 }
1011 
1012 #endif							/* USE_PAM */
1013 
1014 /*
1015  * Tell the user the authentication failed.
1016  */
1017 static void
auth_failed(POOL_CONNECTION * frontend)1018 auth_failed(POOL_CONNECTION * frontend)
1019 {
1020 	int			messagelen;
1021 	char	   *errmessage;
1022 
1023 	messagelen = strlen(frontend->username) + 100;
1024 	errmessage = (char *) palloc(messagelen + 1);
1025 
1026 	switch (frontend->pool_hba->auth_method)
1027 	{
1028 		case uaImplicitReject:
1029 		case uaReject:
1030 			snprintf(errmessage, messagelen,
1031 					 "authentication with pgpool failed for user \"%s\": host rejected",
1032 					 frontend->username);
1033 			break;
1034 /* 		case uaKrb4: */
1035 /* 			snprintf(errmessage, messagelen, */
1036 /* 					 "Kerberos 4 authentication with pgpool failed for user \"%s\"", */
1037 /* 					 frontend->username); */
1038 /* 			break; */
1039 /* 		case uaKrb5: */
1040 /* 			snprintf(errmessage, messagelen, */
1041 /* 					 "Kerberos 5 authentication with pgpool failed for user \"%s\"", */
1042 /* 					 frontend->username); */
1043 /* 			break; */
1044 		case uaTrust:
1045 			snprintf(errmessage, messagelen,
1046 					 "\"trust\" authentication with pgpool failed for user \"%s\"",
1047 					 frontend->username);
1048 			break;
1049 /* 		case uaIdent: */
1050 /* 			snprintf(errmessage, messagelen, */
1051 /* 					 "Ident authentication with pgpool failed for user \"%s\"", */
1052 /* 					 frontend->username); */
1053 /* 			break; */
1054 		case uaMD5:
1055 			snprintf(errmessage, messagelen,
1056 					 "\"MD5\" authentication with pgpool failed for user \"%s\"",
1057 					 frontend->username);
1058 			break;
1059 
1060 		case uaSCRAM:
1061 			snprintf(errmessage, messagelen,
1062 					 "\"SCRAM\" authentication with pgpool failed for user \"%s\"",
1063 					 frontend->username);
1064 			break;
1065 
1066 		case uaCert:
1067 			snprintf(errmessage, messagelen,
1068 					 "\"CERT\" authentication with pgpool failed for user \"%s\"",
1069 					 frontend->username);
1070 			break;
1071 
1072 /* 		case uaCrypt: */
1073 /* 		case uaPassword: */
1074 /* 			snprintf(errmessage, messagelen, */
1075 /* 					 "password authentication with pgpool failed for user \"%s\"", */
1076 /* 					 frontend->username); */
1077 /* 			break; */
1078 #ifdef USE_PAM
1079 		case uaPAM:
1080 			snprintf(errmessage, messagelen,
1081 					 "PAM authentication with pgpool failed for user \"%s\"",
1082 					 frontend->username);
1083 			break;
1084 #endif							/* USE_PAM */
1085 		default:
1086 			snprintf(errmessage, messagelen,
1087 					 "authentication with pgpool failed for user \"%s\": invalid authentication method",
1088 					 frontend->username);
1089 			break;
1090 	}
1091 	close_all_backend_connections();
1092 	ereport(FATAL,
1093 			(return_code(2),
1094 			 errmsg("client authentication failed"),
1095 			 errdetail("%s", errmessage),
1096 			 errhint("see pgpool log for details")));
1097 
1098 }
1099 
1100 
1101 /*
1102  *  Close all of the cached backend connections.
1103  *
1104  *  This is exactly the same as send_frontend_exits() in child.c.
1105  */
1106 static void
close_all_backend_connections(void)1107 close_all_backend_connections(void)
1108 {
1109 	int			i;
1110 	POOL_CONNECTION_POOL *p = pool_connection_pool;
1111 
1112 	pool_sigset_t oldmask;
1113 
1114 	POOL_SETMASK2(&BlockSig, &oldmask);
1115 
1116 	for (i = 0; i < pool_config->max_pool; i++, p++)
1117 	{
1118 		if (!MASTER_CONNECTION(p))
1119 			continue;
1120 		if (MASTER_CONNECTION(p)->sp->user == NULL)
1121 			continue;
1122 		pool_send_frontend_exits(p);
1123 	}
1124 
1125 	POOL_SETMASK(&oldmask);
1126 }
1127 
1128 
1129 /*
1130  *  Determine what authentication method should be used when accessing database
1131  *  "database" from frontend "raddr", user "user".  Return the method and
1132  *  an optional argument (stored in fields of *frontend), and true for success.
1133  *
1134  *  Note that false indicates a problem with the hba config file.
1135  *  If the file is OK but does not contain any entry matching the request,
1136  *  we return true and method = uaReject.
1137  */
1138 static bool
hba_getauthmethod(POOL_CONNECTION * frontend)1139 hba_getauthmethod(POOL_CONNECTION * frontend)
1140 {
1141 	if (check_hba(frontend))
1142 		return true;
1143 	else
1144 		return false;
1145 }
1146 
1147 
1148 /*
1149 *	Scan the pre-parsed hba file, looking for a match to the port's connection
1150 *	request.
1151 */
1152 static bool
check_hba(POOL_CONNECTION * frontend)1153 check_hba(POOL_CONNECTION * frontend)
1154 {
1155 	ListCell   *line;
1156 	HbaLine    *hba;
1157 	MemoryContext oldcxt;
1158 
1159 	if (parsed_hba_lines == NULL)
1160 		return false;
1161 
1162 	foreach(line, parsed_hba_lines)
1163 	{
1164 		hba = (HbaLine *) lfirst(line);
1165 
1166 		/* Check connection type */
1167 		if (hba->conntype == ctLocal)
1168 		{
1169 			if (!IS_AF_UNIX(frontend->raddr.addr.ss_family))
1170 				continue;
1171 		}
1172 		else
1173 		{
1174 			if (IS_AF_UNIX(frontend->raddr.addr.ss_family))
1175 				continue;
1176 
1177 			/* Check SSL state */
1178 #ifdef USE_SSL
1179 			if (frontend->ssl)
1180 			{
1181 				/* Connection is SSL, match both "host" and "hostssl" */
1182 				if (hba->conntype == ctHostNoSSL)
1183 					continue;
1184 			}
1185 			else
1186 #endif
1187 			{
1188 				/* Connection is not SSL, match both "host" and "hostnossl" */
1189 				if (hba->conntype == ctHostSSL)
1190 					continue;
1191 			}
1192 
1193 			/* Check IP address */
1194 			switch (hba->ip_cmp_method)
1195 			{
1196 				case ipCmpMask:
1197 					if (hba->hostname)
1198 					{
1199 						if (!check_hostname(frontend,
1200 											hba->hostname))
1201 							continue;
1202 					}
1203 					else
1204 					{
1205 						if (!check_ip(&frontend->raddr,
1206 									  (struct sockaddr *) &hba->addr,
1207 									  (struct sockaddr *) &hba->mask))
1208 							continue;
1209 					}
1210 					break;
1211 				case ipCmpAll:
1212 					break;
1213 				case ipCmpSameHost:
1214 				case ipCmpSameNet:
1215 					if (!check_same_host_or_net(&frontend->raddr,
1216 												hba->ip_cmp_method))
1217 						continue;
1218 					break;
1219 				default:
1220 					/* shouldn't get here, but deem it no-match if so */
1221 					continue;
1222 			}
1223 		}
1224 
1225 		/* Check database and role */
1226 		if (!check_db(frontend->database, frontend->username, hba->databases))
1227 			continue;
1228 
1229 		if (!check_user(frontend->username, hba->users))
1230 			continue;
1231 
1232 		/* Found a record that matched! */
1233 		frontend->pool_hba = hba;
1234 		return true;
1235 	}
1236 
1237 	/* If no matching entry was found, then implicitly reject. */
1238 	oldcxt = MemoryContextSwitchTo(ProcessLoopContext);
1239 	hba = palloc0(sizeof(HbaLine));
1240 	MemoryContextSwitchTo(oldcxt);
1241 	hba->auth_method = uaImplicitReject;
1242 	frontend->pool_hba = hba;
1243 	return true;
1244 }
1245 
1246 static bool
ipv4eq(struct sockaddr_in * a,struct sockaddr_in * b)1247 ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
1248 {
1249 	return (a->sin_addr.s_addr == b->sin_addr.s_addr);
1250 }
1251 
1252 
1253 static bool
ipv6eq(struct sockaddr_in6 * a,struct sockaddr_in6 * b)1254 ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
1255 {
1256 	int			i;
1257 
1258 	for (i = 0; i < 16; i++)
1259 		if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
1260 			return false;
1261 
1262 	return true;
1263 }
1264 
1265 
1266 /*
1267  * Check whether host name matches pattern.
1268  */
1269 static bool
hostname_match(const char * pattern,const char * actual_hostname)1270 hostname_match(const char *pattern, const char *actual_hostname)
1271 {
1272 	if (pattern[0] == '.')		/* suffix match */
1273 	{
1274 		size_t		plen = strlen(pattern);
1275 		size_t		hlen = strlen(actual_hostname);
1276 
1277 		if (hlen < plen)
1278 			return false;
1279 
1280 		return (strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
1281 	}
1282 	else
1283 		return (strcasecmp(pattern, actual_hostname) == 0);
1284 }
1285 
1286 /*
1287  * Check to see if a connecting IP matches a given host name.
1288  */
1289 static bool
check_hostname(POOL_CONNECTION * frontend,const char * hostname)1290 check_hostname(POOL_CONNECTION * frontend, const char *hostname)
1291 {
1292 	struct addrinfo *gai_result,
1293 			   *gai;
1294 	int			ret;
1295 	bool		found;
1296 
1297 	/* Quick out if remote host name already known bad */
1298 	if (frontend->remote_hostname_resolv < 0)
1299 		return false;
1300 
1301 	/* Lookup remote host name if not already done */
1302 	if (!frontend->remote_hostname)
1303 	{
1304 		char		remote_hostname[NI_MAXHOST];
1305 
1306 		ret = getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen,
1307 							  remote_hostname, sizeof(remote_hostname),
1308 							  NULL, 0,
1309 							  NI_NAMEREQD);
1310 		if (ret != 0)
1311 		{
1312 			/* remember failure; don't complain in the Pgpool-II log yet */
1313 			frontend->remote_hostname_resolv = -2;
1314 			/* frontend->remote_hostname_errcode = ret; */
1315 			return false;
1316 		}
1317 
1318 		frontend->remote_hostname = pstrdup(remote_hostname);
1319 	}
1320 
1321 	/* Now see if remote host name matches this pg_hba line */
1322 	if (!hostname_match(hostname, frontend->remote_hostname))
1323 		return false;
1324 
1325 	/* If we already verified the forward lookup, we're done */
1326 	if (frontend->remote_hostname_resolv == +1)
1327 		return true;
1328 
1329 	/* Lookup IP from host name and check against original IP */
1330 	ret = getaddrinfo(frontend->remote_hostname, NULL, NULL, &gai_result);
1331 	if (ret != 0)
1332 	{
1333 		/* remember failure; don't complain in the postmaster log yet */
1334 		frontend->remote_hostname_resolv = -2;
1335 		/* frontend->remote_hostname_errcode = ret; */
1336 		return false;
1337 	}
1338 
1339 	found = false;
1340 	for (gai = gai_result; gai; gai = gai->ai_next)
1341 	{
1342 		if (gai->ai_addr->sa_family == frontend->raddr.addr.ss_family)
1343 		{
1344 			if (gai->ai_addr->sa_family == AF_INET)
1345 			{
1346 				if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
1347 						   (struct sockaddr_in *) &frontend->raddr.addr))
1348 				{
1349 					found = true;
1350 					break;
1351 				}
1352 			}
1353 			else if (gai->ai_addr->sa_family == AF_INET6)
1354 			{
1355 				if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
1356 						   (struct sockaddr_in6 *) &frontend->raddr.addr))
1357 				{
1358 					found = true;
1359 					break;
1360 				}
1361 			}
1362 		}
1363 	}
1364 
1365 	if (gai_result)
1366 		freeaddrinfo(gai_result);
1367 
1368 	if (!found)
1369 		ereport(DEBUG2,
1370 				(errmsg("pool_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
1371 						hostname)));
1372 
1373 	frontend->remote_hostname_resolv = found ? +1 : -1;
1374 
1375 	return found;
1376 }
1377 
1378 /*
1379  * pg_foreach_ifaddr callback: does client addr match this machine interface?
1380  */
1381 static void
check_network_callback(struct sockaddr * addr,struct sockaddr * netmask,void * cb_data)1382 check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
1383 					   void *cb_data)
1384 {
1385 	check_network_data *cn = (check_network_data *) cb_data;
1386 	struct sockaddr_storage mask;
1387 
1388 	/* Already found a match? */
1389 	if (cn->result)
1390 		return;
1391 
1392 	if (cn->method == ipCmpSameHost)
1393 	{
1394 		/* Make an all-ones netmask of appropriate length for family */
1395 		SockAddr_cidr_mask(&mask, NULL, addr->sa_family);
1396 		cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
1397 	}
1398 	else
1399 	{
1400 		/* Use the netmask of the interface itself */
1401 		cn->result = check_ip(cn->raddr, addr, netmask);
1402 	}
1403 }
1404 
1405 /*
1406  * Use pg_foreach_ifaddr to check a samehost or samenet match
1407  */
1408 static bool
check_same_host_or_net(SockAddr * raddr,IPCompareMethod method)1409 check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
1410 {
1411 	check_network_data cn;
1412 
1413 	cn.method = method;
1414 	cn.raddr = raddr;
1415 	cn.result = false;
1416 
1417 	errno = 0;
1418 	if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
1419 	{
1420 		elog(LOG, "error enumerating network interfaces: %m");
1421 		return false;
1422 	}
1423 
1424 	return cn.result;
1425 }
1426 
1427 /*
1428  * Check to see if a connecting IP matches the given address and netmask.
1429  */
1430 static bool
check_ip(SockAddr * raddr,struct sockaddr * addr,struct sockaddr * mask)1431 check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
1432 {
1433 	if (raddr->addr.ss_family == addr->sa_family &&
1434 		rangeSockAddr(&raddr->addr,
1435 					  (struct sockaddr_storage *) addr,
1436 					  (struct sockaddr_storage *) mask))
1437 		return true;
1438 	return false;
1439 }
1440 
1441 
1442 /*
1443  * Check comma user list for a specific user, handle group names.
1444  */
1445 static bool
check_user(char * user,List * tokens)1446 check_user(char *user, List *tokens)
1447 {
1448 	ListCell   *cell;
1449 	HbaToken   *tok;
1450 
1451 	foreach(cell, tokens)
1452 	{
1453 		tok = lfirst(cell);
1454 		if (!tok->quoted && tok->string[0] == '+')
1455 		{
1456 			/*
1457 			 * pgpool cannot accept groups. commented lines below are the
1458 			 * original code.
1459 			 */
1460 			ereport(LOG,
1461 					(errmsg("group token \"+\" is not supported by Pgpool-II")));
1462 			return false;
1463 		}
1464 		else if (token_matches(tok, user) ||
1465 				 token_is_keyword(tok, "all"))
1466 			return true;
1467 	}
1468 	return false;
1469 
1470 }
1471 
1472 
1473 
1474 /*
1475  * Check to see if db/user combination matches param string.
1476  */
1477 
1478 static bool
check_db(const char * dbname,const char * user,List * tokens)1479 check_db(const char *dbname, const char *user, List *tokens)
1480 {
1481 	ListCell   *cell;
1482 	HbaToken   *tok;
1483 
1484 	foreach(cell, tokens)
1485 	{
1486 		tok = lfirst(cell);
1487 		if (token_is_keyword(tok, "all"))
1488 			return true;
1489 		else if (token_is_keyword(tok, "sameuser"))
1490 		{
1491 			if (strcmp(dbname, user) == 0)
1492 				return true;
1493 		}
1494 		else if (token_is_keyword(tok, "samegroup") ||
1495 				 token_is_keyword(tok, "samerole"))
1496 		{
1497 			ereport(LOG,
1498 					(errmsg("group tokens \"samegroup\" and \"samerole\" are not supported by Pgpool-II")));
1499 			return false;
1500 		}
1501 		else if (token_matches(tok, dbname))
1502 			return true;
1503 	}
1504 	return false;
1505 }
1506 
1507 /*
1508  * tokenize_inc_file
1509  *		Expand a file included from another file into an hba "field"
1510  *
1511  * Opens and tokenises a file included from another HBA config file with @,
1512  * and returns all values found therein as a flat list of HbaTokens.  If a
1513  * @-token is found, recursively expand it.  The newly read tokens are
1514  * appended to "tokens" (so that foo,bar,@baz does what you expect).
1515  * All new tokens are allocated in caller's memory context.
1516  *
1517  * In event of an error, log a message at ereport level elevel, and also
1518  * set *err_msg to a string describing the error.  Note that the result
1519  * may be non-NIL anyway, so *err_msg must be tested to determine whether
1520  * there was an error.
1521  */
1522 static List *
tokenize_inc_file(List * tokens,const char * outer_filename,const char * inc_filename,int elevel,char ** err_msg)1523 tokenize_inc_file(List *tokens,
1524 				  const char *outer_filename,
1525 				  const char *inc_filename,
1526 				  int elevel,
1527 				  char **err_msg)
1528 {
1529 	char	   *inc_fullname;
1530 	FILE	   *inc_file;
1531 	List	   *inc_lines;
1532 	ListCell   *inc_line;
1533 	MemoryContext linecxt;
1534 
1535 	if (is_absolute_path(inc_filename))
1536 	{
1537 		/* absolute path is taken as-is */
1538 		inc_fullname = pstrdup(inc_filename);
1539 	}
1540 	else
1541 	{
1542 		/* relative path is relative to dir of calling file */
1543 		inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
1544 									   strlen(inc_filename) + 1);
1545 		strcpy(inc_fullname, outer_filename);
1546 		get_parent_directory(inc_fullname);
1547 		join_path_components(inc_fullname, inc_fullname, inc_filename);
1548 		canonicalize_path(inc_fullname);
1549 	}
1550 
1551 	inc_file = fopen(inc_fullname, "r");
1552 	if (inc_file == NULL)
1553 	{
1554 		int			save_errno = errno;
1555 
1556 		ereport(elevel,
1557 				(errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
1558 						inc_filename, inc_fullname)));
1559 		*err_msg = psprintf("could not open secondary authentication file \"@%s\" as \"%s\": %s",
1560 							inc_filename, inc_fullname, strerror(save_errno));
1561 		pfree(inc_fullname);
1562 		return tokens;
1563 	}
1564 
1565 	/* There is possible recursion here if the file contains @ */
1566 	linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, elevel);
1567 	fclose(inc_file);
1568 
1569 	/* Copy all tokens found in the file and append to the tokens list */
1570 	foreach(inc_line, inc_lines)
1571 	{
1572 		TokenizedLine *tok_line = (TokenizedLine *) lfirst(inc_line);
1573 		ListCell   *inc_field;
1574 
1575 		/* If any line has an error, propagate that up to caller */
1576 		if (tok_line->err_msg)
1577 		{
1578 			*err_msg = pstrdup(tok_line->err_msg);
1579 			break;
1580 		}
1581 
1582 		foreach(inc_field, tok_line->fields)
1583 		{
1584 			List	   *inc_tokens = lfirst(inc_field);
1585 			ListCell   *inc_token;
1586 
1587 			foreach(inc_token, inc_tokens)
1588 			{
1589 				HbaToken   *token = lfirst(inc_token);
1590 
1591 				tokens = lappend(tokens, copy_hba_token(token));
1592 			}
1593 		}
1594 	}
1595 
1596 	MemoryContextDelete(linecxt);
1597 	return tokens;
1598 }
1599 
1600 
1601 
1602 /*
1603  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
1604  * so provide our own version.
1605  */
1606 static bool
pg_isblank(const char c)1607 pg_isblank(const char c)
1608 {
1609 	return c == ' ' || c == '\t' || c == '\r';
1610 }
1611 
1612 /*
1613  * Tokenize the given file.
1614  *
1615  * The output is a list of TokenizedLine structs; see struct definition above.
1616  *
1617  * filename: the absolute path to the target file
1618  * file: the already-opened target file
1619  * tok_lines: receives output list
1620  * elevel: message logging level
1621  *
1622  * Errors are reported by logging messages at ereport level elevel and by
1623  * adding TokenizedLine structs containing non-null err_msg fields to the
1624  * output list.
1625  *
1626  * Return value is a memory context which contains all memory allocated by
1627  * this function (it's a child of caller's context).
1628  */
1629 static MemoryContext
tokenize_file(const char * filename,FILE * file,List ** tok_lines,int elevel)1630 tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
1631 {
1632 	int			line_number = 1;
1633 	MemoryContext linecxt;
1634 	MemoryContext oldcxt;
1635 
1636 	linecxt = AllocSetContextCreate(CurrentMemoryContext,
1637 									"tokenize_file",
1638 									ALLOCSET_SMALL_SIZES);
1639 	oldcxt = MemoryContextSwitchTo(linecxt);
1640 
1641 	*tok_lines = NIL;
1642 
1643 	while (!feof(file) && !ferror(file))
1644 	{
1645 		char		rawline[MAX_LINE];
1646 		char	   *lineptr;
1647 		List	   *current_line = NIL;
1648 		char	   *err_msg = NULL;
1649 
1650 		if (!fgets(rawline, sizeof(rawline), file))
1651 		{
1652 			int			save_errno = errno;
1653 
1654 			if (!ferror(file))
1655 				break;			/* normal EOF */
1656 			/* I/O error! */
1657 			ereport(elevel,
1658 					(errmsg("could not read file \"%s\": %m", filename)));
1659 			err_msg = psprintf("could not read file \"%s\": %s",
1660 							   filename, strerror(save_errno));
1661 			rawline[0] = '\0';
1662 		}
1663 		if (strlen(rawline) == MAX_LINE - 1)
1664 		{
1665 			/* Line too long! */
1666 			ereport(elevel,
1667 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1668 					 errmsg("authentication file line too long"),
1669 					 errcontext("line %d of configuration file \"%s\"",
1670 								line_number, filename)));
1671 			err_msg = "authentication file line too long";
1672 		}
1673 
1674 		/* Strip trailing linebreak from rawline */
1675 		lineptr = rawline + strlen(rawline) - 1;
1676 		while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r'))
1677 			*lineptr-- = '\0';
1678 
1679 		/* Parse fields */
1680 		lineptr = rawline;
1681 		while (*lineptr && err_msg == NULL)
1682 		{
1683 			List	   *current_field;
1684 
1685 			current_field = next_field_expand(filename, &lineptr,
1686 											  elevel, &err_msg);
1687 			/* add field to line, unless we are at EOL or comment start */
1688 			if (current_field != NIL)
1689 				current_line = lappend(current_line, current_field);
1690 		}
1691 
1692 		/* Reached EOL; emit line to TokenizedLine list unless it's boring */
1693 		if (current_line != NIL || err_msg != NULL)
1694 		{
1695 			TokenizedLine *tok_line;
1696 
1697 			tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine));
1698 			tok_line->fields = current_line;
1699 			tok_line->line_num = line_number;
1700 			tok_line->raw_line = pstrdup(rawline);
1701 			tok_line->err_msg = err_msg;
1702 			*tok_lines = lappend(*tok_lines, tok_line);
1703 		}
1704 
1705 		line_number++;
1706 	}
1707 
1708 	MemoryContextSwitchTo(oldcxt);
1709 
1710 	return linecxt;
1711 }
1712 
1713 
1714 /*
1715  * Tokenize one HBA field from a line, handling file inclusion and comma lists.
1716  *
1717  * filename: current file's pathname (needed to resolve relative pathnames)
1718  * *lineptr: current line pointer, which will be advanced past field
1719  *
1720  * In event of an error, log a message at ereport level elevel, and also
1721  * set *err_msg to a string describing the error.  Note that the result
1722  * may be non-NIL anyway, so *err_msg must be tested to determine whether
1723  * there was an error.
1724  *
1725  * The result is a List of HbaToken structs, one for each token in the field,
1726  * or NIL if we reached EOL.
1727  */
1728 static List *
next_field_expand(const char * filename,char ** lineptr,int elevel,char ** err_msg)1729 next_field_expand(const char *filename, char **lineptr,
1730 				  int elevel, char **err_msg)
1731 {
1732 	char		buf[MAX_TOKEN];
1733 	bool		trailing_comma;
1734 	bool		initial_quote;
1735 	List	   *tokens = NIL;
1736 
1737 	do
1738 	{
1739 		if (!next_token(lineptr, buf, sizeof(buf),
1740 						&initial_quote, &trailing_comma,
1741 						elevel, err_msg))
1742 			break;
1743 
1744 		/* Is this referencing a file? */
1745 		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
1746 			tokens = tokenize_inc_file(tokens, filename, buf + 1,
1747 									   elevel, err_msg);
1748 		else
1749 			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
1750 	} while (trailing_comma && (*err_msg == NULL));
1751 
1752 	return tokens;
1753 }
1754 
1755 /*
1756  * Grab one token out of the string pointed to by *lineptr.
1757  *
1758  * Tokens are strings of non-blank
1759  * characters bounded by blank characters, commas, beginning of line, and
1760  * end of line. Blank means space or tab. Tokens can be delimited by
1761  * double quotes (this allows the inclusion of blanks, but not newlines).
1762  * Comments (started by an unquoted '#') are skipped.
1763  *
1764  * The token, if any, is returned at *buf (a buffer of size bufsz), and
1765  * *lineptr is advanced past the token.
1766  *
1767  * Also, we set *initial_quote to indicate whether there was quoting before
1768  * the first character.  (We use that to prevent "@x" from being treated
1769  * as a file inclusion request.  Note that @"x" should be so treated;
1770  * we want to allow that to support embedded spaces in file paths.)
1771  *
1772  * We set *terminating_comma to indicate whether the token is terminated by a
1773  * comma (which is not returned).
1774  *
1775  * In event of an error, log a message at ereport level elevel, and also
1776  * set *err_msg to a string describing the error.  Currently the only
1777  * possible error is token too long for buf.
1778  *
1779  * If successful: store null-terminated token at *buf and return TRUE.
1780  * If no more tokens on line: set *buf = '\0' and return FALSE.
1781  * If error: fill buf with truncated or misformatted token and return FALSE.
1782  */
1783 static bool
next_token(char ** lineptr,char * buf,int bufsz,bool * initial_quote,bool * terminating_comma,int elevel,char ** err_msg)1784 next_token(char **lineptr, char *buf, int bufsz,
1785 		   bool *initial_quote, bool *terminating_comma,
1786 		   int elevel, char **err_msg)
1787 {
1788 	int			c;
1789 	char	   *start_buf = buf;
1790 	char	   *end_buf = buf + (bufsz - 1);
1791 	bool		in_quote = false;
1792 	bool		was_quote = false;
1793 	bool		saw_quote = false;
1794 
1795 	Assert(end_buf > start_buf);
1796 
1797 	*initial_quote = false;
1798 	*terminating_comma = false;
1799 
1800 	/* Move over any whitespace and commas preceding the next token */
1801 	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
1802 		;
1803 
1804 	/*
1805 	 * Build a token in buf of next characters up to EOL, unquoted comma, or
1806 	 * unquoted whitespace.
1807 	 */
1808 	while (c != '\0' &&
1809 		   (!pg_isblank(c) || in_quote))
1810 	{
1811 		/* skip comments to EOL */
1812 		if (c == '#' && !in_quote)
1813 		{
1814 			while ((c = (*(*lineptr)++)) != '\0')
1815 				;
1816 			break;
1817 		}
1818 
1819 		if (buf >= end_buf)
1820 		{
1821 			*buf = '\0';
1822 			ereport(elevel,
1823 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
1824 					 errmsg("authentication file token too long, skipping: \"%s\"",
1825 							start_buf)));
1826 			*err_msg = "authentication file token too long";
1827 			/* Discard remainder of line */
1828 			while ((c = (*(*lineptr)++)) != '\0')
1829 				;
1830 			/* Un-eat the '\0', in case we're called again */
1831 			(*lineptr)--;
1832 			return false;
1833 		}
1834 
1835 		/* we do not pass back a terminating comma in the token */
1836 		if (c == ',' && !in_quote)
1837 		{
1838 			*terminating_comma = true;
1839 			break;
1840 		}
1841 
1842 		if (c != '"' || was_quote)
1843 			*buf++ = c;
1844 
1845 		/* Literal double-quote is two double-quotes */
1846 		if (in_quote && c == '"')
1847 			was_quote = !was_quote;
1848 		else
1849 			was_quote = false;
1850 
1851 		if (c == '"')
1852 		{
1853 			in_quote = !in_quote;
1854 			saw_quote = true;
1855 			if (buf == start_buf)
1856 				*initial_quote = true;
1857 		}
1858 
1859 		c = *(*lineptr)++;
1860 	}
1861 
1862 	/*
1863 	 * Un-eat the char right after the token (critical in case it is '\0',
1864 	 * else next call will read past end of string).
1865 	 */
1866 	(*lineptr)--;
1867 
1868 	*buf = '\0';
1869 
1870 	return (saw_quote || buf > start_buf);
1871 }
1872 
1873 /*
1874  * Construct a palloc'd HbaToken struct, copying the given string.
1875  */
1876 static HbaToken *
make_hba_token(const char * token,bool quoted)1877 make_hba_token(const char *token, bool quoted)
1878 {
1879 	HbaToken   *hbatoken;
1880 	int			toklen;
1881 
1882 	toklen = strlen(token);
1883 	/* we copy string into same palloc block as the struct */
1884 	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
1885 	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
1886 	hbatoken->quoted = quoted;
1887 	memcpy(hbatoken->string, token, toklen + 1);
1888 
1889 	return hbatoken;
1890 }
1891 
1892 /*
1893  * Copy a HbaToken struct into freshly palloc'd memory.
1894  */
1895 static HbaToken *
copy_hba_token(HbaToken * in)1896 copy_hba_token(HbaToken *in)
1897 {
1898 	HbaToken   *out = make_hba_token(in->string, in->quoted);
1899 
1900 	return out;
1901 }
1902 
1903 #ifdef USE_PAM
1904 
1905 /*
1906  * PAM conversation function
1907  */
1908 static int
pam_passwd_conv_proc(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)1909 pam_passwd_conv_proc(int num_msg, const struct pam_message **msg,
1910 					 struct pam_response **resp, void *appdata_ptr)
1911 {
1912 	if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
1913 	{
1914 		switch (msg[0]->msg_style)
1915 		{
1916 			case PAM_ERROR_MSG:
1917 				ereport(LOG,
1918 						(errmsg("PAM Error"),
1919 						 errdetail("error from underlying PAM layer: %s",
1920 								   msg[0]->msg)));
1921 				return PAM_CONV_ERR;
1922 			default:
1923 				ereport(LOG,
1924 						(errmsg("PAM Error"),
1925 						 errdetail("unsupported PAM conversation %d/%s",
1926 								   msg[0]->msg_style, msg[0]->msg)));
1927 				return PAM_CONV_ERR;
1928 		}
1929 	}
1930 
1931 	if (!appdata_ptr)
1932 	{
1933 		/*
1934 		 * Workaround for Solaris 2.6 where the PAM library is broken and does
1935 		 * not pass appdata_ptr to the conversation routine
1936 		 */
1937 		appdata_ptr = pam_passwd;
1938 	}
1939 
1940 	/*
1941 	 * Password wasn't passed to PAM the first time around - let's go ask the
1942 	 * client to send a password, which we then stuff into PAM.
1943 	 */
1944 	if (strlen(appdata_ptr) == 0)
1945 	{
1946 		char	   *passwd;
1947 
1948 		sendAuthRequest(pam_frontend_kludge, AUTH_REQ_PASSWORD);
1949 		passwd = recv_password_packet(pam_frontend_kludge);
1950 
1951 		if (passwd == NULL)
1952 			return PAM_CONV_ERR;	/* client didn't want to send password */
1953 
1954 		if (strlen(passwd) == 0)
1955 		{
1956 			ereport(LOG,
1957 					(errmsg("PAM Error"),
1958 					 errdetail("empty password returned by client")));
1959 			return PAM_CONV_ERR;
1960 		}
1961 		appdata_ptr = passwd;
1962 	}
1963 
1964 	/*
1965 	 * PAM will free this memory in * pam_end() Do not use Palloc and freinds
1966 	 * to allocate this memory, Since the PAM library will be freeing this
1967 	 * memory who knowns nothing about our MemoryManager
1968 	 */
1969 	*resp = calloc(num_msg, sizeof(struct pam_response));
1970 
1971 	(*resp)[0].resp = strdup((char *) appdata_ptr);
1972 	(*resp)[0].resp_retcode = 0;
1973 
1974 	return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
1975 }
1976 
1977 
1978 /*
1979  * Check authentication against PAM.
1980  */
CheckPAMAuth(POOL_CONNECTION * frontend,char * user,char * password)1981 static POOL_STATUS CheckPAMAuth(POOL_CONNECTION * frontend, char *user, char *password)
1982 {
1983 	int			retval;
1984 	pam_handle_t *pamh = NULL;
1985 
1986 	/*
1987 	 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
1988 	 * workaround
1989 	 */
1990 	pam_passwd = password;
1991 
1992 	/*
1993 	 * Set the application data portion of the conversation struct This is
1994 	 * later used inside the PAM conversation to pass the password to the
1995 	 * authentication module.
1996 	 */
1997 	pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
1998 													 * not allocated */
1999 
2000 	/* Optionally, one can set the service name in pool_hba.conf */
2001 	if (frontend->pool_hba->pamservice && frontend->pool_hba->pamservice[0] != '\0')
2002 		retval = pam_start(frontend->pool_hba->pamservice, "pgpool@",
2003 						   &pam_passw_conv, &pamh);
2004 	else
2005 		retval = pam_start(PGPOOL_PAM_SERVICE, "pgpool@",
2006 						   &pam_passw_conv, &pamh);
2007 
2008 	if (retval != PAM_SUCCESS)
2009 	{
2010 		pam_passwd = NULL;		/* Unset pam_passwd */
2011 		ereport(FATAL,
2012 				(return_code(2),
2013 				 errmsg("failed authentication against PAM"),
2014 				 errdetail("unable to create PAM authenticator: %s", pam_strerror(pamh, retval))));
2015 	}
2016 
2017 	retval = pam_set_item(pamh, PAM_USER, user);
2018 	if (retval != PAM_SUCCESS)
2019 	{
2020 		pam_passwd = NULL;		/* Unset pam_passwd */
2021 		ereport(FATAL,
2022 				(return_code(2),
2023 				 errmsg("failed authentication against PAM"),
2024 				 errdetail("pam_set_item(PAM_USER) failed: %s", pam_strerror(pamh, retval))));
2025 	}
2026 
2027 	retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
2028 	if (retval != PAM_SUCCESS)
2029 	{
2030 		pam_passwd = NULL;		/* Unset pam_passwd */
2031 		ereport(FATAL,
2032 				(return_code(2),
2033 				 errmsg("failed authentication against PAM"),
2034 				 errdetail("pam_set_item(PAM_CONV) failed: %s", pam_strerror(pamh, retval))));
2035 	}
2036 
2037 	retval = pam_authenticate(pamh, 0);
2038 	if (retval != PAM_SUCCESS)	/* service name does not exist */
2039 	{
2040 		pam_passwd = NULL;		/* Unset pam_passwd */
2041 		ereport(FATAL,
2042 				(return_code(2),
2043 				 errmsg("failed authentication against PAM"),
2044 				 errdetail("pam_authenticate failed: %s", pam_strerror(pamh, retval))));
2045 	}
2046 
2047 	retval = pam_acct_mgmt(pamh, 0);
2048 	if (retval != PAM_SUCCESS)
2049 	{
2050 		pam_passwd = NULL;		/* Unset pam_passwd */
2051 		ereport(FATAL,
2052 				(return_code(2),
2053 				 errmsg("failed authentication against PAM"),
2054 				 errdetail("system call pam_acct_mgmt failed : %s", pam_strerror(pamh, retval))));
2055 	}
2056 
2057 	retval = pam_end(pamh, retval);
2058 	if (retval != PAM_SUCCESS)
2059 	{
2060 		ereport(FATAL,
2061 				(return_code(2),
2062 				 errmsg("failed authentication against PAM"),
2063 				 errdetail("unable to release PAM authenticator: %s", pam_strerror(pamh, retval))));
2064 	}
2065 
2066 	pam_passwd = NULL;
2067 	return POOL_CONTINUE;
2068 }
2069 
2070 #endif							/* USE_PAM */
2071 
2072 #ifdef NOT_USED
CheckUserExist(char * username)2073 static POOL_STATUS CheckUserExist(char *username)
2074 {
2075 	char	   *passwd;
2076 
2077 	/* Look for the entry in pool_passwd */
2078 	passwd = pool_get_passwd(username);
2079 
2080 	if (passwd == NULL)
2081 		return POOL_ERROR;
2082 
2083 	/*
2084 	 * Ok for now. Actual authentication will be performed later.
2085 	 */
2086 	return POOL_CONTINUE;
2087 }
2088 #endif
2089