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-2009, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
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 "storage/fd.h"
32 #include "utils/flatfiles.h"
33 #include "utils/guc.h"
37 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
38 #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
40 /* This is used to separate values in multi-valued column strings */
41 #define MULTI_VALUE_SEP "\001"
45 /* pre-parsed content of HBA config file */
46 static List
*parsed_hba_lines
= NIL
;
49 * These variables hold the pre-parsed contents of the ident
50 * configuration files, as well as the flat auth file.
51 * Each is a list of sublists, one sublist for
52 * each (non-empty, non-comment) line of the file. Each sublist's
53 * first item is an integer line number (so we can give somewhat-useful
54 * location info in error messages). Remaining items are palloc'd strings,
55 * one string per token on the line. Note there will always be at least
56 * one token, since blank lines are not entered in the data structure.
59 /* pre-parsed content of ident usermap file and corresponding line #s */
60 static List
*ident_lines
= NIL
;
61 static List
*ident_line_nums
= NIL
;
63 /* pre-parsed content of flat auth file and corresponding line #s */
64 static List
*role_lines
= NIL
;
65 static List
*role_line_nums
= NIL
;
67 /* sorted entries so we can do binary search lookups */
68 static List
**role_sorted
= NULL
; /* sorted role list, for bsearch() */
69 static int role_length
;
71 static void tokenize_file(const char *filename
, FILE *file
,
72 List
**lines
, List
**line_nums
);
73 static char *tokenize_inc_file(const char *outer_filename
,
74 const char *inc_filename
);
77 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
78 * so provide our own version.
81 pg_isblank(const char c
)
83 return c
== ' ' || c
== '\t' || c
== '\r';
88 * Grab one token out of fp. Tokens are strings of non-blank
89 * characters bounded by blank characters, commas, beginning of line, and
90 * end of line. Blank means space or tab. Tokens can be delimited by
91 * double quotes (this allows the inclusion of blanks, but not newlines).
93 * The token, if any, is returned at *buf (a buffer of size bufsz).
95 * If successful: store null-terminated token at *buf and return TRUE.
96 * If no more tokens on line: set *buf = '\0' and return FALSE.
98 * Leave file positioned at the character immediately after the token or EOF,
99 * whichever comes first. If no more tokens on line, position the file to the
100 * beginning of the next line or EOF, whichever comes first.
102 * Handle comments. Treat unquoted keywords that might be role names or
103 * database names specially, by appending a newline to them. Also, when
104 * a token is terminated by a comma, the comma is included in the returned
108 next_token(FILE *fp
, char *buf
, int bufsz
)
111 char *start_buf
= buf
;
112 char *end_buf
= buf
+ (bufsz
- 2);
113 bool in_quote
= false;
114 bool was_quote
= false;
115 bool saw_quote
= false;
117 Assert(end_buf
> start_buf
);
119 /* Move over initial whitespace and commas */
120 while ((c
= getc(fp
)) != EOF
&& (pg_isblank(c
) || c
== ','))
123 if (c
== EOF
|| c
== '\n')
130 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
131 * or unquoted whitespace.
133 while (c
!= EOF
&& c
!= '\n' &&
134 (!pg_isblank(c
) || in_quote
))
136 /* skip comments to EOL */
137 if (c
== '#' && !in_quote
)
139 while ((c
= getc(fp
)) != EOF
&& c
!= '\n')
141 /* If only comment, consume EOL too; return EOL */
142 if (c
!= EOF
&& buf
== start_buf
)
151 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
152 errmsg("authentication file token too long, skipping: \"%s\"",
154 /* Discard remainder of line */
155 while ((c
= getc(fp
)) != EOF
&& c
!= '\n')
160 if (c
!= '"' || was_quote
)
163 /* We pass back the comma so the caller knows there is more */
164 if (c
== ',' && !in_quote
)
167 /* Literal double-quote is two double-quotes */
168 if (in_quote
&& c
== '"')
169 was_quote
= !was_quote
;
175 in_quote
= !in_quote
;
183 * Put back the char right after the token (critical in case it is EOL,
184 * since we need to detect end-of-line at next call).
192 (strcmp(start_buf
, "all") == 0 ||
193 strcmp(start_buf
, "sameuser") == 0 ||
194 strcmp(start_buf
, "samegroup") == 0 ||
195 strcmp(start_buf
, "samerole") == 0))
197 /* append newline to a magical keyword */
202 return (saw_quote
|| buf
> start_buf
);
206 * Tokenize file and handle file inclusion and comma lists. We have
207 * to break apart the commas to expand any file names then
208 * reconstruct with commas.
210 * The result is a palloc'd string, or NULL if we have reached EOL.
213 next_token_expand(const char *filename
, FILE *file
)
216 char *comma_str
= pstrdup("");
217 bool got_something
= false;
224 if (!next_token(file
, buf
, sizeof(buf
)))
227 got_something
= true;
229 if (strlen(buf
) > 0 && buf
[strlen(buf
) - 1] == ',')
231 trailing_comma
= true;
232 buf
[strlen(buf
) - 1] = '\0';
235 trailing_comma
= false;
237 /* Is this referencing a file? */
239 incbuf
= tokenize_inc_file(filename
, buf
+ 1);
241 incbuf
= pstrdup(buf
);
243 needed
= strlen(comma_str
) + strlen(incbuf
) + 1;
246 comma_str
= repalloc(comma_str
, needed
);
247 strcat(comma_str
, incbuf
);
249 strcat(comma_str
, MULTI_VALUE_SEP
);
251 } while (trailing_comma
);
264 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
267 free_lines(List
**lines
, List
**line_nums
)
270 * Either both must be non-NULL, or both must be NULL
272 Assert((*lines
!= NIL
&& *line_nums
!= NIL
) ||
273 (*lines
== NIL
&& *line_nums
== NIL
));
278 * "lines" is a list of lists; each of those sublists consists of
279 * palloc'ed tokens, so we want to free each pointed-to token in a
280 * sublist, followed by the sublist itself, and finally the whole
285 foreach(line
, *lines
)
287 List
*ln
= lfirst(line
);
291 pfree(lfirst(token
));
292 /* free the sublist structure itself */
295 /* free the list structure itself */
297 /* clear the static variable */
303 list_free(*line_nums
);
310 tokenize_inc_file(const char *outer_filename
,
311 const char *inc_filename
)
320 if (is_absolute_path(inc_filename
))
322 /* absolute path is taken as-is */
323 inc_fullname
= pstrdup(inc_filename
);
327 /* relative path is relative to dir of calling file */
328 inc_fullname
= (char *) palloc(strlen(outer_filename
) + 1 +
329 strlen(inc_filename
) + 1);
330 strcpy(inc_fullname
, outer_filename
);
331 get_parent_directory(inc_fullname
);
332 join_path_components(inc_fullname
, inc_fullname
, inc_filename
);
333 canonicalize_path(inc_fullname
);
336 inc_file
= AllocateFile(inc_fullname
, "r");
337 if (inc_file
== NULL
)
340 (errcode_for_file_access(),
341 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
342 inc_filename
, inc_fullname
)));
345 /* return single space, it matches nothing */
349 /* There is possible recursion here if the file contains @ */
350 tokenize_file(inc_fullname
, inc_file
, &inc_lines
, &inc_line_nums
);
355 /* Create comma-separated string from List */
356 comma_str
= pstrdup("");
357 foreach(line
, inc_lines
)
359 List
*token_list
= (List
*) lfirst(line
);
362 foreach(token
, token_list
)
364 int oldlen
= strlen(comma_str
);
367 needed
= oldlen
+ strlen(lfirst(token
)) + 1;
370 comma_str
= repalloc(comma_str
, needed
);
372 strcat(comma_str
, MULTI_VALUE_SEP
);
373 strcat(comma_str
, lfirst(token
));
377 free_lines(&inc_lines
, &inc_line_nums
);
379 /* if file is empty, return single space rather than empty string */
380 if (strlen(comma_str
) == 0)
391 * Tokenize the given file, storing the resulting data into two lists:
392 * a list of sublists, each sublist containing the tokens in a line of
393 * the file, and a list of line numbers.
395 * filename must be the absolute path to the target file.
398 tokenize_file(const char *filename
, FILE *file
,
399 List
**lines
, List
**line_nums
)
401 List
*current_line
= NIL
;
405 *lines
= *line_nums
= NIL
;
409 buf
= next_token_expand(filename
, file
);
411 /* add token to list, unless we are at EOL or comment start */
414 if (current_line
== NIL
)
416 /* make a new line List, record its line number */
417 current_line
= lappend(current_line
, buf
);
418 *lines
= lappend(*lines
, current_line
);
419 *line_nums
= lappend_int(*line_nums
, line_number
);
423 /* append token to current line's list */
424 current_line
= lappend(current_line
, buf
);
429 /* we are at real or logical EOL, so force a new line List */
431 /* Advance line number whenever we reach EOL */
438 * Compare two lines based on their role/member names.
440 * Used for bsearch() lookup.
443 role_bsearch_cmp(const void *role
, const void *list
)
445 char *role2
= linitial(*(List
**) list
);
447 return strcmp(role
, role2
);
452 * Lookup a role name in the pg_auth file
455 get_role_line(const char *role
)
457 /* On some versions of Solaris, bsearch of zero items dumps core */
458 if (role_length
== 0)
461 return (List
**) bsearch((void *) role
,
462 (void *) role_sorted
,
470 * Does user belong to role?
472 * user is always the name given as the attempted login identifier.
473 * We check to see if it is a member of the specified role name.
476 is_member(const char *user
, const char *role
)
481 if ((line
= get_role_line(user
)) == NULL
)
482 return false; /* if user not exist, say "no" */
484 /* A user always belongs to its own role */
485 if (strcmp(user
, role
) == 0)
489 * skip over the role name, password, valuntil, examine all the membership
492 if (list_length(*line
) < 4)
494 for_each_cell(line_item
, lnext(lnext(lnext(list_head(*line
)))))
496 if (strcmp((char *) lfirst(line_item
), role
) == 0)
504 * Check comma-separated list for a match to role, allowing group names.
506 * NB: param_str is destructively modified! In current usage, this is
507 * okay only because this code is run after forking off from the postmaster,
508 * and so it doesn't matter that we clobber the stored hba info.
511 check_role(const char *role
, char *param_str
)
515 for (tok
= strtok(param_str
, MULTI_VALUE_SEP
);
517 tok
= strtok(NULL
, MULTI_VALUE_SEP
))
521 if (is_member(role
, tok
+ 1))
524 else if (strcmp(tok
, role
) == 0 ||
525 strcmp(tok
, "all\n") == 0)
533 * Check to see if db/role combination matches param string.
535 * NB: param_str is destructively modified! In current usage, this is
536 * okay only because this code is run after forking off from the postmaster,
537 * and so it doesn't matter that we clobber the stored hba info.
540 check_db(const char *dbname
, const char *role
, char *param_str
)
544 for (tok
= strtok(param_str
, MULTI_VALUE_SEP
);
546 tok
= strtok(NULL
, MULTI_VALUE_SEP
))
548 if (strcmp(tok
, "all\n") == 0)
550 else if (strcmp(tok
, "sameuser\n") == 0)
552 if (strcmp(dbname
, role
) == 0)
555 else if (strcmp(tok
, "samegroup\n") == 0 ||
556 strcmp(tok
, "samerole\n") == 0)
558 if (is_member(role
, dbname
))
561 else if (strcmp(tok
, dbname
) == 0)
569 * Macros used to check and report on invalid configuration options.
570 * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
572 * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
573 * method is actually the one specified. Used as a shortcut when
574 * the option is only valid for one authentication method.
575 * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method,
576 * reporting error if it's not.
578 #define INVALID_AUTH_OPTION(optname, validmethods) do {\
580 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
581 /* translator: the second %s is a list of auth methods */ \
582 errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
583 optname, _(validmethods)), \
584 errcontext("line %d of configuration file \"%s\"", \
585 line_num, HbaFileName))); \
589 #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
590 if (parsedline->auth_method != methodval) \
591 INVALID_AUTH_OPTION(optname, validmethods); \
594 #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
595 if (argvar == NULL) {\
597 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
598 errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
599 authname, argname), \
600 errcontext("line %d of configuration file \"%s\"", \
601 line_num, HbaFileName))); \
608 * Parse one line in the hba config file and store the result in
609 * a HbaLine structure.
612 parse_hba_line(List
*line
, int line_num
, HbaLine
*parsedline
)
615 struct addrinfo
*gai_result
;
616 struct addrinfo hints
;
622 line_item
= list_head(line
);
624 parsedline
->linenumber
= line_num
;
626 /* Check the record type. */
627 token
= lfirst(line_item
);
628 if (strcmp(token
, "local") == 0)
630 parsedline
->conntype
= ctLocal
;
632 else if (strcmp(token
, "host") == 0
633 || strcmp(token
, "hostssl") == 0
634 || strcmp(token
, "hostnossl") == 0)
637 if (token
[4] == 's') /* "hostssl" */
640 parsedline
->conntype
= ctHostSSL
;
643 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
644 errmsg("hostssl not supported on this platform"),
645 errhint("compile with --enable-ssl to use SSL connections"),
646 errcontext("line %d of configuration file \"%s\"",
647 line_num
, HbaFileName
)));
652 else if (token
[4] == 'n') /* "hostnossl" */
654 parsedline
->conntype
= ctHostNoSSL
;
659 /* "host", or "hostnossl" and SSL support not built in */
660 parsedline
->conntype
= ctHost
;
666 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
667 errmsg("invalid connection type \"%s\"",
669 errcontext("line %d of configuration file \"%s\"",
670 line_num
, HbaFileName
)));
674 /* Get the database. */
675 line_item
= lnext(line_item
);
679 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
680 errmsg("end-of-line before database specification"),
681 errcontext("line %d of configuration file \"%s\"",
682 line_num
, HbaFileName
)));
685 parsedline
->database
= pstrdup(lfirst(line_item
));
688 line_item
= lnext(line_item
);
692 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
693 errmsg("end-of-line before role specification"),
694 errcontext("line %d of configuration file \"%s\"",
695 line_num
, HbaFileName
)));
698 parsedline
->role
= pstrdup(lfirst(line_item
));
700 if (parsedline
->conntype
!= ctLocal
)
702 /* Read the IP address field. (with or without CIDR netmask) */
703 line_item
= lnext(line_item
);
707 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
708 errmsg("end-of-line before IP address specification"),
709 errcontext("line %d of configuration file \"%s\"",
710 line_num
, HbaFileName
)));
713 token
= pstrdup(lfirst(line_item
));
715 /* Check if it has a CIDR suffix and if so isolate it */
716 cidr_slash
= strchr(token
, '/');
720 /* Get the IP address either way */
721 hints
.ai_flags
= AI_NUMERICHOST
;
722 hints
.ai_family
= PF_UNSPEC
;
723 hints
.ai_socktype
= 0;
724 hints
.ai_protocol
= 0;
725 hints
.ai_addrlen
= 0;
726 hints
.ai_canonname
= NULL
;
727 hints
.ai_addr
= NULL
;
728 hints
.ai_next
= NULL
;
730 ret
= pg_getaddrinfo_all(token
, NULL
, &hints
, &gai_result
);
731 if (ret
|| !gai_result
)
734 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
735 errmsg("invalid IP address \"%s\": %s",
736 token
, gai_strerror(ret
)),
737 errcontext("line %d of configuration file \"%s\"",
738 line_num
, HbaFileName
)));
742 pg_freeaddrinfo_all(hints
.ai_family
, gai_result
);
749 memcpy(&parsedline
->addr
, gai_result
->ai_addr
, gai_result
->ai_addrlen
);
750 pg_freeaddrinfo_all(hints
.ai_family
, gai_result
);
752 /* Get the netmask */
755 if (pg_sockaddr_cidr_mask(&parsedline
->mask
, cidr_slash
+ 1,
756 parsedline
->addr
.ss_family
) < 0)
759 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
760 errmsg("invalid CIDR mask in address \"%s\"",
762 errcontext("line %d of configuration file \"%s\"",
763 line_num
, HbaFileName
)));
769 /* Read the mask field. */
770 line_item
= lnext(line_item
);
774 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
775 errmsg("end-of-line before netmask specification"),
776 errcontext("line %d of configuration file \"%s\"",
777 line_num
, HbaFileName
)));
780 token
= lfirst(line_item
);
782 ret
= pg_getaddrinfo_all(token
, NULL
, &hints
, &gai_result
);
783 if (ret
|| !gai_result
)
786 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
787 errmsg("invalid IP mask \"%s\": %s",
788 token
, gai_strerror(ret
)),
789 errcontext("line %d of configuration file \"%s\"",
790 line_num
, HbaFileName
)));
792 pg_freeaddrinfo_all(hints
.ai_family
, gai_result
);
796 memcpy(&parsedline
->mask
, gai_result
->ai_addr
, gai_result
->ai_addrlen
);
797 pg_freeaddrinfo_all(hints
.ai_family
, gai_result
);
799 if (parsedline
->addr
.ss_family
!= parsedline
->mask
.ss_family
)
802 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
803 errmsg("IP address and mask do not match in file \"%s\" line %d",
804 HbaFileName
, line_num
)));
810 /* Get the authentication method */
811 line_item
= lnext(line_item
);
815 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
816 errmsg("end-of-line before authentication method"),
817 errcontext("line %d of configuration file \"%s\"",
818 line_num
, HbaFileName
)));
821 token
= lfirst(line_item
);
824 if (strcmp(token
, "trust") == 0)
825 parsedline
->auth_method
= uaTrust
;
826 else if (strcmp(token
, "ident") == 0)
827 parsedline
->auth_method
= uaIdent
;
828 else if (strcmp(token
, "password") == 0)
829 parsedline
->auth_method
= uaPassword
;
830 else if (strcmp(token
, "krb5") == 0)
832 parsedline
->auth_method
= uaKrb5
;
836 else if (strcmp(token
, "gss") == 0)
838 parsedline
->auth_method
= uaGSS
;
842 else if (strcmp(token
, "sspi") == 0)
844 parsedline
->auth_method
= uaSSPI
;
848 else if (strcmp(token
, "reject") == 0)
849 parsedline
->auth_method
= uaReject
;
850 else if (strcmp(token
, "md5") == 0)
852 if (Db_user_namespace
)
855 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
856 errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
859 parsedline
->auth_method
= uaMD5
;
861 else if (strcmp(token
, "pam") == 0)
863 parsedline
->auth_method
= uaPAM
;
867 else if (strcmp(token
, "ldap") == 0)
869 parsedline
->auth_method
= uaLDAP
;
873 else if (strcmp(token
, "cert") == 0)
875 parsedline
->auth_method
= uaCert
;
882 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
883 errmsg("invalid authentication method \"%s\"",
885 errcontext("line %d of configuration file \"%s\"",
886 line_num
, HbaFileName
)));
893 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
894 errmsg("invalid authentication method \"%s\": not supported on this platform",
896 errcontext("line %d of configuration file \"%s\"",
897 line_num
, HbaFileName
)));
901 /* Invalid authentication combinations */
902 if (parsedline
->conntype
== ctLocal
&&
903 parsedline
->auth_method
== uaKrb5
)
906 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
907 errmsg("krb5 authentication is not supported on local sockets"),
908 errcontext("line %d of configuration file \"%s\"",
909 line_num
, HbaFileName
)));
913 if (parsedline
->conntype
!= ctHostSSL
&&
914 parsedline
->auth_method
== uaCert
)
917 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
918 errmsg("cert authentication is only supported on hostssl connections"),
919 errcontext("line %d of configuration file \"%s\"",
920 line_num
, HbaFileName
)));
924 /* Parse remaining arguments */
925 while ((line_item
= lnext(line_item
)) != NULL
)
929 token
= lfirst(line_item
);
931 c
= strchr(token
, '=');
935 * Got something that's not a name=value pair.
937 * XXX: attempt to do some backwards compatible parsing here?
940 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
941 errmsg("authentication option not in name=value format: %s", token
),
942 errcontext("line %d of configuration file \"%s\"",
943 line_num
, HbaFileName
)));
948 *c
++ = '\0'; /* token now holds "name", c holds "value" */
949 if (strcmp(token
, "map") == 0)
951 if (parsedline
->auth_method
!= uaIdent
&&
952 parsedline
->auth_method
!= uaKrb5
&&
953 parsedline
->auth_method
!= uaGSS
&&
954 parsedline
->auth_method
!= uaSSPI
&&
955 parsedline
->auth_method
!= uaCert
)
956 INVALID_AUTH_OPTION("map", gettext_noop("ident, krb5, gssapi, sspi and cert"));
957 parsedline
->usermap
= pstrdup(c
);
959 else if (strcmp(token
, "clientcert") == 0)
962 * Since we require ctHostSSL, this really can never happen on
963 * non-SSL-enabled builds, so don't bother checking for
966 if (parsedline
->conntype
!= ctHostSSL
)
969 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
970 errmsg("clientcert can only be configured for \"hostssl\" rows"),
971 errcontext("line %d of configuration file \"%s\"",
972 line_num
, HbaFileName
)));
975 if (strcmp(c
, "1") == 0)
977 if (!secure_loaded_verify_locations())
980 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
981 errmsg("client certificates can only be checked if a root certificate store is available"),
982 errdetail("make sure the root certificate store is present and readable"),
983 errcontext("line %d of configuration file \"%s\"",
984 line_num
, HbaFileName
)));
987 parsedline
->clientcert
= true;
991 if (parsedline
->auth_method
== uaCert
)
994 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
995 errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
996 errcontext("line %d of configuration file \"%s\"",
997 line_num
, HbaFileName
)));
1000 parsedline
->clientcert
= false;
1003 else if (strcmp(token
, "pamservice") == 0)
1005 REQUIRE_AUTH_OPTION(uaPAM
, "pamservice", "pam");
1006 parsedline
->pamservice
= pstrdup(c
);
1008 else if (strcmp(token
, "ldaptls") == 0)
1010 REQUIRE_AUTH_OPTION(uaLDAP
, "ldaptls", "ldap");
1011 if (strcmp(c
, "1") == 0)
1012 parsedline
->ldaptls
= true;
1014 parsedline
->ldaptls
= false;
1016 else if (strcmp(token
, "ldapserver") == 0)
1018 REQUIRE_AUTH_OPTION(uaLDAP
, "ldapserver", "ldap");
1019 parsedline
->ldapserver
= pstrdup(c
);
1021 else if (strcmp(token
, "ldapport") == 0)
1023 REQUIRE_AUTH_OPTION(uaLDAP
, "ldapport", "ldap");
1024 parsedline
->ldapport
= atoi(c
);
1025 if (parsedline
->ldapport
== 0)
1028 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
1029 errmsg("invalid LDAP port number: \"%s\"", c
),
1030 errcontext("line %d of configuration file \"%s\"",
1031 line_num
, HbaFileName
)));
1035 else if (strcmp(token
, "ldapprefix") == 0)
1037 REQUIRE_AUTH_OPTION(uaLDAP
, "ldapprefix", "ldap");
1038 parsedline
->ldapprefix
= pstrdup(c
);
1040 else if (strcmp(token
, "ldapsuffix") == 0)
1042 REQUIRE_AUTH_OPTION(uaLDAP
, "ldapsuffix", "ldap");
1043 parsedline
->ldapsuffix
= pstrdup(c
);
1045 else if (strcmp(token
, "krb_server_hostname") == 0)
1047 REQUIRE_AUTH_OPTION(uaKrb5
, "krb_server_hostname", "krb5");
1048 parsedline
->krb_server_hostname
= pstrdup(c
);
1050 else if (strcmp(token
, "krb_realm") == 0)
1052 if (parsedline
->auth_method
!= uaKrb5
&&
1053 parsedline
->auth_method
!= uaGSS
&&
1054 parsedline
->auth_method
!= uaSSPI
)
1055 INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
1056 parsedline
->krb_realm
= pstrdup(c
);
1058 else if (strcmp(token
, "include_realm") == 0)
1060 if (parsedline
->auth_method
!= uaKrb5
&&
1061 parsedline
->auth_method
!= uaGSS
&&
1062 parsedline
->auth_method
!= uaSSPI
)
1063 INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
1064 if (strcmp(c
, "1") == 0)
1065 parsedline
->include_realm
= true;
1067 parsedline
->include_realm
= false;
1072 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
1073 errmsg("unknown authentication option name: \"%s\"", token
),
1074 errcontext("line %d of configuration file \"%s\"",
1075 line_num
, HbaFileName
)));
1082 * Check if the selected authentication method has any mandatory arguments
1085 if (parsedline
->auth_method
== uaLDAP
)
1087 MANDATORY_AUTH_ARG(parsedline
->ldapserver
, "ldapserver", "ldap");
1091 * Enforce any parameters implied by other settings.
1093 if (parsedline
->auth_method
== uaCert
)
1095 parsedline
->clientcert
= true;
1103 * Scan the (pre-parsed) hba file line by line, looking for a match
1104 * to the port's connection request.
1107 check_hba(hbaPort
*port
)
1112 foreach(line
, parsed_hba_lines
)
1114 hba
= (HbaLine
*) lfirst(line
);
1116 /* Check connection type */
1117 if (hba
->conntype
== ctLocal
)
1119 if (!IS_AF_UNIX(port
->raddr
.addr
.ss_family
))
1124 if (IS_AF_UNIX(port
->raddr
.addr
.ss_family
))
1127 /* Check SSL state */
1131 /* Connection is SSL, match both "host" and "hostssl" */
1132 if (hba
->conntype
== ctHostNoSSL
)
1137 /* Connection is not SSL, match both "host" and "hostnossl" */
1138 if (hba
->conntype
== ctHostSSL
)
1142 /* No SSL support, so reject "hostssl" lines */
1143 if (hba
->conntype
== ctHostSSL
)
1147 /* Check IP address */
1148 if (port
->raddr
.addr
.ss_family
== hba
->addr
.ss_family
)
1150 if (!pg_range_sockaddr(&port
->raddr
.addr
, &hba
->addr
, &hba
->mask
))
1154 else if (hba
->addr
.ss_family
== AF_INET
&&
1155 port
->raddr
.addr
.ss_family
== AF_INET6
)
1158 * Wrong address family. We allow only one case: if the file
1159 * has IPv4 and the port is IPv6, promote the file address to
1160 * IPv6 and try to match that way.
1162 struct sockaddr_storage addrcopy
,
1165 memcpy(&addrcopy
, &hba
->addr
, sizeof(addrcopy
));
1166 memcpy(&maskcopy
, &hba
->mask
, sizeof(maskcopy
));
1167 pg_promote_v4_to_v6_addr(&addrcopy
);
1168 pg_promote_v4_to_v6_mask(&maskcopy
);
1170 if (!pg_range_sockaddr(&port
->raddr
.addr
, &addrcopy
, &maskcopy
))
1173 #endif /* HAVE_IPV6 */
1175 /* Wrong address family, no IPV6 */
1179 /* Check database and role */
1180 if (!check_db(port
->database_name
, port
->user_name
, hba
->database
))
1183 if (!check_role(port
->user_name
, hba
->role
))
1186 /* Found a record that matched! */
1191 /* If no matching entry was found, synthesize 'reject' entry. */
1192 hba
= palloc0(sizeof(HbaLine
));
1193 hba
->auth_method
= uaReject
;
1198 * XXX: Return false only happens if we have a parsing error, which we can
1199 * no longer have (parsing now in postmaster). Consider changing API.
1205 * Load role/password mapping file
1213 /* Discard any old data */
1214 if (role_lines
|| role_line_nums
)
1215 free_lines(&role_lines
, &role_line_nums
);
1221 /* Read in the file contents */
1222 filename
= auth_getflatfilename();
1223 role_file
= AllocateFile(filename
, "r");
1225 if (role_file
== NULL
)
1227 /* no complaint if not there */
1228 if (errno
!= ENOENT
)
1230 (errcode_for_file_access(),
1231 errmsg("could not open file \"%s\": %m", filename
)));
1236 tokenize_file(filename
, role_file
, &role_lines
, &role_line_nums
);
1238 FreeFile(role_file
);
1241 /* create array for binary searching */
1242 role_length
= list_length(role_lines
);
1248 /* We assume the flat file was written already-sorted */
1249 role_sorted
= palloc(role_length
* sizeof(List
*));
1250 foreach(line
, role_lines
)
1251 role_sorted
[i
++] = lfirst(line
);
1256 * Free the contents of a hba record
1259 free_hba_record(HbaLine
*record
)
1261 if (record
->database
)
1262 pfree(record
->database
);
1264 pfree(record
->role
);
1265 if (record
->pamservice
)
1266 pfree(record
->pamservice
);
1267 if (record
->ldapserver
)
1268 pfree(record
->ldapserver
);
1269 if (record
->ldapprefix
)
1270 pfree(record
->ldapprefix
);
1271 if (record
->ldapsuffix
)
1272 pfree(record
->ldapsuffix
);
1273 if (record
->krb_server_hostname
)
1274 pfree(record
->krb_server_hostname
);
1275 if (record
->krb_realm
)
1276 pfree(record
->krb_realm
);
1280 * Free all records on the parsed HBA list
1283 clean_hba_list(List
*lines
)
1287 foreach(line
, lines
)
1289 HbaLine
*parsed
= (HbaLine
*) lfirst(line
);
1292 free_hba_record(parsed
);
1298 * Read the config file and create a List of HbaLine records for the contents.
1300 * The configuration is read into a temporary list, and if any parse error occurs
1301 * the old list is kept in place and false is returned. Only if the whole file
1302 * parses Ok is the list replaced, and the function returns true.
1308 List
*hba_lines
= NIL
;
1309 List
*hba_line_nums
= NIL
;
1312 List
*new_parsed_lines
= NIL
;
1315 file
= AllocateFile(HbaFileName
, "r");
1319 (errcode_for_file_access(),
1320 errmsg("could not open configuration file \"%s\": %m",
1324 * Caller will take care of making this a FATAL error in case this is
1325 * the initial startup. If it happens on reload, we just keep the old
1331 tokenize_file(HbaFileName
, file
, &hba_lines
, &hba_line_nums
);
1334 /* Now parse all the lines */
1335 forboth(line
, hba_lines
, line_num
, hba_line_nums
)
1339 newline
= palloc0(sizeof(HbaLine
));
1341 if (!parse_hba_line(lfirst(line
), lfirst_int(line_num
), newline
))
1343 /* Parse error in the file, so indicate there's a problem */
1344 free_hba_record(newline
);
1348 * Keep parsing the rest of the file so we can report errors on
1349 * more than the first row. Error has already been reported in the
1350 * parsing function, so no need to log it here.
1356 new_parsed_lines
= lappend(new_parsed_lines
, newline
);
1361 /* Parsing failed at one or more rows, so bail out */
1362 clean_hba_list(new_parsed_lines
);
1366 /* Loaded new file successfully, replace the one we use */
1367 clean_hba_list(parsed_hba_lines
);
1368 parsed_hba_lines
= new_parsed_lines
;
1370 /* Free the temporary lists */
1371 free_lines(&hba_lines
, &hba_line_nums
);
1377 * Read and parse one line from the flat pg_database file.
1379 * Returns TRUE on success, FALSE if EOF; bad data causes elog(FATAL).
1381 * Output parameters:
1382 * dbname: gets database name (must be of size NAMEDATALEN bytes)
1383 * dboid: gets database OID
1384 * dbtablespace: gets database's default tablespace's OID
1385 * dbfrozenxid: gets database's frozen XID
1387 * This is not much related to the other functions in hba.c, but we put it
1388 * here because it uses the next_token() infrastructure.
1391 read_pg_database_line(FILE *fp
, char *dbname
, Oid
*dboid
,
1392 Oid
*dbtablespace
, TransactionId
*dbfrozenxid
)
1394 char buf
[MAX_TOKEN
];
1398 if (!next_token(fp
, buf
, sizeof(buf
)))
1400 if (strlen(buf
) >= NAMEDATALEN
)
1401 elog(FATAL
, "bad data in flat pg_database file");
1402 strcpy(dbname
, buf
);
1403 next_token(fp
, buf
, sizeof(buf
));
1404 if (!isdigit((unsigned char) buf
[0]))
1405 elog(FATAL
, "bad data in flat pg_database file");
1406 *dboid
= atooid(buf
);
1407 next_token(fp
, buf
, sizeof(buf
));
1408 if (!isdigit((unsigned char) buf
[0]))
1409 elog(FATAL
, "bad data in flat pg_database file");
1410 *dbtablespace
= atooid(buf
);
1411 next_token(fp
, buf
, sizeof(buf
));
1412 if (!isdigit((unsigned char) buf
[0]))
1413 elog(FATAL
, "bad data in flat pg_database file");
1414 *dbfrozenxid
= atoxid(buf
);
1415 /* expect EOL next */
1416 if (next_token(fp
, buf
, sizeof(buf
)))
1417 elog(FATAL
, "bad data in flat pg_database file");
1422 * Process one line from the ident config file.
1424 * Take the line and compare it to the needed map, pg_role and ident_user.
1425 * *found_p and *error_p are set according to our results.
1428 parse_ident_usermap(List
*line
, int line_number
, const char *usermap_name
,
1429 const char *pg_role
, const char *ident_user
,
1430 bool case_insensitive
, bool *found_p
, bool *error_p
)
1432 ListCell
*line_item
;
1436 char *file_ident_user
;
1441 Assert(line
!= NIL
);
1442 line_item
= list_head(line
);
1444 /* Get the map token (must exist) */
1445 token
= lfirst(line_item
);
1448 /* Get the ident user token */
1449 line_item
= lnext(line_item
);
1452 token
= lfirst(line_item
);
1453 file_ident_user
= token
;
1455 /* Get the PG rolename token */
1456 line_item
= lnext(line_item
);
1459 token
= lfirst(line_item
);
1460 file_pgrole
= token
;
1462 if (strcmp(file_map
, usermap_name
) != 0)
1463 /* Line does not match the map name we're looking for, so just abort */
1467 if (file_ident_user
[0] == '/')
1470 * When system username starts with a slash, treat it as a regular
1471 * expression. In this case, we process the system username as a
1472 * regular expression that returns exactly one match. This is replaced
1473 * for \1 in the database username string, if present.
1477 regmatch_t matches
[2];
1481 char *regexp_pgrole
;
1483 wstr
= palloc((strlen(file_ident_user
+ 1) + 1) * sizeof(pg_wchar
));
1484 wlen
= pg_mb2wchar_with_len(file_ident_user
+ 1, wstr
, strlen(file_ident_user
+ 1));
1487 * XXX: Major room for optimization: regexps could be compiled when
1488 * the file is loaded and then re-used in every connection.
1490 r
= pg_regcomp(&re
, wstr
, wlen
, REG_ADVANCED
);
1495 pg_regerror(r
, &re
, errstr
, sizeof(errstr
));
1497 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION
),
1498 errmsg("invalid regular expression \"%s\": %s", file_ident_user
+ 1, errstr
)));
1506 wstr
= palloc((strlen(ident_user
) + 1) * sizeof(pg_wchar
));
1507 wlen
= pg_mb2wchar_with_len(ident_user
, wstr
, strlen(ident_user
));
1509 r
= pg_regexec(&re
, wstr
, wlen
, 0, NULL
, 2, matches
, 0);
1514 if (r
!= REG_NOMATCH
)
1516 /* REG_NOMATCH is not an error, everything else is */
1517 pg_regerror(r
, &re
, errstr
, sizeof(errstr
));
1519 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION
),
1520 errmsg("regular expression match for \"%s\" failed: %s", file_ident_user
+ 1, errstr
)));
1530 if ((ofs
= strstr(file_pgrole
, "\\1")) != NULL
)
1532 /* substitution of the first argument requested */
1533 if (matches
[1].rm_so
< 0)
1536 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION
),
1537 errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
1538 file_ident_user
+ 1, file_pgrole
)));
1545 * length: original length minus length of \1 plus length of match
1546 * plus null terminator
1548 regexp_pgrole
= palloc0(strlen(file_pgrole
) - 2 + (matches
[1].rm_eo
- matches
[1].rm_so
) + 1);
1549 strncpy(regexp_pgrole
, file_pgrole
, (ofs
- file_pgrole
));
1550 memcpy(regexp_pgrole
+ strlen(regexp_pgrole
),
1551 ident_user
+ matches
[1].rm_so
,
1552 matches
[1].rm_eo
- matches
[1].rm_so
);
1553 strcat(regexp_pgrole
, ofs
+ 2);
1557 /* no substitution, so copy the match */
1558 regexp_pgrole
= pstrdup(file_pgrole
);
1564 * now check if the username actually matched what the user is trying
1567 if (case_insensitive
)
1569 if (pg_strcasecmp(regexp_pgrole
, pg_role
) == 0)
1574 if (strcmp(regexp_pgrole
, pg_role
) == 0)
1577 pfree(regexp_pgrole
);
1583 /* Not regular expression, so make complete match */
1584 if (case_insensitive
)
1586 if (pg_strcasecmp(file_pgrole
, pg_role
) == 0 &&
1587 pg_strcasecmp(file_ident_user
, ident_user
) == 0)
1592 if (strcmp(file_pgrole
, pg_role
) == 0 &&
1593 strcmp(file_ident_user
, ident_user
) == 0)
1602 (errcode(ERRCODE_CONFIG_FILE_ERROR
),
1603 errmsg("missing entry in file \"%s\" at end of line %d",
1604 IdentFileName
, line_number
)));
1610 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
1612 * See if the user with ident username "ident_user" is allowed to act
1613 * as Postgres user "pgrole" according to usermap "usermap_name".
1615 * Special case: Usermap NULL, equivalent to what was previously called
1616 * "sameuser" or "samerole", don't look in the usermap
1617 * file. That's an implied map where "pgrole" must be identical to
1618 * "ident_user" in order to be authorized.
1620 * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
1623 check_usermap(const char *usermap_name
,
1624 const char *pg_role
,
1625 const char *auth_user
,
1626 bool case_insensitive
)
1628 bool found_entry
= false,
1631 if (usermap_name
== NULL
|| usermap_name
[0] == '\0')
1633 if (case_insensitive
)
1635 if (pg_strcasecmp(pg_role
, auth_user
) == 0)
1640 if (strcmp(pg_role
, auth_user
) == 0)
1644 (errmsg("provided username (%s) and authenticated username (%s) don't match",
1645 auth_user
, pg_role
)));
1646 return STATUS_ERROR
;
1650 ListCell
*line_cell
,
1653 forboth(line_cell
, ident_lines
, num_cell
, ident_line_nums
)
1655 parse_ident_usermap(lfirst(line_cell
), lfirst_int(num_cell
),
1656 usermap_name
, pg_role
, auth_user
, case_insensitive
,
1657 &found_entry
, &error
);
1658 if (found_entry
|| error
)
1662 if (!found_entry
&& !error
)
1665 (errmsg("no match in usermap for user \"%s\" authenticated as \"%s\"",
1666 pg_role
, auth_user
),
1667 errcontext("usermap \"%s\"", usermap_name
)));
1669 return found_entry
? STATUS_OK
: STATUS_ERROR
;
1674 * Read the ident config file and create a List of Lists of tokens in the file.
1681 if (ident_lines
|| ident_line_nums
)
1682 free_lines(&ident_lines
, &ident_line_nums
);
1684 file
= AllocateFile(IdentFileName
, "r");
1687 /* not fatal ... we just won't do any special ident maps */
1689 (errcode_for_file_access(),
1690 errmsg("could not open Ident usermap file \"%s\": %m",
1695 tokenize_file(IdentFileName
, file
, &ident_lines
, &ident_line_nums
);
1703 * Determine what authentication method should be used when accessing database
1704 * "database" from frontend "raddr", user "user". Return the method and
1705 * an optional argument (stored in fields of *port), and STATUS_OK.
1707 * Note that STATUS_ERROR indicates a problem with the hba config file.
1708 * If the file is OK but does not contain any entry matching the request,
1709 * we return STATUS_OK and method = uaReject.
1712 hba_getauthmethod(hbaPort
*port
)
1714 if (check_hba(port
))
1717 return STATUS_ERROR
;