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

Generated by: LCOV version 2.0-1