LCOV - code coverage report
Current view: top level - src/backend/libpq - hba.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 64.9 % 991 643
Test Date: 2026-07-03 19:57:34 Functions: 82.4 % 34 28
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 51.2 % 942 482

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

Generated by: LCOV version 2.0-1