Commit e5e2fc84 authored by Alvaro Herrera's avatar Alvaro Herrera

Modernise pg_hba.conf token processing

The previous coding was ugly, as it marked special tokens as such in the
wrong stage, relying on workarounds to figure out if they had been
quoted in the original or not.  This made it impossible to have specific
keywords be recognized as such only in certain positions in HBA lines,
for example.  Fix by restructuring the parser code so that it remembers
whether tokens were quoted or not.  This eliminates widespread knowledge
of possible known keywords for all fields.

Also improve memory management in this area, to use memory contexts that
are reset as a whole instead of using retail pfrees; this removes a
whole lotta crufty (and probably slow) code.

Instead of calling strlen() three times in next_field_expand on the
returned token to find out whether there was a comma (and strip it),
pass back the info directly from the callee, which is simpler.

In passing, update historical artifacts in hba.c API.

Authors: Brendan Jurd, Alvaro Herrera
Reviewed by Pavel Stehule
parent 615c3849
...@@ -315,15 +315,11 @@ ClientAuthentication(Port *port) ...@@ -315,15 +315,11 @@ ClientAuthentication(Port *port)
/* /*
* Get the authentication method to use for this frontend/database * Get the authentication method to use for this frontend/database
* combination. Note: a failure return indicates a problem with the hba * combination. Note: we do not parse the file at this point; this has
* config file, not with the request. hba.c should have dropped an error * already been done elsewhere. hba.c dropped an error message
* message into the postmaster logfile if it failed. * into the server logfile if parsing the hba config file failed.
*/ */
if (hba_getauthmethod(port) != STATUS_OK) hba_getauthmethod(port);
ereport(FATAL,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("missing or erroneous pg_hba.conf file"),
errhint("See server log for details.")));
/* /*
* Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We * Enable immediate response to SIGTERM/SIGINT/timeout interrupts. (We
......
...@@ -35,14 +35,12 @@ ...@@ -35,14 +35,12 @@
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/memutils.h"
#define atooid(x) ((Oid) strtoul((x), NULL, 10)) #define atooid(x) ((Oid) strtoul((x), NULL, 10))
#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10)) #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
/* This is used to separate values in multi-valued column strings */
#define MULTI_VALUE_SEP "\001"
#define MAX_TOKEN 256 #define MAX_TOKEN 256
/* callback data for check_network_callback */ /* callback data for check_network_callback */
...@@ -53,26 +51,48 @@ typedef struct check_network_data ...@@ -53,26 +51,48 @@ typedef struct check_network_data
bool result; /* set to true if match */ bool result; /* set to true if match */
} check_network_data; } check_network_data;
/* pre-parsed content of HBA config file: list of HbaLine structs */
#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
#define token_matches(t, k) (strcmp(t->string, k) == 0)
/*
* A single string token lexed from the HBA config file, together with whether
* the token had been quoted.
*/
typedef struct HbaToken
{
char *string;
bool quoted;
} HbaToken;
/*
* pre-parsed content of HBA config file: list of HbaLine structs.
* parsed_hba_context is the memory context where it lives.
*/
static List *parsed_hba_lines = NIL; static List *parsed_hba_lines = NIL;
static MemoryContext parsed_hba_context = NULL;
/* /*
* These variables hold the pre-parsed contents of the ident usermap * These variables hold the pre-parsed contents of the ident usermap
* configuration file. ident_lines is a list of sublists, one sublist for * configuration file. ident_lines is a triple-nested list of lines, fields
* each (non-empty, non-comment) line of the file. The sublist items are * and tokens, as returned by tokenize_file. There will be one line in
* palloc'd strings, one string per token on the line. Note there will always * ident_lines for each (non-empty, non-comment) line of the file. Note there
* be at least one token, since blank lines are not entered in the data * will always be at least one field, since blank lines are not entered in the
* structure. ident_line_nums is an integer list containing the actual line * data structure. ident_line_nums is an integer list containing the actual
* number for each line represented in ident_lines. * line number for each line represented in ident_lines. ident_context is
* the memory context holding all this.
*/ */
static List *ident_lines = NIL; static List *ident_lines = NIL;
static List *ident_line_nums = NIL; static List *ident_line_nums = NIL;
static MemoryContext ident_context = NULL;
static void tokenize_file(const char *filename, FILE *file, static MemoryContext tokenize_file(const char *filename, FILE *file,
List **lines, List **line_nums); List **lines, List **line_nums);
static char *tokenize_inc_file(const char *outer_filename, static List *tokenize_inc_file(List *tokens, const char *outer_filename,
const char *inc_filename); const char *inc_filename);
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
int line_num);
/* /*
* isblank() exists in the ISO C99 spec, but it's not very portable yet, * isblank() exists in the ISO C99 spec, but it's not very portable yet,
...@@ -96,6 +116,8 @@ pg_isblank(const char c) ...@@ -96,6 +116,8 @@ pg_isblank(const char c)
* the first character. (We use that to prevent "@x" from being treated * the first character. (We use that to prevent "@x" from being treated
* as a file inclusion request. Note that @"x" should be so treated; * as a file inclusion request. Note that @"x" should be so treated;
* we want to allow that to support embedded spaces in file paths.) * we want to allow that to support embedded spaces in file paths.)
* We set *terminating_comma to indicate whether the token is terminated by a
* comma (which is not returned.)
* *
* If successful: store null-terminated token at *buf and return TRUE. * If successful: store null-terminated token at *buf and return TRUE.
* If no more tokens on line: set *buf = '\0' and return FALSE. * If no more tokens on line: set *buf = '\0' and return FALSE.
...@@ -104,13 +126,11 @@ pg_isblank(const char c) ...@@ -104,13 +126,11 @@ pg_isblank(const char c)
* whichever comes first. If no more tokens on line, position the file to the * whichever comes first. If no more tokens on line, position the file to the
* beginning of the next line or EOF, whichever comes first. * beginning of the next line or EOF, whichever comes first.
* *
* Handle comments. Treat unquoted keywords that might be role, database, or * Handle comments.
* host names specially, by appending a newline to them. Also, when
* a token is terminated by a comma, the comma is included in the returned
* token.
*/ */
static bool static bool
next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote) next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote,
bool *terminating_comma)
{ {
int c; int c;
char *start_buf = buf; char *start_buf = buf;
...@@ -123,6 +143,7 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote) ...@@ -123,6 +143,7 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
Assert(end_buf > start_buf); Assert(end_buf > start_buf);
*initial_quote = false; *initial_quote = false;
*terminating_comma = false;
/* Move over initial whitespace and commas */ /* Move over initial whitespace and commas */
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ',')) while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
...@@ -165,12 +186,15 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote) ...@@ -165,12 +186,15 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
break; break;
} }
if (c != '"' || was_quote) /* we do not pass back the comma in the token */
*buf++ = c;
/* We pass back the comma so the caller knows there is more */
if (c == ',' && !in_quote) if (c == ',' && !in_quote)
{
*terminating_comma = true;
break; break;
}
if (c != '"' || was_quote)
*buf++ = c;
/* Literal double-quote is two double-quotes */ /* Literal double-quote is two double-quotes */
if (in_quote && c == '"') if (in_quote && c == '"')
...@@ -198,138 +222,85 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote) ...@@ -198,138 +222,85 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
*buf = '\0'; *buf = '\0';
if (!saw_quote &&
(strcmp(start_buf, "all") == 0 ||
strcmp(start_buf, "samehost") == 0 ||
strcmp(start_buf, "samenet") == 0 ||
strcmp(start_buf, "sameuser") == 0 ||
strcmp(start_buf, "samegroup") == 0 ||
strcmp(start_buf, "samerole") == 0 ||
strcmp(start_buf, "replication") == 0))
{
/* append newline to a magical keyword */
*buf++ = '\n';
*buf = '\0';
}
return (saw_quote || buf > start_buf); return (saw_quote || buf > start_buf);
} }
static HbaToken *
make_hba_token(char *token, bool quoted)
{
HbaToken *hbatoken;
int toklen;
toklen = strlen(token);
hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
hbatoken->quoted = quoted;
memcpy(hbatoken->string, token, toklen + 1);
return hbatoken;
}
/* /*
* Tokenize file and handle file inclusion and comma lists. We have * Copy a HbaToken struct into freshly palloc'd memory.
* to break apart the commas to expand any file names then */
* reconstruct with commas. static HbaToken *
copy_hba_token(HbaToken *in)
{
HbaToken *out = make_hba_token(in->string, in->quoted);
return out;
}
/*
* Tokenize one HBA field from a file, handling file inclusion and comma lists.
* *
* The result is a palloc'd string, or NULL if we have reached EOL. * The result is a List of HbaToken structs for each individual token,
* or NIL if we reached EOL.
*/ */
static char * static List *
next_token_expand(const char *filename, FILE *file) next_field_expand(const char *filename, FILE *file)
{ {
char buf[MAX_TOKEN]; char buf[MAX_TOKEN];
char *comma_str = pstrdup("");
bool got_something = false;
bool trailing_comma; bool trailing_comma;
bool initial_quote; bool initial_quote;
char *incbuf; List *tokens = NIL;
int needed;
do do
{ {
if (!next_token(file, buf, sizeof(buf), &initial_quote)) if (!next_token(file, buf, sizeof(buf), &initial_quote, &trailing_comma))
break; break;
got_something = true;
if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
{
trailing_comma = true;
buf[strlen(buf) - 1] = '\0';
}
else
trailing_comma = false;
/* Is this referencing a file? */ /* Is this referencing a file? */
if (!initial_quote && buf[0] == '@' && buf[1] != '\0') if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
incbuf = tokenize_inc_file(filename, buf + 1); tokens = tokenize_inc_file(tokens, filename, buf + 1);
else else
incbuf = pstrdup(buf); tokens = lappend(tokens, make_hba_token(buf, initial_quote));
needed = strlen(comma_str) + strlen(incbuf) + 1;
if (trailing_comma)
needed++;
comma_str = repalloc(comma_str, needed);
strcat(comma_str, incbuf);
if (trailing_comma)
strcat(comma_str, MULTI_VALUE_SEP);
pfree(incbuf);
} while (trailing_comma); } while (trailing_comma);
if (!got_something) return tokens;
{
pfree(comma_str);
return NULL;
}
return comma_str;
} }
/* /*
* Free memory used by lines/tokens (i.e., structure built by tokenize_file) * tokenize_inc_file
*/ * Expand a file included from another file into an hba "field"
static void *
free_lines(List **lines, List **line_nums) * Opens and tokenises a file included from another HBA config file with @,
{ * and returns all values found therein as a flat list of HbaTokens. If a
/* * @-token is found, recursively expand it. The given token list is used as
* Either both must be non-NULL, or both must be NULL * initial contents of list (so foo,bar,@baz does what you expect).
*/
Assert((*lines != NIL && *line_nums != NIL) ||
(*lines == NIL && *line_nums == NIL));
if (*lines)
{
/*
* "lines" is a list of lists; each of those sublists consists of
* palloc'ed tokens, so we want to free each pointed-to token in a
* sublist, followed by the sublist itself, and finally the whole
* list.
*/ */
ListCell *line; static List *
tokenize_inc_file(List *tokens,
foreach(line, *lines) const char *outer_filename,
{
List *ln = lfirst(line);
ListCell *token;
foreach(token, ln)
pfree(lfirst(token));
/* free the sublist structure itself */
list_free(ln);
}
/* free the list structure itself */
list_free(*lines);
/* clear the static variable */
*lines = NIL;
}
if (*line_nums)
{
list_free(*line_nums);
*line_nums = NIL;
}
}
static char *
tokenize_inc_file(const char *outer_filename,
const char *inc_filename) const char *inc_filename)
{ {
char *inc_fullname; char *inc_fullname;
FILE *inc_file; FILE *inc_file;
List *inc_lines; List *inc_lines;
List *inc_line_nums; List *inc_line_nums;
ListCell *line; ListCell *inc_line;
char *comma_str; MemoryContext linecxt;
if (is_absolute_path(inc_filename)) if (is_absolute_path(inc_filename))
{ {
...@@ -355,97 +326,100 @@ tokenize_inc_file(const char *outer_filename, ...@@ -355,97 +326,100 @@ tokenize_inc_file(const char *outer_filename,
errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m", errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
inc_filename, inc_fullname))); inc_filename, inc_fullname)));
pfree(inc_fullname); pfree(inc_fullname);
return tokens;
/* return single space, it matches nothing */
return pstrdup(" ");
} }
/* There is possible recursion here if the file contains @ */ /* There is possible recursion here if the file contains @ */
tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums); linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
FreeFile(inc_file); FreeFile(inc_file);
pfree(inc_fullname); pfree(inc_fullname);
/* Create comma-separated string from List */ foreach(inc_line, inc_lines)
comma_str = pstrdup("");
foreach(line, inc_lines)
{ {
List *token_list = (List *) lfirst(line); List *inc_fields = lfirst(inc_line);
ListCell *token; ListCell *inc_field;
foreach(token, token_list) foreach(inc_field, inc_fields)
{ {
int oldlen = strlen(comma_str); List *inc_tokens = lfirst(inc_field);
int needed; ListCell *inc_token;
needed = oldlen + strlen(lfirst(token)) + 1; foreach(inc_token, inc_tokens)
if (oldlen > 0) {
needed++; HbaToken *token = lfirst(inc_token);
comma_str = repalloc(comma_str, needed);
if (oldlen > 0) tokens = lappend(tokens, copy_hba_token(token));
strcat(comma_str, MULTI_VALUE_SEP);
strcat(comma_str, lfirst(token));
} }
} }
free_lines(&inc_lines, &inc_line_nums);
/* if file is empty, return single space rather than empty string */
if (strlen(comma_str) == 0)
{
pfree(comma_str);
return pstrdup(" ");
} }
return comma_str; MemoryContextDelete(linecxt);
return tokens;
} }
/* /*
* Tokenize the given file, storing the resulting data into two lists: * Tokenize the given file, storing the resulting data into two Lists: a
* a list of sublists, each sublist containing the tokens in a line of * List of lines, and a List of line numbers.
* the file, and a list of line numbers. *
* The list of lines is a triple-nested List structure. Each line is a List of
* fields, and each field is a List of HbaTokens.
* *
* filename must be the absolute path to the target file. * filename must be the absolute path to the target file.
*
* Return value is a memory context which contains all memory allocated by
* this function.
*/ */
static void static MemoryContext
tokenize_file(const char *filename, FILE *file, tokenize_file(const char *filename, FILE *file,
List **lines, List **line_nums) List **lines, List **line_nums)
{ {
List *current_line = NIL; List *current_line = NIL;
List *current_field = NIL;
int line_number = 1; int line_number = 1;
char *buf; MemoryContext linecxt;
MemoryContext oldcxt;
linecxt = AllocSetContextCreate(TopMemoryContext,
"tokenize file cxt",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(linecxt);
*lines = *line_nums = NIL; *lines = *line_nums = NIL;
while (!feof(file) && !ferror(file)) while (!feof(file) && !ferror(file))
{ {
buf = next_token_expand(filename, file); current_field = next_field_expand(filename, file);
/* add token to list, unless we are at EOL or comment start */ /* add tokens to list, unless we are at EOL or comment start */
if (buf) if (list_length(current_field) > 0)
{ {
if (current_line == NIL) if (current_line == NIL)
{ {
/* make a new line List, record its line number */ /* make a new line List, record its line number */
current_line = lappend(current_line, buf); current_line = lappend(current_line, current_field);
*lines = lappend(*lines, current_line); *lines = lappend(*lines, current_line);
*line_nums = lappend_int(*line_nums, line_number); *line_nums = lappend_int(*line_nums, line_number);
} }
else else
{ {
/* append token to current line's list */ /* append tokens to current line's list */
current_line = lappend(current_line, buf); current_line = lappend(current_line, current_field);
} }
} }
else else
{ {
/* we are at real or logical EOL, so force a new line List */ /* we are at real or logical EOL, so force a new line List */
current_line = NIL; current_line = NIL;
/* Advance line number whenever we reach EOL */
line_number++; line_number++;
} }
} }
MemoryContextSwitchTo(oldcxt);
return linecxt;
} }
...@@ -473,74 +447,63 @@ is_member(Oid userid, const char *role) ...@@ -473,74 +447,63 @@ is_member(Oid userid, const char *role)
} }
/* /*
* Check comma-separated list for a match to role, allowing group names. * Check HbaToken list for a match to role, allowing group names.
*
* NB: param_str is destructively modified! In current usage, this is
* okay only because this code is run after forking off from the postmaster,
* and so it doesn't matter that we clobber the stored hba info.
*/ */
static bool static bool
check_role(const char *role, Oid roleid, char *param_str) check_role(const char *role, Oid roleid, List *tokens)
{ {
char *tok; ListCell *cell;
HbaToken *tok;
for (tok = strtok(param_str, MULTI_VALUE_SEP); foreach(cell, tokens)
tok != NULL;
tok = strtok(NULL, MULTI_VALUE_SEP))
{ {
if (tok[0] == '+') tok = lfirst(cell);
if (!tok->quoted && tok->string[0] == '+')
{ {
if (is_member(roleid, tok + 1)) if (is_member(roleid, tok->string + 1))
return true; return true;
} }
else if (strcmp(tok, role) == 0 || else if (token_matches(tok, role) ||
(strcmp(tok, "replication\n") == 0 && token_is_keyword(tok, "all"))
strcmp(role, "replication") == 0) ||
strcmp(tok, "all\n") == 0)
return true; return true;
} }
return false; return false;
} }
/* /*
* Check to see if db/role combination matches param string. * Check to see if db/role combination matches HbaToken list.
*
* NB: param_str is destructively modified! In current usage, this is
* okay only because this code is run after forking off from the postmaster,
* and so it doesn't matter that we clobber the stored hba info.
*/ */
static bool static bool
check_db(const char *dbname, const char *role, Oid roleid, char *param_str) check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
{ {
char *tok; ListCell *cell;
HbaToken *tok;
for (tok = strtok(param_str, MULTI_VALUE_SEP); foreach(cell, tokens)
tok != NULL;
tok = strtok(NULL, MULTI_VALUE_SEP))
{ {
tok = lfirst(cell);
if (am_walsender) if (am_walsender)
{ {
/* walsender connections can only match replication keyword */ /* walsender connections can only match replication keyword */
if (strcmp(tok, "replication\n") == 0) if (token_is_keyword(tok, "replication"))
return true; return true;
} }
else if (strcmp(tok, "all\n") == 0) else if (token_is_keyword(tok, "all"))
return true; return true;
else if (strcmp(tok, "sameuser\n") == 0) else if (token_is_keyword(tok, "sameuser"))
{ {
if (strcmp(dbname, role) == 0) if (strcmp(dbname, role) == 0)
return true; return true;
} }
else if (strcmp(tok, "samegroup\n") == 0 || else if (token_is_keyword(tok, "samegroup") ||
strcmp(tok, "samerole\n") == 0) token_is_keyword(tok, "samerole"))
{ {
if (is_member(roleid, dbname)) if (is_member(roleid, dbname))
return true; return true;
} }
else if (strcmp(tok, "replication\n") == 0) else if (token_is_keyword(tok, "replication"))
continue; /* never match this if not walsender */ continue; /* never match this if not walsender */
else if (strcmp(tok, dbname) == 0) else if (token_matches(tok, dbname))
return true; return true;
} }
return false; return false;
...@@ -784,7 +747,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) ...@@ -784,7 +747,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
} while (0); } while (0);
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\ #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
if (parsedline->auth_method != methodval) \ if (hbaline->auth_method != methodval) \
INVALID_AUTH_OPTION(optname, validmethods); \ INVALID_AUTH_OPTION(optname, validmethods); \
} while (0); } while (0);
...@@ -800,29 +763,83 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) ...@@ -800,29 +763,83 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
} \ } \
} while (0); } while (0);
/*
* IDENT_FIELD_ABSENT:
* Throw an error and exit the function if the given ident field ListCell is
* not populated.
*
* IDENT_MULTI_VALUE:
* Throw an error and exit the function if the given ident token List has more
* than one element.
*/
#define IDENT_FIELD_ABSENT(field) do {\
if (!field) { \
ereport(LOG, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("missing entry in file \"%s\" at end of line %d", \
IdentFileName, line_number))); \
*error_p = true; \
return; \
} \
} while (0);
#define IDENT_MULTI_VALUE(tokens) do {\
if (tokens->length > 1) { \
ereport(LOG, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("multiple values in ident field"), \
errcontext("line %d of configuration file \"%s\"", \
line_number, IdentFileName))); \
*error_p = true; \
return; \
} \
} while (0);
/* /*
* Parse one line in the hba config file and store the result in * Parse one tokenised line from the hba config file and store the result in a
* a HbaLine structure. * HbaLine structure, or NULL if parsing fails.
*
* The tokenised line is a List of fields, each field being a List of
* HbaTokens.
*
* Note: this function leaks memory when an error occurs. Caller is expected
* to have set a memory context that will be reset if this function returns
* NULL.
*/ */
static bool static HbaLine *
parse_hba_line(List *line, int line_num, HbaLine *parsedline) parse_hba_line(List *line, int line_num)
{ {
char *token; char *str;
struct addrinfo *gai_result; struct addrinfo *gai_result;
struct addrinfo hints; struct addrinfo hints;
int ret; int ret;
char *cidr_slash; char *cidr_slash;
char *unsupauth; char *unsupauth;
ListCell *line_item; ListCell *field;
List *tokens;
line_item = list_head(line); ListCell *tokencell;
HbaToken *token;
HbaLine *parsedline;
parsedline = palloc0(sizeof(HbaLine));
parsedline->linenumber = line_num; parsedline->linenumber = line_num;
/* Check the record type. */ /* Check the record type. */
token = lfirst(line_item); field = list_head(line);
if (strcmp(token, "local") == 0) tokens = lfirst(field);
if (tokens->length > 1)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for connection type"),
errhint("Specify exactly one connection type per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return NULL;
}
token = linitial(tokens);
if (strcmp(token->string, "local") == 0)
{ {
#ifdef HAVE_UNIX_SOCKETS #ifdef HAVE_UNIX_SOCKETS
parsedline->conntype = ctLocal; parsedline->conntype = ctLocal;
...@@ -832,15 +849,15 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -832,15 +849,15 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("local connections are not supported by this build"), errmsg("local connections are not supported by this build"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
#endif #endif
} }
else if (strcmp(token, "host") == 0 else if (strcmp(token->string, "host") == 0 ||
|| strcmp(token, "hostssl") == 0 strcmp(token->string, "hostssl") == 0 ||
|| strcmp(token, "hostnossl") == 0) strcmp(token->string, "hostnossl") == 0)
{ {
if (token[4] == 's') /* "hostssl" */ if (token->string[4] == 's') /* "hostssl" */
{ {
/* SSL support must be actually active, else complain */ /* SSL support must be actually active, else complain */
#ifdef USE_SSL #ifdef USE_SSL
...@@ -854,7 +871,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -854,7 +871,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errhint("Set ssl = on in postgresql.conf."), errhint("Set ssl = on in postgresql.conf."),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
#else #else
ereport(LOG, ereport(LOG,
...@@ -863,11 +880,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -863,11 +880,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errhint("Compile with --with-openssl to use SSL connections."), errhint("Compile with --with-openssl to use SSL connections."),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
#endif #endif
} }
#ifdef USE_SSL #ifdef USE_SSL
else if (token[4] == 'n') /* "hostnossl" */ else if (token->string[4] == 'n') /* "hostnossl" */
{ {
parsedline->conntype = ctHostNoSSL; parsedline->conntype = ctHostNoSSL;
} }
...@@ -883,63 +900,86 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -883,63 +900,86 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid connection type \"%s\"", errmsg("invalid connection type \"%s\"",
token), token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
/* Get the database. */ /* Get the databases. */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) if (!field)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before database specification"), errmsg("end-of-line before database specification"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
}
parsedline->databases = NIL;
tokens = lfirst(field);
foreach(tokencell, tokens)
{
parsedline->databases = lappend(parsedline->databases,
copy_hba_token(lfirst(tokencell)));
} }
parsedline->database = pstrdup(lfirst(line_item));
/* Get the role. */ /* Get the roles. */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) if (!field)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before role specification"), errmsg("end-of-line before role specification"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
}
parsedline->roles = NIL;
tokens = lfirst(field);
foreach(tokencell, tokens)
{
parsedline->roles = lappend(parsedline->roles,
copy_hba_token(lfirst(tokencell)));
} }
parsedline->role = pstrdup(lfirst(line_item));
if (parsedline->conntype != ctLocal) if (parsedline->conntype != ctLocal)
{ {
/* Read the IP address field. (with or without CIDR netmask) */ /* Read the IP address field. (with or without CIDR netmask) */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) if (!field)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before IP address specification"), errmsg("end-of-line before IP address specification"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
token = lfirst(line_item); tokens = lfirst(field);
if (tokens->length > 1)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for host address"),
errhint("Specify one address range per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return NULL;
}
token = linitial(tokens);
if (strcmp(token, "all\n") == 0) if (token_is_keyword(token, "all"))
{ {
parsedline->ip_cmp_method = ipCmpAll; parsedline->ip_cmp_method = ipCmpAll;
} }
else if (strcmp(token, "samehost\n") == 0) else if (token_is_keyword(token, "samehost"))
{ {
/* Any IP on this host is allowed to connect */ /* Any IP on this host is allowed to connect */
parsedline->ip_cmp_method = ipCmpSameHost; parsedline->ip_cmp_method = ipCmpSameHost;
} }
else if (strcmp(token, "samenet\n") == 0) else if (token_is_keyword(token, "samenet"))
{ {
/* Any IP on the host's subnets is allowed to connect */ /* Any IP on the host's subnets is allowed to connect */
parsedline->ip_cmp_method = ipCmpSameNet; parsedline->ip_cmp_method = ipCmpSameNet;
...@@ -950,10 +990,10 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -950,10 +990,10 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
parsedline->ip_cmp_method = ipCmpMask; parsedline->ip_cmp_method = ipCmpMask;
/* need a modifiable copy of token */ /* need a modifiable copy of token */
token = pstrdup(token); str = pstrdup(token->string);
/* Check if it has a CIDR suffix and if so isolate it */ /* Check if it has a CIDR suffix and if so isolate it */
cidr_slash = strchr(token, '/'); cidr_slash = strchr(str, '/');
if (cidr_slash) if (cidr_slash)
*cidr_slash = '\0'; *cidr_slash = '\0';
...@@ -967,24 +1007,23 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -967,24 +1007,23 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
hints.ai_addr = NULL; hints.ai_addr = NULL;
hints.ai_next = NULL; hints.ai_next = NULL;
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result); ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
if (ret == 0 && gai_result) if (ret == 0 && gai_result)
memcpy(&parsedline->addr, gai_result->ai_addr, memcpy(&parsedline->addr, gai_result->ai_addr,
gai_result->ai_addrlen); gai_result->ai_addrlen);
else if (ret == EAI_NONAME) else if (ret == EAI_NONAME)
parsedline->hostname = token; parsedline->hostname = str;
else else
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP address \"%s\": %s", errmsg("invalid IP address \"%s\": %s",
token, gai_strerror(ret)), str, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
if (gai_result) if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result); pg_freeaddrinfo_all(hints.ai_family, gai_result);
pfree(token); return NULL;
return false;
} }
pg_freeaddrinfo_all(hints.ai_family, gai_result); pg_freeaddrinfo_all(hints.ai_family, gai_result);
...@@ -994,60 +1033,68 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -994,60 +1033,68 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
{ {
if (parsedline->hostname) if (parsedline->hostname)
{ {
*cidr_slash = '/'; /* restore token for message */
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("specifying both host name and CIDR mask is invalid: \"%s\"", errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
token), token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
pfree(token); return NULL;
return false;
} }
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1, if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0) parsedline->addr.ss_family) < 0)
{ {
*cidr_slash = '/'; /* restore token for message */
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid CIDR mask in address \"%s\"", errmsg("invalid CIDR mask in address \"%s\"",
token), token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
pfree(token); return NULL;
return false;
} }
pfree(token); pfree(str);
} }
else if (!parsedline->hostname) else if (!parsedline->hostname)
{ {
/* Read the mask field. */ /* Read the mask field. */
pfree(token); pfree(str);
line_item = lnext(line_item); field = lnext(field);
if (!line_item) if (!field)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before netmask specification"), errmsg("end-of-line before netmask specification"),
errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
}
tokens = lfirst(field);
if (tokens->length > 1)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for netmask"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return NULL;
} }
token = lfirst(line_item); token = linitial(tokens);
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result); ret = pg_getaddrinfo_all(token->string, NULL,
&hints, &gai_result);
if (ret || !gai_result) if (ret || !gai_result)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\": %s", errmsg("invalid IP mask \"%s\": %s",
token, gai_strerror(ret)), token->string, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
if (gai_result) if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result); pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false; return NULL;
} }
memcpy(&parsedline->mask, gai_result->ai_addr, memcpy(&parsedline->mask, gai_result->ai_addr,
...@@ -1061,55 +1108,66 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1061,55 +1108,66 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("IP address and mask do not match"), errmsg("IP address and mask do not match"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
} }
} }
} /* != ctLocal */ } /* != ctLocal */
/* Get the authentication method */ /* Get the authentication method */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) if (!field)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before authentication method"), errmsg("end-of-line before authentication method"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
token = lfirst(line_item); tokens = lfirst(field);
if (tokens->length > 1)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("multiple values specified for authentication type"),
errhint("Specify exactly one authentication type per line."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return NULL;
}
token = linitial(tokens);
unsupauth = NULL; unsupauth = NULL;
if (strcmp(token, "trust") == 0) if (strcmp(token->string, "trust") == 0)
parsedline->auth_method = uaTrust; parsedline->auth_method = uaTrust;
else if (strcmp(token, "ident") == 0) else if (strcmp(token->string, "ident") == 0)
parsedline->auth_method = uaIdent; parsedline->auth_method = uaIdent;
else if (strcmp(token, "peer") == 0) else if (strcmp(token->string, "peer") == 0)
parsedline->auth_method = uaPeer; parsedline->auth_method = uaPeer;
else if (strcmp(token, "password") == 0) else if (strcmp(token->string, "password") == 0)
parsedline->auth_method = uaPassword; parsedline->auth_method = uaPassword;
else if (strcmp(token, "krb5") == 0) else if (strcmp(token->string, "krb5") == 0)
#ifdef KRB5 #ifdef KRB5
parsedline->auth_method = uaKrb5; parsedline->auth_method = uaKrb5;
#else #else
unsupauth = "krb5"; unsupauth = "krb5";
#endif #endif
else if (strcmp(token, "gss") == 0) else if (strcmp(token->string, "gss") == 0)
#ifdef ENABLE_GSS #ifdef ENABLE_GSS
parsedline->auth_method = uaGSS; parsedline->auth_method = uaGSS;
#else #else
unsupauth = "gss"; unsupauth = "gss";
#endif #endif
else if (strcmp(token, "sspi") == 0) else if (strcmp(token->string, "sspi") == 0)
#ifdef ENABLE_SSPI #ifdef ENABLE_SSPI
parsedline->auth_method = uaSSPI; parsedline->auth_method = uaSSPI;
#else #else
unsupauth = "sspi"; unsupauth = "sspi";
#endif #endif
else if (strcmp(token, "reject") == 0) else if (strcmp(token->string, "reject") == 0)
parsedline->auth_method = uaReject; parsedline->auth_method = uaReject;
else if (strcmp(token, "md5") == 0) else if (strcmp(token->string, "md5") == 0)
{ {
if (Db_user_namespace) if (Db_user_namespace)
{ {
...@@ -1118,39 +1176,39 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1118,39 +1176,39 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"), errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
parsedline->auth_method = uaMD5; parsedline->auth_method = uaMD5;
} }
else if (strcmp(token, "pam") == 0) else if (strcmp(token->string, "pam") == 0)
#ifdef USE_PAM #ifdef USE_PAM
parsedline->auth_method = uaPAM; parsedline->auth_method = uaPAM;
#else #else
unsupauth = "pam"; unsupauth = "pam";
#endif #endif
else if (strcmp(token, "ldap") == 0) else if (strcmp(token->string, "ldap") == 0)
#ifdef USE_LDAP #ifdef USE_LDAP
parsedline->auth_method = uaLDAP; parsedline->auth_method = uaLDAP;
#else #else
unsupauth = "ldap"; unsupauth = "ldap";
#endif #endif
else if (strcmp(token, "cert") == 0) else if (strcmp(token->string, "cert") == 0)
#ifdef USE_SSL #ifdef USE_SSL
parsedline->auth_method = uaCert; parsedline->auth_method = uaCert;
#else #else
unsupauth = "cert"; unsupauth = "cert";
#endif #endif
else if (strcmp(token, "radius") == 0) else if (strcmp(token->string, "radius") == 0)
parsedline->auth_method = uaRADIUS; parsedline->auth_method = uaRADIUS;
else else
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\"", errmsg("invalid authentication method \"%s\"",
token), token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
if (unsupauth) if (unsupauth)
...@@ -1158,10 +1216,10 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1158,10 +1216,10 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\": not supported by this build", errmsg("invalid authentication method \"%s\": not supported by this build",
token), token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
/* /*
...@@ -1181,7 +1239,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1181,7 +1239,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("krb5 authentication is not supported on local sockets"), errmsg("krb5 authentication is not supported on local sockets"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
if (parsedline->conntype == ctLocal && if (parsedline->conntype == ctLocal &&
...@@ -1192,7 +1250,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1192,7 +1250,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("gssapi authentication is not supported on local sockets"), errmsg("gssapi authentication is not supported on local sockets"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
if (parsedline->conntype != ctLocal && if (parsedline->conntype != ctLocal &&
...@@ -1203,15 +1261,13 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1203,15 +1261,13 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("peer authentication is only supported on local sockets"), errmsg("peer authentication is only supported on local sockets"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
/* /*
* SSPI authentication can never be enabled on ctLocal connections, * SSPI authentication can never be enabled on ctLocal connections,
* because it's only supported on Windows, where ctLocal isn't supported. * because it's only supported on Windows, where ctLocal isn't supported.
*/ */
if (parsedline->conntype != ctHostSSL && if (parsedline->conntype != ctHostSSL &&
parsedline->auth_method == uaCert) parsedline->auth_method == uaCert)
{ {
...@@ -1220,51 +1276,125 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1220,51 +1276,125 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
errmsg("cert authentication is only supported on hostssl connections"), errmsg("cert authentication is only supported on hostssl connections"),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
/* Parse remaining arguments */ /* Parse remaining arguments */
while ((line_item = lnext(line_item)) != NULL) while ((field = lnext(field)) != NULL)
{ {
char *c; tokens = lfirst(field);
foreach(tokencell, tokens)
token = lfirst(line_item); {
char *val;
token = lfirst(tokencell);
c = strchr(token, '='); str = pstrdup(token->string);
if (c == NULL) val = strchr(str, '=');
if (val == NULL)
{ {
/* /*
* Got something that's not a name=value pair. * Got something that's not a name=value pair.
*/ */
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("authentication option not in name=value format: %s", token), errmsg("authentication option not in name=value format: %s", token->string),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return NULL;
} }
else
*val++ = '\0'; /* str now holds "name", val holds "value" */
if (!parse_hba_auth_opt(str, val, parsedline, line_num))
/* parse_hba_auth_opt already logged the error message */
return NULL;
pfree(str);
}
}
/*
* Check if the selected authentication method has any mandatory arguments
* that are not set.
*/
if (parsedline->auth_method == uaLDAP)
{
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
/*
* LDAP can operate in two modes: either with a direct bind, using
* ldapprefix and ldapsuffix, or using a search+bind, using
* ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
* Disallow mixing these parameters.
*/
if (parsedline->ldapprefix || parsedline->ldapsuffix)
{ {
*c++ = '\0'; /* token now holds "name", c holds "value" */ if (parsedline->ldapbasedn ||
if (strcmp(token, "map") == 0) parsedline->ldapbinddn ||
parsedline->ldapbindpasswd ||
parsedline->ldapsearchattribute)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return NULL;
}
}
else if (!parsedline->ldapbasedn)
{ {
if (parsedline->auth_method != uaIdent && ereport(LOG,
parsedline->auth_method != uaPeer && (errcode(ERRCODE_CONFIG_FILE_ERROR),
parsedline->auth_method != uaKrb5 && errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
parsedline->auth_method != uaGSS && errcontext("line %d of configuration file \"%s\"",
parsedline->auth_method != uaSSPI && line_num, HbaFileName)));
parsedline->auth_method != uaCert) return NULL;
}
}
if (parsedline->auth_method == uaRADIUS)
{
MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
}
/*
* Enforce any parameters implied by other settings.
*/
if (parsedline->auth_method == uaCert)
{
parsedline->clientcert = true;
}
return parsedline;
}
/*
* Parse one name-value pair as an authentication option into the given
* HbaLine. Return true if we successfully parse the option, false if we
* encounter an error.
*/
static bool
parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
{
if (strcmp(name, "map") == 0)
{
if (hbaline->auth_method != uaIdent &&
hbaline->auth_method != uaPeer &&
hbaline->auth_method != uaKrb5 &&
hbaline->auth_method != uaGSS &&
hbaline->auth_method != uaSSPI &&
hbaline->auth_method != uaCert)
INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert")); INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert"));
parsedline->usermap = pstrdup(c); hbaline->usermap = pstrdup(val);
} }
else if (strcmp(token, "clientcert") == 0) else if (strcmp(name, "clientcert") == 0)
{ {
/* /*
* Since we require ctHostSSL, this really can never happen on * Since we require ctHostSSL, this really can never happen
* non-SSL-enabled builds, so don't bother checking for * on non-SSL-enabled builds, so don't bother checking for
* USE_SSL. * USE_SSL.
*/ */
if (parsedline->conntype != ctHostSSL) if (hbaline->conntype != ctHostSSL)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
...@@ -1273,7 +1403,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1273,7 +1403,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
if (strcmp(c, "1") == 0) if (strcmp(val, "1") == 0)
{ {
if (!secure_loaded_verify_locations()) if (!secure_loaded_verify_locations())
{ {
...@@ -1285,11 +1415,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1285,11 +1415,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
parsedline->clientcert = true; hbaline->clientcert = true;
} }
else else
{ {
if (parsedline->auth_method == uaCert) if (hbaline->auth_method == uaCert)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
...@@ -1298,110 +1428,114 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1298,110 +1428,114 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
parsedline->clientcert = false; hbaline->clientcert = false;
} }
} }
else if (strcmp(token, "pamservice") == 0) else if (strcmp(name, "pamservice") == 0)
{ {
REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam"); REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
parsedline->pamservice = pstrdup(c); hbaline->pamservice = pstrdup(val);
} }
else if (strcmp(token, "ldaptls") == 0) else if (strcmp(name, "ldaptls") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
if (strcmp(c, "1") == 0) if (strcmp(val, "1") == 0)
parsedline->ldaptls = true; hbaline->ldaptls = true;
else else
parsedline->ldaptls = false; hbaline->ldaptls = false;
} }
else if (strcmp(token, "ldapserver") == 0) else if (strcmp(name, "ldapserver") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
parsedline->ldapserver = pstrdup(c); hbaline->ldapserver = pstrdup(val);
} }
else if (strcmp(token, "ldapport") == 0) else if (strcmp(name, "ldapport") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
parsedline->ldapport = atoi(c); hbaline->ldapport = atoi(val);
if (parsedline->ldapport == 0) if (hbaline->ldapport == 0)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid LDAP port number: \"%s\"", c), errmsg("invalid LDAP port number: \"%s\"", val),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
} }
else if (strcmp(token, "ldapbinddn") == 0) else if (strcmp(name, "ldapbinddn") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
parsedline->ldapbinddn = pstrdup(c); hbaline->ldapbinddn = pstrdup(val);
} }
else if (strcmp(token, "ldapbindpasswd") == 0) else if (strcmp(name, "ldapbindpasswd") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
parsedline->ldapbindpasswd = pstrdup(c); hbaline->ldapbindpasswd = pstrdup(val);
} }
else if (strcmp(token, "ldapsearchattribute") == 0) else if (strcmp(name, "ldapsearchattribute") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
parsedline->ldapsearchattribute = pstrdup(c); hbaline->ldapsearchattribute = pstrdup(val);
} }
else if (strcmp(token, "ldapbasedn") == 0) else if (strcmp(name, "ldapbasedn") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
parsedline->ldapbasedn = pstrdup(c); hbaline->ldapbasedn = pstrdup(val);
} }
else if (strcmp(token, "ldapprefix") == 0) else if (strcmp(name, "ldapprefix") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
parsedline->ldapprefix = pstrdup(c); hbaline->ldapprefix = pstrdup(val);
} }
else if (strcmp(token, "ldapsuffix") == 0) else if (strcmp(name, "ldapsuffix") == 0)
{ {
REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap"); REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
parsedline->ldapsuffix = pstrdup(c); hbaline->ldapsuffix = pstrdup(val);
} }
else if (strcmp(token, "krb_server_hostname") == 0) else if (strcmp(name, "krb_server_hostname") == 0)
{ {
REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5"); REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
parsedline->krb_server_hostname = pstrdup(c); hbaline->krb_server_hostname = pstrdup(val);
} }
else if (strcmp(token, "krb_realm") == 0) else if (strcmp(name, "krb_realm") == 0)
{ {
if (parsedline->auth_method != uaKrb5 && if (hbaline->auth_method != uaKrb5 &&
parsedline->auth_method != uaGSS && hbaline->auth_method != uaGSS &&
parsedline->auth_method != uaSSPI) hbaline->auth_method != uaSSPI)
INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi")); INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
parsedline->krb_realm = pstrdup(c); hbaline->krb_realm = pstrdup(val);
} }
else if (strcmp(token, "include_realm") == 0) else if (strcmp(name, "include_realm") == 0)
{ {
if (parsedline->auth_method != uaKrb5 && if (hbaline->auth_method != uaKrb5 &&
parsedline->auth_method != uaGSS && hbaline->auth_method != uaGSS &&
parsedline->auth_method != uaSSPI) hbaline->auth_method != uaSSPI)
INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi")); INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
if (strcmp(c, "1") == 0) if (strcmp(val, "1") == 0)
parsedline->include_realm = true; hbaline->include_realm = true;
else else
parsedline->include_realm = false; hbaline->include_realm = false;
} }
else if (strcmp(token, "radiusserver") == 0) else if (strcmp(name, "radiusserver") == 0)
{ {
struct addrinfo *gai_result;
struct addrinfo hints;
int ret;
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius"); REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
MemSet(&hints, 0, sizeof(hints)); MemSet(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
ret = pg_getaddrinfo_all(c, NULL, &hints, &gai_result); ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
if (ret || !gai_result) if (ret || !gai_result)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not translate RADIUS server name \"%s\" to address: %s", errmsg("could not translate RADIUS server name \"%s\" to address: %s",
c, gai_strerror(ret)), val, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
if (gai_result) if (gai_result)
...@@ -1409,108 +1543,50 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) ...@@ -1409,108 +1543,50 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
return false; return false;
} }
pg_freeaddrinfo_all(hints.ai_family, gai_result); pg_freeaddrinfo_all(hints.ai_family, gai_result);
parsedline->radiusserver = pstrdup(c); hbaline->radiusserver = pstrdup(val);
} }
else if (strcmp(token, "radiusport") == 0) else if (strcmp(name, "radiusport") == 0)
{ {
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius"); REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
parsedline->radiusport = atoi(c); hbaline->radiusport = atoi(val);
if (parsedline->radiusport == 0) if (hbaline->radiusport == 0)
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid RADIUS port number: \"%s\"", c), errmsg("invalid RADIUS port number: \"%s\"", val),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
} }
else if (strcmp(token, "radiussecret") == 0) else if (strcmp(name, "radiussecret") == 0)
{ {
REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius"); REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
parsedline->radiussecret = pstrdup(c); hbaline->radiussecret = pstrdup(val);
} }
else if (strcmp(token, "radiusidentifier") == 0) else if (strcmp(name, "radiusidentifier") == 0)
{ {
REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius"); REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
parsedline->radiusidentifier = pstrdup(c); hbaline->radiusidentifier = pstrdup(val);
} }
else else
{ {
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("unrecognized authentication option name: \"%s\"", errmsg("unrecognized authentication option name: \"%s\"",
token), name),
errcontext("line %d of configuration file \"%s\"", errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName))); line_num, HbaFileName)));
return false; return false;
} }
}
}
/*
* Check if the selected authentication method has any mandatory arguments
* that are not set.
*/
if (parsedline->auth_method == uaLDAP)
{
MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
/*
* LDAP can operate in two modes: either with a direct bind, using
* ldapprefix and ldapsuffix, or using a search+bind, using
* ldapbasedn, ldapbinddn, ldapbindpasswd and ldapsearchattribute.
* Disallow mixing these parameters.
*/
if (parsedline->ldapprefix || parsedline->ldapsuffix)
{
if (parsedline->ldapbasedn ||
parsedline->ldapbinddn ||
parsedline->ldapbindpasswd ||
parsedline->ldapsearchattribute)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
}
else if (!parsedline->ldapbasedn)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
}
if (parsedline->auth_method == uaRADIUS)
{
MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
}
/*
* Enforce any parameters implied by other settings.
*/
if (parsedline->auth_method == uaCert)
{
parsedline->clientcert = true;
}
return true; return true;
} }
/* /*
* Scan the (pre-parsed) hba file line by line, looking for a match * Scan the pre-parsed hba file, looking for a match to the port's connection
* to the port's connection request. * request.
*/ */
static bool static void
check_hba(hbaPort *port) check_hba(hbaPort *port)
{ {
Oid roleid; Oid roleid;
...@@ -1589,72 +1665,21 @@ check_hba(hbaPort *port) ...@@ -1589,72 +1665,21 @@ check_hba(hbaPort *port)
/* Check database and role */ /* Check database and role */
if (!check_db(port->database_name, port->user_name, roleid, if (!check_db(port->database_name, port->user_name, roleid,
hba->database)) hba->databases))
continue; continue;
if (!check_role(port->user_name, roleid, hba->role)) if (!check_role(port->user_name, roleid, hba->roles))
continue; continue;
/* Found a record that matched! */ /* Found a record that matched! */
port->hba = hba; port->hba = hba;
return true; return;
} }
/* If no matching entry was found, then implicitly reject. */ /* If no matching entry was found, then implicitly reject. */
hba = palloc0(sizeof(HbaLine)); hba = palloc0(sizeof(HbaLine));
hba->auth_method = uaImplicitReject; hba->auth_method = uaImplicitReject;
port->hba = hba; port->hba = hba;
return true;
/*
* XXX: Return false only happens if we have a parsing error, which we can
* no longer have (parsing now in postmaster). Consider changing API.
*/
}
/*
* Free an HbaLine structure
*/
static void
free_hba_record(HbaLine *record)
{
if (record->database)
pfree(record->database);
if (record->role)
pfree(record->role);
if (record->usermap)
pfree(record->usermap);
if (record->pamservice)
pfree(record->pamservice);
if (record->ldapserver)
pfree(record->ldapserver);
if (record->ldapprefix)
pfree(record->ldapprefix);
if (record->ldapsuffix)
pfree(record->ldapsuffix);
if (record->krb_server_hostname)
pfree(record->krb_server_hostname);
if (record->krb_realm)
pfree(record->krb_realm);
pfree(record);
}
/*
* Free all records on the parsed HBA list
*/
static void
clean_hba_list(List *lines)
{
ListCell *line;
foreach(line, lines)
{
HbaLine *parsed = (HbaLine *) lfirst(line);
if (parsed)
free_hba_record(parsed);
}
list_free(lines);
} }
/* /*
...@@ -1674,6 +1699,9 @@ load_hba(void) ...@@ -1674,6 +1699,9 @@ load_hba(void)
*line_num; *line_num;
List *new_parsed_lines = NIL; List *new_parsed_lines = NIL;
bool ok = true; bool ok = true;
MemoryContext linecxt;
MemoryContext oldcxt;
MemoryContext hbacxt;
file = AllocateFile(HbaFileName, "r"); file = AllocateFile(HbaFileName, "r");
if (file == NULL) if (file == NULL)
...@@ -1691,20 +1719,29 @@ load_hba(void) ...@@ -1691,20 +1719,29 @@ load_hba(void)
return false; return false;
} }
tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums); linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
FreeFile(file); FreeFile(file);
/* Now parse all the lines */ /* Now parse all the lines */
hbacxt = AllocSetContextCreate(TopMemoryContext,
"hba parser context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(hbacxt);
forboth(line, hba_lines, line_num, hba_line_nums) forboth(line, hba_lines, line_num, hba_line_nums)
{ {
HbaLine *newline; HbaLine *newline;
newline = palloc0(sizeof(HbaLine)); if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num))) == NULL)
if (!parse_hba_line(lfirst(line), lfirst_int(line_num), newline))
{ {
/* Parse error in the file, so indicate there's a problem */ /*
free_hba_record(newline); * Parse error in the file, so indicate there's a problem. NB: a
* problem in a line will free the memory for all previous lines as
* well!
*/
MemoryContextReset(hbacxt);
new_parsed_lines = NIL;
ok = false; ok = false;
/* /*
...@@ -1718,18 +1755,21 @@ load_hba(void) ...@@ -1718,18 +1755,21 @@ load_hba(void)
new_parsed_lines = lappend(new_parsed_lines, newline); new_parsed_lines = lappend(new_parsed_lines, newline);
} }
/* Free the temporary lists */ /* Free tokenizer memory */
free_lines(&hba_lines, &hba_line_nums); MemoryContextDelete(linecxt);
MemoryContextSwitchTo(oldcxt);
if (!ok) if (!ok)
{ {
/* Parsing failed at one or more rows, so bail out */ /* Parsing failed at one or more rows, so bail out */
clean_hba_list(new_parsed_lines); MemoryContextDelete(hbacxt);
return false; return false;
} }
/* Loaded new file successfully, replace the one we use */ /* Loaded new file successfully, replace the one we use */
clean_hba_list(parsed_hba_lines); if (parsed_hba_context != NULL)
MemoryContextDelete(parsed_hba_context);
parsed_hba_context = hbacxt;
parsed_hba_lines = new_parsed_lines; parsed_hba_lines = new_parsed_lines;
return true; return true;
...@@ -1746,8 +1786,9 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name, ...@@ -1746,8 +1786,9 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
const char *pg_role, const char *ident_user, const char *pg_role, const char *ident_user,
bool case_insensitive, bool *found_p, bool *error_p) bool case_insensitive, bool *found_p, bool *error_p)
{ {
ListCell *line_item; ListCell *field;
char *token; List *tokens;
HbaToken *token;
char *file_map; char *file_map;
char *file_pgrole; char *file_pgrole;
char *file_ident_user; char *file_ident_user;
...@@ -1756,25 +1797,29 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name, ...@@ -1756,25 +1797,29 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
*error_p = false; *error_p = false;
Assert(line != NIL); Assert(line != NIL);
line_item = list_head(line); field = list_head(line);
/* Get the map token (must exist) */ /* Get the map token (must exist) */
token = lfirst(line_item); tokens = lfirst(field);
file_map = token; IDENT_MULTI_VALUE(tokens);
token = linitial(tokens);
file_map = token->string;
/* Get the ident user token */ /* Get the ident user token */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) IDENT_FIELD_ABSENT(field);
goto ident_syntax; tokens = lfirst(field);
token = lfirst(line_item); IDENT_MULTI_VALUE(tokens);
file_ident_user = token; token = linitial(tokens);
file_ident_user = token->string;
/* Get the PG rolename token */ /* Get the PG rolename token */
line_item = lnext(line_item); field = lnext(field);
if (!line_item) IDENT_FIELD_ABSENT(field);
goto ident_syntax; tokens = lfirst(field);
token = lfirst(line_item); IDENT_MULTI_VALUE(tokens);
file_pgrole = token; token = linitial(tokens);
file_pgrole = token->string;
if (strcmp(file_map, usermap_name) != 0) if (strcmp(file_map, usermap_name) != 0)
/* Line does not match the map name we're looking for, so just abort */ /* Line does not match the map name we're looking for, so just abort */
...@@ -1913,15 +1958,7 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name, ...@@ -1913,15 +1958,7 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
*found_p = true; *found_p = true;
} }
} }
return; return;
ident_syntax:
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("missing entry in file \"%s\" at end of line %d",
IdentFileName, line_number)));
*error_p = true;
} }
...@@ -1989,15 +2026,21 @@ check_usermap(const char *usermap_name, ...@@ -1989,15 +2026,21 @@ check_usermap(const char *usermap_name,
/* /*
* Read the ident config file and create a List of Lists of tokens in the file. * Read the ident config file and populate ident_lines and ident_line_nums.
*
* Like parsed_hba_lines, ident_lines is a triple-nested List of lines, fields
* and tokens.
*/ */
void void
load_ident(void) load_ident(void)
{ {
FILE *file; FILE *file;
if (ident_lines || ident_line_nums) if (ident_context != NULL)
free_lines(&ident_lines, &ident_line_nums); {
MemoryContextDelete(ident_context);
ident_context = NULL;
}
file = AllocateFile(IdentFileName, "r"); file = AllocateFile(IdentFileName, "r");
if (file == NULL) if (file == NULL)
...@@ -2010,7 +2053,8 @@ load_ident(void) ...@@ -2010,7 +2053,8 @@ load_ident(void)
} }
else else
{ {
tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums); ident_context = tokenize_file(IdentFileName, file, &ident_lines,
&ident_line_nums);
FreeFile(file); FreeFile(file);
} }
} }
...@@ -2022,15 +2066,11 @@ load_ident(void) ...@@ -2022,15 +2066,11 @@ load_ident(void)
* "database" from frontend "raddr", user "user". Return the method and * "database" from frontend "raddr", user "user". Return the method and
* an optional argument (stored in fields of *port), and STATUS_OK. * an optional argument (stored in fields of *port), and STATUS_OK.
* *
* Note that STATUS_ERROR indicates a problem with the hba config file. * If the file does not contain any entry matching the request, we return
* If the file is OK but does not contain any entry matching the request, * method = uaImplicitReject.
* we return STATUS_OK and method = uaImplicitReject.
*/ */
int void
hba_getauthmethod(hbaPort *port) hba_getauthmethod(hbaPort *port)
{ {
if (check_hba(port)) check_hba(port);
return STATUS_OK;
else
return STATUS_ERROR;
} }
...@@ -49,12 +49,12 @@ typedef enum ConnType ...@@ -49,12 +49,12 @@ typedef enum ConnType
ctHostNoSSL ctHostNoSSL
} ConnType; } ConnType;
typedef struct typedef struct HbaLine
{ {
int linenumber; int linenumber;
ConnType conntype; ConnType conntype;
char *database; List *databases;
char *role; List *roles;
struct sockaddr_storage addr; struct sockaddr_storage addr;
struct sockaddr_storage mask; struct sockaddr_storage mask;
IPCompareMethod ip_cmp_method; IPCompareMethod ip_cmp_method;
...@@ -87,7 +87,7 @@ typedef struct Port hbaPort; ...@@ -87,7 +87,7 @@ typedef struct Port hbaPort;
extern bool load_hba(void); extern bool load_hba(void);
extern void load_ident(void); extern void load_ident(void);
extern int hba_getauthmethod(hbaPort *port); extern void hba_getauthmethod(hbaPort *port);
extern int check_usermap(const char *usermap_name, extern int check_usermap(const char *usermap_name,
const char *pg_role, const char *auth_user, const char *pg_role, const char *auth_user,
bool case_sensitive); bool case_sensitive);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment