]> granicus.if.org Git - postgresql/blob - src/backend/libpq/hba.c
e5fb65f75d39a24c9f807712397144b5a3d5825a
[postgresql] / src / backend / libpq / hba.c
1 /*-------------------------------------------------------------------------
2  *
3  * hba.c
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).
7  *
8  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.206 2010/04/21 03:32:53 tgl Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18
19 #include <ctype.h>
20 #include <pwd.h>
21 #include <fcntl.h>
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <unistd.h>
27
28 #include "libpq/ip.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"
36
37
38 #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
39 #define atoxid(x)  ((TransactionId) strtoul((x), NULL, 10))
40
41 /* This is used to separate values in multi-valued column strings */
42 #define MULTI_VALUE_SEP "\001"
43
44 #define MAX_TOKEN       256
45
46 /* callback data for check_network_callback */
47 typedef struct check_network_data
48 {
49         IPCompareMethod method;         /* test method */
50         SockAddr   *raddr;                      /* client's actual address */
51         bool            result;                 /* set to true if match */
52 } check_network_data;
53
54 /* pre-parsed content of HBA config file: list of HbaLine structs */
55 static List *parsed_hba_lines = NIL;
56
57 /*
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.
65  */
66 static List *ident_lines = NIL;
67 static List *ident_line_nums = NIL;
68
69
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);
74
75 /*
76  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
77  * so provide our own version.
78  */
79 bool
80 pg_isblank(const char c)
81 {
82         return c == ' ' || c == '\t' || c == '\r';
83 }
84
85
86 /*
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).
91  *
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.)
97  *
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.
100  *
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.
104  *
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
108  * token.
109  */
110 static bool
111 next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
112 {
113         int                     c;
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;
119
120         /* end_buf reserves two bytes to ensure we can append \n and \0 */
121         Assert(end_buf > start_buf);
122
123         *initial_quote = false;
124
125         /* Move over initial whitespace and commas */
126         while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
127                 ;
128
129         if (c == EOF || c == '\n')
130         {
131                 *buf = '\0';
132                 return false;
133         }
134
135         /*
136          * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
137          * or unquoted whitespace.
138          */
139         while (c != EOF && c != '\n' &&
140                    (!pg_isblank(c) || in_quote))
141         {
142                 /* skip comments to EOL */
143                 if (c == '#' && !in_quote)
144                 {
145                         while ((c = getc(fp)) != EOF && c != '\n')
146                                 ;
147                         /* If only comment, consume EOL too; return EOL */
148                         if (c != EOF && buf == start_buf)
149                                 c = getc(fp);
150                         break;
151                 }
152
153                 if (buf >= end_buf)
154                 {
155                         *buf = '\0';
156                         ereport(LOG,
157                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
158                            errmsg("authentication file token too long, skipping: \"%s\"",
159                                           start_buf)));
160                         /* Discard remainder of line */
161                         while ((c = getc(fp)) != EOF && c != '\n')
162                                 ;
163                         break;
164                 }
165
166                 if (c != '"' || was_quote)
167                         *buf++ = c;
168
169                 /* We pass back the comma so the caller knows there is more */
170                 if (c == ',' && !in_quote)
171                         break;
172
173                 /* Literal double-quote is two double-quotes */
174                 if (in_quote && c == '"')
175                         was_quote = !was_quote;
176                 else
177                         was_quote = false;
178
179                 if (c == '"')
180                 {
181                         in_quote = !in_quote;
182                         saw_quote = true;
183                         if (buf == start_buf)
184                                 *initial_quote = true;
185                 }
186
187                 c = getc(fp);
188         }
189
190         /*
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).
193          */
194         if (c != EOF)
195                 ungetc(c, fp);
196
197         *buf = '\0';
198
199         if (!saw_quote &&
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))
205         {
206                 /* append newline to a magical keyword */
207                 *buf++ = '\n';
208                 *buf = '\0';
209         }
210
211         return (saw_quote || buf > start_buf);
212 }
213
214 /*
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.
218  *
219  * The result is a palloc'd string, or NULL if we have reached EOL.
220  */
221 static char *
222 next_token_expand(const char *filename, FILE *file)
223 {
224         char            buf[MAX_TOKEN];
225         char       *comma_str = pstrdup("");
226         bool            got_something = false;
227         bool            trailing_comma;
228         bool            initial_quote;
229         char       *incbuf;
230         int                     needed;
231
232         do
233         {
234                 if (!next_token(file, buf, sizeof(buf), &initial_quote))
235                         break;
236
237                 got_something = true;
238
239                 if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
240                 {
241                         trailing_comma = true;
242                         buf[strlen(buf) - 1] = '\0';
243                 }
244                 else
245                         trailing_comma = false;
246
247                 /* Is this referencing a file? */
248                 if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
249                         incbuf = tokenize_inc_file(filename, buf + 1);
250                 else
251                         incbuf = pstrdup(buf);
252
253                 needed = strlen(comma_str) + strlen(incbuf) + 1;
254                 if (trailing_comma)
255                         needed++;
256                 comma_str = repalloc(comma_str, needed);
257                 strcat(comma_str, incbuf);
258                 if (trailing_comma)
259                         strcat(comma_str, MULTI_VALUE_SEP);
260                 pfree(incbuf);
261         } while (trailing_comma);
262
263         if (!got_something)
264         {
265                 pfree(comma_str);
266                 return NULL;
267         }
268
269         return comma_str;
270 }
271
272
273 /*
274  * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
275  */
276 static void
277 free_lines(List **lines, List **line_nums)
278 {
279         /*
280          * Either both must be non-NULL, or both must be NULL
281          */
282         Assert((*lines != NIL && *line_nums != NIL) ||
283                    (*lines == NIL && *line_nums == NIL));
284
285         if (*lines)
286         {
287                 /*
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
291                  * list.
292                  */
293                 ListCell   *line;
294
295                 foreach(line, *lines)
296                 {
297                         List       *ln = lfirst(line);
298                         ListCell   *token;
299
300                         foreach(token, ln)
301                                 pfree(lfirst(token));
302                         /* free the sublist structure itself */
303                         list_free(ln);
304                 }
305                 /* free the list structure itself */
306                 list_free(*lines);
307                 /* clear the static variable */
308                 *lines = NIL;
309         }
310
311         if (*line_nums)
312         {
313                 list_free(*line_nums);
314                 *line_nums = NIL;
315         }
316 }
317
318
319 static char *
320 tokenize_inc_file(const char *outer_filename,
321                                   const char *inc_filename)
322 {
323         char       *inc_fullname;
324         FILE       *inc_file;
325         List       *inc_lines;
326         List       *inc_line_nums;
327         ListCell   *line;
328         char       *comma_str;
329
330         if (is_absolute_path(inc_filename))
331         {
332                 /* absolute path is taken as-is */
333                 inc_fullname = pstrdup(inc_filename);
334         }
335         else
336         {
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);
344         }
345
346         inc_file = AllocateFile(inc_fullname, "r");
347         if (inc_file == NULL)
348         {
349                 ereport(LOG,
350                                 (errcode_for_file_access(),
351                                  errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
352                                                 inc_filename, inc_fullname)));
353                 pfree(inc_fullname);
354
355                 /* return single space, it matches nothing */
356                 return pstrdup(" ");
357         }
358
359         /* There is possible recursion here if the file contains @ */
360         tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
361
362         FreeFile(inc_file);
363         pfree(inc_fullname);
364
365         /* Create comma-separated string from List */
366         comma_str = pstrdup("");
367         foreach(line, inc_lines)
368         {
369                 List       *token_list = (List *) lfirst(line);
370                 ListCell   *token;
371
372                 foreach(token, token_list)
373                 {
374                         int                     oldlen = strlen(comma_str);
375                         int                     needed;
376
377                         needed = oldlen + strlen(lfirst(token)) + 1;
378                         if (oldlen > 0)
379                                 needed++;
380                         comma_str = repalloc(comma_str, needed);
381                         if (oldlen > 0)
382                                 strcat(comma_str, MULTI_VALUE_SEP);
383                         strcat(comma_str, lfirst(token));
384                 }
385         }
386
387         free_lines(&inc_lines, &inc_line_nums);
388
389         /* if file is empty, return single space rather than empty string */
390         if (strlen(comma_str) == 0)
391         {
392                 pfree(comma_str);
393                 return pstrdup(" ");
394         }
395
396         return comma_str;
397 }
398
399
400 /*
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.
404  *
405  * filename must be the absolute path to the target file.
406  */
407 static void
408 tokenize_file(const char *filename, FILE *file,
409                           List **lines, List **line_nums)
410 {
411         List       *current_line = NIL;
412         int                     line_number = 1;
413         char       *buf;
414
415         *lines = *line_nums = NIL;
416
417         while (!feof(file) && !ferror(file))
418         {
419                 buf = next_token_expand(filename, file);
420
421                 /* add token to list, unless we are at EOL or comment start */
422                 if (buf)
423                 {
424                         if (current_line == NIL)
425                         {
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);
430                         }
431                         else
432                         {
433                                 /* append token to current line's list */
434                                 current_line = lappend(current_line, buf);
435                         }
436                 }
437                 else
438                 {
439                         /* we are at real or logical EOL, so force a new line List */
440                         current_line = NIL;
441                         /* Advance line number whenever we reach EOL */
442                         line_number++;
443                 }
444         }
445 }
446
447
448 /*
449  * Does user belong to role?
450  *
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.
453  */
454 static bool
455 is_member(Oid userid, const char *role)
456 {
457         Oid                     roleid;
458
459         if (!OidIsValid(userid))
460                 return false;                   /* if user not exist, say "no" */
461
462         roleid = get_roleid(role);
463
464         if (!OidIsValid(roleid))
465                 return false;                   /* if target role not exist, say "no" */
466
467         /* See if user is directly or indirectly a member of role */
468         return is_member_of_role(userid, roleid);
469 }
470
471 /*
472  * Check comma-separated list for a match to role, allowing group names.
473  *
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.
477  */
478 static bool
479 check_role(const char *role, Oid roleid, char *param_str)
480 {
481         char       *tok;
482
483         for (tok = strtok(param_str, MULTI_VALUE_SEP);
484                  tok != NULL;
485                  tok = strtok(NULL, MULTI_VALUE_SEP))
486         {
487                 if (tok[0] == '+')
488                 {
489                         if (is_member(roleid, tok + 1))
490                                 return true;
491                 }
492                 else if (strcmp(tok, role) == 0 ||
493                                  strcmp(tok, "all\n") == 0)
494                         return true;
495         }
496
497         return false;
498 }
499
500 /*
501  * Check to see if db/role combination matches param string.
502  *
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.
506  */
507 static bool
508 check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
509 {
510         char       *tok;
511
512         for (tok = strtok(param_str, MULTI_VALUE_SEP);
513                  tok != NULL;
514                  tok = strtok(NULL, MULTI_VALUE_SEP))
515         {
516                 if (am_walsender)
517                 {
518                         /* walsender connections can only match replication keyword */
519                         if (strcmp(tok, "replication\n") == 0)
520                                 return true;
521                 }
522                 else if (strcmp(tok, "all\n") == 0)
523                         return true;
524                 else if (strcmp(tok, "sameuser\n") == 0)
525                 {
526                         if (strcmp(dbname, role) == 0)
527                                 return true;
528                 }
529                 else if (strcmp(tok, "samegroup\n") == 0 ||
530                                  strcmp(tok, "samerole\n") == 0)
531                 {
532                         if (is_member(roleid, dbname))
533                                 return true;
534                 }
535                 else if (strcmp(tok, "replication\n") == 0)
536                         continue;                       /* never match this if not walsender */
537                 else if (strcmp(tok, dbname) == 0)
538                         return true;
539         }
540         return false;
541 }
542
543 /*
544  * Check to see if a connecting IP matches the given address and netmask.
545  */
546 static bool
547 check_ip(SockAddr *raddr, struct sockaddr * addr, struct sockaddr * mask)
548 {
549         if (raddr->addr.ss_family == addr->sa_family)
550         {
551                 /* Same address family */
552                 if (!pg_range_sockaddr(&raddr->addr,
553                                                            (struct sockaddr_storage *) addr,
554                                                            (struct sockaddr_storage *) mask))
555                         return false;
556         }
557 #ifdef HAVE_IPV6
558         else if (addr->sa_family == AF_INET &&
559                          raddr->addr.ss_family == AF_INET6)
560         {
561                 /*
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.
565                  */
566                 struct sockaddr_storage addrcopy,
567                                         maskcopy;
568
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);
573
574                 if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
575                         return false;
576         }
577 #endif   /* HAVE_IPV6 */
578         else
579         {
580                 /* Wrong address family, no IPV6 */
581                 return false;
582         }
583
584         return true;
585 }
586
587 /*
588  * pg_foreach_ifaddr callback: does client addr match this machine interface?
589  */
590 static void
591 check_network_callback(struct sockaddr * addr, struct sockaddr * netmask,
592                                            void *cb_data)
593 {
594         check_network_data *cn = (check_network_data *) cb_data;
595         struct sockaddr_storage mask;
596
597         /* Already found a match? */
598         if (cn->result)
599                 return;
600
601         if (cn->method == ipCmpSameHost)
602         {
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);
606         }
607         else
608         {
609                 /* Use the netmask of the interface itself */
610                 cn->result = check_ip(cn->raddr, addr, netmask);
611         }
612 }
613
614 /*
615  * Use pg_foreach_ifaddr to check a samehost or samenet match
616  */
617 static bool
618 check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
619 {
620         check_network_data cn;
621
622         cn.method = method;
623         cn.raddr = raddr;
624         cn.result = false;
625
626         errno = 0;
627         if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
628         {
629                 elog(LOG, "error enumerating network interfaces: %m");
630                 return false;
631         }
632
633         return cn.result;
634 }
635
636
637 /*
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
640  *                                               not supported.
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.
646  */
647 #define INVALID_AUTH_OPTION(optname, validmethods) do {\
648         ereport(LOG, \
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))); \
655         return false; \
656 } while (0);
657
658 #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
659         if (parsedline->auth_method != methodval) \
660                 INVALID_AUTH_OPTION(optname, validmethods); \
661 } while (0);
662
663 #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
664         if (argvar == NULL) {\
665                 ereport(LOG, \
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))); \
671                 return false; \
672         } \
673 } while (0);
674
675
676 /*
677  * Parse one line in the hba config file and store the result in
678  * a HbaLine structure.
679  */
680 static bool
681 parse_hba_line(List *line, int line_num, HbaLine *parsedline)
682 {
683         char       *token;
684         struct addrinfo *gai_result;
685         struct addrinfo hints;
686         int                     ret;
687         char       *cidr_slash;
688         char       *unsupauth;
689         ListCell   *line_item;
690
691         line_item = list_head(line);
692
693         parsedline->linenumber = line_num;
694
695         /* Check the record type. */
696         token = lfirst(line_item);
697         if (strcmp(token, "local") == 0)
698         {
699                 parsedline->conntype = ctLocal;
700         }
701         else if (strcmp(token, "host") == 0
702                          || strcmp(token, "hostssl") == 0
703                          || strcmp(token, "hostnossl") == 0)
704         {
705
706                 if (token[4] == 's')    /* "hostssl" */
707                 {
708 #ifdef USE_SSL
709                         parsedline->conntype = ctHostSSL;
710 #else
711                         ereport(LOG,
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)));
717                         return false;
718 #endif
719                 }
720 #ifdef USE_SSL
721                 else if (token[4] == 'n')               /* "hostnossl" */
722                 {
723                         parsedline->conntype = ctHostNoSSL;
724                 }
725 #endif
726                 else
727                 {
728                         /* "host", or "hostnossl" and SSL support not built in */
729                         parsedline->conntype = ctHost;
730                 }
731         }                                                       /* record type */
732         else
733         {
734                 ereport(LOG,
735                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
736                                  errmsg("invalid connection type \"%s\"",
737                                                 token),
738                                  errcontext("line %d of configuration file \"%s\"",
739                                                         line_num, HbaFileName)));
740                 return false;
741         }
742
743         /* Get the database. */
744         line_item = lnext(line_item);
745         if (!line_item)
746         {
747                 ereport(LOG,
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)));
752                 return false;
753         }
754         parsedline->database = pstrdup(lfirst(line_item));
755
756         /* Get the role. */
757         line_item = lnext(line_item);
758         if (!line_item)
759         {
760                 ereport(LOG,
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)));
765                 return false;
766         }
767         parsedline->role = pstrdup(lfirst(line_item));
768
769         if (parsedline->conntype != ctLocal)
770         {
771                 /* Read the IP address field. (with or without CIDR netmask) */
772                 line_item = lnext(line_item);
773                 if (!line_item)
774                 {
775                         ereport(LOG,
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)));
780                         return false;
781                 }
782                 token = lfirst(line_item);
783
784                 /* Is it equal to 'samehost' or 'samenet'? */
785                 if (strcmp(token, "samehost") == 0)
786                 {
787                         /* Any IP on this host is allowed to connect */
788                         parsedline->ip_cmp_method = ipCmpSameHost;
789                 }
790                 else if (strcmp(token, "samenet") == 0)
791                 {
792                         /* Any IP on the host's subnets is allowed to connect */
793                         parsedline->ip_cmp_method = ipCmpSameNet;
794                 }
795                 else
796                 {
797                         /* IP and netmask are specified */
798                         parsedline->ip_cmp_method = ipCmpMask;
799
800                         /* need a modifiable copy of token */
801                         token = pstrdup(token);
802
803                         /* Check if it has a CIDR suffix and if so isolate it */
804                         cidr_slash = strchr(token, '/');
805                         if (cidr_slash)
806                                 *cidr_slash = '\0';
807
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;
817
818                         ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
819                         if (ret || !gai_result)
820                         {
821                                 ereport(LOG,
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)));
827                                 if (gai_result)
828                                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
829                                 pfree(token);
830                                 return false;
831                         }
832
833                         memcpy(&parsedline->addr, gai_result->ai_addr,
834                                    gai_result->ai_addrlen);
835                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
836
837                         /* Get the netmask */
838                         if (cidr_slash)
839                         {
840                                 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
841                                                                                   parsedline->addr.ss_family) < 0)
842                                 {
843                                         *cidr_slash = '/';      /* restore token for message */
844                                         ereport(LOG,
845                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
846                                                          errmsg("invalid CIDR mask in address \"%s\"",
847                                                                         token),
848                                                    errcontext("line %d of configuration file \"%s\"",
849                                                                           line_num, HbaFileName)));
850                                         pfree(token);
851                                         return false;
852                                 }
853                                 pfree(token);
854                         }
855                         else
856                         {
857                                 /* Read the mask field. */
858                                 pfree(token);
859                                 line_item = lnext(line_item);
860                                 if (!line_item)
861                                 {
862                                         ereport(LOG,
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)));
867                                         return false;
868                                 }
869                                 token = lfirst(line_item);
870
871                                 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
872                                 if (ret || !gai_result)
873                                 {
874                                         ereport(LOG,
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)));
880                                         if (gai_result)
881                                                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
882                                         return false;
883                                 }
884
885                                 memcpy(&parsedline->mask, gai_result->ai_addr,
886                                            gai_result->ai_addrlen);
887                                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
888
889                                 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
890                                 {
891                                         ereport(LOG,
892                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
893                                                          errmsg("IP address and mask do not match in file \"%s\" line %d",
894                                                                         HbaFileName, line_num)));
895                                         return false;
896                                 }
897                         }
898                 }
899         }                                                       /* != ctLocal */
900
901         /* Get the authentication method */
902         line_item = lnext(line_item);
903         if (!line_item)
904         {
905                 ereport(LOG,
906                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
907                                  errmsg("end-of-line before authentication method"),
908                                  errcontext("line %d of configuration file \"%s\"",
909                                                         line_num, HbaFileName)));
910                 return false;
911         }
912         token = lfirst(line_item);
913
914         unsupauth = NULL;
915         if (strcmp(token, "trust") == 0)
916                 parsedline->auth_method = uaTrust;
917         else if (strcmp(token, "ident") == 0)
918                 parsedline->auth_method = uaIdent;
919         else if (strcmp(token, "password") == 0)
920                 parsedline->auth_method = uaPassword;
921         else if (strcmp(token, "krb5") == 0)
922 #ifdef KRB5
923                 parsedline->auth_method = uaKrb5;
924 #else
925                 unsupauth = "krb5";
926 #endif
927         else if (strcmp(token, "gss") == 0)
928 #ifdef ENABLE_GSS
929                 parsedline->auth_method = uaGSS;
930 #else
931                 unsupauth = "gss";
932 #endif
933         else if (strcmp(token, "sspi") == 0)
934 #ifdef ENABLE_SSPI
935                 parsedline->auth_method = uaSSPI;
936 #else
937                 unsupauth = "sspi";
938 #endif
939         else if (strcmp(token, "reject") == 0)
940                 parsedline->auth_method = uaReject;
941         else if (strcmp(token, "md5") == 0)
942         {
943                 if (Db_user_namespace)
944                 {
945                         ereport(LOG,
946                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
947                                          errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
948                         return false;
949                 }
950                 parsedline->auth_method = uaMD5;
951         }
952         else if (strcmp(token, "pam") == 0)
953 #ifdef USE_PAM
954                 parsedline->auth_method = uaPAM;
955 #else
956                 unsupauth = "pam";
957 #endif
958         else if (strcmp(token, "ldap") == 0)
959 #ifdef USE_LDAP
960                 parsedline->auth_method = uaLDAP;
961 #else
962                 unsupauth = "ldap";
963 #endif
964         else if (strcmp(token, "cert") == 0)
965 #ifdef USE_SSL
966                 parsedline->auth_method = uaCert;
967 #else
968                 unsupauth = "cert";
969 #endif
970         else if (strcmp(token, "radius") == 0)
971                 parsedline->auth_method = uaRADIUS;
972         else
973         {
974                 ereport(LOG,
975                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
976                                  errmsg("invalid authentication method \"%s\"",
977                                                 token),
978                                  errcontext("line %d of configuration file \"%s\"",
979                                                         line_num, HbaFileName)));
980                 return false;
981         }
982
983         if (unsupauth)
984         {
985                 ereport(LOG,
986                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
987                                  errmsg("invalid authentication method \"%s\": not supported on this platform",
988                                                 token),
989                                  errcontext("line %d of configuration file \"%s\"",
990                                                         line_num, HbaFileName)));
991                 return false;
992         }
993
994         /* Invalid authentication combinations */
995         if (parsedline->conntype == ctLocal &&
996                 parsedline->auth_method == uaKrb5)
997         {
998                 ereport(LOG,
999                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1000                          errmsg("krb5 authentication is not supported on local sockets"),
1001                                  errcontext("line %d of configuration file \"%s\"",
1002                                                         line_num, HbaFileName)));
1003                 return false;
1004         }
1005
1006         if (parsedline->conntype == ctLocal &&
1007                 parsedline->auth_method == uaGSS)
1008         {
1009                 ereport(LOG,
1010                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1011                          errmsg("gssapi authentication is not supported on local sockets"),
1012                                  errcontext("line %d of configuration file \"%s\"",
1013                                                         line_num, HbaFileName)));
1014                 return false;
1015         }
1016         /*
1017          * SSPI authentication can never be enabled on ctLocal connections, because
1018          * it's only supported on Windows, where ctLocal isn't supported.
1019          */
1020
1021
1022         if (parsedline->conntype != ctHostSSL &&
1023                 parsedline->auth_method == uaCert)
1024         {
1025                 ereport(LOG,
1026                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1027                                  errmsg("cert authentication is only supported on hostssl connections"),
1028                                  errcontext("line %d of configuration file \"%s\"",
1029                                                         line_num, HbaFileName)));
1030                 return false;
1031         }
1032
1033         /* Parse remaining arguments */
1034         while ((line_item = lnext(line_item)) != NULL)
1035         {
1036                 char       *c;
1037
1038                 token = lfirst(line_item);
1039
1040                 c = strchr(token, '=');
1041                 if (c == NULL)
1042                 {
1043                         /*
1044                          * Got something that's not a name=value pair.
1045                          */
1046                         ereport(LOG,
1047                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1048                                          errmsg("authentication option not in name=value format: %s", token),
1049                                          errcontext("line %d of configuration file \"%s\"",
1050                                                                 line_num, HbaFileName)));
1051                         return false;
1052                 }
1053                 else
1054                 {
1055                         *c++ = '\0';            /* token now holds "name", c holds "value" */
1056                         if (strcmp(token, "map") == 0)
1057                         {
1058                                 if (parsedline->auth_method != uaIdent &&
1059                                         parsedline->auth_method != uaKrb5 &&
1060                                         parsedline->auth_method != uaGSS &&
1061                                         parsedline->auth_method != uaSSPI &&
1062                                         parsedline->auth_method != uaCert)
1063                                         INVALID_AUTH_OPTION("map", gettext_noop("ident, krb5, gssapi, sspi and cert"));
1064                                 parsedline->usermap = pstrdup(c);
1065                         }
1066                         else if (strcmp(token, "clientcert") == 0)
1067                         {
1068                                 /*
1069                                  * Since we require ctHostSSL, this really can never happen on
1070                                  * non-SSL-enabled builds, so don't bother checking for
1071                                  * USE_SSL.
1072                                  */
1073                                 if (parsedline->conntype != ctHostSSL)
1074                                 {
1075                                         ereport(LOG,
1076                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1077                                                          errmsg("clientcert can only be configured for \"hostssl\" rows"),
1078                                                    errcontext("line %d of configuration file \"%s\"",
1079                                                                           line_num, HbaFileName)));
1080                                         return false;
1081                                 }
1082                                 if (strcmp(c, "1") == 0)
1083                                 {
1084                                         if (!secure_loaded_verify_locations())
1085                                         {
1086                                                 ereport(LOG,
1087                                                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1088                                                                  errmsg("client certificates can only be checked if a root certificate store is available"),
1089                                                                  errdetail("make sure the root certificate store is present and readable"),
1090                                                    errcontext("line %d of configuration file \"%s\"",
1091                                                                           line_num, HbaFileName)));
1092                                                 return false;
1093                                         }
1094                                         parsedline->clientcert = true;
1095                                 }
1096                                 else
1097                                 {
1098                                         if (parsedline->auth_method == uaCert)
1099                                         {
1100                                                 ereport(LOG,
1101                                                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1102                                                                  errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
1103                                                    errcontext("line %d of configuration file \"%s\"",
1104                                                                           line_num, HbaFileName)));
1105                                                 return false;
1106                                         }
1107                                         parsedline->clientcert = false;
1108                                 }
1109                         }
1110                         else if (strcmp(token, "pamservice") == 0)
1111                         {
1112                                 REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
1113                                 parsedline->pamservice = pstrdup(c);
1114                         }
1115                         else if (strcmp(token, "ldaptls") == 0)
1116                         {
1117                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
1118                                 if (strcmp(c, "1") == 0)
1119                                         parsedline->ldaptls = true;
1120                                 else
1121                                         parsedline->ldaptls = false;
1122                         }
1123                         else if (strcmp(token, "ldapserver") == 0)
1124                         {
1125                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
1126                                 parsedline->ldapserver = pstrdup(c);
1127                         }
1128                         else if (strcmp(token, "ldapport") == 0)
1129                         {
1130                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
1131                                 parsedline->ldapport = atoi(c);
1132                                 if (parsedline->ldapport == 0)
1133                                 {
1134                                         ereport(LOG,
1135                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1136                                                          errmsg("invalid LDAP port number: \"%s\"", c),
1137                                                    errcontext("line %d of configuration file \"%s\"",
1138                                                                           line_num, HbaFileName)));
1139                                         return false;
1140                                 }
1141                         }
1142                         else if (strcmp(token, "ldapbinddn") == 0)
1143                         {
1144                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
1145                                 parsedline->ldapbinddn = pstrdup(c);
1146                         }
1147                         else if (strcmp(token, "ldapbindpasswd") == 0)
1148                         {
1149                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
1150                                 parsedline->ldapbindpasswd = pstrdup(c);
1151                         }
1152                         else if (strcmp(token, "ldapsearchattribute") == 0)
1153                         {
1154                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
1155                                 parsedline->ldapsearchattribute = pstrdup(c);
1156                         }
1157                         else if (strcmp(token, "ldapbasedn") == 0)
1158                         {
1159                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
1160                                 parsedline->ldapbasedn = pstrdup(c);
1161                         }
1162                         else if (strcmp(token, "ldapprefix") == 0)
1163                         {
1164                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
1165                                 parsedline->ldapprefix = pstrdup(c);
1166                         }
1167                         else if (strcmp(token, "ldapsuffix") == 0)
1168                         {
1169                                 REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
1170                                 parsedline->ldapsuffix = pstrdup(c);
1171                         }
1172                         else if (strcmp(token, "krb_server_hostname") == 0)
1173                         {
1174                                 REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
1175                                 parsedline->krb_server_hostname = pstrdup(c);
1176                         }
1177                         else if (strcmp(token, "krb_realm") == 0)
1178                         {
1179                                 if (parsedline->auth_method != uaKrb5 &&
1180                                         parsedline->auth_method != uaGSS &&
1181                                         parsedline->auth_method != uaSSPI)
1182                                         INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
1183                                 parsedline->krb_realm = pstrdup(c);
1184                         }
1185                         else if (strcmp(token, "include_realm") == 0)
1186                         {
1187                                 if (parsedline->auth_method != uaKrb5 &&
1188                                         parsedline->auth_method != uaGSS &&
1189                                         parsedline->auth_method != uaSSPI)
1190                                         INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
1191                                 if (strcmp(c, "1") == 0)
1192                                         parsedline->include_realm = true;
1193                                 else
1194                                         parsedline->include_realm = false;
1195                         }
1196                         else if (strcmp(token, "radiusserver") == 0)
1197                         {
1198                                 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
1199
1200                                 MemSet(&hints, 0, sizeof(hints));
1201                                 hints.ai_socktype = SOCK_DGRAM;
1202                                 hints.ai_family = AF_UNSPEC;
1203
1204                                 ret = pg_getaddrinfo_all(c, NULL, &hints, &gai_result);
1205                                 if (ret || !gai_result)
1206                                 {
1207                                         ereport(LOG,
1208                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1209                                                          errmsg("could not translate RADIUS server name \"%s\" to address: %s",
1210                                                                         c, gai_strerror(ret)),
1211                                                    errcontext("line %d of configuration file \"%s\"",
1212                                                                           line_num, HbaFileName)));
1213                                         if (gai_result)
1214                                                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1215                                         return false;
1216                                 }
1217                                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
1218                                 parsedline->radiusserver = pstrdup(c);
1219                         }
1220                         else if (strcmp(token, "radiusport") == 0)
1221                         {
1222                                 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
1223                                 parsedline->radiusport = atoi(c);
1224                                 if (parsedline->radiusport == 0)
1225                                 {
1226                                         ereport(LOG,
1227                                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1228                                                          errmsg("invalid RADIUS port number: \"%s\"", c),
1229                                                    errcontext("line %d of configuration file \"%s\"",
1230                                                                           line_num, HbaFileName)));
1231                                         return false;
1232                                 }
1233                         }
1234                         else if (strcmp(token, "radiussecret") == 0)
1235                         {
1236                                 REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
1237                                 parsedline->radiussecret = pstrdup(c);
1238                         }
1239                         else if (strcmp(token, "radiusidentifier") == 0)
1240                         {
1241                                 REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
1242                                 parsedline->radiusidentifier = pstrdup(c);
1243                         }
1244                         else
1245                         {
1246                                 ereport(LOG,
1247                                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1248                                  errmsg("unknown authentication option name: \"%s\"", token),
1249                                                  errcontext("line %d of configuration file \"%s\"",
1250                                                                         line_num, HbaFileName)));
1251                                 return false;
1252                         }
1253                 }
1254         }
1255
1256         /*
1257          * Check if the selected authentication method has any mandatory arguments
1258          * that are not set.
1259          */
1260         if (parsedline->auth_method == uaLDAP)
1261         {
1262                 MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1263
1264                 /*
1265                  * LDAP can operate in two modes: either with a direct bind, using
1266                  * ldapprefix and ldapsuffix, or using a search+bind, using
1267                  * ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
1268                  * Disallow mixing these parameters.
1269                  */
1270                 if (parsedline->ldapprefix || parsedline->ldapsuffix)
1271                 {
1272                         if (parsedline->ldapbasedn ||
1273                                 parsedline->ldapbinddn ||
1274                                 parsedline->ldapbindpasswd ||
1275                                 parsedline->ldapsearchattribute)
1276                         {
1277                                 ereport(LOG,
1278                                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1279                                                  errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
1280                                                  errcontext("line %d of configuration file \"%s\"",
1281                                                                         line_num, HbaFileName)));
1282                                 return false;
1283                         }
1284                 }
1285                 else if (!parsedline->ldapbasedn)
1286                 {
1287                         ereport(LOG,
1288                                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1289                                          errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1290                                          errcontext("line %d of configuration file \"%s\"",
1291                                                                 line_num, HbaFileName)));
1292                         return false;
1293                 }
1294         }
1295
1296         if (parsedline->auth_method == uaRADIUS)
1297         {
1298                 MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
1299                 MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
1300         }
1301
1302         /*
1303          * Enforce any parameters implied by other settings.
1304          */
1305         if (parsedline->auth_method == uaCert)
1306         {
1307                 parsedline->clientcert = true;
1308         }
1309
1310         return true;
1311 }
1312
1313
1314 /*
1315  *      Scan the (pre-parsed) hba file line by line, looking for a match
1316  *      to the port's connection request.
1317  */
1318 static bool
1319 check_hba(hbaPort *port)
1320 {
1321         Oid                     roleid;
1322         ListCell   *line;
1323         HbaLine    *hba;
1324
1325         /* Get the target role's OID.  Note we do not error out for bad role. */
1326         roleid = get_roleid(port->user_name);
1327
1328         foreach(line, parsed_hba_lines)
1329         {
1330                 hba = (HbaLine *) lfirst(line);
1331
1332                 /* Check connection type */
1333                 if (hba->conntype == ctLocal)
1334                 {
1335                         if (!IS_AF_UNIX(port->raddr.addr.ss_family))
1336                                 continue;
1337                 }
1338                 else
1339                 {
1340                         if (IS_AF_UNIX(port->raddr.addr.ss_family))
1341                                 continue;
1342
1343                         /* Check SSL state */
1344 #ifdef USE_SSL
1345                         if (port->ssl)
1346                         {
1347                                 /* Connection is SSL, match both "host" and "hostssl" */
1348                                 if (hba->conntype == ctHostNoSSL)
1349                                         continue;
1350                         }
1351                         else
1352                         {
1353                                 /* Connection is not SSL, match both "host" and "hostnossl" */
1354                                 if (hba->conntype == ctHostSSL)
1355                                         continue;
1356                         }
1357 #else
1358                         /* No SSL support, so reject "hostssl" lines */
1359                         if (hba->conntype == ctHostSSL)
1360                                 continue;
1361 #endif
1362
1363                         /* Check IP address */
1364                         switch (hba->ip_cmp_method)
1365                         {
1366                                 case ipCmpMask:
1367                                         if (!check_ip(&port->raddr,
1368                                                                   (struct sockaddr *) & hba->addr,
1369                                                                   (struct sockaddr *) & hba->mask))
1370                                                 continue;
1371                                         break;
1372                                 case ipCmpSameHost:
1373                                 case ipCmpSameNet:
1374                                         if (!check_same_host_or_net(&port->raddr,
1375                                                                                                 hba->ip_cmp_method))
1376                                                 continue;
1377                                         break;
1378                                 default:
1379                                         /* shouldn't get here, but deem it no-match if so */
1380                                         continue;
1381                         }
1382                 }                                               /* != ctLocal */
1383
1384                 /* Check database and role */
1385                 if (!check_db(port->database_name, port->user_name, roleid,
1386                                           hba->database))
1387                         continue;
1388
1389                 if (!check_role(port->user_name, roleid, hba->role))
1390                         continue;
1391
1392                 /* Found a record that matched! */
1393                 port->hba = hba;
1394                 return true;
1395         }
1396
1397         /* If no matching entry was found, then implicitly reject. */
1398         hba = palloc0(sizeof(HbaLine));
1399         hba->auth_method = uaImplicitReject;
1400         port->hba = hba;
1401         return true;
1402
1403         /*
1404          * XXX: Return false only happens if we have a parsing error, which we can
1405          * no longer have (parsing now in postmaster). Consider changing API.
1406          */
1407 }
1408
1409 /*
1410  * Free an HbaLine structure
1411  */
1412 static void
1413 free_hba_record(HbaLine *record)
1414 {
1415         if (record->database)
1416                 pfree(record->database);
1417         if (record->role)
1418                 pfree(record->role);
1419         if (record->usermap)
1420                 pfree(record->usermap);
1421         if (record->pamservice)
1422                 pfree(record->pamservice);
1423         if (record->ldapserver)
1424                 pfree(record->ldapserver);
1425         if (record->ldapprefix)
1426                 pfree(record->ldapprefix);
1427         if (record->ldapsuffix)
1428                 pfree(record->ldapsuffix);
1429         if (record->krb_server_hostname)
1430                 pfree(record->krb_server_hostname);
1431         if (record->krb_realm)
1432                 pfree(record->krb_realm);
1433         pfree(record);
1434 }
1435
1436 /*
1437  * Free all records on the parsed HBA list
1438  */
1439 static void
1440 clean_hba_list(List *lines)
1441 {
1442         ListCell   *line;
1443
1444         foreach(line, lines)
1445         {
1446                 HbaLine    *parsed = (HbaLine *) lfirst(line);
1447
1448                 if (parsed)
1449                         free_hba_record(parsed);
1450         }
1451         list_free(lines);
1452 }
1453
1454 /*
1455  * Read the config file and create a List of HbaLine records for the contents.
1456  *
1457  * The configuration is read into a temporary list, and if any parse error occurs
1458  * the old list is kept in place and false is returned. Only if the whole file
1459  * parses Ok is the list replaced, and the function returns true.
1460  */
1461 bool
1462 load_hba(void)
1463 {
1464         FILE       *file;
1465         List       *hba_lines = NIL;
1466         List       *hba_line_nums = NIL;
1467         ListCell   *line,
1468                            *line_num;
1469         List       *new_parsed_lines = NIL;
1470         bool            ok = true;
1471
1472         file = AllocateFile(HbaFileName, "r");
1473         if (file == NULL)
1474         {
1475                 ereport(LOG,
1476                                 (errcode_for_file_access(),
1477                                  errmsg("could not open configuration file \"%s\": %m",
1478                                                 HbaFileName)));
1479
1480                 /*
1481                  * Caller will take care of making this a FATAL error in case this is
1482                  * the initial startup. If it happens on reload, we just keep the old
1483                  * version around.
1484                  */
1485                 return false;
1486         }
1487
1488         tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
1489         FreeFile(file);
1490
1491         /* Now parse all the lines */
1492         forboth(line, hba_lines, line_num, hba_line_nums)
1493         {
1494                 HbaLine    *newline;
1495
1496                 newline = palloc0(sizeof(HbaLine));
1497
1498                 if (!parse_hba_line(lfirst(line), lfirst_int(line_num), newline))
1499                 {
1500                         /* Parse error in the file, so indicate there's a problem */
1501                         free_hba_record(newline);
1502                         ok = false;
1503
1504                         /*
1505                          * Keep parsing the rest of the file so we can report errors on
1506                          * more than the first row. Error has already been reported in the
1507                          * parsing function, so no need to log it here.
1508                          */
1509                         continue;
1510                 }
1511
1512                 new_parsed_lines = lappend(new_parsed_lines, newline);
1513         }
1514
1515         /* Free the temporary lists */
1516         free_lines(&hba_lines, &hba_line_nums);
1517
1518         if (!ok)
1519         {
1520                 /* Parsing failed at one or more rows, so bail out */
1521                 clean_hba_list(new_parsed_lines);
1522                 return false;
1523         }
1524
1525         /* Loaded new file successfully, replace the one we use */
1526         clean_hba_list(parsed_hba_lines);
1527         parsed_hba_lines = new_parsed_lines;
1528
1529         return true;
1530 }
1531
1532 /*
1533  *      Process one line from the ident config file.
1534  *
1535  *      Take the line and compare it to the needed map, pg_role and ident_user.
1536  *      *found_p and *error_p are set according to our results.
1537  */
1538 static void
1539 parse_ident_usermap(List *line, int line_number, const char *usermap_name,
1540                                         const char *pg_role, const char *ident_user,
1541                                         bool case_insensitive, bool *found_p, bool *error_p)
1542 {
1543         ListCell   *line_item;
1544         char       *token;
1545         char       *file_map;
1546         char       *file_pgrole;
1547         char       *file_ident_user;
1548
1549         *found_p = false;
1550         *error_p = false;
1551
1552         Assert(line != NIL);
1553         line_item = list_head(line);
1554
1555         /* Get the map token (must exist) */
1556         token = lfirst(line_item);
1557         file_map = token;
1558
1559         /* Get the ident user token */
1560         line_item = lnext(line_item);
1561         if (!line_item)
1562                 goto ident_syntax;
1563         token = lfirst(line_item);
1564         file_ident_user = token;
1565
1566         /* Get the PG rolename token */
1567         line_item = lnext(line_item);
1568         if (!line_item)
1569                 goto ident_syntax;
1570         token = lfirst(line_item);
1571         file_pgrole = token;
1572
1573         if (strcmp(file_map, usermap_name) != 0)
1574                 /* Line does not match the map name we're looking for, so just abort */
1575                 return;
1576
1577         /* Match? */
1578         if (file_ident_user[0] == '/')
1579         {
1580                 /*
1581                  * When system username starts with a slash, treat it as a regular
1582                  * expression. In this case, we process the system username as a
1583                  * regular expression that returns exactly one match. This is replaced
1584                  * for \1 in the database username string, if present.
1585                  */
1586                 int                     r;
1587                 regex_t         re;
1588                 regmatch_t      matches[2];
1589                 pg_wchar   *wstr;
1590                 int                     wlen;
1591                 char       *ofs;
1592                 char       *regexp_pgrole;
1593
1594                 wstr = palloc((strlen(file_ident_user + 1) + 1) * sizeof(pg_wchar));
1595                 wlen = pg_mb2wchar_with_len(file_ident_user + 1, wstr, strlen(file_ident_user + 1));
1596
1597                 /*
1598                  * XXX: Major room for optimization: regexps could be compiled when
1599                  * the file is loaded and then re-used in every connection.
1600                  */
1601                 r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED);
1602                 if (r)
1603                 {
1604                         char            errstr[100];
1605
1606                         pg_regerror(r, &re, errstr, sizeof(errstr));
1607                         ereport(LOG,
1608                                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1609                                          errmsg("invalid regular expression \"%s\": %s", file_ident_user + 1, errstr)));
1610
1611                         pfree(wstr);
1612                         *error_p = true;
1613                         return;
1614                 }
1615                 pfree(wstr);
1616
1617                 wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
1618                 wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
1619
1620                 r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches, 0);
1621                 if (r)
1622                 {
1623                         char            errstr[100];
1624
1625                         if (r != REG_NOMATCH)
1626                         {
1627                                 /* REG_NOMATCH is not an error, everything else is */
1628                                 pg_regerror(r, &re, errstr, sizeof(errstr));
1629                                 ereport(LOG,
1630                                                 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1631                                                  errmsg("regular expression match for \"%s\" failed: %s", file_ident_user + 1, errstr)));
1632                                 *error_p = true;
1633                         }
1634
1635                         pfree(wstr);
1636                         pg_regfree(&re);
1637                         return;
1638                 }
1639                 pfree(wstr);
1640
1641                 if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
1642                 {
1643                         /* substitution of the first argument requested */
1644                         if (matches[1].rm_so < 0)
1645                         {
1646                                 ereport(LOG,
1647                                                 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1648                                                  errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
1649                                                                 file_ident_user + 1, file_pgrole)));
1650                                 pg_regfree(&re);
1651                                 *error_p = true;
1652                                 return;
1653                         }
1654
1655                         /*
1656                          * length: original length minus length of \1 plus length of match
1657                          * plus null terminator
1658                          */
1659                         regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
1660                         strncpy(regexp_pgrole, file_pgrole, (ofs - file_pgrole));
1661                         memcpy(regexp_pgrole + strlen(regexp_pgrole),
1662                                    ident_user + matches[1].rm_so,
1663                                    matches[1].rm_eo - matches[1].rm_so);
1664                         strcat(regexp_pgrole, ofs + 2);
1665                 }
1666                 else
1667                 {
1668                         /* no substitution, so copy the match */
1669                         regexp_pgrole = pstrdup(file_pgrole);
1670                 }
1671
1672                 pg_regfree(&re);
1673
1674                 /*
1675                  * now check if the username actually matched what the user is trying
1676                  * to connect as
1677                  */
1678                 if (case_insensitive)
1679                 {
1680                         if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
1681                                 *found_p = true;
1682                 }
1683                 else
1684                 {
1685                         if (strcmp(regexp_pgrole, pg_role) == 0)
1686                                 *found_p = true;
1687                 }
1688                 pfree(regexp_pgrole);
1689
1690                 return;
1691         }
1692         else
1693         {
1694                 /* Not regular expression, so make complete match */
1695                 if (case_insensitive)
1696                 {
1697                         if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
1698                                 pg_strcasecmp(file_ident_user, ident_user) == 0)
1699                                 *found_p = true;
1700                 }
1701                 else
1702                 {
1703                         if (strcmp(file_pgrole, pg_role) == 0 &&
1704                                 strcmp(file_ident_user, ident_user) == 0)
1705                                 *found_p = true;
1706                 }
1707         }
1708
1709         return;
1710
1711 ident_syntax:
1712         ereport(LOG,
1713                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
1714                          errmsg("missing entry in file \"%s\" at end of line %d",
1715                                         IdentFileName, line_number)));
1716         *error_p = true;
1717 }
1718
1719
1720 /*
1721  *      Scan the (pre-parsed) ident usermap file line by line, looking for a match
1722  *
1723  *      See if the user with ident username "auth_user" is allowed to act
1724  *      as Postgres user "pg_role" according to usermap "usermap_name".
1725  *
1726  *      Special case: Usermap NULL, equivalent to what was previously called
1727  *      "sameuser" or "samerole", means don't look in the usermap file.
1728  *      That's an implied map wherein "pg_role" must be identical to
1729  *      "auth_user" in order to be authorized.
1730  *
1731  *      Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
1732  */
1733 int
1734 check_usermap(const char *usermap_name,
1735                           const char *pg_role,
1736                           const char *auth_user,
1737                           bool case_insensitive)
1738 {
1739         bool            found_entry = false,
1740                                 error = false;
1741
1742         if (usermap_name == NULL || usermap_name[0] == '\0')
1743         {
1744                 if (case_insensitive)
1745                 {
1746                         if (pg_strcasecmp(pg_role, auth_user) == 0)
1747                                 return STATUS_OK;
1748                 }
1749                 else
1750                 {
1751                         if (strcmp(pg_role, auth_user) == 0)
1752                                 return STATUS_OK;
1753                 }
1754                 ereport(LOG,
1755                                 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
1756                                                 pg_role, auth_user)));
1757                 return STATUS_ERROR;
1758         }
1759         else
1760         {
1761                 ListCell   *line_cell,
1762                                    *num_cell;
1763
1764                 forboth(line_cell, ident_lines, num_cell, ident_line_nums)
1765                 {
1766                         parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
1767                                                   usermap_name, pg_role, auth_user, case_insensitive,
1768                                                                 &found_entry, &error);
1769                         if (found_entry || error)
1770                                 break;
1771                 }
1772         }
1773         if (!found_entry && !error)
1774         {
1775                 ereport(LOG,
1776                 (errmsg("no match in usermap for user \"%s\" authenticated as \"%s\"",
1777                                 pg_role, auth_user),
1778                  errcontext("usermap \"%s\"", usermap_name)));
1779         }
1780         return found_entry ? STATUS_OK : STATUS_ERROR;
1781 }
1782
1783
1784 /*
1785  * Read the ident config file and create a List of Lists of tokens in the file.
1786  */
1787 void
1788 load_ident(void)
1789 {
1790         FILE       *file;
1791
1792         if (ident_lines || ident_line_nums)
1793                 free_lines(&ident_lines, &ident_line_nums);
1794
1795         file = AllocateFile(IdentFileName, "r");
1796         if (file == NULL)
1797         {
1798                 /* not fatal ... we just won't do any special ident maps */
1799                 ereport(LOG,
1800                                 (errcode_for_file_access(),
1801                                  errmsg("could not open Ident usermap file \"%s\": %m",
1802                                                 IdentFileName)));
1803         }
1804         else
1805         {
1806                 tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
1807                 FreeFile(file);
1808         }
1809 }
1810
1811
1812
1813 /*
1814  *      Determine what authentication method should be used when accessing database
1815  *      "database" from frontend "raddr", user "user".  Return the method and
1816  *      an optional argument (stored in fields of *port), and STATUS_OK.
1817  *
1818  *      Note that STATUS_ERROR indicates a problem with the hba config file.
1819  *      If the file is OK but does not contain any entry matching the request,
1820  *      we return STATUS_OK and method = uaImplicitReject.
1821  */
1822 int
1823 hba_getauthmethod(hbaPort *port)
1824 {
1825         if (check_hba(port))
1826                 return STATUS_OK;
1827         else
1828                 return STATUS_ERROR;
1829 }