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

Generated by: LCOV version 1.13