Header And Logo

PostgreSQL
| The world's most advanced open source database.

hba.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * hba.c
00004  *    Routines to handle host based authentication (that's the scheme
00005  *    wherein you authenticate a user by seeing what IP address the system
00006  *    says he comes from and choosing authentication method based on it).
00007  *
00008  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00009  * Portions Copyright (c) 1994, Regents of the University of California
00010  *
00011  *
00012  * IDENTIFICATION
00013  *    src/backend/libpq/hba.c
00014  *
00015  *-------------------------------------------------------------------------
00016  */
00017 #include "postgres.h"
00018 
00019 #include <ctype.h>
00020 #include <pwd.h>
00021 #include <fcntl.h>
00022 #include <sys/param.h>
00023 #include <sys/socket.h>
00024 #include <netinet/in.h>
00025 #include <arpa/inet.h>
00026 #include <unistd.h>
00027 
00028 #include "catalog/pg_collation.h"
00029 #include "libpq/ip.h"
00030 #include "libpq/libpq.h"
00031 #include "postmaster/postmaster.h"
00032 #include "regex/regex.h"
00033 #include "replication/walsender.h"
00034 #include "storage/fd.h"
00035 #include "utils/acl.h"
00036 #include "utils/guc.h"
00037 #include "utils/lsyscache.h"
00038 #include "utils/memutils.h"
00039 
00040 #ifdef USE_LDAP
00041 #ifdef WIN32
00042 #include <winldap.h>
00043 #else
00044 #include <ldap.h>
00045 #endif
00046 #endif
00047 
00048 
00049 #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
00050 #define atoxid(x)  ((TransactionId) strtoul((x), NULL, 10))
00051 
00052 #define MAX_TOKEN   256
00053 #define MAX_LINE    8192
00054 
00055 /* callback data for check_network_callback */
00056 typedef struct check_network_data
00057 {
00058     IPCompareMethod method;     /* test method */
00059     SockAddr   *raddr;          /* client's actual address */
00060     bool        result;         /* set to true if match */
00061 } check_network_data;
00062 
00063 
00064 #define token_is_keyword(t, k)  (!t->quoted && strcmp(t->string, k) == 0)
00065 #define token_matches(t, k)  (strcmp(t->string, k) == 0)
00066 
00067 /*
00068  * A single string token lexed from the HBA config file, together with whether
00069  * the token had been quoted.
00070  */
00071 typedef struct HbaToken
00072 {
00073     char       *string;
00074     bool        quoted;
00075 } HbaToken;
00076 
00077 /*
00078  * pre-parsed content of HBA config file: list of HbaLine structs.
00079  * parsed_hba_context is the memory context where it lives.
00080  */
00081 static List *parsed_hba_lines = NIL;
00082 static MemoryContext parsed_hba_context = NULL;
00083 
00084 /*
00085  * pre-parsed content of ident mapping file: list of IdentLine structs.
00086  * parsed_ident_context is the memory context where it lives.
00087  *
00088  * NOTE: the IdentLine structs can contain pre-compiled regular expressions
00089  * that live outside the memory context. Before destroying or resetting the
00090  * memory context, they need to be expliticly free'd.
00091  */
00092 static List *parsed_ident_lines = NIL;
00093 static MemoryContext parsed_ident_context = NULL;
00094 
00095 
00096 static MemoryContext tokenize_file(const char *filename, FILE *file,
00097               List **lines, List **line_nums, List **raw_lines);
00098 static List *tokenize_inc_file(List *tokens, const char *outer_filename,
00099                   const char *inc_filename);
00100 static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
00101                    int line_num);
00102 
00103 /*
00104  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
00105  * so provide our own version.
00106  */
00107 bool
00108 pg_isblank(const char c)
00109 {
00110     return c == ' ' || c == '\t' || c == '\r';
00111 }
00112 
00113 
00114 /*
00115  * Grab one token out of the string pointed to by lineptr.
00116  * Tokens are strings of non-blank
00117  * characters bounded by blank characters, commas, beginning of line, and
00118  * end of line. Blank means space or tab. Tokens can be delimited by
00119  * double quotes (this allows the inclusion of blanks, but not newlines).
00120  *
00121  * The token, if any, is returned at *buf (a buffer of size bufsz).
00122  * Also, we set *initial_quote to indicate whether there was quoting before
00123  * the first character.  (We use that to prevent "@x" from being treated
00124  * as a file inclusion request.  Note that @"x" should be so treated;
00125  * we want to allow that to support embedded spaces in file paths.)
00126  * We set *terminating_comma to indicate whether the token is terminated by a
00127  * comma (which is not returned.)
00128  *
00129  * If successful: store null-terminated token at *buf and return TRUE.
00130  * If no more tokens on line: set *buf = '\0' and return FALSE.
00131  *
00132  * Leave file positioned at the character immediately after the token or EOF,
00133  * whichever comes first. If no more tokens on line, position the file to the
00134  * beginning of the next line or EOF, whichever comes first.
00135  *
00136  * Handle comments.
00137  */
00138 static bool
00139 next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
00140            bool *terminating_comma)
00141 {
00142     int         c;
00143     char       *start_buf = buf;
00144     char       *end_buf = buf + (bufsz - 2);
00145     bool        in_quote = false;
00146     bool        was_quote = false;
00147     bool        saw_quote = false;
00148 
00149     /* end_buf reserves two bytes to ensure we can append \n and \0 */
00150     Assert(end_buf > start_buf);
00151 
00152     *initial_quote = false;
00153     *terminating_comma = false;
00154 
00155     /* Move over initial whitespace and commas */
00156     while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
00157         ;
00158 
00159     if (c == '\0' || c == '\n')
00160     {
00161         *buf = '\0';
00162         return false;
00163     }
00164 
00165     /*
00166      * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
00167      * or unquoted whitespace.
00168      */
00169     while (c != '\0' && c != '\n' &&
00170            (!pg_isblank(c) || in_quote))
00171     {
00172         /* skip comments to EOL */
00173         if (c == '#' && !in_quote)
00174         {
00175             while ((c = (*(*lineptr)++)) != '\0' && c != '\n')
00176                 ;
00177             /* If only comment, consume EOL too; return EOL */
00178             if (c != '\0' && buf == start_buf)
00179                 (*lineptr)++;
00180             break;
00181         }
00182 
00183         if (buf >= end_buf)
00184         {
00185             *buf = '\0';
00186             ereport(LOG,
00187                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
00188                errmsg("authentication file token too long, skipping: \"%s\"",
00189                       start_buf)));
00190             /* Discard remainder of line */
00191             while ((c = (*(*lineptr)++)) != '\0' && c != '\n')
00192                 ;
00193             break;
00194         }
00195 
00196         /* we do not pass back the comma in the token */
00197         if (c == ',' && !in_quote)
00198         {
00199             *terminating_comma = true;
00200             break;
00201         }
00202 
00203         if (c != '"' || was_quote)
00204             *buf++ = c;
00205 
00206         /* Literal double-quote is two double-quotes */
00207         if (in_quote && c == '"')
00208             was_quote = !was_quote;
00209         else
00210             was_quote = false;
00211 
00212         if (c == '"')
00213         {
00214             in_quote = !in_quote;
00215             saw_quote = true;
00216             if (buf == start_buf)
00217                 *initial_quote = true;
00218         }
00219 
00220         c = *(*lineptr)++;
00221     }
00222 
00223     /*
00224      * Put back the char right after the token (critical in case it is EOL,
00225      * since we need to detect end-of-line at next call).
00226      */
00227     (*lineptr)--;
00228 
00229     *buf = '\0';
00230 
00231     return (saw_quote || buf > start_buf);
00232 }
00233 
00234 static HbaToken *
00235 make_hba_token(char *token, bool quoted)
00236 {
00237     HbaToken   *hbatoken;
00238     int         toklen;
00239 
00240     toklen = strlen(token);
00241     hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
00242     hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
00243     hbatoken->quoted = quoted;
00244     memcpy(hbatoken->string, token, toklen + 1);
00245 
00246     return hbatoken;
00247 }
00248 
00249 /*
00250  * Copy a HbaToken struct into freshly palloc'd memory.
00251  */
00252 static HbaToken *
00253 copy_hba_token(HbaToken *in)
00254 {
00255     HbaToken   *out = make_hba_token(in->string, in->quoted);
00256 
00257     return out;
00258 }
00259 
00260 
00261 /*
00262  * Tokenize one HBA field from a line, handling file inclusion and comma lists.
00263  *
00264  * The result is a List of HbaToken structs for each individual token,
00265  * or NIL if we reached EOL.
00266  */
00267 static List *
00268 next_field_expand(const char *filename, char **lineptr)
00269 {
00270     char        buf[MAX_TOKEN];
00271     bool        trailing_comma;
00272     bool        initial_quote;
00273     List       *tokens = NIL;
00274 
00275     do
00276     {
00277         if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
00278             break;
00279 
00280         /* Is this referencing a file? */
00281         if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
00282             tokens = tokenize_inc_file(tokens, filename, buf + 1);
00283         else
00284             tokens = lappend(tokens, make_hba_token(buf, initial_quote));
00285     } while (trailing_comma);
00286 
00287     return tokens;
00288 }
00289 
00290 /*
00291  * tokenize_inc_file
00292  *      Expand a file included from another file into an hba "field"
00293  *
00294  * Opens and tokenises a file included from another HBA config file with @,
00295  * and returns all values found therein as a flat list of HbaTokens.  If a
00296  * @-token is found, recursively expand it.  The given token list is used as
00297  * initial contents of list (so foo,bar,@baz does what you expect).
00298  */
00299 static List *
00300 tokenize_inc_file(List *tokens,
00301                   const char *outer_filename,
00302                   const char *inc_filename)
00303 {
00304     char       *inc_fullname;
00305     FILE       *inc_file;
00306     List       *inc_lines;
00307     List       *inc_line_nums;
00308     ListCell   *inc_line;
00309     MemoryContext linecxt;
00310 
00311     if (is_absolute_path(inc_filename))
00312     {
00313         /* absolute path is taken as-is */
00314         inc_fullname = pstrdup(inc_filename);
00315     }
00316     else
00317     {
00318         /* relative path is relative to dir of calling file */
00319         inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
00320                                        strlen(inc_filename) + 1);
00321         strcpy(inc_fullname, outer_filename);
00322         get_parent_directory(inc_fullname);
00323         join_path_components(inc_fullname, inc_fullname, inc_filename);
00324         canonicalize_path(inc_fullname);
00325     }
00326 
00327     inc_file = AllocateFile(inc_fullname, "r");
00328     if (inc_file == NULL)
00329     {
00330         ereport(LOG,
00331                 (errcode_for_file_access(),
00332                  errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
00333                         inc_filename, inc_fullname)));
00334         pfree(inc_fullname);
00335         return tokens;
00336     }
00337 
00338     /* There is possible recursion here if the file contains @ */
00339     linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums, NULL);
00340 
00341     FreeFile(inc_file);
00342     pfree(inc_fullname);
00343 
00344     foreach(inc_line, inc_lines)
00345     {
00346         List       *inc_fields = lfirst(inc_line);
00347         ListCell   *inc_field;
00348 
00349         foreach(inc_field, inc_fields)
00350         {
00351             List       *inc_tokens = lfirst(inc_field);
00352             ListCell   *inc_token;
00353 
00354             foreach(inc_token, inc_tokens)
00355             {
00356                 HbaToken   *token = lfirst(inc_token);
00357 
00358                 tokens = lappend(tokens, copy_hba_token(token));
00359             }
00360         }
00361     }
00362 
00363     MemoryContextDelete(linecxt);
00364     return tokens;
00365 }
00366 
00367 /*
00368  * Tokenize the given file, storing the resulting data into three Lists: a
00369  * List of lines, a List of line numbers, and a List of raw line contents.
00370  *
00371  * The list of lines is a triple-nested List structure.  Each line is a List of
00372  * fields, and each field is a List of HbaTokens.
00373  *
00374  * filename must be the absolute path to the target file.
00375  *
00376  * Return value is a memory context which contains all memory allocated by
00377  * this function.
00378  */
00379 static MemoryContext
00380 tokenize_file(const char *filename, FILE *file,
00381               List **lines, List **line_nums, List **raw_lines)
00382 {
00383     List       *current_line = NIL;
00384     List       *current_field = NIL;
00385     int         line_number = 1;
00386     MemoryContext linecxt;
00387     MemoryContext oldcxt;
00388 
00389     linecxt = AllocSetContextCreate(TopMemoryContext,
00390                                     "tokenize file cxt",
00391                                     ALLOCSET_DEFAULT_MINSIZE,
00392                                     ALLOCSET_DEFAULT_INITSIZE,
00393                                     ALLOCSET_DEFAULT_MAXSIZE);
00394     oldcxt = MemoryContextSwitchTo(linecxt);
00395 
00396     *lines = *line_nums = NIL;
00397 
00398     while (!feof(file) && !ferror(file))
00399     {
00400         char rawline[MAX_LINE];
00401         char *lineptr;
00402 
00403         if (!fgets(rawline, sizeof(rawline), file))
00404             break;
00405         if (strlen(rawline) == MAX_LINE-1)
00406             /* Line too long! */
00407             ereport(ERROR,
00408                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
00409                      errmsg("authentication file line too long"),
00410                      errcontext("line %d of configuration file \"%s\"",
00411                                 line_number, filename)));
00412 
00413         /* Strip trailing linebreak from rawline */
00414         while (rawline[strlen(rawline)-1] == '\n' ||
00415                rawline[strlen(rawline)-1] == '\r')
00416             rawline[strlen(rawline)-1] = '\0';
00417 
00418         lineptr = rawline;
00419         while (strlen(lineptr) > 0)
00420         {
00421             current_field = next_field_expand(filename, &lineptr);
00422 
00423             /* add tokens to list, unless we are at EOL or comment start */
00424             if (list_length(current_field) > 0)
00425             {
00426                 if (current_line == NIL)
00427                 {
00428                     /* make a new line List, record its line number */
00429                     current_line = lappend(current_line, current_field);
00430                     *lines = lappend(*lines, current_line);
00431                     *line_nums = lappend_int(*line_nums, line_number);
00432                     if (raw_lines)
00433                         *raw_lines = lappend(*raw_lines, pstrdup(rawline));
00434                 }
00435                 else
00436                 {
00437                     /* append tokens to current line's list */
00438                     current_line = lappend(current_line, current_field);
00439                 }
00440             }
00441         }
00442         /* we are at real or logical EOL, so force a new line List */
00443         current_line = NIL;
00444         line_number++;
00445     }
00446 
00447     MemoryContextSwitchTo(oldcxt);
00448 
00449     return linecxt;
00450 }
00451 
00452 
00453 /*
00454  * Does user belong to role?
00455  *
00456  * userid is the OID of the role given as the attempted login identifier.
00457  * We check to see if it is a member of the specified role name.
00458  */
00459 static bool
00460 is_member(Oid userid, const char *role)
00461 {
00462     Oid         roleid;
00463 
00464     if (!OidIsValid(userid))
00465         return false;           /* if user not exist, say "no" */
00466 
00467     roleid = get_role_oid(role, true);
00468 
00469     if (!OidIsValid(roleid))
00470         return false;           /* if target role not exist, say "no" */
00471 
00472     /*
00473      * See if user is directly or indirectly a member of role. For this
00474      * purpose, a superuser is not considered to be automatically a member of
00475      * the role, so group auth only applies to explicit membership.
00476      */
00477     return is_member_of_role_nosuper(userid, roleid);
00478 }
00479 
00480 /*
00481  * Check HbaToken list for a match to role, allowing group names.
00482  */
00483 static bool
00484 check_role(const char *role, Oid roleid, List *tokens)
00485 {
00486     ListCell   *cell;
00487     HbaToken   *tok;
00488 
00489     foreach(cell, tokens)
00490     {
00491         tok = lfirst(cell);
00492         if (!tok->quoted && tok->string[0] == '+')
00493         {
00494             if (is_member(roleid, tok->string + 1))
00495                 return true;
00496         }
00497         else if (token_matches(tok, role) ||
00498                  token_is_keyword(tok, "all"))
00499             return true;
00500     }
00501     return false;
00502 }
00503 
00504 /*
00505  * Check to see if db/role combination matches HbaToken list.
00506  */
00507 static bool
00508 check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
00509 {
00510     ListCell   *cell;
00511     HbaToken   *tok;
00512 
00513     foreach(cell, tokens)
00514     {
00515         tok = lfirst(cell);
00516         if (am_walsender)
00517         {
00518             /* walsender connections can only match replication keyword */
00519             if (token_is_keyword(tok, "replication"))
00520                 return true;
00521         }
00522         else if (token_is_keyword(tok, "all"))
00523             return true;
00524         else if (token_is_keyword(tok, "sameuser"))
00525         {
00526             if (strcmp(dbname, role) == 0)
00527                 return true;
00528         }
00529         else if (token_is_keyword(tok, "samegroup") ||
00530                  token_is_keyword(tok, "samerole"))
00531         {
00532             if (is_member(roleid, dbname))
00533                 return true;
00534         }
00535         else if (token_is_keyword(tok, "replication"))
00536             continue;           /* never match this if not walsender */
00537         else if (token_matches(tok, dbname))
00538             return true;
00539     }
00540     return false;
00541 }
00542 
00543 static bool
00544 ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
00545 {
00546     return (a->sin_addr.s_addr == b->sin_addr.s_addr);
00547 }
00548 
00549 #ifdef HAVE_IPV6
00550 
00551 static bool
00552 ipv6eq(struct sockaddr_in6 * a, struct sockaddr_in6 * b)
00553 {
00554     int         i;
00555 
00556     for (i = 0; i < 16; i++)
00557         if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
00558             return false;
00559 
00560     return true;
00561 }
00562 #endif   /* HAVE_IPV6 */
00563 
00564 /*
00565  * Check whether host name matches pattern.
00566  */
00567 static bool
00568 hostname_match(const char *pattern, const char *actual_hostname)
00569 {
00570     if (pattern[0] == '.')      /* suffix match */
00571     {
00572         size_t      plen = strlen(pattern);
00573         size_t      hlen = strlen(actual_hostname);
00574 
00575         if (hlen < plen)
00576             return false;
00577 
00578         return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
00579     }
00580     else
00581         return (pg_strcasecmp(pattern, actual_hostname) == 0);
00582 }
00583 
00584 /*
00585  * Check to see if a connecting IP matches a given host name.
00586  */
00587 static bool
00588 check_hostname(hbaPort *port, const char *hostname)
00589 {
00590     struct addrinfo *gai_result,
00591                *gai;
00592     int         ret;
00593     bool        found;
00594 
00595     /* Lookup remote host name if not already done */
00596     if (!port->remote_hostname)
00597     {
00598         char        remote_hostname[NI_MAXHOST];
00599 
00600         if (pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
00601                                remote_hostname, sizeof(remote_hostname),
00602                                NULL, 0,
00603                                0) != 0)
00604             return false;
00605 
00606         port->remote_hostname = pstrdup(remote_hostname);
00607     }
00608 
00609     if (!hostname_match(hostname, port->remote_hostname))
00610         return false;
00611 
00612     /* Lookup IP from host name and check against original IP */
00613 
00614     if (port->remote_hostname_resolv == +1)
00615         return true;
00616     if (port->remote_hostname_resolv == -1)
00617         return false;
00618 
00619     ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
00620     if (ret != 0)
00621         ereport(ERROR,
00622                 (errmsg("could not translate host name \"%s\" to address: %s",
00623                         port->remote_hostname, gai_strerror(ret))));
00624 
00625     found = false;
00626     for (gai = gai_result; gai; gai = gai->ai_next)
00627     {
00628         if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
00629         {
00630             if (gai->ai_addr->sa_family == AF_INET)
00631             {
00632                 if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
00633                            (struct sockaddr_in *) & port->raddr.addr))
00634                 {
00635                     found = true;
00636                     break;
00637                 }
00638             }
00639 #ifdef HAVE_IPV6
00640             else if (gai->ai_addr->sa_family == AF_INET6)
00641             {
00642                 if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
00643                            (struct sockaddr_in6 *) & port->raddr.addr))
00644                 {
00645                     found = true;
00646                     break;
00647                 }
00648             }
00649 #endif
00650         }
00651     }
00652 
00653     if (gai_result)
00654         freeaddrinfo(gai_result);
00655 
00656     if (!found)
00657         elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
00658              hostname);
00659 
00660     port->remote_hostname_resolv = found ? +1 : -1;
00661 
00662     return found;
00663 }
00664 
00665 /*
00666  * Check to see if a connecting IP matches the given address and netmask.
00667  */
00668 static bool
00669 check_ip(SockAddr *raddr, struct sockaddr * addr, struct sockaddr * mask)
00670 {
00671     if (raddr->addr.ss_family == addr->sa_family)
00672     {
00673         /* Same address family */
00674         if (!pg_range_sockaddr(&raddr->addr,
00675                                (struct sockaddr_storage *) addr,
00676                                (struct sockaddr_storage *) mask))
00677             return false;
00678     }
00679 #ifdef HAVE_IPV6
00680     else if (addr->sa_family == AF_INET &&
00681              raddr->addr.ss_family == AF_INET6)
00682     {
00683         /*
00684          * If we're connected on IPv6 but the file specifies an IPv4 address
00685          * to match against, promote the latter to an IPv6 address before
00686          * trying to match the client's address.
00687          */
00688         struct sockaddr_storage addrcopy,
00689                     maskcopy;
00690 
00691         memcpy(&addrcopy, &addr, sizeof(addrcopy));
00692         memcpy(&maskcopy, &mask, sizeof(maskcopy));
00693         pg_promote_v4_to_v6_addr(&addrcopy);
00694         pg_promote_v4_to_v6_mask(&maskcopy);
00695 
00696         if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
00697             return false;
00698     }
00699 #endif   /* HAVE_IPV6 */
00700     else
00701     {
00702         /* Wrong address family, no IPV6 */
00703         return false;
00704     }
00705 
00706     return true;
00707 }
00708 
00709 /*
00710  * pg_foreach_ifaddr callback: does client addr match this machine interface?
00711  */
00712 static void
00713 check_network_callback(struct sockaddr * addr, struct sockaddr * netmask,
00714                        void *cb_data)
00715 {
00716     check_network_data *cn = (check_network_data *) cb_data;
00717     struct sockaddr_storage mask;
00718 
00719     /* Already found a match? */
00720     if (cn->result)
00721         return;
00722 
00723     if (cn->method == ipCmpSameHost)
00724     {
00725         /* Make an all-ones netmask of appropriate length for family */
00726         pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
00727         cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) & mask);
00728     }
00729     else
00730     {
00731         /* Use the netmask of the interface itself */
00732         cn->result = check_ip(cn->raddr, addr, netmask);
00733     }
00734 }
00735 
00736 /*
00737  * Use pg_foreach_ifaddr to check a samehost or samenet match
00738  */
00739 static bool
00740 check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
00741 {
00742     check_network_data cn;
00743 
00744     cn.method = method;
00745     cn.raddr = raddr;
00746     cn.result = false;
00747 
00748     errno = 0;
00749     if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
00750     {
00751         elog(LOG, "error enumerating network interfaces: %m");
00752         return false;
00753     }
00754 
00755     return cn.result;
00756 }
00757 
00758 
00759 /*
00760  * Macros used to check and report on invalid configuration options.
00761  * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's
00762  *                       not supported.
00763  * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the
00764  *                       method is actually the one specified. Used as a shortcut when
00765  *                       the option is only valid for one authentication method.
00766  * MANDATORY_AUTH_ARG  = check if a required option is set for an authentication method,
00767  *                       reporting error if it's not.
00768  */
00769 #define INVALID_AUTH_OPTION(optname, validmethods) do {\
00770     ereport(LOG, \
00771             (errcode(ERRCODE_CONFIG_FILE_ERROR), \
00772              /* translator: the second %s is a list of auth methods */ \
00773              errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
00774                     optname, _(validmethods)), \
00775              errcontext("line %d of configuration file \"%s\"", \
00776                     line_num, HbaFileName))); \
00777     return false; \
00778 } while (0);
00779 
00780 #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
00781     if (hbaline->auth_method != methodval) \
00782         INVALID_AUTH_OPTION(optname, validmethods); \
00783 } while (0);
00784 
00785 #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\
00786     if (argvar == NULL) {\
00787         ereport(LOG, \
00788                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
00789                  errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
00790                         authname, argname), \
00791                  errcontext("line %d of configuration file \"%s\"", \
00792                         line_num, HbaFileName))); \
00793         return NULL; \
00794     } \
00795 } while (0);
00796 
00797 /*
00798  * IDENT_FIELD_ABSENT:
00799  * Throw an error and exit the function if the given ident field ListCell is
00800  * not populated.
00801  *
00802  * IDENT_MULTI_VALUE:
00803  * Throw an error and exit the function if the given ident token List has more
00804  * than one element.
00805  */
00806 #define IDENT_FIELD_ABSENT(field) do {\
00807     if (!field) { \
00808         ereport(LOG, \
00809                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
00810                  errmsg("missing entry in file \"%s\" at end of line %d", \
00811                         IdentFileName, line_number))); \
00812         return NULL; \
00813     } \
00814 } while (0);
00815 
00816 #define IDENT_MULTI_VALUE(tokens) do {\
00817     if (tokens->length > 1) { \
00818         ereport(LOG, \
00819                 (errcode(ERRCODE_CONFIG_FILE_ERROR), \
00820                  errmsg("multiple values in ident field"), \
00821                  errcontext("line %d of configuration file \"%s\"", \
00822                         line_number, IdentFileName))); \
00823         return NULL; \
00824     } \
00825 } while (0);
00826 
00827 
00828 /*
00829  * Parse one tokenised line from the hba config file and store the result in a
00830  * HbaLine structure, or NULL if parsing fails.
00831  *
00832  * The tokenised line is a List of fields, each field being a List of
00833  * HbaTokens.
00834  *
00835  * Note: this function leaks memory when an error occurs.  Caller is expected
00836  * to have set a memory context that will be reset if this function returns
00837  * NULL.
00838  */
00839 static HbaLine *
00840 parse_hba_line(List *line, int line_num, char *raw_line)
00841 {
00842     char       *str;
00843     struct addrinfo *gai_result;
00844     struct addrinfo hints;
00845     int         ret;
00846     char       *cidr_slash;
00847     char       *unsupauth;
00848     ListCell   *field;
00849     List       *tokens;
00850     ListCell   *tokencell;
00851     HbaToken   *token;
00852     HbaLine    *parsedline;
00853 
00854     parsedline = palloc0(sizeof(HbaLine));
00855     parsedline->linenumber = line_num;
00856     parsedline->rawline = pstrdup(raw_line);
00857 
00858     /* Check the record type. */
00859     field = list_head(line);
00860     tokens = lfirst(field);
00861     if (tokens->length > 1)
00862     {
00863         ereport(LOG,
00864                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
00865                  errmsg("multiple values specified for connection type"),
00866                  errhint("Specify exactly one connection type per line."),
00867                  errcontext("line %d of configuration file \"%s\"",
00868                             line_num, HbaFileName)));
00869         return NULL;
00870     }
00871     token = linitial(tokens);
00872     if (strcmp(token->string, "local") == 0)
00873     {
00874 #ifdef HAVE_UNIX_SOCKETS
00875         parsedline->conntype = ctLocal;
00876 #else
00877         ereport(LOG,
00878                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
00879                  errmsg("local connections are not supported by this build"),
00880                  errcontext("line %d of configuration file \"%s\"",
00881                             line_num, HbaFileName)));
00882         return NULL;
00883 #endif
00884     }
00885     else if (strcmp(token->string, "host") == 0 ||
00886              strcmp(token->string, "hostssl") == 0 ||
00887              strcmp(token->string, "hostnossl") == 0)
00888     {
00889 
00890         if (token->string[4] == 's')    /* "hostssl" */
00891         {
00892             /* SSL support must be actually active, else complain */
00893 #ifdef USE_SSL
00894             if (EnableSSL)
00895                 parsedline->conntype = ctHostSSL;
00896             else
00897             {
00898                 ereport(LOG,
00899                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
00900                          errmsg("hostssl requires SSL to be turned on"),
00901                          errhint("Set ssl = on in postgresql.conf."),
00902                          errcontext("line %d of configuration file \"%s\"",
00903                                     line_num, HbaFileName)));
00904                 return NULL;
00905             }
00906 #else
00907             ereport(LOG,
00908                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
00909                      errmsg("hostssl is not supported by this build"),
00910               errhint("Compile with --with-openssl to use SSL connections."),
00911                      errcontext("line %d of configuration file \"%s\"",
00912                                 line_num, HbaFileName)));
00913             return NULL;
00914 #endif
00915         }
00916 #ifdef USE_SSL
00917         else if (token->string[4] == 'n')       /* "hostnossl" */
00918         {
00919             parsedline->conntype = ctHostNoSSL;
00920         }
00921 #endif
00922         else
00923         {
00924             /* "host", or "hostnossl" and SSL support not built in */
00925             parsedline->conntype = ctHost;
00926         }
00927     }                           /* record type */
00928     else
00929     {
00930         ereport(LOG,
00931                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
00932                  errmsg("invalid connection type \"%s\"",
00933                         token->string),
00934                  errcontext("line %d of configuration file \"%s\"",
00935                             line_num, HbaFileName)));
00936         return NULL;
00937     }
00938 
00939     /* Get the databases. */
00940     field = lnext(field);
00941     if (!field)
00942     {
00943         ereport(LOG,
00944                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
00945                  errmsg("end-of-line before database specification"),
00946                  errcontext("line %d of configuration file \"%s\"",
00947                             line_num, HbaFileName)));
00948         return NULL;
00949     }
00950     parsedline->databases = NIL;
00951     tokens = lfirst(field);
00952     foreach(tokencell, tokens)
00953     {
00954         parsedline->databases = lappend(parsedline->databases,
00955                                         copy_hba_token(lfirst(tokencell)));
00956     }
00957 
00958     /* Get the roles. */
00959     field = lnext(field);
00960     if (!field)
00961     {
00962         ereport(LOG,
00963                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
00964                  errmsg("end-of-line before role specification"),
00965                  errcontext("line %d of configuration file \"%s\"",
00966                             line_num, HbaFileName)));
00967         return NULL;
00968     }
00969     parsedline->roles = NIL;
00970     tokens = lfirst(field);
00971     foreach(tokencell, tokens)
00972     {
00973         parsedline->roles = lappend(parsedline->roles,
00974                                     copy_hba_token(lfirst(tokencell)));
00975     }
00976 
00977     if (parsedline->conntype != ctLocal)
00978     {
00979         /* Read the IP address field. (with or without CIDR netmask) */
00980         field = lnext(field);
00981         if (!field)
00982         {
00983             ereport(LOG,
00984                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
00985                      errmsg("end-of-line before IP address specification"),
00986                      errcontext("line %d of configuration file \"%s\"",
00987                                 line_num, HbaFileName)));
00988             return NULL;
00989         }
00990         tokens = lfirst(field);
00991         if (tokens->length > 1)
00992         {
00993             ereport(LOG,
00994                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
00995                      errmsg("multiple values specified for host address"),
00996                      errhint("Specify one address range per line."),
00997                      errcontext("line %d of configuration file \"%s\"",
00998                                 line_num, HbaFileName)));
00999             return NULL;
01000         }
01001         token = linitial(tokens);
01002 
01003         if (token_is_keyword(token, "all"))
01004         {
01005             parsedline->ip_cmp_method = ipCmpAll;
01006         }
01007         else if (token_is_keyword(token, "samehost"))
01008         {
01009             /* Any IP on this host is allowed to connect */
01010             parsedline->ip_cmp_method = ipCmpSameHost;
01011         }
01012         else if (token_is_keyword(token, "samenet"))
01013         {
01014             /* Any IP on the host's subnets is allowed to connect */
01015             parsedline->ip_cmp_method = ipCmpSameNet;
01016         }
01017         else
01018         {
01019             /* IP and netmask are specified */
01020             parsedline->ip_cmp_method = ipCmpMask;
01021 
01022             /* need a modifiable copy of token */
01023             str = pstrdup(token->string);
01024 
01025             /* Check if it has a CIDR suffix and if so isolate it */
01026             cidr_slash = strchr(str, '/');
01027             if (cidr_slash)
01028                 *cidr_slash = '\0';
01029 
01030             /* Get the IP address either way */
01031             hints.ai_flags = AI_NUMERICHOST;
01032             hints.ai_family = PF_UNSPEC;
01033             hints.ai_socktype = 0;
01034             hints.ai_protocol = 0;
01035             hints.ai_addrlen = 0;
01036             hints.ai_canonname = NULL;
01037             hints.ai_addr = NULL;
01038             hints.ai_next = NULL;
01039 
01040             ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
01041             if (ret == 0 && gai_result)
01042                 memcpy(&parsedline->addr, gai_result->ai_addr,
01043                        gai_result->ai_addrlen);
01044             else if (ret == EAI_NONAME)
01045                 parsedline->hostname = str;
01046             else
01047             {
01048                 ereport(LOG,
01049                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
01050                          errmsg("invalid IP address \"%s\": %s",
01051                                 str, gai_strerror(ret)),
01052                          errcontext("line %d of configuration file \"%s\"",
01053                                     line_num, HbaFileName)));
01054                 if (gai_result)
01055                     pg_freeaddrinfo_all(hints.ai_family, gai_result);
01056                 return NULL;
01057             }
01058 
01059             pg_freeaddrinfo_all(hints.ai_family, gai_result);
01060 
01061             /* Get the netmask */
01062             if (cidr_slash)
01063             {
01064                 if (parsedline->hostname)
01065                 {
01066                     ereport(LOG,
01067                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01068                              errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
01069                                     token->string),
01070                            errcontext("line %d of configuration file \"%s\"",
01071                                       line_num, HbaFileName)));
01072                     return NULL;
01073                 }
01074 
01075                 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
01076                                           parsedline->addr.ss_family) < 0)
01077                 {
01078                     ereport(LOG,
01079                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01080                              errmsg("invalid CIDR mask in address \"%s\"",
01081                                     token->string),
01082                            errcontext("line %d of configuration file \"%s\"",
01083                                       line_num, HbaFileName)));
01084                     return NULL;
01085                 }
01086                 pfree(str);
01087             }
01088             else if (!parsedline->hostname)
01089             {
01090                 /* Read the mask field. */
01091                 pfree(str);
01092                 field = lnext(field);
01093                 if (!field)
01094                 {
01095                     ereport(LOG,
01096                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01097                           errmsg("end-of-line before netmask specification"),
01098                              errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
01099                            errcontext("line %d of configuration file \"%s\"",
01100                                       line_num, HbaFileName)));
01101                     return NULL;
01102                 }
01103                 tokens = lfirst(field);
01104                 if (tokens->length > 1)
01105                 {
01106                     ereport(LOG,
01107                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01108                              errmsg("multiple values specified for netmask"),
01109                            errcontext("line %d of configuration file \"%s\"",
01110                                       line_num, HbaFileName)));
01111                     return NULL;
01112                 }
01113                 token = linitial(tokens);
01114 
01115                 ret = pg_getaddrinfo_all(token->string, NULL,
01116                                          &hints, &gai_result);
01117                 if (ret || !gai_result)
01118                 {
01119                     ereport(LOG,
01120                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01121                              errmsg("invalid IP mask \"%s\": %s",
01122                                     token->string, gai_strerror(ret)),
01123                            errcontext("line %d of configuration file \"%s\"",
01124                                       line_num, HbaFileName)));
01125                     if (gai_result)
01126                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
01127                     return NULL;
01128                 }
01129 
01130                 memcpy(&parsedline->mask, gai_result->ai_addr,
01131                        gai_result->ai_addrlen);
01132                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
01133 
01134                 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
01135                 {
01136                     ereport(LOG,
01137                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
01138                              errmsg("IP address and mask do not match"),
01139                            errcontext("line %d of configuration file \"%s\"",
01140                                       line_num, HbaFileName)));
01141                     return NULL;
01142                 }
01143             }
01144         }
01145     }                           /* != ctLocal */
01146 
01147     /* Get the authentication method */
01148     field = lnext(field);
01149     if (!field)
01150     {
01151         ereport(LOG,
01152                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01153                  errmsg("end-of-line before authentication method"),
01154                  errcontext("line %d of configuration file \"%s\"",
01155                             line_num, HbaFileName)));
01156         return NULL;
01157     }
01158     tokens = lfirst(field);
01159     if (tokens->length > 1)
01160     {
01161         ereport(LOG,
01162                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01163                  errmsg("multiple values specified for authentication type"),
01164                  errhint("Specify exactly one authentication type per line."),
01165                  errcontext("line %d of configuration file \"%s\"",
01166                             line_num, HbaFileName)));
01167         return NULL;
01168     }
01169     token = linitial(tokens);
01170 
01171     unsupauth = NULL;
01172     if (strcmp(token->string, "trust") == 0)
01173         parsedline->auth_method = uaTrust;
01174     else if (strcmp(token->string, "ident") == 0)
01175         parsedline->auth_method = uaIdent;
01176     else if (strcmp(token->string, "peer") == 0)
01177         parsedline->auth_method = uaPeer;
01178     else if (strcmp(token->string, "password") == 0)
01179         parsedline->auth_method = uaPassword;
01180     else if (strcmp(token->string, "krb5") == 0)
01181 #ifdef KRB5
01182         parsedline->auth_method = uaKrb5;
01183 #else
01184         unsupauth = "krb5";
01185 #endif
01186     else if (strcmp(token->string, "gss") == 0)
01187 #ifdef ENABLE_GSS
01188         parsedline->auth_method = uaGSS;
01189 #else
01190         unsupauth = "gss";
01191 #endif
01192     else if (strcmp(token->string, "sspi") == 0)
01193 #ifdef ENABLE_SSPI
01194         parsedline->auth_method = uaSSPI;
01195 #else
01196         unsupauth = "sspi";
01197 #endif
01198     else if (strcmp(token->string, "reject") == 0)
01199         parsedline->auth_method = uaReject;
01200     else if (strcmp(token->string, "md5") == 0)
01201     {
01202         if (Db_user_namespace)
01203         {
01204             ereport(LOG,
01205                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01206                      errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
01207                      errcontext("line %d of configuration file \"%s\"",
01208                                 line_num, HbaFileName)));
01209             return NULL;
01210         }
01211         parsedline->auth_method = uaMD5;
01212     }
01213     else if (strcmp(token->string, "pam") == 0)
01214 #ifdef USE_PAM
01215         parsedline->auth_method = uaPAM;
01216 #else
01217         unsupauth = "pam";
01218 #endif
01219     else if (strcmp(token->string, "ldap") == 0)
01220 #ifdef USE_LDAP
01221         parsedline->auth_method = uaLDAP;
01222 #else
01223         unsupauth = "ldap";
01224 #endif
01225     else if (strcmp(token->string, "cert") == 0)
01226 #ifdef USE_SSL
01227         parsedline->auth_method = uaCert;
01228 #else
01229         unsupauth = "cert";
01230 #endif
01231     else if (strcmp(token->string, "radius") == 0)
01232         parsedline->auth_method = uaRADIUS;
01233     else
01234     {
01235         ereport(LOG,
01236                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01237                  errmsg("invalid authentication method \"%s\"",
01238                         token->string),
01239                  errcontext("line %d of configuration file \"%s\"",
01240                             line_num, HbaFileName)));
01241         return NULL;
01242     }
01243 
01244     if (unsupauth)
01245     {
01246         ereport(LOG,
01247                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01248                  errmsg("invalid authentication method \"%s\": not supported by this build",
01249                         token->string),
01250                  errcontext("line %d of configuration file \"%s\"",
01251                             line_num, HbaFileName)));
01252         return NULL;
01253     }
01254 
01255     /*
01256      * XXX: When using ident on local connections, change it to peer, for
01257      * backwards compatibility.
01258      */
01259     if (parsedline->conntype == ctLocal &&
01260         parsedline->auth_method == uaIdent)
01261         parsedline->auth_method = uaPeer;
01262 
01263     /* Invalid authentication combinations */
01264     if (parsedline->conntype == ctLocal &&
01265         parsedline->auth_method == uaKrb5)
01266     {
01267         ereport(LOG,
01268                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01269              errmsg("krb5 authentication is not supported on local sockets"),
01270                  errcontext("line %d of configuration file \"%s\"",
01271                             line_num, HbaFileName)));
01272         return NULL;
01273     }
01274 
01275     if (parsedline->conntype == ctLocal &&
01276         parsedline->auth_method == uaGSS)
01277     {
01278         ereport(LOG,
01279                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01280            errmsg("gssapi authentication is not supported on local sockets"),
01281                  errcontext("line %d of configuration file \"%s\"",
01282                             line_num, HbaFileName)));
01283         return NULL;
01284     }
01285 
01286     if (parsedline->conntype != ctLocal &&
01287         parsedline->auth_method == uaPeer)
01288     {
01289         ereport(LOG,
01290                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01291             errmsg("peer authentication is only supported on local sockets"),
01292                  errcontext("line %d of configuration file \"%s\"",
01293                             line_num, HbaFileName)));
01294         return NULL;
01295     }
01296 
01297     /*
01298      * SSPI authentication can never be enabled on ctLocal connections,
01299      * because it's only supported on Windows, where ctLocal isn't supported.
01300      */
01301 
01302 
01303     if (parsedline->conntype != ctHostSSL &&
01304         parsedline->auth_method == uaCert)
01305     {
01306         ereport(LOG,
01307                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01308                  errmsg("cert authentication is only supported on hostssl connections"),
01309                  errcontext("line %d of configuration file \"%s\"",
01310                             line_num, HbaFileName)));
01311         return NULL;
01312     }
01313 
01314     /* Parse remaining arguments */
01315     while ((field = lnext(field)) != NULL)
01316     {
01317         tokens = lfirst(field);
01318         foreach(tokencell, tokens)
01319         {
01320             char       *val;
01321 
01322             token = lfirst(tokencell);
01323 
01324             str = pstrdup(token->string);
01325             val = strchr(str, '=');
01326             if (val == NULL)
01327             {
01328                 /*
01329                  * Got something that's not a name=value pair.
01330                  */
01331                 ereport(LOG,
01332                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
01333                          errmsg("authentication option not in name=value format: %s", token->string),
01334                          errcontext("line %d of configuration file \"%s\"",
01335                                     line_num, HbaFileName)));
01336                 return NULL;
01337             }
01338 
01339             *val++ = '\0';      /* str now holds "name", val holds "value" */
01340             if (!parse_hba_auth_opt(str, val, parsedline, line_num))
01341                 /* parse_hba_auth_opt already logged the error message */
01342                 return NULL;
01343             pfree(str);
01344         }
01345     }
01346 
01347     /*
01348      * Check if the selected authentication method has any mandatory arguments
01349      * that are not set.
01350      */
01351     if (parsedline->auth_method == uaLDAP)
01352     {
01353         MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
01354 
01355         /*
01356          * LDAP can operate in two modes: either with a direct bind, using
01357          * ldapprefix and ldapsuffix, or using a search+bind, using
01358          * ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
01359          * Disallow mixing these parameters.
01360          */
01361         if (parsedline->ldapprefix || parsedline->ldapsuffix)
01362         {
01363             if (parsedline->ldapbasedn ||
01364                 parsedline->ldapbinddn ||
01365                 parsedline->ldapbindpasswd ||
01366                 parsedline->ldapsearchattribute)
01367             {
01368                 ereport(LOG,
01369                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
01370                          errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"),
01371                          errcontext("line %d of configuration file \"%s\"",
01372                                     line_num, HbaFileName)));
01373                 return NULL;
01374             }
01375         }
01376         else if (!parsedline->ldapbasedn)
01377         {
01378             ereport(LOG,
01379                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01380                      errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
01381                      errcontext("line %d of configuration file \"%s\"",
01382                                 line_num, HbaFileName)));
01383             return NULL;
01384         }
01385     }
01386 
01387     if (parsedline->auth_method == uaRADIUS)
01388     {
01389         MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
01390         MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
01391     }
01392 
01393     /*
01394      * Enforce any parameters implied by other settings.
01395      */
01396     if (parsedline->auth_method == uaCert)
01397     {
01398         parsedline->clientcert = true;
01399     }
01400 
01401     return parsedline;
01402 }
01403 
01404 /*
01405  * Parse one name-value pair as an authentication option into the given
01406  * HbaLine.  Return true if we successfully parse the option, false if we
01407  * encounter an error.
01408  */
01409 static bool
01410 parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
01411 {
01412 #ifdef USE_LDAP
01413     hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
01414 #endif
01415 
01416     if (strcmp(name, "map") == 0)
01417     {
01418         if (hbaline->auth_method != uaIdent &&
01419             hbaline->auth_method != uaPeer &&
01420             hbaline->auth_method != uaKrb5 &&
01421             hbaline->auth_method != uaGSS &&
01422             hbaline->auth_method != uaSSPI &&
01423             hbaline->auth_method != uaCert)
01424             INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi, and cert"));
01425         hbaline->usermap = pstrdup(val);
01426     }
01427     else if (strcmp(name, "clientcert") == 0)
01428     {
01429         /*
01430          * Since we require ctHostSSL, this really can never happen on
01431          * non-SSL-enabled builds, so don't bother checking for USE_SSL.
01432          */
01433         if (hbaline->conntype != ctHostSSL)
01434         {
01435             ereport(LOG,
01436                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01437             errmsg("clientcert can only be configured for \"hostssl\" rows"),
01438                      errcontext("line %d of configuration file \"%s\"",
01439                                 line_num, HbaFileName)));
01440             return false;
01441         }
01442         if (strcmp(val, "1") == 0)
01443         {
01444             if (!secure_loaded_verify_locations())
01445             {
01446                 ereport(LOG,
01447                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
01448                          errmsg("client certificates can only be checked if a root certificate store is available"),
01449                          errhint("Make sure the configuration parameter \"ssl_ca_file\" is set."),
01450                          errcontext("line %d of configuration file \"%s\"",
01451                                     line_num, HbaFileName)));
01452                 return false;
01453             }
01454             hbaline->clientcert = true;
01455         }
01456         else
01457         {
01458             if (hbaline->auth_method == uaCert)
01459             {
01460                 ereport(LOG,
01461                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
01462                          errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
01463                          errcontext("line %d of configuration file \"%s\"",
01464                                     line_num, HbaFileName)));
01465                 return false;
01466             }
01467             hbaline->clientcert = false;
01468         }
01469     }
01470     else if (strcmp(name, "pamservice") == 0)
01471     {
01472         REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
01473         hbaline->pamservice = pstrdup(val);
01474     }
01475     else if (strcmp(name, "ldapurl") == 0)
01476     {
01477 #ifdef LDAP_API_FEATURE_X_OPENLDAP
01478         LDAPURLDesc *urldata;
01479         int rc;
01480 #endif
01481 
01482         REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
01483 #ifdef LDAP_API_FEATURE_X_OPENLDAP
01484         rc = ldap_url_parse(val, &urldata);
01485         if (rc != LDAP_SUCCESS)
01486         {
01487             ereport(LOG,
01488                  (errcode(ERRCODE_CONFIG_FILE_ERROR),
01489                   errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
01490             return false;
01491         }
01492 
01493         if (strcmp(urldata->lud_scheme, "ldap") != 0)
01494         {
01495             ereport(LOG,
01496                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01497                      errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
01498             ldap_free_urldesc(urldata);
01499             return false;
01500         }
01501 
01502         hbaline->ldapserver = pstrdup(urldata->lud_host);
01503         hbaline->ldapport = urldata->lud_port;
01504         hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
01505 
01506         if (urldata->lud_attrs)
01507             hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]);  /* only use first one */
01508         hbaline->ldapscope = urldata->lud_scope;
01509         if (urldata->lud_filter)
01510         {
01511             ereport(LOG,
01512                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01513                      errmsg("filters not supported in LDAP URLs")));
01514             ldap_free_urldesc(urldata);
01515             return false;
01516         }
01517         ldap_free_urldesc(urldata);
01518 #else /* not OpenLDAP */
01519         ereport(LOG,
01520                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01521                  errmsg("LDAP URLs not supported on this platform")));
01522 #endif /* not OpenLDAP */
01523     }
01524     else if (strcmp(name, "ldaptls") == 0)
01525     {
01526         REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
01527         if (strcmp(val, "1") == 0)
01528             hbaline->ldaptls = true;
01529         else
01530             hbaline->ldaptls = false;
01531     }
01532     else if (strcmp(name, "ldapserver") == 0)
01533     {
01534         REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
01535         hbaline->ldapserver = pstrdup(val);
01536     }
01537     else if (strcmp(name, "ldapport") == 0)
01538     {
01539         REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
01540         hbaline->ldapport = atoi(val);
01541         if (hbaline->ldapport == 0)
01542         {
01543             ereport(LOG,
01544                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01545                      errmsg("invalid LDAP port number: \"%s\"", val),
01546                      errcontext("line %d of configuration file \"%s\"",
01547                                 line_num, HbaFileName)));
01548             return false;
01549         }
01550     }
01551     else if (strcmp(name, "ldapbinddn") == 0)
01552     {
01553         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
01554         hbaline->ldapbinddn = pstrdup(val);
01555     }
01556     else if (strcmp(name, "ldapbindpasswd") == 0)
01557     {
01558         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
01559         hbaline->ldapbindpasswd = pstrdup(val);
01560     }
01561     else if (strcmp(name, "ldapsearchattribute") == 0)
01562     {
01563         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
01564         hbaline->ldapsearchattribute = pstrdup(val);
01565     }
01566     else if (strcmp(name, "ldapbasedn") == 0)
01567     {
01568         REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
01569         hbaline->ldapbasedn = pstrdup(val);
01570     }
01571     else if (strcmp(name, "ldapprefix") == 0)
01572     {
01573         REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
01574         hbaline->ldapprefix = pstrdup(val);
01575     }
01576     else if (strcmp(name, "ldapsuffix") == 0)
01577     {
01578         REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
01579         hbaline->ldapsuffix = pstrdup(val);
01580     }
01581     else if (strcmp(name, "krb_server_hostname") == 0)
01582     {
01583         REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
01584         hbaline->krb_server_hostname = pstrdup(val);
01585     }
01586     else if (strcmp(name, "krb_realm") == 0)
01587     {
01588         if (hbaline->auth_method != uaKrb5 &&
01589             hbaline->auth_method != uaGSS &&
01590             hbaline->auth_method != uaSSPI)
01591             INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi, and sspi"));
01592         hbaline->krb_realm = pstrdup(val);
01593     }
01594     else if (strcmp(name, "include_realm") == 0)
01595     {
01596         if (hbaline->auth_method != uaKrb5 &&
01597             hbaline->auth_method != uaGSS &&
01598             hbaline->auth_method != uaSSPI)
01599             INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi, and sspi"));
01600         if (strcmp(val, "1") == 0)
01601             hbaline->include_realm = true;
01602         else
01603             hbaline->include_realm = false;
01604     }
01605     else if (strcmp(name, "radiusserver") == 0)
01606     {
01607         struct addrinfo *gai_result;
01608         struct addrinfo hints;
01609         int         ret;
01610 
01611         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
01612 
01613         MemSet(&hints, 0, sizeof(hints));
01614         hints.ai_socktype = SOCK_DGRAM;
01615         hints.ai_family = AF_UNSPEC;
01616 
01617         ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
01618         if (ret || !gai_result)
01619         {
01620             ereport(LOG,
01621                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01622                      errmsg("could not translate RADIUS server name \"%s\" to address: %s",
01623                             val, gai_strerror(ret)),
01624                      errcontext("line %d of configuration file \"%s\"",
01625                                 line_num, HbaFileName)));
01626             if (gai_result)
01627                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
01628             return false;
01629         }
01630         pg_freeaddrinfo_all(hints.ai_family, gai_result);
01631         hbaline->radiusserver = pstrdup(val);
01632     }
01633     else if (strcmp(name, "radiusport") == 0)
01634     {
01635         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
01636         hbaline->radiusport = atoi(val);
01637         if (hbaline->radiusport == 0)
01638         {
01639             ereport(LOG,
01640                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
01641                      errmsg("invalid RADIUS port number: \"%s\"", val),
01642                      errcontext("line %d of configuration file \"%s\"",
01643                                 line_num, HbaFileName)));
01644             return false;
01645         }
01646     }
01647     else if (strcmp(name, "radiussecret") == 0)
01648     {
01649         REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
01650         hbaline->radiussecret = pstrdup(val);
01651     }
01652     else if (strcmp(name, "radiusidentifier") == 0)
01653     {
01654         REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
01655         hbaline->radiusidentifier = pstrdup(val);
01656     }
01657     else
01658     {
01659         ereport(LOG,
01660                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01661                  errmsg("unrecognized authentication option name: \"%s\"",
01662                         name),
01663                  errcontext("line %d of configuration file \"%s\"",
01664                             line_num, HbaFileName)));
01665         return false;
01666     }
01667     return true;
01668 }
01669 
01670 /*
01671  *  Scan the pre-parsed hba file, looking for a match to the port's connection
01672  *  request.
01673  */
01674 static void
01675 check_hba(hbaPort *port)
01676 {
01677     Oid         roleid;
01678     ListCell   *line;
01679     HbaLine    *hba;
01680 
01681     /* Get the target role's OID.  Note we do not error out for bad role. */
01682     roleid = get_role_oid(port->user_name, true);
01683 
01684     foreach(line, parsed_hba_lines)
01685     {
01686         hba = (HbaLine *) lfirst(line);
01687 
01688         /* Check connection type */
01689         if (hba->conntype == ctLocal)
01690         {
01691             if (!IS_AF_UNIX(port->raddr.addr.ss_family))
01692                 continue;
01693         }
01694         else
01695         {
01696             if (IS_AF_UNIX(port->raddr.addr.ss_family))
01697                 continue;
01698 
01699             /* Check SSL state */
01700 #ifdef USE_SSL
01701             if (port->ssl)
01702             {
01703                 /* Connection is SSL, match both "host" and "hostssl" */
01704                 if (hba->conntype == ctHostNoSSL)
01705                     continue;
01706             }
01707             else
01708             {
01709                 /* Connection is not SSL, match both "host" and "hostnossl" */
01710                 if (hba->conntype == ctHostSSL)
01711                     continue;
01712             }
01713 #else
01714             /* No SSL support, so reject "hostssl" lines */
01715             if (hba->conntype == ctHostSSL)
01716                 continue;
01717 #endif
01718 
01719             /* Check IP address */
01720             switch (hba->ip_cmp_method)
01721             {
01722                 case ipCmpMask:
01723                     if (hba->hostname)
01724                     {
01725                         if (!check_hostname(port,
01726                                             hba->hostname))
01727                             continue;
01728                     }
01729                     else
01730                     {
01731                         if (!check_ip(&port->raddr,
01732                                       (struct sockaddr *) & hba->addr,
01733                                       (struct sockaddr *) & hba->mask))
01734                             continue;
01735                     }
01736                     break;
01737                 case ipCmpAll:
01738                     break;
01739                 case ipCmpSameHost:
01740                 case ipCmpSameNet:
01741                     if (!check_same_host_or_net(&port->raddr,
01742                                                 hba->ip_cmp_method))
01743                         continue;
01744                     break;
01745                 default:
01746                     /* shouldn't get here, but deem it no-match if so */
01747                     continue;
01748             }
01749         }                       /* != ctLocal */
01750 
01751         /* Check database and role */
01752         if (!check_db(port->database_name, port->user_name, roleid,
01753                       hba->databases))
01754             continue;
01755 
01756         if (!check_role(port->user_name, roleid, hba->roles))
01757             continue;
01758 
01759         /* Found a record that matched! */
01760         port->hba = hba;
01761         return;
01762     }
01763 
01764     /* If no matching entry was found, then implicitly reject. */
01765     hba = palloc0(sizeof(HbaLine));
01766     hba->auth_method = uaImplicitReject;
01767     port->hba = hba;
01768 }
01769 
01770 /*
01771  * Read the config file and create a List of HbaLine records for the contents.
01772  *
01773  * The configuration is read into a temporary list, and if any parse error
01774  * occurs the old list is kept in place and false is returned.  Only if the
01775  * whole file parses OK is the list replaced, and the function returns true.
01776  *
01777  * On a false result, caller will take care of reporting a FATAL error in case
01778  * this is the initial startup.  If it happens on reload, we just keep running
01779  * with the old data.
01780  */
01781 bool
01782 load_hba(void)
01783 {
01784     FILE       *file;
01785     List       *hba_lines = NIL;
01786     List       *hba_line_nums = NIL;
01787     List       *hba_raw_lines = NIL;
01788     ListCell   *line,
01789                *line_num,
01790                *raw_line;
01791     List       *new_parsed_lines = NIL;
01792     bool        ok = true;
01793     MemoryContext linecxt;
01794     MemoryContext oldcxt;
01795     MemoryContext hbacxt;
01796 
01797     file = AllocateFile(HbaFileName, "r");
01798     if (file == NULL)
01799     {
01800         ereport(LOG,
01801                 (errcode_for_file_access(),
01802                  errmsg("could not open configuration file \"%s\": %m",
01803                         HbaFileName)));
01804         return false;
01805     }
01806 
01807     linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines);
01808     FreeFile(file);
01809 
01810     /* Now parse all the lines */
01811     hbacxt = AllocSetContextCreate(TopMemoryContext,
01812                                    "hba parser context",
01813                                    ALLOCSET_DEFAULT_MINSIZE,
01814                                    ALLOCSET_DEFAULT_MINSIZE,
01815                                    ALLOCSET_DEFAULT_MAXSIZE);
01816     oldcxt = MemoryContextSwitchTo(hbacxt);
01817     forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines)
01818     {
01819         HbaLine    *newline;
01820 
01821         if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line))) == NULL)
01822         {
01823             /*
01824              * Parse error in the file, so indicate there's a problem.  NB: a
01825              * problem in a line will free the memory for all previous lines
01826              * as well!
01827              */
01828             MemoryContextReset(hbacxt);
01829             new_parsed_lines = NIL;
01830             ok = false;
01831 
01832             /*
01833              * Keep parsing the rest of the file so we can report errors on
01834              * more than the first row. Error has already been reported in the
01835              * parsing function, so no need to log it here.
01836              */
01837             continue;
01838         }
01839 
01840         new_parsed_lines = lappend(new_parsed_lines, newline);
01841     }
01842 
01843     /*
01844      * A valid HBA file must have at least one entry; else there's no way to
01845      * connect to the postmaster.  But only complain about this if we didn't
01846      * already have parsing errors.
01847      */
01848     if (ok && new_parsed_lines == NIL)
01849     {
01850         ereport(LOG,
01851                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
01852                  errmsg("configuration file \"%s\" contains no entries",
01853                         HbaFileName)));
01854         ok = false;
01855     }
01856 
01857     /* Free tokenizer memory */
01858     MemoryContextDelete(linecxt);
01859     MemoryContextSwitchTo(oldcxt);
01860 
01861     if (!ok)
01862     {
01863         /* File contained one or more errors, so bail out */
01864         MemoryContextDelete(hbacxt);
01865         return false;
01866     }
01867 
01868     /* Loaded new file successfully, replace the one we use */
01869     if (parsed_hba_context != NULL)
01870         MemoryContextDelete(parsed_hba_context);
01871     parsed_hba_context = hbacxt;
01872     parsed_hba_lines = new_parsed_lines;
01873 
01874     return true;
01875 }
01876 
01877 /*
01878  * Parse one tokenised line from the ident config file and store the result in
01879  * an IdentLine structure, or NULL if parsing fails.
01880  *
01881  * The tokenised line is a nested List of fields and tokens.
01882  *
01883  * If ident_user is a regular expression (ie. begins with a slash), it is
01884  * compiled and stored in IdentLine structure.
01885  *
01886  * Note: this function leaks memory when an error occurs.  Caller is expected
01887  * to have set a memory context that will be reset if this function returns
01888  * NULL.
01889  */
01890 static IdentLine *
01891 parse_ident_line(List *line, int line_number)
01892 {
01893     ListCell   *field;
01894     List       *tokens;
01895     HbaToken   *token;
01896     IdentLine  *parsedline;
01897 
01898     Assert(line != NIL);
01899     field = list_head(line);
01900 
01901     parsedline = palloc0(sizeof(IdentLine));
01902     parsedline->linenumber = line_number;
01903 
01904     /* Get the map token (must exist) */
01905     tokens = lfirst(field);
01906     IDENT_MULTI_VALUE(tokens);
01907     token = linitial(tokens);
01908     parsedline->usermap = pstrdup(token->string);
01909 
01910     /* Get the ident user token */
01911     field = lnext(field);
01912     IDENT_FIELD_ABSENT(field);
01913     tokens = lfirst(field);
01914     IDENT_MULTI_VALUE(tokens);
01915     token = linitial(tokens);
01916     parsedline->ident_user = pstrdup(token->string);
01917 
01918     /* Get the PG rolename token */
01919     field = lnext(field);
01920     IDENT_FIELD_ABSENT(field);
01921     tokens = lfirst(field);
01922     IDENT_MULTI_VALUE(tokens);
01923     token = linitial(tokens);
01924     parsedline->pg_role = pstrdup(token->string);
01925 
01926     if (parsedline->ident_user[0] == '/')
01927     {
01928         /*
01929          * When system username starts with a slash, treat it as a regular
01930          * expression. Pre-compile it.
01931          */
01932         int         r;
01933         pg_wchar   *wstr;
01934         int         wlen;
01935 
01936         wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar));
01937         wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1,
01938                                     wstr, strlen(parsedline->ident_user + 1));
01939 
01940         r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
01941         if (r)
01942         {
01943             char        errstr[100];
01944 
01945             pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
01946             ereport(LOG,
01947                     (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
01948                      errmsg("invalid regular expression \"%s\": %s",
01949                             parsedline->ident_user + 1, errstr)));
01950 
01951             pfree(wstr);
01952             return NULL;
01953         }
01954         pfree(wstr);
01955     }
01956 
01957     return parsedline;
01958 }
01959 
01960 /*
01961  *  Process one line from the parsed ident config lines.
01962  *
01963  *  Compare input parsed ident line to the needed map, pg_role and ident_user.
01964  *  *found_p and *error_p are set according to our results.
01965  */
01966 static void
01967 check_ident_usermap(IdentLine *identLine, const char *usermap_name,
01968                     const char *pg_role, const char *ident_user,
01969                     bool case_insensitive, bool *found_p, bool *error_p)
01970 {
01971     *found_p = false;
01972     *error_p = false;
01973 
01974     if (strcmp(identLine->usermap, usermap_name) != 0)
01975         /* Line does not match the map name we're looking for, so just abort */
01976         return;
01977 
01978     /* Match? */
01979     if (identLine->ident_user[0] == '/')
01980     {
01981         /*
01982          * When system username starts with a slash, treat it as a regular
01983          * expression. In this case, we process the system username as a
01984          * regular expression that returns exactly one match. This is replaced
01985          * for \1 in the database username string, if present.
01986          */
01987         int         r;
01988         regmatch_t  matches[2];
01989         pg_wchar   *wstr;
01990         int         wlen;
01991         char       *ofs;
01992         char       *regexp_pgrole;
01993 
01994         wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
01995         wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
01996 
01997         r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0);
01998         if (r)
01999         {
02000             char        errstr[100];
02001 
02002             if (r != REG_NOMATCH)
02003             {
02004                 /* REG_NOMATCH is not an error, everything else is */
02005                 pg_regerror(r, &identLine->re, errstr, sizeof(errstr));
02006                 ereport(LOG,
02007                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
02008                      errmsg("regular expression match for \"%s\" failed: %s",
02009                             identLine->ident_user + 1, errstr)));
02010                 *error_p = true;
02011             }
02012 
02013             pfree(wstr);
02014             return;
02015         }
02016         pfree(wstr);
02017 
02018         if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL)
02019         {
02020             /* substitution of the first argument requested */
02021             if (matches[1].rm_so < 0)
02022             {
02023                 ereport(LOG,
02024                         (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
02025                          errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
02026                                 identLine->ident_user + 1, identLine->pg_role)));
02027                 *error_p = true;
02028                 return;
02029             }
02030 
02031             /*
02032              * length: original length minus length of \1 plus length of match
02033              * plus null terminator
02034              */
02035             regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
02036             strncpy(regexp_pgrole, identLine->pg_role, (ofs - identLine->pg_role));
02037             memcpy(regexp_pgrole + strlen(regexp_pgrole),
02038                    ident_user + matches[1].rm_so,
02039                    matches[1].rm_eo - matches[1].rm_so);
02040             strcat(regexp_pgrole, ofs + 2);
02041         }
02042         else
02043         {
02044             /* no substitution, so copy the match */
02045             regexp_pgrole = pstrdup(identLine->pg_role);
02046         }
02047 
02048         /*
02049          * now check if the username actually matched what the user is trying
02050          * to connect as
02051          */
02052         if (case_insensitive)
02053         {
02054             if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
02055                 *found_p = true;
02056         }
02057         else
02058         {
02059             if (strcmp(regexp_pgrole, pg_role) == 0)
02060                 *found_p = true;
02061         }
02062         pfree(regexp_pgrole);
02063 
02064         return;
02065     }
02066     else
02067     {
02068         /* Not regular expression, so make complete match */
02069         if (case_insensitive)
02070         {
02071             if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 &&
02072                 pg_strcasecmp(identLine->ident_user, ident_user) == 0)
02073                 *found_p = true;
02074         }
02075         else
02076         {
02077             if (strcmp(identLine->pg_role, pg_role) == 0 &&
02078                 strcmp(identLine->ident_user, ident_user) == 0)
02079                 *found_p = true;
02080         }
02081     }
02082     return;
02083 }
02084 
02085 
02086 /*
02087  *  Scan the (pre-parsed) ident usermap file line by line, looking for a match
02088  *
02089  *  See if the user with ident username "auth_user" is allowed to act
02090  *  as Postgres user "pg_role" according to usermap "usermap_name".
02091  *
02092  *  Special case: Usermap NULL, equivalent to what was previously called
02093  *  "sameuser" or "samerole", means don't look in the usermap file.
02094  *  That's an implied map wherein "pg_role" must be identical to
02095  *  "auth_user" in order to be authorized.
02096  *
02097  *  Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR.
02098  */
02099 int
02100 check_usermap(const char *usermap_name,
02101               const char *pg_role,
02102               const char *auth_user,
02103               bool case_insensitive)
02104 {
02105     bool        found_entry = false,
02106                 error = false;
02107 
02108     if (usermap_name == NULL || usermap_name[0] == '\0')
02109     {
02110         if (case_insensitive)
02111         {
02112             if (pg_strcasecmp(pg_role, auth_user) == 0)
02113                 return STATUS_OK;
02114         }
02115         else
02116         {
02117             if (strcmp(pg_role, auth_user) == 0)
02118                 return STATUS_OK;
02119         }
02120         ereport(LOG,
02121                 (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
02122                         pg_role, auth_user)));
02123         return STATUS_ERROR;
02124     }
02125     else
02126     {
02127         ListCell   *line_cell;
02128 
02129         foreach(line_cell, parsed_ident_lines)
02130         {
02131             check_ident_usermap(lfirst(line_cell), usermap_name,
02132                                 pg_role, auth_user, case_insensitive,
02133                                 &found_entry, &error);
02134             if (found_entry || error)
02135                 break;
02136         }
02137     }
02138     if (!found_entry && !error)
02139     {
02140         ereport(LOG,
02141                 (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
02142                         usermap_name, pg_role, auth_user)));
02143     }
02144     return found_entry ? STATUS_OK : STATUS_ERROR;
02145 }
02146 
02147 
02148 /*
02149  * Read the ident config file and create a List of IdentLine records for
02150  * the contents.
02151  *
02152  * This works the same as load_hba(), but for the user config file.
02153  */
02154 bool
02155 load_ident(void)
02156 {
02157     FILE       *file;
02158     List       *ident_lines = NIL;
02159     List       *ident_line_nums = NIL;
02160     ListCell   *line_cell,
02161                *num_cell,
02162                *parsed_line_cell;
02163     List       *new_parsed_lines = NIL;
02164     bool        ok = true;
02165     MemoryContext linecxt;
02166     MemoryContext oldcxt;
02167     MemoryContext ident_context;
02168     IdentLine    *newline;
02169 
02170     file = AllocateFile(IdentFileName, "r");
02171     if (file == NULL)
02172     {
02173         /* not fatal ... we just won't do any special ident maps */
02174         ereport(LOG,
02175                 (errcode_for_file_access(),
02176                  errmsg("could not open usermap file \"%s\": %m",
02177                         IdentFileName)));
02178         return false;
02179     }
02180 
02181     linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums, NULL);
02182     FreeFile(file);
02183 
02184     /* Now parse all the lines */
02185     ident_context = AllocSetContextCreate(TopMemoryContext,
02186                                    "ident parser context",
02187                                    ALLOCSET_DEFAULT_MINSIZE,
02188                                    ALLOCSET_DEFAULT_MINSIZE,
02189                                    ALLOCSET_DEFAULT_MAXSIZE);
02190     oldcxt = MemoryContextSwitchTo(ident_context);
02191     forboth(line_cell, ident_lines, num_cell, ident_line_nums)
02192     {
02193         if ((newline = parse_ident_line(lfirst(line_cell), lfirst_int(num_cell))) == NULL)
02194         {
02195             /*
02196              * Parse error in the file, so indicate there's a problem.  Free
02197              * all the memory and regular expressions of lines parsed so far.
02198              */
02199             foreach(parsed_line_cell, new_parsed_lines)
02200             {
02201                 newline = (IdentLine *) lfirst(parsed_line_cell);
02202                 if (newline->ident_user[0] == '/')
02203                     pg_regfree(&newline->re);
02204             }
02205             MemoryContextReset(ident_context);
02206             new_parsed_lines = NIL;
02207             ok = false;
02208 
02209             /*
02210              * Keep parsing the rest of the file so we can report errors on
02211              * more than the first row. Error has already been reported in the
02212              * parsing function, so no need to log it here.
02213              */
02214             continue;
02215         }
02216 
02217         new_parsed_lines = lappend(new_parsed_lines, newline);
02218     }
02219 
02220     /* Free tokenizer memory */
02221     MemoryContextDelete(linecxt);
02222     MemoryContextSwitchTo(oldcxt);
02223 
02224     if (!ok)
02225     {
02226         /* File contained one or more errors, so bail out */
02227         foreach(parsed_line_cell, new_parsed_lines)
02228         {
02229             newline = (IdentLine *) lfirst(parsed_line_cell);
02230             if (newline->ident_user[0] == '/')
02231                 pg_regfree(&newline->re);
02232         }
02233         MemoryContextDelete(ident_context);
02234         return false;
02235     }
02236 
02237     /* Loaded new file successfully, replace the one we use */
02238     if (parsed_ident_lines != NULL)
02239     {
02240         foreach(parsed_line_cell, parsed_ident_lines)
02241         {
02242             newline = (IdentLine *) lfirst(parsed_line_cell);
02243             if (newline->ident_user[0] == '/')
02244                 pg_regfree(&newline->re);
02245         }
02246         MemoryContextDelete(parsed_ident_context);
02247     }
02248     parsed_ident_context = ident_context;
02249     parsed_ident_lines = new_parsed_lines;
02250 
02251     return true;
02252 }
02253 
02254 
02255 
02256 /*
02257  *  Determine what authentication method should be used when accessing database
02258  *  "database" from frontend "raddr", user "user".  Return the method and
02259  *  an optional argument (stored in fields of *port), and STATUS_OK.
02260  *
02261  *  If the file does not contain any entry matching the request, we return
02262  *  method = uaImplicitReject.
02263  */
02264 void
02265 hba_getauthmethod(hbaPort *port)
02266 {
02267     check_hba(port);
02268 }