4 * $Header: /cvsroot/pgpool/pgpool-II/pool_hba.c,v 1.5.2.1 2009/08/22 04:19:49 t-ishii Exp $
6 * pgpool: a language independent connection pool server for PostgreSQL
7 * written by Tatsuo Ishii
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.
23 * pool_hba.c.: Routines to handle host based authentication.
34 #include "pool_path.h"
36 #include "parser/pool_memory.h"
37 #include "parser/pg_list.h"
39 #define MULTI_VALUE_SEP "\001" /* delimiter for multi-valued column strings */
42 static List *hba_lines = NIL;
43 static List *hba_line_nums = NIL;
44 static char *hbaFileName;
46 static POOL_MEMORY_POOL *hba_memory_context = NULL;
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);
65 #ifdef HAVE_PAM_PAM_APPL_H
66 #include <pam/pam_appl.h>
68 #ifdef HAVE_SECURITY_PAM_APPL_H
69 #include <security/pam_appl.h>
72 #define PGPOOL_PAM_SERVICE "pgpool" /* Service name passed to PAM */
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);
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.
81 static char *recv_password_packet(POOL_CONNECTION *frontend);
83 static struct pam_conv pam_passw_conv = {
84 &pam_passwd_conv_proc,
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 */
96 * read in hba config file
98 void load_hba(char *hbapath)
102 POOL_MEMORY_POOL *old_context;
103 if (hba_memory_context == NULL)
105 hba_memory_context = pool_memory_create(PARSER_BLOCK_SIZE);
106 if (hba_memory_context == NULL)
108 pool_error("load_hba: pool_memory_create() failed");
112 /* switch memory context */
113 old_context = pool_memory;
114 pool_memory = hba_memory_context;
116 if (hba_lines || hba_line_nums)
117 free_lines(&hba_lines, &hba_line_nums);
119 file = fopen(hbapath, "r");
122 pool_error("could not open \"%s\". reason: %s",
123 hbapath, strerror(errno));
127 pool_debug("loading \"%s\" for client authentication configuration file",
130 tokenize_file(hbapath, file, &hba_lines, &hba_line_nums);
133 hbaFileName = pstrdup(hbapath);
135 /* switch old memory context */
136 pool_memory = old_context;
141 * do frontend <-> pgpool authentication based on pool_hba.conf
143 void ClientAuthentication(POOL_CONNECTION *frontend)
145 POOL_STATUS status = POOL_ERROR;
147 if (! hba_getauthmethod(frontend))
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();
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.
161 switch (frontend->auth_method)
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.
174 char hostinfo[NI_MAXHOST];
178 getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen,
179 hostinfo, sizeof(hostinfo),
183 messagelen = sizeof(hostinfo) +
184 strlen(frontend->username) + strlen(frontend->database) + 80;
185 if ((errmessage = (char *)malloc(messagelen+1)) == NULL)
187 pool_error("ClientAuthentication: malloc failed: %s", strerror(errno));
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");
197 snprintf(errmessage, messagelen,
198 "no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
199 hostinfo, frontend->username, frontend->database);
201 pool_error(errmessage);
202 pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage,
203 "", "", __FILE__, __LINE__);
224 /* case uaPassword: */
229 pam_frontend_kludge = frontend;
230 status = CheckPAMAuth(frontend, frontend->username, "");
235 status = POOL_CONTINUE;
239 if (status == POOL_CONTINUE)
240 sendAuthRequest(frontend, AUTH_REQ_OK);
241 else if (status != POOL_CONTINUE)
242 auth_failed(frontend);
246 static void sendAuthRequest(POOL_CONNECTION *frontend, AuthRequest areq)
248 int wsize; /* number of bytes to write */
249 int areq_nbo; /* areq in network byte order */
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.
256 if (areq == AUTH_REQ_OK)
259 /* request a password */
260 pool_write(frontend, "R", 1);
262 if (frontend->protoVersion == PROTO_MAJOR_V3)
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); */
269 wsize = htonl(sizeof(int)*2);
270 pool_write(frontend, &wsize, sizeof(int));
273 areq_nbo = htonl(areq);
274 pool_write(frontend, &areq_nbo, sizeof(int));
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); */
282 pool_flush(frontend);
286 #ifdef USE_PAM /* see the prototype comment */
289 * Collect password response packet from frontend.
291 * Returns NULL if couldn't get password, else malloc'd string.
293 static char *recv_password_packet(POOL_CONNECTION *frontend)
299 if (frontend->protoVersion == PROTO_MAJOR_V3)
301 /* Expect 'p' message type */
304 if (pool_read(frontend, &kind, 1) < 0)
309 pool_error("expected password response, got message type %c",
311 return NULL; /* bad message type */
314 /* pre-3.0 protocol does not send a message type */
316 if (pool_read(frontend, &rsize, sizeof(int)) < 0)
319 rsize = ntohl(rsize) - 4;
320 passwd = pool_read2(frontend, rsize); /* retrieve password */
324 /* Do not echo password to logs, for security. */
325 pool_debug("received password packet from frontend for pgpool's HBA");
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.
332 returnVal = strdup(passwd);
333 if (returnVal == NULL)
335 pool_error("recv_password_packet: strdup failed: %s", strerror(errno));
344 * Tell the user the authentication failed.
346 static void auth_failed(POOL_CONNECTION *frontend)
348 bool send_error_to_frontend = true;
352 messagelen = strlen(frontend->username) + 100;
353 if ((errmessage = (char *)malloc(messagelen+1)) == NULL)
355 pool_error("auth_failed: malloc failed: %s", strerror(errno));
359 switch (frontend->auth_method)
362 snprintf(errmessage, messagelen,
363 "authentication with pgpool failed for user \"%s\": host rejected",
366 * if uaReject, frontend should have received 'E' and disconnected already.
368 send_error_to_frontend = false;
371 /* snprintf(errmessage, messagelen, */
372 /* "Kerberos 4 authentication with pgpool failed for user \"%s\"", */
373 /* frontend->username); */
376 /* snprintf(errmessage, messagelen, */
377 /* "Kerberos 5 authentication with pgpool failed for user \"%s\"", */
378 /* frontend->username); */
381 snprintf(errmessage, messagelen,
382 "\"trust\" authentication with pgpool failed for user \"%s\"",
386 /* snprintf(errmessage, messagelen, */
387 /* "Ident authentication with pgpool failed for user \"%s\"", */
388 /* frontend->username); */
392 /* case uaPassword: */
393 /* snprintf(errmessage, messagelen, */
394 /* "password authentication with pgpool failed for user \"%s\"", */
395 /* frontend->username); */
399 snprintf(errmessage, messagelen,
400 "PAM authentication with pgpool failed for user \"%s\"",
405 snprintf(errmessage, messagelen,
406 "authentication with pgpool failed for user \"%s\": invalid authentication method",
411 pool_error(errmessage);
412 if (send_error_to_frontend)
413 pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage,
414 "", "", __FILE__, __LINE__);
417 * don't need to free(errmessage). I will just kill myself.
419 close_all_backend_connections();
425 * Close all of the cached backend connections.
427 * This is exactly the same as send_frontend_exits() in child.c.
429 static void close_all_backend_connections(void)
432 POOL_CONNECTION_POOL *p = pool_connection_pool;
434 #ifdef HAVE_SIGPROCMASK
440 POOL_SETMASK2(&BlockSig, &oldmask);
442 for (i=0;i<pool_config->max_pool;i++, p++)
444 if (!MASTER_CONNECTION(p))
446 if (MASTER_CONNECTION(p)->sp->user == NULL)
448 pool_send_frontend_exits(p);
451 POOL_SETMASK(&oldmask);
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.
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.
464 static bool hba_getauthmethod(POOL_CONNECTION *frontend)
466 if (check_hba(frontend))
474 * Scan the (pre-parsed) hba file line by line, looking for a match
475 * to the port's connection request.
477 static bool check_hba(POOL_CONNECTION *frontend)
479 bool found_entry = false;
484 forboth(line, hba_lines, line_num, hba_line_nums)
486 parse_hba(lfirst(line), lfirst_int(line_num),
487 frontend, &found_entry, &error);
488 if (found_entry || error)
494 /* If no matching entry was found, synthesize 'reject' entry. */
496 frontend->auth_method = uaReject;
505 * Process one line from the hba config file.
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.
514 static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend,
515 bool *found_p, bool *error_p)
519 char *user, *user_tmp;
520 struct addrinfo *gai_result;
521 struct addrinfo hints;
523 struct sockaddr_storage addr;
524 struct sockaddr_storage mask;
528 line_item = list_head(line);
529 /* Check the record type. */
530 token = lfirst(line_item);
531 if (strcmp(token, "local") == 0)
533 /* Get the database. */
534 line_item = lnext(line_item);
537 db = lfirst(line_item);
540 line_item = lnext(line_item);
543 user = lfirst(line_item);
545 line_item = lnext(line_item);
549 /* Read the rest of the line. */
550 parse_hba_auth(&line_item, &frontend->auth_method,
551 &frontend->auth_arg, error_p);
555 /* Disallow auth methods that always need TCP/IP sockets to work */
557 if (frontend->auth_method == uaKrb4 ||
558 frontend->auth_method == uaKrb5)
562 /* Does not match if connection isn't AF_UNIX */
563 if (!IS_AF_UNIX(frontend->raddr.addr.ss_family))
566 else if (strcmp(token, "host") == 0
567 || strcmp(token, "hostssl") == 0
568 || strcmp(token, "hostnossl") == 0)
570 if (token[4] == 's') /* "hostssl" */
573 /* Record does not match if we are not on an SSL connection */
577 /* Placeholder to require specific SSL level, perhaps? */
578 /* Or a client certificate */
580 /* Since we were on SSL, proceed as with normal 'host' mode */
582 /* We don't accept this keyword at all if no SSL support */
587 else if (token[4] == 'n') /* "hostnossl" */
589 /* Record does not match if we are on an SSL connection */
595 /* Get the database. */
596 line_item = lnext(line_item);
599 db = lfirst(line_item);
602 line_item = lnext(line_item);
605 user = lfirst(line_item);
607 /* Read the IP address field. (with or without CIDR netmask) */
608 line_item = lnext(line_item);
611 token = lfirst(line_item);
613 /* Check if it has a CIDR suffix and if so isolate it */
614 cidr_slash = strchr(token, '/');
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;
628 ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
629 if (ret || !gai_result)
631 pool_log("invalid IP address \"%s\" in file \"%s\" line %d: %s",
632 token, hbaFileName, line_num, gai_strerror(ret));
636 freeaddrinfo_all(hints.ai_family, gai_result);
637 goto hba_other_error;
643 memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
644 freeaddrinfo_all(hints.ai_family, gai_result);
646 /* Get the netmask */
649 if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
654 /* Read the mask field. */
655 line_item = lnext(line_item);
658 token = lfirst(line_item);
660 ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
661 if (ret || !gai_result)
663 pool_log("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
664 token, hbaFileName, line_num, gai_strerror(ret));
666 freeaddrinfo_all(hints.ai_family, gai_result);
667 goto hba_other_error;
670 memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
671 freeaddrinfo_all(hints.ai_family, gai_result);
673 if (addr.ss_family != mask.ss_family)
675 pool_log("IP address and mask do not match in file \"%s\" line %d",
676 hbaFileName, line_num);
677 goto hba_other_error;
681 if (addr.ss_family != frontend->raddr.addr.ss_family)
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.
689 if (addr.ss_family == AF_INET && frontend->raddr.addr.ss_family == AF_INET6)
691 promote_v4_to_v6_addr(&addr);
692 promote_v4_to_v6_mask(&mask);
695 #endif /* HAVE_IPV6 */
697 /* Line doesn't match client port, so ignore it. */
702 /* Ignore line if client port is not in the matching addr range. */
703 if (!rangeSockAddr(&frontend->raddr.addr, &addr, &mask))
706 /* Read the rest of the line. */
707 line_item = lnext(line_item);
710 parse_hba_auth(&line_item, &frontend->auth_method,
711 &frontend->auth_arg, error_p);
718 /* Does the entry match database and user? */
720 * duplicate db and username since strtok() in check_db() and check_user()
721 * will override '\001' with '\0'.
726 pool_error("parse_hba: strdup failed: %s", strerror(errno));
729 user_tmp = strdup(user);
730 if (user_tmp == NULL)
732 pool_error("parse_hba: strdup failed: %s", strerror(errno));
735 if (!check_db(frontend->database, frontend->username, db_tmp))
737 if (!check_user(frontend->username, user_tmp))
748 pool_log("invalid entry in file \"%s\" at line %d, token \"%s\"",
749 hbaFileName, line_num, (char *) lfirst(line_item));
751 pool_log("missing field in file \"%s\" at end of line %d",
752 hbaFileName, line_num);
754 /* Come here if suitable message already logged */
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.
766 static void parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
767 char **auth_arg_p, bool *error_p)
779 token = lfirst(*line_item);
780 if (strcmp(token, "trust") == 0)
781 *userauth_p = uaTrust;
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;
792 else if (strcmp(token, "reject") == 0)
793 *userauth_p = uaReject;
795 else if (strcmp(token, "md5") == 0)
797 else if (strcmp(token, "crypt") == 0)
798 *userauth_p = uaCrypt;
801 else if (strcmp(token, "pam") == 0)
809 *line_item = lnext(*line_item);
811 /* Get the authentication argument token, if any */
814 token = lfirst(*line_item);
815 *auth_arg_p = strdup(token);
816 if (*auth_arg_p == NULL)
818 pool_error("parse_hba_auth: strdup failed: %s", strerror(errno));
821 *line_item = lnext(*line_item);
822 /* If there is more on the line, it is an error */
830 * Check comma user list for a specific user, handle group names.
832 static bool check_user(char *user, char *param_str)
836 for (tok = strtok(param_str, MULTI_VALUE_SEP);
837 tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
842 * pgpool cannot accept groups. commented lines below are the
845 pool_error("group token \"+\" is not supported in pgpool");
847 /* if (check_group(tok + 1, user)) */
850 else if (strcmp(tok, user) == 0 || strcmp(tok, "all\n") == 0)
859 * Check to see if db/user combination matches param string.
861 static bool check_db(char *dbname, char *user, char *param_str)
865 for (tok = strtok(param_str, MULTI_VALUE_SEP);
866 tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
868 if (strcmp(tok, "all\n") == 0)
870 else if (strcmp(tok, "sameuser\n") == 0)
872 if (strcmp(dbname, user) == 0)
875 else if (strcmp(tok, "samegroup\n") == 0)
878 * pgpool cannot accept groups. commented lines below are the
881 pool_error("group token \"samegroup\" is not supported in pgpool");
883 /* if (check_group(dbname, user)) */
886 else if (strcmp(tok, dbname) == 0)
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.
899 * filename must be the absolute path to the target file.
901 static void tokenize_file(const char *filename, FILE *file,
902 List **lines, List **line_nums)
904 List *current_line = NIL;
908 *lines = *line_nums = NIL;
912 buf = next_token_expand(filename, file);
914 /* add token to list, unless we are at EOL or comment start */
917 if (current_line == NIL)
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);
926 /* append token to current line's list */
927 current_line = lappend(current_line, buf);
932 /* we are at real or logical EOL, so force a new line List */
934 /* Advance line number whenever we reach EOL */
936 /* Don't forget to free the next_token_expand result */
943 static char * tokenize_inc_file(const char *outer_filename,
944 const char *inc_filename)
953 if (is_absolute_path(inc_filename))
955 /* absolute path is taken as-is */
956 inc_fullname = strdup(inc_filename);
957 if (inc_fullname == NULL)
959 pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
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)
970 pool_error("tokenize_inc_file: malloc failed: %s", strerror(errno));
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);
979 inc_file = fopen(inc_fullname, "r");
980 if (inc_file == NULL)
984 pool_error("could not open secondary authentication file \"@%s\" as \"%s\": reason: %s",
985 inc_filename, inc_fullname, strerror(errno));
988 /* return single space, it matches nothing */
989 returnVal = strdup(" ");
990 if (returnVal == NULL)
992 pool_error("tokenize_inc_file: malloc failed: %s", strerror(errno));
998 /* There is possible recursion here if the file contains @ */
999 tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
1001 /*FreeFile(inc_file);*/
1005 /* Create comma-separated string from List */
1006 comma_str = strdup("");
1007 if (comma_str == NULL)
1009 pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
1012 foreach(line, inc_lines)
1014 List *token_list = (List *) lfirst(line);
1017 foreach(token, token_list)
1019 int oldlen = strlen(comma_str);
1022 needed = oldlen + strlen(lfirst(token)) + 1;
1025 comma_str = realloc(comma_str, needed);
1026 if (comma_str == NULL)
1028 pool_error("tokenize_inc_file: realloc failed: %s", strerror(errno));
1032 strcat(comma_str, MULTI_VALUE_SEP);
1033 strcat(comma_str, lfirst(token));
1037 free_lines(&inc_lines, &inc_line_nums);
1039 /* if file is empty, return single space rather than empty string */
1040 if (strlen(comma_str) == 0)
1045 returnVal = strdup(" ");
1046 if (returnVal == NULL)
1048 pool_error("tokenize_inc_file: strdup failed: %s", strerror(errno));
1059 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
1060 * so provide our own version.
1062 static bool pg_isblank(const char c)
1064 return c == ' ' || c == '\t' || c == '\r';
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.
1073 * The result is always a malloc'd string. If it's zero-length then
1074 * we have reached EOL.
1076 static char * next_token_expand(const char *filename, FILE *file)
1078 char buf[MAX_TOKEN];
1080 bool trailing_comma;
1084 comma_str = strdup("");
1085 if (comma_str == NULL)
1087 pool_error("next_token_expand: strdup failed: %s", strerror(errno));
1093 next_token(file, buf, sizeof(buf));
1097 if (buf[strlen(buf) - 1] == ',')
1099 trailing_comma = true;
1100 buf[strlen(buf) - 1] = '\0';
1103 trailing_comma = false;
1105 /* Is this referencing a file? */
1107 incbuf = tokenize_inc_file(filename, buf + 1);
1110 incbuf = strdup(buf);
1113 pool_error("next_token_expand: strdup failed: %s", strerror(errno));
1118 needed = strlen(comma_str) + strlen(incbuf) + 1;
1121 comma_str = realloc(comma_str, needed);
1122 if (comma_str == NULL)
1124 pool_error("next_token_expand: realloc failed: %s", strerror(errno));
1127 strcat(comma_str, incbuf);
1129 strcat(comma_str, MULTI_VALUE_SEP);
1131 } while (trailing_comma);
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.
1149 static void next_token(FILE *fp, char *buf, int bufsz)
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;
1158 /*Assert(end_buf > start_buf);*/
1160 /* Move over initial whitespace and commas */
1161 while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
1164 if (c == EOF || c == '\n')
1171 * Build a token in buf of next characters up to EOF, EOL, unquoted
1172 * comma, or unquoted whitespace.
1174 while (c != EOF && c != '\n' &&
1175 (!pg_isblank(c) || in_quote == true))
1177 /* skip comments to EOL */
1178 if (c == '#' && !in_quote)
1180 while ((c = getc(fp)) != EOF && c != '\n')
1182 /* If only comment, consume EOL too; return EOL */
1183 if (c != EOF && buf == start_buf)
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')
1198 if (c != '"' || (c == '"' && was_quote))
1201 /* We pass back the comma so the caller knows there is more */
1202 if ((pg_isblank(c) || c == ',') && !in_quote)
1205 /* Literal double-quote is two double-quotes */
1206 if (in_quote && c == '"')
1207 was_quote = !was_quote;
1213 in_quote = !in_quote;
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).
1230 (strcmp(start_buf, "all") == 0 ||
1231 strcmp(start_buf, "sameuser") == 0 ||
1232 strcmp(start_buf, "samegroup") == 0))
1234 /* append newline to a magical keyword */
1242 * free memory used by lines and tokens built by tokenize_file()
1244 static void free_lines(List **lines, List **line_nums)
1250 foreach(line, *lines)
1252 List *ln = lfirst(line);
1256 free(lfirst(token));
1267 list_free(*line_nums);
1276 * PAM conversation function
1278 static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
1279 struct pam_response ** resp, void *appdata_ptr)
1281 if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
1283 switch (msg[0]->msg_style)
1286 pool_log("error from underlying PAM layer: %s",
1288 return PAM_CONV_ERR;
1290 pool_log("unsupported PAM conversation %d/%s",
1291 msg[0]->msg_style, msg[0]->msg);
1292 return PAM_CONV_ERR;
1299 * Workaround for Solaris 2.6 where the PAM library is broken and
1300 * does not pass appdata_ptr to the conversation routine
1302 appdata_ptr = pam_passwd;
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.
1309 if (strlen(appdata_ptr) == 0)
1313 sendAuthRequest(pam_frontend_kludge, AUTH_REQ_PASSWORD);
1314 passwd = recv_password_packet(pam_frontend_kludge);
1317 return PAM_CONV_ERR; /* client didn't want to send password */
1319 if (strlen(passwd) == 0)
1321 pool_log("empty password returned by client");
1322 return PAM_CONV_ERR;
1324 appdata_ptr = passwd;
1328 * PAM will free this memory in * pam_end()
1330 *resp = calloc(num_msg, sizeof(struct pam_response));
1333 /* originally, it was logged as LOG */
1334 pool_error("pam_passwd_conv_proc: calloc failed: %s", strerror(errno));
1335 return PAM_CONV_ERR;
1338 (*resp)[0].resp = strdup((char *) appdata_ptr);
1339 if ((*resp)[0].resp == NULL)
1341 pool_error("pam_passwd_conv_proc: strdup failed: %s", strerror(errno));
1344 (*resp)[0].resp_retcode = 0;
1346 return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
1351 * Check authentication against PAM.
1353 static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *password)
1356 pam_handle_t *pamh = NULL;
1359 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
1362 pam_passwd = password;
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.
1369 pam_passw_conv.appdata_ptr = (char *) password; /* from password above,
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);
1377 retval = pam_start(PGPOOL_PAM_SERVICE, "pgpool@",
1378 &pam_passw_conv, &pamh);
1380 if (retval != PAM_SUCCESS)
1382 pool_log("could not create PAM authenticator: %s",
1383 pam_strerror(pamh, retval));
1384 pam_passwd = NULL; /* Unset pam_passwd */
1388 retval = pam_set_item(pamh, PAM_USER, user);
1389 if (retval != PAM_SUCCESS)
1391 pool_log("pam_set_item(PAM_USER) failed: %s",
1392 pam_strerror(pamh, retval));
1393 pam_passwd = NULL; /* Unset pam_passwd */
1397 retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
1398 if (retval != PAM_SUCCESS)
1400 pool_log("pam_set_item(PAM_CONV) failed: %s",
1401 pam_strerror(pamh, retval));
1402 pam_passwd = NULL; /* Unset pam_passwd */
1406 retval = pam_authenticate(pamh, 0);
1407 if (retval != PAM_SUCCESS) /* service name does not exist */
1409 pool_log("pam_authenticate failed: %s",
1410 pam_strerror(pamh, retval));
1411 pam_passwd = NULL; /* Unset pam_passwd */
1415 retval = pam_acct_mgmt(pamh, 0);
1416 if (retval != PAM_SUCCESS)
1418 pool_log("pam_acct_mgmt failed: %s",
1419 pam_strerror(pamh, retval));
1420 pam_passwd = NULL; /* Unset pam_passwd */
1424 retval = pam_end(pamh, retval);
1425 if (retval != PAM_SUCCESS)
1427 pool_log("could not release PAM authenticator: %s",
1428 pam_strerror(pamh, retval));
1431 pam_passwd = NULL; /* Unset pam_passwd */
1433 return (retval == PAM_SUCCESS ? POOL_CONTINUE : POOL_ERROR);
1436 #endif /* USE_PAM */