1 /*-------------------------------------------------------------------------
4 * Routines to handle host based authentication (that's the scheme
5 * wherein you authenticate a user by seeing what IP address the system
6 * says he comes from and choosing authentication method based on it).
8 * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
13 * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.207 2010/05/26 16:43:13 tgl Exp $
15 *-------------------------------------------------------------------------
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
29 #include "libpq/libpq.h"
30 #include "regex/regex.h"
31 #include "replication/walsender.h"
32 #include "storage/fd.h"
33 #include "utils/acl.h"
34 #include "utils/guc.h"
35 #include "utils/lsyscache.h"
38 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
39 #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
41 /* This is used to separate values in multi-valued column strings */
42 #define MULTI_VALUE_SEP "\001"
46 /* callback data for check_network_callback */
47 typedef struct check_network_data
49 IPCompareMethod method; /* test method */
50 SockAddr *raddr; /* client's actual address */
51 bool result; /* set to true if match */
54 /* pre-parsed content of HBA config file: list of HbaLine structs */
55 static List *parsed_hba_lines = NIL;
58 * These variables hold the pre-parsed contents of the ident usermap
59 * configuration file. ident_lines is a list of sublists, one sublist for
60 * each (non-empty, non-comment) line of the file. The sublist items are
61 * palloc'd strings, one string per token on the line. Note there will always
62 * be at least one token, since blank lines are not entered in the data
63 * structure. ident_line_nums is an integer list containing the actual line
64 * number for each line represented in ident_lines.
66 static List *ident_lines = NIL;
67 static List *ident_line_nums = NIL;
70 static void tokenize_file(const char *filename, FILE *file,
71 List **lines, List **line_nums);
72 static char *tokenize_inc_file(const char *outer_filename,
73 const char *inc_filename);
76 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
77 * so provide our own version.
80 pg_isblank(const char c)
82 return c == ' ' || c == '\t' || c == '\r';
87 * Grab one token out of fp. Tokens are strings of non-blank
88 * characters bounded by blank characters, commas, beginning of line, and
89 * end of line. Blank means space or tab. Tokens can be delimited by
90 * double quotes (this allows the inclusion of blanks, but not newlines).
92 * The token, if any, is returned at *buf (a buffer of size bufsz).
93 * Also, we set *initial_quote to indicate whether there was quoting before
94 * the first character. (We use that to prevent "@x" from being treated
95 * as a file inclusion request. Note that @"x" should be so treated;
96 * we want to allow that to support embedded spaces in file paths.)
98 * If successful: store null-terminated token at *buf and return TRUE.
99 * If no more tokens on line: set *buf = '\0' and return FALSE.
101 * Leave file positioned at the character immediately after the token or EOF,
102 * whichever comes first. If no more tokens on line, position the file to the
103 * beginning of the next line or EOF, whichever comes first.
105 * Handle comments. Treat unquoted keywords that might be role names or
106 * database names specially, by appending a newline to them. Also, when
107 * a token is terminated by a comma, the comma is included in the returned
111 next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
114 char *start_buf = buf;
115 char *end_buf = buf + (bufsz - 2);
116 bool in_quote = false;
117 bool was_quote = false;
118 bool saw_quote = false;
120 /* end_buf reserves two bytes to ensure we can append \n and \0 */
121 Assert(end_buf > start_buf);
123 *initial_quote = false;
125 /* Move over initial whitespace and commas */
126 while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
129 if (c == EOF || c == '\n')
136 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
137 * or unquoted whitespace.
139 while (c != EOF && c != '\n' &&
140 (!pg_isblank(c) || in_quote))
142 /* skip comments to EOL */
143 if (c == '#' && !in_quote)
145 while ((c = getc(fp)) != EOF && c != '\n')
147 /* If only comment, consume EOL too; return EOL */
148 if (c != EOF && buf == start_buf)
157 (errcode(ERRCODE_CONFIG_FILE_ERROR),
158 errmsg("authentication file token too long, skipping: \"%s\"",
160 /* Discard remainder of line */
161 while ((c = getc(fp)) != EOF && c != '\n')
166 if (c != '"' || was_quote)
169 /* We pass back the comma so the caller knows there is more */
170 if (c == ',' && !in_quote)
173 /* Literal double-quote is two double-quotes */
174 if (in_quote && c == '"')
175 was_quote = !was_quote;
181 in_quote = !in_quote;
183 if (buf == start_buf)
184 *initial_quote = true;
191 * Put back the char right after the token (critical in case it is EOL,
192 * since we need to detect end-of-line at next call).
200 (strcmp(start_buf, "all") == 0 ||
201 strcmp(start_buf, "sameuser") == 0 ||
202 strcmp(start_buf, "samegroup") == 0 ||
203 strcmp(start_buf, "samerole") == 0 ||
204 strcmp(start_buf, "replication") == 0))
206 /* append newline to a magical keyword */
211 return (saw_quote || buf > start_buf);
215 * Tokenize file and handle file inclusion and comma lists. We have
216 * to break apart the commas to expand any file names then
217 * reconstruct with commas.
219 * The result is a palloc'd string, or NULL if we have reached EOL.
222 next_token_expand(const char *filename, FILE *file)
225 char *comma_str = pstrdup("");
226 bool got_something = false;
234 if (!next_token(file, buf, sizeof(buf), &initial_quote))
237 got_something = true;
239 if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
241 trailing_comma = true;
242 buf[strlen(buf) - 1] = '\0';
245 trailing_comma = false;
247 /* Is this referencing a file? */
248 if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
249 incbuf = tokenize_inc_file(filename, buf + 1);
251 incbuf = pstrdup(buf);
253 needed = strlen(comma_str) + strlen(incbuf) + 1;
256 comma_str = repalloc(comma_str, needed);
257 strcat(comma_str, incbuf);
259 strcat(comma_str, MULTI_VALUE_SEP);
261 } while (trailing_comma);
274 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
277 free_lines(List **lines, List **line_nums)
280 * Either both must be non-NULL, or both must be NULL
282 Assert((*lines != NIL && *line_nums != NIL) ||
283 (*lines == NIL && *line_nums == NIL));
288 * "lines" is a list of lists; each of those sublists consists of
289 * palloc'ed tokens, so we want to free each pointed-to token in a
290 * sublist, followed by the sublist itself, and finally the whole
295 foreach(line, *lines)
297 List *ln = lfirst(line);
301 pfree(lfirst(token));
302 /* free the sublist structure itself */
305 /* free the list structure itself */
307 /* clear the static variable */
313 list_free(*line_nums);
320 tokenize_inc_file(const char *outer_filename,
321 const char *inc_filename)
330 if (is_absolute_path(inc_filename))
332 /* absolute path is taken as-is */
333 inc_fullname = pstrdup(inc_filename);
337 /* relative path is relative to dir of calling file */
338 inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
339 strlen(inc_filename) + 1);
340 strcpy(inc_fullname, outer_filename);
341 get_parent_directory(inc_fullname);
342 join_path_components(inc_fullname, inc_fullname, inc_filename);
343 canonicalize_path(inc_fullname);
346 inc_file = AllocateFile(inc_fullname, "r");
347 if (inc_file == NULL)
350 (errcode_for_file_access(),
351 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
352 inc_filename, inc_fullname)));
355 /* return single space, it matches nothing */
359 /* There is possible recursion here if the file contains @ */
360 tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
365 /* Create comma-separated string from List */
366 comma_str = pstrdup("");
367 foreach(line, inc_lines)
369 List *token_list = (List *) lfirst(line);
372 foreach(token, token_list)
374 int oldlen = strlen(comma_str);
377 needed = oldlen + strlen(lfirst(token)) + 1;
380 comma_str = repalloc(comma_str, needed);
382 strcat(comma_str, MULTI_VALUE_SEP);
383 strcat(comma_str, lfirst(token));
387 free_lines(&inc_lines, &inc_line_nums);
389 /* if file is empty, return single space rather than empty string */
390 if (strlen(comma_str) == 0)
401 * Tokenize the given file, storing the resulting data into two lists:
402 * a list of sublists, each sublist containing the tokens in a line of
403 * the file, and a list of line numbers.
405 * filename must be the absolute path to the target file.
408 tokenize_file(const char *filename, FILE *file,
409 List **lines, List **line_nums)
411 List *current_line = NIL;
415 *lines = *line_nums = NIL;
417 while (!feof(file) && !ferror(file))
419 buf = next_token_expand(filename, file);
421 /* add token to list, unless we are at EOL or comment start */
424 if (current_line == NIL)
426 /* make a new line List, record its line number */
427 current_line = lappend(current_line, buf);
428 *lines = lappend(*lines, current_line);
429 *line_nums = lappend_int(*line_nums, line_number);
433 /* append token to current line's list */
434 current_line = lappend(current_line, buf);
439 /* we are at real or logical EOL, so force a new line List */
441 /* Advance line number whenever we reach EOL */
449 * Does user belong to role?
451 * userid is the OID of the role given as the attempted login identifier.
452 * We check to see if it is a member of the specified role name.
455 is_member(Oid userid, const char *role)
459 if (!OidIsValid(userid))
460 return false; /* if user not exist, say "no" */
462 roleid = get_roleid(role);
464 if (!OidIsValid(roleid))
465 return false; /* if target role not exist, say "no" */
467 /* See if user is directly or indirectly a member of role */
468 return is_member_of_role(userid, roleid);
472 * Check comma-separated list for a match to role, allowing group names.
474 * NB: param_str is destructively modified! In current usage, this is
475 * okay only because this code is run after forking off from the postmaster,
476 * and so it doesn't matter that we clobber the stored hba info.
479 check_role(const char *role, Oid roleid, char *param_str)
483 for (tok = strtok(param_str, MULTI_VALUE_SEP);
485 tok = strtok(NULL, MULTI_VALUE_SEP))
489 if (is_member(roleid, tok + 1))
492 else if (strcmp(tok, role) == 0 ||
493 strcmp(tok, "all\n") == 0)
501 * Check to see if db/role combination matches param string.
503 * NB: param_str is destructively modified! In current usage, this is
504 * okay only because this code is run after forking off from the postmaster,
505 * and so it doesn't matter that we clobber the stored hba info.
508 check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
512 for (tok = strtok(param_str, MULTI_VALUE_SEP);
514 tok = strtok(NULL, MULTI_VALUE_SEP))
518 /* walsender connections can only match replication keyword */
519 if (strcmp(tok, "replication\n") == 0)
522 else if (strcmp(tok, "all\n") == 0)
524 else if (strcmp(tok, "sameuser\n") == 0)
526 if (strcmp(dbname, role) == 0)
529 else if (strcmp(tok, "samegroup\n") == 0 ||
530 strcmp(tok, "samerole\n") == 0)
532 if (is_member(roleid, dbname))
535 else if (strcmp(tok, "replication\n") == 0)
536 continue; /* never match this if not walsender */
537 else if (strcmp(tok, dbname) == 0)
544 * Check to see if a connecting IP matches the given address and netmask.
547 check_ip(SockAddr *raddr, struct sockaddr * addr, struct sockaddr * mask)
549 if (raddr->addr.ss_family == addr->sa_family)
551 /* Same address family */
552 if (!pg_range_sockaddr(&raddr->addr,
553 (struct sockaddr_storage *) addr,
554 (struct sockaddr_storage *) mask))
558 else if (addr->sa_family == AF_INET &&
559 raddr->addr.ss_family == AF_INET6)
562 * If we're connected on IPv6 but the file specifies an IPv4 address
563 * to match against, promote the latter to an IPv6 address before
564 * trying to match the client's address.
566 struct sockaddr_storage addrcopy,
569 memcpy(&addrcopy, &addr, sizeof(addrcopy));
570 memcpy(&maskcopy, &mask, sizeof(maskcopy));
571 pg_promote_v4_to_v6_addr(&addrcopy);
572 pg_promote_v4_to_v6_mask(&maskcopy);
574 if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
577 #endif /* HAVE_IPV6 */
580 /* Wrong address family, no IPV6 */
588 * pg_foreach_ifaddr callback: does client addr match this machine interface?
591 check_network_callback(struct sockaddr * addr, struct sockaddr * netmask,
594 check_network_data *cn = (check_network_data *) cb_data;
595 struct sockaddr_storage mask;
597 /* Already found a match? */
601 if (cn->method == ipCmpSameHost)
603 /* Make an all-ones netmask of appropriate length for family */
604 pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
605 cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) & mask);
609 /* Use the netmask of the interface itself */
610 cn->result = check_ip(cn->raddr, addr, netmask);
615 * Use pg_foreach_ifaddr to check a samehost or samenet match
618 check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
620 check_network_data cn;
627 if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
629 elog(LOG, "error enumerating network interfaces: %m");
638 * Macros used to check and report on invalid configuration options.
639 * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
641 * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
642 * method is actually the one specified. Used as a shortcut when
643 * the option is only valid for one authentication method.
644 * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
645 * reporting error if it's not.
647 #define INVALID_AUTH_OPTION(optname, validmethods) do {\
649 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
650 /* translator: the second %s is a list of auth methods */ \
651 errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
652 optname, _(validmethods)), \
653 errcontext("line %d of configuration file \"%s\"", \
654 line_num, HbaFileName))); \
658 #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
659 if (parsedline->auth_method != methodval) \
660 INVALID_AUTH_OPTION(optname, validmethods); \
663 #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
664 if (argvar == NULL) {\
666 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
667 errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
668 authname, argname), \
669 errcontext("line %d of configuration file \"%s\"", \
670 line_num, HbaFileName))); \
677 * Parse one line in the hba config file and store the result in
678 * a HbaLine structure.
681 parse_hba_line(List *line, int line_num, HbaLine *parsedline)
684 struct addrinfo *gai_result;
685 struct addrinfo hints;
691 line_item = list_head(line);
693 parsedline->linenumber = line_num;
695 /* Check the record type. */
696 token = lfirst(line_item);
697 if (strcmp(token, "local") == 0)
699 parsedline->conntype = ctLocal;
701 else if (strcmp(token, "host") == 0
702 || strcmp(token, "hostssl") == 0
703 || strcmp(token, "hostnossl") == 0)
706 if (token[4] == 's') /* "hostssl" */
709 parsedline->conntype = ctHostSSL;
712 (errcode(ERRCODE_CONFIG_FILE_ERROR),
713 errmsg("hostssl not supported on this platform"),
714 errhint("Compile with --enable-ssl to use SSL connections."),
715 errcontext("line %d of configuration file \"%s\"",
716 line_num, HbaFileName)));
721 else if (token[4] == 'n') /* "hostnossl" */
723 parsedline->conntype = ctHostNoSSL;
728 /* "host", or "hostnossl" and SSL support not built in */
729 parsedline->conntype = ctHost;
735 (errcode(ERRCODE_CONFIG_FILE_ERROR),
736 errmsg("invalid connection type \"%s\"",
738 errcontext("line %d of configuration file \"%s\"",
739 line_num, HbaFileName)));
743 /* Get the database. */
744 line_item = lnext(line_item);
748 (errcode(ERRCODE_CONFIG_FILE_ERROR),
749 errmsg("end-of-line before database specification"),
750 errcontext("line %d of configuration file \"%s\"",
751 line_num, HbaFileName)));
754 parsedline->database = pstrdup(lfirst(line_item));
757 line_item = lnext(line_item);
761 (errcode(ERRCODE_CONFIG_FILE_ERROR),
762 errmsg("end-of-line before role specification"),
763 errcontext("line %d of configuration file \"%s\"",
764 line_num, HbaFileName)));
767 parsedline->role = pstrdup(lfirst(line_item));
769 if (parsedline->conntype != ctLocal)
771 /* Read the IP address field. (with or without CIDR netmask) */
772 line_item = lnext(line_item);
776 (errcode(ERRCODE_CONFIG_FILE_ERROR),
777 errmsg("end-of-line before IP address specification"),
778 errcontext("line %d of configuration file \"%s\"",
779 line_num, HbaFileName)));
782 token = lfirst(line_item);
784 /* Is it equal to 'samehost' or 'samenet'? */
785 if (strcmp(token, "samehost") == 0)
787 /* Any IP on this host is allowed to connect */
788 parsedline->ip_cmp_method = ipCmpSameHost;
790 else if (strcmp(token, "samenet") == 0)
792 /* Any IP on the host's subnets is allowed to connect */
793 parsedline->ip_cmp_method = ipCmpSameNet;
797 /* IP and netmask are specified */
798 parsedline->ip_cmp_method = ipCmpMask;
800 /* need a modifiable copy of token */
801 token = pstrdup(token);
803 /* Check if it has a CIDR suffix and if so isolate it */
804 cidr_slash = strchr(token, '/');
808 /* Get the IP address either way */
809 hints.ai_flags = AI_NUMERICHOST;
810 hints.ai_family = PF_UNSPEC;
811 hints.ai_socktype = 0;
812 hints.ai_protocol = 0;
813 hints.ai_addrlen = 0;
814 hints.ai_canonname = NULL;
815 hints.ai_addr = NULL;
816 hints.ai_next = NULL;
818 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
819 if (ret || !gai_result)
822 (errcode(ERRCODE_CONFIG_FILE_ERROR),
823 errmsg("invalid IP address \"%s\": %s",
824 token, gai_strerror(ret)),
825 errcontext("line %d of configuration file \"%s\"",
826 line_num, HbaFileName)));
828 pg_freeaddrinfo_all(hints.ai_family, gai_result);
833 memcpy(&parsedline->addr, gai_result->ai_addr,
834 gai_result->ai_addrlen);
835 pg_freeaddrinfo_all(hints.ai_family, gai_result);
837 /* Get the netmask */
840 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
841 parsedline->addr.ss_family) < 0)
843 *cidr_slash = '/'; /* restore token for message */
845 (errcode(ERRCODE_CONFIG_FILE_ERROR),
846 errmsg("invalid CIDR mask in address \"%s\"",
848 errcontext("line %d of configuration file \"%s\"",
849 line_num, HbaFileName)));
857 /* Read the mask field. */
859 line_item = lnext(line_item);
863 (errcode(ERRCODE_CONFIG_FILE_ERROR),
864 errmsg("end-of-line before netmask specification"),
865 errcontext("line %d of configuration file \"%s\"",
866 line_num, HbaFileName)));
869 token = lfirst(line_item);
871 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
872 if (ret || !gai_result)
875 (errcode(ERRCODE_CONFIG_FILE_ERROR),
876 errmsg("invalid IP mask \"%s\": %s",
877 token, gai_strerror(ret)),
878 errcontext("line %d of configuration file \"%s\"",
879 line_num, HbaFileName)));
881 pg_freeaddrinfo_all(hints.ai_family, gai_result);
885 memcpy(&parsedline->mask, gai_result->ai_addr,
886 gai_result->ai_addrlen);
887 pg_freeaddrinfo_all(hints.ai_family, gai_result);
889 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
892 (errcode(ERRCODE_CONFIG_FILE_ERROR),
893 errmsg("IP address and mask do not match"),
894 errcontext("line %d of configuration file \"%s\"",
895 line_num, HbaFileName)));
902 /* Get the authentication method */
903 line_item = lnext(line_item);
907 (errcode(ERRCODE_CONFIG_FILE_ERROR),
908 errmsg("end-of-line before authentication method"),
909 errcontext("line %d of configuration file \"%s\"",
910 line_num, HbaFileName)));
913 token = lfirst(line_item);
916 if (strcmp(token, "trust") == 0)
917 parsedline->auth_method = uaTrust;
918 else if (strcmp(token, "ident") == 0)
919 parsedline->auth_method = uaIdent;
920 else if (strcmp(token, "password") == 0)
921 parsedline->auth_method = uaPassword;
922 else if (strcmp(token, "krb5") == 0)
924 parsedline->auth_method = uaKrb5;
928 else if (strcmp(token, "gss") == 0)
930 parsedline->auth_method = uaGSS;
934 else if (strcmp(token, "sspi") == 0)
936 parsedline->auth_method = uaSSPI;
940 else if (strcmp(token, "reject") == 0)
941 parsedline->auth_method = uaReject;
942 else if (strcmp(token, "md5") == 0)
944 if (Db_user_namespace)
947 (errcode(ERRCODE_CONFIG_FILE_ERROR),
948 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
949 errcontext("line %d of configuration file \"%s\"",
950 line_num, HbaFileName)));
953 parsedline->auth_method = uaMD5;
955 else if (strcmp(token, "pam") == 0)
957 parsedline->auth_method = uaPAM;
961 else if (strcmp(token, "ldap") == 0)
963 parsedline->auth_method = uaLDAP;
967 else if (strcmp(token, "cert") == 0)
969 parsedline->auth_method = uaCert;
973 else if (strcmp(token, "radius") == 0)
974 parsedline->auth_method = uaRADIUS;
978 (errcode(ERRCODE_CONFIG_FILE_ERROR),
979 errmsg("invalid authentication method \"%s\"",
981 errcontext("line %d of configuration file \"%s\"",
982 line_num, HbaFileName)));
989 (errcode(ERRCODE_CONFIG_FILE_ERROR),
990 errmsg("invalid authentication method \"%s\": not supported on this platform",
992 errcontext("line %d of configuration file \"%s\"",
993 line_num, HbaFileName)));
997 /* Invalid authentication combinations */
998 if (parsedline->conntype == ctLocal &&
999 parsedline->auth_method == uaKrb5)
1002 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1003 errmsg("krb5 authentication is not supported on local sockets"),
1004 errcontext("line %d of configuration file \"%s\"",
1005 line_num, HbaFileName)));
1009 if (parsedline->conntype == ctLocal &&
1010 parsedline->auth_method == uaGSS)
1013 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1014 errmsg("gssapi authentication is not supported on local sockets"),
1015 errcontext("line %d of configuration file \"%s\"",
1016 line_num, HbaFileName)));
1020 * SSPI authentication can never be enabled on ctLocal connections, because
1021 * it's only supported on Windows, where ctLocal isn't supported.
1025 if (parsedline->conntype != ctHostSSL &&
1026 parsedline->auth_method == uaCert)
1029 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1030 errmsg("cert authentication is only supported on hostssl connections"),
1031 errcontext("line %d of configuration file \"%s\"",
1032 line_num, HbaFileName)));
1036 /* Parse remaining arguments */
1037 while ((line_item = lnext(line_item)) != NULL)
1041 token = lfirst(line_item);
1043 c = strchr(token, '=');
1047 * Got something that's not a name=value pair.
1050 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1051 errmsg("authentication option not in name=value format: %s", token),
1052 errcontext("line %d of configuration file \"%s\"",
1053 line_num, HbaFileName)));
1058 *c++ = '\0'; /* token now holds "name", c holds "value" */
1059 if (strcmp(token, "map") == 0)
1061 if (parsedline->auth_method != uaIdent &&
1062 parsedline->auth_method != uaKrb5 &&
1063 parsedline->auth_method != uaGSS &&
1064 parsedline->auth_method != uaSSPI &&
1065 parsedline->auth_method != uaCert)
1066 INVALID_AUTH_OPTION("map", gettext_noop("ident, krb5, gssapi, sspi and cert"));
1067 parsedline->usermap = pstrdup(c);
1069 else if (strcmp(token, "clientcert") == 0)
1072 * Since we require ctHostSSL, this really can never happen on
1073 * non-SSL-enabled builds, so don't bother checking for
1076 if (parsedline->conntype != ctHostSSL)
1079 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1080 errmsg("clientcert can only be configured for \"hostssl\" rows"),
1081 errcontext("line %d of configuration file \"%s\"",
1082 line_num, HbaFileName)));
1085 if (strcmp(c, "1") == 0)
1087 if (!secure_loaded_verify_locations())
1090 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1091 errmsg("client certificates can only be checked if a root certificate store is available"),
1092 errhint("Make sure the root.crt file is present and readable."),
1093 errcontext("line %d of configuration file \"%s\"",
1094 line_num, HbaFileName)));
1097 parsedline->clientcert = true;
1101 if (parsedline->auth_method == uaCert)
1104 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1105 errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
1106 errcontext("line %d of configuration file \"%s\"",
1107 line_num, HbaFileName)));
1110 parsedline->clientcert = false;
1113 else if (strcmp(token, "pamservice") == 0)
1115 REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
1116 parsedline->pamservice = pstrdup(c);
1118 else if (strcmp(token, "ldaptls") == 0)
1120 REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
1121 if (strcmp(c, "1") == 0)
1122 parsedline->ldaptls = true;
1124 parsedline->ldaptls = false;
1126 else if (strcmp(token, "ldapserver") == 0)
1128 REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
1129 parsedline->ldapserver = pstrdup(c);
1131 else if (strcmp(token, "ldapport") == 0)
1133 REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
1134 parsedline->ldapport = atoi(c);
1135 if (parsedline->ldapport == 0)
1138 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1139 errmsg("invalid LDAP port number: \"%s\"", c),
1140 errcontext("line %d of configuration file \"%s\"",
1141 line_num, HbaFileName)));
1145 else if (strcmp(token, "ldapbinddn") == 0)
1147 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
1148 parsedline->ldapbinddn = pstrdup(c);
1150 else if (strcmp(token, "ldapbindpasswd") == 0)
1152 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
1153 parsedline->ldapbindpasswd = pstrdup(c);
1155 else if (strcmp(token, "ldapsearchattribute") == 0)
1157 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
1158 parsedline->ldapsearchattribute = pstrdup(c);
1160 else if (strcmp(token, "ldapbasedn") == 0)
1162 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
1163 parsedline->ldapbasedn = pstrdup(c);
1165 else if (strcmp(token, "ldapprefix") == 0)
1167 REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
1168 parsedline->ldapprefix = pstrdup(c);
1170 else if (strcmp(token, "ldapsuffix") == 0)
1172 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
1173 parsedline->ldapsuffix = pstrdup(c);
1175 else if (strcmp(token, "krb_server_hostname") == 0)
1177 REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
1178 parsedline->krb_server_hostname = pstrdup(c);
1180 else if (strcmp(token, "krb_realm") == 0)
1182 if (parsedline->auth_method != uaKrb5 &&
1183 parsedline->auth_method != uaGSS &&
1184 parsedline->auth_method != uaSSPI)
1185 INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
1186 parsedline->krb_realm = pstrdup(c);
1188 else if (strcmp(token, "include_realm") == 0)
1190 if (parsedline->auth_method != uaKrb5 &&
1191 parsedline->auth_method != uaGSS &&
1192 parsedline->auth_method != uaSSPI)
1193 INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
1194 if (strcmp(c, "1") == 0)
1195 parsedline->include_realm = true;
1197 parsedline->include_realm = false;
1199 else if (strcmp(token, "radiusserver") == 0)
1201 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
1203 MemSet(&hints, 0, sizeof(hints));
1204 hints.ai_socktype = SOCK_DGRAM;
1205 hints.ai_family = AF_UNSPEC;
1207 ret = pg_getaddrinfo_all(c, NULL, &hints, &gai_result);
1208 if (ret || !gai_result)
1211 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1212 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
1213 c, gai_strerror(ret)),
1214 errcontext("line %d of configuration file \"%s\"",
1215 line_num, HbaFileName)));
1217 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1220 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1221 parsedline->radiusserver = pstrdup(c);
1223 else if (strcmp(token, "radiusport") == 0)
1225 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
1226 parsedline->radiusport = atoi(c);
1227 if (parsedline->radiusport == 0)
1230 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1231 errmsg("invalid RADIUS port number: \"%s\"", c),
1232 errcontext("line %d of configuration file \"%s\"",
1233 line_num, HbaFileName)));
1237 else if (strcmp(token, "radiussecret") == 0)
1239 REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
1240 parsedline->radiussecret = pstrdup(c);
1242 else if (strcmp(token, "radiusidentifier") == 0)
1244 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
1245 parsedline->radiusidentifier = pstrdup(c);
1250 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1251 errmsg("unrecognized authentication option name: \"%s\"",
1253 errcontext("line %d of configuration file \"%s\"",
1254 line_num, HbaFileName)));
1261 * Check if the selected authentication method has any mandatory arguments
1264 if (parsedline->auth_method == uaLDAP)
1266 MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1269 * LDAP can operate in two modes: either with a direct bind, using
1270 * ldapprefix and ldapsuffix, or using a search+bind, using
1271 * ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
1272 * Disallow mixing these parameters.
1274 if (parsedline->ldapprefix || parsedline->ldapsuffix)
1276 if (parsedline->ldapbasedn ||
1277 parsedline->ldapbinddn ||
1278 parsedline->ldapbindpasswd ||
1279 parsedline->ldapsearchattribute)
1282 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1283 errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
1284 errcontext("line %d of configuration file \"%s\"",
1285 line_num, HbaFileName)));
1289 else if (!parsedline->ldapbasedn)
1292 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1293 errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1294 errcontext("line %d of configuration file \"%s\"",
1295 line_num, HbaFileName)));
1300 if (parsedline->auth_method == uaRADIUS)
1302 MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
1303 MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
1307 * Enforce any parameters implied by other settings.
1309 if (parsedline->auth_method == uaCert)
1311 parsedline->clientcert = true;
1319 * Scan the (pre-parsed) hba file line by line, looking for a match
1320 * to the port's connection request.
1323 check_hba(hbaPort *port)
1329 /* Get the target role's OID. Note we do not error out for bad role. */
1330 roleid = get_roleid(port->user_name);
1332 foreach(line, parsed_hba_lines)
1334 hba = (HbaLine *) lfirst(line);
1336 /* Check connection type */
1337 if (hba->conntype == ctLocal)
1339 if (!IS_AF_UNIX(port->raddr.addr.ss_family))
1344 if (IS_AF_UNIX(port->raddr.addr.ss_family))
1347 /* Check SSL state */
1351 /* Connection is SSL, match both "host" and "hostssl" */
1352 if (hba->conntype == ctHostNoSSL)
1357 /* Connection is not SSL, match both "host" and "hostnossl" */
1358 if (hba->conntype == ctHostSSL)
1362 /* No SSL support, so reject "hostssl" lines */
1363 if (hba->conntype == ctHostSSL)
1367 /* Check IP address */
1368 switch (hba->ip_cmp_method)
1371 if (!check_ip(&port->raddr,
1372 (struct sockaddr *) & hba->addr,
1373 (struct sockaddr *) & hba->mask))
1378 if (!check_same_host_or_net(&port->raddr,
1379 hba->ip_cmp_method))
1383 /* shouldn't get here, but deem it no-match if so */
1388 /* Check database and role */
1389 if (!check_db(port->database_name, port->user_name, roleid,
1393 if (!check_role(port->user_name, roleid, hba->role))
1396 /* Found a record that matched! */
1401 /* If no matching entry was found, then implicitly reject. */
1402 hba = palloc0(sizeof(HbaLine));
1403 hba->auth_method = uaImplicitReject;
1408 * XXX: Return false only happens if we have a parsing error, which we can
1409 * no longer have (parsing now in postmaster). Consider changing API.
1414 * Free an HbaLine structure
1417 free_hba_record(HbaLine *record)
1419 if (record->database)
1420 pfree(record->database);
1422 pfree(record->role);
1423 if (record->usermap)
1424 pfree(record->usermap);
1425 if (record->pamservice)
1426 pfree(record->pamservice);
1427 if (record->ldapserver)
1428 pfree(record->ldapserver);
1429 if (record->ldapprefix)
1430 pfree(record->ldapprefix);
1431 if (record->ldapsuffix)
1432 pfree(record->ldapsuffix);
1433 if (record->krb_server_hostname)
1434 pfree(record->krb_server_hostname);
1435 if (record->krb_realm)
1436 pfree(record->krb_realm);
1441 * Free all records on the parsed HBA list
1444 clean_hba_list(List *lines)
1448 foreach(line, lines)
1450 HbaLine *parsed = (HbaLine *) lfirst(line);
1453 free_hba_record(parsed);
1459 * Read the config file and create a List of HbaLine records for the contents.
1461 * The configuration is read into a temporary list, and if any parse error occurs
1462 * the old list is kept in place and false is returned. Only if the whole file
1463 * parses Ok is the list replaced, and the function returns true.
1469 List *hba_lines = NIL;
1470 List *hba_line_nums = NIL;
1473 List *new_parsed_lines = NIL;
1476 file = AllocateFile(HbaFileName, "r");
1480 (errcode_for_file_access(),
1481 errmsg("could not open configuration file \"%s\": %m",
1485 * Caller will take care of making this a FATAL error in case this is
1486 * the initial startup. If it happens on reload, we just keep the old
1492 tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
1495 /* Now parse all the lines */
1496 forboth(line, hba_lines, line_num, hba_line_nums)
1500 newline = palloc0(sizeof(HbaLine));
1502 if (!parse_hba_line(lfirst(line), lfirst_int(line_num), newline))
1504 /* Parse error in the file, so indicate there's a problem */
1505 free_hba_record(newline);
1509 * Keep parsing the rest of the file so we can report errors on
1510 * more than the first row. Error has already been reported in the
1511 * parsing function, so no need to log it here.
1516 new_parsed_lines = lappend(new_parsed_lines, newline);
1519 /* Free the temporary lists */
1520 free_lines(&hba_lines, &hba_line_nums);
1524 /* Parsing failed at one or more rows, so bail out */
1525 clean_hba_list(new_parsed_lines);
1529 /* Loaded new file successfully, replace the one we use */
1530 clean_hba_list(parsed_hba_lines);
1531 parsed_hba_lines = new_parsed_lines;
1537 * Process one line from the ident config file.
1539 * Take the line and compare it to the needed map, pg_role and ident_user.
1540 * *found_p and *error_p are set according to our results.
1543 parse_ident_usermap(List *line, int line_number, const char *usermap_name,
1544 const char *pg_role, const char *ident_user,
1545 bool case_insensitive, bool *found_p, bool *error_p)
1547 ListCell *line_item;
1551 char *file_ident_user;
1556 Assert(line != NIL);
1557 line_item = list_head(line);
1559 /* Get the map token (must exist) */
1560 token = lfirst(line_item);
1563 /* Get the ident user token */
1564 line_item = lnext(line_item);
1567 token = lfirst(line_item);
1568 file_ident_user = token;
1570 /* Get the PG rolename token */
1571 line_item = lnext(line_item);
1574 token = lfirst(line_item);
1575 file_pgrole = token;
1577 if (strcmp(file_map, usermap_name) != 0)
1578 /* Line does not match the map name we're looking for, so just abort */
1582 if (file_ident_user[0] == '/')
1585 * When system username starts with a slash, treat it as a regular
1586 * expression. In this case, we process the system username as a
1587 * regular expression that returns exactly one match. This is replaced
1588 * for \1 in the database username string, if present.
1592 regmatch_t matches[2];
1596 char *regexp_pgrole;
1598 wstr = palloc((strlen(file_ident_user + 1) + 1) * sizeof(pg_wchar));
1599 wlen = pg_mb2wchar_with_len(file_ident_user + 1, wstr, strlen(file_ident_user + 1));
1602 * XXX: Major room for optimization: regexps could be compiled when
1603 * the file is loaded and then re-used in every connection.
1605 r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED);
1610 pg_regerror(r, &re, errstr, sizeof(errstr));
1612 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1613 errmsg("invalid regular expression \"%s\": %s",
1614 file_ident_user + 1, errstr)));
1622 wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
1623 wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
1625 r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches, 0);
1630 if (r != REG_NOMATCH)
1632 /* REG_NOMATCH is not an error, everything else is */
1633 pg_regerror(r, &re, errstr, sizeof(errstr));
1635 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1636 errmsg("regular expression match for \"%s\" failed: %s",
1637 file_ident_user + 1, errstr)));
1647 if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
1649 /* substitution of the first argument requested */
1650 if (matches[1].rm_so < 0)
1653 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1654 errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
1655 file_ident_user + 1, file_pgrole)));
1662 * length: original length minus length of \1 plus length of match
1663 * plus null terminator
1665 regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
1666 strncpy(regexp_pgrole, file_pgrole, (ofs - file_pgrole));
1667 memcpy(regexp_pgrole + strlen(regexp_pgrole),
1668 ident_user + matches[1].rm_so,
1669 matches[1].rm_eo - matches[1].rm_so);
1670 strcat(regexp_pgrole, ofs + 2);
1674 /* no substitution, so copy the match */
1675 regexp_pgrole = pstrdup(file_pgrole);
1681 * now check if the username actually matched what the user is trying
1684 if (case_insensitive)
1686 if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
1691 if (strcmp(regexp_pgrole, pg_role) == 0)
1694 pfree(regexp_pgrole);
1700 /* Not regular expression, so make complete match */
1701 if (case_insensitive)
1703 if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
1704 pg_strcasecmp(file_ident_user, ident_user) == 0)
1709 if (strcmp(file_pgrole, pg_role) == 0 &&
1710 strcmp(file_ident_user, ident_user) == 0)
1719 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1720 errmsg("missing entry in file \"%s\" at end of line %d",
1721 IdentFileName, line_number)));
1727 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
1729 * See if the user with ident username "auth_user" is allowed to act
1730 * as Postgres user "pg_role" according to usermap "usermap_name".
1732 * Special case: Usermap NULL, equivalent to what was previously called
1733 * "sameuser" or "samerole", means don't look in the usermap file.
1734 * That's an implied map wherein "pg_role" must be identical to
1735 * "auth_user" in order to be authorized.
1737 * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
1740 check_usermap(const char *usermap_name,
1741 const char *pg_role,
1742 const char *auth_user,
1743 bool case_insensitive)
1745 bool found_entry = false,
1748 if (usermap_name == NULL || usermap_name[0] == '\0')
1750 if (case_insensitive)
1752 if (pg_strcasecmp(pg_role, auth_user) == 0)
1757 if (strcmp(pg_role, auth_user) == 0)
1761 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
1762 pg_role, auth_user)));
1763 return STATUS_ERROR;
1767 ListCell *line_cell,
1770 forboth(line_cell, ident_lines, num_cell, ident_line_nums)
1772 parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
1773 usermap_name, pg_role, auth_user, case_insensitive,
1774 &found_entry, &error);
1775 if (found_entry || error)
1779 if (!found_entry && !error)
1782 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
1783 usermap_name, pg_role, auth_user)));
1785 return found_entry ? STATUS_OK : STATUS_ERROR;
1790 * Read the ident config file and create a List of Lists of tokens in the file.
1797 if (ident_lines || ident_line_nums)
1798 free_lines(&ident_lines, &ident_line_nums);
1800 file = AllocateFile(IdentFileName, "r");
1803 /* not fatal ... we just won't do any special ident maps */
1805 (errcode_for_file_access(),
1806 errmsg("could not open usermap file \"%s\": %m",
1811 tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
1819 * Determine what authentication method should be used when accessing database
1820 * "database" from frontend "raddr", user "user". Return the method and
1821 * an optional argument (stored in fields of *port), and STATUS_OK.
1823 * Note that STATUS_ERROR indicates a problem with the hba config file.
1824 * If the file is OK but does not contain any entry matching the request,
1825 * we return STATUS_OK and method = uaImplicitReject.
1828 hba_getauthmethod(hbaPort *port)
1830 if (check_hba(port))
1833 return STATUS_ERROR;