#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#define atooid(x) ((Oid) 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
/* callback data for check_network_callback */
bool result; /* set to true if match */
} 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 MemoryContext parsed_hba_context = NULL;
/*
* These variables hold the pre-parsed contents of the ident usermap
- * configuration file. ident_lines is a list of sublists, one sublist for
- * each (non-empty, non-comment) line of the file. The sublist items are
- * palloc'd strings, one string per token on the line. Note there will always
- * be at least one token, since blank lines are not entered in the data
- * structure. ident_line_nums is an integer list containing the actual line
- * number for each line represented in ident_lines.
+ * configuration file. ident_lines is a triple-nested list of lines, fields
+ * and tokens, as returned by tokenize_file. There will be one line in
+ * ident_lines for each (non-empty, non-comment) line of the file. Note there
+ * will always be at least one field, since blank lines are not entered in the
+ * data structure. ident_line_nums is an integer list containing the actual
+ * 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_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);
-static char *tokenize_inc_file(const char *outer_filename,
+static List *tokenize_inc_file(List *tokens, const char *outer_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,
* the first character. (We use that to prevent "@x" from being 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 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 no more tokens on line: set *buf = '\0' and return FALSE.
* whichever comes first. If no more tokens on line, position the file to the
* beginning of the next line or EOF, whichever comes first.
*
- * Handle comments. Treat unquoted keywords that might be role, database, or
- * 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.
+ * Handle comments.
*/
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;
char *start_buf = buf;
Assert(end_buf > start_buf);
*initial_quote = false;
+ *terminating_comma = false;
/* Move over initial whitespace and commas */
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
break;
}
- if (c != '"' || was_quote)
- *buf++ = c;
-
- /* We pass back the comma so the caller knows there is more */
+ /* we do not pass back the comma in the token */
if (c == ',' && !in_quote)
+ {
+ *terminating_comma = true;
break;
+ }
+
+ if (c != '"' || was_quote)
+ *buf++ = c;
/* Literal double-quote is two double-quotes */
if (in_quote && c == '"')
*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);
}
+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
- * to break apart the commas to expand any file names then
- * reconstruct with commas.
+ * Copy a HbaToken struct into freshly palloc'd memory.
+ */
+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 *
-next_token_expand(const char *filename, FILE *file)
+static List *
+next_field_expand(const char *filename, FILE *file)
{
char buf[MAX_TOKEN];
- char *comma_str = pstrdup("");
- bool got_something = false;
bool trailing_comma;
bool initial_quote;
- char *incbuf;
- int needed;
+ List *tokens = NIL;
do
{
- if (!next_token(file, buf, sizeof(buf), &initial_quote))
+ if (!next_token(file, buf, sizeof(buf), &initial_quote, &trailing_comma))
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? */
if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
- incbuf = tokenize_inc_file(filename, buf + 1);
+ tokens = tokenize_inc_file(tokens, filename, buf + 1);
else
- incbuf = pstrdup(buf);
-
- 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);
+ tokens = lappend(tokens, make_hba_token(buf, initial_quote));
} while (trailing_comma);
- if (!got_something)
- {
- pfree(comma_str);
- return NULL;
- }
-
- return comma_str;
+ return tokens;
}
-
/*
- * 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"
+ *
+ * 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
+ * initial contents of list (so foo,bar,@baz does what you expect).
*/
-static void
-free_lines(List **lines, List **line_nums)
-{
- /*
- * Either both must be non-NULL, or both must be NULL
- */
- 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;
-
- foreach(line, *lines)
- {
- 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,
+static List *
+tokenize_inc_file(List *tokens,
+ const char *outer_filename,
const char *inc_filename)
{
char *inc_fullname;
FILE *inc_file;
List *inc_lines;
List *inc_line_nums;
- ListCell *line;
- char *comma_str;
+ ListCell *inc_line;
+ MemoryContext linecxt;
if (is_absolute_path(inc_filename))
{
errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
inc_filename, inc_fullname)));
pfree(inc_fullname);
-
- /* return single space, it matches nothing */
- return pstrdup(" ");
+ return tokens;
}
/* 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);
pfree(inc_fullname);
- /* Create comma-separated string from List */
- comma_str = pstrdup("");
- foreach(line, inc_lines)
+ foreach(inc_line, inc_lines)
{
- List *token_list = (List *) lfirst(line);
- ListCell *token;
+ List *inc_fields = lfirst(inc_line);
+ ListCell *inc_field;
- foreach(token, token_list)
+ foreach(inc_field, inc_fields)
{
- int oldlen = strlen(comma_str);
- int needed;
-
- needed = oldlen + strlen(lfirst(token)) + 1;
- if (oldlen > 0)
- needed++;
- comma_str = repalloc(comma_str, needed);
- if (oldlen > 0)
- strcat(comma_str, MULTI_VALUE_SEP);
- strcat(comma_str, lfirst(token));
- }
- }
+ List *inc_tokens = lfirst(inc_field);
+ ListCell *inc_token;
- free_lines(&inc_lines, &inc_line_nums);
+ foreach(inc_token, inc_tokens)
+ {
+ HbaToken *token = lfirst(inc_token);
- /* if file is empty, return single space rather than empty string */
- if (strlen(comma_str) == 0)
- {
- pfree(comma_str);
- return pstrdup(" ");
+ tokens = lappend(tokens, copy_hba_token(token));
+ }
+ }
}
- return comma_str;
+ MemoryContextDelete(linecxt);
+ return tokens;
}
-
/*
- * Tokenize the given file, storing the resulting data into two lists:
- * a list of sublists, each sublist containing the tokens in a line of
- * the file, and a list of line numbers.
+ * Tokenize the given file, storing the resulting data into two Lists: a
+ * List of lines, 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.
+ *
+ * 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,
List **lines, List **line_nums)
{
List *current_line = NIL;
+ List *current_field = NIL;
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;
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 */
- if (buf)
+ /* add tokens to list, unless we are at EOL or comment start */
+ if (list_length(current_field) > 0)
{
if (current_line == NIL)
{
/* 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);
*line_nums = lappend_int(*line_nums, line_number);
}
else
{
- /* append token to current line's list */
- current_line = lappend(current_line, buf);
+ /* append tokens to current line's list */
+ current_line = lappend(current_line, current_field);
}
}
else
{
/* we are at real or logical EOL, so force a new line List */
current_line = NIL;
- /* Advance line number whenever we reach EOL */
line_number++;
}
}
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return linecxt;
}
}
/*
- * Check comma-separated 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.
+ * Check HbaToken list for a match to role, allowing group names.
*/
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);
- tok != NULL;
- tok = strtok(NULL, MULTI_VALUE_SEP))
+ foreach(cell, tokens)
{
- 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;
}
- else if (strcmp(tok, role) == 0 ||
- (strcmp(tok, "replication\n") == 0 &&
- strcmp(role, "replication") == 0) ||
- strcmp(tok, "all\n") == 0)
+ else if (token_matches(tok, role) ||
+ token_is_keyword(tok, "all"))
return true;
}
-
return false;
}
/*
- * Check to see if db/role combination matches param string.
- *
- * 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.
+ * Check to see if db/role combination matches HbaToken list.
*/
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);
- tok != NULL;
- tok = strtok(NULL, MULTI_VALUE_SEP))
+ foreach(cell, tokens)
{
+ tok = lfirst(cell);
if (am_walsender)
{
/* walsender connections can only match replication keyword */
- if (strcmp(tok, "replication\n") == 0)
+ if (token_is_keyword(tok, "replication"))
return true;
}
- else if (strcmp(tok, "all\n") == 0)
+ else if (token_is_keyword(tok, "all"))
return true;
- else if (strcmp(tok, "sameuser\n") == 0)
+ else if (token_is_keyword(tok, "sameuser"))
{
if (strcmp(dbname, role) == 0)
return true;
}
- else if (strcmp(tok, "samegroup\n") == 0 ||
- strcmp(tok, "samerole\n") == 0)
+ else if (token_is_keyword(tok, "samegroup") ||
+ token_is_keyword(tok, "samerole"))
{
if (is_member(roleid, dbname))
return true;
}
- else if (strcmp(tok, "replication\n") == 0)
+ else if (token_is_keyword(tok, "replication"))
continue; /* never match this if not walsender */
- else if (strcmp(tok, dbname) == 0)
+ else if (token_matches(tok, dbname))
return true;
}
return false;
} while (0);
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\
- if (parsedline->auth_method != methodval) \
+ if (hbaline->auth_method != methodval) \
INVALID_AUTH_OPTION(optname, validmethods); \
} 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
- * a HbaLine structure.
+ * Parse one tokenised line from the hba config file and store the result in a
+ * 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
-parse_hba_line(List *line, int line_num, HbaLine *parsedline)
+static HbaLine *
+parse_hba_line(List *line, int line_num)
{
- char *token;
+ char *str;
struct addrinfo *gai_result;
struct addrinfo hints;
int ret;
char *cidr_slash;
char *unsupauth;
- ListCell *line_item;
-
- line_item = list_head(line);
+ ListCell *field;
+ List *tokens;
+ ListCell *tokencell;
+ HbaToken *token;
+ HbaLine *parsedline;
+ parsedline = palloc0(sizeof(HbaLine));
parsedline->linenumber = line_num;
/* Check the record type. */
- token = lfirst(line_item);
- if (strcmp(token, "local") == 0)
+ field = list_head(line);
+ 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
parsedline->conntype = ctLocal;
errmsg("local connections are not supported by this build"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
#endif
}
- else if (strcmp(token, "host") == 0
- || strcmp(token, "hostssl") == 0
- || strcmp(token, "hostnossl") == 0)
+ else if (strcmp(token->string, "host") == 0 ||
+ strcmp(token->string, "hostssl") == 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 */
#ifdef USE_SSL
errhint("Set ssl = on in postgresql.conf."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
#else
ereport(LOG,
errhint("Compile with --with-openssl to use SSL connections."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
#endif
}
#ifdef USE_SSL
- else if (token[4] == 'n') /* "hostnossl" */
+ else if (token->string[4] == 'n') /* "hostnossl" */
{
parsedline->conntype = ctHostNoSSL;
}
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid connection type \"%s\"",
- token),
+ token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
- /* Get the database. */
- line_item = lnext(line_item);
- if (!line_item)
+ /* Get the databases. */
+ field = lnext(field);
+ if (!field)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before database specification"),
errcontext("line %d of configuration file \"%s\"",
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. */
- line_item = lnext(line_item);
- if (!line_item)
+ /* Get the roles. */
+ field = lnext(field);
+ if (!field)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before role specification"),
errcontext("line %d of configuration file \"%s\"",
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)
{
/* Read the IP address field. (with or without CIDR netmask) */
- line_item = lnext(line_item);
- if (!line_item)
+ field = lnext(field);
+ if (!field)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before IP address specification"),
errcontext("line %d of configuration file \"%s\"",
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;
}
- else if (strcmp(token, "samehost\n") == 0)
+ else if (token_is_keyword(token, "samehost"))
{
/* Any IP on this host is allowed to connect */
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 */
parsedline->ip_cmp_method = ipCmpSameNet;
parsedline->ip_cmp_method = ipCmpMask;
/* 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 */
- cidr_slash = strchr(token, '/');
+ cidr_slash = strchr(str, '/');
if (cidr_slash)
*cidr_slash = '\0';
hints.ai_addr = 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)
memcpy(&parsedline->addr, gai_result->ai_addr,
gai_result->ai_addrlen);
else if (ret == EAI_NONAME)
- parsedline->hostname = token;
+ parsedline->hostname = str;
else
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP address \"%s\": %s",
- token, gai_strerror(ret)),
+ str, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
- pfree(token);
- return false;
+ return NULL;
}
pg_freeaddrinfo_all(hints.ai_family, gai_result);
{
if (parsedline->hostname)
{
- *cidr_slash = '/'; /* restore token for message */
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
- token),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- pfree(token);
- return false;
+ token->string),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return NULL;
}
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0)
{
- *cidr_slash = '/'; /* restore token for message */
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid CIDR mask in address \"%s\"",
- token),
+ token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- pfree(token);
- return false;
+ return NULL;
}
- pfree(token);
+ pfree(str);
}
else if (!parsedline->hostname)
{
/* Read the mask field. */
- pfree(token);
- line_item = lnext(line_item);
- if (!line_item)
+ pfree(str);
+ field = lnext(field);
+ if (!field)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
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\"",
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)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\": %s",
- token, gai_strerror(ret)),
+ token->string, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
- return false;
+ return NULL;
}
memcpy(&parsedline->mask, gai_result->ai_addr,
errmsg("IP address and mask do not match"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
}
}
} /* != ctLocal */
/* Get the authentication method */
- line_item = lnext(line_item);
- if (!line_item)
+ field = lnext(field);
+ if (!field)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before authentication method"),
errcontext("line %d of configuration file \"%s\"",
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 authentication type"),
+ errhint("Specify exactly one authentication type per line."),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return NULL;
}
- token = lfirst(line_item);
+ token = linitial(tokens);
unsupauth = NULL;
- if (strcmp(token, "trust") == 0)
+ if (strcmp(token->string, "trust") == 0)
parsedline->auth_method = uaTrust;
- else if (strcmp(token, "ident") == 0)
+ else if (strcmp(token->string, "ident") == 0)
parsedline->auth_method = uaIdent;
- else if (strcmp(token, "peer") == 0)
+ else if (strcmp(token->string, "peer") == 0)
parsedline->auth_method = uaPeer;
- else if (strcmp(token, "password") == 0)
+ else if (strcmp(token->string, "password") == 0)
parsedline->auth_method = uaPassword;
- else if (strcmp(token, "krb5") == 0)
+ else if (strcmp(token->string, "krb5") == 0)
#ifdef KRB5
parsedline->auth_method = uaKrb5;
#else
unsupauth = "krb5";
#endif
- else if (strcmp(token, "gss") == 0)
+ else if (strcmp(token->string, "gss") == 0)
#ifdef ENABLE_GSS
parsedline->auth_method = uaGSS;
#else
unsupauth = "gss";
#endif
- else if (strcmp(token, "sspi") == 0)
+ else if (strcmp(token->string, "sspi") == 0)
#ifdef ENABLE_SSPI
parsedline->auth_method = uaSSPI;
#else
unsupauth = "sspi";
#endif
- else if (strcmp(token, "reject") == 0)
+ else if (strcmp(token->string, "reject") == 0)
parsedline->auth_method = uaReject;
- else if (strcmp(token, "md5") == 0)
+ else if (strcmp(token->string, "md5") == 0)
{
if (Db_user_namespace)
{
errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
parsedline->auth_method = uaMD5;
}
- else if (strcmp(token, "pam") == 0)
+ else if (strcmp(token->string, "pam") == 0)
#ifdef USE_PAM
parsedline->auth_method = uaPAM;
#else
unsupauth = "pam";
#endif
- else if (strcmp(token, "ldap") == 0)
+ else if (strcmp(token->string, "ldap") == 0)
#ifdef USE_LDAP
parsedline->auth_method = uaLDAP;
#else
unsupauth = "ldap";
#endif
- else if (strcmp(token, "cert") == 0)
+ else if (strcmp(token->string, "cert") == 0)
#ifdef USE_SSL
parsedline->auth_method = uaCert;
#else
unsupauth = "cert";
#endif
- else if (strcmp(token, "radius") == 0)
+ else if (strcmp(token->string, "radius") == 0)
parsedline->auth_method = uaRADIUS;
else
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\"",
- token),
+ token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
if (unsupauth)
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid authentication method \"%s\": not supported by this build",
- token),
+ token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
/*
errmsg("krb5 authentication is not supported on local sockets"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
if (parsedline->conntype == ctLocal &&
errmsg("gssapi authentication is not supported on local sockets"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
if (parsedline->conntype != ctLocal &&
errmsg("peer authentication is only supported on local sockets"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
/*
* SSPI authentication can never be enabled on ctLocal connections,
* because it's only supported on Windows, where ctLocal isn't supported.
*/
-
-
if (parsedline->conntype != ctHostSSL &&
parsedline->auth_method == uaCert)
{
errmsg("cert authentication is only supported on hostssl connections"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
/* Parse remaining arguments */
- while ((line_item = lnext(line_item)) != NULL)
+ while ((field = lnext(field)) != NULL)
{
- char *c;
-
- token = lfirst(line_item);
-
- c = strchr(token, '=');
- if (c == NULL)
- {
- /*
- * Got something that's not a name=value pair.
- */
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("authentication option not in name=value format: %s", token),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- else
+ tokens = lfirst(field);
+ foreach(tokencell, tokens)
{
- *c++ = '\0'; /* token now holds "name", c holds "value" */
- if (strcmp(token, "map") == 0)
- {
- if (parsedline->auth_method != uaIdent &&
- parsedline->auth_method != uaPeer &&
- parsedline->auth_method != uaKrb5 &&
- parsedline->auth_method != uaGSS &&
- parsedline->auth_method != uaSSPI &&
- parsedline->auth_method != uaCert)
- INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, krb5, gssapi, sspi and cert"));
- parsedline->usermap = pstrdup(c);
- }
- else if (strcmp(token, "clientcert") == 0)
+ char *val;
+ token = lfirst(tokencell);
+
+ str = pstrdup(token->string);
+ val = strchr(str, '=');
+ if (val == NULL)
{
/*
- * Since we require ctHostSSL, this really can never happen on
- * non-SSL-enabled builds, so don't bother checking for
- * USE_SSL.
+ * Got something that's not a name=value pair.
*/
- if (parsedline->conntype != ctHostSSL)
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("clientcert can only be configured for \"hostssl\" rows"),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- if (strcmp(c, "1") == 0)
- {
- if (!secure_loaded_verify_locations())
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("client certificates can only be checked if a root certificate store is available"),
- errhint("Make sure the root.crt file is present and readable."),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- parsedline->clientcert = true;
- }
- else
- {
- if (parsedline->auth_method == uaCert)
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- parsedline->clientcert = false;
- }
- }
- else if (strcmp(token, "pamservice") == 0)
- {
- REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
- parsedline->pamservice = pstrdup(c);
- }
- else if (strcmp(token, "ldaptls") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
- if (strcmp(c, "1") == 0)
- parsedline->ldaptls = true;
- else
- parsedline->ldaptls = false;
- }
- else if (strcmp(token, "ldapserver") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
- parsedline->ldapserver = pstrdup(c);
- }
- else if (strcmp(token, "ldapport") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
- parsedline->ldapport = atoi(c);
- if (parsedline->ldapport == 0)
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("invalid LDAP port number: \"%s\"", c),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- }
- else if (strcmp(token, "ldapbinddn") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
- parsedline->ldapbinddn = pstrdup(c);
- }
- else if (strcmp(token, "ldapbindpasswd") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
- parsedline->ldapbindpasswd = pstrdup(c);
- }
- else if (strcmp(token, "ldapsearchattribute") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
- parsedline->ldapsearchattribute = pstrdup(c);
- }
- else if (strcmp(token, "ldapbasedn") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
- parsedline->ldapbasedn = pstrdup(c);
- }
- else if (strcmp(token, "ldapprefix") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
- parsedline->ldapprefix = pstrdup(c);
- }
- else if (strcmp(token, "ldapsuffix") == 0)
- {
- REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
- parsedline->ldapsuffix = pstrdup(c);
- }
- else if (strcmp(token, "krb_server_hostname") == 0)
- {
- REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
- parsedline->krb_server_hostname = pstrdup(c);
- }
- else if (strcmp(token, "krb_realm") == 0)
- {
- if (parsedline->auth_method != uaKrb5 &&
- parsedline->auth_method != uaGSS &&
- parsedline->auth_method != uaSSPI)
- INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
- parsedline->krb_realm = pstrdup(c);
- }
- else if (strcmp(token, "include_realm") == 0)
- {
- if (parsedline->auth_method != uaKrb5 &&
- parsedline->auth_method != uaGSS &&
- parsedline->auth_method != uaSSPI)
- INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
- if (strcmp(c, "1") == 0)
- parsedline->include_realm = true;
- else
- parsedline->include_realm = false;
- }
- else if (strcmp(token, "radiusserver") == 0)
- {
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
-
- MemSet(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_family = AF_UNSPEC;
-
- ret = pg_getaddrinfo_all(c, NULL, &hints, &gai_result);
- if (ret || !gai_result)
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- c, gai_strerror(ret)),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- if (gai_result)
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
- return false;
- }
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
- parsedline->radiusserver = pstrdup(c);
- }
- else if (strcmp(token, "radiusport") == 0)
- {
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
- parsedline->radiusport = atoi(c);
- if (parsedline->radiusport == 0)
- {
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("invalid RADIUS port number: \"%s\"", c),
- errcontext("line %d of configuration file \"%s\"",
- line_num, HbaFileName)));
- return false;
- }
- }
- else if (strcmp(token, "radiussecret") == 0)
- {
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
- parsedline->radiussecret = pstrdup(c);
- }
- else if (strcmp(token, "radiusidentifier") == 0)
- {
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
- parsedline->radiusidentifier = pstrdup(c);
- }
- else
- {
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("unrecognized authentication option name: \"%s\"",
- token),
+ errmsg("authentication option not in name=value format: %s", token->string),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
+
+ *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);
}
}
errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, or ldapsearchattribute together with ldapprefix"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
- return false;
+ return NULL;
}
}
else if (!parsedline->ldapbasedn)
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;
+ return NULL;
}
}
parsedline->clientcert = true;
}
- return true;
+ return parsedline;
}
-
/*
- * Scan the (pre-parsed) hba file line by line, looking for a match
- * to the port's connection request.
+ * 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"));
+ hbaline->usermap = pstrdup(val);
+ }
+ else if (strcmp(name, "clientcert") == 0)
+ {
+ /*
+ * Since we require ctHostSSL, this really can never happen
+ * on non-SSL-enabled builds, so don't bother checking for
+ * USE_SSL.
+ */
+ if (hbaline->conntype != ctHostSSL)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("clientcert can only be configured for \"hostssl\" rows"),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ if (strcmp(val, "1") == 0)
+ {
+ if (!secure_loaded_verify_locations())
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("client certificates can only be checked if a root certificate store is available"),
+ errhint("Make sure the root.crt file is present and readable."),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ hbaline->clientcert = true;
+ }
+ else
+ {
+ if (hbaline->auth_method == uaCert)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("clientcert can not be set to 0 when using \"cert\" authentication"),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ hbaline->clientcert = false;
+ }
+ }
+ else if (strcmp(name, "pamservice") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
+ hbaline->pamservice = pstrdup(val);
+ }
+ else if (strcmp(name, "ldaptls") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
+ if (strcmp(val, "1") == 0)
+ hbaline->ldaptls = true;
+ else
+ hbaline->ldaptls = false;
+ }
+ else if (strcmp(name, "ldapserver") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
+ hbaline->ldapserver = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapport") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
+ hbaline->ldapport = atoi(val);
+ if (hbaline->ldapport == 0)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("invalid LDAP port number: \"%s\"", val),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ }
+ else if (strcmp(name, "ldapbinddn") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
+ hbaline->ldapbinddn = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapbindpasswd") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
+ hbaline->ldapbindpasswd = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapsearchattribute") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
+ hbaline->ldapsearchattribute = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapbasedn") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
+ hbaline->ldapbasedn = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapprefix") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
+ hbaline->ldapprefix = pstrdup(val);
+ }
+ else if (strcmp(name, "ldapsuffix") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
+ hbaline->ldapsuffix = pstrdup(val);
+ }
+ else if (strcmp(name, "krb_server_hostname") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaKrb5, "krb_server_hostname", "krb5");
+ hbaline->krb_server_hostname = pstrdup(val);
+ }
+ else if (strcmp(name, "krb_realm") == 0)
+ {
+ if (hbaline->auth_method != uaKrb5 &&
+ hbaline->auth_method != uaGSS &&
+ hbaline->auth_method != uaSSPI)
+ INVALID_AUTH_OPTION("krb_realm", gettext_noop("krb5, gssapi and sspi"));
+ hbaline->krb_realm = pstrdup(val);
+ }
+ else if (strcmp(name, "include_realm") == 0)
+ {
+ if (hbaline->auth_method != uaKrb5 &&
+ hbaline->auth_method != uaGSS &&
+ hbaline->auth_method != uaSSPI)
+ INVALID_AUTH_OPTION("include_realm", gettext_noop("krb5, gssapi and sspi"));
+ if (strcmp(val, "1") == 0)
+ hbaline->include_realm = true;
+ else
+ hbaline->include_realm = false;
+ }
+ else if (strcmp(name, "radiusserver") == 0)
+ {
+ struct addrinfo *gai_result;
+ struct addrinfo hints;
+ int ret;
+
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
+
+ MemSet(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_family = AF_UNSPEC;
+
+ ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
+ if (ret || !gai_result)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not translate RADIUS server name \"%s\" to address: %s",
+ val, gai_strerror(ret)),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ if (gai_result)
+ pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ return false;
+ }
+ pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ hbaline->radiusserver = pstrdup(val);
+ }
+ else if (strcmp(name, "radiusport") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
+ hbaline->radiusport = atoi(val);
+ if (hbaline->radiusport == 0)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("invalid RADIUS port number: \"%s\"", val),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ }
+ else if (strcmp(name, "radiussecret") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
+ hbaline->radiussecret = pstrdup(val);
+ }
+ else if (strcmp(name, "radiusidentifier") == 0)
+ {
+ REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
+ hbaline->radiusidentifier = pstrdup(val);
+ }
+ else
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("unrecognized authentication option name: \"%s\"",
+ name),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Scan the pre-parsed hba file, looking for a match to the port's connection
+ * request.
+ */
+static void
check_hba(hbaPort *port)
{
Oid roleid;
/* Check database and role */
if (!check_db(port->database_name, port->user_name, roleid,
- hba->database))
+ hba->databases))
continue;
- if (!check_role(port->user_name, roleid, hba->role))
+ if (!check_role(port->user_name, roleid, hba->roles))
continue;
/* Found a record that matched! */
port->hba = hba;
- return true;
+ return;
}
/* If no matching entry was found, then implicitly reject. */
hba = palloc0(sizeof(HbaLine));
hba->auth_method = uaImplicitReject;
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);
}
/*
*line_num;
List *new_parsed_lines = NIL;
bool ok = true;
+ MemoryContext linecxt;
+ MemoryContext oldcxt;
+ MemoryContext hbacxt;
file = AllocateFile(HbaFileName, "r");
if (file == NULL)
return false;
}
- tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
+ linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
FreeFile(file);
/* 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)
{
HbaLine *newline;
- newline = palloc0(sizeof(HbaLine));
-
- if (!parse_hba_line(lfirst(line), lfirst_int(line_num), newline))
+ if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num))) == NULL)
{
- /* 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;
/*
new_parsed_lines = lappend(new_parsed_lines, newline);
}
- /* Free the temporary lists */
- free_lines(&hba_lines, &hba_line_nums);
+ /* Free tokenizer memory */
+ MemoryContextDelete(linecxt);
+ MemoryContextSwitchTo(oldcxt);
if (!ok)
{
/* Parsing failed at one or more rows, so bail out */
- clean_hba_list(new_parsed_lines);
+ MemoryContextDelete(hbacxt);
return false;
}
/* 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;
return true;
const char *pg_role, const char *ident_user,
bool case_insensitive, bool *found_p, bool *error_p)
{
- ListCell *line_item;
- char *token;
+ ListCell *field;
+ List *tokens;
+ HbaToken *token;
char *file_map;
char *file_pgrole;
char *file_ident_user;
*error_p = false;
Assert(line != NIL);
- line_item = list_head(line);
+ field = list_head(line);
/* Get the map token (must exist) */
- token = lfirst(line_item);
- file_map = token;
+ tokens = lfirst(field);
+ IDENT_MULTI_VALUE(tokens);
+ token = linitial(tokens);
+ file_map = token->string;
/* Get the ident user token */
- line_item = lnext(line_item);
- if (!line_item)
- goto ident_syntax;
- token = lfirst(line_item);
- file_ident_user = token;
+ field = lnext(field);
+ IDENT_FIELD_ABSENT(field);
+ tokens = lfirst(field);
+ IDENT_MULTI_VALUE(tokens);
+ token = linitial(tokens);
+ file_ident_user = token->string;
/* Get the PG rolename token */
- line_item = lnext(line_item);
- if (!line_item)
- goto ident_syntax;
- token = lfirst(line_item);
- file_pgrole = token;
+ field = lnext(field);
+ IDENT_FIELD_ABSENT(field);
+ tokens = lfirst(field);
+ IDENT_MULTI_VALUE(tokens);
+ token = linitial(tokens);
+ file_pgrole = token->string;
if (strcmp(file_map, usermap_name) != 0)
/* Line does not match the map name we're looking for, so just abort */
*found_p = true;
}
}
-
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;
}
/*
- * 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
load_ident(void)
{
FILE *file;
- if (ident_lines || ident_line_nums)
- free_lines(&ident_lines, &ident_line_nums);
+ if (ident_context != NULL)
+ {
+ MemoryContextDelete(ident_context);
+ ident_context = NULL;
+ }
file = AllocateFile(IdentFileName, "r");
if (file == NULL)
}
else
{
- tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
+ ident_context = tokenize_file(IdentFileName, file, &ident_lines,
+ &ident_line_nums);
FreeFile(file);
}
}
* "database" from frontend "raddr", user "user". Return the method and
* 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 is OK but does not contain any entry matching the request,
- * we return STATUS_OK and method = uaImplicitReject.
+ * If the file does not contain any entry matching the request, we return
+ * method = uaImplicitReject.
*/
-int
+void
hba_getauthmethod(hbaPort *port)
{
- if (check_hba(port))
- return STATUS_OK;
- else
- return STATUS_ERROR;
+ check_hba(port);
}