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

Generated by: LCOV version 1.13