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