Merge parser: Improve rule merging/dedup

Currently File rules are the only rules that have rule dedup/merging performed. Extend support for rule merging to all other rule types.

This can result in a small performance regression when rules can not be merged/deduped but can result in a large performance increase when lots of rules can be eliminated.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1065
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
John Johansen 2023-07-10 19:13:25 +00:00
commit 1279f85e4a
40 changed files with 730 additions and 295 deletions

View file

@ -106,7 +106,8 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
file_cache.h immunix.h lib.h mount.h network.h parser.h \
parser_include.h parser_version.h policy_cache.h policydb.h \
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
common_flags.h
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
GENERATED_HDRS = af_names.h generated_af_names.h \

View file

@ -76,6 +76,35 @@ public:
virtual ostream &dump(ostream &os);
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof) = 0;
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
af_rule const &trhs = (rule_cast<af_rule const &>(rhs));
res = af - trhs.af;
if (res)
return res;
res = sock_type_n - trhs.sock_type_n;
if (res)
return res;
res = proto_n - trhs.proto_n;
if (res)
return res;
res = null_strcmp(sock_type, trhs.sock_type);
if (res)
return res;
res = null_strcmp(proto, trhs.proto);
if (res)
return res;
res = null_strcmp(label, trhs.label);
if (res)
return res;
return null_strcmp(peer_label, trhs.peer_label);
};
};
#endif /* __AA_AF_RULE_H */

View file

@ -24,6 +24,7 @@
#include <string>
#include <sstream>
#include "common_optarg.h"
#include "network.h"
#include "parser.h"
#include "profile.h"
@ -203,7 +204,7 @@ void unix_rule::downgrade_rule(Profile &prof) {
* restrictive and may end up denying accesses that might be
* allowed by the profile.
*/
if (warnflags & WARN_RULE_NOT_ENFORCED)
if (parseopts.warn & WARN_RULE_NOT_ENFORCED)
rule_t::warn_once(prof.name, "deny unix socket rule not enforced, can't be downgraded to generic network rule\n");
}
}
@ -321,7 +322,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (features_supports_network || features_supports_networkv8) {
/* only warn if we are building against a kernel
* that requires downgrading */
if (warnflags & WARN_RULE_DOWNGRADED)
if (parseopts.warn & WARN_RULE_DOWNGRADED)
rule_t::warn_once(prof.name, "downgrading extended network unix socket rule to generic network rule\n");
/* TODO: add ability to abort instead of downgrade */
return RULE_OK;
@ -337,7 +338,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_CREATE),
map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0),
dfaflags))
parseopts))
goto fail;
mask &= ~AA_NET_CREATE;
}
@ -362,7 +363,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_BIND),
map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0),
dfaflags))
parseopts))
goto fail;
/* clear if auto, else generic need to generate addr below */
if (addr)
@ -387,7 +388,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(mask & local_mask),
map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0),
dfaflags))
parseopts))
goto fail;
}
@ -401,7 +402,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_LISTEN),
map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0),
dfaflags))
parseopts))
goto fail;
}
if ((mask & AA_NET_OPT) && !has_peer_conds()) {
@ -414,7 +415,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_OPT),
map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0),
dfaflags))
parseopts))
goto fail;
}
mask &= ~AA_LOCAL_NET_PERMS | AA_NET_ACCEPT;
@ -432,7 +433,7 @@ int unix_rule::gen_policy_re(Profile &prof)
goto fail;
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), dfaflags))
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), parseopts))
goto fail;
}

View file

@ -62,6 +62,19 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
// inherit is_mergable() from af_rule
virtual int cmp(rule_t const &rhs) const
{
int res = af_rule::cmp(rhs);
if (res)
return res;
unix_rule const &trhs = (rule_cast<unix_rule const &>(rhs));
res = null_strcmp(addr, trhs.addr);
if (res)
return res;
return null_strcmp(peer_addr, trhs.peer_addr);
};
protected:
virtual void warn_once(const char *name) override;
};

33
parser/common_flags.h Normal file
View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023
* Canonical Ltd. (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. or Canonical
* Ltd.
*/
#ifndef __AA_COMMON_FLAGS_H
#define __AA_COMMON_FLAGS_H
typedef int optflags_t;
typedef struct optflags {
optflags_t control;
optflags_t dump;
optflags_t warn;
optflags_t Werror;
} optflags;
extern optflags parseopts;
#endif /* __AA_COMMON_FLAGS_H */

View file

@ -29,80 +29,83 @@
optflag_table_t dumpflag_table[] = {
{ 1, "rule-exprs", "Dump rule to expr tree conversions",
DFA_DUMP_RULE_EXPR },
{ 1, "expr-stats", "Dump stats on expr tree", DFA_DUMP_TREE_STATS },
{ 1, "expr-tree", "Dump expression tree", DFA_DUMP_TREE },
DUMP_DFA_RULE_EXPR },
{ 1, "expr-stats", "Dump stats on expr tree", DUMP_DFA_TREE_STATS },
{ 1, "expr-tree", "Dump expression tree", DUMP_DFA_TREE },
{ 1, "expr-simplified", "Dump simplified expression tree",
DFA_DUMP_SIMPLE_TREE },
DUMP_DFA_SIMPLE_TREE },
{ 1, "stats", "Dump all compile stats",
DFA_DUMP_TREE_STATS | DFA_DUMP_STATS | DFA_DUMP_TRANS_STATS |
DFA_DUMP_EQUIV_STATS | DFA_DUMP_DIFF_STATS },
DUMP_DFA_TREE_STATS | DUMP_DFA_STATS | DUMP_DFA_TRANS_STATS |
DUMP_DFA_EQUIV_STATS | DUMP_DFA_DIFF_STATS },
{ 1, "progress", "Dump progress for all compile phases",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS | DFA_DUMP_TRANS_PROGRESS |
DFA_DUMP_TRANS_STATS | DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS },
DUMP_DFA_PROGRESS | DUMP_DFA_STATS | DUMP_DFA_TRANS_PROGRESS |
DUMP_DFA_TRANS_STATS | DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
{ 1, "dfa-progress", "Dump dfa creation as in progress",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DFA_DUMP_STATS },
{ 1, "dfa-states", "Dump dfa state diagram", DFA_DUMP_STATES },
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DFA_DUMP_GRAPH },
{ 1, "dfa-minimize", "Dump dfa minimization", DFA_DUMP_MINIMIZE },
DUMP_DFA_PROGRESS | DUMP_DFA_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS },
{ 1, "dfa-states", "Dump dfa state diagram", DUMP_DFA_STATES },
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DUMP_DFA_GRAPH },
{ 1, "dfa-minimize", "Dump dfa minimization", DUMP_DFA_MINIMIZE },
{ 1, "dfa-unreachable", "Dump dfa unreachable states",
DFA_DUMP_UNREACHABLE },
DUMP_DFA_UNREACHABLE },
{ 1, "dfa-node-map", "Dump expr node set to state mapping",
DFA_DUMP_NODE_TO_DFA },
DUMP_DFA_NODE_TO_DFA },
{ 1, "dfa-uniq-perms", "Dump unique perms",
DFA_DUMP_UNIQ_PERMS },
DUMP_DFA_UNIQ_PERMS },
{ 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization",
DFA_DUMP_MIN_UNIQ_PERMS },
DUMP_DFA_MIN_UNIQ_PERMS },
{ 1, "dfa-minimize-partitions", "Dump dfa minimization partitions",
DFA_DUMP_MIN_PARTS },
DUMP_DFA_MIN_PARTS },
{ 1, "compress-progress", "Dump progress of compression",
DFA_DUMP_TRANS_PROGRESS | DFA_DUMP_TRANS_STATS },
DUMP_DFA_TRANS_PROGRESS | DUMP_DFA_TRANS_STATS },
{ 1, "compress-stats", "Dump stats on compression",
DFA_DUMP_TRANS_STATS },
{ 1, "compressed-dfa", "Dump compressed dfa", DFA_DUMP_TRANS_TABLE },
DUMP_DFA_TRANS_STATS },
{ 1, "compressed-dfa", "Dump compressed dfa", DUMP_DFA_TRANS_TABLE },
{ 1, "equiv-stats", "Dump equivalence class stats",
DFA_DUMP_EQUIV_STATS },
{ 1, "equiv", "Dump equivalence class", DFA_DUMP_EQUIV },
DUMP_DFA_EQUIV_STATS },
{ 1, "equiv", "Dump equivalence class", DUMP_DFA_EQUIV },
{ 1, "diff-encode", "Dump differential encoding",
DFA_DUMP_DIFF_ENCODE },
DUMP_DFA_DIFF_ENCODE },
{ 1, "diff-stats", "Dump differential encoding stats",
DFA_DUMP_DIFF_STATS },
DUMP_DFA_DIFF_STATS },
{ 1, "diff-progress", "Dump progress of differential encoding",
DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS },
DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
{ 1, "rule-merge", "dump information about rule merging", DUMP_RULE_MERGE},
{ 0, NULL, NULL, 0 },
};
optflag_table_t optflag_table[] = {
optflag_table_t dfaoptflag_table[] = {
{ 2, "0", "no optimizations",
DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE |
DFA_CONTROL_MINIMIZE | DFA_CONTROL_REMOVE_UNREACHABLE |
DFA_CONTROL_DIFF_ENCODE
CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE |
CONTROL_DFA_MINIMIZE | CONTROL_DFA_REMOVE_UNREACHABLE |
CONTROL_DFA_DIFF_ENCODE
},
{ 1, "equiv", "use equivalent classes", DFA_CONTROL_EQUIV },
{ 1, "equiv", "use equivalent classes", CONTROL_DFA_EQUIV },
{ 1, "expr-normalize", "expression tree normalization",
DFA_CONTROL_TREE_NORMAL },
CONTROL_DFA_TREE_NORMAL },
{ 1, "expr-simplify", "expression tree simplification",
DFA_CONTROL_TREE_SIMPLE },
CONTROL_DFA_TREE_SIMPLE },
{ 0, "expr-left-simplify", "left simplification first",
DFA_CONTROL_TREE_LEFT },
CONTROL_DFA_TREE_LEFT },
{ 2, "expr-right-simplify", "right simplification first",
DFA_CONTROL_TREE_LEFT },
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
CONTROL_DFA_TREE_LEFT },
{ 1, "minimize", "dfa state minimization", CONTROL_DFA_MINIMIZE },
{ 1, "filter-deny", "filter out deny information from final dfa",
DFA_CONTROL_FILTER_DENY },
CONTROL_DFA_FILTER_DENY },
{ 1, "remove-unreachable", "dfa unreachable state removal",
DFA_CONTROL_REMOVE_UNREACHABLE },
CONTROL_DFA_REMOVE_UNREACHABLE },
{ 0, "compress-small",
"do slower dfa transition table compression",
DFA_CONTROL_TRANS_HIGH },
CONTROL_DFA_TRANS_HIGH },
{ 2, "compress-fast", "do faster dfa transition table compression",
DFA_CONTROL_TRANS_HIGH },
CONTROL_DFA_TRANS_HIGH },
{ 1, "diff-encode", "Differentially encode transitions",
DFA_CONTROL_DIFF_ENCODE },
CONTROL_DFA_DIFF_ENCODE },
{ 1, "rule-merge", "turn on rule merging", CONTROL_RULE_MERGE},
{ 0, NULL, NULL, 0 },
};
void print_flag_table(optflag_table_t *table)
{
int i;
@ -114,12 +117,14 @@ void print_flag_table(optflag_table_t *table)
printf("%-*s \t%s\n", longest, " show", "show flags that have been set and exit");
for (i = 0; table[i].option; i++) {
printf("%5s%-*s \t%s\n", (table[i].control & 1) ? "[no-]" : "",
printf("%5s%-*s \t%s\n",
(table[i].control & OPT_FLAG_CONTROL_PREFIX_NO) ? "[no-]" : "",
longest, table[i].option, table[i].desc);
}
}
void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags)
void print_flags(const char *prefix, optflag_table_t *table,
optflags_t flags)
{
int i, count = 0;
@ -137,7 +142,7 @@ void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags)
}
int handle_flag_table(optflag_table_t *table, const char *optarg,
dfaflags_t *flags)
optflags_t *flags)
{
const char *arg = optarg;
int i, invert = 0;

View file

@ -21,25 +21,31 @@
#ifndef __AA_COMMON_OPTARG_H
#define __AA_COMMON_OPTARG_H
#include "common_flags.h"
#include "libapparmor_re/apparmor_re.h"
/*
* flag: 1 - allow no- inversion
* flag: 2 - flags specified should be masked off
*/
#define OPT_FLAG_CONTROL_PREFIX_NO 1
#define OPT_FLAG_CONTROL_MASK 2
typedef struct {
int control;
const char *option;
const char *desc;
dfaflags_t flags;
optflags_t flags;
} optflag_table_t;
extern optflag_table_t dumpflag_table[];
extern optflag_table_t optflag_table[];
extern optflag_table_t dfaoptflag_table[];
void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags);
void print_flags(const char *prefix, optflag_table_t *table,
optflags_t flags);
int handle_flag_table(optflag_table_t *table, const char *optarg,
dfaflags_t *flags);
optflags_t *flags);
void flagtable_help(const char *name, const char *header, const char *command,
optflag_table_t *table);

View file

@ -276,21 +276,21 @@ int dbus_rule::gen_policy_re(Profile &prof)
if (perms & AA_DBUS_BIND) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms & AA_DBUS_BIND,
audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0,
2, vec, dfaflags, false))
2, vec, parseopts, false))
goto fail;
}
if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0,
6, vec, dfaflags, false))
6, vec, parseopts, false))
goto fail;
}
if (perms & AA_DBUS_EAVESDROP) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & AA_DBUS_EAVESDROP,
audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0,
1, vec, dfaflags, false))
1, vec, parseopts, false))
goto fail;
}

View file

@ -62,6 +62,32 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
dbus_rule const &trhs = (rule_cast<dbus_rule const &>(rhs));
res = null_strcmp(bus, trhs.bus);
if (res)
return res;
res = null_strcmp(name, trhs.name);
if (res)
return res;
res = null_strcmp(peer_label, trhs.peer_label);
if (res)
return res;
res = null_strcmp(path, trhs.path);
if (res)
return res;
res = null_strcmp(interface, trhs.interface);
if (res)
return res;
return null_strcmp(member, trhs.member);
};
protected:
virtual void warn_once(const char *name) override;
};

View file

@ -15,6 +15,7 @@
* along with this program; if not, contact or Canonical Ltd.
*/
#include "common_optarg.h"
#include "parser.h"
#include "profile.h"
#include "io_uring.h"
@ -123,14 +124,14 @@ int io_uring_rule::gen_policy_re(Profile &prof)
if (perms & AA_VALID_IO_URING_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0,
dfaflags))
parseopts))
goto fail;
if (perms & AA_IO_URING_OVERRIDE_CREDS) {
buf = buffer.str(); /* update buf to have label */
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags))
parseopts))
goto fail;
}

View file

@ -49,6 +49,16 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
return null_strcmp(label,
(rule_cast<io_uring_rule const &>(rhs)).label);
};
protected:
virtual void warn_once(const char *name) override;
};

View file

@ -45,9 +45,9 @@ aare_rules::~aare_rules(void)
}
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags)
uint32_t audit, optflags const &opts)
{
return add_rule_vec(deny, perms, audit, 1, &rule, flags, false);
return add_rule_vec(deny, perms, audit, 1, &rule, opts, false);
}
void aare_rules::add_to_rules(Node *tree, Node *perms)
@ -72,7 +72,7 @@ static Node *cat_with_oob_separator(Node *l, Node *r)
}
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
int count, const char **rulev, dfaflags_t flags,
int count, const char **rulev, optflags const &opts,
bool oob)
{
Node *tree = NULL, *accept;
@ -110,7 +110,7 @@ bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
accept = unique_perms.insert(deny, perms, audit, exact_match);
if (flags & DFA_DUMP_RULE_EXPR) {
if (opts.dump & DUMP_DFA_RULE_EXPR) {
const char *separator;
if (oob)
separator = "\\-x01";
@ -152,13 +152,13 @@ err:
* advanced by a null character for each xattr.
*/
bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
dfaflags_t flags)
optflags const &opts)
{
Node *tree = NULL;
if (regex_parse(&tree, rule))
return false;
if (flags & DFA_DUMP_RULE_EXPR) {
if (opts.dump & DUMP_DFA_RULE_EXPR) {
cerr << "rule: ";
cerr << rule;
cerr << " -> ";
@ -195,7 +195,7 @@ bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
* else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority.
*/
void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
void *aare_rules::create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa)
{
char *buffer = NULL;
@ -204,15 +204,15 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* set nodes */
PermExprMap::iterator i = expr_map.begin();
if (i != expr_map.end()) {
if (flags & DFA_CONTROL_TREE_SIMPLE) {
Node *tmp = simplify_tree(i->second, flags);
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
Node *tmp = simplify_tree(i->second, opts);
root = new CatNode(tmp, i->first);
} else
root = new CatNode(i->second, i->first);
for (i++; i != expr_map.end(); i++) {
Node *tmp;
if (flags & DFA_CONTROL_TREE_SIMPLE) {
tmp = simplify_tree(i->second, flags);
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
tmp = simplify_tree(i->second, opts);
} else
tmp = i->second;
root = new AltNode(root, new CatNode(tmp, i->first));
@ -226,22 +226,22 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* this debug dump.
*/
label_nodes(root);
if (flags & DFA_DUMP_TREE) {
if (opts.dump & DUMP_DFA_TREE) {
cerr << "\nDFA: Expression Tree\n";
root->dump(cerr);
cerr << "\n\n";
}
if (flags & DFA_CONTROL_TREE_SIMPLE) {
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
/* This is old total tree, simplification point
* For now just do simplification up front. It gets most
* of the benefit running on the smaller chains, and is
* overall faster because there are less nodes. Reevaluate
* once tree simplification is rewritten
*/
//root = simplify_tree(root, flags);
//root = simplify_tree(root, opts);
if (flags & DFA_DUMP_SIMPLE_TREE) {
if (opts.dump & DUMP_DFA_SIMPLE_TREE) {
cerr << "\nDFA: Simplified Expression Tree\n";
root->dump(cerr);
cerr << "\n\n";
@ -250,19 +250,19 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
stringstream stream;
try {
DFA dfa(root, flags, filedfa);
if (flags & DFA_DUMP_UNIQ_PERMS)
DFA dfa(root, opts, filedfa);
if (opts.dump & DUMP_DFA_UNIQ_PERMS)
dfa.dump_uniq_perms("dfa");
if (flags & DFA_CONTROL_MINIMIZE) {
dfa.minimize(flags);
if (opts.control & CONTROL_DFA_MINIMIZE) {
dfa.minimize(opts);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (flags & DFA_CONTROL_FILTER_DENY &&
flags & DFA_CONTROL_MINIMIZE &&
if (opts.control & CONTROL_DFA_FILTER_DENY &&
opts.control & CONTROL_DFA_MINIMIZE &&
dfa.apply_and_clear_deny()) {
/* Do a second minimization pass as removal of deny
* information has moved some states from accepting
@ -271,42 +271,42 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* TODO: add this as a tail pass to minimization
* so we don't need to do a full second pass
*/
dfa.minimize(flags);
dfa.minimize(opts);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
dfa.remove_unreachable(flags);
if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE)
dfa.remove_unreachable(opts);
if (flags & DFA_DUMP_STATES)
if (opts.dump & DUMP_DFA_STATES)
dfa.dump(cerr);
if (flags & DFA_DUMP_GRAPH)
if (opts.dump & DUMP_DFA_GRAPH)
dfa.dump_dot_graph(cerr);
map<transchar, transchar> eq;
if (flags & DFA_CONTROL_EQUIV) {
eq = dfa.equivalence_classes(flags);
if (opts.control & CONTROL_DFA_EQUIV) {
eq = dfa.equivalence_classes(opts);
dfa.apply_equivalence_classes(eq);
if (flags & DFA_DUMP_EQUIV) {
if (opts.dump & DUMP_DFA_EQUIV) {
cerr << "\nDFA equivalence class\n";
dump_equivalence_classes(cerr, eq);
}
} else if (flags & DFA_DUMP_EQUIV)
} else if (opts.dump & DUMP_DFA_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n";
if (flags & DFA_CONTROL_DIFF_ENCODE) {
dfa.diff_encode(flags);
if (opts.control & CONTROL_DFA_DIFF_ENCODE) {
dfa.diff_encode(opts);
if (flags & DFA_DUMP_DIFF_ENCODE)
if (opts.dump & DUMP_DFA_DIFF_ENCODE)
dfa.dump_diff_encode(cerr);
}
CHFA chfa(dfa, eq, flags);
if (flags & DFA_DUMP_TRANS_TABLE)
CHFA chfa(dfa, eq, opts);
if (opts.dump & DUMP_DFA_TRANS_TABLE)
chfa.dump(cerr);
chfa.flex_table(stream, "");
}

View file

@ -23,6 +23,7 @@
#include <stdint.h>
#include "../common_optarg.h"
#include "apparmor_re.h"
#include "expr-tree.h"
@ -101,11 +102,11 @@ class aare_rules {
~aare_rules();
bool add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags);
uint32_t audit, optflags const &opts);
bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count,
const char **rulev, dfaflags_t flags, bool oob);
bool append_rule(const char *rule, bool oob, bool with_perm, dfaflags_t flags);
void *create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
const char **rulev, optflags const &opts, bool oob);
bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts);
void *create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa);
};

View file

@ -19,40 +19,42 @@
#ifndef APPARMOR_RE_H
#define APPARMOR_RE_H
typedef int dfaflags_t;
#include "../common_flags.h"
#define CONTROL_DFA_EQUIV (1 << 0)
#define CONTROL_DFA_TREE_NORMAL (1 << 1)
#define CONTROL_DFA_TREE_SIMPLE (1 << 2)
#define CONTROL_DFA_TREE_LEFT (1 << 3)
#define CONTROL_DFA_MINIMIZE (1 << 4)
#define CONTROL_DFA_FILTER_DENY (1 << 6)
#define CONTROL_DFA_REMOVE_UNREACHABLE (1 << 7)
#define CONTROL_DFA_TRANS_HIGH (1 << 8)
#define CONTROL_DFA_DIFF_ENCODE (1 << 9)
#define CONTROL_RULE_MERGE (1 << 10)
#define DFA_CONTROL_EQUIV (1 << 0)
#define DFA_CONTROL_TREE_NORMAL (1 << 1)
#define DFA_CONTROL_TREE_SIMPLE (1 << 2)
#define DFA_CONTROL_TREE_LEFT (1 << 3)
#define DFA_CONTROL_MINIMIZE (1 << 4)
#define DFA_CONTROL_FILTER_DENY (1 << 6)
#define DFA_CONTROL_REMOVE_UNREACHABLE (1 << 7)
#define DFA_CONTROL_TRANS_HIGH (1 << 8)
#define DFA_CONTROL_DIFF_ENCODE (1 << 9)
#define DFA_DUMP_DIFF_PROGRESS (1 << 10)
#define DFA_DUMP_DIFF_ENCODE (1 << 11)
#define DFA_DUMP_DIFF_STATS (1 << 12)
#define DFA_DUMP_MIN_PARTS (1 << 13)
#define DFA_DUMP_UNIQ_PERMS (1 << 14)
#define DFA_DUMP_MIN_UNIQ_PERMS (1 << 15)
#define DFA_DUMP_TREE_STATS (1 << 16)
#define DFA_DUMP_TREE (1 << 17)
#define DFA_DUMP_SIMPLE_TREE (1 << 18)
#define DFA_DUMP_PROGRESS (1 << 19)
#define DFA_DUMP_STATS (1 << 20)
#define DFA_DUMP_STATES (1 << 21)
#define DFA_DUMP_GRAPH (1 << 22)
#define DFA_DUMP_TRANS_PROGRESS (1 << 23)
#define DFA_DUMP_TRANS_STATS (1 << 24)
#define DFA_DUMP_TRANS_TABLE (1 << 25)
#define DFA_DUMP_EQUIV (1 << 26)
#define DFA_DUMP_EQUIV_STATS (1 << 27)
#define DFA_DUMP_MINIMIZE (1 << 28)
#define DFA_DUMP_UNREACHABLE (1 << 29)
#define DFA_DUMP_RULE_EXPR (1 << 30)
#define DFA_DUMP_NODE_TO_DFA (1 << 31)
#define DUMP_DFA_DIFF_PROGRESS (1 << 0)
#define DUMP_DFA_DIFF_ENCODE (1 << 1)
#define DUMP_DFA_DIFF_STATS (1 << 2)
#define DUMP_DFA_MIN_PARTS (1 << 3)
#define DUMP_DFA_UNIQ_PERMS (1 << 4)
#define DUMP_DFA_MIN_UNIQ_PERMS (1 << 5)
#define DUMP_DFA_TREE_STATS (1 << 6)
#define DUMP_DFA_TREE (1 << 7)
#define DUMP_DFA_SIMPLE_TREE (1 << 8)
#define DUMP_DFA_PROGRESS (1 << 9)
#define DUMP_DFA_STATS (1 << 10)
#define DUMP_DFA_STATES (1 << 11)
#define DUMP_DFA_GRAPH (1 << 12)
#define DUMP_DFA_TRANS_PROGRESS (1 << 13)
#define DUMP_DFA_TRANS_STATS (1 << 14)
#define DUMP_DFA_TRANS_TABLE (1 << 15)
#define DUMP_DFA_EQUIV (1 << 16)
#define DUMP_DFA_EQUIV_STATS (1 << 17)
#define DUMP_DFA_MINIMIZE (1 << 18)
#define DUMP_DFA_UNREACHABLE (1 << 19)
#define DUMP_DFA_RULE_EXPR (1 << 20)
#define DUMP_DFA_NODE_TO_DFA (1 << 21)
#define DUMP_RULE_MERGE (1 << 22)
#endif /* APPARMOR_RE_H */

View file

@ -49,9 +49,10 @@ void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
/**
* new Construct the transition table.
*/
CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts):
eq(eq)
{
if (flags & DFA_DUMP_TRANS_PROGRESS)
if (opts.dump & DUMP_DFA_TRANS_PROGRESS)
fprintf(stderr, "Compressing HFA:\r");
chfaflags = 0;
@ -82,7 +83,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
if (*i == dfa.start || *i == dfa.nonmatching)
continue;
optimal += (*i)->trans.size();
if (flags & DFA_CONTROL_TRANS_HIGH) {
if (opts.control & CONTROL_DFA_TRANS_HIGH) {
size_t range = 0;
if ((*i)->trans.size())
range =
@ -116,7 +117,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
int count = 2;
if (!(flags & DFA_CONTROL_TRANS_HIGH)) {
if (!(opts.control & CONTROL_DFA_TRANS_HIGH)) {
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i != dfa.nonmatching && *i != dfa.start) {
insert_state(free_list, *i, dfa);
@ -124,7 +125,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny);
num.insert(make_pair(*i, num.size()));
}
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
count++;
if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
@ -141,7 +142,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny);
num.insert(make_pair(i->second, num.size()));
}
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
count++;
if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
@ -150,7 +151,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
}
}
if (flags & (DFA_DUMP_TRANS_STATS | DFA_DUMP_TRANS_PROGRESS)) {
if (opts.dump & (DUMP_DFA_TRANS_STATS | DUMP_DFA_TRANS_PROGRESS)) {
ssize_t size = 4 * next_check.size() + 6 * dfa.states.size();
fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n",
dfa.states.size(), next_check.size(), optimal,

View file

@ -37,7 +37,7 @@ class CHFA {
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
public:
CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags);
CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts);
void dump(ostream & os);
void flex_table(ostream &os, const char *name);
void init_free_list(vector<pair<size_t, size_t> > &free_list,

View file

@ -575,12 +575,12 @@ static void count_tree_nodes(Node *t, struct node_counts *counts)
// simplification passes. Simplification may exit sooner if no changes
// are made.
#define MAX_PASSES 1
Node *simplify_tree(Node *t, dfaflags_t flags)
Node *simplify_tree(Node *t, optflags const &opts)
{
bool update = true;
int i;
if (flags & DFA_DUMP_TREE_STATS) {
if (opts.dump & DUMP_DFA_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts);
fprintf(stderr,
@ -598,25 +598,25 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
// the dfa having about 7 thousands states,
// and it having about 1.25 million states
int dir = 1;
if (flags & DFA_CONTROL_TREE_LEFT)
if (opts.control & CONTROL_DFA_TREE_LEFT)
dir = 0;
for (int count = 0; count < 2; count++) {
bool modified;
do {
modified = false;
if (flags & DFA_CONTROL_TREE_NORMAL)
if (opts.control & CONTROL_DFA_TREE_NORMAL)
t->normalize(dir);
t = simplify_tree_base(t, dir, modified);
if (modified)
update = true;
} while (modified);
if (flags & DFA_CONTROL_TREE_LEFT)
if (opts.control & CONTROL_DFA_TREE_LEFT)
dir++;
else
dir--;
}
}
if (flags & DFA_DUMP_TREE_STATS) {
if (opts.dump & DUMP_DFA_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts);
fprintf(stderr,

View file

@ -958,7 +958,7 @@ struct node_counts {
extern EpsNode epsnode;
int debug_tree(Node *t);
Node *simplify_tree(Node *t, dfaflags_t flags);
Node *simplify_tree(Node *t, optflags const &opts);
void label_nodes(Node *root);
unsigned long hash_NodeSet(NodeSet *ns);
void flip_tree(Node *node);

View file

@ -391,12 +391,12 @@ void DFA::dump_node_to_dfa(void)
cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n";
}
void DFA::process_work_queue(const char *header, dfaflags_t flags)
void DFA::process_work_queue(const char *header, optflags const &opts)
{
int i = 0;
while (!work_queue.empty()) {
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS)) {
if (i % 1000 == 0 && (opts.dump & DUMP_DFA_PROGRESS)) {
cerr << "\033[2K" << header << ": queue "
<< work_queue.size()
<< "\tstates "
@ -420,7 +420,7 @@ void DFA::process_work_queue(const char *header, dfaflags_t flags)
/**
* Construct a DFA from a syntax tree.
*/
DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(buildfiledfa)
DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filedfa(buildfiledfa)
{
diffcount = 0; /* set by diff_encode */
max_range = 256;
@ -428,7 +428,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
oob_range = 0;
ord_range = 8;
if (flags & DFA_DUMP_PROGRESS)
if (opts.dump & DUMP_DFA_PROGRESS)
fprintf(stderr, "Creating dfa:\r");
for (depth_first_traversal i(root); i; i++) {
@ -437,7 +437,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
(*i)->compute_lastpos();
}
if (flags & DFA_DUMP_PROGRESS)
if (opts.dump & DUMP_DFA_PROGRESS)
fprintf(stderr, "Creating dfa: followpos\r");
for (depth_first_traversal i(root); i; i++) {
(*i)->compute_followpos();
@ -457,7 +457,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
* work_queue at any given time, thus reducing peak memory use.
*/
work_queue.push_back(start);
process_work_queue("Creating dfa", flags);
process_work_queue("Creating dfa", opts);
max_range += oob_range;
/* if oob_range is ever greater than 256 need to move to computing this */
if (oob_range)
@ -471,10 +471,10 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
(*i)->followpos.clear();
}
if (flags & DFA_DUMP_NODE_TO_DFA)
if (opts.dump & DUMP_DFA_NODE_TO_DFA)
dump_node_to_dfa();
if (flags & (DFA_DUMP_STATS)) {
if (opts.dump & (DUMP_DFA_STATS)) {
cerr << "\033[2KCreated dfa: states "
<< states.size()
<< " proto { "
@ -540,7 +540,7 @@ void DFA::dump_uniq_perms(const char *s)
}
/* Remove dead or unreachable states */
void DFA::remove_unreachable(dfaflags_t flags)
void DFA::remove_unreachable(optflags const &opts)
{
set<State *> reachable;
@ -571,7 +571,7 @@ void DFA::remove_unreachable(dfaflags_t flags)
next = i;
next++;
if (reachable.find(*i) == reachable.end()) {
if (flags & DFA_DUMP_UNREACHABLE) {
if (opts.dump & DUMP_DFA_UNREACHABLE) {
cerr << "unreachable: " << **i;
if (*i == start)
cerr << " <==";
@ -586,7 +586,7 @@ void DFA::remove_unreachable(dfaflags_t flags)
}
}
if (count && (flags & DFA_DUMP_STATS))
if (count && (opts.dump & DUMP_DFA_STATS))
cerr << "DFA: states " << states.size() << " removed "
<< count << " unreachable states\n";
}
@ -645,7 +645,7 @@ int DFA::apply_and_clear_deny(void)
}
/* minimize the number of dfa states */
void DFA::minimize(dfaflags_t flags)
void DFA::minimize(optflags const &opts)
{
map<pair<uint64_t, size_t>, Partition *> perm_map;
list<Partition *> partitions;
@ -680,7 +680,7 @@ void DFA::minimize(dfaflags_t flags)
p->second->push_back(*i);
}
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 1000 == 0))
if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 1000 == 0))
cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit " << partitions.size()
<< " (accept " << accept_count << ")\r";
@ -692,7 +692,7 @@ void DFA::minimize(dfaflags_t flags)
perm_map.clear();
int init_count = partitions.size();
if (flags & DFA_DUMP_PROGRESS)
if (opts.dump & DUMP_DFA_PROGRESS)
cerr << "\033[2KMinimize dfa: partitions " << partitions.size()
<< "\tinit " << init_count << " (accept "
<< accept_count << ")\r";
@ -734,7 +734,7 @@ void DFA::minimize(dfaflags_t flags)
(*m)->partition = new_part;
}
}
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 100 == 0))
if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 100 == 0))
cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit "
<< init_count << " (accept "
@ -743,7 +743,7 @@ void DFA::minimize(dfaflags_t flags)
} while (new_part_count);
if (partitions.size() == states.size()) {
if (flags & DFA_DUMP_STATS)
if (opts.dump & DUMP_DFA_STATS)
cerr << "\033[2KDfa minimization no states removed: partitions "
<< partitions.size() << "\tinit " << init_count
<< " (accept " << accept_count << ")\n";
@ -757,13 +757,13 @@ void DFA::minimize(dfaflags_t flags)
* to states within the same partitions, however this can slow
* down compressed dfa compression as there are more states,
*/
if (flags & DFA_DUMP_MIN_PARTS)
if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << "Partitions after minimization\n";
for (list<Partition *>::iterator p = partitions.begin();
p != partitions.end(); p++) {
/* representative state for this partition */
State *rep = *((*p)->begin());
if (flags & DFA_DUMP_MIN_PARTS)
if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << *rep << " : ";
/* update representative state's transitions */
@ -782,17 +782,17 @@ void DFA::minimize(dfaflags_t flags)
/* clear the state label for all non representative states,
* and accumulate permissions */
for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) {
if (flags & DFA_DUMP_MIN_PARTS)
if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << **i << ", ";
(*i)->label = -1;
rep->perms.add((*i)->perms, filedfa);
}
if (rep->perms.is_accept())
final_accept++;
if (flags & DFA_DUMP_MIN_PARTS)
if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << "\n";
}
if (flags & DFA_DUMP_STATS)
if (opts.dump & DUMP_DFA_STATS)
cerr << "\033[2KMinimized dfa: final partitions "
<< partitions.size() << " (accept " << final_accept
<< ")" << "\tinit " << init_count << " (accept "
@ -875,7 +875,7 @@ static int diff_partition(State *state, Partition &part, int max_range, int uppe
/**
* diff_encode - compress dfa by differentially encoding state transitions
* @dfa_flags: flags controlling dfa creation
* @opts: flags controlling dfa creation
*
* This function reduces the number of transitions that need to be stored
* by encoding transitions as the difference between the state and a
@ -910,7 +910,7 @@ static int diff_partition(State *state, Partition &part, int max_range, int uppe
* the state transition at most will only move 1 deeper into the DAG so for
* the next state the maximum number of states traversed is 2*7.
*/
void DFA::diff_encode(dfaflags_t flags)
void DFA::diff_encode(optflags const &opts)
{
DiffDag *dag;
unsigned int xcount = 0, xweight = 0, transitions = 0, depth = 0;
@ -965,7 +965,7 @@ void DFA::diff_encode(dfaflags_t flags)
}
}
if ((flags & DFA_DUMP_DIFF_PROGRESS) && (i % 100 == 0))
if ((opts.dump & DUMP_DFA_DIFF_PROGRESS) && (i % 100 == 0))
cerr << "\033[2KDiff Encode: " << i << " of "
<< tail << ". Diff states " << xcount
<< " Savings " << xweight << "\r";
@ -992,7 +992,7 @@ void DFA::diff_encode(dfaflags_t flags)
}
}
if (flags & DFA_DUMP_DIFF_STATS)
if (opts.dump & DUMP_DFA_DIFF_STATS)
cerr << "Diff encode states: " << diffcount << " of "
<< tail << " reached @ depth " << depth << ". "
<< aweight << " trans removed\n";
@ -1194,7 +1194,7 @@ void DFA::dump_dot_graph(ostream & os)
* Compute character equivalence classes in the DFA to save space in the
* transition table.
*/
map<transchar, transchar> DFA::equivalence_classes(dfaflags_t flags)
map<transchar, transchar> DFA::equivalence_classes(optflags const &opts)
{
map<transchar, transchar> classes;
transchar next_class = 1;
@ -1251,7 +1251,7 @@ map<transchar, transchar> DFA::equivalence_classes(dfaflags_t flags)
}
}
if (flags & DFA_DUMP_EQUIV_STATS)
if (opts.dump & DUMP_DFA_EQUIV_STATS)
fprintf(stderr, "Equiv class reduces to %d classes\n",
next_class.c - 1);
return classes;

View file

@ -305,7 +305,7 @@ class DFA {
State *add_new_state(NodeSet *nodes, State *other);
State *add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other);
void update_state_transitions(State *state);
void process_work_queue(const char *header, dfaflags_t);
void process_work_queue(const char *header, optflags const &);
void dump_diff_chain(ostream &os, map<State *, Partition> &relmap,
Partition &chain, State *state,
unsigned int &count, unsigned int &total,
@ -318,19 +318,19 @@ class DFA {
list<State *> work_queue;
public:
DFA(Node *root, dfaflags_t flags, bool filedfa);
DFA(Node *root, optflags const &flags, bool filedfa);
virtual ~DFA();
State *match_len(State *state, const char *str, size_t len);
State *match_until(State *state, const char *str, const char term);
State *match(const char *str);
void remove_unreachable(dfaflags_t flags);
void remove_unreachable(optflags const &flags);
bool same_mappings(State *s1, State *s2);
void minimize(dfaflags_t flags);
void minimize(optflags const &flags);
int apply_and_clear_deny(void);
void diff_encode(dfaflags_t flags);
void diff_encode(optflags const &flags);
void undiff_encode(void);
void dump_diff_encode(ostream &os);
@ -338,7 +338,7 @@ public:
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);
map<transchar, transchar> equivalence_classes(dfaflags_t flags);
map<transchar, transchar> equivalence_classes(optflags const &flags);
void apply_equivalence_classes(map<transchar, transchar> &eq);
unsigned int diffcount;

View file

@ -632,6 +632,51 @@ int mnt_rule::expand_variables(void)
return 0;
}
static int cmp_vec_int(std::vector<unsigned int> const &lhs,
std::vector<unsigned int> const &rhs)
{
int res = lhs.size() - rhs.size();
if (res)
return res;
for (unsigned long i = 0; i < lhs.size(); i++) {
res = lhs[i] - rhs[i];
if (res)
return res;
}
return 0;
}
int mnt_rule::cmp(rule_t const &rhs) const {
// for now don't do merging of perms, only exact match
int res = perms_rule_t::cmp(rhs);
if (res != 0)
return res;
mnt_rule const &rhs_mnt = rule_cast<mnt_rule const &>(rhs);
res = null_strcmp(mnt_point, rhs_mnt.mnt_point);
if (res)
return res;
res = null_strcmp(device, rhs_mnt.device);
if (res)
return res;
res = null_strcmp(trans, rhs_mnt.trans);
if (res)
return res;
res = cmp_value_list(dev_type, rhs_mnt.dev_type);
if (res)
return res;
res = cmp_value_list(opts, rhs_mnt.opts);
if (res)
return res;
res = cmp_vec_int(flagsv, rhs_mnt.flagsv);
if (res)
return res;
return cmp_vec_int(opt_flagsv, rhs_mnt.opt_flagsv);
}
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int opt_flags)
{
@ -753,7 +798,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
* else it has full perms
*/
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
vec, dfaflags, false))
vec, parseopts, false))
goto fail;
count++;
@ -765,7 +810,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0),
5, vec, dfaflags, false))
5, vec, parseopts, false))
goto fail;
count++;
}
@ -807,7 +852,7 @@ int mnt_rule::gen_policy_bind_mount(Profile &prof, int &count,
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec,
dfaflags, false))
parseopts, false))
goto fail;
count++;
@ -864,7 +909,7 @@ int mnt_rule::gen_policy_change_mount_type(Profile &prof, int &count,
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec,
dfaflags, false))
parseopts, false))
goto fail;
count++;
@ -907,7 +952,7 @@ int mnt_rule::gen_policy_move_mount(Profile &prof, int &count,
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec,
dfaflags, false))
parseopts, false))
goto fail;
count++;
@ -958,7 +1003,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
}
/* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
vec, dfaflags, false))
vec, parseopts, false))
goto fail;
count++;
@ -970,7 +1015,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0,
5, vec, dfaflags, false))
5, vec, parseopts, false))
goto fail;
count++;
}
@ -1062,7 +1107,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
vec[0] = mntbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0), 1, vec,
dfaflags, false))
parseopts, false))
goto fail;
count++;
}
@ -1077,7 +1122,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
vec[1] = devbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0), 2, vec,
dfaflags, false))
parseopts, false))
goto fail;
count++;
}

View file

@ -21,6 +21,7 @@
#include <ostream>
#include <vector>
#include <algorithm>
#include "parser.h"
#include "rule.h"
@ -173,6 +174,11 @@ public:
virtual int gen_policy_re(Profile &prof);
virtual void post_parse_profile(Profile &prof unused);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const;
// for now use default merge/dedup
protected:
virtual void warn_once(const char *name) override;
};

View file

@ -231,10 +231,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
/* store perms at name match so label doesn't need
* to be checked
*/
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, dfaflags, false))
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, parseopts, false))
goto fail;
/* also provide label match with perm */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, dfaflags, false))
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, parseopts, false))
goto fail;
}
}
@ -266,10 +266,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
}
if (perms & AA_VALID_SYSV_MQ_PERMS) {
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, dfaflags, false))
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, parseopts, false))
goto fail;
/* also provide label match with perm */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, dfaflags, false))
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, parseopts, false))
goto fail;
}
}

View file

@ -107,6 +107,22 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
mqueue_rule const &trhs = rule_cast<mqueue_rule const &>(rhs);
res = qtype - trhs.qtype;
if (res)
return res;
res = null_strcmp(qname, trhs.qname);
if (res)
return res;
return null_strcmp(label, trhs.label);
};
protected:
virtual void warn_once(const char *name) override;
void validate_qname(void);

View file

@ -82,9 +82,6 @@ extern int parser_token;
WARN_UNEXPECTED | WARN_FORMAT | WARN_MISSING | \
WARN_OVERRIDE | WARN_INCLUDE)
extern dfaflags_t warnflags;
extern dfaflags_t werrflags;
typedef enum pattern_t pattern_t;
@ -99,6 +96,8 @@ struct value_list {
struct value_list *next;
};
int cmp_value_list(value_list *lhs, value_list *rhs);
struct cond_entry {
char *name;
int eq; /* where equals was used in specifying list */
@ -360,7 +359,6 @@ extern int conf_quiet;
extern int names_only;
extern int option;
extern int current_lineno;
extern dfaflags_t dfaflags;
extern const char *progname;
extern char *profilename;
extern char *profile_ns;
@ -372,7 +370,7 @@ extern IncludeCache_t *g_includecache;
extern void pwarnf(bool werr, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
extern void common_warn_once(const char *name, const char *msg, const char **warned_name);
#define pwarn(F, args...) do { if (warnflags & (F)) pwarnf((werrflags & (F)), ## args); } while (0)
#define pwarn(F, args...) do { if (parseopts.warn & (F)) pwarnf((parseopts.Werror & (F)), ## args); } while (0)
/* from parser_main (cannot be used in tst builds) */
extern int force_complain;
@ -454,6 +452,8 @@ extern struct cod_entry *new_entry(char *id, perms_t perms, char *link_id);
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
extern int str_to_boolean(const char* str);
extern int null_strcmp(const char *s1, const char *s2);
extern bool strcomp (const char *lhs, const char *rhs);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list);
void debug_cod_entries(struct cod_entry *list);

View file

@ -89,9 +89,6 @@ int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_DIFF_ENCODE);
dfaflags_t warnflags = DEFAULT_WARNINGS;
dfaflags_t werrflags = 0;
const char *progname = __FILE__;
char *profile_ns = NULL;
@ -102,6 +99,14 @@ FILE *ofile = NULL;
IncludeCache_t *g_includecache;
optflags parseopts = {
.control = (optflags_t)(CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_DIFF_ENCODE | CONTROL_RULE_MERGE),
.dump = 0,
.warn = DEFAULT_WARNINGS,
.Werror = 0
};
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
@ -140,8 +145,8 @@ void pwarnf(bool werr, const char *fmt, ...)
/* do we want to warn once/profile or just once per compile?? */
void common_warn_once(const char *name, const char *msg, const char **warned_name)
{
if ((warnflags & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
if (werrflags & WARN_RULE_NOT_ENFORCED)
if ((parseopts.warn & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
cerr << "Warning converted to Error";
else
cerr << "Warning";
@ -154,6 +159,6 @@ void common_warn_once(const char *name, const char *msg, const char **warned_nam
*warned_name = name;
}
if (werrflags & WARN_RULE_NOT_ENFORCED)
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
exit(1);
}

View file

@ -82,6 +82,9 @@ int abort_on_error = 0; /* stop processing profiles if error */
int skip_bad_cache_rebuild = 0;
int mru_skip_cache = 1;
bool O_rule_merge = true;
bool D_rule_merge = false;
/* for jobs_max and jobs
* LONG_MAX : no limit
* LONG_MIN : auto = detect system processing cores
@ -274,6 +277,7 @@ optflag_table_t warnflag_table[] = {
{ 0, NULL, NULL, 0 },
};
/* Parse comma separated cachelocations. Commas can be escaped by \, */
static int parse_cacheloc(const char *arg, const char **cacheloc, int max_size)
{
@ -497,7 +501,7 @@ static int process_arg(int c, char *optarg)
} else if (strcmp(optarg, "Optimize") == 0 ||
strcmp(optarg, "optimize") == 0 ||
strcmp(optarg, "O") == 0) {
flagtable_help("-O ", "", progname, optflag_table);
flagtable_help("-O ", "", progname, dfaoptflag_table);
} else if (strcmp(optarg, "warn") == 0) {
flagtable_help("--warn=", "", progname, warnflag_table);
} else if (strcmp(optarg, "Werror") == 0) {
@ -568,13 +572,13 @@ static int process_arg(int c, char *optarg)
if (!optarg) {
dump_vars = 1;
} else if (strcmp(optarg, "show") == 0) {
print_flags("dump", dumpflag_table, dfaflags);
print_flags("dump", dumpflag_table, parseopts.dump);
} else if (strcmp(optarg, "variables") == 0) {
dump_vars = 1;
} else if (strcmp(optarg, "expanded-variables") == 0) {
dump_expanded_vars = 1;
} else if (!handle_flag_table(dumpflag_table, optarg,
&dfaflags)) {
&parseopts.dump)) {
PERROR("%s: Invalid --Dump option %s\n",
progname, optarg);
exit(1);
@ -582,9 +586,9 @@ static int process_arg(int c, char *optarg)
break;
case 'O':
if (strcmp(optarg, "show") == 0) {
print_flags("Optimize", optflag_table, dfaflags);
} else if (!handle_flag_table(optflag_table, optarg,
&dfaflags)) {
print_flags("Optimize", dfaoptflag_table, parseopts.control);
} else if (!handle_flag_table(dfaoptflag_table, optarg,
&parseopts.control)) {
PERROR("%s: Invalid --Optimize option %s\n",
progname, optarg);
exit(1);
@ -665,7 +669,7 @@ static int process_arg(int c, char *optarg)
case 'q':
conf_verbose = 0;
conf_quiet = 1;
warnflags = 0;
parseopts.warn = 0;
break;
case 'v':
conf_verbose = 1;
@ -723,9 +727,9 @@ static int process_arg(int c, char *optarg)
break;
case ARG_WARN:
if (strcmp(optarg, "show") == 0) {
print_flags("warn", warnflag_table, warnflags);
print_flags("warn", warnflag_table, parseopts.warn);
} else if (!handle_flag_table(warnflag_table, optarg,
&warnflags)) {
&parseopts.warn)) {
PERROR("%s: Invalid --warn option %s\n",
progname, optarg);
exit(1);
@ -733,18 +737,18 @@ static int process_arg(int c, char *optarg)
break;
case ARG_WERROR:
if (!optarg) {
werrflags = -1;
parseopts.Werror = -1;
} else if (strcmp(optarg, "show") == 0) {
print_flags("Werror", warnflag_table, werrflags);
print_flags("Werror", warnflag_table, parseopts.Werror);
} else if (optarg && !handle_flag_table(warnflag_table, optarg,
&werrflags)) {
&parseopts.Werror)) {
PERROR("%s: Invalid --Werror option %s\n",
progname, optarg);
exit(1);
}
break;
case ARG_DEBUG_CACHE:
warnflags |= WARN_DEBUG_CACHE;
parseopts.warn |= WARN_DEBUG_CACHE;
break;
case 'j':
jobs = process_jobs_arg("-j", optarg);
@ -1530,7 +1534,7 @@ static bool get_kernel_features(struct aa_features **features)
if (!kernel_supports_diff_encode)
/* clear diff_encode because it is not supported */
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
parseopts.control &= ~CONTROL_DFA_DIFF_ENCODE;
return true;
}

View file

@ -72,7 +72,7 @@ static int process_file_entries(Profile *prof)
table = (struct cod_entry **) malloc(sizeof(struct cod_entry *) * (count + 1));
if (!table) {
PERROR(_("Couldn't merge entries. Out of Memory\n"));
return ENOMEM;
return -ENOMEM;
}
for (cur = prof->entries, n = 0; cur; cur = cur->next, n++)
@ -84,6 +84,7 @@ static int process_file_entries(Profile *prof)
prof->entries = table[0];
free(table);
count = 0;
/* walk the sorted table merging similar entries */
for (cur = prof->entries, next = cur->next; next; next = cur->next) {
if (file_comp(&cur, &next) != 0) {
@ -102,12 +103,24 @@ static int process_file_entries(Profile *prof)
next->next = NULL;
free_cod_entries(next);
count++;
}
return 0;
return count;
}
int profile_merge_rules(Profile *prof)
{
return process_file_entries(prof);
if (!(parseopts.control & CONTROL_RULE_MERGE))
return 0;
int res, tmp = process_file_entries(prof);
if (tmp < 0)
return -tmp;
res = prof->merge_rules();
if (res < 0)
return -res;
if (parseopts.dump & DUMP_RULE_MERGE)
fprintf(stderr, "RULE MERGE: deleted %d file rules, %d rules\n", tmp, res);
return 0;
}

View file

@ -34,6 +34,8 @@
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
#include <algorithm>
#include "capability.h"
#include "lib.h"
#include "parser.h"
@ -271,6 +273,25 @@ static const char *strn_token(const char *str, size_t &len)
return start;
}
int null_strcmp(const char *s1, const char *s2)
{
if (s1) {
if (s2)
return strcmp(s1, s2);
return 1;
} else if (s2) {
return -1;
}
// both null
return 0;
}
bool strcomp (const char *lhs, const char *rhs)
{
return null_strcmp(lhs, rhs) < 0;
}
/*
* Returns: -1: error
* 0: no change - capability already in table
@ -1065,6 +1086,50 @@ void debug_cod_entries(struct cod_entry *list)
}
}
// these need to move to stl
int ordered_cmp_value_list(value_list *lhs, value_list *rhs)
{
std::vector<const char *> lhstable;
std::vector<const char *> rhstable;
struct value_list *entry;
list_for_each(lhs, entry) {
lhstable.push_back(entry->value);
}
list_for_each(rhs, entry) {
rhstable.push_back(entry->value);
}
int res = lhstable.size() - rhstable.size();
if (res)
return res;
std::sort(lhstable.begin(), lhstable.end(), strcomp);
std::sort(rhstable.begin(), rhstable.end(), strcomp);
for (unsigned long i = 0; i < lhstable.size(); i++) {
res = null_strcmp(lhstable[i], rhstable[i]);
if (res)
return res;
}
return 0;
}
int cmp_value_list(value_list *lhs, value_list *rhs)
{
if (lhs) {
if (rhs) {
return ordered_cmp_value_list(lhs, rhs);
}
return 1;
} else if (rhs) {
return -1;
}
return 0;
}
struct value_list *new_value_list(char *value)
{
struct value_list *val = (struct value_list *) calloc(1, sizeof(struct value_list));

View file

@ -28,7 +28,7 @@
/* #define DEBUG */
#include "common_optarg.h"
#include "lib.h"
#include "parser.h"
#include "profile.h"
@ -128,7 +128,7 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
sptr = aare;
if (dfaflags & DFA_DUMP_RULE_EXPR)
if (parseopts.dump & DUMP_DFA_RULE_EXPR)
fprintf(stderr, "aare: %s -> ", aare);
if (anchor)
@ -427,7 +427,7 @@ out:
if (ret == FALSE)
ptype = ePatternInvalid;
if (dfaflags & DFA_DUMP_RULE_EXPR)
if (parseopts.dump & DUMP_DFA_RULE_EXPR)
fprintf(stderr, "%s\n", pcre.c_str());
return ptype;
@ -507,7 +507,7 @@ static int process_profile_name_xmatch(Profile *prof)
aare_rules *rules = new aare_rules();
if (!rules)
return FALSE;
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) {
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
delete rules;
return FALSE;
}
@ -520,7 +520,7 @@ static int process_profile_name_xmatch(Profile *prof)
ptype = convert_aaregex_to_pcre(alt->name, 0,
glob_default,
tbuf, &len);
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) {
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
delete rules;
return FALSE;
}
@ -562,14 +562,14 @@ static int process_profile_name_xmatch(Profile *prof)
convert_aaregex_to_pcre(xattr_value, 0,
glob_null, tbuf,
&len);
if (!rules->append_rule(tbuf.c_str(), true, true, dfaflags)) {
if (!rules->append_rule(tbuf.c_str(), true, true, parseopts)) {
delete rules;
return FALSE;
}
}
}
build:
prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, dfaflags, true);
prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, parseopts, true);
delete rules;
if (!prof->xmatch)
return FALSE;
@ -638,13 +638,13 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
!dfarules->add_rule(tbuf.c_str(), entry->rule_mode == RULE_DENY,
entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0,
dfaflags))
parseopts))
return FALSE;
} else if (!is_change_profile_perms(entry->perms)) {
if (!dfarules->add_rule(tbuf.c_str(),
entry->rule_mode == RULE_DENY, entry->perms,
entry->audit == AUDIT_FORCE ? entry->perms : 0,
dfaflags))
parseopts))
return FALSE;
}
@ -667,7 +667,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
perms |= LINK_TO_LINK_SUBSET(perms);
vec[1] = "/[^/].*";
}
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, perms, entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, 2, vec, dfaflags, false))
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, perms, entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, 2, vec, parseopts, false))
return FALSE;
}
if (is_change_profile_perms(entry->perms)) {
@ -678,7 +678,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
int index = 1;
uint32_t onexec_perms = AA_ONEXEC;
if ((warnflags & WARN_RULE_DOWNGRADED) && entry->audit == AUDIT_FORCE && warn_change_profile) {
if ((parseopts.warn & WARN_RULE_DOWNGRADED) && entry->audit == AUDIT_FORCE && warn_change_profile) {
/* don't have profile name here, so until this code
* gets refactored just throw out a generic warning
*/
@ -720,12 +720,12 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
/* regular change_profile rule */
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY,
AA_CHANGE_PROFILE | onexec_perms,
0, index - 1, &vec[1], dfaflags, false))
0, index - 1, &vec[1], parseopts, false))
return FALSE;
/* onexec rules - both rules are needed for onexec */
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms,
0, 1, vec, dfaflags, false))
0, 1, vec, parseopts, false))
return FALSE;
/**
@ -734,7 +734,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
*/
onexec_perms |= (entry->perms & (AA_EXEC_BITS | ALL_AA_EXEC_UNSAFE));
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms,
0, index, vec, dfaflags, false))
0, index, vec, parseopts, false))
return FALSE;
}
return TRUE;
@ -770,7 +770,7 @@ int process_profile_regex(Profile *prof)
if (prof->dfa.rules->rule_count > 0) {
int xmatch_len = 0;
prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size,
&xmatch_len, dfaflags, true);
&xmatch_len, parseopts, true);
delete prof->dfa.rules;
prof->dfa.rules = NULL;
if (!prof->dfa.dfa)
@ -848,7 +848,7 @@ int clear_and_convert_entry(std::string& buffer, char *entry)
int post_process_policydb_ents(Profile *prof)
{
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED)
if ((*i)->skip())
continue;
if ((*i)->gen_policy_re(*prof) == RULE_ERROR)
return FALSE;
@ -875,7 +875,7 @@ static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask,
buf = buffer.str();
if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS),
audit ? map_perms(AA_VALID_NET_PERMS) : 0,
dfaflags))
parseopts))
return false;
return true;
@ -969,44 +969,44 @@ int process_profile_policydb(Profile *prof)
/* note: this activates fs based unix domain sockets mediation on connect */
if (kernel_abi_version > 5 &&
!prof->policy.rules->add_rule(mediates_file, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_file, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_mount &&
!prof->policy.rules->add_rule(mediates_mount, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_mount, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_dbus &&
!prof->policy.rules->add_rule(mediates_dbus, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_dbus, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_signal &&
!prof->policy.rules->add_rule(mediates_signal, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_signal, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_ptrace &&
!prof->policy.rules->add_rule(mediates_ptrace, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_ptrace, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_networkv8 &&
!prof->policy.rules->add_rule(mediates_netv8, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_netv8, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_unix &&
(!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, dfaflags) ||
!prof->policy.rules->add_rule(mediates_net_unix, 0, AA_MAY_READ, 0, dfaflags)))
(!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, parseopts) ||
!prof->policy.rules->add_rule(mediates_net_unix, 0, AA_MAY_READ, 0, parseopts)))
goto out;
if (features_supports_userns &&
!prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_posix_mqueue &&
!prof->policy.rules->add_rule(mediates_posix_mqueue, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_posix_mqueue, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_sysv_mqueue &&
!prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (features_supports_io_uring &&
!prof->policy.rules->add_rule(mediates_io_uring, 0, AA_MAY_READ, 0, dfaflags))
!prof->policy.rules->add_rule(mediates_io_uring, 0, AA_MAY_READ, 0, parseopts))
goto out;
if (prof->policy.rules->rule_count > 0) {
int xmatch_len = 0;
prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size,
&xmatch_len, dfaflags, false);
&xmatch_len, parseopts, false);
delete prof->policy.rules;
prof->policy.rules = NULL;

View file

@ -267,7 +267,7 @@ static int process_variables_in_entries(struct cod_entry *entry_list)
static int process_variables_in_rules(Profile &prof)
{
for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED)
if ((*i)->skip())
continue;
int error = (*i)->expand_variables();
if (error)

View file

@ -121,38 +121,39 @@ Profile::~Profile()
free(net.quiet);
}
static bool comp (rule_t *lhs, rule_t *rhs) { return (*lhs < *rhs); }
static bool comp (rule_t *lhs, rule_t *rhs)
{
return (*lhs < *rhs);
}
bool Profile::merge_rules(void)
// TODO: move to block rule
// returns number of rules merged
// returns negative number on error
int Profile::merge_rules(void)
{
int count = 0;
std::vector<rule_t *> table;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); ) {
if ((*i)->is_mergeable())
count++;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
if ((*i)->is_mergeable() && !(*i)->skip())
table.push_back(*i);
}
if (count < 2)
if (table.size() < 2)
return 0;
std::vector<rule_t *> table(count);
int n = 0;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); ) {
if ((*i)->is_mergeable())
table[n++] = *i;
}
std::sort(table.begin(), table.end(), comp);
for (int i = 0, j = 1; j < count; j++) {
unsigned long n = table.size();
for (unsigned long i = 0, j = 1; j < n; j++) {
if (table[j]->skip())
continue;
if (table[i]->cmp(*table[j]) == 0) {
if (!table[i]->merge(*table[j]))
return false;
if (table[i]->merge(*table[j]))
count++;
continue;
}
i = j;
}
return true;
return count;
}
@ -318,7 +319,7 @@ void post_process_file_entries(Profile *prof)
void post_process_rule_entries(Profile *prof)
{
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED)
if ((*i)->skip())
continue;
(*i)->post_parse_profile(*prof);
}

View file

@ -249,7 +249,7 @@ public:
* Requires the merged rules have customized methods
* cmp(), is_mergeable() and merge()
*/
virtual bool merge_rules(void);
virtual int merge_rules(void);
void dump(void)
{

View file

@ -134,7 +134,7 @@ int ptrace_rule::gen_policy_re(Profile &prof)
buf = buffer.str();
if (perms & AA_VALID_PTRACE_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags))
parseopts))
goto fail;
}

View file

@ -52,6 +52,16 @@ public:
return true;
};
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
return null_strcmp(peer_label,
(rule_cast<ptrace_rule const &>(rhs)).peer_label);
};
protected:
virtual void warn_once(const char *name) override;
};

View file

@ -38,6 +38,10 @@ class Profile;
// RULE_TYPE_CLASS needs to be last because various class follow it
#define RULE_TYPE_CLASS 3
// rule_cast should only be used after a comparison of rule_type to ensure
// that it is valid. Change to dynamic_cast for debugging
//#define rule_cast dynamic_cast
#define rule_cast static_cast
typedef enum { RULE_FLAG_NONE = 0,
RULE_FLAG_DELETED = 1, // rule deleted - skip
@ -48,16 +52,39 @@ typedef enum { RULE_FLAG_NONE = 0,
// added because it is implied
} rule_flags_t;
inline rule_flags_t operator|(rule_flags_t a, rule_flags_t b)
{
return static_cast<rule_flags_t>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
}
inline rule_flags_t operator&(rule_flags_t a, rule_flags_t b)
{
return static_cast<rule_flags_t>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
}
inline rule_flags_t& operator|=(rule_flags_t &a, const rule_flags_t &b)
{
a = a | b;
return a;
}
class rule_t {
public:
int rule_type;
rule_flags_t flags;
rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE) { }
rule_t *removed_by;
rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE), removed_by(NULL) { }
virtual ~rule_t() { };
bool is_type(int type) { return rule_type == type; }
// rule has been marked as should be skipped by regular processing
bool skip()
{
return (flags & RULE_FLAG_DELETED);
}
//virtual bool operator<(rule_t const &rhs)const = 0;
virtual std::ostream &dump(std::ostream &os) = 0;
@ -75,15 +102,37 @@ public:
// to support expansion in include names and profile names
virtual int expand_variables(void) = 0;
// called by duplicate rule merge/elimination after final expand_vars
virtual bool is_mergeable(void) { return false; }
virtual int cmp(rule_t const &rhs) const {
return rule_type < rhs.rule_type;
return rule_type - rhs.rule_type;
}
virtual bool operator<(rule_t const &rhs) const {
return cmp(rhs) < 0;
}
virtual bool merge(rule_t &rhs __attribute__ ((unused))) { return false; };
// called by duplicate rule merge/elimination after final expand_vars
// to get default rule dedup
// child object need to provide
// - cmp, operator<
// - is_mergeable() returning true
// if a child object wants to provide merging of permissions,
// it needs to provide a custom cmp fn that doesn't include
// permissions and a merge routine that does more than flagging
// as dup as below
virtual bool is_mergeable(void) { return false; }
// returns true if merged
virtual bool merge(rule_t &rhs)
{
if (rule_type != rhs.rule_type)
return false;
if (skip() || rhs.skip())
return false;
// default merge is just dedup
flags |= RULE_FLAG_MERGED;
rhs.flags |= (RULE_FLAG_MERGED | RULE_FLAG_DELETED);
rhs.removed_by = this;
return true;
};
// called late frontend to generate data for regex backend
virtual int gen_policy_re(Profile &prof) = 0;
@ -156,6 +205,32 @@ public:
return os;
}
int cmp(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit)
return -1;
if ((uint) audit > (uint) rhs.audit)
return 1;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return -1;
if ((uint) rule_mode > (uint) rhs.rule_mode)
return 1;
if (owner < rhs.owner)
return -1;
if (owner > rhs.owner)
return 1;
return 0;
}
bool operator<(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit)
return true;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return true;
if (owner < rhs.owner)
return true;
return false;
}
};
class prefix_rule_t: public rule_t, public prefixes {
@ -215,21 +290,32 @@ public:
return true;
}
virtual bool operator<(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit)
return true;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return true;
if (owner < rhs.owner)
return true;
return false;
int cmp(prefixes const &rhs) const {
return prefixes::cmp(rhs);
}
virtual bool operator<(prefix_rule_t const &rhs) const {
virtual bool operator<(prefixes const &rhs) const {
const prefixes *ptr = this;
return *ptr < rhs;
}
virtual int cmp(rule_t const &rhs) const {
int res = rule_t::cmp(rhs);
if (res)
return res;
prefix_rule_t const &pr = rule_cast<prefix_rule_t const &>(rhs);
const prefixes *lhsptr = this, *rhsptr = &pr;
return lhsptr->cmp(*rhsptr);
}
virtual bool operator<(rule_t const &rhs) const {
if (rule_type < rhs.rule_type)
return true;
if (rhs.rule_type < rule_type)
return false;
return *this < (prefixes const &)rhs;
prefix_rule_t const &pr = rule_cast<prefix_rule_t const &>(rhs);
const prefixes *rhsptr = &pr;
return *this < *rhsptr;
}
virtual ostream &dump(ostream &os) {
@ -247,6 +333,16 @@ public:
int aa_class(void) { return rule_type - RULE_TYPE_CLASS; }
/* inherit cmp */
/* we do not inherit operator< from so class_rules children
* can in herit the generic one that redirects to cmp()
* that does get overriden
*/
virtual bool operator<(rule_t const &rhs) const {
return cmp(rhs) < 0;
}
virtual ostream &dump(ostream &os) {
prefix_rule_t::dump(os);
@ -261,6 +357,13 @@ class perms_rule_t: public class_rule_t {
public:
perms_rule_t(int c): class_rule_t(c), perms(0) { };
virtual int cmp(rule_t const &rhs) const {
int res = class_rule_t::cmp(rhs);
if (res)
return res;
return perms - (rule_cast<perms_rule_t const &>(rhs)).perms;
}
/* defaut perms, override/mask off if none default used */
virtual ostream &dump(ostream &os) {

View file

@ -230,6 +230,35 @@ int signal_rule::expand_variables(void)
return expand_entry_variables(&peer_label);
}
static int cmp_set_int(Signals const &lhs, Signals const &rhs)
{
int res = lhs.size() - rhs.size();
if (res)
return res;
for (Signals::iterator i = lhs.begin(),
j = rhs.begin();
i != lhs.end(); i++, j++) {
res = *i - *j;
if (res)
return res;
}
return 0;
}
int signal_rule::cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
signal_rule const &trhs = rule_cast<signal_rule const &>(rhs);
res = null_strcmp(peer_label, trhs.peer_label);
if (res)
return res;
return cmp_set_int(signals, trhs.signals);
}
void signal_rule::warn_once(const char *name)
{
rule_t::warn_once(name, "signal rules not enforced");
@ -288,7 +317,7 @@ int signal_rule::gen_policy_re(Profile &prof)
buf = buffer.str();
if (perms & (AA_MAY_SEND | AA_MAY_RECEIVE)) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags))
parseopts))
goto fail;
}

View file

@ -57,6 +57,9 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const;
protected:
virtual void warn_once(const char *name) override;
};

View file

@ -97,7 +97,7 @@ int userns_rule::gen_policy_re(Profile &prof)
if (perms & AA_VALID_USERNS_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0,
dfaflags))
parseopts))
goto fail;
}

View file

@ -42,6 +42,12 @@ public:
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
return perms_rule_t::cmp(rhs);
};
protected:
virtual void warn_once(const char *name) override;
};