]> git.8kb.co.uk Git - pgpool-ii/pgpool-ii_2.2.5/blob - pool_hba.c
Attempt to send a proper failure message to frontend when authentication
[pgpool-ii/pgpool-ii_2.2.5] / pool_hba.c
1 /* -*-pgsql-c-*- */
2 /*
3  *
4  * $Header: /cvsroot/pgpool/pgpool-II/pool_hba.c,v 1.5.2.1 2009/08/22 04:19:49 t-ishii Exp $
5  *
6  * pgpool: a language independent connection pool server for PostgreSQL
7  * written by Tatsuo Ishii
8  *
9  * Portions Copyright (c) 2003-2008     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 #include "pool.h"
34 #include "pool_path.h"
35 #include "pool_ip.h"
36 #include "parser/pool_memory.h"
37 #include "parser/pg_list.h"
38
39 #define MULTI_VALUE_SEP "\001" /* delimiter for multi-valued column strings */
40 #define MAX_TOKEN       256
41
42 static List *hba_lines = NIL;
43 static List *hba_line_nums = NIL;
44 static char *hbaFileName;
45
46 static POOL_MEMORY_POOL *hba_memory_context = NULL;
47
48 static void sendAuthRequest(POOL_CONNECTION *frontend, AuthRequest areq);
49 static void auth_failed(POOL_CONNECTION *frontend);
50 static void close_all_backend_connections(void);
51 static bool hba_getauthmethod(POOL_CONNECTION *frontend);
52 static bool check_hba(POOL_CONNECTION *frontend);
53 static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend, bool *found_p, bool *error_p);
54 static void parse_hba_auth(ListCell **line_item, UserAuth *userauth_p, char **auth_arg_p, bool *error_p);
55 static bool check_user(char *user, char *param_str);
56 static bool check_db(char *dbname, char *user, char *param_str);
57 static void free_lines(List **lines, List **line_nums);
58 static void tokenize_file(const char *filename, FILE *file, List **lines, List **line_nums);
59 static char *tokenize_inc_file(const char *outer_filename, const char *inc_filename);
60 static bool pg_isblank(const char c);
61 static void next_token(FILE *fp, char *buf, int bufsz);
62 static char * next_token_expand(const char *filename, FILE *file);
63
64 #ifdef USE_PAM
65 #ifdef HAVE_PAM_PAM_APPL_H
66 #include <pam/pam_appl.h>
67 #endif
68 #ifdef HAVE_SECURITY_PAM_APPL_H
69 #include <security/pam_appl.h>
70 #endif
71
72 #define PGPOOL_PAM_SERVICE "pgpool" /* Service name passed to PAM */
73
74 static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *password);
75 static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr);
76 /*
77  * recv_password_packet is usually used with authentications that require a client
78  * password. However, pgpool's hba function only uses it for PAM authentication,
79  * so declare a prototype here in "#ifdef USE_PAM" to avoid compilation warning.
80  */
81 static char *recv_password_packet(POOL_CONNECTION *frontend);
82
83 static struct pam_conv pam_passw_conv = {
84         &pam_passwd_conv_proc,
85         NULL
86 };
87
88 static char *pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
89 static POOL_CONNECTION *pam_frontend_kludge; /* Workaround for passing
90                                                                                           * POOL_CONNECTION *frontend
91                                                                                           * into pam_passwd_conv_proc */
92 #endif /* USE_PAM */
93
94
95 /*
96  * read in hba config file
97  */
98 void load_hba(char *hbapath)
99 {
100         FILE *file;
101
102         POOL_MEMORY_POOL *old_context;
103         if (hba_memory_context == NULL)
104         {
105                 hba_memory_context = pool_memory_create(PARSER_BLOCK_SIZE);
106                 if (hba_memory_context == NULL)
107                 {
108                         pool_error("load_hba: pool_memory_create() failed");
109                         exit(1);
110                 }
111         }
112         /* switch memory context */
113         old_context = pool_memory;
114         pool_memory = hba_memory_context;
115
116         if (hba_lines || hba_line_nums)
117                 free_lines(&hba_lines, &hba_line_nums);
118
119         file = fopen(hbapath, "r");
120         if (!file)
121         {
122                 pool_error("could not open \"%s\". reason: %s",
123                                    hbapath, strerror(errno));
124                 exit(1);
125         }
126
127         pool_debug("loading \"%s\" for client authentication configuration file",
128                            hbapath);
129
130         tokenize_file(hbapath, file, &hba_lines, &hba_line_nums);
131         fclose(file);
132
133         hbaFileName = pstrdup(hbapath);
134
135         /* switch old memory context */
136         pool_memory = old_context;
137 }
138
139
140 /*
141  * do frontend <-> pgpool authentication based on pool_hba.conf
142  */
143 void ClientAuthentication(POOL_CONNECTION *frontend)
144 {
145         POOL_STATUS status = POOL_ERROR;
146
147         if (! hba_getauthmethod(frontend))
148         {
149                 pool_error("missing or erroneous pool_hba.conf file");
150                 pool_send_error_message(frontend, frontend->protoVersion, "XX000",
151                                                                 "missing or erroneous pool_hba.conf file", "",
152                                                                 "See pgpool log for details.", __FILE__, __LINE__);
153                 close_all_backend_connections();
154                 /*
155                  * use exit(2) since this is not so fatal. other entries in
156                  * pool_hba.conf may be valid, so treat it as reject.
157                  */
158                 child_exit(2);
159         }
160
161         switch (frontend->auth_method)
162         {
163                 case uaReject:
164                 {
165             /*
166                          * This could have come from an explicit "reject" entry in
167                          * pool_hba.conf, but more likely it means there was no matching
168                          * entry.  Take pity on the poor user and issue a helpful
169                          * error message.  NOTE: this is not a security breach,
170                          * because all the info reported here is known at the frontend
171                          * and must be assumed known to bad guys. We're merely helping
172                          * out the less clueful good guys.
173                          */
174                         char hostinfo[NI_MAXHOST];
175                         char *errmessage;
176                         int messagelen;
177
178                         getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen,
179                                                         hostinfo, sizeof(hostinfo),
180                                                         NULL, 0,
181                                                         NI_NUMERICHOST);
182
183                         messagelen = sizeof(hostinfo) +
184                                 strlen(frontend->username) + strlen(frontend->database) + 80;
185                         if ((errmessage = (char *)malloc(messagelen+1)) == NULL)
186                         {
187                                 pool_error("ClientAuthentication: malloc failed: %s", strerror(errno));
188                                 child_exit(1);
189                         }
190
191 #ifdef USE_SSL
192                         snprintf(errmessage, messagelen+7, /* +7 is for "SSL off" */
193                                          "no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
194                                          hostinfo, frontend->username, frontend->database,
195                                          frontend->ssl ? "SSL on" : "SSL off");
196 #else
197                         snprintf(errmessage, messagelen,
198                                          "no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
199                                          hostinfo, frontend->username, frontend->database);
200 #endif
201                         pool_error(errmessage);
202                         pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage,
203                                                                         "", "", __FILE__, __LINE__);
204
205                         free(errmessage);
206                         break;
207                 }
208
209 /*              case uaKrb4: */
210 /*                      break; */
211
212 /*              case uaKrb5: */
213 /*                      break; */
214
215 /*              case uaIdent: */
216 /*                      break; */
217
218 /*              case uaMD5: */
219 /*                      break; */
220
221 /*              case uaCrypt: */
222 /*                      break; */
223
224 /*              case uaPassword: */
225 /*                      break; */
226
227 #ifdef USE_PAM
228                 case uaPAM:
229                         pam_frontend_kludge = frontend;
230                         status = CheckPAMAuth(frontend, frontend->username, "");
231                         break;
232 #endif /* USE_PAM */
233
234                 case uaTrust:
235                         status = POOL_CONTINUE;
236                         break;
237         }
238
239         if (status == POOL_CONTINUE)
240                 sendAuthRequest(frontend, AUTH_REQ_OK);
241         else if (status != POOL_CONTINUE)
242                 auth_failed(frontend);
243 }
244
245
246 static void sendAuthRequest(POOL_CONNECTION *frontend, AuthRequest areq)
247 {
248         int wsize;                                      /* number of bytes to write */
249         int areq_nbo;                           /* areq in network byte order */
250
251         /*
252          * If AUTH_REQ_OK, then frontend is OK to connect __with_pgpool__.
253          * Do not send 'R' to the frontend, he still needs to authenticate
254          * himself with the backend.
255          */
256         if (areq == AUTH_REQ_OK)
257                 return;
258
259         /* request a password */
260         pool_write(frontend, "R", 1);
261
262         if (frontend->protoVersion == PROTO_MAJOR_V3)
263         {
264 /*              if (areq == AUTH_REQ_MD5) */
265 /*                      wsize = htonl(sizeof(int)*2+4); */
266 /*              else if (areq == AUTH_REQ_CRYPT) */
267 /*                      wsize = htonl(sizeof(int)*2+2); */
268 /*              else */
269                         wsize = htonl(sizeof(int)*2);
270                 pool_write(frontend, &wsize, sizeof(int));
271         }
272
273         areq_nbo = htonl(areq);
274         pool_write(frontend, &areq_nbo, sizeof(int));
275
276         /* Add the salt for encrypted passwords. */
277 /*      if (areq == AUTH_REQ_MD5) */
278 /*              pq_sendbytes(&buf, port->md5Salt, 4); */
279 /*      else if (areq == AUTH_REQ_CRYPT) */
280 /*              pq_sendbytes(&buf, port->cryptSalt, 2); */
281
282         pool_flush(frontend);
283 }
284
285
286 #ifdef USE_PAM                                  /* see the prototype comment  */
287
288 /*
289  * Collect password response packet from frontend.
290  *
291  * Returns NULL if couldn't get password, else malloc'd string.
292  */
293 static char *recv_password_packet(POOL_CONNECTION *frontend)
294 {
295         int rsize;
296         char *passwd;
297         char *returnVal;
298
299         if (frontend->protoVersion == PROTO_MAJOR_V3)
300         {
301                 /* Expect 'p' message type */
302                 char kind;
303
304                 if (pool_read(frontend, &kind, 1) < 0)
305                         return NULL;
306
307                 if (kind != 'p')
308                 {
309                         pool_error("expected password response, got message type %c",
310                                            kind);
311                         return NULL;            /* bad message type */
312                 }
313         }
314         /* pre-3.0 protocol does not send a message type */
315
316         if (pool_read(frontend, &rsize, sizeof(int)) < 0)
317                 return NULL;
318
319         rsize = ntohl(rsize) - 4;
320         passwd = pool_read2(frontend, rsize); /* retrieve password */
321         if (passwd == NULL)
322                 return NULL;
323
324         /* Do not echo password to logs, for security. */
325         pool_debug("received password packet from frontend for pgpool's HBA");
326
327         /*
328          * Return the received string.  Note we do not attempt to do any
329          * character-set conversion on it; since we don't yet know the
330          * client's encoding, there wouldn't be much point.
331          */
332         returnVal = strdup(passwd);
333         if (returnVal == NULL)
334         {
335                 pool_error("recv_password_packet: strdup failed: %s", strerror(errno));
336                 exit(1);
337         }
338         return returnVal;
339 }
340
341 #endif /* USE_PAM */
342
343 /*
344  * Tell the user the authentication failed.
345  */
346 static void auth_failed(POOL_CONNECTION *frontend)
347 {
348         bool send_error_to_frontend = true;
349         int messagelen;
350         char *errmessage;
351
352         messagelen = strlen(frontend->username) + 100;
353         if ((errmessage = (char *)malloc(messagelen+1)) == NULL)
354         {
355                 pool_error("auth_failed: malloc failed: %s", strerror(errno));
356                 child_exit(1);
357         }
358
359         switch (frontend->auth_method)
360         {
361                 case uaReject:
362                         snprintf(errmessage, messagelen,
363                                          "authentication with pgpool failed for user \"%s\": host rejected",
364                                          frontend->username);
365                         /*
366                          * if uaReject, frontend should have received 'E' and disconnected already.
367                          */
368                         send_error_to_frontend = false;
369                         break;
370 /*              case uaKrb4: */
371 /*                      snprintf(errmessage, messagelen, */
372 /*                                       "Kerberos 4 authentication with pgpool failed for user \"%s\"", */
373 /*                                       frontend->username); */
374 /*                      break; */
375 /*              case uaKrb5: */
376 /*                      snprintf(errmessage, messagelen, */
377 /*                                       "Kerberos 5 authentication with pgpool failed for user \"%s\"", */
378 /*                                       frontend->username); */
379 /*                      break; */
380                 case uaTrust:
381                         snprintf(errmessage, messagelen,
382                                          "\"trust\" authentication with pgpool failed for user \"%s\"",
383                                          frontend->username);
384                         break;
385 /*              case uaIdent: */
386 /*                      snprintf(errmessage, messagelen, */
387 /*                                       "Ident authentication with pgpool failed for user \"%s\"", */
388 /*                                       frontend->username); */
389 /*                      break; */
390 /*              case uaMD5: */
391 /*              case uaCrypt: */
392 /*              case uaPassword: */
393 /*                      snprintf(errmessage, messagelen, */
394 /*                                       "password authentication with pgpool failed for user \"%s\"", */
395 /*                                       frontend->username); */
396 /*                      break; */
397 #ifdef USE_PAM
398                 case uaPAM:
399                         snprintf(errmessage, messagelen,
400                                          "PAM authentication with pgpool failed for user \"%s\"",
401                                          frontend->username);
402                         break;
403 #endif /* USE_PAM */
404                 default:
405                         snprintf(errmessage, messagelen,
406                                          "authentication with pgpool failed for user \"%s\": invalid authentication method",
407                                          frontend->username);
408                         break;
409         }
410
411         pool_error(errmessage);
412         if (send_error_to_frontend)
413                 pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage,
414                                                                 "", "", __FILE__, __LINE__);
415
416         /*
417          * don't need to free(errmessage). I will just kill myself.
418          */
419         close_all_backend_connections();
420         child_exit(2);
421 }
422
423
424 /*
425  *  Close all of the cached backend connections.
426  *
427  *  This is exactly the same as send_frontend_exits() in child.c.
428  */
429 static void close_all_backend_connections(void)
430 {
431         int i;
432         POOL_CONNECTION_POOL *p = pool_connection_pool;
433
434 #ifdef HAVE_SIGPROCMASK
435         sigset_t oldmask;
436 #else
437         int     oldmask;
438 #endif
439
440         POOL_SETMASK2(&BlockSig, &oldmask);
441
442         for (i=0;i<pool_config->max_pool;i++, p++)
443         {
444                 if (!MASTER_CONNECTION(p))
445                         continue;
446                 if (MASTER_CONNECTION(p)->sp->user == NULL)
447                         continue;
448                 pool_send_frontend_exits(p);
449         }
450
451         POOL_SETMASK(&oldmask);
452 }
453
454
455 /*
456  *  Determine what authentication method should be used when accessing database
457  *  "database" from frontend "raddr", user "user".  Return the method and
458  *  an optional argument (stored in fields of *frontend), and true for success.
459  *
460  *  Note that false indicates a problem with the hba config file.
461  *  If the file is OK but does not contain any entry matching the request,
462  *  we return true and method = uaReject.
463  */
464 static bool hba_getauthmethod(POOL_CONNECTION *frontend)
465 {
466         if (check_hba(frontend))
467                 return true;
468         else
469                 return false;
470 }
471
472
473 /*
474  *  Scan the (pre-parsed) hba file line by line, looking for a match
475  *  to the port's connection request.
476  */
477 static bool check_hba(POOL_CONNECTION *frontend)
478 {
479         bool found_entry = false;
480         bool error = false;
481         ListCell *line;
482         ListCell *line_num;
483
484         forboth(line, hba_lines, line_num, hba_line_nums)
485         {
486                 parse_hba(lfirst(line), lfirst_int(line_num),
487                                   frontend, &found_entry, &error);
488                 if (found_entry || error)
489                         break;
490         }
491
492         if (!error)
493         {
494                 /* If no matching entry was found, synthesize 'reject' entry. */
495                 if (!found_entry)
496                         frontend->auth_method = uaReject;
497                 return true;
498         }
499         else
500                 return false;
501 }
502
503
504 /*
505  *  Process one line from the hba config file.
506  *
507  *  See if it applies to a connection from a frontend with IP address
508  *  frontend->raddr to a database named frontend->database.  If so, return
509  *  *found_p true and fill in the auth arguments into the appropriate
510  *  frontend fields. If not, leave *found_p as it was.  If the record has
511  *  a syntax error, return *error_p true, after issuing a message to the
512  *  log.  If no error, leave *error_p as it was.
513  */
514 static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend,
515                                           bool *found_p, bool *error_p)
516 {
517         char *token;
518         char *db, *db_tmp;
519         char *user, *user_tmp;
520         struct addrinfo *gai_result;
521         struct addrinfo hints;
522         int ret;
523         struct sockaddr_storage addr;
524         struct sockaddr_storage mask;
525         char *cidr_slash;
526         ListCell *line_item;
527
528         line_item = list_head(line);
529         /* Check the record type. */
530         token = lfirst(line_item);
531         if (strcmp(token, "local") == 0)
532         {
533                 /* Get the database. */
534                 line_item = lnext(line_item);
535                 if (!line_item)
536                         goto hba_syntax;
537                 db = lfirst(line_item);
538
539                 /* Get the user. */
540                 line_item = lnext(line_item);
541                 if (!line_item)
542                         goto hba_syntax;
543                 user = lfirst(line_item);
544
545                 line_item = lnext(line_item);
546                 if (!line_item)
547                         goto hba_syntax;
548
549                 /* Read the rest of the line. */
550                 parse_hba_auth(&line_item, &frontend->auth_method,
551                                            &frontend->auth_arg, error_p);
552                 if (*error_p)
553                         goto hba_syntax;
554
555         /* Disallow auth methods that always need TCP/IP sockets to work */
556                 /*
557                 if (frontend->auth_method == uaKrb4 ||
558                         frontend->auth_method == uaKrb5)
559                         goto hba_syntax;
560                 */
561
562                 /* Does not match if connection isn't AF_UNIX */
563                 if (!IS_AF_UNIX(frontend->raddr.addr.ss_family))
564                         return;
565         }
566         else if (strcmp(token, "host") == 0
567                          || strcmp(token, "hostssl") == 0
568                          || strcmp(token, "hostnossl") == 0)
569         {
570                 if (token[4] == 's')    /* "hostssl" */
571                 {
572 #ifdef USE_SSL
573                         /* Record does not match if we are not on an SSL connection */
574                         if (!frontend->ssl)
575                                 return;
576
577                         /* Placeholder to require specific SSL level, perhaps? */
578                         /* Or a client certificate */
579
580                         /* Since we were on SSL, proceed as with normal 'host' mode */
581 #else
582                         /* We don't accept this keyword at all if no SSL support */
583                         goto hba_syntax;
584 #endif
585                 }
586 #ifdef USE_SSL
587                 else if (token[4] == 'n')       /* "hostnossl" */
588                 {
589                         /* Record does not match if we are on an SSL connection */
590                         if (frontend->ssl)
591                                 return;
592                 }
593 #endif
594
595         /* Get the database. */
596                 line_item = lnext(line_item);
597                 if (!line_item)
598                         goto hba_syntax;
599                 db = lfirst(line_item);
600
601                 /* Get the user. */
602                 line_item = lnext(line_item);
603                 if (!line_item)
604                         goto hba_syntax;
605                 user = lfirst(line_item);
606
607                 /* Read the IP address field. (with or without CIDR netmask) */
608                 line_item = lnext(line_item);
609                 if (!line_item)
610                         goto hba_syntax;
611                 token = lfirst(line_item);
612
613                 /* Check if it has a CIDR suffix and if so isolate it */
614                 cidr_slash = strchr(token, '/');
615                 if (cidr_slash)
616                         *cidr_slash = '\0';
617
618                 /* Get the IP address either way */
619                 hints.ai_flags = AI_NUMERICHOST;
620                 hints.ai_family = PF_UNSPEC;
621                 hints.ai_socktype = 0;
622                 hints.ai_protocol = 0;
623                 hints.ai_addrlen = 0;
624                 hints.ai_canonname = NULL;
625                 hints.ai_addr = NULL;
626                 hints.ai_next = NULL;
627
628                 ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
629                 if (ret || !gai_result)
630                 {
631                         pool_log("invalid IP address \"%s\" in file \"%s\" line %d: %s",
632                                          token, hbaFileName, line_num, gai_strerror(ret));
633                         if (cidr_slash)
634                                 *cidr_slash = '/';
635             if (gai_result)
636                                 freeaddrinfo_all(hints.ai_family, gai_result);
637                         goto hba_other_error;
638                 }
639
640                 if (cidr_slash)
641                         *cidr_slash = '/';
642
643                 memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
644                 freeaddrinfo_all(hints.ai_family, gai_result);
645
646                 /* Get the netmask */
647                 if (cidr_slash)
648                 {
649                         if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
650                                 goto hba_syntax;
651                 }
652                 else
653                 {
654                         /* Read the mask field. */
655                         line_item = lnext(line_item);
656                         if (!line_item)
657                                 goto hba_syntax;
658                         token = lfirst(line_item);
659
660                         ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
661                         if (ret || !gai_result)
662                         {
663                                 pool_log("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
664                                                  token, hbaFileName, line_num, gai_strerror(ret));
665                                 if (gai_result)
666                                         freeaddrinfo_all(hints.ai_family, gai_result);
667                                 goto hba_other_error;
668                         }
669
670                         memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
671                         freeaddrinfo_all(hints.ai_family, gai_result);
672
673                         if (addr.ss_family != mask.ss_family)
674                         {
675                                 pool_log("IP address and mask do not match in file \"%s\" line %d",
676                                                  hbaFileName, line_num);
677                                 goto hba_other_error;
678                         }
679                 }
680
681                 if (addr.ss_family != frontend->raddr.addr.ss_family)
682                 {
683                         /*
684                          * Wrong address family.  We allow only one case: if the file
685                          * has IPv4 and the port is IPv6, promote the file address to
686                          * IPv6 and try to match that way.
687                          */
688 #ifdef HAVE_IPV6
689                         if (addr.ss_family == AF_INET && frontend->raddr.addr.ss_family == AF_INET6)
690                         {
691                                 promote_v4_to_v6_addr(&addr);
692                                 promote_v4_to_v6_mask(&mask);
693                         }
694                         else
695 #endif   /* HAVE_IPV6 */
696                         {
697                                 /* Line doesn't match client port, so ignore it. */
698                                 return;
699                         }
700                 }
701
702                 /* Ignore line if client port is not in the matching addr range. */
703                 if (!rangeSockAddr(&frontend->raddr.addr, &addr, &mask))
704                         return;
705
706                 /* Read the rest of the line. */
707                 line_item = lnext(line_item);
708                 if (!line_item)
709                         goto hba_syntax;
710                 parse_hba_auth(&line_item, &frontend->auth_method,
711                                            &frontend->auth_arg, error_p);
712                 if (*error_p)
713                         goto hba_syntax;
714         }
715         else
716                 goto hba_syntax;
717
718         /* Does the entry match database and user? */
719         /*
720          * duplicate db and username since strtok() in check_db() and check_user()
721          * will override '\001' with '\0'.
722          */
723         db_tmp = strdup(db);
724         if (db_tmp == NULL)
725         {
726                 pool_error("parse_hba: strdup failed: %s", strerror(errno));
727                 exit(1);
728         }
729         user_tmp = strdup(user);
730         if (user_tmp == NULL)
731         {
732                 pool_error("parse_hba: strdup failed: %s", strerror(errno));
733                 exit(1);
734         }
735         if (!check_db(frontend->database, frontend->username, db_tmp))
736                 return;
737         if (!check_user(frontend->username, user_tmp))
738         return;
739         free(db_tmp);
740         free(user_tmp);
741
742         /* Success */
743         *found_p = true;
744         return;
745
746  hba_syntax:
747         if (line_item)
748                 pool_log("invalid entry in file \"%s\" at line %d, token \"%s\"",
749                                  hbaFileName, line_num, (char *) lfirst(line_item));
750         else
751                 pool_log("missing field in file \"%s\" at end of line %d",
752                                  hbaFileName, line_num);
753
754         /* Come here if suitable message already logged */
755  hba_other_error:
756         *error_p = true;
757 }
758
759
760 /*
761  *  Scan the rest of a host record (after the mask field)
762  *  and return the interpretation of it as *userauth_p, *auth_arg_p, and
763  *  *error_p.  *line_item points to the next token of the line, and is
764  *  advanced over successfully-read tokens.
765  */
766 static void parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
767                                                    char **auth_arg_p, bool *error_p)
768 {
769         char *token;
770
771         *auth_arg_p = NULL;
772
773         if (!*line_item)
774         {
775                 *error_p = true;
776                 return;
777         }
778
779         token = lfirst(*line_item);
780         if (strcmp(token, "trust") == 0)
781                 *userauth_p = uaTrust;
782         /*
783         else if (strcmp(token, "ident") == 0)
784                 *userauth_p = uaIdent;
785         else if (strcmp(token, "password") == 0)
786                 *userauth_p = uaPassword;
787         else if (strcmp(token, "krb4") == 0)
788                 *userauth_p = uaKrb4;
789         else if (strcmp(token, "krb5") == 0)
790                 *userauth_p = uaKrb5;
791         */
792         else if (strcmp(token, "reject") == 0)
793                 *userauth_p = uaReject;
794         /*
795         else if (strcmp(token, "md5") == 0)
796                 *userauth_p = uaMD5;
797         else if (strcmp(token, "crypt") == 0)
798                 *userauth_p = uaCrypt;
799         */
800 #ifdef USE_PAM
801         else if (strcmp(token, "pam") == 0)
802                 *userauth_p = uaPAM;
803 #endif /* USE_PAM */
804         else
805         {
806                 *error_p = true;
807                 return;
808         }
809         *line_item = lnext(*line_item);
810
811         /* Get the authentication argument token, if any */
812         if (*line_item)
813         {
814                 token = lfirst(*line_item);
815         *auth_arg_p = strdup(token);
816                 if (*auth_arg_p == NULL)
817                 {
818                         pool_error("parse_hba_auth: strdup failed: %s", strerror(errno));
819                         exit(1);
820                 }
821                 *line_item = lnext(*line_item);
822                 /* If there is more on the line, it is an error */
823                 if (*line_item)
824                         *error_p = true;
825         }
826 }
827
828
829 /*
830  * Check comma user list for a specific user, handle group names.
831  */
832 static bool check_user(char *user, char *param_str)
833 {
834         char *tok;
835
836         for (tok = strtok(param_str, MULTI_VALUE_SEP);
837                  tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
838         {
839                 if (tok[0] == '+')
840                 {
841                         /*
842                          * pgpool cannot accept groups. commented lines below are the
843                          * original code.
844                          */
845                         pool_error("group token \"+\" is not supported in pgpool");
846                         return false;
847 /*                      if (check_group(tok + 1, user)) */
848 /*                              return true; */
849                 }
850                 else if (strcmp(tok, user) == 0 || strcmp(tok, "all\n") == 0)
851                         return true;
852         }
853
854         return false;
855 }
856
857
858 /*
859  * Check to see if db/user combination matches param string.
860  */
861 static bool check_db(char *dbname, char *user, char *param_str)
862 {
863         char *tok;
864
865         for (tok = strtok(param_str, MULTI_VALUE_SEP);
866                  tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
867         {
868                 if (strcmp(tok, "all\n") == 0)
869                         return true;
870                 else if (strcmp(tok, "sameuser\n") == 0)
871                 {
872                         if (strcmp(dbname, user) == 0)
873                                 return true;
874                 }
875                 else if (strcmp(tok, "samegroup\n") == 0)
876                 {
877                         /*
878                          * pgpool cannot accept groups. commented lines below are the
879                          * original code.
880                          */
881                         pool_error("group token \"samegroup\" is not supported in pgpool");
882                         return false;
883 /*                      if (check_group(dbname, user)) */
884 /*                              return true; */
885                 }
886                 else if (strcmp(tok, dbname) == 0)
887                         return true;
888         }
889
890         return false;
891 }
892
893
894 /*
895  * tokenize the given file, storing the resulting data into two lists:
896  * a list of sublists, each sublist containing the tokens in a line of
897  * the file, and a list of line numbers.
898  *
899  * filename must be the absolute path to the target file.
900  */
901 static void tokenize_file(const char *filename, FILE *file,
902                                                   List **lines, List **line_nums)
903 {
904         List *current_line = NIL;
905         int line_number = 1;
906         char *buf;
907
908         *lines = *line_nums = NIL;
909
910         while (!feof(file))
911         {
912                 buf = next_token_expand(filename, file);
913
914                 /* add token to list, unless we are at EOL or comment start */
915                 if (buf[0])
916                 {
917                         if (current_line == NIL)
918                         {
919                                 /* make a new line List, record its line number */
920                                 current_line = lappend(current_line, buf);
921                                 *lines = lappend(*lines, current_line);
922                                 *line_nums = lappend_int(*line_nums, line_number);
923                         }
924                         else
925                         {
926                                 /* append token to current line's list */
927                                 current_line = lappend(current_line, buf);
928                         }
929                 }
930                 else
931                 {
932                         /* we are at real or logical EOL, so force a new line List */
933                         current_line = NIL;
934                         /* Advance line number whenever we reach EOL */
935                         line_number++;
936                         /* Don't forget to free the next_token_expand result */
937                         free(buf);
938                 }
939         }
940 }
941
942
943 static char * tokenize_inc_file(const char *outer_filename,
944                                                                 const char *inc_filename)
945 {
946         char *inc_fullname;
947         FILE *inc_file;
948         List *inc_lines;
949         List *inc_line_nums;
950         ListCell *line;
951         char *comma_str;
952
953         if (is_absolute_path(inc_filename))
954         {
955                 /* absolute path is taken as-is */
956                 inc_fullname = strdup(inc_filename);
957                 if (inc_fullname == NULL)
958                 {
959                         pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
960                         exit(1);
961                 }
962         }
963         else
964         {
965                 /* relative path is relative to dir of calling file */
966                 inc_fullname = (char *)malloc(strlen(outer_filename) + 1 +
967                                                                           strlen(inc_filename) + 1);
968                 if (inc_fullname == NULL)
969                 {
970                         pool_error("tokenize_inc_file: malloc failed: %s", strerror(errno));
971                         exit(1);
972                 }
973                 strcpy(inc_fullname, outer_filename);
974                 get_parent_directory(inc_fullname);
975                 join_path_components(inc_fullname, inc_fullname, inc_filename);
976                 canonicalize_path(inc_fullname);
977         }
978
979         inc_file = fopen(inc_fullname, "r");
980         if (inc_file == NULL)
981         {
982                 char *returnVal;
983
984                 pool_error("could not open secondary authentication file \"@%s\" as \"%s\": reason: %s",
985                                    inc_filename, inc_fullname, strerror(errno));
986                 free(inc_fullname);
987
988                 /* return single space, it matches nothing */
989                 returnVal = strdup(" ");
990                 if (returnVal == NULL)
991                 {
992                         pool_error("tokenize_inc_file: malloc failed: %s", strerror(errno));
993                         exit(1);
994                 }
995                 return returnVal;
996         }
997
998     /* There is possible recursion here if the file contains @ */
999         tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
1000
1001         /*FreeFile(inc_file);*/
1002         fclose(inc_file);
1003         free(inc_fullname);
1004
1005         /* Create comma-separated string from List */
1006         comma_str = strdup("");
1007         if (comma_str == NULL)
1008         {
1009                 pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
1010                 exit(1);
1011         }
1012         foreach(line, inc_lines)
1013         {
1014                 List *token_list = (List *) lfirst(line);
1015                 ListCell *token;
1016
1017                 foreach(token, token_list)
1018                 {
1019                         int oldlen = strlen(comma_str);
1020                         int needed;
1021
1022                         needed = oldlen + strlen(lfirst(token)) + 1;
1023                         if (oldlen > 0)
1024                                 needed++;
1025                         comma_str = realloc(comma_str, needed);
1026                         if (comma_str == NULL)
1027                         {
1028                                 pool_error("tokenize_inc_file: realloc failed: %s", strerror(errno));
1029                                 exit(1);
1030                         }
1031                         if (oldlen > 0)
1032                                 strcat(comma_str, MULTI_VALUE_SEP);
1033                         strcat(comma_str, lfirst(token));
1034                 }
1035         }
1036
1037         free_lines(&inc_lines, &inc_line_nums);
1038
1039         /* if file is empty, return single space rather than empty string */
1040         if (strlen(comma_str) == 0)
1041         {
1042                 char *returnVal;
1043
1044                 free(comma_str);
1045                 returnVal = strdup(" ");
1046                 if (returnVal == NULL)
1047                 {
1048                         pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
1049                         exit(1);
1050                 }
1051                 return returnVal;
1052         }
1053
1054         return comma_str;
1055 }
1056
1057
1058 /*
1059  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
1060  * so provide our own version.
1061  */
1062 static bool pg_isblank(const char c)
1063 {
1064         return c == ' ' || c == '\t' || c == '\r';
1065 }
1066
1067
1068 /*
1069  *       Tokenize file and handle file inclusion and comma lists. We have
1070  *       to  break      apart  the      commas  to      expand  any  file names then
1071  *       reconstruct with commas.
1072  *
1073  * The result is always a malloc'd string.  If it's zero-length then
1074  * we have reached EOL.
1075  */
1076 static char * next_token_expand(const char *filename, FILE *file)
1077 {
1078         char buf[MAX_TOKEN];
1079         char *comma_str;
1080         bool trailing_comma;
1081         char *incbuf;
1082         int needed;
1083
1084         comma_str = strdup("");
1085         if (comma_str == NULL)
1086         {
1087                 pool_error("next_token_expand: strdup failed: %s", strerror(errno));
1088                 exit(1);
1089         }
1090
1091         do
1092         {
1093                 next_token(file, buf, sizeof(buf));
1094                 if (!buf[0])
1095                         break;
1096
1097                 if (buf[strlen(buf) - 1] == ',')
1098                 {
1099                         trailing_comma = true;
1100                         buf[strlen(buf) - 1] = '\0';
1101                 }
1102                 else
1103                         trailing_comma = false;
1104
1105                 /* Is this referencing a file? */
1106                 if (buf[0] == '@')
1107                         incbuf = tokenize_inc_file(filename, buf + 1);
1108                 else
1109                 {
1110                         incbuf = strdup(buf);
1111                         if (incbuf == NULL)
1112                         {
1113                                 pool_error("next_token_expand: strdup failed: %s", strerror(errno));
1114                                 exit(1);
1115                         }
1116                 }
1117
1118                 needed = strlen(comma_str) + strlen(incbuf) + 1;
1119                 if (trailing_comma)
1120                         needed++;
1121                 comma_str = realloc(comma_str, needed);
1122                 if (comma_str == NULL)
1123                 {
1124                         pool_error("next_token_expand: realloc failed: %s", strerror(errno));
1125                         exit(1);
1126                 }
1127                 strcat(comma_str, incbuf);
1128                 if (trailing_comma)
1129                         strcat(comma_str, MULTI_VALUE_SEP);
1130                 free(incbuf);
1131         } while (trailing_comma);
1132
1133         return comma_str;
1134 }
1135
1136
1137 /*
1138  * Grab one token out of fp. Tokens are strings of non-blank
1139  * characters bounded by blank characters, beginning of line, and
1140  * end of line. Blank means space or tab. Return the token as
1141  * *buf. Leave file positioned at the character immediately after the
1142  * token or EOF, whichever comes first. If no more tokens on line,
1143  * return empty string as *buf and position the file to the beginning
1144  * of the next line or EOF, whichever comes first. Allow spaces in
1145  * quoted strings. Terminate on unquoted commas. Handle
1146  * comments. Treat unquoted keywords that might be user names or
1147  * database names specially, by appending a newline to them.
1148  */
1149 static void next_token(FILE *fp, char *buf, int bufsz)
1150 {
1151         int c;
1152         char *start_buf = buf;
1153         char *end_buf = buf + (bufsz - 2);
1154         bool in_quote = false;
1155         bool was_quote = false;
1156         bool saw_quote = false;
1157
1158         /*Assert(end_buf > start_buf);*/
1159
1160         /* Move over initial whitespace and commas */
1161         while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
1162                 ;
1163
1164         if (c == EOF || c == '\n')
1165         {
1166                 *buf = '\0';
1167                 return;
1168         }
1169
1170         /*
1171          * Build a token in buf of next characters up to EOF, EOL, unquoted
1172          * comma, or unquoted whitespace.
1173          */
1174         while (c != EOF && c != '\n' &&
1175                    (!pg_isblank(c) || in_quote == true))
1176         {
1177                 /* skip comments to EOL */
1178                 if (c == '#' && !in_quote)
1179                 {
1180                         while ((c = getc(fp)) != EOF && c != '\n')
1181                                 ;
1182                         /* If only comment, consume EOL too; return EOL */
1183                         if (c != EOF && buf == start_buf)
1184                                 c = getc(fp);
1185                         break;
1186                 }
1187
1188                 if (buf >= end_buf)
1189                 {
1190                         *buf = '\0';
1191                         pool_log("authentication file token too long, skipping: \"%s\"", start_buf);
1192                         /* Discard remainder of line */
1193                         while ((c = getc(fp)) != EOF && c != '\n')
1194                                 ;
1195                         break;
1196                 }
1197
1198                 if (c != '"' || (c == '"' && was_quote))
1199                         *buf++ = c;
1200
1201                 /* We pass back the comma so the caller knows there is more */
1202                 if ((pg_isblank(c) || c == ',') && !in_quote)
1203                         break;
1204
1205                 /* Literal double-quote is two double-quotes */
1206                 if (in_quote && c == '"')
1207                         was_quote = !was_quote;
1208                 else
1209                         was_quote = false;
1210
1211                 if (c == '"')
1212                 {
1213                         in_quote = !in_quote;
1214                         saw_quote = true;
1215                 }
1216
1217                 c = getc(fp);
1218         }
1219
1220         /*
1221          * Put back the char right after the token (critical in case it is
1222          * EOL, since we need to detect end-of-line at next call).
1223          */
1224         if (c != EOF)
1225                 ungetc(c, fp);
1226
1227         *buf = '\0';
1228
1229         if (!saw_quote &&
1230                 (strcmp(start_buf, "all") == 0 ||
1231                  strcmp(start_buf, "sameuser") == 0 ||
1232                  strcmp(start_buf, "samegroup") == 0))
1233         {
1234                 /* append newline to a magical keyword */
1235                 *buf++ = '\n';
1236                 *buf = '\0';
1237         }
1238 }
1239
1240
1241 /*
1242  * free memory used by lines and tokens built by tokenize_file()
1243  */
1244 static void free_lines(List **lines, List **line_nums)
1245 {
1246         if (*lines)
1247         {
1248                 ListCell *line;
1249
1250                 foreach(line, *lines)
1251                 {
1252                         List *ln = lfirst(line);
1253                         ListCell *token;
1254
1255                         foreach(token, ln)
1256                                 free(lfirst(token));
1257
1258                         list_free(ln);
1259                 }
1260
1261                 list_free(*lines);
1262                 *lines = NIL;
1263         }
1264
1265         if (*line_nums)
1266         {
1267                 list_free(*line_nums);
1268                 *line_nums = NIL;
1269         }
1270 }
1271
1272
1273 #ifdef USE_PAM
1274
1275 /*
1276  * PAM conversation function
1277  */
1278 static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
1279                                                                 struct pam_response ** resp, void *appdata_ptr)
1280 {
1281         if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
1282         {
1283                 switch (msg[0]->msg_style)
1284                 {
1285                         case PAM_ERROR_MSG:
1286                                 pool_log("error from underlying PAM layer: %s",
1287                                                  msg[0]->msg);
1288                                 return PAM_CONV_ERR;
1289                         default:
1290                                 pool_log("unsupported PAM conversation %d/%s",
1291                                                  msg[0]->msg_style, msg[0]->msg);
1292                                 return PAM_CONV_ERR;
1293                 }
1294         }
1295
1296         if (!appdata_ptr)
1297         {
1298                 /*
1299                  * Workaround for Solaris 2.6 where the PAM library is broken and
1300                  * does not pass appdata_ptr to the conversation routine
1301                  */
1302                 appdata_ptr = pam_passwd;
1303         }
1304
1305         /*
1306          * Password wasn't passed to PAM the first time around - let's go ask
1307          * the client to send a password, which we then stuff into PAM.
1308          */
1309         if (strlen(appdata_ptr) == 0)
1310         {
1311                 char *passwd;
1312
1313                 sendAuthRequest(pam_frontend_kludge, AUTH_REQ_PASSWORD);
1314                 passwd = recv_password_packet(pam_frontend_kludge);
1315
1316                 if (passwd == NULL)
1317                         return PAM_CONV_ERR; /* client didn't want to send password */
1318
1319                 if (strlen(passwd) == 0)
1320                 {
1321                         pool_log("empty password returned by client");
1322                         return PAM_CONV_ERR;
1323                 }
1324                 appdata_ptr = passwd;
1325         }
1326
1327         /*
1328          * PAM will free this memory in * pam_end()
1329          */
1330         *resp = calloc(num_msg, sizeof(struct pam_response));
1331         if (!*resp)
1332         {
1333                 /* originally, it was logged as LOG */
1334                 pool_error("pam_passwd_conv_proc: calloc failed: %s", strerror(errno));
1335                 return PAM_CONV_ERR;
1336         }
1337
1338         (*resp)[0].resp = strdup((char *) appdata_ptr);
1339         if ((*resp)[0].resp == NULL)
1340         {
1341                 pool_error("pam_passwd_conv_proc: strdup failed: %s", strerror(errno));
1342                 exit(1);
1343         }
1344         (*resp)[0].resp_retcode = 0;
1345
1346         return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
1347 }
1348
1349
1350 /*
1351  * Check authentication against PAM.
1352  */
1353 static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *password)
1354 {
1355         int retval;
1356         pam_handle_t *pamh = NULL;
1357
1358         /*
1359          * Apparently, Solaris 2.6 is broken, and needs ugly static variable
1360          * workaround
1361          */
1362         pam_passwd = password;
1363
1364         /*
1365          * Set the application data portion of the conversation struct This is
1366          * later used inside the PAM conversation to pass the password to the
1367          * authentication module.
1368          */
1369         pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
1370                                                                                                          * not allocated */
1371
1372         /* Optionally, one can set the service name in pool_hba.conf */
1373         if (frontend->auth_arg && frontend->auth_arg[0] != '\0')
1374                 retval = pam_start(frontend->auth_arg, "pgpool@",
1375                                                    &pam_passw_conv, &pamh);
1376         else
1377                 retval = pam_start(PGPOOL_PAM_SERVICE, "pgpool@",
1378                                                    &pam_passw_conv, &pamh);
1379
1380         if (retval != PAM_SUCCESS)
1381         {
1382                 pool_log("could not create PAM authenticator: %s",
1383                                  pam_strerror(pamh, retval));
1384                 pam_passwd = NULL;              /* Unset pam_passwd */
1385                 return POOL_ERROR;
1386         }
1387
1388         retval = pam_set_item(pamh, PAM_USER, user);
1389         if (retval != PAM_SUCCESS)
1390         {
1391                 pool_log("pam_set_item(PAM_USER) failed: %s",
1392                                  pam_strerror(pamh, retval));
1393                 pam_passwd = NULL;              /* Unset pam_passwd */
1394                 return POOL_ERROR;
1395         }
1396
1397         retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
1398         if (retval != PAM_SUCCESS)
1399         {
1400                 pool_log("pam_set_item(PAM_CONV) failed: %s",
1401                                  pam_strerror(pamh, retval));
1402                 pam_passwd = NULL;              /* Unset pam_passwd */
1403                 return POOL_ERROR;
1404         }
1405
1406         retval = pam_authenticate(pamh, 0);
1407         if (retval != PAM_SUCCESS)      /* service name does not exist */
1408         {
1409                 pool_log("pam_authenticate failed: %s",
1410                                  pam_strerror(pamh, retval));
1411                 pam_passwd = NULL;              /* Unset pam_passwd */
1412                 return POOL_ERROR;
1413         }
1414
1415         retval = pam_acct_mgmt(pamh, 0);
1416         if (retval != PAM_SUCCESS)
1417         {
1418                 pool_log("pam_acct_mgmt failed: %s",
1419                                  pam_strerror(pamh, retval));
1420                 pam_passwd = NULL;              /* Unset pam_passwd */
1421                 return POOL_ERROR;
1422         }
1423
1424         retval = pam_end(pamh, retval);
1425         if (retval != PAM_SUCCESS)
1426         {
1427                 pool_log("could not release PAM authenticator: %s",
1428                                  pam_strerror(pamh, retval));
1429         }
1430
1431         pam_passwd = NULL;                      /* Unset pam_passwd */
1432
1433         return (retval == PAM_SUCCESS ? POOL_CONTINUE : POOL_ERROR);
1434 }
1435
1436 #endif   /* USE_PAM */