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

Generated by: LCOV version 1.16