LCOV - code coverage report
Current view: top level - src/backend/libpq - hba.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 632 1047 60.4 %
Date: 2023-05-30 23:12:14 Functions: 28 34 82.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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-2023, PostgreSQL Global Development Group
       9             :  * Portions Copyright (c) 1994, Regents of the University of California
      10             :  *
      11             :  *
      12             :  * IDENTIFICATION
      13             :  *    src/backend/libpq/hba.c
      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 <netdb.h>
      25             : #include <netinet/in.h>
      26             : #include <arpa/inet.h>
      27             : #include <unistd.h>
      28             : 
      29             : #include "access/htup_details.h"
      30             : #include "catalog/pg_collation.h"
      31             : #include "catalog/pg_type.h"
      32             : #include "common/ip.h"
      33             : #include "common/string.h"
      34             : #include "funcapi.h"
      35             : #include "libpq/ifaddr.h"
      36             : #include "libpq/libpq.h"
      37             : #include "miscadmin.h"
      38             : #include "postmaster/postmaster.h"
      39             : #include "regex/regex.h"
      40             : #include "replication/walsender.h"
      41             : #include "storage/fd.h"
      42             : #include "utils/acl.h"
      43             : #include "utils/builtins.h"
      44             : #include "utils/conffiles.h"
      45             : #include "utils/guc.h"
      46             : #include "utils/lsyscache.h"
      47             : #include "utils/memutils.h"
      48             : #include "utils/varlena.h"
      49             : 
      50             : #ifdef USE_LDAP
      51             : #ifdef WIN32
      52             : #include <winldap.h>
      53             : #else
      54             : #include <ldap.h>
      55             : #endif
      56             : #endif
      57             : 
      58             : 
      59             : #define MAX_TOKEN   256
      60             : 
      61             : /* callback data for check_network_callback */
      62             : typedef struct check_network_data
      63             : {
      64             :     IPCompareMethod method;     /* test method */
      65             :     SockAddr   *raddr;          /* client's actual address */
      66             :     bool        result;         /* set to true if match */
      67             : } check_network_data;
      68             : 
      69             : typedef struct
      70             : {
      71             :     const char *filename;
      72             :     int         linenum;
      73             : } tokenize_error_callback_arg;
      74             : 
      75             : #define token_has_regexp(t) (t->regex != NULL)
      76             : #define token_is_member_check(t)    (!t->quoted && t->string[0] == '+')
      77             : #define token_is_keyword(t, k)  (!t->quoted && strcmp(t->string, k) == 0)
      78             : #define token_matches(t, k)  (strcmp(t->string, k) == 0)
      79             : #define token_matches_insensitive(t,k) (pg_strcasecmp(t->string, k) == 0)
      80             : 
      81             : /*
      82             :  * Memory context holding the list of TokenizedAuthLines when parsing
      83             :  * HBA or ident configuration files.  This is created when opening the first
      84             :  * file (depth of CONF_FILE_START_DEPTH).
      85             :  */
      86             : static MemoryContext tokenize_context = NULL;
      87             : 
      88             : /*
      89             :  * pre-parsed content of HBA config file: list of HbaLine structs.
      90             :  * parsed_hba_context is the memory context where it lives.
      91             :  */
      92             : static List *parsed_hba_lines = NIL;
      93             : static MemoryContext parsed_hba_context = NULL;
      94             : 
      95             : /*
      96             :  * pre-parsed content of ident mapping file: list of IdentLine structs.
      97             :  * parsed_ident_context is the memory context where it lives.
      98             :  */
      99             : static List *parsed_ident_lines = NIL;
     100             : static MemoryContext parsed_ident_context = NULL;
     101             : 
     102             : /*
     103             :  * The following character array represents the names of the authentication
     104             :  * methods that are supported by PostgreSQL.
     105             :  *
     106             :  * Note: keep this in sync with the UserAuth enum in hba.h.
     107             :  */
     108             : static const char *const UserAuthName[] =
     109             : {
     110             :     "reject",
     111             :     "implicit reject",            /* Not a user-visible option */
     112             :     "trust",
     113             :     "ident",
     114             :     "password",
     115             :     "md5",
     116             :     "scram-sha-256",
     117             :     "gss",
     118             :     "sspi",
     119             :     "pam",
     120             :     "bsd",
     121             :     "ldap",
     122             :     "cert",
     123             :     "radius",
     124             :     "peer"
     125             : };
     126             : 
     127             : /*
     128             :  * Make sure UserAuthName[] tracks additions to the UserAuth enum
     129             :  */
     130             : StaticAssertDecl(lengthof(UserAuthName) == USER_AUTH_LAST + 1,
     131             :                  "UserAuthName[] must match the UserAuth enum");
     132             : 
     133             : 
     134             : static List *tokenize_expand_file(List *tokens, const char *outer_filename,
     135             :                                   const char *inc_filename, int elevel,
     136             :                                   int depth, char **err_msg);
     137             : static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
     138             :                                int elevel, char **err_msg);
     139             : static int  regcomp_auth_token(AuthToken *token, char *filename, int line_num,
     140             :                                char **err_msg, int elevel);
     141             : static int  regexec_auth_token(const char *match, AuthToken *token,
     142             :                                size_t nmatch, regmatch_t pmatch[]);
     143             : static void tokenize_error_callback(void *arg);
     144             : 
     145             : 
     146             : /*
     147             :  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
     148             :  * so provide our own version.
     149             :  */
     150             : bool
     151     1111354 : pg_isblank(const char c)
     152             : {
     153     1111354 :     return c == ' ' || c == '\t' || c == '\r';
     154             : }
     155             : 
     156             : 
     157             : /*
     158             :  * Grab one token out of the string pointed to by *lineptr.
     159             :  *
     160             :  * Tokens are strings of non-blank characters bounded by blank characters,
     161             :  * commas, beginning of line, and end of line.  Blank means space or tab.
     162             :  *
     163             :  * Tokens can be delimited by double quotes (this allows the inclusion of
     164             :  * blanks or '#', but not newlines).  As in SQL, write two double-quotes
     165             :  * to represent a double quote.
     166             :  *
     167             :  * Comments (started by an unquoted '#') are skipped, i.e. the remainder
     168             :  * of the line is ignored.
     169             :  *
     170             :  * (Note that line continuation processing happens before tokenization.
     171             :  * Thus, if a continuation occurs within quoted text or a comment, the
     172             :  * quoted text or comment is considered to continue to the next line.)
     173             :  *
     174             :  * The token, if any, is returned at *buf (a buffer of size bufsz), and
     175             :  * *lineptr is advanced past the token.
     176             :  *
     177             :  * Also, we set *initial_quote to indicate whether there was quoting before
     178             :  * the first character.  (We use that to prevent "@x" from being treated
     179             :  * as a file inclusion request.  Note that @"x" should be so treated;
     180             :  * we want to allow that to support embedded spaces in file paths.)
     181             :  *
     182             :  * We set *terminating_comma to indicate whether the token is terminated by a
     183             :  * comma (which is not returned).
     184             :  *
     185             :  * In event of an error, log a message at ereport level elevel, and also
     186             :  * set *err_msg to a string describing the error.  Currently the only
     187             :  * possible error is token too long for buf.
     188             :  *
     189             :  * If successful: store null-terminated token at *buf and return true.
     190             :  * If no more tokens on line: set *buf = '\0' and return false.
     191             :  * If error: fill buf with truncated or misformatted token and return false.
     192             :  */
     193             : static bool
     194      276178 : next_token(char **lineptr, char *buf, int bufsz,
     195             :            bool *initial_quote, bool *terminating_comma,
     196             :            int elevel, char **err_msg)
     197             : {
     198             :     int         c;
     199      276178 :     char       *start_buf = buf;
     200      276178 :     char       *end_buf = buf + (bufsz - 1);
     201      276178 :     bool        in_quote = false;
     202      276178 :     bool        was_quote = false;
     203      276178 :     bool        saw_quote = false;
     204             : 
     205             :     Assert(end_buf > start_buf);
     206             : 
     207      276178 :     *initial_quote = false;
     208      276178 :     *terminating_comma = false;
     209             : 
     210             :     /* Move over any whitespace and commas preceding the next token */
     211      617246 :     while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
     212             :         ;
     213             : 
     214             :     /*
     215             :      * Build a token in buf of next characters up to EOL, unquoted comma, or
     216             :      * unquoted whitespace.
     217             :      */
     218      502332 :     while (c != '\0' &&
     219      494108 :            (!pg_isblank(c) || in_quote))
     220             :     {
     221             :         /* skip comments to EOL */
     222      463816 :         if (c == '#' && !in_quote)
     223             :         {
     224     9880328 :             while ((c = (*(*lineptr)++)) != '\0')
     225             :                 ;
     226      237646 :             break;
     227             :         }
     228             : 
     229      226170 :         if (buf >= end_buf)
     230             :         {
     231           0 :             *buf = '\0';
     232           0 :             ereport(elevel,
     233             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     234             :                      errmsg("authentication file token too long, skipping: \"%s\"",
     235             :                             start_buf)));
     236           0 :             *err_msg = "authentication file token too long";
     237             :             /* Discard remainder of line */
     238           0 :             while ((c = (*(*lineptr)++)) != '\0')
     239             :                 ;
     240             :             /* Un-eat the '\0', in case we're called again */
     241           0 :             (*lineptr)--;
     242           0 :             return false;
     243             :         }
     244             : 
     245             :         /* we do not pass back a terminating comma in the token */
     246      226170 :         if (c == ',' && !in_quote)
     247             :         {
     248          16 :             *terminating_comma = true;
     249          16 :             break;
     250             :         }
     251             : 
     252      226154 :         if (c != '"' || was_quote)
     253      225840 :             *buf++ = c;
     254             : 
     255             :         /* Literal double-quote is two double-quotes */
     256      226154 :         if (in_quote && c == '"')
     257         156 :             was_quote = !was_quote;
     258             :         else
     259      225998 :             was_quote = false;
     260             : 
     261      226154 :         if (c == '"')
     262             :         {
     263         314 :             in_quote = !in_quote;
     264         314 :             saw_quote = true;
     265         314 :             if (buf == start_buf)
     266          94 :                 *initial_quote = true;
     267             :         }
     268             : 
     269      226154 :         c = *(*lineptr)++;
     270             :     }
     271             : 
     272             :     /*
     273             :      * Un-eat the char right after the token (critical in case it is '\0',
     274             :      * else next call will read past end of string).
     275             :      */
     276      276178 :     (*lineptr)--;
     277             : 
     278      276178 :     *buf = '\0';
     279             : 
     280      276178 :     return (saw_quote || buf > start_buf);
     281             : }
     282             : 
     283             : /*
     284             :  * Construct a palloc'd AuthToken struct, copying the given string.
     285             :  */
     286             : static AuthToken *
     287       54912 : make_auth_token(const char *token, bool quoted)
     288             : {
     289             :     AuthToken  *authtoken;
     290             :     int         toklen;
     291             : 
     292       54912 :     toklen = strlen(token);
     293             :     /* we copy string into same palloc block as the struct */
     294       54912 :     authtoken = (AuthToken *) palloc0(sizeof(AuthToken) + toklen + 1);
     295       54912 :     authtoken->string = (char *) authtoken + sizeof(AuthToken);
     296       54912 :     authtoken->quoted = quoted;
     297       54912 :     authtoken->regex = NULL;
     298       54912 :     memcpy(authtoken->string, token, toklen + 1);
     299             : 
     300       54912 :     return authtoken;
     301             : }
     302             : 
     303             : /*
     304             :  * Free an AuthToken, that may include a regular expression that needs
     305             :  * to be cleaned up explicitly.
     306             :  */
     307             : static void
     308           4 : free_auth_token(AuthToken *token)
     309             : {
     310           4 :     if (token_has_regexp(token))
     311           0 :         pg_regfree(token->regex);
     312           4 : }
     313             : 
     314             : /*
     315             :  * Copy a AuthToken struct into freshly palloc'd memory.
     316             :  */
     317             : static AuthToken *
     318       16380 : copy_auth_token(AuthToken *in)
     319             : {
     320       16380 :     AuthToken  *out = make_auth_token(in->string, in->quoted);
     321             : 
     322       16380 :     return out;
     323             : }
     324             : 
     325             : /*
     326             :  * Compile the regular expression and store it in the AuthToken given in
     327             :  * input.  Returns the result of pg_regcomp().  On error, the details are
     328             :  * stored in "err_msg".
     329             :  */
     330             : static int
     331       16380 : regcomp_auth_token(AuthToken *token, char *filename, int line_num,
     332             :                    char **err_msg, int elevel)
     333             : {
     334             :     pg_wchar   *wstr;
     335             :     int         wlen;
     336             :     int         rc;
     337             : 
     338             :     Assert(token->regex == NULL);
     339             : 
     340       16380 :     if (token->string[0] != '/')
     341       16292 :         return 0;               /* nothing to compile */
     342             : 
     343          88 :     token->regex = (regex_t *) palloc0(sizeof(regex_t));
     344          88 :     wstr = palloc((strlen(token->string + 1) + 1) * sizeof(pg_wchar));
     345          88 :     wlen = pg_mb2wchar_with_len(token->string + 1,
     346          88 :                                 wstr, strlen(token->string + 1));
     347             : 
     348          88 :     rc = pg_regcomp(token->regex, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
     349             : 
     350          88 :     if (rc)
     351             :     {
     352             :         char        errstr[100];
     353             : 
     354           0 :         pg_regerror(rc, token->regex, errstr, sizeof(errstr));
     355           0 :         ereport(elevel,
     356             :                 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
     357             :                  errmsg("invalid regular expression \"%s\": %s",
     358             :                         token->string + 1, errstr),
     359             :                  errcontext("line %d of configuration file \"%s\"",
     360             :                             line_num, filename)));
     361             : 
     362           0 :         *err_msg = psprintf("invalid regular expression \"%s\": %s",
     363           0 :                             token->string + 1, errstr);
     364             :     }
     365             : 
     366          88 :     pfree(wstr);
     367          88 :     return rc;
     368             : }
     369             : 
     370             : /*
     371             :  * Execute a regular expression computed in an AuthToken, checking for a match
     372             :  * with the string specified in "match".  The caller may optionally give an
     373             :  * array to store the matches.  Returns the result of pg_regexec().
     374             :  */
     375             : static int
     376          50 : regexec_auth_token(const char *match, AuthToken *token, size_t nmatch,
     377             :                    regmatch_t pmatch[])
     378             : {
     379             :     pg_wchar   *wmatchstr;
     380             :     int         wmatchlen;
     381             :     int         r;
     382             : 
     383             :     Assert(token->string[0] == '/' && token->regex);
     384             : 
     385          50 :     wmatchstr = palloc((strlen(match) + 1) * sizeof(pg_wchar));
     386          50 :     wmatchlen = pg_mb2wchar_with_len(match, wmatchstr, strlen(match));
     387             : 
     388          50 :     r = pg_regexec(token->regex, wmatchstr, wmatchlen, 0, NULL, nmatch, pmatch, 0);
     389             : 
     390          50 :     pfree(wmatchstr);
     391          50 :     return r;
     392             : }
     393             : 
     394             : /*
     395             :  * Tokenize one HBA field from a line, handling file inclusion and comma lists.
     396             :  *
     397             :  * filename: current file's pathname (needed to resolve relative pathnames)
     398             :  * *lineptr: current line pointer, which will be advanced past field
     399             :  *
     400             :  * In event of an error, log a message at ereport level elevel, and also
     401             :  * set *err_msg to a string describing the error.  Note that the result
     402             :  * may be non-NIL anyway, so *err_msg must be tested to determine whether
     403             :  * there was an error.
     404             :  *
     405             :  * The result is a List of AuthToken structs, one for each token in the field,
     406             :  * or NIL if we reached EOL.
     407             :  */
     408             : static List *
     409      276162 : next_field_expand(const char *filename, char **lineptr,
     410             :                   int elevel, int depth, char **err_msg)
     411             : {
     412             :     char        buf[MAX_TOKEN];
     413             :     bool        trailing_comma;
     414             :     bool        initial_quote;
     415      276162 :     List       *tokens = NIL;
     416             : 
     417             :     do
     418             :     {
     419      276178 :         if (!next_token(lineptr, buf, sizeof(buf),
     420             :                         &initial_quote, &trailing_comma,
     421             :                         elevel, err_msg))
     422      237646 :             break;
     423             : 
     424             :         /* Is this referencing a file? */
     425       38532 :         if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
     426           4 :             tokens = tokenize_expand_file(tokens, filename, buf + 1,
     427             :                                           elevel, depth + 1, err_msg);
     428             :         else
     429             :         {
     430             :             MemoryContext oldcxt;
     431             : 
     432             :             /*
     433             :              * lappend() may do its own allocations, so move to the context
     434             :              * for the list of tokens.
     435             :              */
     436       38528 :             oldcxt = MemoryContextSwitchTo(tokenize_context);
     437       38528 :             tokens = lappend(tokens, make_auth_token(buf, initial_quote));
     438       38528 :             MemoryContextSwitchTo(oldcxt);
     439             :         }
     440       38532 :     } while (trailing_comma && (*err_msg == NULL));
     441             : 
     442      276162 :     return tokens;
     443             : }
     444             : 
     445             : /*
     446             :  * tokenize_include_file
     447             :  *      Include a file from another file into an hba "field".
     448             :  *
     449             :  * Opens and tokenises a file included from another authentication file
     450             :  * with one of the include records ("include", "include_if_exists" or
     451             :  * "include_dir"), and assign all values found to an existing list of
     452             :  * list of AuthTokens.
     453             :  *
     454             :  * All new tokens are allocated in the memory context dedicated to the
     455             :  * tokenization, aka tokenize_context.
     456             :  *
     457             :  * If missing_ok is true, ignore a missing file.
     458             :  *
     459             :  * In event of an error, log a message at ereport level elevel, and also
     460             :  * set *err_msg to a string describing the error.  Note that the result
     461             :  * may be non-NIL anyway, so *err_msg must be tested to determine whether
     462             :  * there was an error.
     463             :  */
     464             : static void
     465          42 : tokenize_include_file(const char *outer_filename,
     466             :                       const char *inc_filename,
     467             :                       List **tok_lines,
     468             :                       int elevel,
     469             :                       int depth,
     470             :                       bool missing_ok,
     471             :                       char **err_msg)
     472             : {
     473             :     char       *inc_fullname;
     474             :     FILE       *inc_file;
     475             : 
     476          42 :     inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
     477          42 :     inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
     478             : 
     479          42 :     if (!inc_file)
     480             :     {
     481           6 :         if (errno == ENOENT && missing_ok)
     482             :         {
     483           6 :             ereport(elevel,
     484             :                     (errmsg("skipping missing authentication file \"%s\"",
     485             :                             inc_fullname)));
     486           6 :             *err_msg = NULL;
     487           6 :             pfree(inc_fullname);
     488           6 :             return;
     489             :         }
     490             : 
     491             :         /* error in err_msg, so leave and report */
     492           0 :         pfree(inc_fullname);
     493             :         Assert(err_msg);
     494           0 :         return;
     495             :     }
     496             : 
     497          36 :     tokenize_auth_file(inc_fullname, inc_file, tok_lines, elevel,
     498             :                        depth);
     499          36 :     free_auth_file(inc_file, depth);
     500          36 :     pfree(inc_fullname);
     501             : }
     502             : 
     503             : /*
     504             :  * tokenize_expand_file
     505             :  *      Expand a file included from another file into an hba "field"
     506             :  *
     507             :  * Opens and tokenises a file included from another HBA config file with @,
     508             :  * and returns all values found therein as a flat list of AuthTokens.  If a
     509             :  * @-token or include record is found, recursively expand it.  The newly
     510             :  * read tokens are appended to "tokens" (so that foo,bar,@baz does what you
     511             :  * expect).  All new tokens are allocated in the memory context dedicated
     512             :  * to the list of TokenizedAuthLines, aka tokenize_context.
     513             :  *
     514             :  * In event of an error, log a message at ereport level elevel, and also
     515             :  * set *err_msg to a string describing the error.  Note that the result
     516             :  * may be non-NIL anyway, so *err_msg must be tested to determine whether
     517             :  * there was an error.
     518             :  */
     519             : static List *
     520           4 : tokenize_expand_file(List *tokens,
     521             :                      const char *outer_filename,
     522             :                      const char *inc_filename,
     523             :                      int elevel,
     524             :                      int depth,
     525             :                      char **err_msg)
     526             : {
     527             :     char       *inc_fullname;
     528             :     FILE       *inc_file;
     529           4 :     List       *inc_lines = NIL;
     530             :     ListCell   *inc_line;
     531             : 
     532           4 :     inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
     533           4 :     inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
     534             : 
     535           4 :     if (inc_file == NULL)
     536             :     {
     537             :         /* error already logged */
     538           0 :         pfree(inc_fullname);
     539           0 :         return tokens;
     540             :     }
     541             : 
     542             :     /*
     543             :      * There is possible recursion here if the file contains @ or an include
     544             :      * record.
     545             :      */
     546           4 :     tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel,
     547             :                        depth);
     548             : 
     549           4 :     pfree(inc_fullname);
     550             : 
     551             :     /*
     552             :      * Move all the tokens found in the file to the tokens list.  These are
     553             :      * already saved in tokenize_context.
     554             :      */
     555          12 :     foreach(inc_line, inc_lines)
     556             :     {
     557           8 :         TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(inc_line);
     558             :         ListCell   *inc_field;
     559             : 
     560             :         /* If any line has an error, propagate that up to caller */
     561           8 :         if (tok_line->err_msg)
     562             :         {
     563           0 :             *err_msg = pstrdup(tok_line->err_msg);
     564           0 :             break;
     565             :         }
     566             : 
     567          16 :         foreach(inc_field, tok_line->fields)
     568             :         {
     569           8 :             List       *inc_tokens = lfirst(inc_field);
     570             :             ListCell   *inc_token;
     571             : 
     572          16 :             foreach(inc_token, inc_tokens)
     573             :             {
     574           8 :                 AuthToken  *token = lfirst(inc_token);
     575             :                 MemoryContext oldcxt;
     576             : 
     577             :                 /*
     578             :                  * lappend() may do its own allocations, so move to the
     579             :                  * context for the list of tokens.
     580             :                  */
     581           8 :                 oldcxt = MemoryContextSwitchTo(tokenize_context);
     582           8 :                 tokens = lappend(tokens, token);
     583           8 :                 MemoryContextSwitchTo(oldcxt);
     584             :             }
     585             :         }
     586             :     }
     587             : 
     588           4 :     free_auth_file(inc_file, depth);
     589           4 :     return tokens;
     590             : }
     591             : 
     592             : /*
     593             :  * free_auth_file
     594             :  *      Free a file opened by open_auth_file().
     595             :  */
     596             : void
     597        2838 : free_auth_file(FILE *file, int depth)
     598             : {
     599        2838 :     FreeFile(file);
     600             : 
     601             :     /* If this is the last cleanup, remove the tokenization context */
     602        2838 :     if (depth == CONF_FILE_START_DEPTH)
     603             :     {
     604        2798 :         MemoryContextDelete(tokenize_context);
     605        2798 :         tokenize_context = NULL;
     606             :     }
     607        2838 : }
     608             : 
     609             : /*
     610             :  * open_auth_file
     611             :  *      Open the given file.
     612             :  *
     613             :  * filename: the absolute path to the target file
     614             :  * elevel: message logging level
     615             :  * depth: recursion level when opening the file
     616             :  * err_msg: details about the error
     617             :  *
     618             :  * Return value is the opened file.  On error, returns NULL with details
     619             :  * about the error stored in "err_msg".
     620             :  */
     621             : FILE *
     622        2844 : open_auth_file(const char *filename, int elevel, int depth,
     623             :                char **err_msg)
     624             : {
     625             :     FILE       *file;
     626             : 
     627             :     /*
     628             :      * Reject too-deep include nesting depth.  This is just a safety check to
     629             :      * avoid dumping core due to stack overflow if an include file loops back
     630             :      * to itself.  The maximum nesting depth is pretty arbitrary.
     631             :      */
     632        2844 :     if (depth > CONF_FILE_MAX_DEPTH)
     633             :     {
     634           0 :         ereport(elevel,
     635             :                 (errcode_for_file_access(),
     636             :                  errmsg("could not open file \"%s\": maximum nesting depth exceeded",
     637             :                         filename)));
     638           0 :         if (err_msg)
     639           0 :             *err_msg = psprintf("could not open file \"%s\": maximum nesting depth exceeded",
     640             :                                 filename);
     641           0 :         return NULL;
     642             :     }
     643             : 
     644        2844 :     file = AllocateFile(filename, "r");
     645        2844 :     if (file == NULL)
     646             :     {
     647           6 :         int         save_errno = errno;
     648             : 
     649           6 :         ereport(elevel,
     650             :                 (errcode_for_file_access(),
     651             :                  errmsg("could not open file \"%s\": %m",
     652             :                         filename)));
     653           6 :         if (err_msg)
     654           6 :             *err_msg = psprintf("could not open file \"%s\": %s",
     655             :                                 filename, strerror(save_errno));
     656             :         /* the caller may care about some specific errno */
     657           6 :         errno = save_errno;
     658           6 :         return NULL;
     659             :     }
     660             : 
     661             :     /*
     662             :      * When opening the top-level file, create the memory context used for the
     663             :      * tokenization.  This will be closed with this file when coming back to
     664             :      * this level of cleanup.
     665             :      */
     666        2838 :     if (depth == CONF_FILE_START_DEPTH)
     667             :     {
     668             :         /*
     669             :          * A context may be present, but assume that it has been eliminated
     670             :          * already.
     671             :          */
     672        2798 :         tokenize_context = AllocSetContextCreate(CurrentMemoryContext,
     673             :                                                  "tokenize_context",
     674             :                                                  ALLOCSET_START_SMALL_SIZES);
     675             :     }
     676             : 
     677        2838 :     return file;
     678             : }
     679             : 
     680             : /*
     681             :  * error context callback for tokenize_auth_file()
     682             :  */
     683             : static void
     684           8 : tokenize_error_callback(void *arg)
     685             : {
     686           8 :     tokenize_error_callback_arg *callback_arg = (tokenize_error_callback_arg *) arg;
     687             : 
     688           8 :     errcontext("line %d of configuration file \"%s\"",
     689             :                callback_arg->linenum, callback_arg->filename);
     690           8 : }
     691             : 
     692             : /*
     693             :  * tokenize_auth_file
     694             :  *      Tokenize the given file.
     695             :  *
     696             :  * The output is a list of TokenizedAuthLine structs; see the struct definition
     697             :  * in libpq/hba.h.  This is the central piece in charge of parsing the
     698             :  * authentication files.  All the operations of this function happen in its own
     699             :  * local memory context, easing the cleanup of anything allocated here.  This
     700             :  * matters a lot when reloading authentication files in the postmaster.
     701             :  *
     702             :  * filename: the absolute path to the target file
     703             :  * file: the already-opened target file
     704             :  * tok_lines: receives output list, saved into tokenize_context
     705             :  * elevel: message logging level
     706             :  * depth: level of recursion when tokenizing the target file
     707             :  *
     708             :  * Errors are reported by logging messages at ereport level elevel and by
     709             :  * adding TokenizedAuthLine structs containing non-null err_msg fields to the
     710             :  * output list.
     711             :  */
     712             : void
     713        2838 : tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
     714             :                    int elevel, int depth)
     715             : {
     716        2838 :     int         line_number = 1;
     717             :     StringInfoData buf;
     718             :     MemoryContext linecxt;
     719             :     MemoryContext funccxt;      /* context of this function's caller */
     720             :     ErrorContextCallback tokenerrcontext;
     721             :     tokenize_error_callback_arg callback_arg;
     722             : 
     723             :     Assert(tokenize_context);
     724             : 
     725        2838 :     callback_arg.filename = filename;
     726        2838 :     callback_arg.linenum = line_number;
     727             : 
     728        2838 :     tokenerrcontext.callback = tokenize_error_callback;
     729        2838 :     tokenerrcontext.arg = (void *) &callback_arg;
     730        2838 :     tokenerrcontext.previous = error_context_stack;
     731        2838 :     error_context_stack = &tokenerrcontext;
     732             : 
     733             :     /*
     734             :      * Do all the local tokenization in its own context, to ease the cleanup
     735             :      * of any memory allocated while tokenizing.
     736             :      */
     737        2838 :     linecxt = AllocSetContextCreate(CurrentMemoryContext,
     738             :                                     "tokenize_auth_file",
     739             :                                     ALLOCSET_SMALL_SIZES);
     740        2838 :     funccxt = MemoryContextSwitchTo(linecxt);
     741             : 
     742        2838 :     initStringInfo(&buf);
     743             : 
     744        2838 :     if (depth == CONF_FILE_START_DEPTH)
     745        2798 :         *tok_lines = NIL;
     746             : 
     747      259932 :     while (!feof(file) && !ferror(file))
     748             :     {
     749             :         TokenizedAuthLine *tok_line;
     750             :         MemoryContext oldcxt;
     751             :         char       *lineptr;
     752      257094 :         List       *current_line = NIL;
     753      257094 :         char       *err_msg = NULL;
     754      257094 :         int         last_backslash_buflen = 0;
     755      257094 :         int         continuations = 0;
     756             : 
     757             :         /* Collect the next input line, handling backslash continuations */
     758      257094 :         resetStringInfo(&buf);
     759             : 
     760      257128 :         while (pg_get_line_append(file, &buf, NULL))
     761             :         {
     762             :             /* Strip trailing newline, including \r in case we're on Windows */
     763      254290 :             buf.len = pg_strip_crlf(buf.data);
     764             : 
     765             :             /*
     766             :              * Check for backslash continuation.  The backslash must be after
     767             :              * the last place we found a continuation, else two backslashes
     768             :              * followed by two \n's would behave surprisingly.
     769             :              */
     770      254290 :             if (buf.len > last_backslash_buflen &&
     771      245904 :                 buf.data[buf.len - 1] == '\\')
     772             :             {
     773             :                 /* Continuation, so strip it and keep reading */
     774          34 :                 buf.data[--buf.len] = '\0';
     775          34 :                 last_backslash_buflen = buf.len;
     776          34 :                 continuations++;
     777          34 :                 continue;
     778             :             }
     779             : 
     780             :             /* Nope, so we have the whole line */
     781      254256 :             break;
     782             :         }
     783             : 
     784      257094 :         if (ferror(file))
     785             :         {
     786             :             /* I/O error! */
     787           0 :             int         save_errno = errno;
     788             : 
     789           0 :             ereport(elevel,
     790             :                     (errcode_for_file_access(),
     791             :                      errmsg("could not read file \"%s\": %m", filename)));
     792           0 :             err_msg = psprintf("could not read file \"%s\": %s",
     793             :                                filename, strerror(save_errno));
     794           0 :             break;
     795             :         }
     796             : 
     797             :         /* Parse fields */
     798      257094 :         lineptr = buf.data;
     799      533256 :         while (*lineptr && err_msg == NULL)
     800             :         {
     801             :             List       *current_field;
     802             : 
     803      276162 :             current_field = next_field_expand(filename, &lineptr,
     804             :                                               elevel, depth, &err_msg);
     805             :             /* add field to line, unless we are at EOL or comment start */
     806      276162 :             if (current_field != NIL)
     807             :             {
     808             :                 /*
     809             :                  * lappend() may do its own allocations, so move to the
     810             :                  * context for the list of tokens.
     811             :                  */
     812       38516 :                 oldcxt = MemoryContextSwitchTo(tokenize_context);
     813       38516 :                 current_line = lappend(current_line, current_field);
     814       38516 :                 MemoryContextSwitchTo(oldcxt);
     815             :             }
     816             :         }
     817             : 
     818             :         /*
     819             :          * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
     820             :          * boring.
     821             :          */
     822      257094 :         if (current_line == NIL && err_msg == NULL)
     823      248870 :             goto next_line;
     824             : 
     825             :         /* If the line is valid, check if that's an include directive */
     826        8224 :         if (err_msg == NULL && list_length(current_line) == 2)
     827             :         {
     828             :             AuthToken  *first,
     829             :                        *second;
     830             : 
     831          36 :             first = linitial(linitial_node(List, current_line));
     832          36 :             second = linitial(lsecond_node(List, current_line));
     833             : 
     834          36 :             if (strcmp(first->string, "include") == 0)
     835             :             {
     836          18 :                 tokenize_include_file(filename, second->string, tok_lines,
     837             :                                       elevel, depth + 1, false, &err_msg);
     838             : 
     839          18 :                 if (err_msg)
     840           0 :                     goto process_line;
     841             : 
     842             :                 /*
     843             :                  * tokenize_auth_file() has taken care of creating the
     844             :                  * TokenizedAuthLines.
     845             :                  */
     846          18 :                 goto next_line;
     847             :             }
     848          18 :             else if (strcmp(first->string, "include_dir") == 0)
     849             :             {
     850             :                 char      **filenames;
     851           6 :                 char       *dir_name = second->string;
     852             :                 int         num_filenames;
     853             :                 StringInfoData err_buf;
     854             : 
     855           6 :                 filenames = GetConfFilesInDir(dir_name, filename, elevel,
     856             :                                               &num_filenames, &err_msg);
     857             : 
     858           6 :                 if (!filenames)
     859             :                 {
     860             :                     /* the error is in err_msg, so create an entry */
     861           0 :                     goto process_line;
     862             :                 }
     863             : 
     864           6 :                 initStringInfo(&err_buf);
     865          18 :                 for (int i = 0; i < num_filenames; i++)
     866             :                 {
     867          12 :                     tokenize_include_file(filename, filenames[i], tok_lines,
     868             :                                           elevel, depth + 1, false, &err_msg);
     869             :                     /* cumulate errors if any */
     870          12 :                     if (err_msg)
     871             :                     {
     872           0 :                         if (err_buf.len > 0)
     873           0 :                             appendStringInfoChar(&err_buf, '\n');
     874           0 :                         appendStringInfoString(&err_buf, err_msg);
     875             :                     }
     876             :                 }
     877             : 
     878             :                 /* clean up things */
     879          18 :                 for (int i = 0; i < num_filenames; i++)
     880          12 :                     pfree(filenames[i]);
     881           6 :                 pfree(filenames);
     882             : 
     883             :                 /*
     884             :                  * If there were no errors, the line is fully processed,
     885             :                  * bypass the general TokenizedAuthLine processing.
     886             :                  */
     887           6 :                 if (err_buf.len == 0)
     888           6 :                     goto next_line;
     889             : 
     890             :                 /* Otherwise, process the cumulated errors, if any. */
     891           0 :                 err_msg = err_buf.data;
     892           0 :                 goto process_line;
     893             :             }
     894          12 :             else if (strcmp(first->string, "include_if_exists") == 0)
     895             :             {
     896             : 
     897          12 :                 tokenize_include_file(filename, second->string, tok_lines,
     898             :                                       elevel, depth + 1, true, &err_msg);
     899          12 :                 if (err_msg)
     900           0 :                     goto process_line;
     901             : 
     902             :                 /*
     903             :                  * tokenize_auth_file() has taken care of creating the
     904             :                  * TokenizedAuthLines.
     905             :                  */
     906          12 :                 goto next_line;
     907             :             }
     908             :         }
     909             : 
     910        8188 : process_line:
     911             : 
     912             :         /*
     913             :          * General processing: report the error if any and emit line to the
     914             :          * TokenizedAuthLine.  This is saved in the memory context dedicated
     915             :          * to this list.
     916             :          */
     917        8188 :         oldcxt = MemoryContextSwitchTo(tokenize_context);
     918        8188 :         tok_line = (TokenizedAuthLine *) palloc0(sizeof(TokenizedAuthLine));
     919        8188 :         tok_line->fields = current_line;
     920        8188 :         tok_line->file_name = pstrdup(filename);
     921        8188 :         tok_line->line_num = line_number;
     922        8188 :         tok_line->raw_line = pstrdup(buf.data);
     923        8188 :         tok_line->err_msg = err_msg ? pstrdup(err_msg) : NULL;
     924        8188 :         *tok_lines = lappend(*tok_lines, tok_line);
     925        8188 :         MemoryContextSwitchTo(oldcxt);
     926             : 
     927      257094 : next_line:
     928      257094 :         line_number += continuations + 1;
     929      257094 :         callback_arg.linenum = line_number;
     930             :     }
     931             : 
     932        2838 :     MemoryContextSwitchTo(funccxt);
     933        2838 :     MemoryContextDelete(linecxt);
     934             : 
     935        2838 :     error_context_stack = tokenerrcontext.previous;
     936        2838 : }
     937             : 
     938             : 
     939             : /*
     940             :  * Does user belong to role?
     941             :  *
     942             :  * userid is the OID of the role given as the attempted login identifier.
     943             :  * We check to see if it is a member of the specified role name.
     944             :  */
     945             : static bool
     946          26 : is_member(Oid userid, const char *role)
     947             : {
     948             :     Oid         roleid;
     949             : 
     950          26 :     if (!OidIsValid(userid))
     951           0 :         return false;           /* if user not exist, say "no" */
     952             : 
     953          26 :     roleid = get_role_oid(role, true);
     954             : 
     955          26 :     if (!OidIsValid(roleid))
     956           0 :         return false;           /* if target role not exist, say "no" */
     957             : 
     958             :     /*
     959             :      * See if user is directly or indirectly a member of role. For this
     960             :      * purpose, a superuser is not considered to be automatically a member of
     961             :      * the role, so group auth only applies to explicit membership.
     962             :      */
     963          26 :     return is_member_of_role_nosuper(userid, roleid);
     964             : }
     965             : 
     966             : /*
     967             :  * Check AuthToken list for a match to role, allowing group names.
     968             :  *
     969             :  * Each AuthToken listed is checked one-by-one.  Keywords are processed
     970             :  * first (these cannot have regular expressions), followed by regular
     971             :  * expressions (if any), the case-insensitive match (if requested) and
     972             :  * the exact match.
     973             :  */
     974             : static bool
     975       18292 : check_role(const char *role, Oid roleid, List *tokens, bool case_insensitive)
     976             : {
     977             :     ListCell   *cell;
     978             :     AuthToken  *tok;
     979             : 
     980       18420 :     foreach(cell, tokens)
     981             :     {
     982       18300 :         tok = lfirst(cell);
     983       18300 :         if (token_is_member_check(tok))
     984             :         {
     985          16 :             if (is_member(roleid, tok->string + 1))
     986       18172 :                 return true;
     987             :         }
     988       18284 :         else if (token_is_keyword(tok, "all"))
     989       18120 :             return true;
     990         164 :         else if (token_has_regexp(tok))
     991             :         {
     992          18 :             if (regexec_auth_token(role, tok, 0, NULL) == REG_OKAY)
     993          10 :                 return true;
     994             :         }
     995         146 :         else if (case_insensitive)
     996             :         {
     997           0 :             if (token_matches_insensitive(tok, role))
     998           0 :                 return true;
     999             :         }
    1000         146 :         else if (token_matches(tok, role))
    1001          28 :             return true;
    1002             :     }
    1003         120 :     return false;
    1004             : }
    1005             : 
    1006             : /*
    1007             :  * Check to see if db/role combination matches AuthToken list.
    1008             :  *
    1009             :  * Each AuthToken listed is checked one-by-one.  Keywords are checked
    1010             :  * first (these cannot have regular expressions), followed by regular
    1011             :  * expressions (if any) and the exact match.
    1012             :  */
    1013             : static bool
    1014       19226 : check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
    1015             : {
    1016             :     ListCell   *cell;
    1017             :     AuthToken  *tok;
    1018             : 
    1019       20208 :     foreach(cell, tokens)
    1020             :     {
    1021       19234 :         tok = lfirst(cell);
    1022       19234 :         if (am_walsender && !am_db_walsender)
    1023             :         {
    1024             :             /*
    1025             :              * physical replication walsender connections can only match
    1026             :              * replication keyword
    1027             :              */
    1028        1364 :             if (token_is_keyword(tok, "replication"))
    1029       18252 :                 return true;
    1030             :         }
    1031       17870 :         else if (token_is_keyword(tok, "all"))
    1032       17290 :             return true;
    1033         580 :         else if (token_is_keyword(tok, "sameuser"))
    1034             :         {
    1035           0 :             if (strcmp(dbname, role) == 0)
    1036           0 :                 return true;
    1037             :         }
    1038         580 :         else if (token_is_keyword(tok, "samegroup") ||
    1039         576 :                  token_is_keyword(tok, "samerole"))
    1040             :         {
    1041          10 :             if (is_member(roleid, dbname))
    1042           8 :                 return true;
    1043             :         }
    1044         570 :         else if (token_is_keyword(tok, "replication"))
    1045           0 :             continue;           /* never match this if not walsender */
    1046         570 :         else if (token_has_regexp(tok))
    1047             :         {
    1048           8 :             if (regexec_auth_token(dbname, tok, 0, NULL) == REG_OKAY)
    1049           2 :                 return true;
    1050             :         }
    1051         562 :         else if (token_matches(tok, dbname))
    1052         270 :             return true;
    1053             :     }
    1054         974 :     return false;
    1055             : }
    1056             : 
    1057             : static bool
    1058           0 : ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
    1059             : {
    1060           0 :     return (a->sin_addr.s_addr == b->sin_addr.s_addr);
    1061             : }
    1062             : 
    1063             : static bool
    1064           0 : ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
    1065             : {
    1066             :     int         i;
    1067             : 
    1068           0 :     for (i = 0; i < 16; i++)
    1069           0 :         if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
    1070           0 :             return false;
    1071             : 
    1072           0 :     return true;
    1073             : }
    1074             : 
    1075             : /*
    1076             :  * Check whether host name matches pattern.
    1077             :  */
    1078             : static bool
    1079           0 : hostname_match(const char *pattern, const char *actual_hostname)
    1080             : {
    1081           0 :     if (pattern[0] == '.')      /* suffix match */
    1082             :     {
    1083           0 :         size_t      plen = strlen(pattern);
    1084           0 :         size_t      hlen = strlen(actual_hostname);
    1085             : 
    1086           0 :         if (hlen < plen)
    1087           0 :             return false;
    1088             : 
    1089           0 :         return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
    1090             :     }
    1091             :     else
    1092           0 :         return (pg_strcasecmp(pattern, actual_hostname) == 0);
    1093             : }
    1094             : 
    1095             : /*
    1096             :  * Check to see if a connecting IP matches a given host name.
    1097             :  */
    1098             : static bool
    1099           0 : check_hostname(hbaPort *port, const char *hostname)
    1100             : {
    1101             :     struct addrinfo *gai_result,
    1102             :                *gai;
    1103             :     int         ret;
    1104             :     bool        found;
    1105             : 
    1106             :     /* Quick out if remote host name already known bad */
    1107           0 :     if (port->remote_hostname_resolv < 0)
    1108           0 :         return false;
    1109             : 
    1110             :     /* Lookup remote host name if not already done */
    1111           0 :     if (!port->remote_hostname)
    1112             :     {
    1113             :         char        remote_hostname[NI_MAXHOST];
    1114             : 
    1115           0 :         ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
    1116             :                                  remote_hostname, sizeof(remote_hostname),
    1117             :                                  NULL, 0,
    1118             :                                  NI_NAMEREQD);
    1119           0 :         if (ret != 0)
    1120             :         {
    1121             :             /* remember failure; don't complain in the postmaster log yet */
    1122           0 :             port->remote_hostname_resolv = -2;
    1123           0 :             port->remote_hostname_errcode = ret;
    1124           0 :             return false;
    1125             :         }
    1126             : 
    1127           0 :         port->remote_hostname = pstrdup(remote_hostname);
    1128             :     }
    1129             : 
    1130             :     /* Now see if remote host name matches this pg_hba line */
    1131           0 :     if (!hostname_match(hostname, port->remote_hostname))
    1132           0 :         return false;
    1133             : 
    1134             :     /* If we already verified the forward lookup, we're done */
    1135           0 :     if (port->remote_hostname_resolv == +1)
    1136           0 :         return true;
    1137             : 
    1138             :     /* Lookup IP from host name and check against original IP */
    1139           0 :     ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
    1140           0 :     if (ret != 0)
    1141             :     {
    1142             :         /* remember failure; don't complain in the postmaster log yet */
    1143           0 :         port->remote_hostname_resolv = -2;
    1144           0 :         port->remote_hostname_errcode = ret;
    1145           0 :         return false;
    1146             :     }
    1147             : 
    1148           0 :     found = false;
    1149           0 :     for (gai = gai_result; gai; gai = gai->ai_next)
    1150             :     {
    1151           0 :         if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
    1152             :         {
    1153           0 :             if (gai->ai_addr->sa_family == AF_INET)
    1154             :             {
    1155           0 :                 if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
    1156           0 :                            (struct sockaddr_in *) &port->raddr.addr))
    1157             :                 {
    1158           0 :                     found = true;
    1159           0 :                     break;
    1160             :                 }
    1161             :             }
    1162           0 :             else if (gai->ai_addr->sa_family == AF_INET6)
    1163             :             {
    1164           0 :                 if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
    1165           0 :                            (struct sockaddr_in6 *) &port->raddr.addr))
    1166             :                 {
    1167           0 :                     found = true;
    1168           0 :                     break;
    1169             :                 }
    1170             :             }
    1171             :         }
    1172             :     }
    1173             : 
    1174           0 :     if (gai_result)
    1175           0 :         freeaddrinfo(gai_result);
    1176             : 
    1177           0 :     if (!found)
    1178           0 :         elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
    1179             :              hostname);
    1180             : 
    1181           0 :     port->remote_hostname_resolv = found ? +1 : -1;
    1182             : 
    1183           0 :     return found;
    1184             : }
    1185             : 
    1186             : /*
    1187             :  * Check to see if a connecting IP matches the given address and netmask.
    1188             :  */
    1189             : static bool
    1190        1126 : check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
    1191             : {
    1192        1968 :     if (raddr->addr.ss_family == addr->sa_family &&
    1193         842 :         pg_range_sockaddr(&raddr->addr,
    1194             :                           (struct sockaddr_storage *) addr,
    1195             :                           (struct sockaddr_storage *) mask))
    1196         842 :         return true;
    1197         284 :     return false;
    1198             : }
    1199             : 
    1200             : /*
    1201             :  * pg_foreach_ifaddr callback: does client addr match this machine interface?
    1202             :  */
    1203             : static void
    1204           0 : check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
    1205             :                        void *cb_data)
    1206             : {
    1207           0 :     check_network_data *cn = (check_network_data *) cb_data;
    1208             :     struct sockaddr_storage mask;
    1209             : 
    1210             :     /* Already found a match? */
    1211           0 :     if (cn->result)
    1212           0 :         return;
    1213             : 
    1214           0 :     if (cn->method == ipCmpSameHost)
    1215             :     {
    1216             :         /* Make an all-ones netmask of appropriate length for family */
    1217           0 :         pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
    1218           0 :         cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
    1219             :     }
    1220             :     else
    1221             :     {
    1222             :         /* Use the netmask of the interface itself */
    1223           0 :         cn->result = check_ip(cn->raddr, addr, netmask);
    1224             :     }
    1225             : }
    1226             : 
    1227             : /*
    1228             :  * Use pg_foreach_ifaddr to check a samehost or samenet match
    1229             :  */
    1230             : static bool
    1231           0 : check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
    1232             : {
    1233             :     check_network_data cn;
    1234             : 
    1235           0 :     cn.method = method;
    1236           0 :     cn.raddr = raddr;
    1237           0 :     cn.result = false;
    1238             : 
    1239           0 :     errno = 0;
    1240           0 :     if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
    1241             :     {
    1242           0 :         ereport(LOG,
    1243             :                 (errmsg("error enumerating network interfaces: %m")));
    1244           0 :         return false;
    1245             :     }
    1246             : 
    1247           0 :     return cn.result;
    1248             : }
    1249             : 
    1250             : 
    1251             : /*
    1252             :  * Macros used to check and report on invalid configuration options.
    1253             :  * On error: log a message at level elevel, set *err_msg, and exit the function.
    1254             :  * These macros are not as general-purpose as they look, because they know
    1255             :  * what the calling function's error-exit value is.
    1256             :  *
    1257             :  * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
    1258             :  *                       not supported.
    1259             :  * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
    1260             :  *                       method is actually the one specified. Used as a shortcut when
    1261             :  *                       the option is only valid for one authentication method.
    1262             :  * MANDATORY_AUTH_ARG  = check if a required option is set for an authentication method,
    1263             :  *                       reporting error if it's not.
    1264             :  */
    1265             : #define INVALID_AUTH_OPTION(optname, validmethods) \
    1266             : do { \
    1267             :     ereport(elevel, \
    1268             :             (errcode(ERRCODE_CONFIG_FILE_ERROR), \
    1269             :              /* translator: the second %s is a list of auth methods */ \
    1270             :              errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
    1271             :                     optname, _(validmethods)), \
    1272             :              errcontext("line %d of configuration file \"%s\"", \
    1273             :                     line_num, file_name))); \
    1274             :     *err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \
    1275             :                         optname, validmethods); \
    1276             :     return false; \
    1277             : } while (0)
    1278             : 
    1279             : #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \
    1280             : do { \
    1281             :     if (hbaline->auth_method != methodval) \
    1282             :         INVALID_AUTH_OPTION(optname, validmethods); \
    1283             : } while (0)
    1284             : 
    1285             : #define MANDATORY_AUTH_ARG(argvar, argname, authname) \
    1286             : do { \
    1287             :     if (argvar == NULL) { \
    1288             :         ereport(elevel, \
    1289             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
    1290             :                  errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
    1291             :                         authname, argname), \
    1292             :                  errcontext("line %d of configuration file \"%s\"", \
    1293             :                         line_num, file_name))); \
    1294             :         *err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \
    1295             :                             authname, argname); \
    1296             :         return NULL; \
    1297             :     } \
    1298             : } while (0)
    1299             : 
    1300             : /*
    1301             :  * Macros for handling pg_ident problems, similar as above.
    1302             :  *
    1303             :  * IDENT_FIELD_ABSENT:
    1304             :  * Reports when the given ident field ListCell is not populated.
    1305             :  *
    1306             :  * IDENT_MULTI_VALUE:
    1307             :  * Reports when the given ident token List has more than one element.
    1308             :  */
    1309             : #define IDENT_FIELD_ABSENT(field) \
    1310             : do { \
    1311             :     if (!field) { \
    1312             :         ereport(elevel, \
    1313             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
    1314             :                  errmsg("missing entry at end of line"), \
    1315             :                  errcontext("line %d of configuration file \"%s\"", \
    1316             :                             line_num, file_name))); \
    1317             :         *err_msg = pstrdup("missing entry at end of line"); \
    1318             :         return NULL; \
    1319             :     } \
    1320             : } while (0)
    1321             : 
    1322             : #define IDENT_MULTI_VALUE(tokens) \
    1323             : do { \
    1324             :     if (tokens->length > 1) { \
    1325             :         ereport(elevel, \
    1326             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
    1327             :                  errmsg("multiple values in ident field"), \
    1328             :                  errcontext("line %d of configuration file \"%s\"", \
    1329             :                             line_num, file_name))); \
    1330             :         *err_msg = pstrdup("multiple values in ident field"); \
    1331             :         return NULL; \
    1332             :     } \
    1333             : } while (0)
    1334             : 
    1335             : 
    1336             : /*
    1337             :  * Parse one tokenised line from the hba config file and store the result in a
    1338             :  * HbaLine structure.
    1339             :  *
    1340             :  * If parsing fails, log a message at ereport level elevel, store an error
    1341             :  * string in tok_line->err_msg, and return NULL.  (Some non-error conditions
    1342             :  * can also result in such messages.)
    1343             :  *
    1344             :  * Note: this function leaks memory when an error occurs.  Caller is expected
    1345             :  * to have set a memory context that will be reset if this function returns
    1346             :  * NULL.
    1347             :  */
    1348             : HbaLine *
    1349        8002 : parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
    1350             : {
    1351        8002 :     int         line_num = tok_line->line_num;
    1352        8002 :     char       *file_name = tok_line->file_name;
    1353        8002 :     char      **err_msg = &tok_line->err_msg;
    1354             :     char       *str;
    1355             :     struct addrinfo *gai_result;
    1356             :     struct addrinfo hints;
    1357             :     int         ret;
    1358             :     char       *cidr_slash;
    1359             :     char       *unsupauth;
    1360             :     ListCell   *field;
    1361             :     List       *tokens;
    1362             :     ListCell   *tokencell;
    1363             :     AuthToken  *token;
    1364             :     HbaLine    *parsedline;
    1365             : 
    1366        8002 :     parsedline = palloc0(sizeof(HbaLine));
    1367        8002 :     parsedline->sourcefile = pstrdup(file_name);
    1368        8002 :     parsedline->linenumber = line_num;
    1369        8002 :     parsedline->rawline = pstrdup(tok_line->raw_line);
    1370             : 
    1371             :     /* Check the record type. */
    1372             :     Assert(tok_line->fields != NIL);
    1373        8002 :     field = list_head(tok_line->fields);
    1374        8002 :     tokens = lfirst(field);
    1375        8002 :     if (tokens->length > 1)
    1376             :     {
    1377           0 :         ereport(elevel,
    1378             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1379             :                  errmsg("multiple values specified for connection type"),
    1380             :                  errhint("Specify exactly one connection type per line."),
    1381             :                  errcontext("line %d of configuration file \"%s\"",
    1382             :                             line_num, file_name)));
    1383           0 :         *err_msg = "multiple values specified for connection type";
    1384           0 :         return NULL;
    1385             :     }
    1386        8002 :     token = linitial(tokens);
    1387        8002 :     if (strcmp(token->string, "local") == 0)
    1388             :     {
    1389        2640 :         parsedline->conntype = ctLocal;
    1390             :     }
    1391        5362 :     else if (strcmp(token->string, "host") == 0 ||
    1392         378 :              strcmp(token->string, "hostssl") == 0 ||
    1393           0 :              strcmp(token->string, "hostnossl") == 0 ||
    1394           0 :              strcmp(token->string, "hostgssenc") == 0 ||
    1395           0 :              strcmp(token->string, "hostnogssenc") == 0)
    1396             :     {
    1397             : 
    1398        5362 :         if (token->string[4] == 's') /* "hostssl" */
    1399             :         {
    1400         378 :             parsedline->conntype = ctHostSSL;
    1401             :             /* Log a warning if SSL support is not active */
    1402             : #ifdef USE_SSL
    1403         378 :             if (!EnableSSL)
    1404             :             {
    1405           0 :                 ereport(elevel,
    1406             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1407             :                          errmsg("hostssl record cannot match because SSL is disabled"),
    1408             :                          errhint("Set ssl = on in postgresql.conf."),
    1409             :                          errcontext("line %d of configuration file \"%s\"",
    1410             :                                     line_num, file_name)));
    1411           0 :                 *err_msg = "hostssl record cannot match because SSL is disabled";
    1412             :             }
    1413             : #else
    1414             :             ereport(elevel,
    1415             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1416             :                      errmsg("hostssl record cannot match because SSL is not supported by this build"),
    1417             :                      errcontext("line %d of configuration file \"%s\"",
    1418             :                                 line_num, file_name)));
    1419             :             *err_msg = "hostssl record cannot match because SSL is not supported by this build";
    1420             : #endif
    1421             :         }
    1422        4984 :         else if (token->string[4] == 'g')    /* "hostgssenc" */
    1423             :         {
    1424           0 :             parsedline->conntype = ctHostGSS;
    1425             : #ifndef ENABLE_GSS
    1426           0 :             ereport(elevel,
    1427             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1428             :                      errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
    1429             :                      errcontext("line %d of configuration file \"%s\"",
    1430             :                                 line_num, file_name)));
    1431           0 :             *err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
    1432             : #endif
    1433             :         }
    1434        4984 :         else if (token->string[4] == 'n' && token->string[6] == 's')
    1435           0 :             parsedline->conntype = ctHostNoSSL;
    1436        4984 :         else if (token->string[4] == 'n' && token->string[6] == 'g')
    1437           0 :             parsedline->conntype = ctHostNoGSS;
    1438             :         else
    1439             :         {
    1440             :             /* "host" */
    1441        4984 :             parsedline->conntype = ctHost;
    1442             :         }
    1443             :     }                           /* record type */
    1444             :     else
    1445             :     {
    1446           0 :         ereport(elevel,
    1447             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1448             :                  errmsg("invalid connection type \"%s\"",
    1449             :                         token->string),
    1450             :                  errcontext("line %d of configuration file \"%s\"",
    1451             :                             line_num, file_name)));
    1452           0 :         *err_msg = psprintf("invalid connection type \"%s\"", token->string);
    1453           0 :         return NULL;
    1454             :     }
    1455             : 
    1456             :     /* Get the databases. */
    1457        8002 :     field = lnext(tok_line->fields, field);
    1458        8002 :     if (!field)
    1459             :     {
    1460           0 :         ereport(elevel,
    1461             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1462             :                  errmsg("end-of-line before database specification"),
    1463             :                  errcontext("line %d of configuration file \"%s\"",
    1464             :                             line_num, file_name)));
    1465           0 :         *err_msg = "end-of-line before database specification";
    1466           0 :         return NULL;
    1467             :     }
    1468        8002 :     parsedline->databases = NIL;
    1469        8002 :     tokens = lfirst(field);
    1470       16016 :     foreach(tokencell, tokens)
    1471             :     {
    1472        8014 :         AuthToken  *tok = copy_auth_token(lfirst(tokencell));
    1473             : 
    1474             :         /* Compile a regexp for the database token, if necessary */
    1475        8014 :         if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
    1476           0 :             return NULL;
    1477             : 
    1478        8014 :         parsedline->databases = lappend(parsedline->databases, tok);
    1479             :     }
    1480             : 
    1481             :     /* Get the roles. */
    1482        8002 :     field = lnext(tok_line->fields, field);
    1483        8002 :     if (!field)
    1484             :     {
    1485           0 :         ereport(elevel,
    1486             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1487             :                  errmsg("end-of-line before role specification"),
    1488             :                  errcontext("line %d of configuration file \"%s\"",
    1489             :                             line_num, file_name)));
    1490           0 :         *err_msg = "end-of-line before role specification";
    1491           0 :         return NULL;
    1492             :     }
    1493        8002 :     parsedline->roles = NIL;
    1494        8002 :     tokens = lfirst(field);
    1495       16012 :     foreach(tokencell, tokens)
    1496             :     {
    1497        8010 :         AuthToken  *tok = copy_auth_token(lfirst(tokencell));
    1498             : 
    1499             :         /* Compile a regexp from the role token, if necessary */
    1500        8010 :         if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
    1501           0 :             return NULL;
    1502             : 
    1503        8010 :         parsedline->roles = lappend(parsedline->roles, tok);
    1504             :     }
    1505             : 
    1506        8002 :     if (parsedline->conntype != ctLocal)
    1507             :     {
    1508             :         /* Read the IP address field. (with or without CIDR netmask) */
    1509        5362 :         field = lnext(tok_line->fields, field);
    1510        5362 :         if (!field)
    1511             :         {
    1512           0 :             ereport(elevel,
    1513             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1514             :                      errmsg("end-of-line before IP address specification"),
    1515             :                      errcontext("line %d of configuration file \"%s\"",
    1516             :                                 line_num, file_name)));
    1517           0 :             *err_msg = "end-of-line before IP address specification";
    1518           0 :             return NULL;
    1519             :         }
    1520        5362 :         tokens = lfirst(field);
    1521        5362 :         if (tokens->length > 1)
    1522             :         {
    1523           0 :             ereport(elevel,
    1524             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1525             :                      errmsg("multiple values specified for host address"),
    1526             :                      errhint("Specify one address range per line."),
    1527             :                      errcontext("line %d of configuration file \"%s\"",
    1528             :                                 line_num, file_name)));
    1529           0 :             *err_msg = "multiple values specified for host address";
    1530           0 :             return NULL;
    1531             :         }
    1532        5362 :         token = linitial(tokens);
    1533             : 
    1534        5362 :         if (token_is_keyword(token, "all"))
    1535             :         {
    1536           0 :             parsedline->ip_cmp_method = ipCmpAll;
    1537             :         }
    1538        5362 :         else if (token_is_keyword(token, "samehost"))
    1539             :         {
    1540             :             /* Any IP on this host is allowed to connect */
    1541           0 :             parsedline->ip_cmp_method = ipCmpSameHost;
    1542             :         }
    1543        5362 :         else if (token_is_keyword(token, "samenet"))
    1544             :         {
    1545             :             /* Any IP on the host's subnets is allowed to connect */
    1546           0 :             parsedline->ip_cmp_method = ipCmpSameNet;
    1547             :         }
    1548             :         else
    1549             :         {
    1550             :             /* IP and netmask are specified */
    1551        5362 :             parsedline->ip_cmp_method = ipCmpMask;
    1552             : 
    1553             :             /* need a modifiable copy of token */
    1554        5362 :             str = pstrdup(token->string);
    1555             : 
    1556             :             /* Check if it has a CIDR suffix and if so isolate it */
    1557        5362 :             cidr_slash = strchr(str, '/');
    1558        5362 :             if (cidr_slash)
    1559        5362 :                 *cidr_slash = '\0';
    1560             : 
    1561             :             /* Get the IP address either way */
    1562        5362 :             hints.ai_flags = AI_NUMERICHOST;
    1563        5362 :             hints.ai_family = AF_UNSPEC;
    1564        5362 :             hints.ai_socktype = 0;
    1565        5362 :             hints.ai_protocol = 0;
    1566        5362 :             hints.ai_addrlen = 0;
    1567        5362 :             hints.ai_canonname = NULL;
    1568        5362 :             hints.ai_addr = NULL;
    1569        5362 :             hints.ai_next = NULL;
    1570             : 
    1571        5362 :             ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
    1572        5362 :             if (ret == 0 && gai_result)
    1573             :             {
    1574        5362 :                 memcpy(&parsedline->addr, gai_result->ai_addr,
    1575        5362 :                        gai_result->ai_addrlen);
    1576        5362 :                 parsedline->addrlen = gai_result->ai_addrlen;
    1577             :             }
    1578           0 :             else if (ret == EAI_NONAME)
    1579           0 :                 parsedline->hostname = str;
    1580             :             else
    1581             :             {
    1582           0 :                 ereport(elevel,
    1583             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1584             :                          errmsg("invalid IP address \"%s\": %s",
    1585             :                                 str, gai_strerror(ret)),
    1586             :                          errcontext("line %d of configuration file \"%s\"",
    1587             :                                     line_num, file_name)));
    1588           0 :                 *err_msg = psprintf("invalid IP address \"%s\": %s",
    1589             :                                     str, gai_strerror(ret));
    1590           0 :                 if (gai_result)
    1591           0 :                     pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1592           0 :                 return NULL;
    1593             :             }
    1594             : 
    1595        5362 :             pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1596             : 
    1597             :             /* Get the netmask */
    1598        5362 :             if (cidr_slash)
    1599             :             {
    1600        5362 :                 if (parsedline->hostname)
    1601             :                 {
    1602           0 :                     ereport(elevel,
    1603             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1604             :                              errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
    1605             :                                     token->string),
    1606             :                              errcontext("line %d of configuration file \"%s\"",
    1607             :                                         line_num, file_name)));
    1608           0 :                     *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
    1609             :                                         token->string);
    1610           0 :                     return NULL;
    1611             :                 }
    1612             : 
    1613        5362 :                 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
    1614        5362 :                                           parsedline->addr.ss_family) < 0)
    1615             :                 {
    1616           0 :                     ereport(elevel,
    1617             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1618             :                              errmsg("invalid CIDR mask in address \"%s\"",
    1619             :                                     token->string),
    1620             :                              errcontext("line %d of configuration file \"%s\"",
    1621             :                                         line_num, file_name)));
    1622           0 :                     *err_msg = psprintf("invalid CIDR mask in address \"%s\"",
    1623             :                                         token->string);
    1624           0 :                     return NULL;
    1625             :                 }
    1626        5362 :                 parsedline->masklen = parsedline->addrlen;
    1627        5362 :                 pfree(str);
    1628             :             }
    1629           0 :             else if (!parsedline->hostname)
    1630             :             {
    1631             :                 /* Read the mask field. */
    1632           0 :                 pfree(str);
    1633           0 :                 field = lnext(tok_line->fields, field);
    1634           0 :                 if (!field)
    1635             :                 {
    1636           0 :                     ereport(elevel,
    1637             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1638             :                              errmsg("end-of-line before netmask specification"),
    1639             :                              errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
    1640             :                              errcontext("line %d of configuration file \"%s\"",
    1641             :                                         line_num, file_name)));
    1642           0 :                     *err_msg = "end-of-line before netmask specification";
    1643           0 :                     return NULL;
    1644             :                 }
    1645           0 :                 tokens = lfirst(field);
    1646           0 :                 if (tokens->length > 1)
    1647             :                 {
    1648           0 :                     ereport(elevel,
    1649             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1650             :                              errmsg("multiple values specified for netmask"),
    1651             :                              errcontext("line %d of configuration file \"%s\"",
    1652             :                                         line_num, file_name)));
    1653           0 :                     *err_msg = "multiple values specified for netmask";
    1654           0 :                     return NULL;
    1655             :                 }
    1656           0 :                 token = linitial(tokens);
    1657             : 
    1658           0 :                 ret = pg_getaddrinfo_all(token->string, NULL,
    1659             :                                          &hints, &gai_result);
    1660           0 :                 if (ret || !gai_result)
    1661             :                 {
    1662           0 :                     ereport(elevel,
    1663             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1664             :                              errmsg("invalid IP mask \"%s\": %s",
    1665             :                                     token->string, gai_strerror(ret)),
    1666             :                              errcontext("line %d of configuration file \"%s\"",
    1667             :                                         line_num, file_name)));
    1668           0 :                     *err_msg = psprintf("invalid IP mask \"%s\": %s",
    1669             :                                         token->string, gai_strerror(ret));
    1670           0 :                     if (gai_result)
    1671           0 :                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1672           0 :                     return NULL;
    1673             :                 }
    1674             : 
    1675           0 :                 memcpy(&parsedline->mask, gai_result->ai_addr,
    1676           0 :                        gai_result->ai_addrlen);
    1677           0 :                 parsedline->masklen = gai_result->ai_addrlen;
    1678           0 :                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1679             : 
    1680           0 :                 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
    1681             :                 {
    1682           0 :                     ereport(elevel,
    1683             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1684             :                              errmsg("IP address and mask do not match"),
    1685             :                              errcontext("line %d of configuration file \"%s\"",
    1686             :                                         line_num, file_name)));
    1687           0 :                     *err_msg = "IP address and mask do not match";
    1688           0 :                     return NULL;
    1689             :                 }
    1690             :             }
    1691             :         }
    1692             :     }                           /* != ctLocal */
    1693             : 
    1694             :     /* Get the authentication method */
    1695        8002 :     field = lnext(tok_line->fields, field);
    1696        8002 :     if (!field)
    1697             :     {
    1698           0 :         ereport(elevel,
    1699             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1700             :                  errmsg("end-of-line before authentication method"),
    1701             :                  errcontext("line %d of configuration file \"%s\"",
    1702             :                             line_num, file_name)));
    1703           0 :         *err_msg = "end-of-line before authentication method";
    1704           0 :         return NULL;
    1705             :     }
    1706        8002 :     tokens = lfirst(field);
    1707        8002 :     if (tokens->length > 1)
    1708             :     {
    1709           0 :         ereport(elevel,
    1710             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1711             :                  errmsg("multiple values specified for authentication type"),
    1712             :                  errhint("Specify exactly one authentication type per line."),
    1713             :                  errcontext("line %d of configuration file \"%s\"",
    1714             :                             line_num, file_name)));
    1715           0 :         *err_msg = "multiple values specified for authentication type";
    1716           0 :         return NULL;
    1717             :     }
    1718        8002 :     token = linitial(tokens);
    1719             : 
    1720        8002 :     unsupauth = NULL;
    1721        8002 :     if (strcmp(token->string, "trust") == 0)
    1722        7636 :         parsedline->auth_method = uaTrust;
    1723         366 :     else if (strcmp(token->string, "ident") == 0)
    1724           0 :         parsedline->auth_method = uaIdent;
    1725         366 :     else if (strcmp(token->string, "peer") == 0)
    1726          38 :         parsedline->auth_method = uaPeer;
    1727         328 :     else if (strcmp(token->string, "password") == 0)
    1728          16 :         parsedline->auth_method = uaPassword;
    1729         312 :     else if (strcmp(token->string, "gss") == 0)
    1730             : #ifdef ENABLE_GSS
    1731             :         parsedline->auth_method = uaGSS;
    1732             : #else
    1733           0 :         unsupauth = "gss";
    1734             : #endif
    1735         312 :     else if (strcmp(token->string, "sspi") == 0)
    1736             : #ifdef ENABLE_SSPI
    1737             :         parsedline->auth_method = uaSSPI;
    1738             : #else
    1739           0 :         unsupauth = "sspi";
    1740             : #endif
    1741         312 :     else if (strcmp(token->string, "reject") == 0)
    1742          36 :         parsedline->auth_method = uaReject;
    1743         276 :     else if (strcmp(token->string, "md5") == 0)
    1744             :     {
    1745          44 :         if (Db_user_namespace)
    1746             :         {
    1747           0 :             ereport(elevel,
    1748             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1749             :                      errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
    1750             :                      errcontext("line %d of configuration file \"%s\"",
    1751             :                                 line_num, file_name)));
    1752           0 :             *err_msg = "MD5 authentication is not supported when \"db_user_namespace\" is enabled";
    1753           0 :             return NULL;
    1754             :         }
    1755          44 :         parsedline->auth_method = uaMD5;
    1756             :     }
    1757         232 :     else if (strcmp(token->string, "scram-sha-256") == 0)
    1758          30 :         parsedline->auth_method = uaSCRAM;
    1759         202 :     else if (strcmp(token->string, "pam") == 0)
    1760             : #ifdef USE_PAM
    1761           0 :         parsedline->auth_method = uaPAM;
    1762             : #else
    1763             :         unsupauth = "pam";
    1764             : #endif
    1765         202 :     else if (strcmp(token->string, "bsd") == 0)
    1766             : #ifdef USE_BSD_AUTH
    1767             :         parsedline->auth_method = uaBSD;
    1768             : #else
    1769           0 :         unsupauth = "bsd";
    1770             : #endif
    1771         202 :     else if (strcmp(token->string, "ldap") == 0)
    1772             : #ifdef USE_LDAP
    1773          34 :         parsedline->auth_method = uaLDAP;
    1774             : #else
    1775             :         unsupauth = "ldap";
    1776             : #endif
    1777         168 :     else if (strcmp(token->string, "cert") == 0)
    1778             : #ifdef USE_SSL
    1779         168 :         parsedline->auth_method = uaCert;
    1780             : #else
    1781             :         unsupauth = "cert";
    1782             : #endif
    1783           0 :     else if (strcmp(token->string, "radius") == 0)
    1784           0 :         parsedline->auth_method = uaRADIUS;
    1785             :     else
    1786             :     {
    1787           0 :         ereport(elevel,
    1788             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1789             :                  errmsg("invalid authentication method \"%s\"",
    1790             :                         token->string),
    1791             :                  errcontext("line %d of configuration file \"%s\"",
    1792             :                             line_num, file_name)));
    1793           0 :         *err_msg = psprintf("invalid authentication method \"%s\"",
    1794             :                             token->string);
    1795           0 :         return NULL;
    1796             :     }
    1797             : 
    1798        8002 :     if (unsupauth)
    1799             :     {
    1800           0 :         ereport(elevel,
    1801             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1802             :                  errmsg("invalid authentication method \"%s\": not supported by this build",
    1803             :                         token->string),
    1804             :                  errcontext("line %d of configuration file \"%s\"",
    1805             :                             line_num, file_name)));
    1806           0 :         *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
    1807             :                             token->string);
    1808           0 :         return NULL;
    1809             :     }
    1810             : 
    1811             :     /*
    1812             :      * XXX: When using ident on local connections, change it to peer, for
    1813             :      * backwards compatibility.
    1814             :      */
    1815        8002 :     if (parsedline->conntype == ctLocal &&
    1816        2640 :         parsedline->auth_method == uaIdent)
    1817           0 :         parsedline->auth_method = uaPeer;
    1818             : 
    1819             :     /* Invalid authentication combinations */
    1820        8002 :     if (parsedline->conntype == ctLocal &&
    1821        2640 :         parsedline->auth_method == uaGSS)
    1822             :     {
    1823           0 :         ereport(elevel,
    1824             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1825             :                  errmsg("gssapi authentication is not supported on local sockets"),
    1826             :                  errcontext("line %d of configuration file \"%s\"",
    1827             :                             line_num, file_name)));
    1828           0 :         *err_msg = "gssapi authentication is not supported on local sockets";
    1829           0 :         return NULL;
    1830             :     }
    1831             : 
    1832        8002 :     if (parsedline->conntype != ctLocal &&
    1833        5362 :         parsedline->auth_method == uaPeer)
    1834             :     {
    1835           0 :         ereport(elevel,
    1836             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1837             :                  errmsg("peer authentication is only supported on local sockets"),
    1838             :                  errcontext("line %d of configuration file \"%s\"",
    1839             :                             line_num, file_name)));
    1840           0 :         *err_msg = "peer authentication is only supported on local sockets";
    1841           0 :         return NULL;
    1842             :     }
    1843             : 
    1844             :     /*
    1845             :      * SSPI authentication can never be enabled on ctLocal connections,
    1846             :      * because it's only supported on Windows, where ctLocal isn't supported.
    1847             :      */
    1848             : 
    1849             : 
    1850        8002 :     if (parsedline->conntype != ctHostSSL &&
    1851        7624 :         parsedline->auth_method == uaCert)
    1852             :     {
    1853           0 :         ereport(elevel,
    1854             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1855             :                  errmsg("cert authentication is only supported on hostssl connections"),
    1856             :                  errcontext("line %d of configuration file \"%s\"",
    1857             :                             line_num, file_name)));
    1858           0 :         *err_msg = "cert authentication is only supported on hostssl connections";
    1859           0 :         return NULL;
    1860             :     }
    1861             : 
    1862             :     /*
    1863             :      * For GSS and SSPI, set the default value of include_realm to true.
    1864             :      * Having include_realm set to false is dangerous in multi-realm
    1865             :      * situations and is generally considered bad practice.  We keep the
    1866             :      * capability around for backwards compatibility, but we might want to
    1867             :      * remove it at some point in the future.  Users who still need to strip
    1868             :      * the realm off would be better served by using an appropriate regex in a
    1869             :      * pg_ident.conf mapping.
    1870             :      */
    1871        8002 :     if (parsedline->auth_method == uaGSS ||
    1872        8002 :         parsedline->auth_method == uaSSPI)
    1873           0 :         parsedline->include_realm = true;
    1874             : 
    1875             :     /*
    1876             :      * For SSPI, include_realm defaults to the SAM-compatible domain (aka
    1877             :      * NetBIOS name) and user names instead of the Kerberos principal name for
    1878             :      * compatibility.
    1879             :      */
    1880        8002 :     if (parsedline->auth_method == uaSSPI)
    1881             :     {
    1882           0 :         parsedline->compat_realm = true;
    1883           0 :         parsedline->upn_username = false;
    1884             :     }
    1885             : 
    1886             :     /* Parse remaining arguments */
    1887        8534 :     while ((field = lnext(tok_line->fields, field)) != NULL)
    1888             :     {
    1889         532 :         tokens = lfirst(field);
    1890        1064 :         foreach(tokencell, tokens)
    1891             :         {
    1892             :             char       *val;
    1893             : 
    1894         532 :             token = lfirst(tokencell);
    1895             : 
    1896         532 :             str = pstrdup(token->string);
    1897         532 :             val = strchr(str, '=');
    1898         532 :             if (val == NULL)
    1899             :             {
    1900             :                 /*
    1901             :                  * Got something that's not a name=value pair.
    1902             :                  */
    1903           0 :                 ereport(elevel,
    1904             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1905             :                          errmsg("authentication option not in name=value format: %s", token->string),
    1906             :                          errcontext("line %d of configuration file \"%s\"",
    1907             :                                     line_num, file_name)));
    1908           0 :                 *err_msg = psprintf("authentication option not in name=value format: %s",
    1909             :                                     token->string);
    1910           0 :                 return NULL;
    1911             :             }
    1912             : 
    1913         532 :             *val++ = '\0';      /* str now holds "name", val holds "value" */
    1914         532 :             if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
    1915             :                 /* parse_hba_auth_opt already logged the error message */
    1916           0 :                 return NULL;
    1917         532 :             pfree(str);
    1918             :         }
    1919             :     }
    1920             : 
    1921             :     /*
    1922             :      * Check if the selected authentication method has any mandatory arguments
    1923             :      * that are not set.
    1924             :      */
    1925        8002 :     if (parsedline->auth_method == uaLDAP)
    1926             :     {
    1927             : #ifndef HAVE_LDAP_INITIALIZE
    1928             :         /* Not mandatory for OpenLDAP, because it can use DNS SRV records */
    1929             :         MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
    1930             : #endif
    1931             : 
    1932             :         /*
    1933             :          * LDAP can operate in two modes: either with a direct bind, using
    1934             :          * ldapprefix and ldapsuffix, or using a search+bind, using
    1935             :          * ldapbasedn, ldapbinddn, ldapbindpasswd and one of
    1936             :          * ldapsearchattribute or ldapsearchfilter.  Disallow mixing these
    1937             :          * parameters.
    1938             :          */
    1939          34 :         if (parsedline->ldapprefix || parsedline->ldapsuffix)
    1940             :         {
    1941           4 :             if (parsedline->ldapbasedn ||
    1942           4 :                 parsedline->ldapbinddn ||
    1943           4 :                 parsedline->ldapbindpasswd ||
    1944           4 :                 parsedline->ldapsearchattribute ||
    1945           4 :                 parsedline->ldapsearchfilter)
    1946             :             {
    1947           0 :                 ereport(elevel,
    1948             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1949             :                          errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix"),
    1950             :                          errcontext("line %d of configuration file \"%s\"",
    1951             :                                     line_num, file_name)));
    1952           0 :                 *err_msg = "cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix";
    1953           0 :                 return NULL;
    1954             :             }
    1955             :         }
    1956          30 :         else if (!parsedline->ldapbasedn)
    1957             :         {
    1958           0 :             ereport(elevel,
    1959             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1960             :                      errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
    1961             :                      errcontext("line %d of configuration file \"%s\"",
    1962             :                                 line_num, file_name)));
    1963           0 :             *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
    1964           0 :             return NULL;
    1965             :         }
    1966             : 
    1967             :         /*
    1968             :          * When using search+bind, you can either use a simple attribute
    1969             :          * (defaulting to "uid") or a fully custom search filter.  You can't
    1970             :          * do both.
    1971             :          */
    1972          34 :         if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter)
    1973             :         {
    1974           0 :             ereport(elevel,
    1975             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1976             :                      errmsg("cannot use ldapsearchattribute together with ldapsearchfilter"),
    1977             :                      errcontext("line %d of configuration file \"%s\"",
    1978             :                                 line_num, file_name)));
    1979           0 :             *err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter";
    1980           0 :             return NULL;
    1981             :         }
    1982             :     }
    1983             : 
    1984        8002 :     if (parsedline->auth_method == uaRADIUS)
    1985             :     {
    1986           0 :         MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
    1987           0 :         MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
    1988             : 
    1989           0 :         if (parsedline->radiusservers == NIL)
    1990             :         {
    1991           0 :             ereport(elevel,
    1992             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1993             :                      errmsg("list of RADIUS servers cannot be empty"),
    1994             :                      errcontext("line %d of configuration file \"%s\"",
    1995             :                                 line_num, file_name)));
    1996           0 :             *err_msg = "list of RADIUS servers cannot be empty";
    1997           0 :             return NULL;
    1998             :         }
    1999             : 
    2000           0 :         if (parsedline->radiussecrets == NIL)
    2001             :         {
    2002           0 :             ereport(elevel,
    2003             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2004             :                      errmsg("list of RADIUS secrets cannot be empty"),
    2005             :                      errcontext("line %d of configuration file \"%s\"",
    2006             :                                 line_num, file_name)));
    2007           0 :             *err_msg = "list of RADIUS secrets cannot be empty";
    2008           0 :             return NULL;
    2009             :         }
    2010             : 
    2011             :         /*
    2012             :          * Verify length of option lists - each can be 0 (except for secrets,
    2013             :          * but that's already checked above), 1 (use the same value
    2014             :          * everywhere) or the same as the number of servers.
    2015             :          */
    2016           0 :         if (!(list_length(parsedline->radiussecrets) == 1 ||
    2017           0 :               list_length(parsedline->radiussecrets) == list_length(parsedline->radiusservers)))
    2018             :         {
    2019           0 :             ereport(elevel,
    2020             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2021             :                      errmsg("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2022             :                             list_length(parsedline->radiussecrets),
    2023             :                             list_length(parsedline->radiusservers)),
    2024             :                      errcontext("line %d of configuration file \"%s\"",
    2025             :                                 line_num, file_name)));
    2026           0 :             *err_msg = psprintf("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2027           0 :                                 list_length(parsedline->radiussecrets),
    2028           0 :                                 list_length(parsedline->radiusservers));
    2029           0 :             return NULL;
    2030             :         }
    2031           0 :         if (!(list_length(parsedline->radiusports) == 0 ||
    2032           0 :               list_length(parsedline->radiusports) == 1 ||
    2033           0 :               list_length(parsedline->radiusports) == list_length(parsedline->radiusservers)))
    2034             :         {
    2035           0 :             ereport(elevel,
    2036             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2037             :                      errmsg("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2038             :                             list_length(parsedline->radiusports),
    2039             :                             list_length(parsedline->radiusservers)),
    2040             :                      errcontext("line %d of configuration file \"%s\"",
    2041             :                                 line_num, file_name)));
    2042           0 :             *err_msg = psprintf("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2043           0 :                                 list_length(parsedline->radiusports),
    2044           0 :                                 list_length(parsedline->radiusservers));
    2045           0 :             return NULL;
    2046             :         }
    2047           0 :         if (!(list_length(parsedline->radiusidentifiers) == 0 ||
    2048           0 :               list_length(parsedline->radiusidentifiers) == 1 ||
    2049           0 :               list_length(parsedline->radiusidentifiers) == list_length(parsedline->radiusservers)))
    2050             :         {
    2051           0 :             ereport(elevel,
    2052             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2053             :                      errmsg("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2054             :                             list_length(parsedline->radiusidentifiers),
    2055             :                             list_length(parsedline->radiusservers)),
    2056             :                      errcontext("line %d of configuration file \"%s\"",
    2057             :                                 line_num, file_name)));
    2058           0 :             *err_msg = psprintf("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
    2059           0 :                                 list_length(parsedline->radiusidentifiers),
    2060           0 :                                 list_length(parsedline->radiusservers));
    2061           0 :             return NULL;
    2062             :         }
    2063             :     }
    2064             : 
    2065             :     /*
    2066             :      * Enforce any parameters implied by other settings.
    2067             :      */
    2068        8002 :     if (parsedline->auth_method == uaCert)
    2069             :     {
    2070             :         /*
    2071             :          * For auth method cert, client certificate validation is mandatory,
    2072             :          * and it implies the level of verify-full.
    2073             :          */
    2074         168 :         parsedline->clientcert = clientCertFull;
    2075             :     }
    2076             : 
    2077        8002 :     return parsedline;
    2078             : }
    2079             : 
    2080             : 
    2081             : /*
    2082             :  * Parse one name-value pair as an authentication option into the given
    2083             :  * HbaLine.  Return true if we successfully parse the option, false if we
    2084             :  * encounter an error.  In the event of an error, also log a message at
    2085             :  * ereport level elevel, and store a message string into *err_msg.
    2086             :  */
    2087             : static bool
    2088         532 : parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
    2089             :                    int elevel, char **err_msg)
    2090             : {
    2091         532 :     int         line_num = hbaline->linenumber;
    2092         532 :     char       *file_name = hbaline->sourcefile;
    2093             : 
    2094             : #ifdef USE_LDAP
    2095         532 :     hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
    2096             : #endif
    2097             : 
    2098         532 :     if (strcmp(name, "map") == 0)
    2099             :     {
    2100         162 :         if (hbaline->auth_method != uaIdent &&
    2101         162 :             hbaline->auth_method != uaPeer &&
    2102         126 :             hbaline->auth_method != uaGSS &&
    2103         126 :             hbaline->auth_method != uaSSPI &&
    2104         126 :             hbaline->auth_method != uaCert)
    2105           0 :             INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
    2106         162 :         hbaline->usermap = pstrdup(val);
    2107             :     }
    2108         370 :     else if (strcmp(name, "clientcert") == 0)
    2109             :     {
    2110         126 :         if (hbaline->conntype != ctHostSSL)
    2111             :         {
    2112           0 :             ereport(elevel,
    2113             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2114             :                      errmsg("clientcert can only be configured for \"hostssl\" rows"),
    2115             :                      errcontext("line %d of configuration file \"%s\"",
    2116             :                                 line_num, file_name)));
    2117           0 :             *err_msg = "clientcert can only be configured for \"hostssl\" rows";
    2118           0 :             return false;
    2119             :         }
    2120             : 
    2121         126 :         if (strcmp(val, "verify-full") == 0)
    2122             :         {
    2123          84 :             hbaline->clientcert = clientCertFull;
    2124             :         }
    2125          42 :         else if (strcmp(val, "verify-ca") == 0)
    2126             :         {
    2127          42 :             if (hbaline->auth_method == uaCert)
    2128             :             {
    2129           0 :                 ereport(elevel,
    2130             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2131             :                          errmsg("clientcert only accepts \"verify-full\" when using \"cert\" authentication"),
    2132             :                          errcontext("line %d of configuration file \"%s\"",
    2133             :                                     line_num, file_name)));
    2134           0 :                 *err_msg = "clientcert can only be set to \"verify-full\" when using \"cert\" authentication";
    2135           0 :                 return false;
    2136             :             }
    2137             : 
    2138          42 :             hbaline->clientcert = clientCertCA;
    2139             :         }
    2140             :         else
    2141             :         {
    2142           0 :             ereport(elevel,
    2143             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2144             :                      errmsg("invalid value for clientcert: \"%s\"", val),
    2145             :                      errcontext("line %d of configuration file \"%s\"",
    2146             :                                 line_num, file_name)));
    2147           0 :             return false;
    2148             :         }
    2149             :     }
    2150         244 :     else if (strcmp(name, "clientname") == 0)
    2151             :     {
    2152         126 :         if (hbaline->conntype != ctHostSSL)
    2153             :         {
    2154           0 :             ereport(elevel,
    2155             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2156             :                      errmsg("clientname can only be configured for \"hostssl\" rows"),
    2157             :                      errcontext("line %d of configuration file \"%s\"",
    2158             :                                 line_num, file_name)));
    2159           0 :             *err_msg = "clientname can only be configured for \"hostssl\" rows";
    2160           0 :             return false;
    2161             :         }
    2162             : 
    2163         126 :         if (strcmp(val, "CN") == 0)
    2164             :         {
    2165          42 :             hbaline->clientcertname = clientCertCN;
    2166             :         }
    2167          84 :         else if (strcmp(val, "DN") == 0)
    2168             :         {
    2169          84 :             hbaline->clientcertname = clientCertDN;
    2170             :         }
    2171             :         else
    2172             :         {
    2173           0 :             ereport(elevel,
    2174             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2175             :                      errmsg("invalid value for clientname: \"%s\"", val),
    2176             :                      errcontext("line %d of configuration file \"%s\"",
    2177             :                                 line_num, file_name)));
    2178           0 :             return false;
    2179             :         }
    2180             :     }
    2181         118 :     else if (strcmp(name, "pamservice") == 0)
    2182             :     {
    2183           0 :         REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
    2184           0 :         hbaline->pamservice = pstrdup(val);
    2185             :     }
    2186         118 :     else if (strcmp(name, "pam_use_hostname") == 0)
    2187             :     {
    2188           0 :         REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
    2189           0 :         if (strcmp(val, "1") == 0)
    2190           0 :             hbaline->pam_use_hostname = true;
    2191             :         else
    2192           0 :             hbaline->pam_use_hostname = false;
    2193             :     }
    2194         118 :     else if (strcmp(name, "ldapurl") == 0)
    2195             :     {
    2196             : #ifdef LDAP_API_FEATURE_X_OPENLDAP
    2197             :         LDAPURLDesc *urldata;
    2198             :         int         rc;
    2199             : #endif
    2200             : 
    2201          10 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
    2202             : #ifdef LDAP_API_FEATURE_X_OPENLDAP
    2203          10 :         rc = ldap_url_parse(val, &urldata);
    2204          10 :         if (rc != LDAP_SUCCESS)
    2205             :         {
    2206           0 :             ereport(elevel,
    2207             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2208             :                      errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
    2209           0 :             *err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
    2210             :                                 val, ldap_err2string(rc));
    2211           0 :             return false;
    2212             :         }
    2213             : 
    2214          10 :         if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
    2215           4 :             strcmp(urldata->lud_scheme, "ldaps") != 0)
    2216             :         {
    2217           0 :             ereport(elevel,
    2218             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2219             :                      errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
    2220           0 :             *err_msg = psprintf("unsupported LDAP URL scheme: %s",
    2221           0 :                                 urldata->lud_scheme);
    2222           0 :             ldap_free_urldesc(urldata);
    2223           0 :             return false;
    2224             :         }
    2225             : 
    2226          10 :         if (urldata->lud_scheme)
    2227          10 :             hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
    2228          10 :         if (urldata->lud_host)
    2229          10 :             hbaline->ldapserver = pstrdup(urldata->lud_host);
    2230          10 :         hbaline->ldapport = urldata->lud_port;
    2231          10 :         if (urldata->lud_dn)
    2232          10 :             hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
    2233             : 
    2234          10 :         if (urldata->lud_attrs)
    2235           2 :             hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]);    /* only use first one */
    2236          10 :         hbaline->ldapscope = urldata->lud_scope;
    2237          10 :         if (urldata->lud_filter)
    2238           6 :             hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter);
    2239          10 :         ldap_free_urldesc(urldata);
    2240             : #else                           /* not OpenLDAP */
    2241             :         ereport(elevel,
    2242             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2243             :                  errmsg("LDAP URLs not supported on this platform")));
    2244             :         *err_msg = "LDAP URLs not supported on this platform";
    2245             : #endif                          /* not OpenLDAP */
    2246             :     }
    2247         108 :     else if (strcmp(name, "ldaptls") == 0)
    2248             :     {
    2249           4 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
    2250           4 :         if (strcmp(val, "1") == 0)
    2251           4 :             hbaline->ldaptls = true;
    2252             :         else
    2253           0 :             hbaline->ldaptls = false;
    2254             :     }
    2255         104 :     else if (strcmp(name, "ldapscheme") == 0)
    2256             :     {
    2257           2 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
    2258           2 :         if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
    2259           0 :             ereport(elevel,
    2260             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2261             :                      errmsg("invalid ldapscheme value: \"%s\"", val),
    2262             :                      errcontext("line %d of configuration file \"%s\"",
    2263             :                                 line_num, file_name)));
    2264           2 :         hbaline->ldapscheme = pstrdup(val);
    2265             :     }
    2266         102 :     else if (strcmp(name, "ldapserver") == 0)
    2267             :     {
    2268          24 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
    2269          24 :         hbaline->ldapserver = pstrdup(val);
    2270             :     }
    2271          78 :     else if (strcmp(name, "ldapport") == 0)
    2272             :     {
    2273          24 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
    2274          24 :         hbaline->ldapport = atoi(val);
    2275          24 :         if (hbaline->ldapport == 0)
    2276             :         {
    2277           0 :             ereport(elevel,
    2278             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2279             :                      errmsg("invalid LDAP port number: \"%s\"", val),
    2280             :                      errcontext("line %d of configuration file \"%s\"",
    2281             :                                 line_num, file_name)));
    2282           0 :             *err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
    2283           0 :             return false;
    2284             :         }
    2285             :     }
    2286          54 :     else if (strcmp(name, "ldapbinddn") == 0)
    2287             :     {
    2288          10 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
    2289          10 :         hbaline->ldapbinddn = pstrdup(val);
    2290             :     }
    2291          44 :     else if (strcmp(name, "ldapbindpasswd") == 0)
    2292             :     {
    2293           8 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
    2294           8 :         hbaline->ldapbindpasswd = pstrdup(val);
    2295             :     }
    2296          36 :     else if (strcmp(name, "ldapsearchattribute") == 0)
    2297             :     {
    2298           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
    2299           0 :         hbaline->ldapsearchattribute = pstrdup(val);
    2300             :     }
    2301          36 :     else if (strcmp(name, "ldapsearchfilter") == 0)
    2302             :     {
    2303           8 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter", "ldap");
    2304           8 :         hbaline->ldapsearchfilter = pstrdup(val);
    2305             :     }
    2306          28 :     else if (strcmp(name, "ldapbasedn") == 0)
    2307             :     {
    2308          20 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
    2309          20 :         hbaline->ldapbasedn = pstrdup(val);
    2310             :     }
    2311           8 :     else if (strcmp(name, "ldapprefix") == 0)
    2312             :     {
    2313           4 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
    2314           4 :         hbaline->ldapprefix = pstrdup(val);
    2315             :     }
    2316           4 :     else if (strcmp(name, "ldapsuffix") == 0)
    2317             :     {
    2318           4 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
    2319           4 :         hbaline->ldapsuffix = pstrdup(val);
    2320             :     }
    2321           0 :     else if (strcmp(name, "krb_realm") == 0)
    2322             :     {
    2323           0 :         if (hbaline->auth_method != uaGSS &&
    2324           0 :             hbaline->auth_method != uaSSPI)
    2325           0 :             INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
    2326           0 :         hbaline->krb_realm = pstrdup(val);
    2327             :     }
    2328           0 :     else if (strcmp(name, "include_realm") == 0)
    2329             :     {
    2330           0 :         if (hbaline->auth_method != uaGSS &&
    2331           0 :             hbaline->auth_method != uaSSPI)
    2332           0 :             INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
    2333           0 :         if (strcmp(val, "1") == 0)
    2334           0 :             hbaline->include_realm = true;
    2335             :         else
    2336           0 :             hbaline->include_realm = false;
    2337             :     }
    2338           0 :     else if (strcmp(name, "compat_realm") == 0)
    2339             :     {
    2340           0 :         if (hbaline->auth_method != uaSSPI)
    2341           0 :             INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
    2342           0 :         if (strcmp(val, "1") == 0)
    2343           0 :             hbaline->compat_realm = true;
    2344             :         else
    2345           0 :             hbaline->compat_realm = false;
    2346             :     }
    2347           0 :     else if (strcmp(name, "upn_username") == 0)
    2348             :     {
    2349           0 :         if (hbaline->auth_method != uaSSPI)
    2350           0 :             INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
    2351           0 :         if (strcmp(val, "1") == 0)
    2352           0 :             hbaline->upn_username = true;
    2353             :         else
    2354           0 :             hbaline->upn_username = false;
    2355             :     }
    2356           0 :     else if (strcmp(name, "radiusservers") == 0)
    2357             :     {
    2358             :         struct addrinfo *gai_result;
    2359             :         struct addrinfo hints;
    2360             :         int         ret;
    2361             :         List       *parsed_servers;
    2362             :         ListCell   *l;
    2363           0 :         char       *dupval = pstrdup(val);
    2364             : 
    2365           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
    2366             : 
    2367           0 :         if (!SplitGUCList(dupval, ',', &parsed_servers))
    2368             :         {
    2369             :             /* syntax error in list */
    2370           0 :             ereport(elevel,
    2371             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2372             :                      errmsg("could not parse RADIUS server list \"%s\"",
    2373             :                             val),
    2374             :                      errcontext("line %d of configuration file \"%s\"",
    2375             :                                 line_num, file_name)));
    2376           0 :             return false;
    2377             :         }
    2378             : 
    2379             :         /* For each entry in the list, translate it */
    2380           0 :         foreach(l, parsed_servers)
    2381             :         {
    2382           0 :             MemSet(&hints, 0, sizeof(hints));
    2383           0 :             hints.ai_socktype = SOCK_DGRAM;
    2384           0 :             hints.ai_family = AF_UNSPEC;
    2385             : 
    2386           0 :             ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
    2387           0 :             if (ret || !gai_result)
    2388             :             {
    2389           0 :                 ereport(elevel,
    2390             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2391             :                          errmsg("could not translate RADIUS server name \"%s\" to address: %s",
    2392             :                                 (char *) lfirst(l), gai_strerror(ret)),
    2393             :                          errcontext("line %d of configuration file \"%s\"",
    2394             :                                     line_num, file_name)));
    2395           0 :                 if (gai_result)
    2396           0 :                     pg_freeaddrinfo_all(hints.ai_family, gai_result);
    2397             : 
    2398           0 :                 list_free(parsed_servers);
    2399           0 :                 return false;
    2400             :             }
    2401           0 :             pg_freeaddrinfo_all(hints.ai_family, gai_result);
    2402             :         }
    2403             : 
    2404             :         /* All entries are OK, so store them */
    2405           0 :         hbaline->radiusservers = parsed_servers;
    2406           0 :         hbaline->radiusservers_s = pstrdup(val);
    2407             :     }
    2408           0 :     else if (strcmp(name, "radiusports") == 0)
    2409             :     {
    2410             :         List       *parsed_ports;
    2411             :         ListCell   *l;
    2412           0 :         char       *dupval = pstrdup(val);
    2413             : 
    2414           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
    2415             : 
    2416           0 :         if (!SplitGUCList(dupval, ',', &parsed_ports))
    2417             :         {
    2418           0 :             ereport(elevel,
    2419             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2420             :                      errmsg("could not parse RADIUS port list \"%s\"",
    2421             :                             val),
    2422             :                      errcontext("line %d of configuration file \"%s\"",
    2423             :                                 line_num, file_name)));
    2424           0 :             *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
    2425           0 :             return false;
    2426             :         }
    2427             : 
    2428           0 :         foreach(l, parsed_ports)
    2429             :         {
    2430           0 :             if (atoi(lfirst(l)) == 0)
    2431             :             {
    2432           0 :                 ereport(elevel,
    2433             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2434             :                          errmsg("invalid RADIUS port number: \"%s\"", val),
    2435             :                          errcontext("line %d of configuration file \"%s\"",
    2436             :                                     line_num, file_name)));
    2437             : 
    2438           0 :                 return false;
    2439             :             }
    2440             :         }
    2441           0 :         hbaline->radiusports = parsed_ports;
    2442           0 :         hbaline->radiusports_s = pstrdup(val);
    2443             :     }
    2444           0 :     else if (strcmp(name, "radiussecrets") == 0)
    2445             :     {
    2446             :         List       *parsed_secrets;
    2447           0 :         char       *dupval = pstrdup(val);
    2448             : 
    2449           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
    2450             : 
    2451           0 :         if (!SplitGUCList(dupval, ',', &parsed_secrets))
    2452             :         {
    2453             :             /* syntax error in list */
    2454           0 :             ereport(elevel,
    2455             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2456             :                      errmsg("could not parse RADIUS secret list \"%s\"",
    2457             :                             val),
    2458             :                      errcontext("line %d of configuration file \"%s\"",
    2459             :                                 line_num, file_name)));
    2460           0 :             return false;
    2461             :         }
    2462             : 
    2463           0 :         hbaline->radiussecrets = parsed_secrets;
    2464           0 :         hbaline->radiussecrets_s = pstrdup(val);
    2465             :     }
    2466           0 :     else if (strcmp(name, "radiusidentifiers") == 0)
    2467             :     {
    2468             :         List       *parsed_identifiers;
    2469           0 :         char       *dupval = pstrdup(val);
    2470             : 
    2471           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
    2472             : 
    2473           0 :         if (!SplitGUCList(dupval, ',', &parsed_identifiers))
    2474             :         {
    2475             :             /* syntax error in list */
    2476           0 :             ereport(elevel,
    2477             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2478             :                      errmsg("could not parse RADIUS identifiers list \"%s\"",
    2479             :                             val),
    2480             :                      errcontext("line %d of configuration file \"%s\"",
    2481             :                                 line_num, file_name)));
    2482           0 :             return false;
    2483             :         }
    2484             : 
    2485           0 :         hbaline->radiusidentifiers = parsed_identifiers;
    2486           0 :         hbaline->radiusidentifiers_s = pstrdup(val);
    2487             :     }
    2488             :     else
    2489             :     {
    2490           0 :         ereport(elevel,
    2491             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2492             :                  errmsg("unrecognized authentication option name: \"%s\"",
    2493             :                         name),
    2494             :                  errcontext("line %d of configuration file \"%s\"",
    2495             :                             line_num, file_name)));
    2496           0 :         *err_msg = psprintf("unrecognized authentication option name: \"%s\"",
    2497             :                             name);
    2498           0 :         return false;
    2499             :     }
    2500         532 :     return true;
    2501             : }
    2502             : 
    2503             : /*
    2504             :  *  Scan the pre-parsed hba file, looking for a match to the port's connection
    2505             :  *  request.
    2506             :  */
    2507             : static void
    2508       18152 : check_hba(hbaPort *port)
    2509             : {
    2510             :     Oid         roleid;
    2511             :     ListCell   *line;
    2512             :     HbaLine    *hba;
    2513             : 
    2514             :     /* Get the target role's OID.  Note we do not error out for bad role. */
    2515       18152 :     roleid = get_role_oid(port->user_name, true);
    2516             : 
    2517       21190 :     foreach(line, parsed_hba_lines)
    2518             :     {
    2519       21176 :         hba = (HbaLine *) lfirst(line);
    2520             : 
    2521             :         /* Check connection type */
    2522       21176 :         if (hba->conntype == ctLocal)
    2523             :         {
    2524       18668 :             if (port->raddr.addr.ss_family != AF_UNIX)
    2525         284 :                 continue;
    2526             :         }
    2527             :         else
    2528             :         {
    2529        2508 :             if (port->raddr.addr.ss_family == AF_UNIX)
    2530        1364 :                 continue;
    2531             : 
    2532             :             /* Check SSL state */
    2533        1144 :             if (port->ssl_in_use)
    2534             :             {
    2535             :                 /* Connection is SSL, match both "host" and "hostssl" */
    2536         558 :                 if (hba->conntype == ctHostNoSSL)
    2537           0 :                     continue;
    2538             :             }
    2539             :             else
    2540             :             {
    2541             :                 /* Connection is not SSL, match both "host" and "hostnossl" */
    2542         586 :                 if (hba->conntype == ctHostSSL)
    2543          18 :                     continue;
    2544             :             }
    2545             : 
    2546             :             /* Check GSSAPI state */
    2547             : #ifdef ENABLE_GSS
    2548             :             if (port->gss && port->gss->enc &&
    2549             :                 hba->conntype == ctHostNoGSS)
    2550             :                 continue;
    2551             :             else if (!(port->gss && port->gss->enc) &&
    2552             :                      hba->conntype == ctHostGSS)
    2553             :                 continue;
    2554             : #else
    2555        1126 :             if (hba->conntype == ctHostGSS)
    2556           0 :                 continue;
    2557             : #endif
    2558             : 
    2559             :             /* Check IP address */
    2560        1126 :             switch (hba->ip_cmp_method)
    2561             :             {
    2562        1126 :                 case ipCmpMask:
    2563        1126 :                     if (hba->hostname)
    2564             :                     {
    2565           0 :                         if (!check_hostname(port,
    2566           0 :                                             hba->hostname))
    2567           0 :                             continue;
    2568             :                     }
    2569             :                     else
    2570             :                     {
    2571        1126 :                         if (!check_ip(&port->raddr,
    2572        1126 :                                       (struct sockaddr *) &hba->addr,
    2573        1126 :                                       (struct sockaddr *) &hba->mask))
    2574         284 :                             continue;
    2575             :                     }
    2576         842 :                     break;
    2577           0 :                 case ipCmpAll:
    2578           0 :                     break;
    2579           0 :                 case ipCmpSameHost:
    2580             :                 case ipCmpSameNet:
    2581           0 :                     if (!check_same_host_or_net(&port->raddr,
    2582             :                                                 hba->ip_cmp_method))
    2583           0 :                         continue;
    2584           0 :                     break;
    2585           0 :                 default:
    2586             :                     /* shouldn't get here, but deem it no-match if so */
    2587           0 :                     continue;
    2588             :             }
    2589             :         }                       /* != ctLocal */
    2590             : 
    2591             :         /* Check database and role */
    2592       19226 :         if (!check_db(port->database_name, port->user_name, roleid,
    2593             :                       hba->databases))
    2594         974 :             continue;
    2595             : 
    2596       18252 :         if (!check_role(port->user_name, roleid, hba->roles, false))
    2597         114 :             continue;
    2598             : 
    2599             :         /* Found a record that matched! */
    2600       18138 :         port->hba = hba;
    2601       18138 :         return;
    2602             :     }
    2603             : 
    2604             :     /* If no matching entry was found, then implicitly reject. */
    2605          14 :     hba = palloc0(sizeof(HbaLine));
    2606          14 :     hba->auth_method = uaImplicitReject;
    2607          14 :     port->hba = hba;
    2608             : }
    2609             : 
    2610             : /*
    2611             :  * Read the config file and create a List of HbaLine records for the contents.
    2612             :  *
    2613             :  * The configuration is read into a temporary list, and if any parse error
    2614             :  * occurs the old list is kept in place and false is returned.  Only if the
    2615             :  * whole file parses OK is the list replaced, and the function returns true.
    2616             :  *
    2617             :  * On a false result, caller will take care of reporting a FATAL error in case
    2618             :  * this is the initial startup.  If it happens on reload, we just keep running
    2619             :  * with the old data.
    2620             :  */
    2621             : bool
    2622        1392 : load_hba(void)
    2623             : {
    2624             :     FILE       *file;
    2625        1392 :     List       *hba_lines = NIL;
    2626             :     ListCell   *line;
    2627        1392 :     List       *new_parsed_lines = NIL;
    2628        1392 :     bool        ok = true;
    2629             :     MemoryContext oldcxt;
    2630             :     MemoryContext hbacxt;
    2631             : 
    2632        1392 :     file = open_auth_file(HbaFileName, LOG, 0, NULL);
    2633        1392 :     if (file == NULL)
    2634             :     {
    2635             :         /* error already logged */
    2636           0 :         return false;
    2637             :     }
    2638             : 
    2639        1392 :     tokenize_auth_file(HbaFileName, file, &hba_lines, LOG, 0);
    2640             : 
    2641             :     /* Now parse all the lines */
    2642             :     Assert(PostmasterContext);
    2643        1392 :     hbacxt = AllocSetContextCreate(PostmasterContext,
    2644             :                                    "hba parser context",
    2645             :                                    ALLOCSET_SMALL_SIZES);
    2646        1392 :     oldcxt = MemoryContextSwitchTo(hbacxt);
    2647        9338 :     foreach(line, hba_lines)
    2648             :     {
    2649        7946 :         TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
    2650             :         HbaLine    *newline;
    2651             : 
    2652             :         /* don't parse lines that already have errors */
    2653        7946 :         if (tok_line->err_msg != NULL)
    2654             :         {
    2655           0 :             ok = false;
    2656           0 :             continue;
    2657             :         }
    2658             : 
    2659        7946 :         if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
    2660             :         {
    2661             :             /* Parse error; remember there's trouble */
    2662           0 :             ok = false;
    2663             : 
    2664             :             /*
    2665             :              * Keep parsing the rest of the file so we can report errors on
    2666             :              * more than the first line.  Error has already been logged, no
    2667             :              * need for more chatter here.
    2668             :              */
    2669           0 :             continue;
    2670             :         }
    2671             : 
    2672        7946 :         new_parsed_lines = lappend(new_parsed_lines, newline);
    2673             :     }
    2674             : 
    2675             :     /*
    2676             :      * A valid HBA file must have at least one entry; else there's no way to
    2677             :      * connect to the postmaster.  But only complain about this if we didn't
    2678             :      * already have parsing errors.
    2679             :      */
    2680        1392 :     if (ok && new_parsed_lines == NIL)
    2681             :     {
    2682           0 :         ereport(LOG,
    2683             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2684             :                  errmsg("configuration file \"%s\" contains no entries",
    2685             :                         HbaFileName)));
    2686           0 :         ok = false;
    2687             :     }
    2688             : 
    2689             :     /* Free tokenizer memory */
    2690        1392 :     free_auth_file(file, 0);
    2691        1392 :     MemoryContextSwitchTo(oldcxt);
    2692             : 
    2693        1392 :     if (!ok)
    2694             :     {
    2695             :         /*
    2696             :          * File contained one or more errors, so bail out.
    2697             :          * MemoryContextDelete is enough to clean up everything, including
    2698             :          * regexes.
    2699             :          */
    2700           0 :         MemoryContextDelete(hbacxt);
    2701           0 :         return false;
    2702             :     }
    2703             : 
    2704             :     /* Loaded new file successfully, replace the one we use */
    2705        1392 :     if (parsed_hba_context != NULL)
    2706         206 :         MemoryContextDelete(parsed_hba_context);
    2707        1392 :     parsed_hba_context = hbacxt;
    2708        1392 :     parsed_hba_lines = new_parsed_lines;
    2709             : 
    2710        1392 :     return true;
    2711             : }
    2712             : 
    2713             : 
    2714             : /*
    2715             :  * Parse one tokenised line from the ident config file and store the result in
    2716             :  * an IdentLine structure.
    2717             :  *
    2718             :  * If parsing fails, log a message at ereport level elevel, store an error
    2719             :  * string in tok_line->err_msg and return NULL.
    2720             :  *
    2721             :  * If ident_user is a regular expression (ie. begins with a slash), it is
    2722             :  * compiled and stored in IdentLine structure.
    2723             :  *
    2724             :  * Note: this function leaks memory when an error occurs.  Caller is expected
    2725             :  * to have set a memory context that will be reset if this function returns
    2726             :  * NULL.
    2727             :  */
    2728             : IdentLine *
    2729         178 : parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
    2730             : {
    2731         178 :     int         line_num = tok_line->line_num;
    2732         178 :     char       *file_name = tok_line->file_name;
    2733         178 :     char      **err_msg = &tok_line->err_msg;
    2734             :     ListCell   *field;
    2735             :     List       *tokens;
    2736             :     AuthToken  *token;
    2737             :     IdentLine  *parsedline;
    2738             : 
    2739             :     Assert(tok_line->fields != NIL);
    2740         178 :     field = list_head(tok_line->fields);
    2741             : 
    2742         178 :     parsedline = palloc0(sizeof(IdentLine));
    2743         178 :     parsedline->linenumber = line_num;
    2744             : 
    2745             :     /* Get the map token (must exist) */
    2746         178 :     tokens = lfirst(field);
    2747         178 :     IDENT_MULTI_VALUE(tokens);
    2748         178 :     token = linitial(tokens);
    2749         178 :     parsedline->usermap = pstrdup(token->string);
    2750             : 
    2751             :     /* Get the ident user token */
    2752         178 :     field = lnext(tok_line->fields, field);
    2753         178 :     IDENT_FIELD_ABSENT(field);
    2754         178 :     tokens = lfirst(field);
    2755         178 :     IDENT_MULTI_VALUE(tokens);
    2756         178 :     token = linitial(tokens);
    2757             : 
    2758             :     /* Copy the ident user token */
    2759         178 :     parsedline->system_user = copy_auth_token(token);
    2760             : 
    2761             :     /* Get the PG rolename token */
    2762         178 :     field = lnext(tok_line->fields, field);
    2763         178 :     IDENT_FIELD_ABSENT(field);
    2764         178 :     tokens = lfirst(field);
    2765         178 :     IDENT_MULTI_VALUE(tokens);
    2766         178 :     token = linitial(tokens);
    2767         178 :     parsedline->pg_user = copy_auth_token(token);
    2768             : 
    2769             :     /*
    2770             :      * Now that the field validation is done, compile a regex from the user
    2771             :      * tokens, if necessary.
    2772             :      */
    2773         178 :     if (regcomp_auth_token(parsedline->system_user, file_name, line_num,
    2774             :                            err_msg, elevel))
    2775             :     {
    2776             :         /* err_msg includes the error to report */
    2777           0 :         return NULL;
    2778             :     }
    2779             : 
    2780         178 :     if (regcomp_auth_token(parsedline->pg_user, file_name, line_num,
    2781             :                            err_msg, elevel))
    2782             :     {
    2783             :         /* err_msg includes the error to report */
    2784           0 :         return NULL;
    2785             :     }
    2786             : 
    2787         178 :     return parsedline;
    2788             : }
    2789             : 
    2790             : /*
    2791             :  *  Process one line from the parsed ident config lines.
    2792             :  *
    2793             :  *  Compare input parsed ident line to the needed map, pg_user and system_user.
    2794             :  *  *found_p and *error_p are set according to our results.
    2795             :  */
    2796             : static void
    2797          50 : check_ident_usermap(IdentLine *identLine, const char *usermap_name,
    2798             :                     const char *pg_user, const char *system_user,
    2799             :                     bool case_insensitive, bool *found_p, bool *error_p)
    2800             : {
    2801             :     Oid         roleid;
    2802             : 
    2803          50 :     *found_p = false;
    2804          50 :     *error_p = false;
    2805             : 
    2806          50 :     if (strcmp(identLine->usermap, usermap_name) != 0)
    2807             :         /* Line does not match the map name we're looking for, so just abort */
    2808           6 :         return;
    2809             : 
    2810             :     /* Get the target role's OID.  Note we do not error out for bad role. */
    2811          44 :     roleid = get_role_oid(pg_user, true);
    2812             : 
    2813             :     /* Match? */
    2814          44 :     if (token_has_regexp(identLine->system_user))
    2815             :     {
    2816             :         /*
    2817             :          * Process the system username as a regular expression that returns
    2818             :          * exactly one match. This is replaced for \1 in the database username
    2819             :          * string, if present.
    2820             :          */
    2821             :         int         r;
    2822             :         regmatch_t  matches[2];
    2823             :         char       *ofs;
    2824             :         AuthToken  *expanded_pg_user_token;
    2825          24 :         bool        created_temporary_token = false;
    2826             : 
    2827          24 :         r = regexec_auth_token(system_user, identLine->system_user, 2, matches);
    2828          24 :         if (r)
    2829             :         {
    2830             :             char        errstr[100];
    2831             : 
    2832           2 :             if (r != REG_NOMATCH)
    2833             :             {
    2834             :                 /* REG_NOMATCH is not an error, everything else is */
    2835           0 :                 pg_regerror(r, identLine->system_user->regex, errstr, sizeof(errstr));
    2836           0 :                 ereport(LOG,
    2837             :                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    2838             :                          errmsg("regular expression match for \"%s\" failed: %s",
    2839             :                                 identLine->system_user->string + 1, errstr)));
    2840           0 :                 *error_p = true;
    2841             :             }
    2842           2 :             return;
    2843             :         }
    2844             : 
    2845             :         /*
    2846             :          * Replace \1 with the first captured group unless the field already
    2847             :          * has some special meaning, like a group membership or a regexp-based
    2848             :          * check.
    2849             :          */
    2850          22 :         if (!token_is_member_check(identLine->pg_user) &&
    2851          16 :             !token_has_regexp(identLine->pg_user) &&
    2852          12 :             (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL)
    2853           4 :         {
    2854             :             char       *expanded_pg_user;
    2855             :             int         offset;
    2856             : 
    2857             :             /* substitution of the first argument requested */
    2858           6 :             if (matches[1].rm_so < 0)
    2859             :             {
    2860           2 :                 ereport(LOG,
    2861             :                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    2862             :                          errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
    2863             :                                 identLine->system_user->string + 1, identLine->pg_user->string)));
    2864           2 :                 *error_p = true;
    2865           2 :                 return;
    2866             :             }
    2867             : 
    2868             :             /*
    2869             :              * length: original length minus length of \1 plus length of match
    2870             :              * plus null terminator
    2871             :              */
    2872           4 :             expanded_pg_user = palloc0(strlen(identLine->pg_user->string) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
    2873           4 :             offset = ofs - identLine->pg_user->string;
    2874           4 :             memcpy(expanded_pg_user, identLine->pg_user->string, offset);
    2875           4 :             memcpy(expanded_pg_user + offset,
    2876           4 :                    system_user + matches[1].rm_so,
    2877           4 :                    matches[1].rm_eo - matches[1].rm_so);
    2878           4 :             strcat(expanded_pg_user, ofs + 2);
    2879             : 
    2880             :             /*
    2881             :              * Mark the token as quoted, so it will only be compared literally
    2882             :              * and not for some special meaning, such as "all" or a group
    2883             :              * membership check.
    2884             :              */
    2885           4 :             expanded_pg_user_token = make_auth_token(expanded_pg_user, true);
    2886           4 :             created_temporary_token = true;
    2887           4 :             pfree(expanded_pg_user);
    2888             :         }
    2889             :         else
    2890             :         {
    2891          16 :             expanded_pg_user_token = identLine->pg_user;
    2892             :         }
    2893             : 
    2894             :         /* check the Postgres user */
    2895          20 :         *found_p = check_role(pg_user, roleid,
    2896          20 :                               list_make1(expanded_pg_user_token),
    2897             :                               case_insensitive);
    2898             : 
    2899          20 :         if (created_temporary_token)
    2900           4 :             free_auth_token(expanded_pg_user_token);
    2901             : 
    2902          20 :         return;
    2903             :     }
    2904             :     else
    2905             :     {
    2906             :         /*
    2907             :          * Not a regular expression, so make a complete match.  If the system
    2908             :          * user does not match, just leave.
    2909             :          */
    2910          20 :         if (case_insensitive)
    2911             :         {
    2912           0 :             if (!token_matches_insensitive(identLine->system_user,
    2913             :                                            system_user))
    2914           0 :                 return;
    2915             :         }
    2916             :         else
    2917             :         {
    2918          20 :             if (!token_matches(identLine->system_user, system_user))
    2919           0 :                 return;
    2920             :         }
    2921             : 
    2922             :         /* check the Postgres user */
    2923          20 :         *found_p = check_role(pg_user, roleid,
    2924          20 :                               list_make1(identLine->pg_user),
    2925             :                               case_insensitive);
    2926             :     }
    2927             : }
    2928             : 
    2929             : 
    2930             : /*
    2931             :  *  Scan the (pre-parsed) ident usermap file line by line, looking for a match
    2932             :  *
    2933             :  *  See if the system user with ident username "system_user" is allowed to act as
    2934             :  *  Postgres user "pg_user" according to usermap "usermap_name".
    2935             :  *
    2936             :  *  Special case: Usermap NULL, equivalent to what was previously called
    2937             :  *  "sameuser" or "samerole", means don't look in the usermap file.
    2938             :  *  That's an implied map wherein "pg_user" must be identical to
    2939             :  *  "system_user" in order to be authorized.
    2940             :  *
    2941             :  *  Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
    2942             :  */
    2943             : int
    2944         108 : check_usermap(const char *usermap_name,
    2945             :               const char *pg_user,
    2946             :               const char *system_user,
    2947             :               bool case_insensitive)
    2948             : {
    2949         108 :     bool        found_entry = false,
    2950         108 :                 error = false;
    2951             : 
    2952         108 :     if (usermap_name == NULL || usermap_name[0] == '\0')
    2953             :     {
    2954          64 :         if (case_insensitive)
    2955             :         {
    2956           0 :             if (pg_strcasecmp(pg_user, system_user) == 0)
    2957           0 :                 return STATUS_OK;
    2958             :         }
    2959             :         else
    2960             :         {
    2961          64 :             if (strcmp(pg_user, system_user) == 0)
    2962          58 :                 return STATUS_OK;
    2963             :         }
    2964           6 :         ereport(LOG,
    2965             :                 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
    2966             :                         pg_user, system_user)));
    2967           6 :         return STATUS_ERROR;
    2968             :     }
    2969             :     else
    2970             :     {
    2971             :         ListCell   *line_cell;
    2972             : 
    2973          58 :         foreach(line_cell, parsed_ident_lines)
    2974             :         {
    2975          50 :             check_ident_usermap(lfirst(line_cell), usermap_name,
    2976             :                                 pg_user, system_user, case_insensitive,
    2977             :                                 &found_entry, &error);
    2978          50 :             if (found_entry || error)
    2979             :                 break;
    2980             :         }
    2981             :     }
    2982          44 :     if (!found_entry && !error)
    2983             :     {
    2984           8 :         ereport(LOG,
    2985             :                 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
    2986             :                         usermap_name, pg_user, system_user)));
    2987             :     }
    2988          44 :     return found_entry ? STATUS_OK : STATUS_ERROR;
    2989             : }
    2990             : 
    2991             : 
    2992             : /*
    2993             :  * Read the ident config file and create a List of IdentLine records for
    2994             :  * the contents.
    2995             :  *
    2996             :  * This works the same as load_hba(), but for the user config file.
    2997             :  */
    2998             : bool
    2999        1392 : load_ident(void)
    3000             : {
    3001             :     FILE       *file;
    3002        1392 :     List       *ident_lines = NIL;
    3003             :     ListCell   *line_cell;
    3004        1392 :     List       *new_parsed_lines = NIL;
    3005        1392 :     bool        ok = true;
    3006             :     MemoryContext oldcxt;
    3007             :     MemoryContext ident_context;
    3008             :     IdentLine  *newline;
    3009             : 
    3010             :     /* not FATAL ... we just won't do any special ident maps */
    3011        1392 :     file = open_auth_file(IdentFileName, LOG, 0, NULL);
    3012        1392 :     if (file == NULL)
    3013             :     {
    3014             :         /* error already logged */
    3015           0 :         return false;
    3016             :     }
    3017             : 
    3018        1392 :     tokenize_auth_file(IdentFileName, file, &ident_lines, LOG, 0);
    3019             : 
    3020             :     /* Now parse all the lines */
    3021             :     Assert(PostmasterContext);
    3022        1392 :     ident_context = AllocSetContextCreate(PostmasterContext,
    3023             :                                           "ident parser context",
    3024             :                                           ALLOCSET_SMALL_SIZES);
    3025        1392 :     oldcxt = MemoryContextSwitchTo(ident_context);
    3026        1570 :     foreach(line_cell, ident_lines)
    3027             :     {
    3028         178 :         TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line_cell);
    3029             : 
    3030             :         /* don't parse lines that already have errors */
    3031         178 :         if (tok_line->err_msg != NULL)
    3032             :         {
    3033           0 :             ok = false;
    3034           0 :             continue;
    3035             :         }
    3036             : 
    3037         178 :         if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
    3038             :         {
    3039             :             /* Parse error; remember there's trouble */
    3040           0 :             ok = false;
    3041             : 
    3042             :             /*
    3043             :              * Keep parsing the rest of the file so we can report errors on
    3044             :              * more than the first line.  Error has already been logged, no
    3045             :              * need for more chatter here.
    3046             :              */
    3047           0 :             continue;
    3048             :         }
    3049             : 
    3050         178 :         new_parsed_lines = lappend(new_parsed_lines, newline);
    3051             :     }
    3052             : 
    3053             :     /* Free tokenizer memory */
    3054        1392 :     free_auth_file(file, 0);
    3055        1392 :     MemoryContextSwitchTo(oldcxt);
    3056             : 
    3057        1392 :     if (!ok)
    3058             :     {
    3059             :         /*
    3060             :          * File contained one or more errors, so bail out.
    3061             :          * MemoryContextDelete is enough to clean up everything, including
    3062             :          * regexes.
    3063             :          */
    3064           0 :         MemoryContextDelete(ident_context);
    3065           0 :         return false;
    3066             :     }
    3067             : 
    3068             :     /* Loaded new file successfully, replace the one we use */
    3069        1392 :     if (parsed_ident_context != NULL)
    3070         206 :         MemoryContextDelete(parsed_ident_context);
    3071             : 
    3072        1392 :     parsed_ident_context = ident_context;
    3073        1392 :     parsed_ident_lines = new_parsed_lines;
    3074             : 
    3075        1392 :     return true;
    3076             : }
    3077             : 
    3078             : 
    3079             : 
    3080             : /*
    3081             :  *  Determine what authentication method should be used when accessing database
    3082             :  *  "database" from frontend "raddr", user "user".  Return the method and
    3083             :  *  an optional argument (stored in fields of *port), and STATUS_OK.
    3084             :  *
    3085             :  *  If the file does not contain any entry matching the request, we return
    3086             :  *  method = uaImplicitReject.
    3087             :  */
    3088             : void
    3089       18152 : hba_getauthmethod(hbaPort *port)
    3090             : {
    3091       18152 :     check_hba(port);
    3092       18152 : }
    3093             : 
    3094             : 
    3095             : /*
    3096             :  * Return the name of the auth method in use ("gss", "md5", "trust", etc.).
    3097             :  *
    3098             :  * The return value is statically allocated (see the UserAuthName array) and
    3099             :  * should not be freed.
    3100             :  */
    3101             : const char *
    3102         462 : hba_authname(UserAuth auth_method)
    3103             : {
    3104         462 :     return UserAuthName[auth_method];
    3105             : }

Generated by: LCOV version 1.14