From 5ff00bba3a01aeee0cfe145e66062c3900b5abab Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 9 May 2024 19:48:37 -0700 Subject: [PATCH 1/9] parser: drop unused hash minimization remnants Hash minimization was removed in f0b154528 Fix dfa minimization however some remnants of minimization remained. A comment and the use of the hash but only as a 0 value. Drop this dead code and comment. Signed-off-by: John Johansen --- parser/libapparmor_re/hfa.cc | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/parser/libapparmor_re/hfa.cc b/parser/libapparmor_re/hfa.cc index e0164c501..16dffe4e1 100644 --- a/parser/libapparmor_re/hfa.cc +++ b/parser/libapparmor_re/hfa.cc @@ -652,35 +652,26 @@ typedef pair uint128_t; /* minimize the number of dfa states */ void DFA::minimize(optflags const &opts) { - map, Partition *> perm_map; + map perm_map; list partitions; /* Set up the initial partitions * minimum of - 1 non accepting, and 1 accepting - * if trans hashing is used the accepting and non-accepting partitions - * can be further split based on the number and type of transitions - * a state makes. - * If permission hashing is enabled the accepting partitions can - * be further divided by permissions. This can result in not - * obtaining a truly minimized dfa but comes close, and can speedup - * minimization. */ int accept_count = 0; int final_accept = 0; for (Partition::iterator i = states.begin(); i != states.end(); i++) { - size_t hash = 0; - uint128_t permtype; - permtype.first = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32); - permtype.second = (uint64_t) (*i)->perms.allow | ((uint64_t) (*i)->perms.prompt << 32); - pair group = make_pair(permtype, hash); - map, Partition *>::iterator p = perm_map.find(group); + uint128_t group; + group.first = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32); + group.second = (uint64_t) (*i)->perms.allow | ((uint64_t) (*i)->perms.prompt << 32); + map::iterator p = perm_map.find(group); if (p == perm_map.end()) { Partition *part = new Partition(); part->push_back(*i); perm_map.insert(make_pair(group, part)); partitions.push_back(part); (*i)->partition = part; - if (permtype.first || permtype.second) + if (group.first || group.second) accept_count++; } else { (*i)->partition = p->second; From 1fa45b7c1f8d3e744808ad2eef52110f94987092 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 9 May 2024 20:00:41 -0700 Subject: [PATCH 2/9] parser: dfa minimization prepare for extended permissions Instead of compressing the permission set into 128 bit and using that as the index in the permission map, just use the permissions directly as the index into the permission map. Note: this will break equality and minimization tests. Because deny is not being cleared it will result in more partitions in the initial setup. This will be addressed and the tests will be fixed in a follow on patch. Signed-off-by: John Johansen --- parser/libapparmor_re/hfa.cc | 12 ++++-------- parser/libapparmor_re/hfa.h | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/parser/libapparmor_re/hfa.cc b/parser/libapparmor_re/hfa.cc index 16dffe4e1..affd68e70 100644 --- a/parser/libapparmor_re/hfa.cc +++ b/parser/libapparmor_re/hfa.cc @@ -647,12 +647,11 @@ int DFA::apply_and_clear_deny(void) } -typedef pair uint128_t; /* minimize the number of dfa states */ void DFA::minimize(optflags const &opts) { - map perm_map; + map perm_map; list partitions; /* Set up the initial partitions @@ -661,17 +660,14 @@ void DFA::minimize(optflags const &opts) int accept_count = 0; int final_accept = 0; for (Partition::iterator i = states.begin(); i != states.end(); i++) { - uint128_t group; - group.first = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32); - group.second = (uint64_t) (*i)->perms.allow | ((uint64_t) (*i)->perms.prompt << 32); - map::iterator p = perm_map.find(group); + map::iterator p = perm_map.find((*i)->perms); if (p == perm_map.end()) { Partition *part = new Partition(); part->push_back(*i); - perm_map.insert(make_pair(group, part)); + perm_map.insert(make_pair((*i)->perms, part)); partitions.push_back(part); (*i)->partition = part; - if (group.first || group.second) + if ((*i)->perms.is_accept()) accept_count++; } else { (*i)->partition = p->second; diff --git a/parser/libapparmor_re/hfa.h b/parser/libapparmor_re/hfa.h index 1aa599266..56dd45ebf 100644 --- a/parser/libapparmor_re/hfa.h +++ b/parser/libapparmor_re/hfa.h @@ -53,7 +53,7 @@ class perms_t { public: perms_t(void): allow(0), deny(0), audit(0), quiet(0), exact(0) { }; - bool is_accept(void) { return (allow | prompt | audit | quiet); } + bool is_accept(void) { return (allow | deny | prompt | audit | quiet); } void dump_header(ostream &os) { From 2737cb2c2befcb0670a943cad6b2473280e8ce6e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 10 May 2024 03:06:22 -0700 Subject: [PATCH 3/9] parser: minimization - remove unnecessary second minimization pass Moving apply_and_clear_deny() before the first minimization pass, which was necessary to propperly support building accept information for older none extended permission dfas, allows us to also get rid of doing a second minimization pass if we want to force clearing explicit deny info from extended permission tables. Signed-off-by: John Johansen --- parser/af_unix.cc | 19 ++- parser/dbus.cc | 23 ++-- parser/io_uring.cc | 14 ++- parser/libapparmor_re/aare_rules.cc | 46 +++---- parser/libapparmor_re/aare_rules.h | 27 ++-- parser/libapparmor_re/expr-tree.h | 9 +- parser/libapparmor_re/hfa.cc | 56 ++++++--- parser/libapparmor_re/hfa.h | 26 +++- parser/mount.cc | 27 ++-- parser/mqueue.cc | 26 +++- parser/network.cc | 30 +++-- parser/parser.h | 1 + parser/parser_misc.c | 2 + parser/parser_regex.c | 55 ++++---- parser/ptrace.cc | 7 +- parser/rule.h | 6 +- parser/signal.cc | 3 +- .../features.extended-perms-no-policydb | 68 ++++++++++ .../features.extended-perms-policydb | 117 ++++++++++++++++++ parser/tst/minimize.sh | 62 ++++++++-- parser/userns.cc | 3 +- 21 files changed, 477 insertions(+), 150 deletions(-) create mode 100644 parser/tst/features_files/features.extended-perms-no-policydb create mode 100644 parser/tst/features_files/features.extended-perms-policydb diff --git a/parser/af_unix.cc b/parser/af_unix.cc index a098ee7f3..29724cd18 100644 --- a/parser/af_unix.cc +++ b/parser/af_unix.cc @@ -344,7 +344,8 @@ int unix_rule::gen_policy_re(Profile &prof) write_to_prot(buffer); if ((mask & AA_NET_CREATE) && !has_peer_conds()) { buf = buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(AA_NET_CREATE), map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0), parseopts)) @@ -369,7 +370,8 @@ int unix_rule::gen_policy_re(Profile &prof) tmp << "\\x00"; buf = tmp.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(AA_NET_BIND), map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0), parseopts)) @@ -394,7 +396,8 @@ int unix_rule::gen_policy_re(Profile &prof) AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD; if (mask & local_mask) { buf = buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(mask & local_mask), map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0), parseopts)) @@ -408,7 +411,9 @@ int unix_rule::gen_policy_re(Profile &prof) /* TODO: backlog conditional: for now match anything*/ tmp << ".."; buf = tmp.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, + if (!prof.policy.rules->add_rule(buf.c_str(), + priority, + rule_mode, map_perms(AA_NET_LISTEN), map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0), parseopts)) @@ -422,6 +427,7 @@ int unix_rule::gen_policy_re(Profile &prof) tmp << ".."; buf = tmp.str(); if (!prof.policy.rules->add_rule(buf.c_str(), + priority, rule_mode, map_perms(mask & AA_NET_OPT), map_perms(audit == AUDIT_FORCE ? @@ -444,7 +450,10 @@ int unix_rule::gen_policy_re(Profile &prof) goto fail; buf = buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, 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/dbus.cc b/parser/dbus.cc index 163d8c396..6af32d4cd 100644 --- a/parser/dbus.cc +++ b/parser/dbus.cc @@ -274,23 +274,24 @@ int dbus_rule::gen_policy_re(Profile &prof) } if (perms & AA_DBUS_BIND) { - if (!prof.policy.rules->add_rule_vec(rule_mode, perms & AA_DBUS_BIND, - audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0, - 2, vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, + perms & AA_DBUS_BIND, + audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0, + 2, vec, parseopts, false)) goto fail; } if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) { - if (!prof.policy.rules->add_rule_vec(rule_mode, - perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE), - audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0, - 6, vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, + perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE), + audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0, + 6, vec, parseopts, false)) goto fail; } if (perms & AA_DBUS_EAVESDROP) { - if (!prof.policy.rules->add_rule_vec(rule_mode, - perms & AA_DBUS_EAVESDROP, - audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0, - 1, vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, + perms & AA_DBUS_EAVESDROP, + audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0, + 1, vec, parseopts, false)) goto fail; } diff --git a/parser/io_uring.cc b/parser/io_uring.cc index 687b6d08c..17fa39614 100644 --- a/parser/io_uring.cc +++ b/parser/io_uring.cc @@ -122,16 +122,18 @@ 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, perms, - audit == AUDIT_FORCE ? perms : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, perms, + audit == AUDIT_FORCE ? perms : 0, + 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, - perms, audit == AUDIT_FORCE ? perms : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), + priority, rule_mode, + perms, audit == AUDIT_FORCE ? perms : 0, + parseopts)) goto fail; } diff --git a/parser/libapparmor_re/aare_rules.cc b/parser/libapparmor_re/aare_rules.cc index 5956c9db5..6892b70a7 100644 --- a/parser/libapparmor_re/aare_rules.cc +++ b/parser/libapparmor_re/aare_rules.cc @@ -44,10 +44,11 @@ aare_rules::~aare_rules(void) expr_map.clear(); } -bool aare_rules::add_rule(const char *rule, rule_mode_t mode, perm32_t perms, - perm32_t audit, optflags const &opts) +bool aare_rules::add_rule(const char *rule, int priority, rule_mode_t mode, + perm32_t perms, perm32_t audit, optflags const &opts) { - return add_rule_vec(mode, perms, audit, 1, &rule, opts, false); + return add_rule_vec(priority, mode, perms, audit, 1, &rule, opts, + false); } void aare_rules::add_to_rules(Node *tree, Node *perms) @@ -71,9 +72,9 @@ static Node *cat_with_oob_separator(Node *l, Node *r) return new CatNode(new CatNode(l, new CharNode(transchar(-1, true))), r); } -bool aare_rules::add_rule_vec(rule_mode_t mode, perm32_t perms, perm32_t audit, - int count, const char **rulev, optflags const &opts, - bool oob) +bool aare_rules::add_rule_vec(int priority, rule_mode_t mode, perm32_t perms, + perm32_t audit, int count, const char **rulev, + optflags const &opts, bool oob) { Node *tree = NULL, *accept; int exact_match; @@ -107,7 +108,7 @@ bool aare_rules::add_rule_vec(rule_mode_t mode, perm32_t perms, perm32_t audit, if (reverse) flip_tree(tree); - accept = unique_perms.insert(mode, perms, audit, exact_match); + accept = unique_perms.insert(priority, mode, perms, audit, exact_match); if (opts.dump & DUMP_DFA_RULE_EXPR) { const char *separator; @@ -124,6 +125,7 @@ bool aare_rules::add_rule_vec(rule_mode_t mode, perm32_t perms, perm32_t audit, cerr << " -> "; tree->dump(cerr); // TODO: split out from prefixes class + cerr << " priority=" << priority; if (mode == RULE_DENY) cerr << " deny"; else if (mode == RULE_PROMPT) @@ -256,6 +258,20 @@ CHFA *aare_rules::create_chfa(int *min_match_len, if (opts.dump & DUMP_DFA_UNIQ_PERMS) dfa.dump_uniq_perms("dfa"); + /* since we are building a chfa, use the info about + * whether the chfa supports extended perms to help + * determine whether we clear the deny info. + * This will let us build the minimal dfa for the + * information supported by the backed + */ + if (!extended_perms || + // TODO: we should drop DFA_MINIMIZE check here but doing + // so changes behavior. Do as a separate patch and fixup + // tests, etc. + ((opts.control & CONTROL_DFA_FILTER_DENY) && + (opts.control & CONTROL_DFA_MINIMIZE))) + dfa.apply_and_clear_deny(); + if (opts.control & CONTROL_DFA_MINIMIZE) { dfa.minimize(opts); @@ -263,22 +279,6 @@ CHFA *aare_rules::create_chfa(int *min_match_len, dfa.dump_uniq_perms("minimized dfa"); } - 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 - * to none accepting partitions - * - * TODO: add this as a tail pass to minimization - * so we don't need to do a full second pass - */ - dfa.minimize(opts); - - if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS) - dfa.dump_uniq_perms("minimized dfa"); - } - if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE) dfa.remove_unreachable(opts); diff --git a/parser/libapparmor_re/aare_rules.h b/parser/libapparmor_re/aare_rules.h index a35ffd9e9..f3232a619 100644 --- a/parser/libapparmor_re/aare_rules.h +++ b/parser/libapparmor_re/aare_rules.h @@ -35,6 +35,7 @@ class UniquePerm { public: + int priority; rule_mode_t mode; bool exact_match; uint32_t perms; @@ -42,6 +43,8 @@ public: bool operator<(UniquePerm const &rhs)const { + if (priority < rhs.priority) + return priority < rhs.priority; if (mode >= rhs.mode) { if (exact_match == rhs.exact_match) { if (perms == rhs.perms) @@ -71,21 +74,21 @@ public: nodes.clear(); } - Node *insert(rule_mode_t mode, uint32_t perms, uint32_t audit, - bool exact_match) + Node *insert(int priority, rule_mode_t mode, uint32_t perms, + uint32_t audit, bool exact_match) { - UniquePerm tmp = { mode, exact_match, perms, audit }; + UniquePerm tmp = { priority, mode, exact_match, perms, audit }; iterator res = nodes.find(tmp); if (res == nodes.end()) { Node *node; if (mode == RULE_DENY) - node = new DenyMatchFlag(perms, audit); + node = new DenyMatchFlag(priority, perms, audit); else if (mode == RULE_PROMPT) - node = new PromptMatchFlag(perms, audit); + node = new PromptMatchFlag(priority, perms, audit); else if (exact_match) - node = new ExactMatchFlag(perms, audit); + node = new ExactMatchFlag(priority, perms, audit); else - node = new MatchFlag(perms, audit); + node = new MatchFlag(priority, perms, audit); pair val = nodes.insert(make_pair(tmp, node)); if (val.second == false) return val.first->second; @@ -109,11 +112,11 @@ class aare_rules { aare_rules(int reverse): root(NULL), unique_perms(), expr_map(), reverse(reverse), rule_count(0) { }; ~aare_rules(); - bool add_rule(const char *rule, rule_mode_t mode, perm32_t perms, - perm32_t audit, optflags const &opts); - bool add_rule_vec(rule_mode_t mode, perm32_t perms, perm32_t audit, - int count, const char **rulev, optflags const &opts, - bool oob); + bool add_rule(const char *rule, int priority, rule_mode_t mode, + perm32_t perms, perm32_t audit, optflags const &opts); + bool add_rule_vec(int priority, rule_mode_t mode, perm32_t perms, + perm32_t audit, int count, const char **rulev, + optflags const &opts, bool oob); bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts); CHFA *create_chfa(int *min_match_len, vector &perms_table, diff --git a/parser/libapparmor_re/expr-tree.h b/parser/libapparmor_re/expr-tree.h index 1a674539e..4befb294b 100644 --- a/parser/libapparmor_re/expr-tree.h +++ b/parser/libapparmor_re/expr-tree.h @@ -886,19 +886,20 @@ public: class MatchFlag: public AcceptNode { public: - MatchFlag(perm32_t perms, perm32_t audit): perms(perms), audit(audit) + MatchFlag(int priority, perm32_t perms, perm32_t audit): priority(priority), perms(perms), audit(audit) { type_flags |= NODE_TYPE_MATCHFLAG; } ostream &dump(ostream &os) { return os << "< 0x" << hex << perms << '>'; } + int priority; perm32_t perms; perm32_t audit; }; class ExactMatchFlag: public MatchFlag { public: - ExactMatchFlag(perm32_t perms, perm32_t audit): MatchFlag(perms, audit) + ExactMatchFlag(int priority, perm32_t perms, perm32_t audit): MatchFlag(priority, perms, audit) { type_flags |= NODE_TYPE_EXACTMATCHFLAG; } @@ -906,7 +907,7 @@ public: class DenyMatchFlag: public MatchFlag { public: - DenyMatchFlag(perm32_t perms, perm32_t quiet): MatchFlag(perms, quiet) + DenyMatchFlag(int priority, perm32_t perms, perm32_t quiet): MatchFlag(priority, perms, quiet) { type_flags |= NODE_TYPE_DENYMATCHFLAG; } @@ -914,7 +915,7 @@ public: class PromptMatchFlag: public MatchFlag { public: - PromptMatchFlag(perm32_t prompt, perm32_t audit): MatchFlag(prompt, audit) {} + PromptMatchFlag(int priority, perm32_t prompt, perm32_t audit): MatchFlag(priority, prompt, audit) {} }; diff --git a/parser/libapparmor_re/hfa.cc b/parser/libapparmor_re/hfa.cc index affd68e70..9318f8562 100644 --- a/parser/libapparmor_re/hfa.cc +++ b/parser/libapparmor_re/hfa.cc @@ -493,6 +493,11 @@ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filed */ nnodes_cache.clear(); node_map.clear(); + /* once created the priority information is no longer needed and + * can prevent sets with the same perms and different priorities + * from being merged during minimization + */ + clear_priorities(); } DFA::~DFA() @@ -646,6 +651,12 @@ int DFA::apply_and_clear_deny(void) return c; } +void DFA::clear_priorities(void) +{ + for (Partition::iterator i = states.begin(); i != states.end(); i++) + (*i)->perms.priority = 0; +} + /* minimize the number of dfa states */ @@ -1379,9 +1390,7 @@ static inline int diff_qualifiers(perm32_t perm1, perm32_t perm2) int accept_perms(NodeVec *state, perms_t &perms, bool filedfa) { int error = 0; - perm32_t exact_match_allow = 0; - perm32_t exact_match_prompt = 0; - perm32_t exact_audit = 0; + perms_t exact; perms.clear(); @@ -1393,13 +1402,20 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa) continue; MatchFlag *match = static_cast(*i); + if (perms.priority > match->priority) + continue; + + if (perms.priority < match->priority) { + perms.clear(match->priority); + exact.clear(match->priority); + } if (match->is_type(NODE_TYPE_EXACTMATCHFLAG)) { /* exact match only ever happens with x */ - if (filedfa && !is_merged_x_consistent(exact_match_allow, - match->perms)) - error = 1;; - exact_match_allow |= match->perms; - exact_audit |= match->audit; + if (filedfa && + !is_merged_x_consistent(exact.allow, match->perms)) + error = 1; + exact.allow |= match->perms; + exact.audit |= match->audit; } else if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) { perms.deny |= match->perms; perms.quiet |= match->audit; @@ -1407,7 +1423,8 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa) perms.prompt |= match->perms; perms.audit |= match->audit; } else { - if (filedfa && !is_merged_x_consistent(perms.allow, match->perms)) + if (filedfa && + !is_merged_x_consistent(perms.allow, match->perms)) error = 1; perms.allow |= match->perms; perms.audit |= match->audit; @@ -1415,21 +1432,21 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa) } if (filedfa) { - perms.allow |= exact_match_allow & ~(ALL_AA_EXEC_TYPE); - perms.prompt |= exact_match_prompt & ~(ALL_AA_EXEC_TYPE); - perms.audit |= exact_audit & ~(ALL_AA_EXEC_TYPE); + perms.allow |= exact.allow & ~(ALL_AA_EXEC_TYPE); + perms.prompt |= exact.prompt & ~(ALL_AA_EXEC_TYPE); + perms.audit |= exact.audit & ~(ALL_AA_EXEC_TYPE); } else { - perms.allow |= exact_match_allow; - perms.prompt |= exact_match_prompt; - perms.audit |= exact_audit; + perms.allow |= exact.allow; + perms.prompt |= exact.prompt; + perms.audit |= exact.audit; } - if (exact_match_allow & AA_USER_EXEC) { - perms.allow = (exact_match_allow & AA_USER_EXEC_TYPE) | + if (exact.allow & AA_USER_EXEC) { + perms.allow = (exact.allow & AA_USER_EXEC_TYPE) | (perms.allow & ~AA_USER_EXEC_TYPE); perms.exact = AA_USER_EXEC_TYPE; } - if (exact_match_allow & AA_OTHER_EXEC) { - perms.allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) | + if (exact.allow & AA_OTHER_EXEC) { + perms.allow = (exact.allow & AA_OTHER_EXEC_TYPE) | (perms.allow & ~AA_OTHER_EXEC_TYPE); perms.exact |= AA_OTHER_EXEC_TYPE; } @@ -1443,7 +1460,6 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa) perms.quiet &= perms.deny; perms.prompt &= ~perms.deny; perms.prompt &= ~perms.allow; - if (error) fprintf(stderr, "profile has merged rule with conflicting x modifiers\n"); diff --git a/parser/libapparmor_re/hfa.h b/parser/libapparmor_re/hfa.h index 56dd45ebf..3c6afb071 100644 --- a/parser/libapparmor_re/hfa.h +++ b/parser/libapparmor_re/hfa.h @@ -30,6 +30,7 @@ #include #include +#include #include #include "expr-tree.h" @@ -51,24 +52,37 @@ ostream &operator<<(ostream &os, State &state); class perms_t { public: - perms_t(void): allow(0), deny(0), audit(0), quiet(0), exact(0) { }; + perms_t(void): priority(INT_MIN), allow(0), deny(0), prompt(0), audit(0), quiet(0), exact(0) { }; bool is_accept(void) { return (allow | deny | prompt | audit | quiet); } void dump_header(ostream &os) { - os << "(allow/deny/prompt/audit/quiet)"; + os << "priority (allow/deny/prompt/audit/quiet)"; } void dump(ostream &os) { - os << " (0x " << hex + os << " " << priority << " (0x " << hex << allow << "/" << deny << "/" << "/" << prompt << "/" << audit << "/" << quiet << ')' << dec; } - void clear(void) { allow = deny = prompt = audit = quiet = 0; } + void clear(void) { + priority = INT_MIN; + allow = deny = prompt = audit = quiet = exact = 0; + } + void clear(int p) { + priority = p; + allow = deny = prompt = audit = quiet = exact = 0; + } void add(perms_t &rhs, bool filedfa) { + if (priority > rhs.priority) + return; + if (priority < rhs.priority) { + *this = rhs; + return; + } //else if (rhs.priority == priority) { deny |= rhs.deny; if (filedfa && !is_merged_x_consistent(allow & ALL_USER_EXEC, @@ -131,6 +145,8 @@ public: bool operator<(perms_t const &rhs)const { + if (priority < rhs.priority) + return priority < rhs.priority; if (allow < rhs.allow) return allow < rhs.allow; if (deny < rhs.deny) @@ -142,6 +158,7 @@ public: return quiet < rhs.quiet; } + int priority; perm32_t allow, deny, prompt, audit, quiet, exact; }; @@ -351,6 +368,7 @@ public: bool same_mappings(State *s1, State *s2); void minimize(optflags const &flags); int apply_and_clear_deny(void); + void clear_priorities(void); void diff_encode(optflags const &flags); void undiff_encode(void); diff --git a/parser/mount.cc b/parser/mount.cc index bbd2bab12..f3b9c01a4 100644 --- a/parser/mount.cc +++ b/parser/mount.cc @@ -797,8 +797,9 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count, * if a data match is required this only has AA_MATCH_CONT perms * else it has full perms */ - if (!prof.policy.rules->add_rule_vec(rule_mode, tmpperms, tmpaudit, 4, - vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, tmpperms, + tmpaudit, 4, vec, parseopts, + false)) goto fail; count++; @@ -808,7 +809,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count, if (!build_mnt_opts(optsbuf, opts)) goto fail; vec[4] = optsbuf.c_str(); - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, (audit == AUDIT_FORCE ? perms : 0), 5, vec, parseopts, false)) goto fail; @@ -850,7 +851,8 @@ int mnt_rule::gen_policy_bind_mount(Profile &prof, int &count, opt_flags & MS_BIND_FLAGS)) goto fail; vec[3] = flagsbuf; - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, + audit == AUDIT_FORCE ? perms : 0, 4, vec, parseopts, false)) goto fail; @@ -907,7 +909,8 @@ int mnt_rule::gen_policy_change_mount_type(Profile &prof, int &count, opt_flags & MS_MAKE_FLAGS)) goto fail; vec[3] = flagsbuf; - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, + audit == AUDIT_FORCE ? perms : 0, 4, vec, parseopts, false)) goto fail; @@ -950,7 +953,8 @@ int mnt_rule::gen_policy_move_mount(Profile &prof, int &count, opt_flags & MS_MOVE_FLAGS)) goto fail; vec[3] = flagsbuf; - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, + audit == AUDIT_FORCE ? perms : 0, 4, vec, parseopts, false)) goto fail; @@ -1002,8 +1006,9 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count, tmpaudit = audit == AUDIT_FORCE ? perms : 0; } /* rule for match without required data || data MATCH_CONT */ - if (!prof.policy.rules->add_rule_vec(rule_mode, tmpperms, tmpaudit, 4, - vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, tmpperms, + tmpaudit, 4, vec, parseopts, + false)) goto fail; count++; @@ -1013,7 +1018,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count, if (!build_mnt_opts(optsbuf, opts)) goto fail; vec[4] = optsbuf.c_str(); - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, 5, vec, parseopts, false)) goto fail; @@ -1105,7 +1110,7 @@ int mnt_rule::gen_policy_re(Profile &prof) if (!convert_entry(mntbuf, mnt_point)) goto fail; vec[0] = mntbuf.c_str(); - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, (audit == AUDIT_FORCE ? perms : 0), 1, vec, parseopts, false)) goto fail; @@ -1120,7 +1125,7 @@ int mnt_rule::gen_policy_re(Profile &prof) if (!clear_and_convert_entry(devbuf, device)) goto fail; vec[1] = devbuf.c_str(); - if (!prof.policy.rules->add_rule_vec(rule_mode, perms, + if (!prof.policy.rules->add_rule_vec(priority, rule_mode, perms, (audit == AUDIT_FORCE ? perms : 0), 2, vec, parseopts, false)) goto fail; diff --git a/parser/mqueue.cc b/parser/mqueue.cc index 8166110c3..7eba630ba 100644 --- a/parser/mqueue.cc +++ b/parser/mqueue.cc @@ -231,10 +231,19 @@ 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, map_mqueue_perms(perms), audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, 1, vec, parseopts, false)) + if (!label && !prof.policy.rules->add_rule_vec( + priority, + rule_mode, + map_mqueue_perms(perms), + audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, 1, + vec, parseopts, false)) goto fail; /* also provide label match with perm */ - if (!prof.policy.rules->add_rule_vec(rule_mode, map_mqueue_perms(perms), audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, size, vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, + rule_mode, + map_mqueue_perms(perms), + audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, + size, vec, parseopts, false)) goto fail; } } @@ -266,10 +275,19 @@ 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, map_mqueue_perms(perms), audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, 1, vec, parseopts, false)) + if (!label && + !prof.policy.rules->add_rule_vec(priority, + rule_mode, + map_mqueue_perms(perms), + audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, 1, + vec, parseopts, false)) goto fail; /* also provide label match with perm */ - if (!prof.policy.rules->add_rule_vec(rule_mode, map_mqueue_perms(perms), audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, size, vec, parseopts, false)) + if (!prof.policy.rules->add_rule_vec(priority, + rule_mode, + map_mqueue_perms(perms), + audit == AUDIT_FORCE ? map_mqueue_perms(perms) : 0, + size, vec, parseopts, false)) goto fail; } } diff --git a/parser/network.cc b/parser/network.cc index 55bf17170..750926f96 100644 --- a/parser/network.cc +++ b/parser/network.cc @@ -697,7 +697,8 @@ bool network_rule::gen_ip_conds(Profile &prof, std::list &st buf = oss.str(); /* AA_CONT_MATCH mapping (cond_perms) only applies to perms, not audit */ - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, cond_perms, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, cond_perms, dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, parseopts)) return false; @@ -710,7 +711,8 @@ bool network_rule::gen_ip_conds(Profile &prof, std::list &st oss << "\\x00"; /* null transition */ buf = oss.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, cond_perms, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, cond_perms, dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, parseopts)) return false; @@ -735,9 +737,10 @@ bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mas if (!features_supports_inet || (family != AF_INET && family != AF_INET6)) { buf = buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, map_perms(perms), - dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(perms), + dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, + parseopts)) return false; return true; } @@ -745,7 +748,8 @@ bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mas buf = buffer.str(); /* create perms need to be generated excluding the rest of the perms */ if (perms & AA_NET_CREATE) { - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, map_perms(perms & AA_NET_CREATE) | (AA_CONT_MATCH << 1), + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(perms & AA_NET_CREATE) | (AA_CONT_MATCH << 1), dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms & AA_NET_CREATE) : 0, parseopts)) return false; @@ -797,9 +801,10 @@ bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mas /* length of queue allowed - not used for now */ listen_buffer << ".."; buf = listen_buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, map_perms(perms), - dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(perms), + dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, + parseopts)) return false; } } @@ -816,9 +821,10 @@ bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mas /* socket mapping - not used for now */ opt_buffer << ".."; buf = opt_buffer.str(); - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, map_perms(perms), - dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, map_perms(perms), + dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0, + parseopts)) return false; } } diff --git a/parser/parser.h b/parser/parser.h index 557f881c5..ca7274f25 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -114,6 +114,7 @@ struct cond_entry_list { }; struct cod_entry { + int priority; char *name; union { char *link_name; diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 124e8b7a2..9664c120b 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -984,6 +984,7 @@ struct cod_entry *new_entry(char *id, perm32_t perms, char *link_id) if (!entry) return NULL; + entry->priority = 0; entry->name = id; entry->link_name = link_id; entry->perms = perms; @@ -1010,6 +1011,7 @@ struct cod_entry *copy_cod_entry(struct cod_entry *orig) DUP_STRING(orig, entry, name, err); DUP_STRING(orig, entry, link_name, err); DUP_STRING(orig, entry, nt_name, err); + entry->priority = orig->priority; entry->perms = orig->perms; entry->audit = orig->audit; entry->rule_mode = orig->rule_mode; diff --git a/parser/parser_regex.c b/parser/parser_regex.c index c347a1360..3e0c945c1 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -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(), RULE_ALLOW, + if (!rules->add_rule(tbuf.c_str(), 0, RULE_ALLOW, AA_MAY_EXEC, 0, parseopts)) { delete rules; return FALSE; @@ -521,7 +521,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(), + if (!rules->add_rule(tbuf.c_str(), 0, RULE_ALLOW, AA_MAY_EXEC, 0, parseopts)) { delete rules; @@ -644,13 +644,14 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry) if (entry->rule_mode == RULE_DENY) { if ((entry->perms & ~AA_LINK_BITS) && !is_change_profile_perms(entry->perms) && - !dfarules->add_rule(tbuf.c_str(), entry->rule_mode, + !dfarules->add_rule(tbuf.c_str(), entry->priority, + entry->rule_mode, entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE), entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0, parseopts)) return FALSE; } else if (!is_change_profile_perms(entry->perms)) { - if (!dfarules->add_rule(tbuf.c_str(), + if (!dfarules->add_rule(tbuf.c_str(), entry->priority, entry->rule_mode, entry->perms, entry->audit == AUDIT_FORCE ? entry->perms : 0, parseopts)) @@ -676,7 +677,10 @@ 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, perms, entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, 2, vec, parseopts, false)) + if (!dfarules->add_rule_vec(entry->priority, + entry->rule_mode, perms, + entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, + 2, vec, parseopts, false)) return FALSE; } if (is_change_profile_perms(entry->perms)) { @@ -727,13 +731,14 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry) } /* regular change_profile rule */ - if (!dfarules->add_rule_vec(entry->rule_mode, + if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode, AA_CHANGE_PROFILE | onexec_perms, 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, onexec_perms, + if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode, + onexec_perms, 0, 1, vec, parseopts, false)) return FALSE; @@ -742,8 +747,9 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry) * unsafe exec transitions */ onexec_perms |= (entry->perms & (AA_EXEC_BITS | ALL_AA_EXEC_UNSAFE)); - if (!dfarules->add_rule_vec(entry->rule_mode, onexec_perms, - 0, index, vec, parseopts, false)) + if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode, + onexec_perms, 0, index, vec, + parseopts, false)) return FALSE; } return TRUE; @@ -999,7 +1005,8 @@ static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask, buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff); } buf = buffer.str(); - if (!prof->policy.rules->add_rule(buf.c_str(), rmode, map_perms(AA_VALID_NET_PERMS), + if (!prof->policy.rules->add_rule(buf.c_str(), 0, rmode, + map_perms(AA_VALID_NET_PERMS), audit ? map_perms(AA_VALID_NET_PERMS) : 0, parseopts)) return false; @@ -1091,7 +1098,7 @@ int process_profile_policydb(Profile *prof) * to be supported */ if (features_supports_userns && - !prof->policy.rules->add_rule(mediates_ns, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_ns, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; /* don't add mediated classes to unconfined profiles */ @@ -1099,35 +1106,35 @@ int process_profile_policydb(Profile *prof) prof->flags.mode != MODE_DEFAULT_ALLOW) { /* note: this activates fs based unix domain sockets mediation on connect */ if (kernel_abi_version > 5 && - !prof->policy.rules->add_rule(mediates_file, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_file, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_mount && - !prof->policy.rules->add_rule(mediates_mount, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_mount, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_dbus && - !prof->policy.rules->add_rule(mediates_dbus, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_dbus, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_signal && - !prof->policy.rules->add_rule(mediates_signal, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_signal, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_ptrace && - !prof->policy.rules->add_rule(mediates_ptrace, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_ptrace, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_networkv8 && - !prof->policy.rules->add_rule(mediates_netv8, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_netv8, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_unix && - (!prof->policy.rules->add_rule(mediates_extended_net, RULE_ALLOW, AA_MAY_READ, 0, parseopts) || - !prof->policy.rules->add_rule(mediates_net_unix, RULE_ALLOW, AA_MAY_READ, 0, parseopts))) + (!prof->policy.rules->add_rule(mediates_extended_net, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts) || + !prof->policy.rules->add_rule(mediates_net_unix, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts))) goto out; if (features_supports_posix_mqueue && - !prof->policy.rules->add_rule(mediates_posix_mqueue, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_posix_mqueue, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_sysv_mqueue && - !prof->policy.rules->add_rule(mediates_sysv_mqueue, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_io_uring && - !prof->policy.rules->add_rule(mediates_io_uring, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_io_uring, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; } @@ -1136,11 +1143,11 @@ int process_profile_policydb(Profile *prof) // This requires file rule processing happen first if (!prof->dfa.rules->rule_count) { // add null dfa - if (!prof->dfa.rules->add_rule(deny_file, RULE_DENY, AA_MAY_READ, 0, parseopts)) + if (!prof->dfa.rules->add_rule(deny_file, 0, RULE_DENY, AA_MAY_READ, 0, parseopts)) goto out; } if (!prof->policy.rules->rule_count) { - if (!prof->policy.rules->add_rule(mediates_file, RULE_DENY, AA_MAY_READ, 0, parseopts)) + if (!prof->policy.rules->add_rule(mediates_file, 0, RULE_DENY, AA_MAY_READ, 0, parseopts)) goto out; } int xmatch_len = 0; diff --git a/parser/ptrace.cc b/parser/ptrace.cc index 9627f84b4..2580f0aec 100644 --- a/parser/ptrace.cc +++ b/parser/ptrace.cc @@ -133,9 +133,10 @@ 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, perms, - audit == AUDIT_FORCE ? perms : 0, - parseopts)) + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, perms, + audit == AUDIT_FORCE ? perms : 0, + parseopts)) goto fail; } diff --git a/parser/rule.h b/parser/rule.h index 1192b270a..e876b8a7a 100644 --- a/parser/rule.h +++ b/parser/rule.h @@ -82,10 +82,11 @@ class rule_t { public: int rule_type; rule_flags_t flags; + int priority; rule_t *removed_by; - rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE), removed_by(NULL) { } + rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE), priority(0), removed_by(NULL) { } virtual ~rule_t() { }; bool is_type(int type) { return rule_type == type; } @@ -113,6 +114,9 @@ public: virtual int expand_variables(void) = 0; virtual int cmp(rule_t const &rhs) const { + int tmp = priority - rhs.priority; + if (tmp != 0) + return tmp; return rule_type - rhs.rule_type; } virtual bool operator<(rule_t const &rhs) const { diff --git a/parser/signal.cc b/parser/signal.cc index a161c275a..4133513ea 100644 --- a/parser/signal.cc +++ b/parser/signal.cc @@ -316,7 +316,8 @@ 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, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, parseopts)) goto fail; diff --git a/parser/tst/features_files/features.extended-perms-no-policydb b/parser/tst/features_files/features.extended-perms-no-policydb new file mode 100644 index 000000000..9f8d5b06b --- /dev/null +++ b/parser/tst/features_files/features.extended-perms-no-policydb @@ -0,0 +1,68 @@ +capability {0xffffff +} +caps {extended {yes +} +mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf checkpoint_restore +} +} +dbus {mask {acquire send receive +} +} +domain {attach_conditions {xattr {yes +} +} +change_hat {yes +} +change_hatv {yes +} +change_onexec {yes +} +change_profile {yes +} +computed_longest_left {yes +} +disconnected.path {yes +} +fix_binfmt_elf_mmap {yes +} +interruptible {yes +} +kill.signal {yes +} +post_nnp_subset {yes +} +stack {yes +} +unconfined_allowed_children {yes +} +version {1.2 +} +} +policy {outofband {0x000001 +} +permstable32 {allow deny subtree cond kill complain prompt audit quiet hide xindex tag label +} +permstable32_version {0x000003 +} +set_load {yes +} +versions {v5 {yes +} +v6 {yes +} +v7 {yes +} +v8 {yes +} +v9 {yes +} +} +} +query {label {data {yes +} +multi_transaction {yes +} +perms {allow deny audit quiet +} +} +} diff --git a/parser/tst/features_files/features.extended-perms-policydb b/parser/tst/features_files/features.extended-perms-policydb new file mode 100644 index 000000000..bf629623d --- /dev/null +++ b/parser/tst/features_files/features.extended-perms-policydb @@ -0,0 +1,117 @@ +capability {0xffffff +} +caps {extended {yes +} +mask {chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf checkpoint_restore +} +} +dbus {mask {acquire send receive +} +} +domain {attach_conditions {xattr {yes +} +} +change_hat {yes +} +change_hatv {yes +} +change_onexec {yes +} +change_profile {yes +} +computed_longest_left {yes +} +disconnected.path {yes +} +fix_binfmt_elf_mmap {yes +} +interruptible {yes +} +kill.signal {yes +} +post_nnp_subset {yes +} +stack {yes +} +unconfined_allowed_children {yes +} +version {1.2 +} +} +file {mask {create read write exec append mmap_exec link lock +} +} +io_uring {mask {sqpoll override_creds +} +} +ipc {posix_mqueue {create read write open delete setattr getattr +} +} +mount {mask {mount umount pivot_root +} +move_mount {detached +} +} +namespaces {mask {userns_create +} +pivot_root {no +} +profile {yes +} +userns_create {pciu& +} +} +network {af_mask {unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp mctp +} +af_unix {yes +} +} +network_v8 {af_inet {yes +} +af_mask {unspec unix inet ax25 ipx appletalk netrom bridge atmpvc x25 inet6 rose netbeui security key netlink packet ash econet atmsvc rds sna irda pppox wanpipe llc ib mpls can tipc bluetooth iucv rxrpc isdn phonet ieee802154 caif alg nfc vsock kcm qipcrtr smc xdp mctp +} +} +policy {outofband {0x000001 +} +permstable32 {allow deny subtree cond kill complain prompt audit quiet hide xindex tag label +} +permstable32_version {0x000003 +} +set_load {yes +} +unconfined_restrictions {change_profile {yes +} +io_uring {0 +} +userns {0 +} +} +versions {v5 {yes +} +v6 {yes +} +v7 {yes +} +v8 {yes +} +v9 {yes +} +} +} +ptrace {mask {read trace +} +} +query {label {data {yes +} +multi_transaction {yes +} +perms {allow deny audit quiet +} +} +} +rlimit {mask {cpu fsize data stack core rss nproc nofile memlock as locks sigpending msgqueue nice rtprio rttime +} +} +signal {mask {hup int quit ill trap abrt bus fpe kill usr1 segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg xcpu xfsz vtalrm prof winch io pwr sys emt lost +} +} diff --git a/parser/tst/minimize.sh b/parser/tst/minimize.sh index 3c71d9189..93bbd17a2 100755 --- a/parser/tst/minimize.sh +++ b/parser/tst/minimize.sh @@ -78,7 +78,7 @@ APPARMOR_PARSER="${APPARMOR_PARSER:-../apparmor_parser}" # {a} (0x 40030/0/0/0) echo -n "Minimize profiles basic perms " -if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 6 ] ; then +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then echo "failed" exit 1; fi @@ -93,7 +93,7 @@ echo "ok" # {9} (0x 12804a/0/2800a/0) # {c} (0x 40030/0/0/0) echo -n "Minimize profiles audit perms " -if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 6 ] ; then +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then echo "failed" exit 1; fi @@ -112,7 +112,7 @@ echo "ok" # {c} (0x 40030/0/0/0) echo -n "Minimize profiles deny perms " -if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 6 ] ; then +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then echo "failed" exit 1; fi @@ -130,13 +130,59 @@ echo "ok" # {c} (0x 40030/0/0/0) echo -n "Minimize profiles audit deny perms " -if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 5 ] ; then +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 5 ] ; then echo "failed" exit 1; fi echo "ok" +# ---------------------- extended perms ------------------------------ + + +# Test that not filtering deny results in more states. This test is testing +# without filtering. The following test does the filtering +# +# {1} <== (allow/deny/prompt/audit/quiet) +# {3} (0x 0/2800a///0/0/0) +# {4} (0x 10004/2800a///0/0/0) +# {7} (0x 40010/2800a///0/0/0) +# {8} (0x 80020/2800a///0/0/0) +# {9} (0x 100040/2800a///0/0/0) +# {12} (0x 40030/0///0/0/0) +# {2} (0x 4/0//0/0/0) <- from policydb still showing up bug + +## NOTE: change count from 6 to 7 when extend perms is not dependent on +## prompt rules being present +echo -n "Minimize profiles extended no-filter audit deny perms " +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then + echo "failed" + exit 1; +fi +echo "ok" + +# same test as above except with filter-deny which should result in one less +# accept state +# +# {1} <== (allow/deny/prompt/audit/quiet) +# {4} (0x 10004/0///0/0/0) +# {7} (0x 40010/0///0/0/0) +# {8} (0x 80020/0///0/0/0) +# {9} (0x 100040/0///0/0/0) +# {12} (0x 40030/0///0/0/0) +# {2} (0x 4/0//0/0/0) <- from policydb still showing up bug + +echo -n "Minimize profiles extended filter audit deny perms " +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then + echo "failed" + exit 1; +fi +echo "ok" + + + +# ======================= x transition =============================== + # The x transition test profile is setup so that there are 3 conflicting x # permissions, two are on paths that won't collide during dfa creation. The # 3rd is a generic permission that should be overridden during dfa creation. @@ -162,7 +208,7 @@ echo "ok" # echo -n "Minimize profiles xtrans " -if [ "$(echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 3 ] ; then +if [ "$(echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 3 ] ; then echo "failed" exit 1; fi @@ -170,7 +216,7 @@ echo "ok" # same test as above + audit echo -n "Minimize profiles audit xtrans " -if [ "$(echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 3 ] ; then +if [ "$(echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 3 ] ; then echo "failed" exit 1; fi @@ -183,7 +229,7 @@ echo "ok" # {3} (0x 0/fe17f85/0/14005) echo -n "Minimize profiles deny xtrans " -if [ "$(echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 1 ] ; then +if [ "$(echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 1 ] ; then echo "failed" exit 1; fi @@ -195,7 +241,7 @@ echo "ok" # {3} (0x 0/fe17f85/0/0) echo -n "Minimize profiles audit deny xtrans " -if [ "$(echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} (.*)$')" -ne 0 ] ; then +if [ "$(echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 0 ] ; then echo "failed" exit 1; fi diff --git a/parser/userns.cc b/parser/userns.cc index 96a60c649..c66ce062e 100644 --- a/parser/userns.cc +++ b/parser/userns.cc @@ -95,7 +95,8 @@ int userns_rule::gen_policy_re(Profile &prof) buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NS; buf = buffer.str(); if (perms & AA_VALID_USERNS_PERMS) { - if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, perms, + if (!prof.policy.rules->add_rule(buf.c_str(), priority, + rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, parseopts)) goto fail; From ee1a5e6e18b1d59390085cc51dc702e9692f0162 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 14 May 2024 05:02:19 -0700 Subject: [PATCH 4/9] parser: enable extended perms if supported by the kernel Currently use of extended perms are dependent on prompt rules being present in policy. Switch to using extended perms if they are supported. Signed-off-by: John Johansen --- parser/parser_main.c | 5 ++++- parser/parser_regex.c | 4 ++-- parser/tst/minimize.sh | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/parser/parser_main.c b/parser/parser_main.c index 14d05a086..a1c7d9c08 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -1583,7 +1583,10 @@ static bool get_kernel_features(struct aa_features **features) } kernel_supports_permstable32_v1 = aa_features_supports(*features, "policy/permstable32_version/0x000001"); if (kernel_supports_permstable32_v1) { - //fprintf(stderr, "kernel supports prompt_v1\n"); + /* permstabl32 is broken in kernels that only support v1 + * so disable it + */ + kernel_supports_permstable32 = false; } /* set default prompt_compat_mode to the best that is supported */ diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 3e0c945c1..6250b5d7a 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -791,7 +791,7 @@ int process_profile_regex(Profile *prof) prof->dfa.dfa = prof->dfa.rules->create_dfablob(&prof->dfa.size, &xmatch_len, prof->dfa.perms_table, parseopts, true, - prof->uses_prompt_rules && (prompt_compat_mode == PROMPT_COMPAT_PERMSV2), + kernel_supports_permstable32, prof->uses_prompt_rules); delete prof->dfa.rules; prof->dfa.rules = NULL; @@ -1174,7 +1174,7 @@ int process_profile_policydb(Profile *prof) &xmatch_len, prof->policy.perms_table, parseopts, false, - prof->uses_prompt_rules && (prompt_compat_mode == PROMPT_COMPAT_PERMSV2), + kernel_supports_permstable32, prof->uses_prompt_rules); delete prof->policy.rules; diff --git a/parser/tst/minimize.sh b/parser/tst/minimize.sh index 93bbd17a2..054831fe8 100755 --- a/parser/tst/minimize.sh +++ b/parser/tst/minimize.sh @@ -155,7 +155,7 @@ echo "ok" ## NOTE: change count from 6 to 7 when extend perms is not dependent on ## prompt rules being present echo -n "Minimize profiles extended no-filter audit deny perms " -if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then +if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 7 ] ; then echo "failed" exit 1; fi From cd95d46397e5c6769f856dfd43cb544cd2e61b4f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 11 May 2024 23:46:49 -0700 Subject: [PATCH 5/9] parser: split parse boolean into boolean and integer the parser front end boolean is used for both boolean and integer values. This is confusing when integer values different than 1 or 0 are being assigned to and from boolean. Split its uses into the correct semantic boolean and integer cases. Signed-off-by: John Johansen --- parser/parser_yacc.y | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index f94a5808f..c8fa67df5 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -222,7 +222,8 @@ static void abi_features(char *filename, bool search); struct value_list *val_list; struct cond_entry *cond_entry; struct cond_entry_list cond_entry_list; - int boolean; + bool boolean; + int integer; owner_t owner; struct prefixes prefix; IncludeCache_t *includecache; @@ -270,7 +271,7 @@ static void abi_features(char *filename, bool search); %type opt_subset_flag %type opt_audit_flag %type opt_owner_flag -%type opt_profile_flag +%type opt_profile_flag %type opt_flags %type opt_rule_mode %type opt_id @@ -294,7 +295,7 @@ static void abi_features(char *filename, bool search); %type unix_rule %type opt_target %type opt_named_transition -%type opt_exec_mode +%type opt_exec_mode %type opt_file %type userns_perm %type userns_perms @@ -588,13 +589,13 @@ flags: { /* nothing */ $$ = fv; }; -opt_flags: { /* nothing */ $$ = 0; } +opt_flags: { /* nothing */ $$ = false; } | TOK_CONDID TOK_EQUALS { if (strcmp($1, "flags") != 0) yyerror("expected flags= got %s=", $1); free($1); - $$ = 1; + $$ = true; } flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN @@ -1011,8 +1012,8 @@ opt_exec_mode: { /* nothing */ $$ = EXEC_MODE_EMPTY; } | TOK_UNSAFE { $$ = EXEC_MODE_UNSAFE; }; | TOK_SAFE { $$ = EXEC_MODE_SAFE; }; -opt_file: { /* nothing */ $$ = 0; } - | TOK_FILE { $$ = 1; } +opt_file: { /* nothing */ $$ = false; } + | TOK_FILE { $$ = true; } frule: id_or_var file_perms opt_named_transition TOK_END_OF_RULE { From abc18e45a43cfd4539b6e2b7c297419a3d87a10b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 12 May 2024 02:19:17 -0700 Subject: [PATCH 6/9] parser: simplify prefix comparison code The prefix comparison doesn't need to do as many operations as it is doing, and the operator< can be based on the cmp() fn further reducing the chance that the code will get out of sync if prefixes are changed. Signed-off-by: John Johansen --- parser/rule.h | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/parser/rule.h b/parser/rule.h index e876b8a7a..ec4664755 100644 --- a/parser/rule.h +++ b/parser/rule.h @@ -246,14 +246,12 @@ public: } 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; + int tmp = (int) audit - (int) rhs.audit; + if (tmp != 0) + return tmp; + tmp = (int) rule_mode - (int) rhs.rule_mode; + if (tmp != 0) + return tmp; if ((uint) owner < (uint) rhs.owner) return -1; if ((uint) owner > (uint) rhs.owner) @@ -262,11 +260,7 @@ public: } 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 ((uint) owner < (uint) rhs.owner) + if (cmp(rhs) < 0) return true; return false; } From 61b7568e194273276410dc0541f7871bbb9a6b45 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 21 May 2024 09:50:23 -0700 Subject: [PATCH 7/9] parser: bug fix mediates_X stub rules. Currently mediates_X stub rules are added to the dfa to ensure a valid transition state will exist if X should be mediated. The kernel uses this to test whether the dfa supports certain mediation classes. Unfortunately the mediates stub rules can be removed by other rules, combined with minimization. In the allow case this is not a problem, as if the stub rule is removed it will be due to state merging and the test will still be valid. Unfortunately the deny case can wipe out the stub rule in a couple of cases, meaning the when the kernel tests that its in a valid state for mediation it will fail and treat the dfa as not mediating the rule type, which results in allowing instead of denying. Fix this by making sure mediated stub rules can't be overridden by a deny rule by giving them maximum priority. Note: there is another issue with stub rule elimination in the allow case. It will can cause equality tests to fail when combined with priority rules, because the stub rules where added at priority 0 and an actual rule of higher priority could completely override it removing the permission on the stub rule. This issue will be caught by the equality.sh tests in the following patch that exposes priority to rules in policy. Signed-off-by: John Johansen --- parser/parser_regex.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 6250b5d7a..71126c5f9 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -1083,6 +1083,20 @@ static const char *mediates_sysv_mqueue = CLASS_STR(AA_CLASS_SYSV_MQUEUE); static const char *mediates_io_uring = CLASS_STR(AA_CLASS_IO_URING); static const char *deny_file = ".*"; +/* Set the mediates priority to the maximum possible. This is to help + * ensure that the mediates information is not wiped out by a rule + * of higher priority. Which for allow rules isn't really a problem + * in that these are only used as a place holder to ensure we have + * a valid state at the mediates check, and an allow rule that wipes + * these out would guarantee it. But a deny rule wiping these out + * could result in the dfa allowing stuff as unmediated when it shouldn't + * + * Note: it turns out the above bug does exist for dbus rules in parsers + * that do not support priority, and we don't have a way to fix it. + * We fix it here by capping user specified priority to be < INT_MAX. + */ +static int mediates_priority = INT_MAX; + int process_profile_policydb(Profile *prof) { int error = -1; @@ -1098,7 +1112,7 @@ int process_profile_policydb(Profile *prof) * to be supported */ if (features_supports_userns && - !prof->policy.rules->add_rule(mediates_ns, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_ns, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; /* don't add mediated classes to unconfined profiles */ @@ -1106,35 +1120,35 @@ int process_profile_policydb(Profile *prof) prof->flags.mode != MODE_DEFAULT_ALLOW) { /* note: this activates fs based unix domain sockets mediation on connect */ if (kernel_abi_version > 5 && - !prof->policy.rules->add_rule(mediates_file, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_file, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_mount && - !prof->policy.rules->add_rule(mediates_mount, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_mount, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_dbus && - !prof->policy.rules->add_rule(mediates_dbus, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_dbus, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_signal && - !prof->policy.rules->add_rule(mediates_signal, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_signal, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_ptrace && - !prof->policy.rules->add_rule(mediates_ptrace, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_ptrace, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_networkv8 && - !prof->policy.rules->add_rule(mediates_netv8, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_netv8, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_unix && - (!prof->policy.rules->add_rule(mediates_extended_net, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts) || - !prof->policy.rules->add_rule(mediates_net_unix, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts))) + (!prof->policy.rules->add_rule(mediates_extended_net, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts) || + !prof->policy.rules->add_rule(mediates_net_unix, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts))) goto out; if (features_supports_posix_mqueue && - !prof->policy.rules->add_rule(mediates_posix_mqueue, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_posix_mqueue, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_sysv_mqueue && - !prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_sysv_mqueue, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; if (features_supports_io_uring && - !prof->policy.rules->add_rule(mediates_io_uring, 0, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) + !prof->policy.rules->add_rule(mediates_io_uring, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts)) goto out; } From e3fca60d1121d3a081c36831b90c5830bff8b777 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 11 May 2024 23:33:42 -0700 Subject: [PATCH 8/9] parser: add the ability to specify a priority prefix to rules This enables adding a priority to a rules in policy, finishing out the priority work done to plumb priority support through the internals in the previous patch. Rules have a default priority of 0. The priority prefix can be added before the other currently support rule prefixes, ie. [priority prefix][audit qualifier][rule mode][owner] If present a numerical priority can be assigned to the rule, where the greater the number the higher the priority. Eg. priority=1 audit file r /etc/passwd, priority=-1 deny file w /etc/**, Rule priority allows the rule with the highest priority to completely override lower priority rules where they overlap. Within a given priority level rules will accumulate in standard apparmor fashion. Eg. given priority=1 w /*c, priority=0 r /a*, priority=-1 k /*b*, /abc, /bc, /ac .. will have permissions of w /ab, /abb, /aaa, .. will have permissions of r /b, /bcb, /bab, .. will have permissions of k User specified rule priorities are currently capped at the arbitrary values of 1000, and -1000. Notes: * not all rule types support the priority prefix. Rukes like - network - capability - rlimits need to be reworked need to be reworked to properly preserve the policy rule structure. * this patch does not support priority on rule blocks * this patch does not support using a variable in the priority value. Signed-off-by: John Johansen --- parser/af_unix.cc | 2 +- parser/af_unix.h | 3 + parser/all_rule.h | 4 + parser/apparmor.d.pod | 15 +- parser/network.h | 5 + parser/parser.h | 6 + parser/parser_lex.l | 20 +- parser/parser_misc.c | 2 +- parser/parser_yacc.y | 32 +- parser/rule.h | 26 +- parser/tst/equality.sh | 683 +++++++++++------- .../file/priority/front_perms_ok_1.sd | 24 + .../file/priority/front_perms_ok_2.sd | 24 + parser/tst/simple_tests/file/priority/ok_1.sd | 7 + parser/tst/simple_tests/file/priority/ok_2.sd | 7 + parser/tst/simple_tests/file/priority/ok_3.sd | 9 + parser/tst/simple_tests/file/priority/ok_4.sd | 7 + parser/tst/simple_tests/file/priority/ok_5.sd | 7 + .../file/priority/ok_alternations_1.sd | 7 + .../file/priority/ok_alternations_2.sd | 7 + .../file/priority/ok_alternations_3.sd | 8 + .../simple_tests/file/priority/ok_append_1.sd | 13 + .../file/priority/ok_audit_deny_link.sd | 9 + .../simple_tests/file/priority/ok_bare_1.sd | 8 + .../simple_tests/file/priority/ok_carat_1.sd | 7 + .../simple_tests/file/priority/ok_carat_2.sd | 7 + .../simple_tests/file/priority/ok_comma_1.sd | 7 + .../simple_tests/file/priority/ok_comma_2.sd | 7 + .../simple_tests/file/priority/ok_deny_1.sd | 9 + .../simple_tests/file/priority/ok_deny_2.sd | 9 + .../simple_tests/file/priority/ok_deny_3.sd | 10 + .../simple_tests/file/priority/ok_deny_4.sd | 10 + .../file/priority/ok_deny_link.sd | 9 + .../file/priority/ok_embedded_spaces_1.sd | 6 + .../file/priority/ok_embedded_spaces_2.sd | 6 + .../file/priority/ok_embedded_spaces_3.sd | 6 + .../file/priority/ok_embedded_spaces_4.sd | 6 + .../file/priority/ok_inv_char_class.sd | 7 + .../simple_tests/file/priority/ok_link_1.sd | 10 + .../simple_tests/file/priority/ok_link_2.sd | 10 + .../simple_tests/file/priority/ok_link_3.sd | 10 + .../ok_link_audit_deny_owner_subset.sd | 10 + .../file/priority/ok_link_owner.sd | 10 + .../simple_tests/file/priority/ok_lock_1.sd | 17 + .../simple_tests/file/priority/ok_mmap_1.sd | 12 + .../simple_tests/file/priority/ok_mmap_2.sd | 14 + .../simple_tests/file/priority/ok_octal_1.sd | 8 + .../simple_tests/file/priority/ok_octal_2.sd | 8 + .../simple_tests/file/priority/ok_other_1.sd | 7 + .../simple_tests/file/priority/ok_other_2.sd | 7 + .../simple_tests/file/priority/ok_other_3.sd | 7 + .../simple_tests/file/priority/ok_quoted_1.sd | 9 + .../simple_tests/file/priority/ok_quoted_2.sd | 9 + .../simple_tests/file/priority/ok_quoted_3.sd | 9 + .../simple_tests/file/priority/ok_quoted_4.sd | 9 + .../simple_tests/file/priority/ok_quoted_5.sd | 9 + .../file/priority/ok_slashquote_1.sd | 8 + .../file/priority/stacking_ok_1.sd | 7 + .../file/priority/var1_ok_audit_deny_link.sd | 10 + .../file/priority/var1_ok_deny_link.sd | 10 + .../file/priority/var1_ok_link_1.sd | 11 + .../file/priority/var1_ok_link_2.sd | 11 + .../file/priority/var1_ok_link_3.sd | 11 + .../priority/var1_src_ok_audit_deny_link.sd | 10 + .../file/priority/var1_src_ok_deny_link.sd | 10 + .../file/priority/var1_src_ok_link_1.sd | 11 + .../file/priority/var1_src_ok_link_2.sd | 11 + .../file/priority/var1_src_ok_link_3.sd | 11 + .../var1_target_ok_audit_deny_link.sd | 10 + .../file/priority/var1_target_ok_deny_link.sd | 10 + .../file/priority/var1_target_ok_link_1.sd | 11 + .../file/priority/var1_target_ok_link_2.sd | 11 + .../file/priority/var1_target_ok_link_3.sd | 11 + .../file/priority/var2_ok_audit_deny_link.sd | 10 + .../file/priority/var2_ok_deny_link.sd | 10 + .../file/priority/var2_ok_link_1.sd | 11 + .../file/priority/var2_ok_link_2.sd | 11 + .../file/priority/var2_ok_link_3.sd | 11 + .../priority/var2_src_ok_audit_deny_link.sd | 10 + .../file/priority/var2_src_ok_deny_link.sd | 10 + .../file/priority/var2_src_ok_link_1.sd | 11 + .../file/priority/var2_src_ok_link_2.sd | 11 + .../file/priority/var2_src_ok_link_3.sd | 11 + .../var2_target_ok_audit_deny_link.sd | 10 + .../file/priority/var2_target_ok_deny_link.sd | 10 + .../file/priority/var2_target_ok_link_1.sd | 11 + .../file/priority/var2_target_ok_link_2.sd | 11 + .../file/priority/var2_target_ok_link_3.sd | 11 + utils/test/test-parser-simple-tests.py | 3 + 89 files changed, 1267 insertions(+), 285 deletions(-) create mode 100644 parser/tst/simple_tests/file/priority/front_perms_ok_1.sd create mode 100644 parser/tst/simple_tests/file/priority/front_perms_ok_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_4.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_5.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_alternations_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_alternations_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_alternations_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_append_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_bare_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_carat_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_carat_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_comma_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_comma_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_deny_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_deny_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_deny_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_deny_4.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_embedded_spaces_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_embedded_spaces_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_embedded_spaces_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_embedded_spaces_4.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_inv_char_class.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_link_audit_deny_owner_subset.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_link_owner.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_lock_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_mmap_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_mmap_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_octal_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_octal_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_other_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_other_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_other_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_quoted_1.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_quoted_2.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_quoted_3.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_quoted_4.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_quoted_5.sd create mode 100644 parser/tst/simple_tests/file/priority/ok_slashquote_1.sd create mode 100644 parser/tst/simple_tests/file/priority/stacking_ok_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_src_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_src_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_src_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_src_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_src_ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_target_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_target_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_target_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_target_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var1_target_ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_src_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_src_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_src_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_src_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_src_ok_link_3.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_target_ok_audit_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_target_ok_deny_link.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_target_ok_link_1.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_target_ok_link_2.sd create mode 100644 parser/tst/simple_tests/file/priority/var2_target_ok_link_3.sd diff --git a/parser/af_unix.cc b/parser/af_unix.cc index 29724cd18..f9b977a49 100644 --- a/parser/af_unix.cc +++ b/parser/af_unix.cc @@ -203,7 +203,7 @@ void unix_rule::downgrade_rule(Profile &prof) { prof.net.audit[AF_UNIX] |= mask; const char *error; network_rule *netv8 = new network_rule(perms, AF_UNIX, sock_type_n); - if(!netv8->add_prefix({audit, rule_mode, owner}, error)) + if(!netv8->add_prefix({0, audit, rule_mode, owner}, error)) yyerror(error); prof.rule_ents.push_back(netv8); } else { diff --git a/parser/af_unix.h b/parser/af_unix.h index 3441263e9..70685380c 100644 --- a/parser/af_unix.h +++ b/parser/af_unix.h @@ -48,6 +48,9 @@ public: }; virtual bool valid_prefix(const prefixes &p, const char *&error) { + // priority is partially supported for unix rules + // rules that get downgraded to just network socket + // won't support them but the fine grained do. if (p.owner) { error = "owner prefix not allowed on unix rules"; return false; diff --git a/parser/all_rule.h b/parser/all_rule.h index d72020ab4..eb936d520 100644 --- a/parser/all_rule.h +++ b/parser/all_rule.h @@ -32,6 +32,10 @@ public: all_rule(void): prefix_rule_t(RULE_TYPE_ALL) { } virtual bool valid_prefix(const prefixes &p, const char *&error) { + if (p.priority != 0) { + error = _("priority prefix not allowed on all rules"); + return false; + } if (p.owner) { error = _("owner prefix not allowed on all rules"); return false; diff --git a/parser/apparmor.d.pod b/parser/apparmor.d.pod index 4b03295f1..6624bb587 100644 --- a/parser/apparmor.d.pod +++ b/parser/apparmor.d.pod @@ -139,9 +139,11 @@ B = (must start with alphanumeric character. See aa_change_hat(2) for a B = I I +B = (+ | -)? [[:digit:]]+ + B = ( 'allow' | 'deny' ) -B = [ 'audit' ] [ I ] +B = [ 'priority' '=' ] [ 'audit' ] [ I ] B = [ I ] 'capability' [ I ] @@ -1878,6 +1880,17 @@ Rule qualifiers can modify the rule and/or permissions within the rule. =over 4 +=item B + +Specifies the priority of the rule. Currently the allowed range is +-1000 to 1000 with the default priority of rule is 0. Rules with +higher priority are given preferences and will completely override +permissions of lower priority rules where they overlap. When rules +partially overlap the permissions of the higher priority rule will +completely override lower priority rules within in overlap. Within a +given priority level rules that overlap will accumulate permissions in +the standard apparmor fashion. + =item B Specifies that permissions requests that match the rule are allowed. This diff --git a/parser/network.h b/parser/network.h index cb396c9f1..5298ce6a5 100644 --- a/parser/network.h +++ b/parser/network.h @@ -194,7 +194,12 @@ public: bool parse_address(ip_conds &entry); bool parse_port(ip_conds &entry); + // update TODO: in equality.sh when priority is a valid prefix virtual bool valid_prefix(const prefixes &p, const char *&error) { + if (p.priority != 0) { + error = _("priority prefix not allowed on network rules"); + return false; + } if (p.owner) { error = _("owner prefix not allowed on network rules"); return false; diff --git a/parser/parser.h b/parser/parser.h index ca7274f25..6f0425c81 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -53,6 +53,12 @@ using namespace std; */ extern int parser_token; +/* Arbitrary max and minimum priority that userspace can specify, internally + * we handle up to INT_MAX and INT_MIN. Do not ever allow INT_MAX, see + * note on mediates_priority + */ +#define MAX_PRIORITY 1000 +#define MIN_PRIORITY -1000 #define WARN_RULE_NOT_ENFORCED 0x1 #define WARN_RULE_DOWNGRADED 0x2 diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 62303fa39..15f0bccd2 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -277,6 +277,7 @@ QUOTED_ID \"{ALLOWED_QUOTED_ID}*\" IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER} +INTEGER [+-]?{NUMBER} HAT hat{WS}* PROFILE profile{WS}* KEYWORD [[:alpha:]_]+ @@ -332,7 +333,7 @@ GT > %x USERNS_MODE %x MQUEUE_MODE %x IOURING_MODE - +%x INTEGER_MODE %% %{ @@ -344,7 +345,7 @@ GT > } %} -{ +{ {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } } @@ -389,6 +390,11 @@ GT > yylval.id = processid(yytext, yyleng); PUSH_AND_RETURN(EXTCONDLIST_MODE, TOK_CONDLISTID); } + priority/{WS}*= { + /* has to be before {VARIABLE_NAME} matches below */ + PUSH_AND_RETURN(INTEGER_MODE, TOK_PRIORITY); + + } {VARIABLE_NAME}/{WS}*= { /* we match to the = in the lexer so that we can switch scanner * state. By the time the parser see the = it may be too late @@ -630,6 +636,15 @@ GT > } } +{ + {EQUALS} { RETURN_TOKEN(TOK_EQUALS); } + + {INTEGER} { + yylval.mode = strdup(yytext); + POP_AND_RETURN(TOK_VALUE); + } +} + #include{WS}+if{WS}+exists/{WS}.*\r?\n { /* Don't use PUSH() macro here as we don't want #include echoed out. * It needs to be handled specially @@ -814,4 +829,5 @@ unordered_map state_names = { STATE_TABLE_ENT(USERNS_MODE), STATE_TABLE_ENT(MQUEUE_MODE), STATE_TABLE_ENT(IOURING_MODE), + STATE_TABLE_ENT(INTEGER_MODE), }; diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 9664c120b..bdbe4bb65 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -131,7 +131,7 @@ static struct keyword_table keyword_table[] = { {"override_creds", TOK_OVERRIDE_CREDS}, {"sqpoll", TOK_SQPOLL}, {"all", TOK_ALL}, - + {"priority", TOK_PRIORITY}, /* terminate */ {NULL, 0} }; diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index c8fa67df5..532ddb55a 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -19,6 +19,7 @@ */ #define YYERROR_VERBOSE 1 +#include #include #include #include @@ -149,6 +150,7 @@ static void abi_features(char *filename, bool search); %token TOK_OVERRIDE_CREDS %token TOK_SQPOLL %token TOK_ALL +%token TOK_PRIORITY /* rlimits */ %token TOK_RLIMIT @@ -269,6 +271,7 @@ static void abi_features(char *filename, bool search); %type id_or_var %type opt_id_or_var %type opt_subset_flag +%type opt_priority %type opt_audit_flag %type opt_owner_flag %type opt_profile_flag @@ -627,6 +630,23 @@ opt_subset_flag: { /* nothing */ $$ = false; } | TOK_SUBSET { $$ = true; } | TOK_LE { $$ = true; } + +opt_priority: { $$ = 0; } + | TOK_PRIORITY TOK_EQUALS TOK_VALUE + { + char *end; + long tmp = strtol($3, &end, 10); + if (end == $3 || *end != '\0') + yyerror("invalid priority %s", $3); + free($3); + /* see note on mediates_priority */ + if (tmp > MAX_PRIORITY) + yyerror("invalid priority %l > %d", tmp, MAX_PRIORITY); + if (tmp < MIN_PRIORITY) + yyerror("invalid priority %l > %d", tmp, MIN_PRIORITY); + $$ = tmp; + } + opt_audit_flag: { /* nothing */ $$ = AUDIT_UNSPECIFIED; } | TOK_AUDIT { $$ = AUDIT_FORCE; }; @@ -639,11 +659,12 @@ opt_rule_mode: { /* nothing */ $$ = RULE_UNSPECIFIED; } | TOK_DENY { $$ = RULE_DENY; } | TOK_PROMPT { $$ = RULE_PROMPT; } -opt_prefix: opt_audit_flag opt_rule_mode opt_owner_flag +opt_prefix: opt_priority opt_audit_flag opt_rule_mode opt_owner_flag { - $$.audit = $1; - $$.rule_mode = $2; - $$.owner = $3; + $$.priority = $1; + $$.audit = $2; + $$.rule_mode = $3; + $$.owner = $4; } rules: { /* nothing */ @@ -680,6 +701,9 @@ rules: rules opt_prefix block { struct cod_entry *entry, *tmp; + if (($2).priority != 0) { + yyerror(_("priority is not allowed on rule blocks")); + } PDEBUG("matched: %s%s%sblock\n", $2.audit == AUDIT_FORCE ? "audit " : "", $2.rule_mode == RULE_DENY ? "deny " : "", diff --git a/parser/rule.h b/parser/rule.h index ec4664755..e48089d3b 100644 --- a/parser/rule.h +++ b/parser/rule.h @@ -82,11 +82,10 @@ class rule_t { public: int rule_type; rule_flags_t flags; - int priority; rule_t *removed_by; - rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE), priority(0), removed_by(NULL) { } + 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; } @@ -114,9 +113,6 @@ public: virtual int expand_variables(void) = 0; virtual int cmp(rule_t const &rhs) const { - int tmp = priority - rhs.priority; - if (tmp != 0) - return tmp; return rule_type - rhs.rule_type; } virtual bool operator<(rule_t const &rhs) const { @@ -177,6 +173,7 @@ typedef enum { OWNER_UNSPECIFIED, OWNER_SPECIFIED, OWNER_NOT } owner_t; */ class prefixes { public: + int priority; audit_t audit; rule_mode_t rule_mode; owner_t owner; @@ -246,7 +243,10 @@ public: } int cmp(prefixes const &rhs) const { - int tmp = (int) audit - (int) rhs.audit; + int tmp = priority - rhs.priority; + if (tmp != 0) + return tmp; + tmp = (int) audit - (int) rhs.audit; if (tmp != 0) return tmp; tmp = (int) rule_mode - (int) rhs.rule_mode; @@ -271,6 +271,7 @@ public: prefix_rule_t(int t = RULE_TYPE_PREFIX) : rule_t(t) { /* Must construct prefix here see note on prefixes */ + priority = 0; audit = AUDIT_UNSPECIFIED; rule_mode = RULE_UNSPECIFIED; owner = OWNER_UNSPECIFIED; @@ -281,6 +282,19 @@ public: virtual bool add_prefix(const prefixes &p, const char *&error) { if (!valid_prefix(p, error)) return false; + + // priority does NOT conflict but allowed at the block + // level yet. priority at the block level applies to + // the entire block, but only for the level of rules + // it is at. + // priority within the block arranges order of rules + // within the block. + if (priority != 0) { + error = "priority levels not supported"; + return false; + } + priority = p.priority; + /* audit conflicts */ if (p.audit != AUDIT_UNSPECIFIED) { if (audit != AUDIT_UNSPECIFIED && diff --git a/parser/tst/equality.sh b/parser/tst/equality.sh index 082b68f7c..8c68a2854 100755 --- a/parser/tst/equality.sh +++ b/parser/tst/equality.sh @@ -122,169 +122,175 @@ verify_binary_inequality() verify_binary "inequality" "$@" } -printf "Equality Tests:\n" -verify_binary_equality "dbus send" \ - "/t { dbus send, }" \ - "/t { dbus write, }" \ - "/t { dbus w, }" +########################################################################## +### wrapper fn, should be indented but isn't to reduce wrap +verify_set() +{ + local p1="$1" + local p2="$2" + echo -e "\n equality $e of '$p1' vs '$p2'\n" -verify_binary_equality "dbus receive" \ - "/t { dbus receive, }" \ - "/t { dbus read, }" \ - "/t { dbus r, }" +verify_binary_equality "'$p1'x'$p2' dbus send" \ + "/t { $p1 dbus send, }" \ + "/t { $p2 dbus write, }" \ + "/t { $p2 dbus w, }" -verify_binary_equality "dbus send + receive" \ - "/t { dbus (send, receive), }" \ - "/t { dbus (read, write), }" \ - "/t { dbus (r, w), }" \ - "/t { dbus (rw), }" \ - "/t { dbus rw, }" \ +verify_binary_equality "'$p1'x'$p2' dbus receive" \ + "/t { $p1 dbus receive, }" \ + "/t { $p2 dbus read, }" \ + "/t { $p2 dbus r, }" -verify_binary_equality "dbus all accesses" \ - "/t { dbus (send, receive, bind, eavesdrop), }" \ - "/t { dbus (read, write, bind, eavesdrop), }" \ - "/t { dbus (r, w, bind, eavesdrop), }" \ - "/t { dbus (rw, bind, eavesdrop), }" \ - "/t { dbus (), }" \ - "/t { dbus, }" \ +verify_binary_equality "'$p1'x'$p2' dbus send + receive" \ + "/t { $p1 dbus (send, receive), }" \ + "/t { $p2 dbus (read, write), }" \ + "/t { $p2 dbus (r, w), }" \ + "/t { $p2 dbus (rw), }" \ + "/t { $p2 dbus rw, }" \ -verify_binary_equality "dbus implied accesses with a bus conditional" \ - "/t { dbus (send, receive, bind, eavesdrop) bus=session, }" \ - "/t { dbus (read, write, bind, eavesdrop) bus=session, }" \ - "/t { dbus (r, w, bind, eavesdrop) bus=session, }" \ - "/t { dbus (rw, bind, eavesdrop) bus=session, }" \ - "/t { dbus () bus=session, }" \ - "/t { dbus bus=session, }" \ +verify_binary_equality "'$p1'x'$p2' dbus all accesses" \ + "/t { $p1 dbus (send, receive, bind, eavesdrop), }" \ + "/t { $p2 dbus (read, write, bind, eavesdrop), }" \ + "/t { $p2 dbus (r, w, bind, eavesdrop), }" \ + "/t { $p2 dbus (rw, bind, eavesdrop), }" \ + "/t { $p2 dbus (), }" \ + "/t { $p2 dbus, }" \ -verify_binary_equality "dbus implied accesses for services" \ - "/t { dbus bind name=com.foo, }" \ - "/t { dbus name=com.foo, }" +verify_binary_equality "'$p1'x'$p2' dbus implied accesses with a bus conditional" \ + "/t { $p1 dbus (send, receive, bind, eavesdrop) bus=session, }" \ + "/t { $p2 dbus (read, write, bind, eavesdrop) bus=session, }" \ + "/t { $p2 dbus (r, w, bind, eavesdrop) bus=session, }" \ + "/t { $p2 dbus (rw, bind, eavesdrop) bus=session, }" \ + "/t { $p2 dbus () bus=session, }" \ + "/t { $p2 dbus bus=session, }" \ -verify_binary_equality "dbus implied accesses for messages" \ - "/t { dbus (send, receive) path=/com/foo interface=org.foo, }" \ - "/t { dbus path=/com/foo interface=org.foo, }" +verify_binary_equality "'$p1'x'$p2' dbus implied accesses for services" \ + "/t { $p1 dbus bind name=com.foo, }" \ + "/t { $p2 dbus name=com.foo, }" -verify_binary_equality "dbus implied accesses for messages with peer names" \ - "/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(name=com.foo), }" \ - "/t { dbus path=/com/foo interface=org.foo peer=(name=com.foo), }" \ - "/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(name=(com.foo)), }" \ - "/t { dbus path=/com/foo interface=org.foo peer=(name=(com.foo)), }" +verify_binary_equality "'$p1'x'$p2' dbus implied accesses for messages" \ + "/t { $p1 dbus (send, receive) path=/com/foo interface=org.foo, }" \ + "/t { $p2 dbus path=/com/foo interface=org.foo, }" -verify_binary_equality "dbus implied accesses for messages with peer labels" \ - "/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }" \ - "/t { dbus path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }" +verify_binary_equality "'$p1'x'$p2' dbus implied accesses for messages with peer names" \ + "/t { $p1 dbus (send, receive) path=/com/foo interface=org.foo peer=(name=com.foo), }" \ + "/t { $p2 dbus path=/com/foo interface=org.foo peer=(name=com.foo), }" \ + "/t { $p2 dbus (send, receive) path=/com/foo interface=org.foo peer=(name=(com.foo)), }" \ + "/t { $p2 dbus path=/com/foo interface=org.foo peer=(name=(com.foo)), }" -verify_binary_equality "dbus element parsing" \ - "/t { dbus bus=b path=/ interface=i member=m peer=(name=n label=l), }" \ - "/t { dbus bus=\"b\" path=\"/\" interface=\"i\" member=\"m\" peer=(name=\"n\" label=\"l\"), }" \ - "/t { dbus bus=(b) path=(/) interface=(i) member=(m) peer=(name=(n) label=(l)), }" \ - "/t { dbus bus=(\"b\") path=(\"/\") interface=(\"i\") member=(\"m\") peer=(name=(\"n\") label=(\"l\")), }" \ - "/t { dbus bus =b path =/ interface =i member =m peer =(name =n label =l), }" \ - "/t { dbus bus= b path= / interface= i member= m peer= (name= n label= l), }" \ - "/t { dbus bus = b path = / interface = i member = m peer = ( name = n label = l ), }" +verify_binary_equality "'$p1'x'$p2' dbus implied accesses for messages with peer labels" \ + "/t { $p1 dbus (send, receive) path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }" \ + "/t { $p2 dbus path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }" -verify_binary_equality "dbus access parsing" \ - "/t { dbus, }" \ - "/t { dbus (), }" \ - "/t { dbus (send, receive, bind, eavesdrop), }" \ - "/t { dbus (send receive bind eavesdrop), }" \ - "/t { dbus (send, receive bind, eavesdrop), }" \ - "/t { dbus (send,receive,bind,eavesdrop), }" \ - "/t { dbus (send,receive,,,,,,,,,,,,,,,,bind,eavesdrop), }" \ - "/t { dbus (send,send,send,send send receive,bind eavesdrop), }" \ +verify_binary_equality "'$p1'x'$p2' dbus element parsing" \ + "/t { $p1 dbus bus=b path=/ interface=i member=m peer=(name=n label=l), }" \ + "/t { $p2 dbus bus=\"b\" path=\"/\" interface=\"i\" member=\"m\" peer=(name=\"n\" label=\"l\"), }" \ + "/t { $p2 dbus bus=(b) path=(/) interface=(i) member=(m) peer=(name=(n) label=(l)), }" \ + "/t { $p2 dbus bus=(\"b\") path=(\"/\") interface=(\"i\") member=(\"m\") peer=(name=(\"n\") label=(\"l\")), }" \ + "/t { $p2 dbus bus =b path =/ interface =i member =m peer =(name =n label =l), }" \ + "/t { $p2 dbus bus= b path= / interface= i member= m peer= (name= n label= l), }" \ + "/t { $p2 dbus bus = b path = / interface = i member = m peer = ( name = n label = l ), }" -verify_binary_equality "dbus variable expansion" \ - "/t { dbus (send, receive) path=/com/foo member=spork interface=org.foo peer=(name=com.foo label=/com/foo), }" \ +verify_binary_equality "'$p1'x'$p2' dbus access parsing" \ + "/t { $p1 dbus, }" \ + "/t { $p2 dbus (), }" \ + "/t { $p2 dbus (send, receive, bind, eavesdrop), }" \ + "/t { $p2 dbus (send receive bind eavesdrop), }" \ + "/t { $p2 dbus (send, receive bind, eavesdrop), }" \ + "/t { $p2 dbus (send,receive,bind,eavesdrop), }" \ + "/t { $p2 dbus (send,receive,,,,,,,,,,,,,,,,bind,eavesdrop), }" \ + "/t { $p2 dbus (send,send,send,send send receive,bind eavesdrop), }" \ + +verify_binary_equality "'$p1'x'$p2' dbus variable expansion" \ + "/t { $p1 dbus (send, receive) path=/com/foo member=spork interface=org.foo peer=(name=com.foo label=/com/foo), }" \ "@{FOO}=foo - /t { dbus (send, receive) path=/com/@{FOO} member=spork interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO} member=spork interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \ "@{FOO}=foo @{SPORK}=spork - /t { dbus (send, receive) path=/com/@{FOO} member=@{SPORK} interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO} member=@{SPORK} interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \ "@{FOO}=/com/foo - /t { dbus (send, receive) path=@{FOO} member=spork interface=org.foo peer=(name=com.foo label=@{FOO}), }" \ + /t { $p2 dbus (send, receive) path=@{FOO} member=spork interface=org.foo peer=(name=com.foo label=@{FOO}), }" \ "@{FOO}=com - /t { dbus (send, receive) path=/@{FOO}/foo member=spork interface=org.foo peer=(name=@{FOO}.foo label=/@{FOO}/foo), }" + /t { $p2 dbus (send, receive) path=/@{FOO}/foo member=spork interface=org.foo peer=(name=@{FOO}.foo label=/@{FOO}/foo), }" -verify_binary_equality "dbus variable expansion, multiple values/rules" \ - "/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \ - "/t { dbus (send, receive) path=/com/{foo,bar}, }" \ - "/t { dbus (send, receive) path={/com/foo,/com/bar}, }" \ +verify_binary_equality "'$p1'x'$p2' dbus variable expansion, multiple values/rules" \ + "/t { $p1 dbus (send, receive) path=/com/foo, $p1 dbus (send, receive) path=/com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com/{foo,bar}, }" \ + "/t { $p2 dbus (send, receive) path={/com/foo,/com/bar}, }" \ "@{FOO}=foo - /t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/bar, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com/bar, }" \ "@{FOO}=foo bar - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=bar foo - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}={bar,foo} - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=foo @{BAR}=bar - /t { dbus (send, receive) path=/com/{@{FOO},@{BAR}}, }" \ + /t { $p2 dbus (send, receive) path=/com/{@{FOO},@{BAR}}, }" \ -verify_binary_equality "dbus variable expansion, ensure rule de-duping occurs" \ - "/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \ - "/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, dbus (send, receive) path=/com/bar, }" \ +verify_binary_equality "'$p1'x'$p2' dbus variable expansion, ensure rule de-duping occurs" \ + "/t { $p1 dbus (send, receive) path=/com/foo, $p1 dbus (send, receive) path=/com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com/foo, $p2 dbus (send, receive) path=/com/bar, dbus (send, receive) path=/com/bar, }" \ "@{FOO}=bar foo bar foo - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=bar foo bar foo - /t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/@{FOO}, }" + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com/@{FOO}, }" -verify_binary_equality "dbus minimization with all perms" \ - "/t { dbus, }" \ - "/t { dbus bus=session, dbus, }" \ - "/t { dbus (send, receive, bind, eavesdrop), dbus, }" +verify_binary_equality "'$p1'x'$p2' dbus minimization with all perms" \ + "/t { $p1 dbus, }" \ + "/t { $p2 dbus bus=session, $p2 dbus, }" \ + "/t { $p2 dbus (send, receive, bind, eavesdrop), $p2 dbus, }" -verify_binary_equality "dbus minimization with bind" \ - "/t { dbus bind, }" \ - "/t { dbus bind bus=session, dbus bind, }" \ - "/t { dbus bind bus=system name=com.foo, dbus bind, }" +verify_binary_equality "'$p1'x'$p2' dbus minimization with bind" \ + "/t { $p1 dbus bind, }" \ + "/t { $p2 dbus bind bus=session, $p2 dbus bind, }" \ + "/t { $p2 dbus bind bus=system name=com.foo, $p2 dbus bind, }" -verify_binary_equality "dbus minimization with send and a bus conditional" \ - "/t { dbus send bus=system, }" \ - "/t { dbus send bus=system path=/com/foo interface=com.foo member=bar, dbus send bus=system, }" \ - "/t { dbus send bus=system peer=(label=/usr/bin/foo), dbus send bus=system, }" +verify_binary_equality "'$p1'x'$p2' dbus minimization with send and a bus conditional" \ + "/t { $p1 dbus send bus=system, }" \ + "/t { $p2 dbus send bus=system path=/com/foo interface=com.foo member=bar, dbus send bus=system, }" \ + "/t { $p2 dbus send bus=system peer=(label=/usr/bin/foo), $p2 dbus send bus=system, }" -verify_binary_equality "dbus minimization with an audit modifier" \ - "/t { audit dbus eavesdrop, }" \ - "/t { audit dbus eavesdrop bus=session, audit dbus eavesdrop, }" +verify_binary_equality "'$p1'x'$p2' dbus minimization with an audit modifier" \ + "/t { $p1 audit dbus eavesdrop, }" \ + "/t { $p2 audit dbus eavesdrop bus=session, $p2 audit dbus eavesdrop, }" -verify_binary_equality "dbus minimization with a deny modifier" \ - "/t { deny dbus send bus=system peer=(name=com.foo), }" \ - "/t { deny dbus send bus=system peer=(name=com.foo label=/usr/bin/foo), deny dbus send bus=system peer=(name=com.foo), }" \ +verify_binary_equality "'$p1'x'$p2' dbus minimization with a deny modifier" \ + "/t { $p1 deny dbus send bus=system peer=(name=com.foo), }" \ + "/t { $p2 deny dbus send bus=system peer=(name=com.foo label=/usr/bin/foo), $p2 deny dbus send bus=system peer=(name=com.foo), }" \ -verify_binary_equality "dbus minimization found in dbus abstractions" \ - "/t { dbus send bus=session, }" \ - "/t { dbus send +verify_binary_equality "'$p1'x'$p2' dbus minimization found in dbus abstractions" \ + "/t { $p1 dbus send bus=session, }" \ + "/t { $p2 dbus send bus=session path=/org/freedesktop/DBus interface=org.freedesktop.DBus member={Hello,AddMatch,RemoveMatch,GetNameOwner,NameHasOwner,StartServiceByName} peer=(name=org.freedesktop.DBus), - dbus send bus=session, }" + $p2 dbus send bus=session, }" # verify slash filtering for dbus paths. -verify_binary_equality "dbus slash filtering for paths" \ - "/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \ - "/t { dbus (send, receive) path=/com///foo, dbus (send, receive) path=///com/bar, }" \ - "/t { dbus (send, receive) path=/com//{foo,bar}, }" \ - "/t { dbus (send, receive) path={//com/foo,/com//bar}, }" \ +verify_binary_equality "'$p1'x'$p2' dbus slash filtering for paths" \ + "/t { $p1 dbus (send, receive) path=/com/foo, $p1 dbus (send, receive) path=/com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com///foo, $p2 dbus (send, receive) path=///com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com//{foo,bar}, }" \ + "/t { $p2 dbus (send, receive) path={//com/foo,/com//bar}, }" \ "@{FOO}=/foo - /t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/bar, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com/bar, }" \ "@{FOO}=/foo /bar - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=/bar //foo - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=//{bar,foo} - /t { dbus (send, receive) path=/com/@{FOO}, }" \ + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ "@{FOO}=/foo @{BAR}=bar - /t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com//@{BAR}, }" + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com//@{BAR}, }" # Rules compatible with audit, deny, and audit deny # note: change_profile does not support audit/allow/deny atm for rule in "capability" "capability mac_admin" \ - "network" "network tcp" "network inet6 tcp"\ "mount" "mount /a" "mount /a -> /b" "mount options in (ro) /a -> b" \ "remount" "remount /a" \ "umount" "umount /a" \ @@ -302,6 +308,35 @@ for rule in "capability" "capability mac_admin" \ "link /a -> /b" "link subset /a -> /b" \ "l /a -> /b" "l subset /a -> /b" \ "file l /a -> /b" "l subset /a -> /b" +do + verify_binary_equality "'$p1'x'$p2' allow modifier for \"${rule}\"" \ + "/t { $p1 ${rule}, }" \ + "/t { $p2 allow ${rule}, }" + + verify_binary_equality "'$p1'x'$p2' audit allow modifier for \"${rule}\"" \ + "/t { $p1 audit ${rule}, }" \ + "/t { $p2 audit allow ${rule}, }" + + verify_binary_inequality "'$p1'x'$p2' audit, deny, and audit deny modifiers for \"${rule}\"" \ + "/t { $p1 ${rule}, }" \ + "/t { $p2 audit ${rule}, }" \ + "/t { $p2 audit allow ${rule}, }" \ + "/t { $p2 deny ${rule}, }" \ + "/t { $p2 audit deny ${rule}, }" + + verify_binary_inequality "'$p1'x'$p2' audit vs deny and audit deny modifiers for \"${rule}\"" \ + "/t { $p1 audit ${rule}, }" \ + "/t { $p2 deny ${rule}, }" \ + "/t { $p2 audit deny ${rule}, }" + + verify_binary_inequality "'$p1'x'$p2' deny and audit deny modifiers for \"${rule}\"" \ + "/t { $p1 deny ${rule}, }" \ + "/t { $p2 audit deny ${rule}, }" +done + +####### special case for network TODO: for network above when network +####### rules fixed +for rule in "network" "network tcp" "network inet6 tcp" do verify_binary_equality "allow modifier for \"${rule}\"" \ "/t { ${rule}, }" \ @@ -357,36 +392,36 @@ for rule in "/f ux" "/f Ux" "/f px" "/f Px" "/f cx" "/f Cx" "/f ix" \ "file /* cux -> b" "file /* Cux -> b" "file /* cix -> b" "file /* Cix -> b" do - verify_binary_equality "allow modifier for \"${rule}\"" \ - "/t { ${rule}, }" \ - "/t { allow ${rule}, }" + verify_binary_equality "'$p1'x'$p2' allow modifier for \"${rule}\"" \ + "/t { $p1 ${rule}, }" \ + "/t { $p2 allow ${rule}, }" - verify_binary_equality "audit allow modifier for \"${rule}\"" \ - "/t { audit ${rule}, }" \ - "/t { audit allow ${rule}, }" + verify_binary_equality "'$p1'x'$p2' audit allow modifier for \"${rule}\"" \ + "/t { $p1 audit ${rule}, }" \ + "/t { $p2 audit allow ${rule}, }" # skip rules that don't end with x perm if [ -n "${rule##*x}" ] ; then continue ; fi - verify_binary_inequality "deny, audit deny modifier for \"${rule}\"" \ - "/t { ${rule}, }" \ - "/t { audit ${rule}, }" \ - "/t { audit allow ${rule}, }" \ - "/t { deny ${rule% *} x, }" \ - "/t { audit deny ${rule% *} x, }" + verify_binary_inequality "'$p1'x'$p2' deny, audit deny modifier for \"${rule}\"" \ + "/t { $p1 ${rule}, }" \ + "/t { $p2 audit ${rule}, }" \ + "/t { $p2 audit allow ${rule}, }" \ + "/t { $p2 deny ${rule% *} x, }" \ + "/t { $p2 audit deny ${rule% *} x, }" - verify_binary_inequality "audit vs deny and audit deny modifiers for \"${rule}\"" \ - "/t { audit ${rule}, }" \ - "/t { deny ${rule% *} x, }" \ - "/t { audit deny ${rule% *} x, }" + verify_binary_inequality "'$p1'x'$p2' audit vs deny and audit deny modifiers for \"${rule}\"" \ + "/t { $p1 audit ${rule}, }" \ + "/t { $p2 deny ${rule% *} x, }" \ + "/t { $p2 audit deny ${rule% *} x, }" done # verify deny and audit deny differ for x perms for prefix in "/f" "/*" "file /f" "file /*" ; do - verify_binary_inequality "deny and audit deny x modifiers for \"${prefix}\"" \ - "/t { deny ${prefix} x, }" \ - "/t { audit deny ${prefix} x, }" + verify_binary_inequality "'$p1'x'$p2' deny and audit deny x modifiers for \"${prefix}\"" \ + "/t { $p1 deny ${prefix} x, }" \ + "/t { $p2 audit deny ${prefix} x, }" done #Test equality of leading and trailing file permissions @@ -403,26 +438,26 @@ for audit in "" "audit" ; do "lkm" "rwlk" "rwlm" "rwkm" \ "ralk" "ralm" "wlkm" "alkm" \ "rwlkm" "ralkm" ; do - verify_binary_equality "leading and trailing perms for \"${perm}\"" \ - "/t { ${prefix} /f ${perm}, }" \ - "/t { ${prefix} ${perm} /f, }" + verify_binary_equality "'$p1'x'$p2' leading and trailing perms for \"${perm}\"" \ + "/t { $p1 ${prefix} /f ${perm}, }" \ + "/t { $p2 ${prefix} ${perm} /f, }" done if [ "$allow" == "deny" ] ; then continue ; fi for perm in "ux" "Ux" "px" "Px" "cx" "Cx" \ "ix" "pux" "Pux" "pix" "Pix" \ "cux" "Cux" "cix" "Cix" do - verify_binary_equality "leading and trailing perms for \"${perm}\"" \ - "/t { ${prefix} /f ${perm}, }" \ - "/t { ${prefix} ${perm} /f, }" + verify_binary_equality "'$p1'x'$p2' leading and trailing perms for \"${perm}\"" \ + "/t { $p1 ${prefix} /f ${perm}, }" \ + "/t { $p2 ${prefix} ${perm} /f, }" done for perm in "px" "Px" "cx" "Cx" \ "pux" "Pux" "pix" "Pix" \ "cux" "Cux" "cix" "Cix" do - verify_binary_equality "leading and trailing perms for x-transition \"${perm}\"" \ - "/t { ${prefix} /f ${perm} -> b, }" \ - "/t { ${prefix} ${perm} /f -> b, }" + verify_binary_equality "'$p1'x'$p2' leading and trailing perms for x-transition \"${perm}\"" \ + "/t { $p1 ${prefix} /f ${perm} -> b, }" \ + "/t { $p2 ${prefix} ${perm} /f -> b, }" done done done @@ -443,128 +478,103 @@ do "cix -> b" "Cix -> b" do if [ "$perm1" == "$perm2" ] ; then - verify_binary_equality "Exec perm \"${perm1}\" - most specific match: same as glob" \ - "/t { /* ${perm1}, /f ${perm2}, }" \ - "/t { /* ${perm1}, }" + verify_binary_equality "'$p1'x'$p2' Exec perm \"${perm1}\" - most specific match: same as glob" \ + "/t { $p1 /* ${perm1}, /f ${perm2}, }" \ + "/t { $p2 /* ${perm1}, }" else - verify_binary_inequality "Exec \"${perm1}\" vs \"${perm2}\" - most specific match: different from glob" \ - "/t { /* ${perm1}, /f ${perm2}, }" \ - "/t { /* ${perm1}, }" + verify_binary_inequality "'$p1'x'$p2' Exec \"${perm1}\" vs \"${perm2}\" - most specific match: different from glob" \ + "/t { $p1 /* ${perm1}, /f ${perm2}, }" \ + "/t { $p2 /* ${perm1}, }" fi done - verify_binary_inequality "Exec \"${perm1}\" vs deny x - most specific match: different from glob" \ - "/t { /* ${perm1}, audit deny /f x, }" \ - "/t { /* ${perm1}, }" + verify_binary_inequality "'$p1'x'$p2' Exec \"${perm1}\" vs deny x - most specific match: different from glob" \ + "/t { $p1 /* ${perm1}, audit deny /f x, }" \ + "/t { $p2 /* ${perm1}, }" done #Test deny carves out permission -verify_binary_inequality "Deny removes r perm" \ - "/t { /foo/[abc] r, audit deny /foo/b r, }" \ - "/t { /foo/[abc] r, }" +verify_binary_inequality "'$p1'x'$p2' Deny removes r perm" \ + "/t { $p1 /foo/[abc] r, audit deny /foo/b r, }" \ + "/t { $p2 /foo/[abc] r, }" -verify_binary_equality "Deny removes r perm" \ - "/t { /foo/[abc] r, audit deny /foo/b r, }" \ - "/t { /foo/[ac] r, }" +verify_binary_equality "'$p1'x'$p2' Deny removes r perm" \ + "/t { $p1 /foo/[abc] r, audit deny /foo/b r, }" \ + "/t { $p2 /foo/[ac] r, }" #this one may not be true in the future depending on if the compiled profile #is explicitly including deny permissions for dynamic composition -verify_binary_equality "Deny of ungranted perm" \ - "/t { /foo/[abc] r, audit deny /foo/b w, }" \ - "/t { /foo/[abc] r, }" +verify_binary_equality "'$p1'x'$p2' Deny of ungranted perm" \ + "/t { $p1 /foo/[abc] r, audit deny /foo/b w, }" \ + "/t { $p2 /foo/[abc] r, }" -verify_binary_equality "change_profile == change_profile -> **" \ - "/t { change_profile, }" \ - "/t { change_profile -> **, }" +verify_binary_equality "'$p1'x'$p2' change_profile == change_profile -> **" \ + "/t { $p1 change_profile, }" \ + "/t { $p2 change_profile -> **, }" -verify_binary_equality "change_profile /** == change_profile /** -> **" \ - "/t { change_profile /**, }" \ - "/t { change_profile /** -> **, }" +verify_binary_equality "'$p1'x'$p2' change_profile /** == change_profile /** -> **" \ + "/t { $p1 change_profile /**, }" \ + "/t { $p2 change_profile /** -> **, }" -verify_binary_equality "change_profile /** == change_profile /** -> **" \ - "/t { change_profile unsafe /**, }" \ - "/t { change_profile unsafe /** -> **, }" +verify_binary_equality "'$p1'x'$p2' change_profile /** == change_profile /** -> **" \ + "/t { $p1 change_profile unsafe /**, }" \ + "/t { $p2 change_profile unsafe /** -> **, }" -verify_binary_equality "change_profile /** == change_profile /** -> **" \ - "/t { change_profile /**, }" \ - "/t { change_profile safe /** -> **, }" +verify_binary_equality "'$p1'x'$p2' change_profile /** == change_profile /** -> **" \ + "/t { $p1 change_profile /**, }" \ + "/t { $p2 change_profile safe /** -> **, }" -verify_binary_inequality "change_profile /** == change_profile /** -> **" \ - "/t { change_profile /**, }" \ - "/t { change_profile unsafe /**, }" +verify_binary_inequality "'$p1'x'$p2' change_profile /** == change_profile /** -> **" \ + "/t { $p1 change_profile /**, }" \ + "/t { $p2 change_profile unsafe /**, }" -verify_binary_equality "profile name is hname in rule" \ - ":ns:/hname { signal peer=/hname, }" \ - ":ns:/hname { signal peer=@{profile_name}, }" +verify_binary_equality "'$p1'x'$p2' profile name is hname in rule" \ + ":ns:/hname { $p1 signal peer=/hname, }" \ + ":ns:/hname { $p2 signal peer=@{profile_name}, }" -verify_binary_inequality "profile name is NOT fq name in rule" \ - ":ns:/hname { signal peer=:ns:/hname, }" \ - ":ns:/hname { signal peer=@{profile_name}, }" +verify_binary_inequality "'$p1'x'$p2' profile name is NOT fq name in rule" \ + ":ns:/hname { $p1 signal peer=:ns:/hname, }" \ + ":ns:/hname { $p2 signal peer=@{profile_name}, }" -verify_binary_equality "profile name is hname in sub pofile rule" \ - ":ns:/hname { profile child { signal peer=/hname//child, } }" \ - ":ns:/hname { profile child { signal peer=@{profile_name}, } }" +verify_binary_equality "'$p1'x'$p2' profile name is hname in sub pofile rule" \ + ":ns:/hname { profile child { $p1 signal peer=/hname//child, } }" \ + ":ns:/hname { profile child { $p2 signal peer=@{profile_name}, } }" -verify_binary_inequality "profile name is NOT fq name in sub profile rule" \ - ":ns:/hname { profile child { signal peer=:ns:/hname//child, } }" \ - ":ns:/hname { profile child { signal peer=@{profile_name}, } }" +verify_binary_inequality "'$p1'x'$p2' profile name is NOT fq name in sub profile rule" \ + ":ns:/hname { profile child { $p1 signal peer=:ns:/hname//child, } }" \ + ":ns:/hname { profile child { $p2 signal peer=@{profile_name}, } }" -verify_binary_equality "profile name is hname in hat rule" \ - ":ns:/hname { ^child { signal peer=/hname//child, } }" \ - ":ns:/hname { ^child { signal peer=@{profile_name}, } }" +verify_binary_equality "'$p1'x'$p2' profile name is hname in hat rule" \ + ":ns:/hname { ^child { $p1 signal peer=/hname//child, } }" \ + ":ns:/hname { ^child { $p2 signal peer=@{profile_name}, } }" -verify_binary_inequality "profile name is NOT fq name in hat rule" \ - ":ns:/hname { ^child { signal peer=:ns:/hname//child, } }" \ - ":ns:/hname { ^child { signal peer=@{profile_name}, } }" +verify_binary_inequality "'$p1'x'$p2' profile name is NOT fq name in hat rule" \ + ":ns:/hname { ^child { $p1 signal peer=:ns:/hname//child, } }" \ + ":ns:/hname { ^child { $p2 signal peer=@{profile_name}, } }" -verify_binary_equality "@{profile_name} is literal in peer" \ - "/{a,b} { signal peer=/\{a,b\}, }" \ - "/{a,b} { signal peer=@{profile_name}, }" +verify_binary_equality "'$p1'x'$p2' @{profile_name} is literal in peer" \ + "/{a,b} { $p1 signal peer=/\{a,b\}, }" \ + "/{a,b} { $p2 signal peer=@{profile_name}, }" -verify_binary_equality "@{profile_name} is literal in peer with pattern" \ - "/{a,b} { signal peer={/\{a,b\},c}, }" \ - "/{a,b} { signal peer={@{profile_name},c}, }" +verify_binary_equality "'$p1'x'$p2' @{profile_name} is literal in peer with pattern" \ + "/{a,b} { $p1 signal peer={/\{a,b\},c}, }" \ + "/{a,b} { $p2 signal peer={@{profile_name},c}, }" -verify_binary_inequality "@{profile_name} is not pattern in peer" \ - "/{a,b} { signal peer=/{a,b}, }" \ - "/{a,b} { signal peer=@{profile_name}, }" +verify_binary_inequality "'$p1'x'$p2' @{profile_name} is not pattern in peer" \ + "/{a,b} { $p1 signal peer=/{a,b}, }" \ + "/{a,b} { $p2 signal peer=@{profile_name}, }" -verify_binary_equality "@{profile_name} is literal in peer with esc sequence" \ - "/\\\\a { signal peer=/\\\\a, }" \ - "/\\\\a { signal peer=@{profile_name}, }" +verify_binary_equality "'$p1'x'$p2' @{profile_name} is literal in peer with esc sequence" \ + "/\\\\a { $p1 signal peer=/\\\\a, }" \ + "/\\\\a { $p2 signal peer=@{profile_name}, }" -verify_binary_equality "@{profile_name} is literal in peer with esc alt sequence" \ - "/\\{a,b\\},c { signal peer=/\\{a,b\\},c, }" \ - "/\\{a,b\\},c { signal peer=@{profile_name}, }" +verify_binary_equality "'$p1'x'$p2' @{profile_name} is literal in peer with esc alt sequence" \ + "/\\{a,b\\},c { $p1 signal peer=/\\{a,b\\},c, }" \ + "/\\{a,b\\},c { $p2 signal peer=@{profile_name}, }" -# verify rlimit data conversions -verify_binary_equality "set rlimit rttime <= 12 weeks" \ - "/t { set rlimit rttime <= 12 weeks, }" \ - "/t { set rlimit rttime <= $((12 * 7)) days, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24)) hours, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24 * 60)) minutes, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60)) seconds, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000)) ms, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)) us, }" \ - "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)), }" - -verify_binary_equality "set rlimit cpu <= 42 weeks" \ - "/t { set rlimit cpu <= 42 weeks, }" \ - "/t { set rlimit cpu <= $((42 * 7)) days, }" \ - "/t { set rlimit cpu <= $((42 * 7 * 24)) hours, }" \ - "/t { set rlimit cpu <= $((42 * 7 * 24 * 60)) minutes, }" \ - "/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)) seconds, }" \ - "/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)), }" - -verify_binary_equality "set rlimit memlock <= 2GB" \ - "/t { set rlimit memlock <= 2GB, }" \ - "/t { set rlimit memlock <= $((2 * 1024)) MB, }" \ - "/t { set rlimit memlock <= $((2 * 1024 * 1024)) KB, }" \ - "/t { set rlimit memlock <= $((2 * 1024 * 1024 * 1024)) , }" - # Unfortunately we can not just compare an empty profile and hat to a # ie. "/t { ^test { /f r, }}" # to the second profile with the equivalent rule inserted manually @@ -577,62 +587,62 @@ verify_binary_equality "set rlimit memlock <= 2GB" \ # the "write" permission in the second profile and the test will fail. # If the parser is adding the change_hat proc attr rules then the # rules should merge and be equivalent. -verify_binary_equality "change_hat rules automatically inserted"\ - "/t { owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}" \ - "/t { owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}" +verify_binary_equality "'$p1'x'$p2' change_hat rules automatically inserted"\ + "/t { $p1 owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}" \ + "/t { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}" # verify slash filtering for unix socket address paths. # see https://bugs.launchpad.net/apparmor/+bug/1856738 -verify_binary_equality "unix rules addr conditional" \ - "/t { unix bind addr=@/a/bar, }" \ - "/t { unix bind addr=@/a//bar, }" \ - "/t { unix bind addr=@//a/bar, }" \ - "/t { unix bind addr=@/a///bar, }" \ +verify_binary_equality "'$p1'x'$p2' unix rules addr conditional" \ + "/t { $p1 unix bind addr=@/a/bar, }" \ + "/t { $p2 unix bind addr=@/a//bar, }" \ + "/t { $p2 unix bind addr=@//a/bar, }" \ + "/t { $p2 unix bind addr=@/a///bar, }" \ "@{HOME}=/a/ - /t { unix bind addr=@@{HOME}/bar, }" \ + /t { $p2 unix bind addr=@@{HOME}/bar, }" \ "@{HOME}=/a/ - /t { unix bind addr=@//@{HOME}bar, }" \ + /t { $p2 unix bind addr=@//@{HOME}bar, }" \ "@{HOME}=/a/ - /t { unix bind addr=@/@{HOME}/bar, }" + /t { $p2 unix bind addr=@/@{HOME}/bar, }" -verify_binary_equality "unix rules peer addr conditional" \ - "/t { unix peer=(addr=@/a/bar), }" \ - "/t { unix peer=(addr=@/a//bar), }" \ - "/t { unix peer=(addr=@//a/bar), }" \ - "/t { unix peer=(addr=@/a///bar), }" \ +verify_binary_equality "'$p1'x'$p2' unix rules peer addr conditional" \ + "/t { $p1 unix peer=(addr=@/a/bar), }" \ + "/t { $p2 unix peer=(addr=@/a//bar), }" \ + "/t { $p2 unix peer=(addr=@//a/bar), }" \ + "/t { $p2 unix peer=(addr=@/a///bar), }" \ "@{HOME}=/a/ - /t { unix peer=(addr=@@{HOME}/bar), }" \ + /t { $p2 unix peer=(addr=@@{HOME}/bar), }" \ "@{HOME}=/a/ - /t { unix peer=(addr=@//@{HOME}bar), }" \ + /t { $p2 unix peer=(addr=@//@{HOME}bar), }" \ "@{HOME}=/a/ - /t { unix peer=(addr=@/@{HOME}/bar), }" + /t { $p2 unix peer=(addr=@/@{HOME}/bar), }" # verify slash filtering for mount rules -verify_binary_equality "mount rules slash filtering" \ - "/t { mount /dev/foo -> /mnt/bar, }" \ - "/t { mount ///dev/foo -> /mnt/bar, }" \ - "/t { mount /dev/foo -> /mnt//bar, }" \ - "/t { mount /dev///foo -> ////mnt/bar, }" \ +verify_binary_equality "'$p1'x'$p2' mount rules slash filtering" \ + "/t { $p1 mount /dev/foo -> /mnt/bar, }" \ + "/t { $p2 mount ///dev/foo -> /mnt/bar, }" \ + "/t { $p2 mount /dev/foo -> /mnt//bar, }" \ + "/t { $p2 mount /dev///foo -> ////mnt/bar, }" \ "@{MNT}=/mnt/ - /t { mount /dev///foo -> @{MNT}/bar, }" \ + /t { $p2 mount /dev///foo -> @{MNT}/bar, }" \ "@{FOO}=/foo - /t { mount /dev//@{FOO} -> /mnt/bar, }" + /t { $p2 mount /dev//@{FOO} -> /mnt/bar, }" # verify slash filtering for link rules -verify_binary_equality "link rules slash filtering" \ - "/t { link /dev/foo -> /mnt/bar, }" \ - "/t { link ///dev/foo -> /mnt/bar, }" \ - "/t { link /dev/foo -> /mnt//bar, }" \ - "/t { link /dev///foo -> ////mnt/bar, }" \ +verify_binary_equality "'$p1'x'$p2' link rules slash filtering" \ + "/t { $p1 link /dev/foo -> /mnt/bar, }" \ + "/t { $p2 link ///dev/foo -> /mnt/bar, }" \ + "/t { $p2 link /dev/foo -> /mnt//bar, }" \ + "/t { $p2 link /dev///foo -> ////mnt/bar, }" \ "@{BAR}=/mnt/ - /t { link /dev///foo -> @{BAR}/bar, }" \ + /t { $p2 link /dev///foo -> @{BAR}/bar, }" \ "@{FOO}=/dev/ - /t { link @{FOO}//foo -> /mnt/bar, }" \ + /t { $p2 link @{FOO}//foo -> /mnt/bar, }" \ "@{FOO}=/dev/ @{BAR}=/mnt/ - /t { link @{FOO}/foo -> @{BAR}/bar, }" + /t { $p2 link @{FOO}/foo -> @{BAR}/bar, }" -verify_binary_equality "attachment slash filtering" \ +verify_binary_equality "'$p1'x'$p2' attachment slash filtering" \ "/t /bin/foo { }" \ "/t /bin//foo { }" \ "@{BAR}=/bin/ @@ -660,9 +670,9 @@ verify_binary_equality "value like comment at end of set var" \ # dfas dumped will be different, even if the binary is the same # Note: this test in the future will require -O filter-deny and # -O minimize and -O remove-unreachable. -verify_binary_equality "mount specific deny doesn't affect non-overlapping" \ - "/t { mount options=bind /e/ -> /**, }" \ - "/t { audit deny mount /s/** -> /**, +verify_binary_equality "'$p1'x'$p2' mount specific deny doesn't affect non-overlapping" \ + "/t { $p1 mount options=bind /e/ -> /**, }" \ + "/t { $p2 audit deny mount /s/** -> /**, mount options=bind /e/ -> /**, }" if [ $fails -ne 0 ] || [ $errors -ne 0 ] @@ -671,6 +681,139 @@ then exit $((fails + errors)) fi + +## priority override equivalence tests +## compare single rule, to multi-rule profile where one rule overrides +## the other rule via priority. + + +verify_binary_equality "'$p1'x'$p2' dbus variable expansion, multiple values/rules" \ + "/t { dbus (send, receive) path=/com/foo, }" \ + "/t { $p1 dbus (send, receive) path=/com/foo, $p2 dbus (send, receive) path=/com/foo, }" \ + "@{FOO}=foo + /t { $p1 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com/foo, }" \ + +verify_binary_equality "'$p1'x'$p2' dbus variable expansion, ensure rule de-duping occurs" \ + "/t { $p1 dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, dbus (send, receive) path=/com/bar, }" \ + "@{FOO}=bar foo bar foo + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ + "@{FOO}=bar foo bar foo + /t { $p2 dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/@{FOO}, }" + +verify_binary_equality "'$p1'x'$p2' dbus minimization with all perms" \ + "/t { $p1 dbus, }" \ + "/t { $p2 dbus bus=session, $p2 dbus, }" \ + "/t { $p2 dbus (send, receive, bind, eavesdrop), $p2 dbus, }" + +verify_binary_equality "'$p1'x'$p2' dbus minimization with bind" \ + "/t { $p1 dbus bind, }" \ + "/t { $p2 dbus bind bus=session, $p2 dbus bind, }" \ + "/t { $p2 dbus bind bus=system name=com.foo, $p2 dbus bind, }" + +verify_binary_equality "'$p1'x'$p2' dbus minimization with send and a bus conditional" \ + "/t { $p1 dbus send bus=system, }" \ + "/t { $p2 dbus send bus=system path=/com/foo interface=com.foo member=bar, dbus send bus=system, }" \ + "/t { $p2 dbus send bus=system peer=(label=/usr/bin/foo), $p2 dbus send bus=system, }" + +verify_binary_equality "'$p1'x'$p2' dbus minimization with an audit modifier" \ + "/t { $p1 audit dbus eavesdrop, }" \ + "/t { $p2 audit dbus eavesdrop bus=session, $p2 audit dbus eavesdrop, }" + +verify_binary_equality "'$p1'x'$p2' dbus minimization with a deny modifier" \ + "/t { $p1 deny dbus send bus=system peer=(name=com.foo), }" \ + "/t { $p2 deny dbus send bus=system peer=(name=com.foo label=/usr/bin/foo), $p2 deny dbus send bus=system peer=(name=com.foo), }" \ + +verify_binary_equality "'$p1'x'$p2' dbus minimization found in dbus abstractions" \ + "/t { $p1 dbus send bus=session, }" \ + "/t { $p2 dbus send + bus=session + path=/org/freedesktop/DBus + interface=org.freedesktop.DBus + member={Hello,AddMatch,RemoveMatch,GetNameOwner,NameHasOwner,StartServiceByName} + peer=(name=org.freedesktop.DBus), + $p2 dbus send bus=session, }" + +# verify slash filtering for dbus paths. +verify_binary_equality "'$p1'x'$p2' dbus slash filtering for paths" \ + "/t { $p1 dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com///foo, dbus (send, receive) path=///com/bar, }" \ + "/t { $p2 dbus (send, receive) path=/com//{foo,bar}, }" \ + "/t { $p2 dbus (send, receive) path={//com/foo,/com//bar}, }" \ + "@{FOO}=/foo + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com/bar, }" \ + "@{FOO}=/foo /bar + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ + "@{FOO}=/bar //foo + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ + "@{FOO}=//{bar,foo} + /t { $p2 dbus (send, receive) path=/com/@{FOO}, }" \ + "@{FOO}=/foo + @{BAR}=bar + /t { $p2 dbus (send, receive) path=/com/@{FOO}, $p2 dbus (send, receive) path=/com//@{BAR}, }" + + + +#### end of wrapper fn +} + + +printf "Equality Tests:\n" + +#rules that don't support priority + +# verify rlimit data conversions +verify_binary_equality "set rlimit rttime <= 12 weeks" \ + "/t { set rlimit rttime <= 12 weeks, }" \ + "/t { set rlimit rttime <= $((12 * 7)) days, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24)) hours, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24 * 60)) minutes, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60)) seconds, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000)) ms, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)) us, }" \ + "/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)), }" + +verify_binary_equality "set rlimit cpu <= 42 weeks" \ + "/t { set rlimit cpu <= 42 weeks, }" \ + "/t { set rlimit cpu <= $((42 * 7)) days, }" \ + "/t { set rlimit cpu <= $((42 * 7 * 24)) hours, }" \ + "/t { set rlimit cpu <= $((42 * 7 * 24 * 60)) minutes, }" \ + "/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)) seconds, }" \ + "/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)), }" + +verify_binary_equality "set rlimit memlock <= 2GB" \ + "/t { set rlimit memlock <= 2GB, }" \ + "/t { set rlimit memlock <= $((2 * 1024)) MB, }" \ + "/t { set rlimit memlock <= $((2 * 1024 * 1024)) KB, }" \ + "/t { set rlimit memlock <= $((2 * 1024 * 1024 * 1024)) , }" + + +# verify combinations of different priority levels +# for single rule comparisons, rules should keep same expected result +# even when the priorities are different. +# different priorities within a profile comparison resulting in +# different permission could affected expected results + + +priorities="none 0 1 -1" + +for pri1 in $priorities ; do + if [ "$pri1" = "none" ] ; then + priority1="" + else + priority1="priority=$pri1" + fi + for pri2 in $priorities ; do + if [ "$pri2" = "none" ] ; then + priority2="" + else + priority2="priority=$pri2" + fi + + verify_set "$priority1" "$priority2" + done +done + [ -z "${verbose}" ] && printf "\n" printf "PASS\n" exit 0 diff --git a/parser/tst/simple_tests/file/priority/front_perms_ok_1.sd b/parser/tst/simple_tests/file/priority/front_perms_ok_1.sd new file mode 100644 index 000000000..2375fd1dc --- /dev/null +++ b/parser/tst/simple_tests/file/priority/front_perms_ok_1.sd @@ -0,0 +1,24 @@ +# +#=DESCRIPTION perms before pathname +#=EXRESULT PASS +# +/usr/bin/foo { + + priority=-1 file r /foo1, + priority=-1 file w /foo1, + priority=-1 file a /foo1, + priority=-1 file k /foo1, + priority=-1 file m /foo1, + priority=-1 file l /foo1, + priority=-1 file px /foo1, + priority=-1 file Px /foo2, + priority=-1 file ux /foo3, + priority=-1 file Ux /foo4, + priority=-1 file ix /foo5, + priority=-1 file unsafe px /foo6, + priority=-1 file unsafe Px /foo7, + priority=-1 file unsafe ux /foo8, + priority=-1 file unsafe Ux /foo9, + priority=-1 file unsafe ix /foo10, + +} diff --git a/parser/tst/simple_tests/file/priority/front_perms_ok_2.sd b/parser/tst/simple_tests/file/priority/front_perms_ok_2.sd new file mode 100644 index 000000000..f1ee0837a --- /dev/null +++ b/parser/tst/simple_tests/file/priority/front_perms_ok_2.sd @@ -0,0 +1,24 @@ +# +#=DESCRIPTION perms before pathname +#=EXRESULT PASS +# +/usr/bin/foo { + + priority=-1 r /foo1, + priority=-1 w /foo1, + priority=-1 a /foo1, + priority=-1 k /foo1, + priority=-1 m /foo1, + priority=-1 l /foo1, + priority=-1 px /foo1, + priority=-1 Px /foo2, + priority=-1 ux /foo3, + priority=-1 Ux /foo4, + priority=-1 ix /foo5, + priority=-1 unsafe px /foo6, + priority=-1 unsafe Px /foo7, + priority=-1 unsafe ux /foo8, + priority=-1 unsafe Ux /foo9, + priority=-1 unsafe ix /foo10, + +} diff --git a/parser/tst/simple_tests/file/priority/ok_1.sd b/parser/tst/simple_tests/file/priority/ok_1.sd new file mode 100644 index 000000000..a20946456 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_1.sd @@ -0,0 +1,7 @@ +# +#=Description basic file rule +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /usr/bin/foo r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_2.sd b/parser/tst/simple_tests/file/priority/ok_2.sd new file mode 100644 index 000000000..563202e5e --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_2.sd @@ -0,0 +1,7 @@ +# +#=Description basic uppercase permission file rule (should emit warning) +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /usr/bin/foo RWM, +} diff --git a/parser/tst/simple_tests/file/priority/ok_3.sd b/parser/tst/simple_tests/file/priority/ok_3.sd new file mode 100644 index 000000000..377c24834 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_3.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION A simple successful profile +#=EXRESULT PASS +# +/usr/bin/foo { + /usr/bin/foo r, + priority=-1 /usr/bin/blah rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_4.sd b/parser/tst/simple_tests/file/priority/ok_4.sd new file mode 100644 index 000000000..0078ba94f --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_4.sd @@ -0,0 +1,7 @@ +# +#=Description basic inherit uppercase exec permission (should emit warning) +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /usr/bin/foo iX, +} diff --git a/parser/tst/simple_tests/file/priority/ok_5.sd b/parser/tst/simple_tests/file/priority/ok_5.sd new file mode 100644 index 000000000..1ca876d28 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_5.sd @@ -0,0 +1,7 @@ +# +#=Description basic unconfined uppercase exec permission (should emit warning) +#=EXRESULT PASS +# +/usr/bin/foo { + priority=+5 /usr/bin/foo UX, +} diff --git a/parser/tst/simple_tests/file/priority/ok_alternations_1.sd b/parser/tst/simple_tests/file/priority/ok_alternations_1.sd new file mode 100644 index 000000000..1fc60fde5 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_alternations_1.sd @@ -0,0 +1,7 @@ +# +#=Description basic file rule w/alternations +#=EXRESULT PASS +# +/usr/bin/foo { + priority=0 /a/b/c/**{cache,data,download,/ext,fileadmin,files,images,joomla,moodledata/sessions}/** rw, +} diff --git a/parser/tst/simple_tests/file/priority/ok_alternations_2.sd b/parser/tst/simple_tests/file/priority/ok_alternations_2.sd new file mode 100644 index 000000000..8e89eb113 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_alternations_2.sd @@ -0,0 +1,7 @@ +# +#=Description basic file rule w/nested alternations +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /a/b/c/**{cache,data,download,/ext,file{admin,s},images,joomla,moodledata/sessions}/** rw, +} diff --git a/parser/tst/simple_tests/file/priority/ok_alternations_3.sd b/parser/tst/simple_tests/file/priority/ok_alternations_3.sd new file mode 100644 index 000000000..42043afb2 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_alternations_3.sd @@ -0,0 +1,8 @@ +# +#=Description basic file rule w/large number of alternations +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /{a/a,a/b,a/c,a/d,a/e,a/f,a/g,a/h,a/i,a/j,a/k,a/l,a/m,a/n,a/o,a/p,a/q,a/r,a/s,a/t,a/u,a/v,a/w,a/x,a/y,a/z,a/A,a/B,a/C,a/D,a/E,a/F,a/G,a/H,a/I,a/J,a/K,a/L,a/M,a/N,a/O,a/P,a/Q,a/R,a/S,a/T,a/U,a/V,a/W,a/X,a/Y,a/Z,b/a,b/b,b/c,b/d,b/e,b/f,b/g,b/h,b/i,b/j,b/k,b/l,b/m,b/n,b/o,b/p,b/q,b/r,b/s,b/t,b/u,b/v,b/w,b/x,b/y,b/z,b/A,b/B,b/C,b/D,b/E,b/F,b/G,b/H,b/I,b/J,b/K,b/L,b/M,b/N,b/O,b/P,b/Q,b/R,b/S,b/T,b/U,b/V,b/W,b/X,b/Y,b/Z,c/a,c/b,c/c,c/d,c/e,c/f,c/g,c/h,c/i,c/j,c/k,c/l,c/m,c/n,c/o,c/p,c/q,c/r,c/s,c/t,c/u,c/v,c/w,c/x,c/y,c/z,c/A,c/B,c/C,c/D,c/E,c/F,c/G,c/H,c/I,c/J,c/K,c/L,c/M,c/N,c/O,c/P,c/Q,c/R,c/S,c/T,c/U,c/V,c/W,c/X,c/Y,c/Z,d/a,d/b,d/c,d/d,d/e,d/f,d/g,d/h,d/i,d/j,d/k,d/l,d/m,d/n,d/o,d/p,d/q,d/r,d/s,d/t,d/u,d/v,d/w,d/x,d/y,d/z,d/A,d/B,d/C,d/D,d/E,d/F,d/G,d/H,d/I,d/J,d/K,d/L,d/M,d/N,d/O,d/P,d/Q,d/R,d/S,d/T,d/U,d/V,d/W,d/X,d/Y,d/Z,e/a,e/b,e/c,e/d,e/e,e/f,e/g,e/h,e/i,e/j,e/k,e/l,e/m,e/n,e/o,e/p,e/q,e/r,e/s,e/t,e/u,e/v,e/w,e/x,e/y,e/z,e/A,e/B,e/C,e/D,e/E,e/F,e/G,e/H,e/I,e/J,e/K,e/L,e/M,e/N,e/O,e/P,e/Q,e/R,e/S,e/T,e/U,e/V,e/W,e/X,e/Y,e/Z,f/a,f/b,f/c,f/d,f/e,f/f,f/g,f/h,f/i,f/j,f/k,f/l,f/m,f/n,f/o,f/p,f/q,f/r,f/s,f/t,f/u,f/v,f/w,f/x,f/y,f/z,f/A,f/B,f/C,f/D,f/E,f/F,f/G,f/H,f/I,f/J,f/K,f/L,f/M,f/N,f/O,f/P,f/Q,f/R,f/S,f/T,f/U,f/V,f/W,f/X,f/Y,f/Z,g/a,g/b,g/c,g/d,g/e,g/f,g/g,g/h,g/i,g/j,g/k,g/l,g/m,g/n,g/o,g/p,g/q,g/r,g/s,g/t,g/u,g/v,g/w,g/x,g/y,g/z,g/A,g/B,g/C,g/D,g/E,g/F,g/G,g/H,g/I,g/J,g/K,g/L,g/M,g/N,g/O,g/P,g/Q,g/R,g/S,g/T,g/U,g/V,g/W,g/X,g/Y,g/Z,h/a,h/b,h/c,h/d,h/e,h/f,h/g,h/h,h/i,h/j,h/k,h/l,h/m,h/n,h/o,h/p,h/q,h/r,h/s,h/t,h/u,h/v,h/w,h/x,h/y,h/z,h/A,h/B,h/C,h/D,h/E,h/F,h/G,h/H,h/I,h/J,h/K,h/L,h/M,h/N,h/O,h/P,h/Q,h/R,h/S,h/T,h/U,h/V,h/W,h/X,h/Y,h/Z,i/a,i/b,i/c,i/d,i/e,i/f,i/g,i/h,i/i,i/j,i/k,i/l,i/m,i/n,i/o,i/p,i/q,i/r,i/s,i/t,i/u,i/v,i/w,i/x,i/y,i/z,i/A,i/B,i/C,i/D,i/E,i/F,i/G,i/H,i/I,i/J,i/K,i/L,i/M,i/N,i/O,i/P,i/Q,i/R,i/S,i/T,i/U,i/V,i/W,i/X,i/Y,i/Z,j/a,j/b,j/c,j/d,j/e,j/f,j/g,j/h,j/i,j/j,j/k,j/l,j/m,j/n,j/o,j/p,j/q,j/r,j/s,j/t,j/u,j/v,j/w,j/x,j/y,j/z,j/A,j/B,j/C,j/D,j/E,j/F,j/G,j/H,j/I,j/J,j/K,j/L,j/M,j/N,j/O,j/P,j/Q,j/R,j/S,j/T,j/U,j/V,j/W,j/X,j/Y,j/Z,k/a,k/b,k/c,k/d,k/e,k/f,k/g,k/h,k/i,k/j,k/k,k/l,k/m,k/n,k/o,k/p,k/q,k/r,k/s,k/t,k/u,k/v,k/w,k/x,k/y,k/z,k/A,k/B,k/C,k/D,k/E,k/F,k/G,k/H,k/I,k/J,k/K,k/L,k/M,k/N,k/O,k/P,k/Q,k/R,k/S,k/T,k/U,k/V,k/W,k/X,k/Y,k/Z,l/a,l/b,l/c,l/d,l/e,l/f,l/g,l/h,l/i,l/j,l/k,l/l,l/m,l/n,l/o,l/p,l/q,l/r,l/s,l/t,l/u,l/v,l/w,l/x,l/y,l/z,l/A,l/B,l/C,l/D,l/E,l/F,l/G,l/H,l/I,l/J,l/K,l/L,l/M,l/N,l/O,l/P,l/Q,l/R,l/S,l/T,l/U,l/V,l/W,l/X,l/Y,l/Z,m/a,m/b,m/c,m/d,m/e,m/f,m/g,m/h,m/i,m/j,m/k,m/l,m/m,m/n,m/o,m/p,m/q,m/r,m/s,m/t,m/u,m/v,m/w,m/x,m/y,m/z,m/A,m/B,m/C,m/D,m/E,m/F,m/G,m/H,m/I,m/J,m/K,m/L,m/M,m/N,m/O,m/P,m/Q,m/R,m/S,m/T,m/U,m/V,m/W,m/X,m/Y,m/Z,n/a,n/b,n/c,n/d,n/e,n/f,n/g,n/h,n/i,n/j,n/k,n/l,n/m,n/n,n/o,n/p,n/q,n/r,n/s,n/t,n/u,n/v,n/w,n/x,n/y,n/z,n/A,n/B,n/C,n/D,n/E,n/F,n/G,n/H,n/I,n/J,n/K,n/L,n/M,n/N,n/O,n/P,n/Q,n/R,n/S,n/T,n/U,n/V,n/W,n/X,n/Y,n/Z,o/a,o/b,o/c,o/d,o/e,o/f,o/g,o/h,o/i,o/j,o/k,o/l,o/m,o/n,o/o,o/p,o/q,o/r,o/s,o/t,o/u,o/v,o/w,o/x,o/y,o/z,o/A,o/B,o/C,o/D,o/E,o/F,o/G,o/H,o/I,o/J,o/K,o/L,o/M,o/N,o/O,o/P,o/Q,o/R,o/S,o/T,o/U,o/V,o/W,o/X,o/Y,o/Z,p/a,p/b,p/c,p/d,p/e,p/f,p/g,p/h,p/i,p/j,p/k,p/l,p/m,p/n,p/o,p/p,p/q,p/r,p/s,p/t,p/u,p/v,p/w,p/x,p/y,p/z,p/A,p/B,p/C,p/D,p/E,p/F,p/G,p/H,p/I,p/J,p/K,p/L,p/M,p/N,p/O,p/P,p/Q,p/R,p/S,p/T,p/U,p/V,p/W,p/X,p/Y,p/Z,q/a,q/b,q/c,q/d,q/e,q/f,q/g,q/h,q/i,q/j,q/k,q/l,q/m,q/n,q/o,q/p,q/q,q/r,q/s,q/t,q/u,q/v,q/w,q/x,q/y,q/z,q/A,q/B,q/C,q/D,q/E,q/F,q/G,q/H,q/I,q/J,q/K,q/L,q/M,q/N,q/O,q/P,q/Q,q/R,q/S,q/T,q/U,q/V,q/W,q/X,q/Y,q/Z,r/a,r/b,r/c,r/d,r/e,r/f,r/g,r/h,r/i,r/j,r/k,r/l,r/m,r/n,r/o,r/p,r/q,r/r,r/s,r/t,r/u,r/v,r/w,r/x,r/y,r/z,r/A,r/B,r/C,r/D,r/E,r/F,r/G,r/H,r/I,r/J,r/K,r/L,r/M,r/N,r/O,r/P,r/Q,r/R,r/S,r/T,r/U,r/V,r/W,r/X,r/Y,r/Z,s/a,s/b,s/c,s/d,s/e,s/f,s/g,s/h,s/i,s/j,s/k,s/l,s/m,s/n,s/o,s/p,s/q,s/r,s/s,s/t,s/u,s/v,s/w,s/x,s/y,s/z,s/A,s/B,s/C,s/D,s/E,s/F,s/G,s/H,s/I,s/J,s/K,s/L,s/M,s/N,s/O,s/P,s/Q,s/R,s/S,s/T,s/U,s/V,s/W,s/X,s/Y,s/Z,t/a,t/b,t/c,t/d,t/e,t/f,t/g,t/h,t/i,t/j,t/k,t/l,t/m,t/n,t/o,t/p,t/q,t/r,t/s,t/t,t/u,t/v,t/w,t/x,t/y,t/z,t/A,t/B,t/C,t/D,t/E,t/F,t/G,t/H,t/I,t/J,t/K,t/L,t/M,t/N,t/O,t/P,t/Q,t/R,t/S,t/T,t/U,t/V,t/W,t/X,t/Y,t/Z,u/a,u/b,u/c,u/d,u/e,u/f,u/g,u/h,u/i,u/j,u/k,u/l,u/m,u/n,u/o,u/p,u/q,u/r,u/s,u/t,u/u,u/v,u/w,u/x,u/y,u/z,u/A,u/B,u/C,u/D,u/E,u/F,u/G,u/H,u/I,u/J,u/K,u/L,u/M,u/N,u/O,u/P,u/Q,u/R,u/S,u/T,u/U,u/V,u/W,u/X,u/Y,u/Z,v/a,v/b,v/c,v/d,v/e,v/f,v/g,v/h,v/i,v/j,v/k,v/l,v/m,v/n,v/o,v/p,v/q,v/r,v/s,v/t,v/u,v/v,v/w,v/x,v/y,v/z,v/A,v/B,v/C,v/D,v/E,v/F,v/G,v/H,v/I,v/J,v/K,v/L,v/M,v/N,v/O,v/P,v/Q,v/R,v/S,v/T,v/U,v/V,v/W,v/X,v/Y,v/Z,w/a,w/b,w/c,w/d,w/e,w/f,w/g,w/h,w/i,w/j,w/k,w/l,w/m,w/n,w/o,w/p,w/q,w/r,w/s,w/t,w/u,w/v,w/w,w/x,w/y,w/z,w/A,w/B,w/C,w/D,w/E,w/F,w/G,w/H,w/I,w/J,w/K,w/L,w/M,w/N,w/O,w/P,w/Q,w/R,w/S,w/T,w/U,w/V,w/W,w/X,w/Y,w/Z,x/a,x/b,x/c,x/d,x/e,x/f,x/g,x/h,x/i,x/j,x/k,x/l,x/m,x/n,x/o,x/p,x/q,x/r,x/s,x/t,x/u,x/v,x/w,x/x,x/y,x/z,x/A,x/B,x/C,x/D,x/E,x/F,x/G,x/H,x/I,x/J,x/K,x/L,x/M,x/N,x/O,x/P,x/Q,x/R,x/S,x/T,x/U,x/V,x/W,x/X,x/Y,x/Z,y/a,y/b,y/c,y/d,y/e,y/f,y/g,y/h,y/i,y/j,y/k,y/l,y/m,y/n,y/o,y/p,y/q,y/r,y/s,y/t,y/u,y/v,y/w,y/x,y/y,y/z,y/A,y/B,y/C,y/D,y/E,y/F,y/G,y/H,y/I,y/J,y/K,y/L,y/M,y/N,y/O,y/P,y/Q,y/R,y/S,y/T,y/U,y/V,y/W,y/X,y/Y,y/Z,z/a,z/b,z/c,z/d,z/e,z/f,z/g,z/h,z/i,z/j,z/k,z/l,z/m,z/n,z/o,z/p,z/q,z/r,z/s,z/t,z/u,z/v,z/w,z/x,z/y,z/z} rw, + +} diff --git a/parser/tst/simple_tests/file/priority/ok_append_1.sd b/parser/tst/simple_tests/file/priority/ok_append_1.sd new file mode 100644 index 000000000..daa96daba --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_append_1.sd @@ -0,0 +1,13 @@ +# +#=DESCRIPTION test append +#=EXRESULT PASS +# +/usr/bin/foo { + /bin/cat a, + /bin/true ra, + /bin/false ma, + priority=-1 /lib/libc.so la, + /bin/less ixa, + /bin/more pxa, + /a uxa, +} diff --git a/parser/tst/simple_tests/file/priority/ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/ok_audit_deny_link.sd new file mode 100644 index 000000000..2e49f3de7 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_audit_deny_link.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +profile test { + priority=-1 audit deny link /alpha/beta -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_bare_1.sd b/parser/tst/simple_tests/file/priority/ok_bare_1.sd new file mode 100644 index 000000000..5993127cc --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_bare_1.sd @@ -0,0 +1,8 @@ +# +#=Description bare file rule +#=EXRESULT PASS +#=TODO https://launchpad.net/bugs/1215637 +# +/usr/bin/foo { + priority=19 deny file, +} diff --git a/parser/tst/simple_tests/file/priority/ok_carat_1.sd b/parser/tst/simple_tests/file/priority/ok_carat_1.sd new file mode 100644 index 000000000..bc25543bc --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_carat_1.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION carat in pathname +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /foo^bar r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_carat_2.sd b/parser/tst/simple_tests/file/priority/ok_carat_2.sd new file mode 100644 index 000000000..6b13e0dc1 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_carat_2.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION trailing carat in pathname +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /foo/bar^ r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_comma_1.sd b/parser/tst/simple_tests/file/priority/ok_comma_1.sd new file mode 100644 index 000000000..4fbaa0bd6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_comma_1.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION comma in pathname +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /foo,bar r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_comma_2.sd b/parser/tst/simple_tests/file/priority/ok_comma_2.sd new file mode 100644 index 000000000..4dc7b617c --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_comma_2.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION comma at end of pathname +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 "/foobar," r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_deny_1.sd b/parser/tst/simple_tests/file/priority/ok_deny_1.sd new file mode 100644 index 000000000..1fa3f802e --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_deny_1.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION A simple deny rule +#=EXRESULT PASS +# vim:syntax=apparmor +# +/usr/bin/foo { + priority=-1 deny /usr/bin/foo r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_deny_2.sd b/parser/tst/simple_tests/file/priority/ok_deny_2.sd new file mode 100644 index 000000000..1fa3f802e --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_deny_2.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION A simple deny rule +#=EXRESULT PASS +# vim:syntax=apparmor +# +/usr/bin/foo { + priority=-1 deny /usr/bin/foo r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_deny_3.sd b/parser/tst/simple_tests/file/priority/ok_deny_3.sd new file mode 100644 index 000000000..72fa5ddb5 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_deny_3.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION an overlapping deny rule +#=EXRESULT PASS +# vim:syntax=apparmor +# +/usr/bin/foo { + priority=-1 /usr/bin/** r, + priority=5 deny /usr/bin/foo r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_deny_4.sd b/parser/tst/simple_tests/file/priority/ok_deny_4.sd new file mode 100644 index 000000000..c5bb8a8da --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_deny_4.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION an exact overlapping deny rule +#=EXRESULT PASS +# vim:syntax=apparmor +# +/usr/bin/foo { + priority=-1 /usr/bin/foo r, + priority=-1 deny /usr/bin/foo r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_deny_link.sd b/parser/tst/simple_tests/file/priority/ok_deny_link.sd new file mode 100644 index 000000000..c9f126d26 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_deny_link.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +profile test { + priority=-1 deny link /alpha/beta -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_embedded_spaces_1.sd b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_1.sd new file mode 100644 index 000000000..3b5d5dd3b --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_1.sd @@ -0,0 +1,6 @@ +#=DESCRIPTION Simple test case for embedded spaces +#=EXRESULT PASS + +/bin/foo { + priority=-1 "/abc\ def" r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_embedded_spaces_2.sd b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_2.sd new file mode 100644 index 000000000..bee05bf43 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_2.sd @@ -0,0 +1,6 @@ +#=DESCRIPTION Simple test case for embedded spaces +#=EXRESULT PASS + +/bin/foo { + priority=-1 "/abc def" r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_embedded_spaces_3.sd b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_3.sd new file mode 100644 index 000000000..eef2b736a --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_3.sd @@ -0,0 +1,6 @@ +#=DESCRIPTION Simple test case for embedded spaces +#=EXRESULT PASS + +"/bin/fo o" { + priority=-1 "/abc def" r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_embedded_spaces_4.sd b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_4.sd new file mode 100644 index 000000000..f49fc2de5 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_embedded_spaces_4.sd @@ -0,0 +1,6 @@ +#=DESCRIPTION Simple test case for embedded spaces +#=EXRESULT PASS + +/bin/foo { + priority=-1 /abc\ def r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_inv_char_class.sd b/parser/tst/simple_tests/file/priority/ok_inv_char_class.sd new file mode 100644 index 000000000..33f0bc309 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_inv_char_class.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION carat in pathname +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /foo[^me]bar r, +} diff --git a/parser/tst/simple_tests/file/priority/ok_link_1.sd b/parser/tst/simple_tests/file/priority/ok_link_1.sd new file mode 100644 index 000000000..bb227d6b3 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_link_1.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +profile test { + priority=-1 /alpha/beta rl, + /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_link_2.sd b/parser/tst/simple_tests/file/priority/ok_link_2.sd new file mode 100644 index 000000000..d9683fe79 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_link_2.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +profile test { + priority=-1 link /alpha/beta -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_link_3.sd b/parser/tst/simple_tests/file/priority/ok_link_3.sd new file mode 100644 index 000000000..7bf458aa5 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_link_3.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +profile test { + priority=-1 link subset /alpha/beta -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_link_audit_deny_owner_subset.sd b/parser/tst/simple_tests/file/priority/ok_link_audit_deny_owner_subset.sd new file mode 100644 index 000000000..67321d3e6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_link_audit_deny_owner_subset.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION link access test with audit deny and owner restriction +#=EXRESULT PASS +# + +profile test { + priority=-1 audit deny owner link subset /alpha/beta -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_link_owner.sd b/parser/tst/simple_tests/file/priority/ok_link_owner.sd new file mode 100644 index 000000000..975657a75 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_link_owner.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test with owner restriction +#=EXRESULT PASS +# + +profile test { + priority=-1 owner link subset /alpha/beta -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_lock_1.sd b/parser/tst/simple_tests/file/priority/ok_lock_1.sd new file mode 100644 index 000000000..8909cd45c --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_lock_1.sd @@ -0,0 +1,17 @@ +# +#=DESCRIPTION k and other perms do not conflict +#=EXRESULT PASS +# +/usr/bin/foo { + /bin/a k, + /bin/b rk, + /bin/c wk, + priority=-1 /bin/d ak, + /bin/e lk, + /bin/e mk, + /bin/f pxk, + /bin/g Pxk, + /bin/h ixk, + /bin/i uxk, + /bin/j Uxk, +} diff --git a/parser/tst/simple_tests/file/priority/ok_mmap_1.sd b/parser/tst/simple_tests/file/priority/ok_mmap_1.sd new file mode 100644 index 000000000..e7b12da37 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_mmap_1.sd @@ -0,0 +1,12 @@ +# +#=DESCRIPTION m and [uUpPi]x do not conflict +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /bin/cat mix, + /bin/true mpx, + priority=-1 /bin/false mux, + priority=-1 /lib/libc.so rwlm, + /bin/less mUx, + priority=10 /bin/more mPx, +} diff --git a/parser/tst/simple_tests/file/priority/ok_mmap_2.sd b/parser/tst/simple_tests/file/priority/ok_mmap_2.sd new file mode 100644 index 000000000..67212eacb --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_mmap_2.sd @@ -0,0 +1,14 @@ +# +#=DESCRIPTION m and [upi]x do not conflict, separate rules +#=EXRESULT PASS +# +/usr/bin/foo { + /bin/cat rm, + /bin/cat ix, + priority=-1 /bin/true px, + /bin/true m, + /bin/false m, + /bin/false ux, + priority=-1 /lib/libc.so rwl, + /lib/libc.so m, +} diff --git a/parser/tst/simple_tests/file/priority/ok_octal_1.sd b/parser/tst/simple_tests/file/priority/ok_octal_1.sd new file mode 100644 index 000000000..c09abb95a --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_octal_1.sd @@ -0,0 +1,8 @@ +# +#=DESCRIPTION simple octal test +#=EXRESULT PASS +# + +profile ascii { + priority=-1 /bin/\141bcde rix, +} diff --git a/parser/tst/simple_tests/file/priority/ok_octal_2.sd b/parser/tst/simple_tests/file/priority/ok_octal_2.sd new file mode 100644 index 000000000..26530126a --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_octal_2.sd @@ -0,0 +1,8 @@ +# +#=DESCRIPTION simple quoted octal expansion +#=EXRESULT PASS +# + +profile octal { + priority=-1 "/bin/a b \143 d e" rix, +} diff --git a/parser/tst/simple_tests/file/priority/ok_other_1.sd b/parser/tst/simple_tests/file/priority/ok_other_1.sd new file mode 100644 index 000000000..39d16cb83 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_other_1.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION simple other flag test +#=EXRESULT PASS + +profile test { + priority=-1 other /tmp/** rw, +} diff --git a/parser/tst/simple_tests/file/priority/ok_other_2.sd b/parser/tst/simple_tests/file/priority/ok_other_2.sd new file mode 100644 index 000000000..bbbbe43ee --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_other_2.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION simple deny other flag test +#=EXRESULT PASS + +profile test { + priority=-1 deny other /tmp/** rw, +} diff --git a/parser/tst/simple_tests/file/priority/ok_other_3.sd b/parser/tst/simple_tests/file/priority/ok_other_3.sd new file mode 100644 index 000000000..ac0e4be6b --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_other_3.sd @@ -0,0 +1,7 @@ +# +#=DESCRIPTION simple other flag test +#=EXRESULT PASS + +profile test { + priority=-1 audit other /tmp/** rw, +} diff --git a/parser/tst/simple_tests/file/priority/ok_quoted_1.sd b/parser/tst/simple_tests/file/priority/ok_quoted_1.sd new file mode 100644 index 000000000..d0844ad82 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_quoted_1.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple quoted tab expansion +#=EXRESULT PASS +# + +profile test { + priority=-1 "/bin/alpha\tbeta" rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_quoted_2.sd b/parser/tst/simple_tests/file/priority/ok_quoted_2.sd new file mode 100644 index 000000000..1bc317668 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_quoted_2.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple quoted newline expansion +#=EXRESULT PASS +# + +profile test { + priority=-1 "/bin/alpha\nbeta" rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_quoted_3.sd b/parser/tst/simple_tests/file/priority/ok_quoted_3.sd new file mode 100644 index 000000000..459e23f1e --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_quoted_3.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple quoted carriage return expansion +#=EXRESULT PASS +# + +profile test { + priority=-1 "/bin/alpha\rbeta" rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_quoted_4.sd b/parser/tst/simple_tests/file/priority/ok_quoted_4.sd new file mode 100644 index 000000000..4227a539b --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_quoted_4.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple quoted quote expansion +#=EXRESULT PASS +# + +profile test { + priority=-1 "/bin/alpha\"beta" rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_quoted_5.sd b/parser/tst/simple_tests/file/priority/ok_quoted_5.sd new file mode 100644 index 000000000..fd1836f8b --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_quoted_5.sd @@ -0,0 +1,9 @@ +# +#=DESCRIPTION simple quoted backslash expansion +#=EXRESULT PASS +# + +profile test { + priority=-1 "/bin/alpha\\beta" rix, +} + diff --git a/parser/tst/simple_tests/file/priority/ok_slashquote_1.sd b/parser/tst/simple_tests/file/priority/ok_slashquote_1.sd new file mode 100644 index 000000000..0924ceb9b --- /dev/null +++ b/parser/tst/simple_tests/file/priority/ok_slashquote_1.sd @@ -0,0 +1,8 @@ +# +#=DESCRIPTION unnecessary slash quotes are okay (should emit warning) +#=EXRESULT PASS +# + +profile blart { + priority=-1 /bingo/bang\o/bongo rw, +} diff --git a/parser/tst/simple_tests/file/priority/stacking_ok_1.sd b/parser/tst/simple_tests/file/priority/stacking_ok_1.sd new file mode 100644 index 000000000..56b399923 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/stacking_ok_1.sd @@ -0,0 +1,7 @@ +# +#=Description basic file exec rule with stacking target +#=EXRESULT PASS +# +/usr/bin/foo { + priority=-1 /bin/bar px -> &baz, +} diff --git a/parser/tst/simple_tests/file/priority/var1_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_ok_audit_deny_link.sd new file mode 100644 index 000000000..168999ff6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 audit deny link @{var} -> @{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_ok_deny_link.sd new file mode 100644 index 000000000..a4d55650a --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 deny link @{var} -> @{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var1_ok_link_1.sd new file mode 100644 index 000000000..5069fd4f6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 @{var} rl, + priority=-1 /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var1_ok_link_2.sd new file mode 100644 index 000000000..f074f9c85 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link @{var} -> @{var}, + priority=-1 @{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var1_ok_link_3.sd new file mode 100644 index 000000000..54b8e03e4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link subset @{var} -> @{var}, + priority=-1 @{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_src_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_src_ok_audit_deny_link.sd new file mode 100644 index 000000000..d05278fc4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_src_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 audit deny link @{var} -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_src_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_src_ok_deny_link.sd new file mode 100644 index 000000000..9fe80b0f4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_src_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 deny link @{var} -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_src_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var1_src_ok_link_1.sd new file mode 100644 index 000000000..5069fd4f6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_src_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 @{var} rl, + priority=-1 /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_src_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var1_src_ok_link_2.sd new file mode 100644 index 000000000..373508ec2 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_src_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link @{var} -> /tmp/**, + priority=-1 /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_src_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var1_src_ok_link_3.sd new file mode 100644 index 000000000..60b5b9b84 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_src_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link subset @{var} -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_target_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_target_ok_audit_deny_link.sd new file mode 100644 index 000000000..e0fc27a79 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_target_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 audit deny link /alpha/beta -> @{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_target_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var1_target_ok_deny_link.sd new file mode 100644 index 000000000..d52a0e71d --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_target_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 deny link /alpha/beta -> @{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_target_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var1_target_ok_link_1.sd new file mode 100644 index 000000000..6078a72c4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_target_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 /alpha/beta rl, + /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_target_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var1_target_ok_link_2.sd new file mode 100644 index 000000000..d0f735798 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_target_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link /alpha/beta -> @{var}, + priority=-1 @{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var1_target_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var1_target_ok_link_3.sd new file mode 100644 index 000000000..c561351c7 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var1_target_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link subset /alpha/beta -> @{var}, + priority=-1 @{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_ok_audit_deny_link.sd new file mode 100644 index 000000000..8547060d5 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 audit deny link /foo@{var} -> /foo@{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_ok_deny_link.sd new file mode 100644 index 000000000..4360476e8 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 deny link /foo@{var} -> /foo@{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var2_ok_link_1.sd new file mode 100644 index 000000000..3075c2fb6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=11 /foo@{var} rl, + /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var2_ok_link_2.sd new file mode 100644 index 000000000..69d59834c --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link /foo@{var} -> /foo@{var}, + /foo@{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var2_ok_link_3.sd new file mode 100644 index 000000000..81b44b024 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link subset /foo@{var} -> /foo@{var}, + /foo@{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_src_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_src_ok_audit_deny_link.sd new file mode 100644 index 000000000..2d880b19c --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_src_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + audit deny link /foo@{var} -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_src_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_src_ok_deny_link.sd new file mode 100644 index 000000000..a6c4bace6 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_src_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + deny link /foo@{var} -> /tmp/**, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_src_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var2_src_ok_link_1.sd new file mode 100644 index 000000000..fe1b2dcf8 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_src_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + /foo@{var} rl, + /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_src_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var2_src_ok_link_2.sd new file mode 100644 index 000000000..5bc6ef81c --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_src_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + link /foo@{var} -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_src_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var2_src_ok_link_3.sd new file mode 100644 index 000000000..0bdd95fc4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_src_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + link subset /foo@{var} -> /tmp/**, + /tmp/** r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_target_ok_audit_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_target_ok_audit_deny_link.sd new file mode 100644 index 000000000..9c83e7ea9 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_target_ok_audit_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 audit deny link /alpha/beta -> /foo@{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_target_ok_deny_link.sd b/parser/tst/simple_tests/file/priority/var2_target_ok_deny_link.sd new file mode 100644 index 000000000..83321243f --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_target_ok_deny_link.sd @@ -0,0 +1,10 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + deny link /alpha/beta -> /foo@{var}, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_target_ok_link_1.sd b/parser/tst/simple_tests/file/priority/var2_target_ok_link_1.sd new file mode 100644 index 000000000..6078a72c4 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_target_ok_link_1.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 /alpha/beta rl, + /gamma/* rwl, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_target_ok_link_2.sd b/parser/tst/simple_tests/file/priority/var2_target_ok_link_2.sd new file mode 100644 index 000000000..0b8ab3980 --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_target_ok_link_2.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link /alpha/beta -> /foo@{var}, + /foo@{var} r, +} + diff --git a/parser/tst/simple_tests/file/priority/var2_target_ok_link_3.sd b/parser/tst/simple_tests/file/priority/var2_target_ok_link_3.sd new file mode 100644 index 000000000..e5eb73cea --- /dev/null +++ b/parser/tst/simple_tests/file/priority/var2_target_ok_link_3.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION simple link access test +#=EXRESULT PASS +# + +@{var}=/test +profile test { + priority=-1 link subset /alpha/beta -> /foo@{var}, + /foo@{var} r, +} + diff --git a/utils/test/test-parser-simple-tests.py b/utils/test/test-parser-simple-tests.py index c6c1a6e25..2e53ddf9b 100644 --- a/utils/test/test-parser-simple-tests.py +++ b/utils/test/test-parser-simple-tests.py @@ -35,6 +35,9 @@ skip_startswith = ( # Pux and Cux (which actually mean PUx and CUx) get rejected by the tools 'generated_x/exact-', + + # don't handle rule priorities yet + 'file/priority/', ) # testcases that should raise an exception, but don't From 903a1b568906e3a27f8523774db86c2596f02acd Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 14 Aug 2024 17:58:00 -0700 Subject: [PATCH 9/9] parser: make ix of file, rule have lower priority so it can be overridden the ix portion of file, causes x conflicts in regular priority. The long term goal is to fix this by using dominance for x rules. But in the mean time we can fix by giving the ix portion of the rule a reduced priority. Signed-off-by: John Johansen --- parser/all_rule.cc | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/parser/all_rule.cc b/parser/all_rule.cc index 4ebc47a8a..503ede117 100644 --- a/parser/all_rule.cc +++ b/parser/all_rule.cc @@ -89,15 +89,13 @@ void all_rule::add_implied_rules(Profile &prof) /* rules that have not been converted to use rule.h */ - //file + //file no x { const char *error; struct cod_entry *entry; char *path = strdup("/{**,}"); - int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) | - (AA_MAY_EXEC)); + int perms = (AA_BASE_PERMS & ~(AA_EXEC_TYPE | AA_MAY_EXEC)); if (rule_mode != RULE_DENY) - perms |= AA_EXEC_INHERIT; /* duplicate to other permission set */ perms |= perms << AA_OTHER_SHIFT; if (!path) @@ -108,7 +106,35 @@ void all_rule::add_implied_rules(Profile &prof) } add_entry_to_policy(&prof, entry); } + // lower priority ix + { + const char *error; + struct cod_entry *entry; + char *path = strdup("/{**,}"); + int perms = AA_MAY_EXEC; + prefixes ix_prefix; + // TODO: + // need a better way to make sure the prefix is intialized + // without a constructor or copy constructor + ix_prefix.priority = prefix->priority -1; + ix_prefix.audit = prefix->audit; + ix_prefix.rule_mode = prefix->rule_mode; + ix_prefix.owner = prefix->owner; + + ix_prefix.priority -= 1; + if (rule_mode != RULE_DENY) + perms |= AA_EXEC_INHERIT; + /* duplicate to other permission set */ + perms |= perms << AA_OTHER_SHIFT; + if (!path) + yyerror(_("Memory allocation error.")); + entry = new_entry(path, perms, NULL); + if (!entry_add_prefix(entry, ix_prefix, error)) { + yyerror(_("%s"), error); + } + add_entry_to_policy(&prof, entry); + } // caps { if (prefix->owner)