2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 18:17:09 +00:00
apparmor/parser/parser_variable.c
Georgia Garcia dfbd2dc4b1 parser: refactor variables and symbols table into their own class
While symtab for now has only static members, it will allow for a
change in the future for each profile to have their own symbols like
profile_name, etc.
2025-06-25 12:29:17 -03:00

527 lines
16 KiB
C

/*
* 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>
#include <string>
/* #define DEBUG */
#include "parser.h"
#include "profile.h"
#include "mount.h"
#include "dbus.h"
#include "symtab.h"
/* 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;
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;
}
}
return 0;
}
static int process_variables_in_rules(Profile &prof)
{
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);
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;
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
* have the correct name
*/
error = process_variables_in_name(*prof);
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());
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.
*/
/* 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);
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);
if (error)
goto cleanup_attach;
}
error = process_variables_in_entries(prof->entries);
if (error)
goto cleanup;
error = process_variables_in_rules(*prof);
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) {
symtab::delete_var(PROFILE_EXEC_VAR);
if (saved_exec_path)
symtab::add_var(*saved_exec_path);
}
cleanup_attach:
if (prof->attachment) {
symtab::delete_var(PROFILE_ATTACH_VAR);
if (saved_attach_path)
symtab::add_var(*saved_attach_path);
}
cleanup_name:
symtab::delete_var(PROFILE_NAME_VARIABLE);
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 */