Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ish/envvar #321

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/conf-yaml-loader.c
Expand Up @@ -24,6 +24,7 @@
*/

#include <yaml.h>
#include <pcre.h>
#include "suricata-common.h"
#include "conf.h"
#include "util-debug.h"
Expand Down Expand Up @@ -118,7 +119,8 @@ ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq)
if (seq_node->name == NULL)
return -1;
snprintf(seq_node->name, DEFAULT_NAME_LEN, "%d", seq_idx++);
seq_node->val = SCStrdup(value);
if (NULL == (seq_node->val = ConfExpandEnvVar(value)))
seq_node->val = SCStrdup(value);
TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
}
else {
Expand Down Expand Up @@ -161,7 +163,8 @@ ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq)
if (node->allow_override) {
if (node->val != NULL)
SCFree(node->val);
node->val = SCStrdup(value);
if (NULL == (node->val = ConfExpandEnvVar(value)))
node->val = SCStrdup(value);
}
state = CONF_KEY;
}
Expand Down
288 changes: 288 additions & 0 deletions src/conf.c
Expand Up @@ -773,8 +773,141 @@ char *ConfLoadCompleteIncludePath(char *file)
return path;
}

/**
* \brief Get the compiled pcre for environment variable expansion.
*
* This compiles the regular expression used in variable expansion and
* caches it for re-user.
*
* \retval The compiled pcre
*/
static pcre *
ConfGetEnvVarPcre(void)
{
static pcre *envvar_pcre = NULL;
const char *error;
int erroroffset;

static const char pattern[] = "\\$\\{"
"(" "[^\\$\\{\\}:-]*" ")"
"(:-(" "[^\\$\\{\\}:-]*" "))?"
"\\}";

if (envvar_pcre == NULL) {
envvar_pcre = pcre_compile(pattern, 0, &error, &erroroffset, NULL);
if (envvar_pcre == NULL) {
fprintf(stderr, "ERROR: Failed to compile pcre: %s", error);
exit(1);
}
}

return envvar_pcre;
}

/**
* \brief Perform environment variable expansion on the provided string.
*
* \param string The string to perform environment variable expansion.
*
* \retval A new string with environment variables expanded, only if expansion
* took place. Otherwise NULL is returned. As this is a new string it
* must be free'd by the caller.
*/
char *
ConfExpandEnvVar(char *input)
{
char *string = NULL;
const char *var_name;
const char *var_val = NULL;
int var_val_len = 0;
int segment_start;
int segment_end;
const char *default_val = NULL;
char *pre_string = NULL;
char *post_string = NULL;
char *new_str = NULL;
int new_str_len;
int ovector[12];

/* Make a copy of the input string as we're destructive. */
string = strdup(input);
if (unlikely(string == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC,
"Error allocating memory for variable expansion");
exit(EXIT_FAILURE);
}

int match = pcre_exec(ConfGetEnvVarPcre(), NULL, string, strlen(string), 0,
0, ovector, 12);
if (match < 2) {
goto end;
}
segment_start = ovector[0];
segment_end = ovector[1];

if (pcre_get_substring(string, ovector, match, 1, &var_name) < 0) {
fprintf(stderr, "pcre failure\n");
exit(1);
}

/* Do we also have a default? */
if (match == 4) {
if (pcre_get_substring(string, ovector, match, 3, &default_val) < 0) {
fprintf(stderr, "pcre failure\n");
exit(1);
}
}

/* Get the environment variable, using the optional default if the
* environment variable is not set. */
if (NULL != (var_val = getenv(var_name))) {
var_val_len = strlen(var_val);
}
else if (default_val != NULL) {
var_val = default_val;
var_val_len = strlen(var_val);
}

/* Calculate the length of the new string including termination
* and then allocate it. */

new_str_len = strlen(string) - (segment_end - segment_start) +
var_val_len + 1;
BUG_ON(new_str_len < 1);
new_str = SCCalloc(1, new_str_len);
if (unlikely(new_str == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC,
"Error allocating memory for variable expansion");
exit(EXIT_FAILURE);
}

/* Build the new string. */
if ((size_t)segment_end < strlen(string)) {
post_string = string + segment_end;
}
if (segment_start) {
pre_string = string;
pre_string[segment_start] = '\0';
}
if (pre_string)
strlcat(new_str, pre_string, new_str_len);
if (var_val != NULL)
strlcat(new_str, var_val, new_str_len);
if (post_string != NULL)
strlcat(new_str, post_string, new_str_len);

/* Recurse to expand other variables. */
char *new_new_str = ConfExpandEnvVar(new_str);
if (new_new_str != NULL) {
SCFree(new_str);
new_str = new_new_str;
}

end:
SCFree(string);

return new_str;
}

#ifdef UNITTESTS

Expand Down Expand Up @@ -1194,6 +1327,158 @@ ConfSetTest(void)
return 1;
}

static int
ConfEnvVarExpandTest(void)
{
char *new;
const char *old_foo = getenv("FOO");
const char *old_bar = getenv("BAR");

setenv("FOO", "bar", 1);
setenv("BAR", "foo", 1);

if (ConfExpandEnvVar("something") != NULL)
return 0;
if (ConfExpandEnvVar("$something") != NULL)
return 0;
if (ConfExpandEnvVar("${something") != NULL)
return 0;

new = ConfExpandEnvVar("${}");
if (new == NULL || strcmp(new, "") != 0) {
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${FOO}");
if (new == NULL || strcmp(new, "bar") != 0) {
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("pre${FOO}");
if (new == NULL || strcmp(new, "prebar") != 0) {
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${FOO}post");
if (new == NULL || strcmp(new, "barpost") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "barpost", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("pre${FOO}post");
if (new == NULL || strcmp(new, "prebarpost") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "prebarpost", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("pre${NOFOO}post");
if (new == NULL || strcmp(new, "prepost") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "prepost", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${FOO}${BAR}");
if (new == NULL || strcmp(new, "barfoo") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "barfoo", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${FOO}${BAR}${FOOBAR}");
if (new == NULL || strcmp(new, "barfoo") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "barfoo", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${USER}");
if (new == NULL || strcmp(new, getenv("USER")) != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", getenv("USER"), new);
return 0;
}
SCFree(new);

unsetenv("FOO");
unsetenv("BAR");

if (old_foo != NULL)
setenv("FOO", old_foo, 1);
if (old_bar != NULL)
setenv("BAR", old_bar, 1);

return 1;
}

static int
ConfEnvVarExpandTestWithDefaultValue(void)
{
char *new;

new = ConfExpandEnvVar("${FOO:-foo}");
if (new == NULL || strcmp(new, "foo") != 0) {
fprintf(stderr, "%d: expected '%s', got '%s'\n", __LINE__, "foo", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("pre${FOO:-foo}post");
if (new == NULL || strcmp(new, "prefoopost") != 0) {
fprintf(stderr, "%d: expected '%s', got '%s'\n", __LINE__, "foo", new);
return 0;
}
SCFree(new);

const char *old_foo = getenv("FOO");
const char *old_bar = getenv("BAR");

setenv("FOO", "bar", 1);
setenv("BAR", "foo", 1);

new = ConfExpandEnvVar("${FOO:-foo}");
if (new == NULL || strcmp(new, "bar") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "foo", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${FOO:-${BAR}}");
if (new == NULL || strcmp(new, "bar") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "foo", new);
return 0;
}
SCFree(new);

new = ConfExpandEnvVar("${NOFOO:-${BAR}}");
if (new == NULL || strcmp(new, "foo") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "foo", new);
return 0;
}
SCFree(new);

/* A bit silly now... */
new = ConfExpandEnvVar("${NOFOO:-${NOBAR:-nofoobar}}");
if (new == NULL || strcmp(new, "nofoobar") != 0) {
fprintf(stderr, "expected '%s', got '%s'\n", "foo", new);
return 0;
}
SCFree(new);

unsetenv("FOO");
unsetenv("BAR");

if (old_foo != NULL)
setenv("FOO", old_foo, 1);
if (old_bar != NULL)
setenv("BAR", old_bar, 1);

return 1;
}

void
ConfRegisterTests(void)
Expand All @@ -1211,6 +1496,9 @@ ConfRegisterTests(void)
UtRegisterTest("ConfGetChildValueWithDefaultTest", ConfGetChildValueWithDefaultTest, 1);
UtRegisterTest("ConfGetChildValueIntWithDefaultTest", ConfGetChildValueIntWithDefaultTest, 1);
UtRegisterTest("ConfGetChildValueBoolWithDefaultTest", ConfGetChildValueBoolWithDefaultTest, 1);
UtRegisterTest("ConfEnvVarExpandTest", ConfEnvVarExpandTest, 1);
UtRegisterTest("ConfEnvVarExpandTestWithDefaultValue",
ConfEnvVarExpandTestWithDefaultValue, 1);
}

#endif /* UNITTESTS */
2 changes: 2 additions & 0 deletions src/conf.h
Expand Up @@ -85,4 +85,6 @@ int ConfGetChildValueIntWithDefault(ConfNode *base, ConfNode *dflt, char *name,
int ConfGetChildValueBoolWithDefault(ConfNode *base, ConfNode *dflt, char *name, int *val);
char *ConfLoadCompleteIncludePath(char *);

char *ConfExpandEnvVar(char *string);

#endif /* ! __CONF_H__ */
2 changes: 1 addition & 1 deletion suricata.yaml.in
Expand Up @@ -825,7 +825,7 @@ ipfw:

# Set the default rule path here to search for the files.
# if not set, it will look at the current working dir
default-rule-path: @e_sysconfdir@rules
default-rule-path: ${SURICATA_RULE_PATH:-@e_sysconfdir@rules}
rule-files:
- botcc.rules
- ciarmy.rules
Expand Down