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