} check_network_data;
+#define token_has_regexp(t) (t->regex != NULL)
#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
#define token_matches(t, k) (strcmp(t->string, k) == 0)
* pre-parsed content of ident mapping file: list of IdentLine structs.
* parsed_ident_context is the memory context where it lives.
*
- * NOTE: the IdentLine structs can contain pre-compiled regular expressions
- * that live outside the memory context. Before destroying or resetting the
- * memory context, they need to be explicitly free'd.
+ * NOTE: the IdentLine structs can contain AuthTokens with pre-compiled
+ * regular expressions that live outside the memory context. Before
+ * destroying or resetting the memory context, they need to be explicitly
+ * free'd.
*/
static List *parsed_ident_lines = NIL;
static MemoryContext parsed_ident_context = NULL;
const char *inc_filename, int elevel, char **err_msg);
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
int elevel, char **err_msg);
+static int regcomp_auth_token(AuthToken *token);
+static int regexec_auth_token(const char *match, AuthToken *token,
+ size_t nmatch, regmatch_t pmatch[]);
/*
toklen = strlen(token);
/* we copy string into same palloc block as the struct */
- authtoken = (AuthToken *) palloc(sizeof(AuthToken) + toklen + 1);
+ authtoken = (AuthToken *) palloc0(sizeof(AuthToken) + toklen + 1);
authtoken->string = (char *) authtoken + sizeof(AuthToken);
authtoken->quoted = quoted;
+ authtoken->regex = NULL;
memcpy(authtoken->string, token, toklen + 1);
return authtoken;
}
+/*
+ * Free an AuthToken, that may include a regular expression that needs
+ * to be cleaned up explicitly.
+ */
+static void
+free_auth_token(AuthToken *token)
+{
+ if (token_has_regexp(token))
+ pg_regfree(token->regex);
+}
+
/*
* Copy a AuthToken struct into freshly palloc'd memory.
*/
return out;
}
+/*
+ * Compile the regular expression and store it in the AuthToken given in
+ * input. Returns the result of pg_regcomp().
+ */
+static int
+regcomp_auth_token(AuthToken *token)
+{
+ pg_wchar *wstr;
+ int wlen;
+ int rc;
+
+ Assert(token->regex == NULL);
+
+ if (token->string[0] != '/')
+ return 0; /* nothing to compile */
+
+ token->regex = (regex_t *) palloc0(sizeof(regex_t));
+ wstr = palloc((strlen(token->string + 1) + 1) * sizeof(pg_wchar));
+ wlen = pg_mb2wchar_with_len(token->string + 1,
+ wstr, strlen(token->string + 1));
+
+ rc = pg_regcomp(token->regex, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
+
+ pfree(wstr);
+ return rc;
+}
+
+/*
+ * Execute a regular expression computed in an AuthToken, checking for a match
+ * with the string specified in "match". The caller may optionally give an
+ * array to store the matches. Returns the result of pg_regexec().
+ */
+static int
+regexec_auth_token(const char *match, AuthToken *token, size_t nmatch,
+ regmatch_t pmatch[])
+{
+ pg_wchar *wmatchstr;
+ int wmatchlen;
+ int r;
+
+ Assert(token->string[0] == '/' && token->regex);
+
+ wmatchstr = palloc((strlen(match) + 1) * sizeof(pg_wchar));
+ wmatchlen = pg_mb2wchar_with_len(match, wmatchstr, strlen(match));
+
+ r = pg_regexec(token->regex, wmatchstr, wmatchlen, 0, NULL, nmatch, pmatch, 0);
+
+ pfree(wmatchstr);
+ return r;
+}
/*
* Tokenize one HBA field from a line, handling file inclusion and comma lists.
List *tokens;
AuthToken *token;
IdentLine *parsedline;
+ int rc;
Assert(tok_line->fields != NIL);
field = list_head(tok_line->fields);
tokens = lfirst(field);
IDENT_MULTI_VALUE(tokens);
token = linitial(tokens);
- parsedline->ident_user = pstrdup(token->string);
+
+ /* Copy the ident user token */
+ parsedline->token = copy_auth_token(token);
/* Get the PG rolename token */
field = lnext(tok_line->fields, field);
token = linitial(tokens);
parsedline->pg_role = pstrdup(token->string);
- if (parsedline->ident_user[0] == '/')
+ /*
+ * Now that the field validation is done, compile a regex from the user
+ * token, if necessary.
+ */
+ rc = regcomp_auth_token(parsedline->token);
+ if (rc)
{
- /*
- * When system username starts with a slash, treat it as a regular
- * expression. Pre-compile it.
- */
- int r;
- pg_wchar *wstr;
- int wlen;
-
- wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar));
- wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1,
- wstr, strlen(parsedline->ident_user + 1));
-
- r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
- if (r)
- {
- char errstr[100];
+ char errstr[100];
- pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
- ereport(elevel,
- (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
- errmsg("invalid regular expression \"%s\": %s",
- parsedline->ident_user + 1, errstr),
- errcontext("line %d of configuration file \"%s\"",
+ pg_regerror(rc, parsedline->token->regex, errstr, sizeof(errstr));
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+ errmsg("invalid regular expression \"%s\": %s",
+ parsedline->token->string + 1, errstr),
+ errcontext("line %d of configuration file \"%s\"",
line_num, IdentFileName)));
- *err_msg = psprintf("invalid regular expression \"%s\": %s",
- parsedline->ident_user + 1, errstr);
+ *err_msg = psprintf("invalid regular expression \"%s\": %s",
+ parsedline->token->string + 1, errstr);
- pfree(wstr);
- return NULL;
- }
- pfree(wstr);
+ return NULL;
}
return parsedline;
return;
/* Match? */
- if (identLine->ident_user[0] == '/')
+ if (token_has_regexp(identLine->token))
{
/*
- * When system username starts with a slash, treat it as a regular
- * expression. In this case, we process the system username as a
- * regular expression that returns exactly one match. This is replaced
- * for \1 in the database username string, if present.
+ * Process the system username as a regular expression that returns
+ * exactly one match. This is replaced for \1 in the database username
+ * string, if present.
*/
int r;
regmatch_t matches[2];
- pg_wchar *wstr;
- int wlen;
char *ofs;
char *regexp_pgrole;
- wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
- wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
-
- r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0);
+ r = regexec_auth_token(ident_user, identLine->token, 2, matches);
if (r)
{
char errstr[100];
if (r != REG_NOMATCH)
{
/* REG_NOMATCH is not an error, everything else is */
- pg_regerror(r, &identLine->re, errstr, sizeof(errstr));
+ pg_regerror(r, identLine->token->regex, errstr, sizeof(errstr));
ereport(LOG,
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
errmsg("regular expression match for \"%s\" failed: %s",
- identLine->ident_user + 1, errstr)));
+ identLine->token->string + 1, errstr)));
*error_p = true;
}
-
- pfree(wstr);
return;
}
- pfree(wstr);
if ((ofs = strstr(identLine->pg_role, "\\1")) != NULL)
{
ereport(LOG,
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
- identLine->ident_user + 1, identLine->pg_role)));
+ identLine->token->string + 1, identLine->pg_role)));
*error_p = true;
return;
}
if (case_insensitive)
{
if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 &&
- pg_strcasecmp(identLine->ident_user, ident_user) == 0)
+ pg_strcasecmp(identLine->token->string, ident_user) == 0)
*found_p = true;
}
else
{
if (strcmp(identLine->pg_role, pg_role) == 0 &&
- strcmp(identLine->ident_user, ident_user) == 0)
+ strcmp(identLine->token->string, ident_user) == 0)
*found_p = true;
}
}
foreach(parsed_line_cell, new_parsed_lines)
{
newline = (IdentLine *) lfirst(parsed_line_cell);
- if (newline->ident_user[0] == '/')
- pg_regfree(&newline->re);
+ free_auth_token(newline->token);
}
MemoryContextDelete(ident_context);
return false;
foreach(parsed_line_cell, parsed_ident_lines)
{
newline = (IdentLine *) lfirst(parsed_line_cell);
- if (newline->ident_user[0] == '/')
- pg_regfree(&newline->re);
+ free_auth_token(newline->token);
}
}
if (parsed_ident_context != NULL)