Merge parser: add the ability to specify a priority prefix to policy rules

This enables adding a priority to a rules in policy.

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 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.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1261
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
John Johansen 2024-08-15 01:46:42 +00:00
commit 5edcb6f45c
108 changed files with 1807 additions and 473 deletions

View file

@ -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 {
@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -139,9 +139,11 @@ B<HATNAME> = (must start with alphanumeric character. See aa_change_hat(2) for a
B<QUALIFIER BLOCK> = I<QUALIFIERS> I<BLOCK>
B<INTEGER> = (+ | -)? [[:digit:]]+
B<ACCESS TYPE> = ( 'allow' | 'deny' )
B<QUALIFIERS> = [ 'audit' ] [ I<ACCESS TYPE> ]
B<QUALIFIERS> = [ 'priority' '=' <INTEGER> ] [ 'audit' ] [ I<ACCESS TYPE> ]
B<CAPABILITY RULE> = [ I<QUALIFIERS> ] 'capability' [ I<CAPABILITY LIST> ]
@ -1878,6 +1880,17 @@ Rule qualifiers can modify the rule and/or permissions within the rule.
=over 4
=item B<priority>
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<allow>
Specifies that permissions requests that match the rule are allowed. This

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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<iterator, bool> 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 <aa_perms> &perms_table,

View file

@ -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) {}
};

View file

@ -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,41 +651,34 @@ 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;
}
typedef pair<uint64_t,uint64_t> uint128_t;
/* minimize the number of dfa states */
void DFA::minimize(optflags const &opts)
{
map<pair<uint128_t, size_t>, Partition *> perm_map;
map<perms_t, Partition *> perm_map;
list<Partition *> 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<uint128_t, size_t> group = make_pair(permtype, hash);
map<pair<uint128_t, size_t>, Partition *>::iterator p = perm_map.find(group);
map<perms_t, Partition *>::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 (permtype.first || permtype.second)
if ((*i)->perms.is_accept())
accept_count++;
} else {
(*i)->partition = p->second;
@ -1392,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();
@ -1406,13 +1402,20 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa)
continue;
MatchFlag *match = static_cast<MatchFlag *>(*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;
@ -1420,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;
@ -1428,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;
}
@ -1456,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");

View file

@ -30,6 +30,7 @@
#include <iostream>
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#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 | prompt | audit | quiet); }
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);

View file

@ -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;

View file

@ -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;
}
}

View file

@ -697,7 +697,8 @@ bool network_rule::gen_ip_conds(Profile &prof, std::list<std::ostringstream> &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<std::ostringstream> &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;
}
}

View file

@ -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;

View file

@ -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
@ -114,6 +120,7 @@ struct cond_entry_list {
};
struct cod_entry {
int priority;
char *name;
union {
char *link_name;

View file

@ -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 >
}
%}
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE,MQUEUE_MODE,IOURING_MODE>{
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE,MQUEUE_MODE,IOURING_MODE,INTEGER_MODE>{
{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 >
}
}
<INTEGER_MODE>{
{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<int, string> state_names = {
STATE_TABLE_ENT(USERNS_MODE),
STATE_TABLE_ENT(MQUEUE_MODE),
STATE_TABLE_ENT(IOURING_MODE),
STATE_TABLE_ENT(INTEGER_MODE),
};

View file

@ -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 */

View file

@ -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}
};
@ -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;

View file

@ -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;
@ -785,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;
@ -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;
@ -1076,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;
@ -1091,7 +1112,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, mediates_priority, RULE_ALLOW, AA_MAY_READ, 0, parseopts))
goto out;
/* don't add mediated classes to unconfined profiles */
@ -1099,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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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;
}
@ -1136,11 +1157,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;
@ -1167,7 +1188,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;

View file

@ -19,6 +19,7 @@
*/
#define YYERROR_VERBOSE 1
#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
@ -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
@ -222,7 +224,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;
@ -268,9 +271,10 @@ static void abi_features(char *filename, bool search);
%type <id> id_or_var
%type <id> opt_id_or_var
%type <boolean> opt_subset_flag
%type <integer> opt_priority
%type <audit> opt_audit_flag
%type <owner> opt_owner_flag
%type <boolean> opt_profile_flag
%type <integer> opt_profile_flag
%type <boolean> opt_flags
%type <rule_mode> opt_rule_mode
%type <id> opt_id
@ -294,7 +298,7 @@ static void abi_features(char *filename, bool search);
%type <unix_entry> unix_rule
%type <id> opt_target
%type <id> opt_named_transition
%type <boolean> opt_exec_mode
%type <integer> opt_exec_mode
%type <boolean> opt_file
%type <fperms> userns_perm
%type <fperms> userns_perms
@ -588,13 +592,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
@ -626,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; };
@ -638,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 */
@ -679,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 " : "",
@ -1011,8 +1036,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
{

View file

@ -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;
}

View file

@ -173,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;
@ -242,14 +243,15 @@ 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 = 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;
if (tmp != 0)
return tmp;
if ((uint) owner < (uint) rhs.owner)
return -1;
if ((uint) owner > (uint) rhs.owner)
@ -258,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;
}
@ -273,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;
@ -283,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 &&

View file

@ -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;

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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
}
}

View file

@ -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 7 ] ; 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

View file

@ -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,
}

View file

@ -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,
}

View file

@ -0,0 +1,7 @@
#
#=Description basic file rule
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /usr/bin/foo r,
}

View file

@ -0,0 +1,7 @@
#
#=Description basic uppercase permission file rule (should emit warning)
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /usr/bin/foo RWM,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION A simple successful profile
#=EXRESULT PASS
#
/usr/bin/foo {
/usr/bin/foo r,
priority=-1 /usr/bin/blah rix,
}

View file

@ -0,0 +1,7 @@
#
#=Description basic inherit uppercase exec permission (should emit warning)
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /usr/bin/foo iX,
}

View file

@ -0,0 +1,7 @@
#
#=Description basic unconfined uppercase exec permission (should emit warning)
#=EXRESULT PASS
#
/usr/bin/foo {
priority=+5 /usr/bin/foo UX,
}

View file

@ -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,
}

View file

@ -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,
}

File diff suppressed because one or more lines are too long

View file

@ -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,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
profile test {
priority=-1 audit deny link /alpha/beta -> /tmp/**,
}

View file

@ -0,0 +1,8 @@
#
#=Description bare file rule
#=EXRESULT PASS
#=TODO https://launchpad.net/bugs/1215637
#
/usr/bin/foo {
priority=19 deny file,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /foo^bar r,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION trailing carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /foo/bar^ r,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION comma in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /foo,bar r,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION comma at end of pathname
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 "/foobar," r,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION A simple deny rule
#=EXRESULT PASS
# vim:syntax=apparmor
#
/usr/bin/foo {
priority=-1 deny /usr/bin/foo r,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION A simple deny rule
#=EXRESULT PASS
# vim:syntax=apparmor
#
/usr/bin/foo {
priority=-1 deny /usr/bin/foo r,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
profile test {
priority=-1 deny link /alpha/beta -> /tmp/**,
}

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
/bin/foo {
priority=-1 "/abc\ def" r,
}

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
/bin/foo {
priority=-1 "/abc def" r,
}

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
"/bin/fo o" {
priority=-1 "/abc def" r,
}

View file

@ -0,0 +1,6 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
/bin/foo {
priority=-1 /abc\ def r,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /foo[^me]bar r,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
profile test {
priority=-1 /alpha/beta rl,
/gamma/* rwl,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
profile test {
priority=-1 link /alpha/beta -> /tmp/**,
/tmp/** r,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
profile test {
priority=-1 link subset /alpha/beta -> /tmp/**,
/tmp/** r,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION simple octal test
#=EXRESULT PASS
#
profile ascii {
priority=-1 /bin/\141bcde rix,
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION simple quoted octal expansion
#=EXRESULT PASS
#
profile octal {
priority=-1 "/bin/a b \143 d e" rix,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple other flag test
#=EXRESULT PASS
profile test {
priority=-1 other /tmp/** rw,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple deny other flag test
#=EXRESULT PASS
profile test {
priority=-1 deny other /tmp/** rw,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple other flag test
#=EXRESULT PASS
profile test {
priority=-1 audit other /tmp/** rw,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple quoted tab expansion
#=EXRESULT PASS
#
profile test {
priority=-1 "/bin/alpha\tbeta" rix,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple quoted newline expansion
#=EXRESULT PASS
#
profile test {
priority=-1 "/bin/alpha\nbeta" rix,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple quoted carriage return expansion
#=EXRESULT PASS
#
profile test {
priority=-1 "/bin/alpha\rbeta" rix,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple quoted quote expansion
#=EXRESULT PASS
#
profile test {
priority=-1 "/bin/alpha\"beta" rix,
}

View file

@ -0,0 +1,9 @@
#
#=DESCRIPTION simple quoted backslash expansion
#=EXRESULT PASS
#
profile test {
priority=-1 "/bin/alpha\\beta" rix,
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION unnecessary slash quotes are okay (should emit warning)
#=EXRESULT PASS
#
profile blart {
priority=-1 /bingo/bang\o/bongo rw,
}

View file

@ -0,0 +1,7 @@
#
#=Description basic file exec rule with stacking target
#=EXRESULT PASS
#
/usr/bin/foo {
priority=-1 /bin/bar px -> &baz,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 audit deny link @{var} -> @{var},
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 deny link @{var} -> @{var},
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 @{var} rl,
priority=-1 /gamma/* rwl,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 link @{var} -> @{var},
priority=-1 @{var} r,
}

View file

@ -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,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 audit deny link @{var} -> /tmp/**,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 deny link @{var} -> /tmp/**,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 @{var} rl,
priority=-1 /gamma/* rwl,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 link @{var} -> /tmp/**,
priority=-1 /tmp/** r,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 link subset @{var} -> /tmp/**,
/tmp/** r,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 audit deny link /alpha/beta -> @{var},
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 deny link /alpha/beta -> @{var},
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 /alpha/beta rl,
/gamma/* rwl,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 audit deny link /foo@{var} -> /foo@{var},
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=-1 deny link /foo@{var} -> /foo@{var},
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
priority=11 /foo@{var} rl,
/gamma/* rwl,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
audit deny link /foo@{var} -> /tmp/**,
}

View file

@ -0,0 +1,10 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
deny link /foo@{var} -> /tmp/**,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
/foo@{var} rl,
/gamma/* rwl,
}

View file

@ -0,0 +1,11 @@
#
#=DESCRIPTION simple link access test
#=EXRESULT PASS
#
@{var}=/test
profile test {
link /foo@{var} -> /tmp/**,
/tmp/** r,
}

Some files were not shown because too many files have changed in this diff Show more