2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 10:07:12 +00:00
apparmor/parser/parser_variable.c

532 lines
16 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"
#include "symtab.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
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)
{
return variable::expand_by_alternation(name);
}
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;
if (entry->link_name) {
error = expand_entry_variables(&entry->link_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++) {
if ((*i)->skip())
continue;
int error = (*i)->expand_variables();
if (error)
return error;
}
return 0;
}
static int process_variable_in_attach_disconnected(char **disconnected)
{
int error = expand_entry_variables(disconnected);
if (error)
return error;
filter_slashes(*disconnected);
// TODO: semantic check should go somewhere else
if ((*disconnected)[0] != '/')
yyerror(_("attach_disconnected path must begin with a /"));
int n = strlen(*disconnected);
// removing trailing / */
while (n && (*disconnected)[n-1] == '/')
(*disconnected)[--n] = 0;
return error;
}
static int process_variables_in_name(Profile &prof)
{
/* this needs to be done before alias expansion, ie. altnames are
* setup
*/
int error = expand_entry_variables(&prof.name);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
if (!error) {
if (prof.attachment)
error = expand_entry_variables(&prof.attachment);
else if (prof.name[0] == '/') {
/* had to wait to do this until after processing the
* variables in the profile name
*/
prof.attachment = strdup(local_name(prof.name));
if (!prof.attachment) {
errno = ENOMEM;
return -1;
}
filter_slashes(prof.attachment);
}
}
if (!error && prof.flags.disconnected_path)
error = process_variable_in_attach_disconnected(&prof.flags.disconnected_path);
if (!error && prof.flags.disconnected_ipc)
error = process_variable_in_attach_disconnected(&prof.flags.disconnected_ipc);
return error;
}
static std::string escape_re(std::string str)
{
for (size_t i = 0; i < str.length(); i++) {
if (str[i] == '\\') {
/* skip \ and follow char. Skipping \ and first
* char is enough for multichar escape sequence
*/
i++;
continue;
}
if (strchr("{}[]*?", str[i]) != NULL) {
str.insert(i++, "\\");
}
}
return str;
}
int process_profile_variables(Profile *prof)
{
int error = 0;
variable *saved_exec_path = NULL;
variable *saved_attach_path = NULL;
variable *tmp = NULL;
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
* have the correct name
*/
error = process_variables_in_name(*prof);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
if (error)
goto out;
/* escape profile name elements that could be interpreted as
* regular expressions.
*/
error = symtab::add_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
if (error)
goto out;
if (prof->attachment) {
/* IF we didn't want a path based profile name to generate
* an attachment. The code could be moved here. Add the
* output fed into the vars directly instead of setting
* the attachment.
*/
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
/* need to take into account alias, but not yet */
saved_attach_path = symtab::delete_var(PROFILE_ATTACH_VAR);
error = symtab::add_var(PROFILE_ATTACH_VAR, (const char*) prof->attachment);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
if (error)
goto cleanup_name;
/* update to use kernel vars if available */
saved_exec_path = symtab::delete_var(PROFILE_EXEC_VAR);
error = symtab::add_var(PROFILE_EXEC_VAR, (const char*) prof->attachment);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
if (error)
goto cleanup_attach;
}
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
error = process_variables_in_entries(prof->entries);
if (error)
goto cleanup;
error = process_variables_in_rules(*prof);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
cleanup:
/* ideally these variables would be local scoped and we would not
* have to clean them up here, but unfortunately variables
* don't support that yet.
*/
if (prof->attachment) {
tmp = symtab::delete_var(PROFILE_EXEC_VAR);
delete tmp;
if (saved_exec_path)
symtab::add_var(*saved_exec_path);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
}
cleanup_attach:
if (prof->attachment) {
tmp = symtab::delete_var(PROFILE_ATTACH_VAR);
delete tmp;
if (saved_attach_path)
symtab::add_var(*saved_attach_path);
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
}
cleanup_name:
tmp = symtab::delete_var(PROFILE_NAME_VARIABLE);
delete tmp;
delete saved_exec_path;
delete saved_attach_path;
parser: Add support for automatic @{attach_path} variable Have the parser extract the attachment path from the profile declaration and make it available as a variable within the profile. This allows profile rules to use the executable attachment path in rules. eg. ``` profile ex /bin/** { @{attach_path} r, # ... } profile /path/to/bin { @{attach_path} r, # ... } ``` if a profile does not define an attachment like ``` profile noattach { @{attach_path} r, } ``` the apparmor_parser will fail the compile with the error. ``` Found reference to variable attach_path, but is never declared ``` The attachment xattr/label conditionals are not made available at this time as regular file path rules can not use them. Similarly a @{exec_path} variable is made available. It is different than @{attach_path} in that it is intended to be a kernel variable that represents the specific executable that was matched at run time. However to support policy on kernels that don't define the kernel variable it has a fallback value that is the same as @{attach_path}. This patch is a follow on to MR:1637 (https://gitlab.com/apparmor/apparmor/-/merge_requests/1637) and is similar to how the apparmor.d project uses the manually setup @{exec_path} variable. We can bike shed over the variable name. @{attach_path} was chosen here because this is the attachment conditional path for the executable, not the executable's actual path. While @{exec_path} is intended to be the applications actual executable path. support the @{exec_path} kernel variable (all of them atm). Notes: The minimize.sh tests are changed because this patch causes path based profile names to create an attachment. This could be done by doing the attach_variable expansion in the alternate location marked by the patch, but since the kernel is going to start doing this for all profiles that don't have an attachment it is better for the parser to do it, as it can optimize better. This patch may cause breakage if policy declares either @{attach_path} or @{exec_path} this will not be dealt with here, but in a subsequent patch that allows variables to have a local scope so that the compiler defined vars will just get declared locally. Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-04-20 16:34:51 -07:00
out:
return error;
}
#ifdef UNIT_TEST
#include "unit_test.h"
int test_split_string(void)
{
int rc = 0;
char *tst_string;
const char *prefix = "abcdefg";
const char *var = "boogie";
const char *suffix = "suffixication";
std::tuple<std::string, std::string, std::string> result;
std::string result_prefix;
std::string result_var;
std::string result_suffix;
char *pvar;
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split string 1 prefix");
MY_TEST(strcmp(pvar, var) == 0, "split string 1 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split string 1 suffix");
free(pvar);
free(tst_string);
asprintf(&tst_string, "@{%s}%s", var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(result_prefix.empty(), "split string 2 prefix");
MY_TEST(strcmp(pvar, var) == 0, "split string 2 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split string 2 suffix");
free(pvar);
free(tst_string);
asprintf(&tst_string, "%s@{%s}", prefix, var);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split string 3 prefix");
MY_TEST(strcmp(pvar, var) == 0, "split string 3 var");
MY_TEST(result_suffix.empty(), "split string 3 suffix");
free(pvar);
free(tst_string);
asprintf(&tst_string, "@{%s}", var);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(result_prefix.empty(), "split string 4 prefix");
MY_TEST(strcmp(pvar, var) == 0, "split string 4 var");
MY_TEST(result_suffix.empty(), "split string 4 suffix");
free(pvar);
free(tst_string);
asprintf(&tst_string, "%s%s%s", prefix, var, suffix);;
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
MY_TEST(result_prefix.empty(), "split string 5 prefix");
MY_TEST(result_var.empty(), "split string 5 var");
MY_TEST(result_suffix.empty(), "split string 5 suffix");
free(tst_string);
return rc;
}
int test_split_out_var(void)
{
int rc = 0;
char *tst_string, *tmp;
const char *prefix = "abcdefg";
const char *var = "boogie";
const char *var2 = "V4rW1thNum5";
const char *var3 = "boogie_board";
const char *suffix = "suffixication";
std::tuple<std::string, std::string, std::string> result;
std::string result_prefix;
std::string result_var;
std::string result_suffix;
char *pvar = NULL;
/* simple case */
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable 1 pvar");
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 1 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 1 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 1 suffix");
if (pvar)
free(pvar);
free(tst_string);
/* no prefix */
asprintf(&tst_string, "@{%s}%s", var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable 2 pvar");
MY_TEST(result_prefix.empty(), "extract_variable 2 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 2 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 2 suffix");
if (pvar)
free(pvar);
free(tst_string);
/* no suffix */
asprintf(&tst_string, "%s@{%s}", prefix, var);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable 3 pvar");
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 3 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 3 var");
MY_TEST(result_suffix.empty(), "extract_variable 3 suffix");
if (pvar)
free(pvar);
free(tst_string);
/* var only */
asprintf(&tst_string, "@{%s}", var);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable 4 pvar");
MY_TEST(result_prefix.empty(), "extract_variable 4 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 4 var");
MY_TEST(result_suffix.empty(), "extract_variable 4 suffix");
if (pvar)
free(pvar);
free(tst_string);
/* quoted var, shouldn't split */
asprintf(&tst_string, "%s\\@{%s}%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
MY_TEST(result_prefix.empty(), "extract_variable - quoted @ prefix");
MY_TEST(result_var.empty(), "extract_variable - quoted @ var");
MY_TEST(result_suffix.empty(), "extract_variable - quoted @ suffix");
free(tst_string);
/* quoted \, split should succeed */
asprintf(&tst_string, "%s\\\\@{%s}%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
tmp = strndup(tst_string, strlen(prefix) + 2);
MY_TEST(pvar != NULL, "extract_variable 5 pvar");
MY_TEST(strcmp(result_prefix.c_str(), tmp) == 0, "extract_variable 5 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 5 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 5 suffix");
if (pvar)
free(pvar);
free(tst_string);
free(tmp);
/* un terminated var, should fail */
asprintf(&tst_string, "%s@{%s%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
MY_TEST(result_prefix.empty(), "extract_variable - un-terminated var prefix");
MY_TEST(result_var.empty(), "extract_variable - un-terminated var var");
MY_TEST(result_suffix.empty(), "extract_variable - un-terminated var suffix");
free(tst_string);
/* invalid char in var, should fail */
asprintf(&tst_string, "%s@{%s^%s}%s", prefix, var, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar == NULL, "process_var - invalid char in var");
if (pvar)
free(pvar);
free(tst_string);
/* two vars, should only strip out first */
asprintf(&tmp, "@{%s}%s}", suffix, suffix);
asprintf(&tst_string, "%s@{%s}%s", prefix, var, tmp);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable 6 pvar");
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 6 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 6 var");
MY_TEST(strcmp(result_suffix.c_str(), tmp) == 0, "extract_variable 6 suffix");
if (pvar)
free(pvar);
free(tst_string);
free(tmp);
/* quoted @ followed by var, split should succeed */
asprintf(&tst_string, "%s\\@@{%s}%s", prefix, var, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
tmp = strndup(tst_string, strlen(prefix) + 2);
MY_TEST(pvar != NULL, "extract_variable 7 pvar");
MY_TEST(strcmp(result_prefix.c_str(), tmp) == 0, "extract_variable 7 prefix");
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 7 var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 7 suffix");
if (pvar)
free(pvar);
free(tst_string);
free(tmp);
/* numeric char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable numeric var pvar");
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split out numeric var prefix");
MY_TEST(strcmp(pvar, var2) == 0, "split numeric var var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split out numeric var suffix");
if (pvar)
free(pvar);
free(tst_string);
/* numeric first char in var, should fail */
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar == NULL, "process_var - invalid char in var");
if (pvar)
free(pvar);
free(tst_string);
/* underscore char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar != NULL, "extract_variable underscore var pvar");
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split out underscore var prefix");
MY_TEST(strcmp(pvar, var3) == 0, "split out underscore var");
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split out underscore var suffix");
if (pvar)
free(pvar);
free(tst_string);
/* underscore first char in var, should fail */
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar == NULL, "process_var - invalid char in var");
if (pvar)
free(pvar);
free(tst_string);
/* empty var name, should fail */
asprintf(&tst_string, "%s@{}%s", prefix, suffix);
result = extract_variable(tst_string);
result_prefix = std::get<0>(result);
result_var = std::get<1>(result);
result_suffix = std::get<2>(result);
pvar = variable::process_var(result_var.c_str());
MY_TEST(pvar == NULL, "process_var - empty var name");
if (pvar)
free(pvar);
free(tst_string);
return rc;
}
int main(void)
{
int rc = 0;
int 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 */