From 2737cb2c2befcb0670a943cad6b2473280e8ce6e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 10 May 2024 03:06:22 -0700 Subject: [PATCH] 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;