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