LCOV - code coverage report
Current view: top level - src/backend/libpq - hba.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 331 1026 32.3 %
Date: 2017-09-29 13:40:31 Functions: 17 31 54.8 %
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-2017, 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-sha256",
     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, char *optionname,
     148             :                           List *masters, 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        1340 : pg_isblank(const char c)
     161             : {
     162        1340 :     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         262 : 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         262 :     char       *start_buf = buf;
     201         262 :     char       *end_buf = buf + (bufsz - 1);
     202         262 :     bool        in_quote = false;
     203         262 :     bool        was_quote = false;
     204         262 :     bool        saw_quote = false;
     205             : 
     206         262 :     Assert(end_buf > start_buf);
     207             : 
     208         262 :     *initial_quote = false;
     209         262 :     *terminating_comma = false;
     210             : 
     211             :     /* Move over any whitespace and commas preceding the next token */
     212         262 :     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        1390 :     while (c != '\0' &&
     220         602 :            (!pg_isblank(c) || in_quote))
     221             :     {
     222             :         /* skip comments to EOL */
     223         514 :         if (c == '#' && !in_quote)
     224             :         {
     225         206 :             while ((c = (*(*lineptr)++)) != '\0')
     226             :                 ;
     227         206 :             break;
     228             :         }
     229             : 
     230         308 :         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         308 :         if (c == ',' && !in_quote)
     248             :         {
     249           0 :             *terminating_comma = true;
     250           0 :             break;
     251             :         }
     252             : 
     253         308 :         if (c != '"' || was_quote)
     254         308 :             *buf++ = c;
     255             : 
     256             :         /* Literal double-quote is two double-quotes */
     257         308 :         if (in_quote && c == '"')
     258           0 :             was_quote = !was_quote;
     259             :         else
     260         308 :             was_quote = false;
     261             : 
     262         308 :         if (c == '"')
     263             :         {
     264           0 :             in_quote = !in_quote;
     265           0 :             saw_quote = true;
     266           0 :             if (buf == start_buf)
     267           0 :                 *initial_quote = true;
     268             :         }
     269             : 
     270         308 :         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         262 :     (*lineptr)--;
     278             : 
     279         262 :     *buf = '\0';
     280             : 
     281         262 :     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          80 : make_hba_token(const char *token, bool quoted)
     289             : {
     290             :     HbaToken   *hbatoken;
     291             :     int         toklen;
     292             : 
     293          80 :     toklen = strlen(token);
     294             :     /* we copy string into same palloc block as the struct */
     295          80 :     hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
     296          80 :     hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
     297          80 :     hbatoken->quoted = quoted;
     298          80 :     memcpy(hbatoken->string, token, toklen + 1);
     299             : 
     300          80 :     return hbatoken;
     301             : }
     302             : 
     303             : /*
     304             :  * Copy a HbaToken struct into freshly palloc'd memory.
     305             :  */
     306             : static HbaToken *
     307          24 : copy_hba_token(HbaToken *in)
     308             : {
     309          24 :     HbaToken   *out = make_hba_token(in->string, in->quoted);
     310             : 
     311          24 :     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         262 : 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         262 :     List       *tokens = NIL;
     337             : 
     338             :     do
     339             :     {
     340         262 :         if (!next_token(lineptr, buf, sizeof(buf),
     341             :                         &initial_quote, &trailing_comma,
     342             :                         elevel, err_msg))
     343         206 :             break;
     344             : 
     345             :         /* Is this referencing a file? */
     346          56 :         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          56 :             tokens = lappend(tokens, make_hba_token(buf, initial_quote));
     351          56 :     } while (trailing_comma && (*err_msg == NULL));
     352             : 
     353         262 :     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           3 : tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
     471             : {
     472           3 :     int         line_number = 1;
     473             :     MemoryContext linecxt;
     474             :     MemoryContext oldcxt;
     475             : 
     476           3 :     linecxt = AllocSetContextCreate(CurrentMemoryContext,
     477             :                                     "tokenize_file",
     478             :                                     ALLOCSET_SMALL_SIZES);
     479           3 :     oldcxt = MemoryContextSwitchTo(linecxt);
     480             : 
     481           3 :     *tok_lines = NIL;
     482             : 
     483         234 :     while (!feof(file) && !ferror(file))
     484             :     {
     485             :         char        rawline[MAX_LINE];
     486             :         char       *lineptr;
     487         231 :         List       *current_line = NIL;
     488         231 :         char       *err_msg = NULL;
     489             : 
     490         231 :         if (!fgets(rawline, sizeof(rawline), file))
     491             :         {
     492           3 :             int         save_errno = errno;
     493             : 
     494           3 :             if (!ferror(file))
     495           6 :                 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         228 :         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         228 :         lineptr = rawline + strlen(rawline) - 1;
     517         684 :         while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r'))
     518         228 :             *lineptr-- = '\0';
     519             : 
     520             :         /* Parse fields */
     521         228 :         lineptr = rawline;
     522         718 :         while (*lineptr && err_msg == NULL)
     523             :         {
     524             :             List       *current_field;
     525             : 
     526         262 :             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         262 :             if (current_field != NIL)
     530          56 :                 current_line = lappend(current_line, current_field);
     531             :         }
     532             : 
     533             :         /* Reached EOL; emit line to TokenizedLine list unless it's boring */
     534         228 :         if (current_line != NIL || err_msg != NULL)
     535             :         {
     536             :             TokenizedLine *tok_line;
     537             : 
     538          12 :             tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine));
     539          12 :             tok_line->fields = current_line;
     540          12 :             tok_line->line_num = line_number;
     541          12 :             tok_line->raw_line = pstrdup(rawline);
     542          12 :             tok_line->err_msg = err_msg;
     543          12 :             *tok_lines = lappend(*tok_lines, tok_line);
     544             :         }
     545             : 
     546         228 :         line_number++;
     547             :     }
     548             : 
     549           3 :     MemoryContextSwitchTo(oldcxt);
     550             : 
     551           3 :     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         215 : check_role(const char *role, Oid roleid, List *tokens)
     587             : {
     588             :     ListCell   *cell;
     589             :     HbaToken   *tok;
     590             : 
     591         215 :     foreach(cell, tokens)
     592             :     {
     593         215 :         tok = lfirst(cell);
     594         215 :         if (!tok->quoted && tok->string[0] == '+')
     595             :         {
     596           0 :             if (is_member(roleid, tok->string + 1))
     597           0 :                 return true;
     598             :         }
     599         430 :         else if (token_matches(tok, role) ||
     600         430 :                  token_is_keyword(tok, "all"))
     601         215 :             return true;
     602             :     }
     603           0 :     return false;
     604             : }
     605             : 
     606             : /*
     607             :  * Check to see if db/role combination matches HbaToken list.
     608             :  */
     609             : static bool
     610         215 : check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
     611             : {
     612             :     ListCell   *cell;
     613             :     HbaToken   *tok;
     614             : 
     615         215 :     foreach(cell, tokens)
     616             :     {
     617         215 :         tok = lfirst(cell);
     618         215 :         if (am_walsender && !am_db_walsender)
     619             :         {
     620             :             /*
     621             :              * physical replication walsender connections can only match
     622             :              * replication keyword
     623             :              */
     624           0 :             if (token_is_keyword(tok, "replication"))
     625           0 :                 return true;
     626             :         }
     627         215 :         else if (token_is_keyword(tok, "all"))
     628         215 :             return true;
     629           0 :         else if (token_is_keyword(tok, "sameuser"))
     630             :         {
     631           0 :             if (strcmp(dbname, role) == 0)
     632           0 :                 return true;
     633             :         }
     634           0 :         else if (token_is_keyword(tok, "samegroup") ||
     635           0 :                  token_is_keyword(tok, "samerole"))
     636             :         {
     637           0 :             if (is_member(roleid, dbname))
     638           0 :                 return true;
     639             :         }
     640           0 :         else if (token_is_keyword(tok, "replication"))
     641           0 :             continue;           /* never match this if not walsender */
     642           0 :         else if (token_matches(tok, dbname))
     643           0 :             return true;
     644             :     }
     645           0 :     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           0 : check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
     787             : {
     788           0 :     if (raddr->addr.ss_family == addr->sa_family &&
     789           0 :         pg_range_sockaddr(&raddr->addr,
     790             :                           (struct sockaddr_storage *) addr,
     791             :                           (struct sockaddr_storage *) mask))
     792           0 :         return true;
     793           0 :     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          12 : parse_hba_line(TokenizedLine *tok_line, int elevel)
     946             : {
     947          12 :     int         line_num = tok_line->line_num;
     948          12 :     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          12 :     parsedline = palloc0(sizeof(HbaLine));
     962          12 :     parsedline->linenumber = line_num;
     963          12 :     parsedline->rawline = pstrdup(tok_line->raw_line);
     964             : 
     965             :     /* Check the record type. */
     966          12 :     Assert(tok_line->fields != NIL);
     967          12 :     field = list_head(tok_line->fields);
     968          12 :     tokens = lfirst(field);
     969          12 :     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          12 :     token = linitial(tokens);
     981          12 :     if (strcmp(token->string, "local") == 0)
     982             :     {
     983             : #ifdef HAVE_UNIX_SOCKETS
     984           4 :         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           8 :     else if (strcmp(token->string, "host") == 0 ||
     996           0 :              strcmp(token->string, "hostssl") == 0 ||
     997           0 :              strcmp(token->string, "hostnossl") == 0)
     998             :     {
     999             : 
    1000          16 :         if (token->string[4] == 's') /* "hostssl" */
    1001             :         {
    1002           0 :             parsedline->conntype = ctHostSSL;
    1003             :             /* Log a warning if SSL support is not active */
    1004             : #ifdef USE_SSL
    1005             :             if (!EnableSSL)
    1006             :             {
    1007             :                 ereport(elevel,
    1008             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1009             :                          errmsg("hostssl record cannot match because SSL is disabled"),
    1010             :                          errhint("Set ssl = on in postgresql.conf."),
    1011             :                          errcontext("line %d of configuration file \"%s\"",
    1012             :                                     line_num, HbaFileName)));
    1013             :                 *err_msg = "hostssl record cannot match because SSL is disabled";
    1014             :             }
    1015             : #else
    1016           0 :             ereport(elevel,
    1017             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1018             :                      errmsg("hostssl record cannot match because SSL is not supported by this build"),
    1019             :                      errhint("Compile with --with-openssl to use SSL connections."),
    1020             :                      errcontext("line %d of configuration file \"%s\"",
    1021             :                                 line_num, HbaFileName)));
    1022           0 :             *err_msg = "hostssl record cannot match because SSL is not supported by this build";
    1023             : #endif
    1024             :         }
    1025           8 :         else if (token->string[4] == 'n')    /* "hostnossl" */
    1026             :         {
    1027           0 :             parsedline->conntype = ctHostNoSSL;
    1028             :         }
    1029             :         else
    1030             :         {
    1031             :             /* "host" */
    1032           8 :             parsedline->conntype = ctHost;
    1033             :         }
    1034             :     }                           /* record type */
    1035             :     else
    1036             :     {
    1037           0 :         ereport(elevel,
    1038             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1039             :                  errmsg("invalid connection type \"%s\"",
    1040             :                         token->string),
    1041             :                  errcontext("line %d of configuration file \"%s\"",
    1042             :                             line_num, HbaFileName)));
    1043           0 :         *err_msg = psprintf("invalid connection type \"%s\"", token->string);
    1044           0 :         return NULL;
    1045             :     }
    1046             : 
    1047             :     /* Get the databases. */
    1048          12 :     field = lnext(field);
    1049          12 :     if (!field)
    1050             :     {
    1051           0 :         ereport(elevel,
    1052             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1053             :                  errmsg("end-of-line before database specification"),
    1054             :                  errcontext("line %d of configuration file \"%s\"",
    1055             :                             line_num, HbaFileName)));
    1056           0 :         *err_msg = "end-of-line before database specification";
    1057           0 :         return NULL;
    1058             :     }
    1059          12 :     parsedline->databases = NIL;
    1060          12 :     tokens = lfirst(field);
    1061          24 :     foreach(tokencell, tokens)
    1062             :     {
    1063          12 :         parsedline->databases = lappend(parsedline->databases,
    1064          12 :                                         copy_hba_token(lfirst(tokencell)));
    1065             :     }
    1066             : 
    1067             :     /* Get the roles. */
    1068          12 :     field = lnext(field);
    1069          12 :     if (!field)
    1070             :     {
    1071           0 :         ereport(elevel,
    1072             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1073             :                  errmsg("end-of-line before role specification"),
    1074             :                  errcontext("line %d of configuration file \"%s\"",
    1075             :                             line_num, HbaFileName)));
    1076           0 :         *err_msg = "end-of-line before role specification";
    1077           0 :         return NULL;
    1078             :     }
    1079          12 :     parsedline->roles = NIL;
    1080          12 :     tokens = lfirst(field);
    1081          24 :     foreach(tokencell, tokens)
    1082             :     {
    1083          12 :         parsedline->roles = lappend(parsedline->roles,
    1084          12 :                                     copy_hba_token(lfirst(tokencell)));
    1085             :     }
    1086             : 
    1087          12 :     if (parsedline->conntype != ctLocal)
    1088             :     {
    1089             :         /* Read the IP address field. (with or without CIDR netmask) */
    1090           8 :         field = lnext(field);
    1091           8 :         if (!field)
    1092             :         {
    1093           0 :             ereport(elevel,
    1094             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1095             :                      errmsg("end-of-line before IP address specification"),
    1096             :                      errcontext("line %d of configuration file \"%s\"",
    1097             :                                 line_num, HbaFileName)));
    1098           0 :             *err_msg = "end-of-line before IP address specification";
    1099           0 :             return NULL;
    1100             :         }
    1101           8 :         tokens = lfirst(field);
    1102           8 :         if (tokens->length > 1)
    1103             :         {
    1104           0 :             ereport(elevel,
    1105             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1106             :                      errmsg("multiple values specified for host address"),
    1107             :                      errhint("Specify one address range per line."),
    1108             :                      errcontext("line %d of configuration file \"%s\"",
    1109             :                                 line_num, HbaFileName)));
    1110           0 :             *err_msg = "multiple values specified for host address";
    1111           0 :             return NULL;
    1112             :         }
    1113           8 :         token = linitial(tokens);
    1114             : 
    1115           8 :         if (token_is_keyword(token, "all"))
    1116             :         {
    1117           0 :             parsedline->ip_cmp_method = ipCmpAll;
    1118             :         }
    1119           8 :         else if (token_is_keyword(token, "samehost"))
    1120             :         {
    1121             :             /* Any IP on this host is allowed to connect */
    1122           0 :             parsedline->ip_cmp_method = ipCmpSameHost;
    1123             :         }
    1124           8 :         else if (token_is_keyword(token, "samenet"))
    1125             :         {
    1126             :             /* Any IP on the host's subnets is allowed to connect */
    1127           0 :             parsedline->ip_cmp_method = ipCmpSameNet;
    1128             :         }
    1129             :         else
    1130             :         {
    1131             :             /* IP and netmask are specified */
    1132           8 :             parsedline->ip_cmp_method = ipCmpMask;
    1133             : 
    1134             :             /* need a modifiable copy of token */
    1135           8 :             str = pstrdup(token->string);
    1136             : 
    1137             :             /* Check if it has a CIDR suffix and if so isolate it */
    1138           8 :             cidr_slash = strchr(str, '/');
    1139           8 :             if (cidr_slash)
    1140           8 :                 *cidr_slash = '\0';
    1141             : 
    1142             :             /* Get the IP address either way */
    1143           8 :             hints.ai_flags = AI_NUMERICHOST;
    1144           8 :             hints.ai_family = AF_UNSPEC;
    1145           8 :             hints.ai_socktype = 0;
    1146           8 :             hints.ai_protocol = 0;
    1147           8 :             hints.ai_addrlen = 0;
    1148           8 :             hints.ai_canonname = NULL;
    1149           8 :             hints.ai_addr = NULL;
    1150           8 :             hints.ai_next = NULL;
    1151             : 
    1152           8 :             ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
    1153           8 :             if (ret == 0 && gai_result)
    1154           8 :                 memcpy(&parsedline->addr, gai_result->ai_addr,
    1155           8 :                        gai_result->ai_addrlen);
    1156           0 :             else if (ret == EAI_NONAME)
    1157           0 :                 parsedline->hostname = str;
    1158             :             else
    1159             :             {
    1160           0 :                 ereport(elevel,
    1161             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1162             :                          errmsg("invalid IP address \"%s\": %s",
    1163             :                                 str, gai_strerror(ret)),
    1164             :                          errcontext("line %d of configuration file \"%s\"",
    1165             :                                     line_num, HbaFileName)));
    1166           0 :                 *err_msg = psprintf("invalid IP address \"%s\": %s",
    1167             :                                     str, gai_strerror(ret));
    1168           0 :                 if (gai_result)
    1169           0 :                     pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1170           0 :                 return NULL;
    1171             :             }
    1172             : 
    1173           8 :             pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1174             : 
    1175             :             /* Get the netmask */
    1176           8 :             if (cidr_slash)
    1177             :             {
    1178           8 :                 if (parsedline->hostname)
    1179             :                 {
    1180           0 :                     ereport(elevel,
    1181             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1182             :                              errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
    1183             :                                     token->string),
    1184             :                              errcontext("line %d of configuration file \"%s\"",
    1185             :                                         line_num, HbaFileName)));
    1186           0 :                     *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
    1187             :                                         token->string);
    1188           0 :                     return NULL;
    1189             :                 }
    1190             : 
    1191           8 :                 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
    1192           8 :                                           parsedline->addr.ss_family) < 0)
    1193             :                 {
    1194           0 :                     ereport(elevel,
    1195             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1196             :                              errmsg("invalid CIDR mask in address \"%s\"",
    1197             :                                     token->string),
    1198             :                              errcontext("line %d of configuration file \"%s\"",
    1199             :                                         line_num, HbaFileName)));
    1200           0 :                     *err_msg = psprintf("invalid CIDR mask in address \"%s\"",
    1201             :                                         token->string);
    1202           0 :                     return NULL;
    1203             :                 }
    1204           8 :                 pfree(str);
    1205             :             }
    1206           0 :             else if (!parsedline->hostname)
    1207             :             {
    1208             :                 /* Read the mask field. */
    1209           0 :                 pfree(str);
    1210           0 :                 field = lnext(field);
    1211           0 :                 if (!field)
    1212             :                 {
    1213           0 :                     ereport(elevel,
    1214             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1215             :                              errmsg("end-of-line before netmask specification"),
    1216             :                              errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
    1217             :                              errcontext("line %d of configuration file \"%s\"",
    1218             :                                         line_num, HbaFileName)));
    1219           0 :                     *err_msg = "end-of-line before netmask specification";
    1220           0 :                     return NULL;
    1221             :                 }
    1222           0 :                 tokens = lfirst(field);
    1223           0 :                 if (tokens->length > 1)
    1224             :                 {
    1225           0 :                     ereport(elevel,
    1226             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1227             :                              errmsg("multiple values specified for netmask"),
    1228             :                              errcontext("line %d of configuration file \"%s\"",
    1229             :                                         line_num, HbaFileName)));
    1230           0 :                     *err_msg = "multiple values specified for netmask";
    1231           0 :                     return NULL;
    1232             :                 }
    1233           0 :                 token = linitial(tokens);
    1234             : 
    1235           0 :                 ret = pg_getaddrinfo_all(token->string, NULL,
    1236             :                                          &hints, &gai_result);
    1237           0 :                 if (ret || !gai_result)
    1238             :                 {
    1239           0 :                     ereport(elevel,
    1240             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1241             :                              errmsg("invalid IP mask \"%s\": %s",
    1242             :                                     token->string, gai_strerror(ret)),
    1243             :                              errcontext("line %d of configuration file \"%s\"",
    1244             :                                         line_num, HbaFileName)));
    1245           0 :                     *err_msg = psprintf("invalid IP mask \"%s\": %s",
    1246             :                                         token->string, gai_strerror(ret));
    1247           0 :                     if (gai_result)
    1248           0 :                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1249           0 :                     return NULL;
    1250             :                 }
    1251             : 
    1252           0 :                 memcpy(&parsedline->mask, gai_result->ai_addr,
    1253           0 :                        gai_result->ai_addrlen);
    1254           0 :                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1255             : 
    1256           0 :                 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
    1257             :                 {
    1258           0 :                     ereport(elevel,
    1259             :                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1260             :                              errmsg("IP address and mask do not match"),
    1261             :                              errcontext("line %d of configuration file \"%s\"",
    1262             :                                         line_num, HbaFileName)));
    1263           0 :                     *err_msg = "IP address and mask do not match";
    1264           0 :                     return NULL;
    1265             :                 }
    1266             :             }
    1267             :         }
    1268             :     }                           /* != ctLocal */
    1269             : 
    1270             :     /* Get the authentication method */
    1271          12 :     field = lnext(field);
    1272          12 :     if (!field)
    1273             :     {
    1274           0 :         ereport(elevel,
    1275             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1276             :                  errmsg("end-of-line before authentication method"),
    1277             :                  errcontext("line %d of configuration file \"%s\"",
    1278             :                             line_num, HbaFileName)));
    1279           0 :         *err_msg = "end-of-line before authentication method";
    1280           0 :         return NULL;
    1281             :     }
    1282          12 :     tokens = lfirst(field);
    1283          12 :     if (tokens->length > 1)
    1284             :     {
    1285           0 :         ereport(elevel,
    1286             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1287             :                  errmsg("multiple values specified for authentication type"),
    1288             :                  errhint("Specify exactly one authentication type per line."),
    1289             :                  errcontext("line %d of configuration file \"%s\"",
    1290             :                             line_num, HbaFileName)));
    1291           0 :         *err_msg = "multiple values specified for authentication type";
    1292           0 :         return NULL;
    1293             :     }
    1294          12 :     token = linitial(tokens);
    1295             : 
    1296          12 :     unsupauth = NULL;
    1297          12 :     if (strcmp(token->string, "trust") == 0)
    1298          12 :         parsedline->auth_method = uaTrust;
    1299           0 :     else if (strcmp(token->string, "ident") == 0)
    1300           0 :         parsedline->auth_method = uaIdent;
    1301           0 :     else if (strcmp(token->string, "peer") == 0)
    1302           0 :         parsedline->auth_method = uaPeer;
    1303           0 :     else if (strcmp(token->string, "password") == 0)
    1304           0 :         parsedline->auth_method = uaPassword;
    1305           0 :     else if (strcmp(token->string, "gss") == 0)
    1306             : #ifdef ENABLE_GSS
    1307             :         parsedline->auth_method = uaGSS;
    1308             : #else
    1309           0 :         unsupauth = "gss";
    1310             : #endif
    1311           0 :     else if (strcmp(token->string, "sspi") == 0)
    1312             : #ifdef ENABLE_SSPI
    1313             :         parsedline->auth_method = uaSSPI;
    1314             : #else
    1315           0 :         unsupauth = "sspi";
    1316             : #endif
    1317           0 :     else if (strcmp(token->string, "reject") == 0)
    1318           0 :         parsedline->auth_method = uaReject;
    1319           0 :     else if (strcmp(token->string, "md5") == 0)
    1320             :     {
    1321           0 :         if (Db_user_namespace)
    1322             :         {
    1323           0 :             ereport(elevel,
    1324             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1325             :                      errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
    1326             :                      errcontext("line %d of configuration file \"%s\"",
    1327             :                                 line_num, HbaFileName)));
    1328           0 :             *err_msg = "MD5 authentication is not supported when \"db_user_namespace\" is enabled";
    1329           0 :             return NULL;
    1330             :         }
    1331           0 :         parsedline->auth_method = uaMD5;
    1332             :     }
    1333           0 :     else if (strcmp(token->string, "scram-sha-256") == 0)
    1334           0 :         parsedline->auth_method = uaSCRAM;
    1335           0 :     else if (strcmp(token->string, "pam") == 0)
    1336             : #ifdef USE_PAM
    1337             :         parsedline->auth_method = uaPAM;
    1338             : #else
    1339           0 :         unsupauth = "pam";
    1340             : #endif
    1341           0 :     else if (strcmp(token->string, "bsd") == 0)
    1342             : #ifdef USE_BSD_AUTH
    1343             :         parsedline->auth_method = uaBSD;
    1344             : #else
    1345           0 :         unsupauth = "bsd";
    1346             : #endif
    1347           0 :     else if (strcmp(token->string, "ldap") == 0)
    1348             : #ifdef USE_LDAP
    1349             :         parsedline->auth_method = uaLDAP;
    1350             : #else
    1351           0 :         unsupauth = "ldap";
    1352             : #endif
    1353           0 :     else if (strcmp(token->string, "cert") == 0)
    1354             : #ifdef USE_SSL
    1355             :         parsedline->auth_method = uaCert;
    1356             : #else
    1357           0 :         unsupauth = "cert";
    1358             : #endif
    1359           0 :     else if (strcmp(token->string, "radius") == 0)
    1360           0 :         parsedline->auth_method = uaRADIUS;
    1361             :     else
    1362             :     {
    1363           0 :         ereport(elevel,
    1364             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1365             :                  errmsg("invalid authentication method \"%s\"",
    1366             :                         token->string),
    1367             :                  errcontext("line %d of configuration file \"%s\"",
    1368             :                             line_num, HbaFileName)));
    1369           0 :         *err_msg = psprintf("invalid authentication method \"%s\"",
    1370             :                             token->string);
    1371           0 :         return NULL;
    1372             :     }
    1373             : 
    1374          12 :     if (unsupauth)
    1375             :     {
    1376           0 :         ereport(elevel,
    1377             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1378             :                  errmsg("invalid authentication method \"%s\": not supported by this build",
    1379             :                         token->string),
    1380             :                  errcontext("line %d of configuration file \"%s\"",
    1381             :                             line_num, HbaFileName)));
    1382           0 :         *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
    1383             :                             token->string);
    1384           0 :         return NULL;
    1385             :     }
    1386             : 
    1387             :     /*
    1388             :      * XXX: When using ident on local connections, change it to peer, for
    1389             :      * backwards compatibility.
    1390             :      */
    1391          16 :     if (parsedline->conntype == ctLocal &&
    1392           4 :         parsedline->auth_method == uaIdent)
    1393           0 :         parsedline->auth_method = uaPeer;
    1394             : 
    1395             :     /* Invalid authentication combinations */
    1396          16 :     if (parsedline->conntype == ctLocal &&
    1397           4 :         parsedline->auth_method == uaGSS)
    1398             :     {
    1399           0 :         ereport(elevel,
    1400             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1401             :                  errmsg("gssapi authentication is not supported on local sockets"),
    1402             :                  errcontext("line %d of configuration file \"%s\"",
    1403             :                             line_num, HbaFileName)));
    1404           0 :         *err_msg = "gssapi authentication is not supported on local sockets";
    1405           0 :         return NULL;
    1406             :     }
    1407             : 
    1408          20 :     if (parsedline->conntype != ctLocal &&
    1409           8 :         parsedline->auth_method == uaPeer)
    1410             :     {
    1411           0 :         ereport(elevel,
    1412             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1413             :                  errmsg("peer authentication is only supported on local sockets"),
    1414             :                  errcontext("line %d of configuration file \"%s\"",
    1415             :                             line_num, HbaFileName)));
    1416           0 :         *err_msg = "peer authentication is only supported on local sockets";
    1417           0 :         return NULL;
    1418             :     }
    1419             : 
    1420             :     /*
    1421             :      * SSPI authentication can never be enabled on ctLocal connections,
    1422             :      * because it's only supported on Windows, where ctLocal isn't supported.
    1423             :      */
    1424             : 
    1425             : 
    1426          24 :     if (parsedline->conntype != ctHostSSL &&
    1427          12 :         parsedline->auth_method == uaCert)
    1428             :     {
    1429           0 :         ereport(elevel,
    1430             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1431             :                  errmsg("cert authentication is only supported on hostssl connections"),
    1432             :                  errcontext("line %d of configuration file \"%s\"",
    1433             :                             line_num, HbaFileName)));
    1434           0 :         *err_msg = "cert authentication is only supported on hostssl connections";
    1435           0 :         return NULL;
    1436             :     }
    1437             : 
    1438             :     /*
    1439             :      * For GSS and SSPI, set the default value of include_realm to true.
    1440             :      * Having include_realm set to false is dangerous in multi-realm
    1441             :      * situations and is generally considered bad practice.  We keep the
    1442             :      * capability around for backwards compatibility, but we might want to
    1443             :      * remove it at some point in the future.  Users who still need to strip
    1444             :      * the realm off would be better served by using an appropriate regex in a
    1445             :      * pg_ident.conf mapping.
    1446             :      */
    1447          24 :     if (parsedline->auth_method == uaGSS ||
    1448          12 :         parsedline->auth_method == uaSSPI)
    1449           0 :         parsedline->include_realm = true;
    1450             : 
    1451             :     /*
    1452             :      * For SSPI, include_realm defaults to the SAM-compatible domain (aka
    1453             :      * NetBIOS name) and user names instead of the Kerberos principal name for
    1454             :      * compatibility.
    1455             :      */
    1456          12 :     if (parsedline->auth_method == uaSSPI)
    1457             :     {
    1458           0 :         parsedline->compat_realm = true;
    1459           0 :         parsedline->upn_username = false;
    1460             :     }
    1461             : 
    1462             :     /* Parse remaining arguments */
    1463          24 :     while ((field = lnext(field)) != NULL)
    1464             :     {
    1465           0 :         tokens = lfirst(field);
    1466           0 :         foreach(tokencell, tokens)
    1467             :         {
    1468             :             char       *val;
    1469             : 
    1470           0 :             token = lfirst(tokencell);
    1471             : 
    1472           0 :             str = pstrdup(token->string);
    1473           0 :             val = strchr(str, '=');
    1474           0 :             if (val == NULL)
    1475             :             {
    1476             :                 /*
    1477             :                  * Got something that's not a name=value pair.
    1478             :                  */
    1479           0 :                 ereport(elevel,
    1480             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1481             :                          errmsg("authentication option not in name=value format: %s", token->string),
    1482             :                          errcontext("line %d of configuration file \"%s\"",
    1483             :                                     line_num, HbaFileName)));
    1484           0 :                 *err_msg = psprintf("authentication option not in name=value format: %s",
    1485             :                                     token->string);
    1486           0 :                 return NULL;
    1487             :             }
    1488             : 
    1489           0 :             *val++ = '\0';      /* str now holds "name", val holds "value" */
    1490           0 :             if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
    1491             :                 /* parse_hba_auth_opt already logged the error message */
    1492           0 :                 return NULL;
    1493           0 :             pfree(str);
    1494             :         }
    1495             :     }
    1496             : 
    1497             :     /*
    1498             :      * Check if the selected authentication method has any mandatory arguments
    1499             :      * that are not set.
    1500             :      */
    1501          12 :     if (parsedline->auth_method == uaLDAP)
    1502             :     {
    1503           0 :         MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
    1504             : 
    1505             :         /*
    1506             :          * LDAP can operate in two modes: either with a direct bind, using
    1507             :          * ldapprefix and ldapsuffix, or using a search+bind, using
    1508             :          * ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
    1509             :          * Disallow mixing these parameters.
    1510             :          */
    1511           0 :         if (parsedline->ldapprefix || parsedline->ldapsuffix)
    1512             :         {
    1513           0 :             if (parsedline->ldapbasedn ||
    1514           0 :                 parsedline->ldapbinddn ||
    1515           0 :                 parsedline->ldapbindpasswd ||
    1516           0 :                 parsedline->ldapsearchattribute)
    1517             :             {
    1518           0 :                 ereport(elevel,
    1519             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1520             :                          errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"),
    1521             :                          errcontext("line %d of configuration file \"%s\"",
    1522             :                                     line_num, HbaFileName)));
    1523           0 :                 *err_msg = "cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix";
    1524           0 :                 return NULL;
    1525             :             }
    1526             :         }
    1527           0 :         else if (!parsedline->ldapbasedn)
    1528             :         {
    1529           0 :             ereport(elevel,
    1530             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1531             :                      errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
    1532             :                      errcontext("line %d of configuration file \"%s\"",
    1533             :                                 line_num, HbaFileName)));
    1534           0 :             *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
    1535           0 :             return NULL;
    1536             :         }
    1537             :     }
    1538             : 
    1539          12 :     if (parsedline->auth_method == uaRADIUS)
    1540             :     {
    1541           0 :         MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
    1542           0 :         MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
    1543             : 
    1544           0 :         if (list_length(parsedline->radiusservers) < 1)
    1545             :         {
    1546           0 :             ereport(LOG,
    1547             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1548             :                      errmsg("list of RADIUS servers cannot be empty"),
    1549             :                      errcontext("line %d of configuration file \"%s\"",
    1550             :                                 line_num, HbaFileName)));
    1551           0 :             return NULL;
    1552             :         }
    1553             : 
    1554           0 :         if (list_length(parsedline->radiussecrets) < 1)
    1555             :         {
    1556           0 :             ereport(LOG,
    1557             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1558             :                      errmsg("list of RADIUS secrets cannot be empty"),
    1559             :                      errcontext("line %d of configuration file \"%s\"",
    1560             :                                 line_num, HbaFileName)));
    1561           0 :             return NULL;
    1562             :         }
    1563             : 
    1564             :         /*
    1565             :          * Verify length of option lists - each can be 0 (except for secrets,
    1566             :          * but that's already checked above), 1 (use the same value
    1567             :          * everywhere) or the same as the number of servers.
    1568             :          */
    1569           0 :         if (!verify_option_list_length(parsedline->radiussecrets,
    1570             :                                        "RADIUS secrets",
    1571             :                                        parsedline->radiusservers,
    1572             :                                        "RADIUS servers",
    1573             :                                        line_num))
    1574           0 :             return NULL;
    1575           0 :         if (!verify_option_list_length(parsedline->radiusports,
    1576             :                                        "RADIUS ports",
    1577             :                                        parsedline->radiusservers,
    1578             :                                        "RADIUS servers",
    1579             :                                        line_num))
    1580           0 :             return NULL;
    1581           0 :         if (!verify_option_list_length(parsedline->radiusidentifiers,
    1582             :                                        "RADIUS identifiers",
    1583             :                                        parsedline->radiusservers,
    1584             :                                        "RADIUS servers",
    1585             :                                        line_num))
    1586           0 :             return NULL;
    1587             :     }
    1588             : 
    1589             :     /*
    1590             :      * Enforce any parameters implied by other settings.
    1591             :      */
    1592          12 :     if (parsedline->auth_method == uaCert)
    1593             :     {
    1594           0 :         parsedline->clientcert = true;
    1595             :     }
    1596             : 
    1597          12 :     return parsedline;
    1598             : }
    1599             : 
    1600             : 
    1601             : static bool
    1602           0 : verify_option_list_length(List *options, char *optionname, List *masters, char *mastername, int line_num)
    1603             : {
    1604           0 :     if (list_length(options) == 0 ||
    1605           0 :         list_length(options) == 1 ||
    1606           0 :         list_length(options) == list_length(masters))
    1607           0 :         return true;
    1608             : 
    1609           0 :     ereport(LOG,
    1610             :             (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1611             :              errmsg("the number of %s (%i) must be 1 or the same as the number of %s (%i)",
    1612             :                     optionname,
    1613             :                     list_length(options),
    1614             :                     mastername,
    1615             :                     list_length(masters)
    1616             :                     ),
    1617             :              errcontext("line %d of configuration file \"%s\"",
    1618             :                         line_num, HbaFileName)));
    1619           0 :     return false;
    1620             : }
    1621             : 
    1622             : /*
    1623             :  * Parse one name-value pair as an authentication option into the given
    1624             :  * HbaLine.  Return true if we successfully parse the option, false if we
    1625             :  * encounter an error.  In the event of an error, also log a message at
    1626             :  * ereport level elevel, and store a message string into *err_msg.
    1627             :  */
    1628             : static bool
    1629           0 : parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
    1630             :                    int elevel, char **err_msg)
    1631             : {
    1632           0 :     int         line_num = hbaline->linenumber;
    1633             : 
    1634             : #ifdef USE_LDAP
    1635             :     hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
    1636             : #endif
    1637             : 
    1638           0 :     if (strcmp(name, "map") == 0)
    1639             :     {
    1640           0 :         if (hbaline->auth_method != uaIdent &&
    1641           0 :             hbaline->auth_method != uaPeer &&
    1642           0 :             hbaline->auth_method != uaGSS &&
    1643           0 :             hbaline->auth_method != uaSSPI &&
    1644           0 :             hbaline->auth_method != uaCert)
    1645           0 :             INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, and cert"));
    1646           0 :         hbaline->usermap = pstrdup(val);
    1647             :     }
    1648           0 :     else if (strcmp(name, "clientcert") == 0)
    1649             :     {
    1650           0 :         if (hbaline->conntype != ctHostSSL)
    1651             :         {
    1652           0 :             ereport(elevel,
    1653             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1654             :                      errmsg("clientcert can only be configured for \"hostssl\" rows"),
    1655             :                      errcontext("line %d of configuration file \"%s\"",
    1656             :                                 line_num, HbaFileName)));
    1657           0 :             *err_msg = "clientcert can only be configured for \"hostssl\" rows";
    1658           0 :             return false;
    1659             :         }
    1660           0 :         if (strcmp(val, "1") == 0)
    1661             :         {
    1662           0 :             hbaline->clientcert = true;
    1663             :         }
    1664             :         else
    1665             :         {
    1666           0 :             if (hbaline->auth_method == uaCert)
    1667             :             {
    1668           0 :                 ereport(elevel,
    1669             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1670             :                          errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
    1671             :                          errcontext("line %d of configuration file \"%s\"",
    1672             :                                     line_num, HbaFileName)));
    1673           0 :                 *err_msg = "clientcert can not be set to 0 when using \"cert\" authentication";
    1674           0 :                 return false;
    1675             :             }
    1676           0 :             hbaline->clientcert = false;
    1677             :         }
    1678             :     }
    1679           0 :     else if (strcmp(name, "pamservice") == 0)
    1680             :     {
    1681           0 :         REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
    1682           0 :         hbaline->pamservice = pstrdup(val);
    1683             :     }
    1684           0 :     else if (strcmp(name, "pam_use_hostname") == 0)
    1685             :     {
    1686           0 :         REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
    1687           0 :         if (strcmp(val, "1") == 0)
    1688           0 :             hbaline->pam_use_hostname = true;
    1689             :         else
    1690           0 :             hbaline->pam_use_hostname = false;
    1691             : 
    1692             :     }
    1693           0 :     else if (strcmp(name, "ldapurl") == 0)
    1694             :     {
    1695             : #ifdef LDAP_API_FEATURE_X_OPENLDAP
    1696             :         LDAPURLDesc *urldata;
    1697             :         int         rc;
    1698             : #endif
    1699             : 
    1700           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
    1701             : #ifdef LDAP_API_FEATURE_X_OPENLDAP
    1702             :         rc = ldap_url_parse(val, &urldata);
    1703             :         if (rc != LDAP_SUCCESS)
    1704             :         {
    1705             :             ereport(elevel,
    1706             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1707             :                      errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
    1708             :             *err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
    1709             :                                 val, ldap_err2string(rc));
    1710             :             return false;
    1711             :         }
    1712             : 
    1713             :         if (strcmp(urldata->lud_scheme, "ldap") != 0)
    1714             :         {
    1715             :             ereport(elevel,
    1716             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1717             :                      errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
    1718             :             *err_msg = psprintf("unsupported LDAP URL scheme: %s",
    1719             :                                 urldata->lud_scheme);
    1720             :             ldap_free_urldesc(urldata);
    1721             :             return false;
    1722             :         }
    1723             : 
    1724             :         hbaline->ldapserver = pstrdup(urldata->lud_host);
    1725             :         hbaline->ldapport = urldata->lud_port;
    1726             :         hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
    1727             : 
    1728             :         if (urldata->lud_attrs)
    1729             :             hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]);    /* only use first one */
    1730             :         hbaline->ldapscope = urldata->lud_scope;
    1731             :         if (urldata->lud_filter)
    1732             :         {
    1733             :             ereport(elevel,
    1734             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1735             :                      errmsg("filters not supported in LDAP URLs")));
    1736             :             *err_msg = "filters not supported in LDAP URLs";
    1737             :             ldap_free_urldesc(urldata);
    1738             :             return false;
    1739             :         }
    1740             :         ldap_free_urldesc(urldata);
    1741             : #else                           /* not OpenLDAP */
    1742           0 :         ereport(elevel,
    1743             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1744             :                  errmsg("LDAP URLs not supported on this platform")));
    1745           0 :         *err_msg = "LDAP URLs not supported on this platform";
    1746             : #endif                          /* not OpenLDAP */
    1747             :     }
    1748           0 :     else if (strcmp(name, "ldaptls") == 0)
    1749             :     {
    1750           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
    1751           0 :         if (strcmp(val, "1") == 0)
    1752           0 :             hbaline->ldaptls = true;
    1753             :         else
    1754           0 :             hbaline->ldaptls = false;
    1755             :     }
    1756           0 :     else if (strcmp(name, "ldapserver") == 0)
    1757             :     {
    1758           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
    1759           0 :         hbaline->ldapserver = pstrdup(val);
    1760             :     }
    1761           0 :     else if (strcmp(name, "ldapport") == 0)
    1762             :     {
    1763           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
    1764           0 :         hbaline->ldapport = atoi(val);
    1765           0 :         if (hbaline->ldapport == 0)
    1766             :         {
    1767           0 :             ereport(elevel,
    1768             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1769             :                      errmsg("invalid LDAP port number: \"%s\"", val),
    1770             :                      errcontext("line %d of configuration file \"%s\"",
    1771             :                                 line_num, HbaFileName)));
    1772           0 :             *err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
    1773           0 :             return false;
    1774             :         }
    1775             :     }
    1776           0 :     else if (strcmp(name, "ldapbinddn") == 0)
    1777             :     {
    1778           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
    1779           0 :         hbaline->ldapbinddn = pstrdup(val);
    1780             :     }
    1781           0 :     else if (strcmp(name, "ldapbindpasswd") == 0)
    1782             :     {
    1783           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
    1784           0 :         hbaline->ldapbindpasswd = pstrdup(val);
    1785             :     }
    1786           0 :     else if (strcmp(name, "ldapsearchattribute") == 0)
    1787             :     {
    1788           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
    1789           0 :         hbaline->ldapsearchattribute = pstrdup(val);
    1790             :     }
    1791           0 :     else if (strcmp(name, "ldapbasedn") == 0)
    1792             :     {
    1793           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
    1794           0 :         hbaline->ldapbasedn = pstrdup(val);
    1795             :     }
    1796           0 :     else if (strcmp(name, "ldapprefix") == 0)
    1797             :     {
    1798           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
    1799           0 :         hbaline->ldapprefix = pstrdup(val);
    1800             :     }
    1801           0 :     else if (strcmp(name, "ldapsuffix") == 0)
    1802             :     {
    1803           0 :         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
    1804           0 :         hbaline->ldapsuffix = pstrdup(val);
    1805             :     }
    1806           0 :     else if (strcmp(name, "krb_realm") == 0)
    1807             :     {
    1808           0 :         if (hbaline->auth_method != uaGSS &&
    1809           0 :             hbaline->auth_method != uaSSPI)
    1810           0 :             INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
    1811           0 :         hbaline->krb_realm = pstrdup(val);
    1812             :     }
    1813           0 :     else if (strcmp(name, "include_realm") == 0)
    1814             :     {
    1815           0 :         if (hbaline->auth_method != uaGSS &&
    1816           0 :             hbaline->auth_method != uaSSPI)
    1817           0 :             INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
    1818           0 :         if (strcmp(val, "1") == 0)
    1819           0 :             hbaline->include_realm = true;
    1820             :         else
    1821           0 :             hbaline->include_realm = false;
    1822             :     }
    1823           0 :     else if (strcmp(name, "compat_realm") == 0)
    1824             :     {
    1825           0 :         if (hbaline->auth_method != uaSSPI)
    1826           0 :             INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
    1827           0 :         if (strcmp(val, "1") == 0)
    1828           0 :             hbaline->compat_realm = true;
    1829             :         else
    1830           0 :             hbaline->compat_realm = false;
    1831             :     }
    1832           0 :     else if (strcmp(name, "upn_username") == 0)
    1833             :     {
    1834           0 :         if (hbaline->auth_method != uaSSPI)
    1835           0 :             INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
    1836           0 :         if (strcmp(val, "1") == 0)
    1837           0 :             hbaline->upn_username = true;
    1838             :         else
    1839           0 :             hbaline->upn_username = false;
    1840             :     }
    1841           0 :     else if (strcmp(name, "radiusservers") == 0)
    1842             :     {
    1843             :         struct addrinfo *gai_result;
    1844             :         struct addrinfo hints;
    1845             :         int         ret;
    1846             :         List       *parsed_servers;
    1847             :         ListCell   *l;
    1848           0 :         char       *dupval = pstrdup(val);
    1849             : 
    1850           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
    1851             : 
    1852           0 :         if (!SplitIdentifierString(dupval, ',', &parsed_servers))
    1853             :         {
    1854             :             /* syntax error in list */
    1855           0 :             ereport(elevel,
    1856             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1857             :                      errmsg("could not parse RADIUS server list \"%s\"",
    1858             :                             val),
    1859             :                      errcontext("line %d of configuration file \"%s\"",
    1860             :                                 line_num, HbaFileName)));
    1861           0 :             return false;
    1862             :         }
    1863             : 
    1864             :         /* For each entry in the list, translate it */
    1865           0 :         foreach(l, parsed_servers)
    1866             :         {
    1867           0 :             MemSet(&hints, 0, sizeof(hints));
    1868           0 :             hints.ai_socktype = SOCK_DGRAM;
    1869           0 :             hints.ai_family = AF_UNSPEC;
    1870             : 
    1871           0 :             ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
    1872           0 :             if (ret || !gai_result)
    1873             :             {
    1874           0 :                 ereport(elevel,
    1875             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1876             :                          errmsg("could not translate RADIUS server name \"%s\" to address: %s",
    1877             :                                 (char *) lfirst(l), gai_strerror(ret)),
    1878             :                          errcontext("line %d of configuration file \"%s\"",
    1879             :                                     line_num, HbaFileName)));
    1880           0 :                 if (gai_result)
    1881           0 :                     pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1882             : 
    1883           0 :                 list_free(parsed_servers);
    1884           0 :                 return false;
    1885             :             }
    1886           0 :             pg_freeaddrinfo_all(hints.ai_family, gai_result);
    1887             :         }
    1888             : 
    1889             :         /* All entries are OK, so store them */
    1890           0 :         hbaline->radiusservers = parsed_servers;
    1891           0 :         hbaline->radiusservers_s = pstrdup(val);
    1892             :     }
    1893           0 :     else if (strcmp(name, "radiusports") == 0)
    1894             :     {
    1895             :         List       *parsed_ports;
    1896             :         ListCell   *l;
    1897           0 :         char       *dupval = pstrdup(val);
    1898             : 
    1899           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
    1900             : 
    1901           0 :         if (!SplitIdentifierString(dupval, ',', &parsed_ports))
    1902             :         {
    1903           0 :             ereport(elevel,
    1904             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1905             :                      errmsg("could not parse RADIUS port list \"%s\"",
    1906             :                             val),
    1907             :                      errcontext("line %d of configuration file \"%s\"",
    1908             :                                 line_num, HbaFileName)));
    1909           0 :             *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
    1910           0 :             return false;
    1911             :         }
    1912             : 
    1913           0 :         foreach(l, parsed_ports)
    1914             :         {
    1915           0 :             if (atoi(lfirst(l)) == 0)
    1916             :             {
    1917           0 :                 ereport(elevel,
    1918             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1919             :                          errmsg("invalid RADIUS port number: \"%s\"", val),
    1920             :                          errcontext("line %d of configuration file \"%s\"",
    1921             :                                     line_num, HbaFileName)));
    1922             : 
    1923           0 :                 return false;
    1924             :             }
    1925             :         }
    1926           0 :         hbaline->radiusports = parsed_ports;
    1927           0 :         hbaline->radiusports_s = pstrdup(val);
    1928             :     }
    1929           0 :     else if (strcmp(name, "radiussecrets") == 0)
    1930             :     {
    1931             :         List       *parsed_secrets;
    1932           0 :         char       *dupval = pstrdup(val);
    1933             : 
    1934           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
    1935             : 
    1936           0 :         if (!SplitIdentifierString(dupval, ',', &parsed_secrets))
    1937             :         {
    1938             :             /* syntax error in list */
    1939           0 :             ereport(elevel,
    1940             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1941             :                      errmsg("could not parse RADIUS secret list \"%s\"",
    1942             :                             val),
    1943             :                      errcontext("line %d of configuration file \"%s\"",
    1944             :                                 line_num, HbaFileName)));
    1945           0 :             return false;
    1946             :         }
    1947             : 
    1948           0 :         hbaline->radiussecrets = parsed_secrets;
    1949           0 :         hbaline->radiussecrets_s = pstrdup(val);
    1950             :     }
    1951           0 :     else if (strcmp(name, "radiusidentifiers") == 0)
    1952             :     {
    1953             :         List       *parsed_identifiers;
    1954           0 :         char       *dupval = pstrdup(val);
    1955             : 
    1956           0 :         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
    1957             : 
    1958           0 :         if (!SplitIdentifierString(dupval, ',', &parsed_identifiers))
    1959             :         {
    1960             :             /* syntax error in list */
    1961           0 :             ereport(elevel,
    1962             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1963             :                      errmsg("could not parse RADIUS identifiers list \"%s\"",
    1964             :                             val),
    1965             :                      errcontext("line %d of configuration file \"%s\"",
    1966             :                                 line_num, HbaFileName)));
    1967           0 :             return false;
    1968             :         }
    1969             : 
    1970           0 :         hbaline->radiusidentifiers = parsed_identifiers;
    1971           0 :         hbaline->radiusidentifiers_s = pstrdup(val);
    1972             :     }
    1973             :     else
    1974             :     {
    1975           0 :         ereport(elevel,
    1976             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    1977             :                  errmsg("unrecognized authentication option name: \"%s\"",
    1978             :                         name),
    1979             :                  errcontext("line %d of configuration file \"%s\"",
    1980             :                             line_num, HbaFileName)));
    1981           0 :         *err_msg = psprintf("unrecognized authentication option name: \"%s\"",
    1982             :                             name);
    1983           0 :         return false;
    1984             :     }
    1985           0 :     return true;
    1986             : }
    1987             : 
    1988             : /*
    1989             :  *  Scan the pre-parsed hba file, looking for a match to the port's connection
    1990             :  *  request.
    1991             :  */
    1992             : static void
    1993         215 : check_hba(hbaPort *port)
    1994             : {
    1995             :     Oid         roleid;
    1996             :     ListCell   *line;
    1997             :     HbaLine    *hba;
    1998             : 
    1999             :     /* Get the target role's OID.  Note we do not error out for bad role. */
    2000         215 :     roleid = get_role_oid(port->user_name, true);
    2001             : 
    2002         215 :     foreach(line, parsed_hba_lines)
    2003             :     {
    2004         215 :         hba = (HbaLine *) lfirst(line);
    2005             : 
    2006             :         /* Check connection type */
    2007         215 :         if (hba->conntype == ctLocal)
    2008             :         {
    2009         215 :             if (!IS_AF_UNIX(port->raddr.addr.ss_family))
    2010           0 :                 continue;
    2011             :         }
    2012             :         else
    2013             :         {
    2014           0 :             if (IS_AF_UNIX(port->raddr.addr.ss_family))
    2015           0 :                 continue;
    2016             : 
    2017             :             /* Check SSL state */
    2018           0 :             if (port->ssl_in_use)
    2019             :             {
    2020             :                 /* Connection is SSL, match both "host" and "hostssl" */
    2021           0 :                 if (hba->conntype == ctHostNoSSL)
    2022           0 :                     continue;
    2023             :             }
    2024             :             else
    2025             :             {
    2026             :                 /* Connection is not SSL, match both "host" and "hostnossl" */
    2027           0 :                 if (hba->conntype == ctHostSSL)
    2028           0 :                     continue;
    2029             :             }
    2030             : 
    2031             :             /* Check IP address */
    2032           0 :             switch (hba->ip_cmp_method)
    2033             :             {
    2034             :                 case ipCmpMask:
    2035           0 :                     if (hba->hostname)
    2036             :                     {
    2037           0 :                         if (!check_hostname(port,
    2038           0 :                                             hba->hostname))
    2039           0 :                             continue;
    2040             :                     }
    2041             :                     else
    2042             :                     {
    2043           0 :                         if (!check_ip(&port->raddr,
    2044           0 :                                       (struct sockaddr *) &hba->addr,
    2045           0 :                                       (struct sockaddr *) &hba->mask))
    2046           0 :                             continue;
    2047             :                     }
    2048           0 :                     break;
    2049             :                 case ipCmpAll:
    2050           0 :                     break;
    2051             :                 case ipCmpSameHost:
    2052             :                 case ipCmpSameNet:
    2053           0 :                     if (!check_same_host_or_net(&port->raddr,
    2054             :                                                 hba->ip_cmp_method))
    2055           0 :                         continue;
    2056           0 :                     break;
    2057             :                 default:
    2058             :                     /* shouldn't get here, but deem it no-match if so */
    2059           0 :                     continue;
    2060             :             }
    2061             :         }                       /* != ctLocal */
    2062             : 
    2063             :         /* Check database and role */
    2064         215 :         if (!check_db(port->database_name, port->user_name, roleid,
    2065             :                       hba->databases))
    2066           0 :             continue;
    2067             : 
    2068         215 :         if (!check_role(port->user_name, roleid, hba->roles))
    2069           0 :             continue;
    2070             : 
    2071             :         /* Found a record that matched! */
    2072         215 :         port->hba = hba;
    2073         430 :         return;
    2074             :     }
    2075             : 
    2076             :     /* If no matching entry was found, then implicitly reject. */
    2077           0 :     hba = palloc0(sizeof(HbaLine));
    2078           0 :     hba->auth_method = uaImplicitReject;
    2079           0 :     port->hba = hba;
    2080             : }
    2081             : 
    2082             : /*
    2083             :  * Read the config file and create a List of HbaLine records for the contents.
    2084             :  *
    2085             :  * The configuration is read into a temporary list, and if any parse error
    2086             :  * occurs the old list is kept in place and false is returned.  Only if the
    2087             :  * whole file parses OK is the list replaced, and the function returns true.
    2088             :  *
    2089             :  * On a false result, caller will take care of reporting a FATAL error in case
    2090             :  * this is the initial startup.  If it happens on reload, we just keep running
    2091             :  * with the old data.
    2092             :  */
    2093             : bool
    2094           1 : load_hba(void)
    2095             : {
    2096             :     FILE       *file;
    2097           1 :     List       *hba_lines = NIL;
    2098             :     ListCell   *line;
    2099           1 :     List       *new_parsed_lines = NIL;
    2100           1 :     bool        ok = true;
    2101             :     MemoryContext linecxt;
    2102             :     MemoryContext oldcxt;
    2103             :     MemoryContext hbacxt;
    2104             : 
    2105           1 :     file = AllocateFile(HbaFileName, "r");
    2106           1 :     if (file == NULL)
    2107             :     {
    2108           0 :         ereport(LOG,
    2109             :                 (errcode_for_file_access(),
    2110             :                  errmsg("could not open configuration file \"%s\": %m",
    2111             :                         HbaFileName)));
    2112           0 :         return false;
    2113             :     }
    2114             : 
    2115           1 :     linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG);
    2116           1 :     FreeFile(file);
    2117             : 
    2118             :     /* Now parse all the lines */
    2119           1 :     Assert(PostmasterContext);
    2120           1 :     hbacxt = AllocSetContextCreate(PostmasterContext,
    2121             :                                    "hba parser context",
    2122             :                                    ALLOCSET_SMALL_SIZES);
    2123           1 :     oldcxt = MemoryContextSwitchTo(hbacxt);
    2124           7 :     foreach(line, hba_lines)
    2125             :     {
    2126           6 :         TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
    2127             :         HbaLine    *newline;
    2128             : 
    2129             :         /* don't parse lines that already have errors */
    2130           6 :         if (tok_line->err_msg != NULL)
    2131             :         {
    2132           0 :             ok = false;
    2133           0 :             continue;
    2134             :         }
    2135             : 
    2136           6 :         if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
    2137             :         {
    2138             :             /* Parse error; remember there's trouble */
    2139           0 :             ok = false;
    2140             : 
    2141             :             /*
    2142             :              * Keep parsing the rest of the file so we can report errors on
    2143             :              * more than the first line.  Error has already been logged, no
    2144             :              * need for more chatter here.
    2145             :              */
    2146           0 :             continue;
    2147             :         }
    2148             : 
    2149           6 :         new_parsed_lines = lappend(new_parsed_lines, newline);
    2150             :     }
    2151             : 
    2152             :     /*
    2153             :      * A valid HBA file must have at least one entry; else there's no way to
    2154             :      * connect to the postmaster.  But only complain about this if we didn't
    2155             :      * already have parsing errors.
    2156             :      */
    2157           1 :     if (ok && new_parsed_lines == NIL)
    2158             :     {
    2159           0 :         ereport(LOG,
    2160             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
    2161             :                  errmsg("configuration file \"%s\" contains no entries",
    2162             :                         HbaFileName)));
    2163           0 :         ok = false;
    2164             :     }
    2165             : 
    2166             :     /* Free tokenizer memory */
    2167           1 :     MemoryContextDelete(linecxt);
    2168           1 :     MemoryContextSwitchTo(oldcxt);
    2169             : 
    2170           1 :     if (!ok)
    2171             :     {
    2172             :         /* File contained one or more errors, so bail out */
    2173           0 :         MemoryContextDelete(hbacxt);
    2174           0 :         return false;
    2175             :     }
    2176             : 
    2177             :     /* Loaded new file successfully, replace the one we use */
    2178           1 :     if (parsed_hba_context != NULL)
    2179           0 :         MemoryContextDelete(parsed_hba_context);
    2180           1 :     parsed_hba_context = hbacxt;
    2181           1 :     parsed_hba_lines = new_parsed_lines;
    2182             : 
    2183           1 :     return true;
    2184             : }
    2185             : 
    2186             : /*
    2187             :  * This macro specifies the maximum number of authentication options
    2188             :  * that are possible with any given authentication method that is supported.
    2189             :  * Currently LDAP supports 10, so the macro value is well above the most any
    2190             :  * method needs.
    2191             :  */
    2192             : #define MAX_HBA_OPTIONS 12
    2193             : 
    2194             : /*
    2195             :  * Create a text array listing the options specified in the HBA line.
    2196             :  * Return NULL if no options are specified.
    2197             :  */
    2198             : static ArrayType *
    2199           6 : gethba_options(HbaLine *hba)
    2200             : {
    2201             :     int         noptions;
    2202             :     Datum       options[MAX_HBA_OPTIONS];
    2203             : 
    2204           6 :     noptions = 0;
    2205             : 
    2206           6 :     if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
    2207             :     {
    2208           0 :         if (hba->include_realm)
    2209           0 :             options[noptions++] =
    2210           0 :                 CStringGetTextDatum("include_realm=true");
    2211             : 
    2212           0 :         if (hba->krb_realm)
    2213           0 :             options[noptions++] =
    2214           0 :                 CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm));
    2215             :     }
    2216             : 
    2217           6 :     if (hba->usermap)
    2218           0 :         options[noptions++] =
    2219           0 :             CStringGetTextDatum(psprintf("map=%s", hba->usermap));
    2220             : 
    2221           6 :     if (hba->clientcert)
    2222           0 :         options[noptions++] =
    2223           0 :             CStringGetTextDatum("clientcert=true");
    2224             : 
    2225           6 :     if (hba->pamservice)
    2226           0 :         options[noptions++] =
    2227           0 :             CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice));
    2228             : 
    2229           6 :     if (hba->auth_method == uaLDAP)
    2230             :     {
    2231           0 :         if (hba->ldapserver)
    2232           0 :             options[noptions++] =
    2233           0 :                 CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver));
    2234             : 
    2235           0 :         if (hba->ldapport)
    2236           0 :             options[noptions++] =
    2237           0 :                 CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport));
    2238             : 
    2239           0 :         if (hba->ldaptls)
    2240           0 :             options[noptions++] =
    2241           0 :                 CStringGetTextDatum("ldaptls=true");
    2242             : 
    2243           0 :         if (hba->ldapprefix)
    2244           0 :             options[noptions++] =
    2245           0 :                 CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix));
    2246             : 
    2247           0 :         if (hba->ldapsuffix)
    2248           0 :             options[noptions++] =
    2249           0 :                 CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix));
    2250             : 
    2251           0 :         if (hba->ldapbasedn)
    2252           0 :             options[noptions++] =
    2253           0 :                 CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn));
    2254             : 
    2255           0 :         if (hba->ldapbinddn)
    2256           0 :             options[noptions++] =
    2257           0 :                 CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn));
    2258             : 
    2259           0 :         if (hba->ldapbindpasswd)
    2260           0 :             options[noptions++] =
    2261           0 :                 CStringGetTextDatum(psprintf("ldapbindpasswd=%s",
    2262             :                                              hba->ldapbindpasswd));
    2263             : 
    2264           0 :         if (hba->ldapsearchattribute)
    2265           0 :             options[noptions++] =
    2266           0 :                 CStringGetTextDatum(psprintf("ldapsearchattribute=%s",
    2267             :                                              hba->ldapsearchattribute));
    2268             : 
    2269           0 :         if (hba->ldapscope)
    2270           0 :             options[noptions++] =
    2271           0 :                 CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
    2272             :     }
    2273             : 
    2274           6 :     if (hba->auth_method == uaRADIUS)
    2275             :     {
    2276           0 :         if (hba->radiusservers_s)
    2277           0 :             options[noptions++] =
    2278           0 :                 CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
    2279             : 
    2280           0 :         if (hba->radiussecrets_s)
    2281           0 :             options[noptions++] =
    2282           0 :                 CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
    2283             : 
    2284           0 :         if (hba->radiusidentifiers_s)
    2285           0 :             options[noptions++] =
    2286           0 :                 CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
    2287             : 
    2288           0 :         if (hba->radiusports_s)
    2289           0 :             options[noptions++] =
    2290           0 :                 CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
    2291             :     }
    2292             : 
    2293           6 :     Assert(noptions <= MAX_HBA_OPTIONS);
    2294             : 
    2295           6 :     if (noptions > 0)
    2296           0 :         return construct_array(options, noptions, TEXTOID, -1, false, 'i');
    2297             :     else
    2298           6 :         return NULL;
    2299             : }
    2300             : 
    2301             : /* Number of columns in pg_hba_file_rules view */
    2302             : #define NUM_PG_HBA_FILE_RULES_ATTS   9
    2303             : 
    2304             : /*
    2305             :  * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore
    2306             :  *
    2307             :  * tuple_store: where to store data
    2308             :  * tupdesc: tuple descriptor for the view
    2309             :  * lineno: pg_hba.conf line number (must always be valid)
    2310             :  * hba: parsed line data (can be NULL, in which case err_msg should be set)
    2311             :  * err_msg: error message (NULL if none)
    2312             :  *
    2313             :  * Note: leaks memory, but we don't care since this is run in a short-lived
    2314             :  * memory context.
    2315             :  */
    2316             : static void
    2317           6 : fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
    2318             :               int lineno, HbaLine *hba, const char *err_msg)
    2319             : {
    2320             :     Datum       values[NUM_PG_HBA_FILE_RULES_ATTS];
    2321             :     bool        nulls[NUM_PG_HBA_FILE_RULES_ATTS];
    2322             :     char        buffer[NI_MAXHOST];
    2323             :     HeapTuple   tuple;
    2324             :     int         index;
    2325             :     ListCell   *lc;
    2326             :     const char *typestr;
    2327             :     const char *addrstr;
    2328             :     const char *maskstr;
    2329             :     ArrayType  *options;
    2330             : 
    2331           6 :     Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS);
    2332             : 
    2333           6 :     memset(values, 0, sizeof(values));
    2334           6 :     memset(nulls, 0, sizeof(nulls));
    2335           6 :     index = 0;
    2336             : 
    2337             :     /* line_number */
    2338           6 :     values[index++] = Int32GetDatum(lineno);
    2339             : 
    2340           6 :     if (hba != NULL)
    2341             :     {
    2342             :         /* type */
    2343             :         /* Avoid a default: case so compiler will warn about missing cases */
    2344           6 :         typestr = NULL;
    2345           6 :         switch (hba->conntype)
    2346             :         {
    2347             :             case ctLocal:
    2348           2 :                 typestr = "local";
    2349           2 :                 break;
    2350             :             case ctHost:
    2351           4 :                 typestr = "host";
    2352           4 :                 break;
    2353             :             case ctHostSSL:
    2354           0 :                 typestr = "hostssl";
    2355           0 :                 break;
    2356             :             case ctHostNoSSL:
    2357           0 :                 typestr = "hostnossl";
    2358           0 :                 break;
    2359             :         }
    2360           6 :         if (typestr)
    2361           6 :             values[index++] = CStringGetTextDatum(typestr);
    2362             :         else
    2363           0 :             nulls[index++] = true;
    2364             : 
    2365             :         /* database */
    2366           6 :         if (hba->databases)
    2367             :         {
    2368             :             /*
    2369             :              * Flatten HbaToken list to string list.  It might seem that we
    2370             :              * should re-quote any quoted tokens, but that has been rejected
    2371             :              * on the grounds that it makes it harder to compare the array
    2372             :              * elements to other system catalogs.  That makes entries like
    2373             :              * "all" or "samerole" formally ambiguous ... but users who name
    2374             :              * databases/roles that way are inflicting their own pain.
    2375             :              */
    2376           6 :             List       *names = NIL;
    2377             : 
    2378          12 :             foreach(lc, hba->databases)
    2379             :             {
    2380           6 :                 HbaToken   *tok = lfirst(lc);
    2381             : 
    2382           6 :                 names = lappend(names, tok->string);
    2383             :             }
    2384           6 :             values[index++] = PointerGetDatum(strlist_to_textarray(names));
    2385             :         }
    2386             :         else
    2387           0 :             nulls[index++] = true;
    2388             : 
    2389             :         /* user */
    2390           6 :         if (hba->roles)
    2391             :         {
    2392             :             /* Flatten HbaToken list to string list; see comment above */
    2393           6 :             List       *roles = NIL;
    2394             : 
    2395          12 :             foreach(lc, hba->roles)
    2396             :             {
    2397           6 :                 HbaToken   *tok = lfirst(lc);
    2398             : 
    2399           6 :                 roles = lappend(roles, tok->string);
    2400             :             }
    2401           6 :             values[index++] = PointerGetDatum(strlist_to_textarray(roles));
    2402             :         }
    2403             :         else
    2404           0 :             nulls[index++] = true;
    2405             : 
    2406             :         /* address and netmask */
    2407             :         /* Avoid a default: case so compiler will warn about missing cases */
    2408           6 :         addrstr = maskstr = NULL;
    2409           6 :         switch (hba->ip_cmp_method)
    2410             :         {
    2411             :             case ipCmpMask:
    2412           6 :                 if (hba->hostname)
    2413             :                 {
    2414           0 :                     addrstr = hba->hostname;
    2415             :                 }
    2416             :                 else
    2417             :                 {
    2418           6 :                     if (pg_getnameinfo_all(&hba->addr, sizeof(hba->addr),
    2419             :                                            buffer, sizeof(buffer),
    2420             :                                            NULL, 0,
    2421             :                                            NI_NUMERICHOST) == 0)
    2422             :                     {
    2423           4 :                         clean_ipv6_addr(hba->addr.ss_family, buffer);
    2424           4 :                         addrstr = pstrdup(buffer);
    2425             :                     }
    2426           6 :                     if (pg_getnameinfo_all(&hba->mask, sizeof(hba->mask),
    2427             :                                            buffer, sizeof(buffer),
    2428             :                                            NULL, 0,
    2429             :                                            NI_NUMERICHOST) == 0)
    2430             :                     {
    2431           4 :                         clean_ipv6_addr(hba->mask.ss_family, buffer);
    2432           4 :                         maskstr = pstrdup(buffer);
    2433             :                     }
    2434             :                 }
    2435           6 :                 break;
    2436             :             case ipCmpAll:
    2437           0 :                 addrstr = "all";
    2438           0 :                 break;
    2439             :             case ipCmpSameHost:
    2440           0 :                 addrstr = "samehost";
    2441           0 :                 break;
    2442             :             case ipCmpSameNet:
    2443           0 :                 addrstr = "samenet";
    2444           0 :                 break;
    2445             :         }
    2446           6 :         if (addrstr)
    2447           4 :             values[index++] = CStringGetTextDatum(addrstr);
    2448             :         else
    2449           2 :             nulls[index++] = true;
    2450           6 :         if (maskstr)
    2451           4 :             values[index++] = CStringGetTextDatum(maskstr);
    2452             :         else
    2453           2 :             nulls[index++] = true;
    2454             : 
    2455             :         /*
    2456             :          * Make sure UserAuthName[] tracks additions to the UserAuth enum
    2457             :          */
    2458             :         StaticAssertStmt(lengthof(UserAuthName) == USER_AUTH_LAST + 1,
    2459             :                          "UserAuthName[] must match the UserAuth enum");
    2460             : 
    2461             :         /* auth_method */
    2462           6 :         values[index++] = CStringGetTextDatum(UserAuthName[hba->auth_method]);
    2463             : 
    2464             :         /* options */
    2465           6 :         options = gethba_options(hba);
    2466           6 :         if (options)
    2467           0 :             values[index++] = PointerGetDatum(options);
    2468             :         else
    2469           6 :             nulls[index++] = true;
    2470             :     }
    2471             :     else
    2472             :     {
    2473             :         /* no parsing result, so set relevant fields to nulls */
    2474           0 :         memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool));
    2475             :     }
    2476             : 
    2477             :     /* error */
    2478           6 :     if (err_msg)
    2479           0 :         values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg);
    2480             :     else
    2481           6 :         nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true;
    2482             : 
    2483           6 :     tuple = heap_form_tuple(tupdesc, values, nulls);
    2484           6 :     tuplestore_puttuple(tuple_store, tuple);
    2485           6 : }
    2486             : 
    2487             : /*
    2488             :  * Read the pg_hba.conf file and fill the tuplestore with view records.
    2489             :  */
    2490             : static void
    2491           1 : fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
    2492             : {
    2493             :     FILE       *file;
    2494           1 :     List       *hba_lines = NIL;
    2495             :     ListCell   *line;
    2496             :     MemoryContext linecxt;
    2497             :     MemoryContext hbacxt;
    2498             :     MemoryContext oldcxt;
    2499             : 
    2500             :     /*
    2501             :      * In the unlikely event that we can't open pg_hba.conf, we throw an
    2502             :      * error, rather than trying to report it via some sort of view entry.
    2503             :      * (Most other error conditions should result in a message in a view
    2504             :      * entry.)
    2505             :      */
    2506           1 :     file = AllocateFile(HbaFileName, "r");
    2507           1 :     if (file == NULL)
    2508           0 :         ereport(ERROR,
    2509             :                 (errcode_for_file_access(),
    2510             :                  errmsg("could not open configuration file \"%s\": %m",
    2511             :                         HbaFileName)));
    2512             : 
    2513           1 :     linecxt = tokenize_file(HbaFileName, file, &hba_lines, DEBUG3);
    2514           1 :     FreeFile(file);
    2515             : 
    2516             :     /* Now parse all the lines */
    2517           1 :     hbacxt = AllocSetContextCreate(CurrentMemoryContext,
    2518             :                                    "hba parser context",
    2519             :                                    ALLOCSET_SMALL_SIZES);
    2520           1 :     oldcxt = MemoryContextSwitchTo(hbacxt);
    2521           7 :     foreach(line, hba_lines)
    2522             :     {
    2523           6 :         TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
    2524           6 :         HbaLine    *hbaline = NULL;
    2525             : 
    2526             :         /* don't parse lines that already have errors */
    2527           6 :         if (tok_line->err_msg == NULL)
    2528           6 :             hbaline = parse_hba_line(tok_line, DEBUG3);
    2529             : 
    2530           6 :         fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
    2531           6 :                       hbaline, tok_line->err_msg);
    2532             :     }
    2533             : 
    2534             :     /* Free tokenizer memory */
    2535           1 :     MemoryContextDelete(linecxt);
    2536             :     /* Free parse_hba_line memory */
    2537           1 :     MemoryContextSwitchTo(oldcxt);
    2538           1 :     MemoryContextDelete(hbacxt);
    2539           1 : }
    2540             : 
    2541             : /*
    2542             :  * SQL-accessible SRF to return all the entries in the pg_hba.conf file.
    2543             :  */
    2544             : Datum
    2545           1 : pg_hba_file_rules(PG_FUNCTION_ARGS)
    2546             : {
    2547             :     Tuplestorestate *tuple_store;
    2548             :     TupleDesc   tupdesc;
    2549             :     MemoryContext old_cxt;
    2550             :     ReturnSetInfo *rsi;
    2551             : 
    2552             :     /*
    2553             :      * We must use the Materialize mode to be safe against HBA file changes
    2554             :      * while the cursor is open. It's also more efficient than having to look
    2555             :      * up our current position in the parsed list every time.
    2556             :      */
    2557           1 :     rsi = (ReturnSetInfo *) fcinfo->resultinfo;
    2558             : 
    2559             :     /* Check to see if caller supports us returning a tuplestore */
    2560           1 :     if (rsi == NULL || !IsA(rsi, ReturnSetInfo))
    2561           0 :         ereport(ERROR,
    2562             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2563             :                  errmsg("set-valued function called in context that cannot accept a set")));
    2564           1 :     if (!(rsi->allowedModes & SFRM_Materialize))
    2565           0 :         ereport(ERROR,
    2566             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2567             :                  errmsg("materialize mode required, but it is not " \
    2568             :                         "allowed in this context")));
    2569             : 
    2570           1 :     rsi->returnMode = SFRM_Materialize;
    2571             : 
    2572             :     /* Build a tuple descriptor for our result type */
    2573           1 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
    2574           0 :         elog(ERROR, "return type must be a row type");
    2575             : 
    2576             :     /* Build tuplestore to hold the result rows */
    2577           1 :     old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
    2578             : 
    2579           1 :     tuple_store =
    2580           1 :         tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
    2581             :                               false, work_mem);
    2582           1 :     rsi->setDesc = tupdesc;
    2583           1 :     rsi->setResult = tuple_store;
    2584             : 
    2585           1 :     MemoryContextSwitchTo(old_cxt);
    2586             : 
    2587             :     /* Fill the tuplestore */
    2588           1 :     fill_hba_view(tuple_store, tupdesc);
    2589             : 
    2590           1 :     PG_RETURN_NULL();
    2591             : }
    2592             : 
    2593             : 
    2594             : /*
    2595             :  * Parse one tokenised line from the ident config file and store the result in
    2596             :  * an IdentLine structure.
    2597             :  *
    2598             :  * If parsing fails, log a message and return NULL.
    2599             :  *
    2600             :  * If ident_user is a regular expression (ie. begins with a slash), it is
    2601             :  * compiled and stored in IdentLine structure.
    2602             :  *
    2603             :  * Note: this function leaks memory when an error occurs.  Caller is expected
    2604             :  * to have set a memory context that will be reset if this function returns
    2605             :  * NULL.
    2606             :  */
    2607             : static IdentLine *
    2608           0 : parse_ident_line(TokenizedLine *tok_line)
    2609             : {
    2610           0 :     int         line_num = tok_line->line_num;
    2611             :     ListCell   *field;
    2612             :     List       *tokens;
    2613             :     HbaToken   *token;
    2614             :     IdentLine  *parsedline;
    2615             : 
    2616           0 :     Assert(tok_line->fields != NIL);
    2617           0 :     field = list_head(tok_line->fields);
    2618             : 
    2619           0 :     parsedline = palloc0(sizeof(IdentLine));
    2620           0 :     parsedline->linenumber = line_num;
    2621             : 
    2622             :     /* Get the map token (must exist) */
    2623           0 :     tokens = lfirst(field);
    2624           0 :     IDENT_MULTI_VALUE(tokens);
    2625           0 :     token = linitial(tokens);
    2626           0 :     parsedline->usermap = pstrdup(token->string);
    2627             : 
    2628             :     /* Get the ident user token */
    2629           0 :     field = lnext(field);
    2630           0 :     IDENT_FIELD_ABSENT(field);
    2631           0 :     tokens = lfirst(field);
    2632           0 :     IDENT_MULTI_VALUE(tokens);
    2633           0 :     token = linitial(tokens);
    2634           0 :     parsedline->ident_user = pstrdup(token->string);
    2635             : 
    2636             :     /* Get the PG rolename token */
    2637           0 :     field = lnext(field);
    2638           0 :     IDENT_FIELD_ABSENT(field);
    2639           0 :     tokens = lfirst(field);
    2640           0 :     IDENT_MULTI_VALUE(tokens);
    2641           0 :     token = linitial(tokens);
    2642           0 :     parsedline->pg_role = pstrdup(token->string);
    2643             : 
    2644           0 :     if (parsedline->ident_user[0] == '/')
    2645             :     {
    2646             :         /*
    2647             :          * When system username starts with a slash, treat it as a regular
    2648             :          * expression. Pre-compile it.
    2649             :          */
    2650             :         int         r;
    2651             :         pg_wchar   *wstr;
    2652             :         int         wlen;
    2653             : 
    2654           0 :         wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar));
    2655           0 :         wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1,
    2656           0 :                                     wstr, strlen(parsedline->ident_user + 1));
    2657             : 
    2658           0 :         r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
    2659           0 :         if (r)
    2660             :         {
    2661             :             char        errstr[100];
    2662             : 
    2663           0 :             pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
    2664           0 :             ereport(LOG,
    2665             :                     (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    2666             :                      errmsg("invalid regular expression \"%s\": %s",
    2667             :                             parsedline->ident_user + 1, errstr)));
    2668             : 
    2669           0 :             pfree(wstr);
    2670           0 :             return NULL;
    2671             :         }
    2672           0 :         pfree(wstr);
    2673             :     }
    2674             : 
    2675           0 :     return parsedline;
    2676             : }
    2677             : 
    2678             : /*
    2679             :  *  Process one line from the parsed ident config lines.
    2680             :  *
    2681             :  *  Compare input parsed ident line to the needed map, pg_role and ident_user.
    2682             :  *  *found_p and *error_p are set according to our results.
    2683             :  */
    2684             : static void
    2685           0 : check_ident_usermap(IdentLine *identLine, const char *usermap_name,
    2686             :                     const char *pg_role, const char *ident_user,
    2687             :                     bool case_insensitive, bool *found_p, bool *error_p)
    2688             : {
    2689           0 :     *found_p = false;
    2690           0 :     *error_p = false;
    2691             : 
    2692           0 :     if (strcmp(identLine->usermap, usermap_name) != 0)
    2693             :         /* Line does not match the map name we're looking for, so just abort */
    2694           0 :         return;
    2695             : 
    2696             :     /* Match? */
    2697           0 :     if (identLine->ident_user[0] == '/')
    2698             :     {
    2699             :         /*
    2700             :          * When system username starts with a slash, treat it as a regular
    2701             :          * expression. In this case, we process the system username as a
    2702             :          * regular expression that returns exactly one match. This is replaced
    2703             :          * for \1 in the database username string, if present.
    2704             :          */
    2705             :         int         r;
    2706             :         regmatch_t  matches[2];
    2707             :         pg_wchar   *wstr;
    2708             :         int         wlen;
    2709             :         char       *ofs;
    2710             :         char       *regexp_pgrole;
    2711             : 
    2712           0 :         wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
    2713           0 :         wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
    2714             : 
    2715           0 :         r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0);
    2716           0 :         if (r)
    2717             :         {
    2718             :             char        errstr[100];
    2719             : 
    2720           0 :             if (r != REG_NOMATCH)
    2721             :             {
    2722             :                 /* REG_NOMATCH is not an error, everything else is */
    2723           0 :                 pg_regerror(r, &identLine->re, errstr, sizeof(errstr));
    2724           0 :                 ereport(LOG,
    2725             :                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    2726             :                          errmsg("regular expression match for \"%s\" failed: %s",
    2727             :                                 identLine->ident_user + 1, errstr)));
    2728           0 :                 *error_p = true;
    2729             :             }
    2730             : 
    2731           0 :             pfree(wstr);
    2732           0 :             return;
    2733             :         }
    2734           0 :         pfree(wstr);
    2735             : 
    2736           0 :         if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL)
    2737             :         {
    2738             :             int         offset;
    2739             : 
    2740             :             /* substitution of the first argument requested */
    2741           0 :             if (matches[1].rm_so < 0)
    2742             :             {
    2743           0 :                 ereport(LOG,
    2744             :                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    2745             :                          errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
    2746             :                                 identLine->ident_user + 1, identLine->pg_role)));
    2747           0 :                 *error_p = true;
    2748           0 :                 return;
    2749             :             }
    2750             : 
    2751             :             /*
    2752             :              * length: original length minus length of \1 plus length of match
    2753             :              * plus null terminator
    2754             :              */
    2755           0 :             regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
    2756           0 :             offset = ofs - identLine->pg_role;
    2757           0 :             memcpy(regexp_pgrole, identLine->pg_role, offset);
    2758           0 :             memcpy(regexp_pgrole + offset,
    2759           0 :                    ident_user + matches[1].rm_so,
    2760           0 :                    matches[1].rm_eo - matches[1].rm_so);
    2761           0 :             strcat(regexp_pgrole, ofs + 2);
    2762             :         }
    2763             :         else
    2764             :         {
    2765             :             /* no substitution, so copy the match */
    2766           0 :             regexp_pgrole = pstrdup(identLine->pg_role);
    2767             :         }
    2768             : 
    2769             :         /*
    2770             :          * now check if the username actually matched what the user is trying
    2771             :          * to connect as
    2772             :          */
    2773           0 :         if (case_insensitive)
    2774             :         {
    2775           0 :             if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
    2776           0 :                 *found_p = true;
    2777             :         }
    2778             :         else
    2779             :         {
    2780           0 :             if (strcmp(regexp_pgrole, pg_role) == 0)
    2781           0 :                 *found_p = true;
    2782             :         }
    2783           0 :         pfree(regexp_pgrole);
    2784             : 
    2785           0 :         return;
    2786             :     }
    2787             :     else
    2788             :     {
    2789             :         /* Not regular expression, so make complete match */
    2790           0 :         if (case_insensitive)
    2791             :         {
    2792           0 :             if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 &&
    2793           0 :                 pg_strcasecmp(identLine->ident_user, ident_user) == 0)
    2794           0 :                 *found_p = true;
    2795             :         }
    2796             :         else
    2797             :         {
    2798           0 :             if (strcmp(identLine->pg_role, pg_role) == 0 &&
    2799           0 :                 strcmp(identLine->ident_user, ident_user) == 0)
    2800           0 :                 *found_p = true;
    2801             :         }
    2802             :     }
    2803           0 :     return;
    2804             : }
    2805             : 
    2806             : 
    2807             : /*
    2808             :  *  Scan the (pre-parsed) ident usermap file line by line, looking for a match
    2809             :  *
    2810             :  *  See if the user with ident username "auth_user" is allowed to act
    2811             :  *  as Postgres user "pg_role" according to usermap "usermap_name".
    2812             :  *
    2813             :  *  Special case: Usermap NULL, equivalent to what was previously called
    2814             :  *  "sameuser" or "samerole", means don't look in the usermap file.
    2815             :  *  That's an implied map wherein "pg_role" must be identical to
    2816             :  *  "auth_user" in order to be authorized.
    2817             :  *
    2818             :  *  Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
    2819             :  */
    2820             : int
    2821           0 : check_usermap(const char *usermap_name,
    2822             :               const char *pg_role,
    2823             :               const char *auth_user,
    2824             :               bool case_insensitive)
    2825             : {
    2826           0 :     bool        found_entry = false,
    2827           0 :                 error = false;
    2828             : 
    2829           0 :     if (usermap_name == NULL || usermap_name[0] == '\0')
    2830             :     {
    2831           0 :         if (case_insensitive)
    2832             :         {
    2833           0 :             if (pg_strcasecmp(pg_role, auth_user) == 0)
    2834           0 :                 return STATUS_OK;
    2835             :         }
    2836             :         else
    2837             :         {
    2838           0 :             if (strcmp(pg_role, auth_user) == 0)
    2839           0 :                 return STATUS_OK;
    2840             :         }
    2841           0 :         ereport(LOG,
    2842             :                 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
    2843             :                         pg_role, auth_user)));
    2844           0 :         return STATUS_ERROR;
    2845             :     }
    2846             :     else
    2847             :     {
    2848             :         ListCell   *line_cell;
    2849             : 
    2850           0 :         foreach(line_cell, parsed_ident_lines)
    2851             :         {
    2852           0 :             check_ident_usermap(lfirst(line_cell), usermap_name,
    2853             :                                 pg_role, auth_user, case_insensitive,
    2854             :                                 &found_entry, &error);
    2855           0 :             if (found_entry || error)
    2856             :                 break;
    2857             :         }
    2858             :     }
    2859           0 :     if (!found_entry && !error)
    2860             :     {
    2861           0 :         ereport(LOG,
    2862             :                 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
    2863             :                         usermap_name, pg_role, auth_user)));
    2864             :     }
    2865           0 :     return found_entry ? STATUS_OK : STATUS_ERROR;
    2866             : }
    2867             : 
    2868             : 
    2869             : /*
    2870             :  * Read the ident config file and create a List of IdentLine records for
    2871             :  * the contents.
    2872             :  *
    2873             :  * This works the same as load_hba(), but for the user config file.
    2874             :  */
    2875             : bool
    2876           1 : load_ident(void)
    2877             : {
    2878             :     FILE       *file;
    2879           1 :     List       *ident_lines = NIL;
    2880             :     ListCell   *line_cell,
    2881             :                *parsed_line_cell;
    2882           1 :     List       *new_parsed_lines = NIL;
    2883           1 :     bool        ok = true;
    2884             :     MemoryContext linecxt;
    2885             :     MemoryContext oldcxt;
    2886             :     MemoryContext ident_context;
    2887             :     IdentLine  *newline;
    2888             : 
    2889           1 :     file = AllocateFile(IdentFileName, "r");
    2890           1 :     if (file == NULL)
    2891             :     {
    2892             :         /* not fatal ... we just won't do any special ident maps */
    2893           0 :         ereport(LOG,
    2894             :                 (errcode_for_file_access(),
    2895             :                  errmsg("could not open usermap file \"%s\": %m",
    2896             :                         IdentFileName)));
    2897           0 :         return false;
    2898             :     }
    2899             : 
    2900           1 :     linecxt = tokenize_file(IdentFileName, file, &ident_lines, LOG);
    2901           1 :     FreeFile(file);
    2902             : 
    2903             :     /* Now parse all the lines */
    2904           1 :     Assert(PostmasterContext);
    2905           1 :     ident_context = AllocSetContextCreate(PostmasterContext,
    2906             :                                           "ident parser context",
    2907             :                                           ALLOCSET_SMALL_SIZES);
    2908           1 :     oldcxt = MemoryContextSwitchTo(ident_context);
    2909           1 :     foreach(line_cell, ident_lines)
    2910             :     {
    2911           0 :         TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell);
    2912             : 
    2913             :         /* don't parse lines that already have errors */
    2914           0 :         if (tok_line->err_msg != NULL)
    2915             :         {
    2916           0 :             ok = false;
    2917           0 :             continue;
    2918             :         }
    2919             : 
    2920           0 :         if ((newline = parse_ident_line(tok_line)) == NULL)
    2921             :         {
    2922             :             /* Parse error; remember there's trouble */
    2923           0 :             ok = false;
    2924             : 
    2925             :             /*
    2926             :              * Keep parsing the rest of the file so we can report errors on
    2927             :              * more than the first line.  Error has already been logged, no
    2928             :              * need for more chatter here.
    2929             :              */
    2930           0 :             continue;
    2931             :         }
    2932             : 
    2933           0 :         new_parsed_lines = lappend(new_parsed_lines, newline);
    2934             :     }
    2935             : 
    2936             :     /* Free tokenizer memory */
    2937           1 :     MemoryContextDelete(linecxt);
    2938           1 :     MemoryContextSwitchTo(oldcxt);
    2939             : 
    2940           1 :     if (!ok)
    2941             :     {
    2942             :         /*
    2943             :          * File contained one or more errors, so bail out, first being careful
    2944             :          * to clean up whatever we allocated.  Most stuff will go away via
    2945             :          * MemoryContextDelete, but we have to clean up regexes explicitly.
    2946             :          */
    2947           0 :         foreach(parsed_line_cell, new_parsed_lines)
    2948             :         {
    2949           0 :             newline = (IdentLine *) lfirst(parsed_line_cell);
    2950           0 :             if (newline->ident_user[0] == '/')
    2951           0 :                 pg_regfree(&newline->re);
    2952             :         }
    2953           0 :         MemoryContextDelete(ident_context);
    2954           0 :         return false;
    2955             :     }
    2956             : 
    2957             :     /* Loaded new file successfully, replace the one we use */
    2958           1 :     if (parsed_ident_lines != NIL)
    2959             :     {
    2960           0 :         foreach(parsed_line_cell, parsed_ident_lines)
    2961             :         {
    2962           0 :             newline = (IdentLine *) lfirst(parsed_line_cell);
    2963           0 :             if (newline->ident_user[0] == '/')
    2964           0 :                 pg_regfree(&newline->re);
    2965             :         }
    2966             :     }
    2967           1 :     if (parsed_ident_context != NULL)
    2968           0 :         MemoryContextDelete(parsed_ident_context);
    2969             : 
    2970           1 :     parsed_ident_context = ident_context;
    2971           1 :     parsed_ident_lines = new_parsed_lines;
    2972             : 
    2973           1 :     return true;
    2974             : }
    2975             : 
    2976             : 
    2977             : 
    2978             : /*
    2979             :  *  Determine what authentication method should be used when accessing database
    2980             :  *  "database" from frontend "raddr", user "user".  Return the method and
    2981             :  *  an optional argument (stored in fields of *port), and STATUS_OK.
    2982             :  *
    2983             :  *  If the file does not contain any entry matching the request, we return
    2984             :  *  method = uaImplicitReject.
    2985             :  */
    2986             : void
    2987         215 : hba_getauthmethod(hbaPort *port)
    2988             : {
    2989         215 :     check_hba(port);
    2990         215 : }

Generated by: LCOV version 1.11