apparmor/parser/parser_variable.c

510 lines
14 KiB
C
Raw Normal View History

/*
2007-04-11 08:12:51 +00:00
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*/
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <linux/limits.h>
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
#include <string>
/* #define DEBUG */
#include "parser.h"
#include "profile.h"
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
#include "mount.h"
#include "dbus.h"
static inline const char *get_var_end(const char *var)
{
const char *eptr = var;
while (*eptr) {
if (*eptr == '}')
return eptr;
/* first character must be alpha */
if (eptr == var) {
if (!isalpha(*eptr))
return NULL; /* invalid char */
} else {
if (!(*eptr == '_' || isalnum(*eptr)))
return NULL; /* invalid char */
}
eptr++;
}
return NULL; /* no terminating '}' */
}
static struct var_string *split_string(const char *string, const char *var_begin,
const char *var_end)
{
struct var_string *n = (struct var_string *) calloc(1, sizeof(struct var_string));
unsigned int offset = strlen("@{");
if (!n) {
PERROR("Memory allocation error\n");
return NULL;
}
if (var_begin != string) {
n->prefix = strndup(string, var_begin - string);
}
n->var = strndup(var_begin + offset, var_end - (var_begin + offset));
if (strlen(var_end + 1) != 0) {
n->suffix = strdup(var_end + 1);
}
return n;
}
struct var_string *split_out_var(const char *string)
{
struct var_string *n = NULL;
const char *sptr;
BOOL bEscape = 0; /* flag to indicate escape */
if (!string) /* shouldn't happen */
return NULL;
sptr = string;
while (!n && *sptr) {
switch (*sptr) {
case '\\':
if (bEscape) {
bEscape = FALSE;
} else {
bEscape = TRUE;
}
break;
case '@':
if (bEscape) {
bEscape = FALSE;
} else if (*(sptr + 1) == '{') {
const char *eptr = get_var_end(sptr + 2);
if (!eptr)
break; /* no variable end found */
if (eptr == sptr + 2) {
/* XXX - better diagnostics here, please */
PERROR("Empty variable name found!\n");
exit(1);
}
n = split_string(string, sptr, eptr);
}
break;
default:
if (bEscape)
bEscape = FALSE;
}
sptr++;
}
return n;
}
void free_var_string(struct var_string *var)
{
if (!var)
return;
if (var->prefix)
free(var->prefix);
if (var->var)
free(var->var);
if (var->suffix)
free(var->suffix);
free(var);
}
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
static void trim_trailing_slash(std::string& str)
{
std::size_t found = str.find_last_not_of('/');
if (found != std::string::npos)
str.erase(found + 1);
else
str.clear(); // str is all '/'
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
}
static void write_replacement(const char separator, const char* value,
std::string& replacement, bool filter_leading_slash,
bool filter_trailing_slash)
{
const char *p = value;
replacement.append(1, separator);
if (filter_leading_slash)
while (*p == '/')
p++;
replacement.append(p);
if (filter_trailing_slash)
trim_trailing_slash(replacement);
}
static int expand_by_alternations(struct set_value **valuelist,
struct var_string *split_var,
char **name)
{
char *value, *first_value;
std::string replacement;
bool filter_leading_slash = false;
bool filter_trailing_slash = false;
first_value = get_next_set_value(valuelist);
if (!first_value) {
PERROR("ASSERT: set variable (%s) should always have at least one value assigned to it\n",
split_var->var);
exit(1);
}
free(*name);
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
value = get_next_set_value(valuelist);
if (!value) {
/* only one entry for the variable, so just sub it in */
if (asprintf(name, "%s%s%s",
split_var->prefix ? split_var->prefix : "",
first_value,
split_var->suffix ? split_var->suffix : "") == -1)
return -1;
return 0;
}
if (split_var->prefix && split_var->prefix[strlen(split_var->prefix) - 1] == '/')
filter_leading_slash = true;
if (split_var->suffix && *split_var->suffix == '/')
filter_trailing_slash = true;
write_replacement('{', first_value, replacement, filter_leading_slash, filter_trailing_slash);
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
while ((value = get_next_set_value(valuelist))) {
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
}
if (asprintf(name, "%s%s}%s",
split_var->prefix ? split_var->prefix : "",
replacement.c_str(),
split_var->suffix ? split_var->suffix : "") == -1) {
return -1;
}
return 0;
}
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
/* doesn't handle variables in options atm */
int expand_entry_variables(char **name)
{
struct set_value *valuelist;
struct var_string *split_var;
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
int ret;
assert(name);
if (!*name) /* can happen when entry is optional */
return 0;
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
while ((split_var = split_out_var(*name))) {
valuelist = get_set_var(split_var->var);
if (!valuelist) {
int boolean = get_boolean_var(split_var->var);
if (boolean == -1)
PERROR("Found reference to variable %s, but is never declared\n",
split_var->var);
else
PERROR("Found reference to set variable %s, but declared boolean\n",
split_var->var);
exit(1);
}
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
ret = expand_by_alternations(&valuelist, split_var, name);
free_var_string(split_var);
parser: convert var expansion to use alternations This patch converts the parser's variable expansion from adding new entries for each additional variable value to incorporating an alternation that includes all the values for the variable; e.g. given: @{BINS}=/bin /usr/bin /sbin /usr/sbin @{BINS}/binary ix, rather than expanding to exntries for /bin/binary /usr/bin/binary /sbin/binary /usr/sbin/binary one entry would remain that looks like: {/bin,/usr/bin,/sbin,/usr/sbin}/binary One complication with this patch is that we try to prevent mistakes for our users with variable expansion around '/'s; it's common for people to write profiles that contain things like: @{BAR}=/bingo/*/ /bango/ /foo/@{BAR}/baz We already have a post-processing step that walks entries looking for multiple sequences of '/'s and filters them into single '/' which worked when creating new entries for each variable expansion. Converting to alternation expansion breaks this filtering, so code is added that removes leading and trailing slashes in variable values in the expansion if the character immediately preceding or following the variable is also a slash. The intent behind this is to reduce the amount of memory allocations and structure walking that needed to occur in when converting from the entry strings to the back end nodes. Examples with real world profiles showed performance improvements ranging from 2.5% to 10%. However, because the back end operations are sensitive to the front end inputs, it is possible for worse results to occur; for example, it takes the simple_tests/vars/vars_stress_0[123].sd tests significantly longer to complete after this patch is applied (vars_stress_03.sd in particular takes ~23 times longer). An initial analysis of profiling output in this negative case looks like it causes the tree simplification in the back end to do more work for unknown reasons. On the other hand, the test simple_tests/vars/vars_dbus_9.sd (introduced in "[patch 09/12] parser: more dbus variable testcases") takes ~1 sec to complete on my laptop before this patch, and roughly 0.01s with this patch applied. (One option would be to keep the "expand entries" approach as an alternative, but I couldn't come up with a good heuristic for when to use it instead.) Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-16 01:28:38 -08:00
if (ret != 0)
return -1;
}
return 0;
}
static int process_variables_in_entries(struct cod_entry *entry_list)
{
int error = 0;
struct cod_entry *entry;
2007-02-27 02:29:16 +00:00
list_for_each(entry_list, entry) {
error = expand_entry_variables(&entry->name);
if (error)
return error;
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
}
return 0;
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
}
static int process_variables_in_rules(Profile &prof)
Add mount rules Add the ability to control mounting and unmounting The basic form of the rules are. [audit] [deny] mount [conds]* [device] [ -> [conds] path], [audit] [deny] remount [conds]* [path], [audit] [deny] umount [conds]* [path], [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile> remount is just a short cut for mount options=remount where [conds] can be fstype=<expr> options=<expr> conds follow the extended conditional syntax of allowing either: * a single value after the equals, which has the same character range as regular IDS (ie most anything but it can't be terminated with a , (comma) and if spaces or other characters are needed it can be quoted eg. options=foo options = foo options="foo bar" * a list of values after the equals, the list of values is enclosed within parenthesis () and its has a slightly reduced character set but again elements can be quoted. the separation between elements is whitespace and commas. eg. options=(foo bar) options=(foo, bar) options=(foo , bar) options=(foo,bar) The rules are flexible and follow a similar pattern as network, capability, etc. mount, # allow all mounts, but not umount or pivotroot mount fstype=procfs, # allow mounting procfs anywhere mount options=(bind, ro) /foo -> /bar, # readonly bind mount mount /dev/sda -> /mnt, mount /dev/sd** -> /mnt/**, mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/ umount, umount /m*, Currently variables and regexs are are supported on the device and mount point. ie. mount <devince> -> <mount point>, Regexes are supported in fstype and options. The options have a further caveat that regexs only work if the option is fs specific option. eg. options=(upperdir=/tmp/*,lowerdir=/) regex's will not currently work against the standard options like ro, rw nosuid Conditionals (fstype) can only be applied to the device (source) at this time and will be disregarded in situations where the mount is manipulating an existing mount (bind, remount). Options can be specified multiple times mount option=rw option=(nosuid,upperdir=/foo), and will be combined together into a single set of values The ordering of the standard mount options (rw,ro, ...) does not matter but the ordering of fs specific options does. Specifying that the value of a particular option does not matter can be acheived by providing both the positive and negative forms of and option option=(rw,ro) options=(suid,nosuid) For the fs specific options specifying that a particular value does not matter is achieve using a regex with alternations. Improvements to the syntax and order restrictions are planned for the future. Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-02-24 04:19:38 -08:00
{
for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) {
int error = (*i)->expand_variables();
if (error)
return error;
}
return 0;
}
int process_profile_variables(Profile *prof)
{
int error = 0, rc;
error = new_set_var(PROFILE_NAME_VARIABLE, prof->get_name(true).c_str());
if (!error)
error = process_variables_in_entries(prof->entries);
if (!error)
error = process_variables_in_rules(*prof);
rc = delete_set_var(PROFILE_NAME_VARIABLE);
if (!error)
error = rc;
return error;
}
#ifdef UNIT_TEST
#include "unit_test.h"
int test_get_var_end(void)
{
int rc = 0;
const char *retchar;
const char *testchar;
testchar = "TRUE}";
retchar = get_var_end(testchar);
MY_TEST(retchar - testchar == strlen("TRUE"), "get var end for TRUE}");
testchar = "some_var}some other text";
retchar = get_var_end(testchar);
MY_TEST(retchar - testchar == strlen("some_var"), "get var end for some_var}");
testchar = "some_var}some other} text";
retchar = get_var_end(testchar);
MY_TEST(retchar - testchar == strlen("some_var"), "get var end for some_var} 2");
testchar = "FALSE";
retchar = get_var_end(testchar);
MY_TEST(retchar == NULL, "get var end for FALSE");
testchar = "pah,pah}pah ";
retchar = get_var_end(testchar);
MY_TEST(retchar == NULL, "get var end for pah,pah}");
return rc;
}
int test_split_string(void)
{
int rc = 0;
char *tst_string, *var_start, *var_end;
struct var_string *ret_struct;
const char *prefix = "abcdefg";
const char *var = "boogie";
const char *suffix = "suffixication";
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
var_start = tst_string + strlen(prefix);
var_end = var_start + strlen(var) + strlen("@\{");
ret_struct = split_string(tst_string, var_start, var_end);
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split string 1 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 1 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split string 1 suffix");
free_var_string(ret_struct);
asprintf(&tst_string, "@{%s}%s", var, suffix);
var_start = tst_string;
var_end = var_start + strlen(var) + strlen("@\{");
ret_struct = split_string(tst_string, var_start, var_end);
MY_TEST(ret_struct->prefix == NULL, "split string 2 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 2 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split string 2 suffix");
free_var_string(ret_struct);
asprintf(&tst_string, "%s@{%s}", prefix, var);
var_start = tst_string + strlen(prefix);
var_end = var_start + strlen(var) + strlen("@\{");
ret_struct = split_string(tst_string, var_start, var_end);
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split string 3 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 3 var");
MY_TEST(ret_struct->suffix == NULL, "split string 3 suffix");
free_var_string(ret_struct);
asprintf(&tst_string, "@{%s}", var);
var_start = tst_string;
var_end = var_start + strlen(var) + strlen("@\{");
ret_struct = split_string(tst_string, var_start, var_end);
MY_TEST(ret_struct->prefix == NULL, "split string 4 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 4 var");
MY_TEST(ret_struct->suffix == NULL, "split string 4 suffix");
free_var_string(ret_struct);
return rc;
}
int test_split_out_var(void)
{
int rc = 0;
char *tst_string, *tmp;
struct var_string *ret_struct;
const char *prefix = "abcdefg";
const char *var = "boogie";
const char *var2 = "V4rW1thNum5";
const char *var3 = "boogie_board";
const char *suffix = "suffixication";
/* simple case */
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 1 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 1 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 1 suffix");
free_var_string(ret_struct);
/* no prefix */
asprintf(&tst_string, "@{%s}%s", var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct->prefix == NULL, "split out var 2 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 2 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 2 suffix");
free_var_string(ret_struct);
/* no suffix */
asprintf(&tst_string, "%s@{%s}", prefix, var);
ret_struct = split_out_var(tst_string);
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 3 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 3 var");
MY_TEST(ret_struct->suffix == NULL, "split out var 3 suffix");
free_var_string(ret_struct);
/* var only */
asprintf(&tst_string, "@{%s}", var);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct->prefix == NULL, "split out var 4 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 4 var");
MY_TEST(ret_struct->suffix == NULL, "split out var 4 suffix");
free_var_string(ret_struct);
/* quoted var, shouldn't split */
asprintf(&tst_string, "%s\\@{%s}%s", prefix, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - quoted @");
free_var_string(ret_struct);
/* quoted \, split should succeed */
asprintf(&tst_string, "%s\\\\@{%s}%s", prefix, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(strcmp(ret_struct->prefix, strndup(tst_string, strlen(prefix) + 2)) == 0, "split out var 5 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 5 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 5 suffix");
free_var_string(ret_struct);
/* un terminated var, should fail */
asprintf(&tst_string, "%s@{%s%s", prefix, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - un-terminated var");
free_var_string(ret_struct);
/* invalid char in var, should fail */
asprintf(&tst_string, "%s@{%s^%s}%s", prefix, var, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - invalid char in var");
free_var_string(ret_struct);
/* two vars, should only strip out first */
asprintf(&tmp, "@{%s}%s}", suffix, suffix);
asprintf(&tst_string, "%s@{%s}%s", prefix, var, tmp);
ret_struct = split_out_var(tst_string);
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 6 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 6 var");
MY_TEST(strcmp(ret_struct->suffix, tmp) == 0, "split out var 6 suffix");
free_var_string(ret_struct);
/* quoted @ followed by var, split should succeed */
asprintf(&tst_string, "%s\\@@{%s}%s", prefix, var, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(strcmp(ret_struct->prefix, strndup(tst_string, strlen(prefix) + 2)) == 0, "split out var 7 prefix");
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 7 var");
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 7 suffix");
free_var_string(ret_struct);
/* numeric char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out numeric var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var2) == 0, "split numeric var var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out numeric var suffix");
free_var_string(ret_struct);
/* numeric first char in var, should fail */
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - numeric first char in var");
free_var_string(ret_struct);
/* underscore char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out underscore var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var3) == 0, "split out underscore var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out underscore var suffix");
free_var_string(ret_struct);
/* underscore first char in var, should fail */
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - underscore first char in var");
free_var_string(ret_struct);
return rc;
}
int main(void)
{
int rc = 0;
int retval;
retval = test_get_var_end();
if (retval != 0)
rc = retval;
retval = test_split_string();
if (retval != 0)
rc = retval;
retval = test_split_out_var();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */