LCOV - code coverage report
Current view: top level - src/backend/libpq - hba.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 64.9 % 991 643
Test Date: 2026-05-02 10:16:34 Functions: 82.4 % 34 28
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1