LCOV - code coverage report
Current view: top level - src/backend/libpq - hba.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 27.4 % 1305 357
Test Date: 2026-01-26 10:56:24 Functions: 50.0 % 34 17
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 9.4 % 1837 172

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

Generated by: LCOV version 2.3.2-1