mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
Track full permission set through all stages of DFA construction.
Previously permission information was thrown away early and permissions where packed to their CHFA form at the start of DFA construction. Because of this permissions hashing to setup the initial DFA partitions was required as x transition conflicts, etc. could not be resolved. Move the mapping of permissions to CHFA construction, and track the full permission set through DFA construction. This allows removal of the perm_hashing hack, which prevented a full minimization from happening in some DFAs. It also could result in x conflicts not being correctly detected, and deny rules not being fully applied in some situations. Eg. pre full minimization Created dfa: states 33451 Minimized dfa: final partitions 17033 with full minimization Created dfa: states 33451 Minimized dfa: final partitions 9550 Dfa minimization no states removed: partitions 9550 The tracking of deny rules through to the completed DFA construction creates a new class of states. That is states that are marked as being accepting (carry permission information) but infact are non-accepting as they only carry deny information. We add a second minimization pass where such states have their permission information cleared and are thus moved into the non-accepting partion. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Kees Cook <kees@ubuntu.com>
This commit is contained in:
parent
82a20d9bb8
commit
6f95ff5637
6 changed files with 120 additions and 72 deletions
|
@ -270,6 +270,21 @@ extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
|
|||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
|
||||
if (dfa.apply_and_clear_deny() && flags & DFA_CONTROL_MINIMIZE) {
|
||||
/* 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(flags);
|
||||
|
||||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
|
||||
dfa.remove_unreachable(flags);
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ typedef enum dfaflags {
|
|||
DFA_CONTROL_TREE_LEFT = 1 << 3,
|
||||
DFA_CONTROL_MINIMIZE = 1 << 4,
|
||||
DFA_CONTROL_MINIMIZE_HASH_TRANS = 1 << 5,
|
||||
DFA_CONTROL_MINIMIZE_HASH_PERMS = 1 << 6,
|
||||
DFA_CONTROL_REMOVE_UNREACHABLE = 1 << 7,
|
||||
DFA_CONTROL_TRANS_HIGH = 1 << 8,
|
||||
|
||||
|
|
|
@ -419,10 +419,19 @@ size_t DFA::hash_trans(State *s)
|
|||
return hash;
|
||||
}
|
||||
|
||||
int DFA::apply_and_clear_deny(void)
|
||||
{
|
||||
int c = 0;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
c += (*i)->apply_and_clear_deny();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* minimize the number of dfa states */
|
||||
void DFA::minimize(dfaflags_t flags)
|
||||
{
|
||||
map<pair<uint64_t, size_t>, Partition *> perm_map;
|
||||
map<size_t, Partition *> perm_map;
|
||||
list<Partition *> partitions;
|
||||
|
||||
/* Set up the initial partitions
|
||||
|
@ -438,27 +447,21 @@ void DFA::minimize(dfaflags_t flags)
|
|||
int accept_count = 0;
|
||||
int final_accept = 0;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
uint64_t perm_hash = 0;
|
||||
if (flags & DFA_CONTROL_MINIMIZE_HASH_PERMS) {
|
||||
/* make every unique perm create a new partition */
|
||||
perm_hash = ((uint64_t) PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32 |
|
||||
(uint64_t) (*i)->perms.allow;
|
||||
} else if (!(*i)->perms.is_null()) {
|
||||
/* combine all perms together into a single parition */
|
||||
perm_hash = 1;
|
||||
} /* else not an accept state so 0 for perm_hash */
|
||||
size_t trans_hash = 0;
|
||||
size_t hash = 0;
|
||||
if (!(*i)->perms.is_null())
|
||||
/* combine all states carrying accept info together
|
||||
into an single initial parition */
|
||||
hash = 1;
|
||||
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
|
||||
trans_hash = hash_trans(*i);
|
||||
pair<uint64_t, size_t> group = make_pair(perm_hash, trans_hash);
|
||||
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
|
||||
hash |= hash_trans(*i) << 1;
|
||||
map<size_t, Partition *>::iterator p = perm_map.find(hash);
|
||||
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(hash, part));
|
||||
partitions.push_back(part);
|
||||
(*i)->partition = part;
|
||||
if (perm_hash)
|
||||
if (hash & 1)
|
||||
accept_count++;
|
||||
} else {
|
||||
(*i)->partition = p->second;
|
||||
|
@ -470,6 +473,7 @@ void DFA::minimize(dfaflags_t flags)
|
|||
<< partitions.size() << "\tinit " << partitions.size()
|
||||
<< " (accept " << accept_count << ")\r";
|
||||
}
|
||||
|
||||
/* perm_map is no longer needed so free the memory it is using.
|
||||
* Don't remove - doing it manually here helps reduce peak memory usage.
|
||||
*/
|
||||
|
@ -569,12 +573,9 @@ void DFA::minimize(dfaflags_t flags)
|
|||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << **i << ", ";
|
||||
(*i)->label = -1;
|
||||
rep->perms.allow |= (*i)->perms.allow;
|
||||
rep->perms.deny |= (*i)->perms.deny;
|
||||
rep->perms.audit |= (*i)->perms.audit;
|
||||
rep->perms.quiet |= (*i)->perms.quiet;
|
||||
rep->perms.add((*i)->perms);
|
||||
}
|
||||
if (rep->perms.allow || rep->perms.audit || rep->perms.quiet)
|
||||
if (!rep->perms.is_null())
|
||||
final_accept++;
|
||||
//if ((*p)->size() > 1)
|
||||
//cerr << "\n";
|
||||
|
@ -856,8 +857,8 @@ static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
|
|||
int accept_perms(NodeSet *state, perms_t &perms)
|
||||
{
|
||||
int error = 0;
|
||||
uint32_t allow = 0, exact_match_allow = 0;
|
||||
uint32_t audit = 0, exact_audit = 0, quiet = 0, deny = 0;
|
||||
uint32_t exact_match_allow = 0;
|
||||
uint32_t exact_audit = 0;
|
||||
|
||||
perms.clear();
|
||||
|
||||
|
@ -876,60 +877,40 @@ int accept_perms(NodeSet *state, perms_t &perms)
|
|||
exact_match_allow |= match->flag;
|
||||
exact_audit |= match->audit;
|
||||
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
|
||||
deny |= match->flag;
|
||||
quiet |= match->audit;
|
||||
perms.deny |= match->flag;
|
||||
perms.quiet |= match->audit;
|
||||
} else {
|
||||
if (!is_merged_x_consistent(allow, match->flag))
|
||||
if (!is_merged_x_consistent(perms.allow, match->flag))
|
||||
error = 1;
|
||||
allow |= match->flag;
|
||||
audit |= match->audit;
|
||||
perms.allow |= match->flag;
|
||||
perms.audit |= match->audit;
|
||||
}
|
||||
}
|
||||
|
||||
//if (audit || quiet)
|
||||
//fprintf(stderr, "allow: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", allow, audit, exact_match_allow, exact_audit, deny, quiet);
|
||||
|
||||
allow |= exact_match_allow & ~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
|
||||
perms.allow |= exact_match_allow & ~(ALL_AA_EXEC_TYPE);
|
||||
|
||||
if (exact_match_allow & AA_USER_EXEC_TYPE) {
|
||||
allow = (exact_match_allow & AA_USER_EXEC_TYPE) |
|
||||
(allow & ~AA_USER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_USER_EXEC_TYPE) |
|
||||
(audit & ~AA_USER_EXEC_TYPE);
|
||||
perms.allow = (exact_match_allow & AA_USER_EXEC_TYPE) |
|
||||
(perms.allow & ~AA_USER_EXEC_TYPE);
|
||||
perms.audit = (exact_audit & AA_USER_EXEC_TYPE) |
|
||||
(perms.audit & ~AA_USER_EXEC_TYPE);
|
||||
perms.exact = AA_USER_EXEC_TYPE;
|
||||
}
|
||||
if (exact_match_allow & AA_OTHER_EXEC_TYPE) {
|
||||
allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) |
|
||||
(allow & ~AA_OTHER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
|
||||
(audit & ~AA_OTHER_EXEC_TYPE);
|
||||
perms.allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) |
|
||||
(perms.allow & ~AA_OTHER_EXEC_TYPE);
|
||||
perms.audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
|
||||
(perms.audit & ~AA_OTHER_EXEC_TYPE);
|
||||
perms.exact |= AA_OTHER_EXEC_TYPE;
|
||||
}
|
||||
if (allow & AA_USER_EXEC & deny)
|
||||
allow &= ~AA_USER_EXEC_TYPE;
|
||||
if (AA_USER_EXEC & perms.deny)
|
||||
perms.deny |= AA_USER_EXEC_TYPE;
|
||||
|
||||
if (allow & AA_OTHER_EXEC & deny)
|
||||
allow &= ~AA_OTHER_EXEC_TYPE;
|
||||
if (AA_OTHER_EXEC & perms.deny)
|
||||
perms.deny |= AA_OTHER_EXEC_TYPE;
|
||||
|
||||
allow &= ~deny;
|
||||
|
||||
perms.allow = allow & ~deny;
|
||||
perms.deny = deny;
|
||||
perms.audit = audit;
|
||||
perms.quiet = quiet & deny;
|
||||
|
||||
// if (allow & AA_ERROR_BIT) {
|
||||
// fprintf(stderr, "error bit 0x%x\n", allow);
|
||||
// exit(255);
|
||||
//}
|
||||
|
||||
//if (allow & AA_EXEC_BITS)
|
||||
//fprintf(stderr, "accept perm: 0x%x\n", allow);
|
||||
/*
|
||||
if (allow & ~AA_VALID_PERMS)
|
||||
yyerror(_("Internal error accumulated invalid perm 0x%llx\n"), allow);
|
||||
*/
|
||||
|
||||
//if (allow & AA_CHANGE_HAT)
|
||||
// fprintf(stderr, "change_hat 0x%x\n", allow);
|
||||
perms.allow &= ~perms.deny;
|
||||
perms.quiet &= perms.deny;
|
||||
|
||||
if (error)
|
||||
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");
|
||||
|
|
|
@ -37,9 +37,11 @@ class State;
|
|||
typedef map<uchar, State *> StateTrans;
|
||||
typedef list<State *> Partition;
|
||||
|
||||
#include "../immunix.h"
|
||||
|
||||
class perms_t {
|
||||
public:
|
||||
perms_t(void): allow(0), deny(0), audit(0), quiet(0) { };
|
||||
perms_t(void) throw(int): allow(0), deny(0), audit(0), quiet(0), exact(0) { };
|
||||
|
||||
bool is_null(void) { return !(allow | deny | audit | quiet); }
|
||||
|
||||
|
@ -47,9 +49,59 @@ public:
|
|||
void add(perms_t &rhs)
|
||||
{
|
||||
deny |= rhs.deny;
|
||||
allow = (allow | rhs.audit) & ~deny;
|
||||
|
||||
if (!is_merged_x_consistent(allow & AA_USER_EXEC_TYPE,
|
||||
rhs.allow & AA_USER_EXEC_TYPE)) {
|
||||
if ((exact & AA_USER_EXEC_TYPE) &&
|
||||
!(rhs.exact & AA_USER_EXEC_TYPE)) {
|
||||
/* do nothing */
|
||||
} else if ((rhs.exact & AA_USER_EXEC_TYPE) &&
|
||||
!(exact & AA_USER_EXEC_TYPE)) {
|
||||
allow = (allow & ~AA_USER_EXEC_TYPE) |
|
||||
(rhs.allow & AA_USER_EXEC_TYPE);
|
||||
} else
|
||||
throw 1;
|
||||
} else
|
||||
allow |= rhs.allow & AA_USER_EXEC_TYPE;
|
||||
|
||||
if (!is_merged_x_consistent(allow & AA_OTHER_EXEC_TYPE,
|
||||
rhs.allow & AA_OTHER_EXEC_TYPE)) {
|
||||
if ((exact & AA_OTHER_EXEC_TYPE) &&
|
||||
!(rhs.exact & AA_OTHER_EXEC_TYPE)) {
|
||||
/* do nothing */
|
||||
} else if ((rhs.exact & AA_OTHER_EXEC_TYPE) &&
|
||||
!(exact & AA_OTHER_EXEC_TYPE)) {
|
||||
allow = (allow & ~AA_OTHER_EXEC_TYPE) |
|
||||
(rhs.allow & AA_OTHER_EXEC_TYPE);
|
||||
} else
|
||||
throw 1;
|
||||
} else
|
||||
allow |= rhs.allow & AA_OTHER_EXEC_TYPE;
|
||||
|
||||
|
||||
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE)) & ~deny;
|
||||
audit |= rhs.audit;
|
||||
quiet = (quiet | rhs.quiet) & deny;
|
||||
|
||||
/*
|
||||
if (exec & AA_USER_EXEC_TYPE &&
|
||||
(exec & AA_USER_EXEC_TYPE) != (allow & AA_USER_EXEC_TYPE))
|
||||
throw 1;
|
||||
if (exec & AA_OTHER_EXEC_TYPE &&
|
||||
(exec & AA_OTHER_EXEC_TYPE) != (allow & AA_OTHER_EXEC_TYPE))
|
||||
throw 1;
|
||||
*/
|
||||
}
|
||||
|
||||
int apply_and_clear_deny(void)
|
||||
{
|
||||
if (deny) {
|
||||
allow &= ~deny;
|
||||
quiet &= deny;
|
||||
deny = 0;
|
||||
return is_null();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator<(perms_t const &rhs)const
|
||||
|
@ -63,7 +115,7 @@ public:
|
|||
return quiet < rhs.quiet;
|
||||
}
|
||||
|
||||
uint32_t allow, deny, audit, quiet;
|
||||
uint32_t allow, deny, audit, quiet, exact;
|
||||
};
|
||||
|
||||
int accept_perms(NodeSet *state, perms_t &perms);
|
||||
|
@ -311,6 +363,8 @@ public:
|
|||
return otherwise;
|
||||
};
|
||||
|
||||
int apply_and_clear_deny(void) { return perms.apply_and_clear_deny(); }
|
||||
|
||||
int label;
|
||||
perms_t perms;
|
||||
StateTrans trans;
|
||||
|
@ -385,6 +439,7 @@ public:
|
|||
bool same_mappings(State *s1, State *s2);
|
||||
size_t hash_trans(State *s);
|
||||
void minimize(dfaflags_t flags);
|
||||
int apply_and_clear_deny(void);
|
||||
void dump(ostream &os);
|
||||
void dump_dot_graph(ostream &os);
|
||||
void dump_uniq_perms(const char *s);
|
||||
|
|
|
@ -34,7 +34,7 @@ int names_only = 0;
|
|||
int current_lineno = 1;
|
||||
int option = OPTION_ADD;
|
||||
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS;
|
||||
|
||||
char *subdomainbase = NULL;
|
||||
char *progname = __FILE__;
|
||||
|
|
|
@ -228,8 +228,6 @@ optflag_table_t optflag_table[] = {
|
|||
{ 2, "expr-right-simplify", "right simplification first",
|
||||
DFA_CONTROL_TREE_LEFT },
|
||||
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
|
||||
{ 1, "hash-perms", "minimization - hash permissions during setup",
|
||||
DFA_CONTROL_MINIMIZE_HASH_PERMS },
|
||||
{ 1, "hash-trans", "minimization - hash transitions during setup",
|
||||
DFA_CONTROL_MINIMIZE_HASH_TRANS },
|
||||
{ 1, "remove-unreachable", "dfa unreachable state removal",
|
||||
|
|
Loading…
Add table
Reference in a new issue