diff --git a/parser/Makefile b/parser/Makefile index 3280ce528..d112e0307 100644 --- a/parser/Makefile +++ b/parser/Makefile @@ -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 \ diff --git a/parser/af_rule.h b/parser/af_rule.h index 8ff77e738..bc06e6193 100644 --- a/parser/af_rule.h +++ b/parser/af_rule.h @@ -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(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 */ diff --git a/parser/af_unix.cc b/parser/af_unix.cc index dfd7f427e..117688692 100644 --- a/parser/af_unix.cc +++ b/parser/af_unix.cc @@ -24,6 +24,7 @@ #include #include +#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; } diff --git a/parser/af_unix.h b/parser/af_unix.h index 27cb662bb..ff10565d0 100644 --- a/parser/af_unix.h +++ b/parser/af_unix.h @@ -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(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; }; diff --git a/parser/common_flags.h b/parser/common_flags.h new file mode 100644 index 000000000..68421a32a --- /dev/null +++ b/parser/common_flags.h @@ -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 */ diff --git a/parser/common_optarg.c b/parser/common_optarg.c index bd77b0b1b..7bf327378 100644 --- a/parser/common_optarg.c +++ b/parser/common_optarg.c @@ -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; diff --git a/parser/common_optarg.h b/parser/common_optarg.h index d10e70109..4e44f5b8f 100644 --- a/parser/common_optarg.h +++ b/parser/common_optarg.h @@ -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); diff --git a/parser/dbus.cc b/parser/dbus.cc index 8c0b8dbee..0371f66c1 100644 --- a/parser/dbus.cc +++ b/parser/dbus.cc @@ -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; } diff --git a/parser/dbus.h b/parser/dbus.h index f35797e9a..a273bffe5 100644 --- a/parser/dbus.h +++ b/parser/dbus.h @@ -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(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; }; diff --git a/parser/io_uring.cc b/parser/io_uring.cc index aef4b8899..d7715b69d 100644 --- a/parser/io_uring.cc +++ b/parser/io_uring.cc @@ -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; } diff --git a/parser/io_uring.h b/parser/io_uring.h index 7644403c7..d363c5443 100644 --- a/parser/io_uring.h +++ b/parser/io_uring.h @@ -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(rhs)).label); + }; + protected: virtual void warn_once(const char *name) override; }; diff --git a/parser/libapparmor_re/aare_rules.cc b/parser/libapparmor_re/aare_rules.cc index b250e1013..385e4508a 100644 --- a/parser/libapparmor_re/aare_rules.cc +++ b/parser/libapparmor_re/aare_rules.cc @@ -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 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, ""); } diff --git a/parser/libapparmor_re/aare_rules.h b/parser/libapparmor_re/aare_rules.h index ab88f0af0..71087b887 100644 --- a/parser/libapparmor_re/aare_rules.h +++ b/parser/libapparmor_re/aare_rules.h @@ -23,6 +23,7 @@ #include +#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); }; diff --git a/parser/libapparmor_re/apparmor_re.h b/parser/libapparmor_re/apparmor_re.h index 8e9fd9984..243d728ae 100644 --- a/parser/libapparmor_re/apparmor_re.h +++ b/parser/libapparmor_re/apparmor_re.h @@ -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 */ diff --git a/parser/libapparmor_re/chfa.cc b/parser/libapparmor_re/chfa.cc index 235df335b..658218958 100644 --- a/parser/libapparmor_re/chfa.cc +++ b/parser/libapparmor_re/chfa.cc @@ -49,9 +49,10 @@ void CHFA::init_free_list(vector > &free_list, /** * new Construct the transition table. */ -CHFA::CHFA(DFA &dfa, map &eq, dfaflags_t flags): eq(eq) +CHFA::CHFA(DFA &dfa, map &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 &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 &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 &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 &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 &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, diff --git a/parser/libapparmor_re/chfa.h b/parser/libapparmor_re/chfa.h index d0081ae56..9548080b2 100644 --- a/parser/libapparmor_re/chfa.h +++ b/parser/libapparmor_re/chfa.h @@ -37,7 +37,7 @@ class CHFA { typedef vector > DefaultBase; typedef vector > NextCheck; public: - CHFA(DFA &dfa, map &eq, dfaflags_t flags); + CHFA(DFA &dfa, map &eq, optflags const &opts); void dump(ostream & os); void flex_table(ostream &os, const char *name); void init_free_list(vector > &free_list, diff --git a/parser/libapparmor_re/expr-tree.cc b/parser/libapparmor_re/expr-tree.cc index 7dc18b041..53c640d4e 100644 --- a/parser/libapparmor_re/expr-tree.cc +++ b/parser/libapparmor_re/expr-tree.cc @@ -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, diff --git a/parser/libapparmor_re/expr-tree.h b/parser/libapparmor_re/expr-tree.h index 3530eb167..2aec980d9 100644 --- a/parser/libapparmor_re/expr-tree.h +++ b/parser/libapparmor_re/expr-tree.h @@ -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); diff --git a/parser/libapparmor_re/hfa.cc b/parser/libapparmor_re/hfa.cc index e1ef1803b..ebb0f4b70 100644 --- a/parser/libapparmor_re/hfa.cc +++ b/parser/libapparmor_re/hfa.cc @@ -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 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, Partition *> perm_map; list 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::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 DFA::equivalence_classes(dfaflags_t flags) +map DFA::equivalence_classes(optflags const &opts) { map classes; transchar next_class = 1; @@ -1251,7 +1251,7 @@ map 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; diff --git a/parser/libapparmor_re/hfa.h b/parser/libapparmor_re/hfa.h index 451d74659..738efc076 100644 --- a/parser/libapparmor_re/hfa.h +++ b/parser/libapparmor_re/hfa.h @@ -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 &relmap, Partition &chain, State *state, unsigned int &count, unsigned int &total, @@ -318,19 +318,19 @@ class DFA { list 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 equivalence_classes(dfaflags_t flags); + map equivalence_classes(optflags const &flags); void apply_equivalence_classes(map &eq); unsigned int diffcount; diff --git a/parser/mount.cc b/parser/mount.cc index 7b551a6fd..f61bf3435 100644 --- a/parser/mount.cc +++ b/parser/mount.cc @@ -632,6 +632,51 @@ int mnt_rule::expand_variables(void) return 0; } +static int cmp_vec_int(std::vector const &lhs, + std::vector 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(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++; } diff --git a/parser/mount.h b/parser/mount.h index f28196c92..fa1794f87 100644 --- a/parser/mount.h +++ b/parser/mount.h @@ -21,6 +21,7 @@ #include #include +#include #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; }; diff --git a/parser/mqueue.cc b/parser/mqueue.cc index 86ab193ae..86c7c1d06 100644 --- a/parser/mqueue.cc +++ b/parser/mqueue.cc @@ -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; } } diff --git a/parser/mqueue.h b/parser/mqueue.h index 24265666c..da5e414b8 100644 --- a/parser/mqueue.h +++ b/parser/mqueue.h @@ -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(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); diff --git a/parser/parser.h b/parser/parser.h index 0399db8be..95b177060 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -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); diff --git a/parser/parser_common.c b/parser/parser_common.c index 75b637e19..f117d2ffa 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -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); } diff --git a/parser/parser_main.c b/parser/parser_main.c index 00a8b52fb..d46eb8ef2 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -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; } diff --git a/parser/parser_merge.c b/parser/parser_merge.c index 77c295e19..523933cab 100644 --- a/parser/parser_merge.c +++ b/parser/parser_merge.c @@ -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; } diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 651b4ce98..ed80d068f 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -34,6 +34,8 @@ #include #include +#include + #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 lhstable; + std::vector 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)); diff --git a/parser/parser_regex.c b/parser/parser_regex.c index deb704f36..578f6aa44 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -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; diff --git a/parser/parser_variable.c b/parser/parser_variable.c index 15978538b..61cdcc3a8 100644 --- a/parser/parser_variable.c +++ b/parser/parser_variable.c @@ -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) diff --git a/parser/profile.cc b/parser/profile.cc index 27fe570ee..9eeb39018 100644 --- a/parser/profile.cc +++ b/parser/profile.cc @@ -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 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 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); } diff --git a/parser/profile.h b/parser/profile.h index 683e97695..c73b59faf 100644 --- a/parser/profile.h +++ b/parser/profile.h @@ -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) { diff --git a/parser/ptrace.cc b/parser/ptrace.cc index 8d16f98ca..3729b7606 100644 --- a/parser/ptrace.cc +++ b/parser/ptrace.cc @@ -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; } diff --git a/parser/ptrace.h b/parser/ptrace.h index 89881de91..b129c5795 100644 --- a/parser/ptrace.h +++ b/parser/ptrace.h @@ -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(rhs)).peer_label); + }; + protected: virtual void warn_once(const char *name) override; }; diff --git a/parser/rule.h b/parser/rule.h index 469b91580..a040333ba 100644 --- a/parser/rule.h +++ b/parser/rule.h @@ -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(static_cast(a) | static_cast(b)); +} + +inline rule_flags_t operator&(rule_flags_t a, rule_flags_t b) +{ + return static_cast(static_cast(a) & static_cast(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(rhs); + const prefixes *lhsptr = this, *rhsptr = ≺ + 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(rhs); + const prefixes *rhsptr = ≺ + 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(rhs)).perms; + } + /* defaut perms, override/mask off if none default used */ virtual ostream &dump(ostream &os) { diff --git a/parser/signal.cc b/parser/signal.cc index 5e3eff701..09e144ba0 100644 --- a/parser/signal.cc +++ b/parser/signal.cc @@ -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(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; } diff --git a/parser/signal.h b/parser/signal.h index 391c8b34d..e5df3d2a7 100644 --- a/parser/signal.h +++ b/parser/signal.h @@ -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; }; diff --git a/parser/userns.cc b/parser/userns.cc index c868bebb0..f70f8e84e 100644 --- a/parser/userns.cc +++ b/parser/userns.cc @@ -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; } diff --git a/parser/userns.h b/parser/userns.h index b72577124..0c7f8a5c1 100644 --- a/parser/userns.h +++ b/parser/userns.h @@ -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; };