2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-01 06:45:38 +00:00

Merge parser: Improve rule merging/dedup

Currently File rules are the only rules that have rule dedup/merging performed. Extend support for rule merging to all other rule types.

This can result in a small performance regression when rules can not be merged/deduped but can result in a large performance increase when lots of rules can be eliminated.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1065
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
John Johansen
2023-07-10 19:13:25 +00:00
40 changed files with 730 additions and 295 deletions

View File

@@ -106,7 +106,8 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \ STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
file_cache.h immunix.h lib.h mount.h network.h parser.h \ file_cache.h immunix.h lib.h mount.h network.h parser.h \
parser_include.h parser_version.h policy_cache.h policydb.h \ parser_include.h parser_version.h policy_cache.h policydb.h \
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
common_flags.h
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
GENERATED_HDRS = af_names.h generated_af_names.h \ GENERATED_HDRS = af_names.h generated_af_names.h \

View File

@@ -76,6 +76,35 @@ public:
virtual ostream &dump(ostream &os); virtual ostream &dump(ostream &os);
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof) = 0; virtual int gen_policy_re(Profile &prof) = 0;
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
af_rule const &trhs = (rule_cast<af_rule const &>(rhs));
res = af - trhs.af;
if (res)
return res;
res = sock_type_n - trhs.sock_type_n;
if (res)
return res;
res = proto_n - trhs.proto_n;
if (res)
return res;
res = null_strcmp(sock_type, trhs.sock_type);
if (res)
return res;
res = null_strcmp(proto, trhs.proto);
if (res)
return res;
res = null_strcmp(label, trhs.label);
if (res)
return res;
return null_strcmp(peer_label, trhs.peer_label);
};
}; };
#endif /* __AA_AF_RULE_H */ #endif /* __AA_AF_RULE_H */

View File

@@ -24,6 +24,7 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include "common_optarg.h"
#include "network.h" #include "network.h"
#include "parser.h" #include "parser.h"
#include "profile.h" #include "profile.h"
@@ -203,7 +204,7 @@ void unix_rule::downgrade_rule(Profile &prof) {
* restrictive and may end up denying accesses that might be * restrictive and may end up denying accesses that might be
* allowed by the profile. * allowed by the profile.
*/ */
if (warnflags & WARN_RULE_NOT_ENFORCED) if (parseopts.warn & WARN_RULE_NOT_ENFORCED)
rule_t::warn_once(prof.name, "deny unix socket rule not enforced, can't be downgraded to generic network rule\n"); rule_t::warn_once(prof.name, "deny unix socket rule not enforced, can't be downgraded to generic network rule\n");
} }
} }
@@ -321,7 +322,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (features_supports_network || features_supports_networkv8) { if (features_supports_network || features_supports_networkv8) {
/* only warn if we are building against a kernel /* only warn if we are building against a kernel
* that requires downgrading */ * that requires downgrading */
if (warnflags & WARN_RULE_DOWNGRADED) if (parseopts.warn & WARN_RULE_DOWNGRADED)
rule_t::warn_once(prof.name, "downgrading extended network unix socket rule to generic network rule\n"); rule_t::warn_once(prof.name, "downgrading extended network unix socket rule to generic network rule\n");
/* TODO: add ability to abort instead of downgrade */ /* TODO: add ability to abort instead of downgrade */
return RULE_OK; return RULE_OK;
@@ -337,7 +338,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_CREATE), map_perms(AA_NET_CREATE),
map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0), map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0),
dfaflags)) parseopts))
goto fail; goto fail;
mask &= ~AA_NET_CREATE; mask &= ~AA_NET_CREATE;
} }
@@ -362,7 +363,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_BIND), map_perms(AA_NET_BIND),
map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0), map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0),
dfaflags)) parseopts))
goto fail; goto fail;
/* clear if auto, else generic need to generate addr below */ /* clear if auto, else generic need to generate addr below */
if (addr) if (addr)
@@ -387,7 +388,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(mask & local_mask), map_perms(mask & local_mask),
map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0), map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0),
dfaflags)) parseopts))
goto fail; goto fail;
} }
@@ -401,7 +402,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_LISTEN), map_perms(AA_NET_LISTEN),
map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0), map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0),
dfaflags)) parseopts))
goto fail; goto fail;
} }
if ((mask & AA_NET_OPT) && !has_peer_conds()) { if ((mask & AA_NET_OPT) && !has_peer_conds()) {
@@ -414,7 +415,7 @@ int unix_rule::gen_policy_re(Profile &prof)
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_OPT), map_perms(AA_NET_OPT),
map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0), map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0),
dfaflags)) parseopts))
goto fail; goto fail;
} }
mask &= ~AA_LOCAL_NET_PERMS | AA_NET_ACCEPT; mask &= ~AA_LOCAL_NET_PERMS | AA_NET_ACCEPT;
@@ -432,7 +433,7 @@ int unix_rule::gen_policy_re(Profile &prof)
goto fail; goto fail;
buf = buffer.str(); buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), dfaflags)) if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), parseopts))
goto fail; goto fail;
} }

View File

@@ -62,6 +62,19 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
// inherit is_mergable() from af_rule
virtual int cmp(rule_t const &rhs) const
{
int res = af_rule::cmp(rhs);
if (res)
return res;
unix_rule const &trhs = (rule_cast<unix_rule const &>(rhs));
res = null_strcmp(addr, trhs.addr);
if (res)
return res;
return null_strcmp(peer_addr, trhs.peer_addr);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

33
parser/common_flags.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_COMMON_FLAGS_H
#define __AA_COMMON_FLAGS_H
typedef int optflags_t;
typedef struct optflags {
optflags_t control;
optflags_t dump;
optflags_t warn;
optflags_t Werror;
} optflags;
extern optflags parseopts;
#endif /* __AA_COMMON_FLAGS_H */

View File

@@ -29,80 +29,83 @@
optflag_table_t dumpflag_table[] = { optflag_table_t dumpflag_table[] = {
{ 1, "rule-exprs", "Dump rule to expr tree conversions", { 1, "rule-exprs", "Dump rule to expr tree conversions",
DFA_DUMP_RULE_EXPR }, DUMP_DFA_RULE_EXPR },
{ 1, "expr-stats", "Dump stats on expr tree", DFA_DUMP_TREE_STATS }, { 1, "expr-stats", "Dump stats on expr tree", DUMP_DFA_TREE_STATS },
{ 1, "expr-tree", "Dump expression tree", DFA_DUMP_TREE }, { 1, "expr-tree", "Dump expression tree", DUMP_DFA_TREE },
{ 1, "expr-simplified", "Dump simplified expression tree", { 1, "expr-simplified", "Dump simplified expression tree",
DFA_DUMP_SIMPLE_TREE }, DUMP_DFA_SIMPLE_TREE },
{ 1, "stats", "Dump all compile stats", { 1, "stats", "Dump all compile stats",
DFA_DUMP_TREE_STATS | DFA_DUMP_STATS | DFA_DUMP_TRANS_STATS | DUMP_DFA_TREE_STATS | DUMP_DFA_STATS | DUMP_DFA_TRANS_STATS |
DFA_DUMP_EQUIV_STATS | DFA_DUMP_DIFF_STATS }, DUMP_DFA_EQUIV_STATS | DUMP_DFA_DIFF_STATS },
{ 1, "progress", "Dump progress for all compile phases", { 1, "progress", "Dump progress for all compile phases",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS | DFA_DUMP_TRANS_PROGRESS | DUMP_DFA_PROGRESS | DUMP_DFA_STATS | DUMP_DFA_TRANS_PROGRESS |
DFA_DUMP_TRANS_STATS | DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS }, DUMP_DFA_TRANS_STATS | DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
{ 1, "dfa-progress", "Dump dfa creation as in progress", { 1, "dfa-progress", "Dump dfa creation as in progress",
DFA_DUMP_PROGRESS | DFA_DUMP_STATS }, DUMP_DFA_PROGRESS | DUMP_DFA_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DFA_DUMP_STATS }, { 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS },
{ 1, "dfa-states", "Dump dfa state diagram", DFA_DUMP_STATES }, { 1, "dfa-states", "Dump dfa state diagram", DUMP_DFA_STATES },
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DFA_DUMP_GRAPH }, { 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DUMP_DFA_GRAPH },
{ 1, "dfa-minimize", "Dump dfa minimization", DFA_DUMP_MINIMIZE }, { 1, "dfa-minimize", "Dump dfa minimization", DUMP_DFA_MINIMIZE },
{ 1, "dfa-unreachable", "Dump dfa unreachable states", { 1, "dfa-unreachable", "Dump dfa unreachable states",
DFA_DUMP_UNREACHABLE }, DUMP_DFA_UNREACHABLE },
{ 1, "dfa-node-map", "Dump expr node set to state mapping", { 1, "dfa-node-map", "Dump expr node set to state mapping",
DFA_DUMP_NODE_TO_DFA }, DUMP_DFA_NODE_TO_DFA },
{ 1, "dfa-uniq-perms", "Dump unique perms", { 1, "dfa-uniq-perms", "Dump unique perms",
DFA_DUMP_UNIQ_PERMS }, DUMP_DFA_UNIQ_PERMS },
{ 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization", { 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization",
DFA_DUMP_MIN_UNIQ_PERMS }, DUMP_DFA_MIN_UNIQ_PERMS },
{ 1, "dfa-minimize-partitions", "Dump dfa minimization partitions", { 1, "dfa-minimize-partitions", "Dump dfa minimization partitions",
DFA_DUMP_MIN_PARTS }, DUMP_DFA_MIN_PARTS },
{ 1, "compress-progress", "Dump progress of compression", { 1, "compress-progress", "Dump progress of compression",
DFA_DUMP_TRANS_PROGRESS | DFA_DUMP_TRANS_STATS }, DUMP_DFA_TRANS_PROGRESS | DUMP_DFA_TRANS_STATS },
{ 1, "compress-stats", "Dump stats on compression", { 1, "compress-stats", "Dump stats on compression",
DFA_DUMP_TRANS_STATS }, DUMP_DFA_TRANS_STATS },
{ 1, "compressed-dfa", "Dump compressed dfa", DFA_DUMP_TRANS_TABLE }, { 1, "compressed-dfa", "Dump compressed dfa", DUMP_DFA_TRANS_TABLE },
{ 1, "equiv-stats", "Dump equivalence class stats", { 1, "equiv-stats", "Dump equivalence class stats",
DFA_DUMP_EQUIV_STATS }, DUMP_DFA_EQUIV_STATS },
{ 1, "equiv", "Dump equivalence class", DFA_DUMP_EQUIV }, { 1, "equiv", "Dump equivalence class", DUMP_DFA_EQUIV },
{ 1, "diff-encode", "Dump differential encoding", { 1, "diff-encode", "Dump differential encoding",
DFA_DUMP_DIFF_ENCODE }, DUMP_DFA_DIFF_ENCODE },
{ 1, "diff-stats", "Dump differential encoding stats", { 1, "diff-stats", "Dump differential encoding stats",
DFA_DUMP_DIFF_STATS }, DUMP_DFA_DIFF_STATS },
{ 1, "diff-progress", "Dump progress of differential encoding", { 1, "diff-progress", "Dump progress of differential encoding",
DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS }, DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
{ 1, "rule-merge", "dump information about rule merging", DUMP_RULE_MERGE},
{ 0, NULL, NULL, 0 }, { 0, NULL, NULL, 0 },
}; };
optflag_table_t optflag_table[] = { optflag_table_t dfaoptflag_table[] = {
{ 2, "0", "no optimizations", { 2, "0", "no optimizations",
DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE |
DFA_CONTROL_MINIMIZE | DFA_CONTROL_REMOVE_UNREACHABLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_REMOVE_UNREACHABLE |
DFA_CONTROL_DIFF_ENCODE CONTROL_DFA_DIFF_ENCODE
}, },
{ 1, "equiv", "use equivalent classes", DFA_CONTROL_EQUIV }, { 1, "equiv", "use equivalent classes", CONTROL_DFA_EQUIV },
{ 1, "expr-normalize", "expression tree normalization", { 1, "expr-normalize", "expression tree normalization",
DFA_CONTROL_TREE_NORMAL }, CONTROL_DFA_TREE_NORMAL },
{ 1, "expr-simplify", "expression tree simplification", { 1, "expr-simplify", "expression tree simplification",
DFA_CONTROL_TREE_SIMPLE }, CONTROL_DFA_TREE_SIMPLE },
{ 0, "expr-left-simplify", "left simplification first", { 0, "expr-left-simplify", "left simplification first",
DFA_CONTROL_TREE_LEFT }, CONTROL_DFA_TREE_LEFT },
{ 2, "expr-right-simplify", "right simplification first", { 2, "expr-right-simplify", "right simplification first",
DFA_CONTROL_TREE_LEFT }, CONTROL_DFA_TREE_LEFT },
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE }, { 1, "minimize", "dfa state minimization", CONTROL_DFA_MINIMIZE },
{ 1, "filter-deny", "filter out deny information from final dfa", { 1, "filter-deny", "filter out deny information from final dfa",
DFA_CONTROL_FILTER_DENY }, CONTROL_DFA_FILTER_DENY },
{ 1, "remove-unreachable", "dfa unreachable state removal", { 1, "remove-unreachable", "dfa unreachable state removal",
DFA_CONTROL_REMOVE_UNREACHABLE }, CONTROL_DFA_REMOVE_UNREACHABLE },
{ 0, "compress-small", { 0, "compress-small",
"do slower dfa transition table compression", "do slower dfa transition table compression",
DFA_CONTROL_TRANS_HIGH }, CONTROL_DFA_TRANS_HIGH },
{ 2, "compress-fast", "do faster dfa transition table compression", { 2, "compress-fast", "do faster dfa transition table compression",
DFA_CONTROL_TRANS_HIGH }, CONTROL_DFA_TRANS_HIGH },
{ 1, "diff-encode", "Differentially encode transitions", { 1, "diff-encode", "Differentially encode transitions",
DFA_CONTROL_DIFF_ENCODE }, CONTROL_DFA_DIFF_ENCODE },
{ 1, "rule-merge", "turn on rule merging", CONTROL_RULE_MERGE},
{ 0, NULL, NULL, 0 }, { 0, NULL, NULL, 0 },
}; };
void print_flag_table(optflag_table_t *table) void print_flag_table(optflag_table_t *table)
{ {
int i; int i;
@@ -114,12 +117,14 @@ void print_flag_table(optflag_table_t *table)
printf("%-*s \t%s\n", longest, " show", "show flags that have been set and exit"); printf("%-*s \t%s\n", longest, " show", "show flags that have been set and exit");
for (i = 0; table[i].option; i++) { for (i = 0; table[i].option; i++) {
printf("%5s%-*s \t%s\n", (table[i].control & 1) ? "[no-]" : "", printf("%5s%-*s \t%s\n",
(table[i].control & OPT_FLAG_CONTROL_PREFIX_NO) ? "[no-]" : "",
longest, table[i].option, table[i].desc); longest, table[i].option, table[i].desc);
} }
} }
void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags) void print_flags(const char *prefix, optflag_table_t *table,
optflags_t flags)
{ {
int i, count = 0; int i, count = 0;
@@ -137,7 +142,7 @@ void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags)
} }
int handle_flag_table(optflag_table_t *table, const char *optarg, int handle_flag_table(optflag_table_t *table, const char *optarg,
dfaflags_t *flags) optflags_t *flags)
{ {
const char *arg = optarg; const char *arg = optarg;
int i, invert = 0; int i, invert = 0;

View File

@@ -21,25 +21,31 @@
#ifndef __AA_COMMON_OPTARG_H #ifndef __AA_COMMON_OPTARG_H
#define __AA_COMMON_OPTARG_H #define __AA_COMMON_OPTARG_H
#include "common_flags.h"
#include "libapparmor_re/apparmor_re.h" #include "libapparmor_re/apparmor_re.h"
/* /*
* flag: 1 - allow no- inversion * flag: 1 - allow no- inversion
* flag: 2 - flags specified should be masked off * flag: 2 - flags specified should be masked off
*/ */
#define OPT_FLAG_CONTROL_PREFIX_NO 1
#define OPT_FLAG_CONTROL_MASK 2
typedef struct { typedef struct {
int control; int control;
const char *option; const char *option;
const char *desc; const char *desc;
dfaflags_t flags; optflags_t flags;
} optflag_table_t; } optflag_table_t;
extern optflag_table_t dumpflag_table[]; extern optflag_table_t dumpflag_table[];
extern optflag_table_t optflag_table[]; extern optflag_table_t dfaoptflag_table[];
void print_flags(const char *prefix, optflag_table_t *table, dfaflags_t flags);
void print_flags(const char *prefix, optflag_table_t *table,
optflags_t flags);
int handle_flag_table(optflag_table_t *table, const char *optarg, int handle_flag_table(optflag_table_t *table, const char *optarg,
dfaflags_t *flags); optflags_t *flags);
void flagtable_help(const char *name, const char *header, const char *command, void flagtable_help(const char *name, const char *header, const char *command,
optflag_table_t *table); optflag_table_t *table);

View File

@@ -276,21 +276,21 @@ int dbus_rule::gen_policy_re(Profile &prof)
if (perms & AA_DBUS_BIND) { if (perms & AA_DBUS_BIND) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms & AA_DBUS_BIND, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms & AA_DBUS_BIND,
audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0, audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0,
2, vec, dfaflags, false)) 2, vec, parseopts, false))
goto fail; goto fail;
} }
if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) { if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE), perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0, audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0,
6, vec, dfaflags, false)) 6, vec, parseopts, false))
goto fail; goto fail;
} }
if (perms & AA_DBUS_EAVESDROP) { if (perms & AA_DBUS_EAVESDROP) {
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & AA_DBUS_EAVESDROP, perms & AA_DBUS_EAVESDROP,
audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0, audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0,
1, vec, dfaflags, false)) 1, vec, parseopts, false))
goto fail; goto fail;
} }

View File

@@ -62,6 +62,32 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
dbus_rule const &trhs = (rule_cast<dbus_rule const &>(rhs));
res = null_strcmp(bus, trhs.bus);
if (res)
return res;
res = null_strcmp(name, trhs.name);
if (res)
return res;
res = null_strcmp(peer_label, trhs.peer_label);
if (res)
return res;
res = null_strcmp(path, trhs.path);
if (res)
return res;
res = null_strcmp(interface, trhs.interface);
if (res)
return res;
return null_strcmp(member, trhs.member);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

View File

@@ -15,6 +15,7 @@
* along with this program; if not, contact or Canonical Ltd. * along with this program; if not, contact or Canonical Ltd.
*/ */
#include "common_optarg.h"
#include "parser.h" #include "parser.h"
#include "profile.h" #include "profile.h"
#include "io_uring.h" #include "io_uring.h"
@@ -123,14 +124,14 @@ int io_uring_rule::gen_policy_re(Profile &prof)
if (perms & AA_VALID_IO_URING_PERMS) { if (perms & AA_VALID_IO_URING_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0, audit == AUDIT_FORCE ? perms : 0,
dfaflags)) parseopts))
goto fail; goto fail;
if (perms & AA_IO_URING_OVERRIDE_CREDS) { if (perms & AA_IO_URING_OVERRIDE_CREDS) {
buf = buffer.str(); /* update buf to have label */ buf = buffer.str(); /* update buf to have label */
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
perms, audit == AUDIT_FORCE ? perms : 0, perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags)) parseopts))
goto fail; goto fail;
} }

View File

@@ -49,6 +49,16 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
return null_strcmp(label,
(rule_cast<io_uring_rule const &>(rhs)).label);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

View File

@@ -45,9 +45,9 @@ aare_rules::~aare_rules(void)
} }
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms, bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags) uint32_t audit, optflags const &opts)
{ {
return add_rule_vec(deny, perms, audit, 1, &rule, flags, false); return add_rule_vec(deny, perms, audit, 1, &rule, opts, false);
} }
void aare_rules::add_to_rules(Node *tree, Node *perms) void aare_rules::add_to_rules(Node *tree, Node *perms)
@@ -72,7 +72,7 @@ static Node *cat_with_oob_separator(Node *l, Node *r)
} }
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit, bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
int count, const char **rulev, dfaflags_t flags, int count, const char **rulev, optflags const &opts,
bool oob) bool oob)
{ {
Node *tree = NULL, *accept; Node *tree = NULL, *accept;
@@ -110,7 +110,7 @@ bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
accept = unique_perms.insert(deny, perms, audit, exact_match); accept = unique_perms.insert(deny, perms, audit, exact_match);
if (flags & DFA_DUMP_RULE_EXPR) { if (opts.dump & DUMP_DFA_RULE_EXPR) {
const char *separator; const char *separator;
if (oob) if (oob)
separator = "\\-x01"; separator = "\\-x01";
@@ -152,13 +152,13 @@ err:
* advanced by a null character for each xattr. * advanced by a null character for each xattr.
*/ */
bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm, bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
dfaflags_t flags) optflags const &opts)
{ {
Node *tree = NULL; Node *tree = NULL;
if (regex_parse(&tree, rule)) if (regex_parse(&tree, rule))
return false; return false;
if (flags & DFA_DUMP_RULE_EXPR) { if (opts.dump & DUMP_DFA_RULE_EXPR) {
cerr << "rule: "; cerr << "rule: ";
cerr << rule; cerr << rule;
cerr << " -> "; cerr << " -> ";
@@ -195,7 +195,7 @@ bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
* else NULL on failure, @min_match_len set to the shortest string * else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority. * that can match the dfa for determining xmatch priority.
*/ */
void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags, void *aare_rules::create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa) bool filedfa)
{ {
char *buffer = NULL; char *buffer = NULL;
@@ -204,15 +204,15 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* set nodes */ * set nodes */
PermExprMap::iterator i = expr_map.begin(); PermExprMap::iterator i = expr_map.begin();
if (i != expr_map.end()) { if (i != expr_map.end()) {
if (flags & DFA_CONTROL_TREE_SIMPLE) { if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
Node *tmp = simplify_tree(i->second, flags); Node *tmp = simplify_tree(i->second, opts);
root = new CatNode(tmp, i->first); root = new CatNode(tmp, i->first);
} else } else
root = new CatNode(i->second, i->first); root = new CatNode(i->second, i->first);
for (i++; i != expr_map.end(); i++) { for (i++; i != expr_map.end(); i++) {
Node *tmp; Node *tmp;
if (flags & DFA_CONTROL_TREE_SIMPLE) { if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
tmp = simplify_tree(i->second, flags); tmp = simplify_tree(i->second, opts);
} else } else
tmp = i->second; tmp = i->second;
root = new AltNode(root, new CatNode(tmp, i->first)); root = new AltNode(root, new CatNode(tmp, i->first));
@@ -226,22 +226,22 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* this debug dump. * this debug dump.
*/ */
label_nodes(root); label_nodes(root);
if (flags & DFA_DUMP_TREE) { if (opts.dump & DUMP_DFA_TREE) {
cerr << "\nDFA: Expression Tree\n"; cerr << "\nDFA: Expression Tree\n";
root->dump(cerr); root->dump(cerr);
cerr << "\n\n"; cerr << "\n\n";
} }
if (flags & DFA_CONTROL_TREE_SIMPLE) { if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
/* This is old total tree, simplification point /* This is old total tree, simplification point
* For now just do simplification up front. It gets most * For now just do simplification up front. It gets most
* of the benefit running on the smaller chains, and is * of the benefit running on the smaller chains, and is
* overall faster because there are less nodes. Reevaluate * overall faster because there are less nodes. Reevaluate
* once tree simplification is rewritten * once tree simplification is rewritten
*/ */
//root = simplify_tree(root, flags); //root = simplify_tree(root, opts);
if (flags & DFA_DUMP_SIMPLE_TREE) { if (opts.dump & DUMP_DFA_SIMPLE_TREE) {
cerr << "\nDFA: Simplified Expression Tree\n"; cerr << "\nDFA: Simplified Expression Tree\n";
root->dump(cerr); root->dump(cerr);
cerr << "\n\n"; cerr << "\n\n";
@@ -250,19 +250,19 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
stringstream stream; stringstream stream;
try { try {
DFA dfa(root, flags, filedfa); DFA dfa(root, opts, filedfa);
if (flags & DFA_DUMP_UNIQ_PERMS) if (opts.dump & DUMP_DFA_UNIQ_PERMS)
dfa.dump_uniq_perms("dfa"); dfa.dump_uniq_perms("dfa");
if (flags & DFA_CONTROL_MINIMIZE) { if (opts.control & CONTROL_DFA_MINIMIZE) {
dfa.minimize(flags); dfa.minimize(opts);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS) if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa"); dfa.dump_uniq_perms("minimized dfa");
} }
if (flags & DFA_CONTROL_FILTER_DENY && if (opts.control & CONTROL_DFA_FILTER_DENY &&
flags & DFA_CONTROL_MINIMIZE && opts.control & CONTROL_DFA_MINIMIZE &&
dfa.apply_and_clear_deny()) { dfa.apply_and_clear_deny()) {
/* Do a second minimization pass as removal of deny /* Do a second minimization pass as removal of deny
* information has moved some states from accepting * information has moved some states from accepting
@@ -271,42 +271,42 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
* TODO: add this as a tail pass to minimization * TODO: add this as a tail pass to minimization
* so we don't need to do a full second pass * so we don't need to do a full second pass
*/ */
dfa.minimize(flags); dfa.minimize(opts);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS) if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa"); dfa.dump_uniq_perms("minimized dfa");
} }
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE) if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE)
dfa.remove_unreachable(flags); dfa.remove_unreachable(opts);
if (flags & DFA_DUMP_STATES) if (opts.dump & DUMP_DFA_STATES)
dfa.dump(cerr); dfa.dump(cerr);
if (flags & DFA_DUMP_GRAPH) if (opts.dump & DUMP_DFA_GRAPH)
dfa.dump_dot_graph(cerr); dfa.dump_dot_graph(cerr);
map<transchar, transchar> eq; map<transchar, transchar> eq;
if (flags & DFA_CONTROL_EQUIV) { if (opts.control & CONTROL_DFA_EQUIV) {
eq = dfa.equivalence_classes(flags); eq = dfa.equivalence_classes(opts);
dfa.apply_equivalence_classes(eq); dfa.apply_equivalence_classes(eq);
if (flags & DFA_DUMP_EQUIV) { if (opts.dump & DUMP_DFA_EQUIV) {
cerr << "\nDFA equivalence class\n"; cerr << "\nDFA equivalence class\n";
dump_equivalence_classes(cerr, eq); dump_equivalence_classes(cerr, eq);
} }
} else if (flags & DFA_DUMP_EQUIV) } else if (opts.dump & DUMP_DFA_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n"; cerr << "\nDFA did not generate an equivalence class\n";
if (flags & DFA_CONTROL_DIFF_ENCODE) { if (opts.control & CONTROL_DFA_DIFF_ENCODE) {
dfa.diff_encode(flags); dfa.diff_encode(opts);
if (flags & DFA_DUMP_DIFF_ENCODE) if (opts.dump & DUMP_DFA_DIFF_ENCODE)
dfa.dump_diff_encode(cerr); dfa.dump_diff_encode(cerr);
} }
CHFA chfa(dfa, eq, flags); CHFA chfa(dfa, eq, opts);
if (flags & DFA_DUMP_TRANS_TABLE) if (opts.dump & DUMP_DFA_TRANS_TABLE)
chfa.dump(cerr); chfa.dump(cerr);
chfa.flex_table(stream, ""); chfa.flex_table(stream, "");
} }

View File

@@ -23,6 +23,7 @@
#include <stdint.h> #include <stdint.h>
#include "../common_optarg.h"
#include "apparmor_re.h" #include "apparmor_re.h"
#include "expr-tree.h" #include "expr-tree.h"
@@ -101,11 +102,11 @@ class aare_rules {
~aare_rules(); ~aare_rules();
bool add_rule(const char *rule, int deny, uint32_t perms, bool add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags); uint32_t audit, optflags const &opts);
bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count, bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count,
const char **rulev, dfaflags_t flags, bool oob); const char **rulev, optflags const &opts, bool oob);
bool append_rule(const char *rule, bool oob, bool with_perm, dfaflags_t flags); bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts);
void *create_dfa(size_t *size, int *min_match_len, dfaflags_t flags, void *create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa); bool filedfa);
}; };

View File

@@ -19,40 +19,42 @@
#ifndef APPARMOR_RE_H #ifndef APPARMOR_RE_H
#define APPARMOR_RE_H #define APPARMOR_RE_H
typedef int dfaflags_t; #include "../common_flags.h"
#define CONTROL_DFA_EQUIV (1 << 0)
#define CONTROL_DFA_TREE_NORMAL (1 << 1)
#define CONTROL_DFA_TREE_SIMPLE (1 << 2)
#define CONTROL_DFA_TREE_LEFT (1 << 3)
#define CONTROL_DFA_MINIMIZE (1 << 4)
#define CONTROL_DFA_FILTER_DENY (1 << 6)
#define CONTROL_DFA_REMOVE_UNREACHABLE (1 << 7)
#define CONTROL_DFA_TRANS_HIGH (1 << 8)
#define CONTROL_DFA_DIFF_ENCODE (1 << 9)
#define CONTROL_RULE_MERGE (1 << 10)
#define DFA_CONTROL_EQUIV (1 << 0) #define DUMP_DFA_DIFF_PROGRESS (1 << 0)
#define DFA_CONTROL_TREE_NORMAL (1 << 1) #define DUMP_DFA_DIFF_ENCODE (1 << 1)
#define DFA_CONTROL_TREE_SIMPLE (1 << 2) #define DUMP_DFA_DIFF_STATS (1 << 2)
#define DFA_CONTROL_TREE_LEFT (1 << 3) #define DUMP_DFA_MIN_PARTS (1 << 3)
#define DFA_CONTROL_MINIMIZE (1 << 4) #define DUMP_DFA_UNIQ_PERMS (1 << 4)
#define DFA_CONTROL_FILTER_DENY (1 << 6) #define DUMP_DFA_MIN_UNIQ_PERMS (1 << 5)
#define DFA_CONTROL_REMOVE_UNREACHABLE (1 << 7) #define DUMP_DFA_TREE_STATS (1 << 6)
#define DFA_CONTROL_TRANS_HIGH (1 << 8) #define DUMP_DFA_TREE (1 << 7)
#define DFA_CONTROL_DIFF_ENCODE (1 << 9) #define DUMP_DFA_SIMPLE_TREE (1 << 8)
#define DUMP_DFA_PROGRESS (1 << 9)
#define DFA_DUMP_DIFF_PROGRESS (1 << 10) #define DUMP_DFA_STATS (1 << 10)
#define DFA_DUMP_DIFF_ENCODE (1 << 11) #define DUMP_DFA_STATES (1 << 11)
#define DFA_DUMP_DIFF_STATS (1 << 12) #define DUMP_DFA_GRAPH (1 << 12)
#define DFA_DUMP_MIN_PARTS (1 << 13) #define DUMP_DFA_TRANS_PROGRESS (1 << 13)
#define DFA_DUMP_UNIQ_PERMS (1 << 14) #define DUMP_DFA_TRANS_STATS (1 << 14)
#define DFA_DUMP_MIN_UNIQ_PERMS (1 << 15) #define DUMP_DFA_TRANS_TABLE (1 << 15)
#define DFA_DUMP_TREE_STATS (1 << 16) #define DUMP_DFA_EQUIV (1 << 16)
#define DFA_DUMP_TREE (1 << 17) #define DUMP_DFA_EQUIV_STATS (1 << 17)
#define DFA_DUMP_SIMPLE_TREE (1 << 18) #define DUMP_DFA_MINIMIZE (1 << 18)
#define DFA_DUMP_PROGRESS (1 << 19) #define DUMP_DFA_UNREACHABLE (1 << 19)
#define DFA_DUMP_STATS (1 << 20) #define DUMP_DFA_RULE_EXPR (1 << 20)
#define DFA_DUMP_STATES (1 << 21) #define DUMP_DFA_NODE_TO_DFA (1 << 21)
#define DFA_DUMP_GRAPH (1 << 22) #define DUMP_RULE_MERGE (1 << 22)
#define DFA_DUMP_TRANS_PROGRESS (1 << 23)
#define DFA_DUMP_TRANS_STATS (1 << 24)
#define DFA_DUMP_TRANS_TABLE (1 << 25)
#define DFA_DUMP_EQUIV (1 << 26)
#define DFA_DUMP_EQUIV_STATS (1 << 27)
#define DFA_DUMP_MINIMIZE (1 << 28)
#define DFA_DUMP_UNREACHABLE (1 << 29)
#define DFA_DUMP_RULE_EXPR (1 << 30)
#define DFA_DUMP_NODE_TO_DFA (1 << 31)
#endif /* APPARMOR_RE_H */ #endif /* APPARMOR_RE_H */

View File

@@ -49,9 +49,10 @@ void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
/** /**
* new Construct the transition table. * new Construct the transition table.
*/ */
CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq) CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts):
eq(eq)
{ {
if (flags & DFA_DUMP_TRANS_PROGRESS) if (opts.dump & DUMP_DFA_TRANS_PROGRESS)
fprintf(stderr, "Compressing HFA:\r"); fprintf(stderr, "Compressing HFA:\r");
chfaflags = 0; chfaflags = 0;
@@ -82,7 +83,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
if (*i == dfa.start || *i == dfa.nonmatching) if (*i == dfa.start || *i == dfa.nonmatching)
continue; continue;
optimal += (*i)->trans.size(); optimal += (*i)->trans.size();
if (flags & DFA_CONTROL_TRANS_HIGH) { if (opts.control & CONTROL_DFA_TRANS_HIGH) {
size_t range = 0; size_t range = 0;
if ((*i)->trans.size()) if ((*i)->trans.size())
range = range =
@@ -116,7 +117,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
int count = 2; int count = 2;
if (!(flags & DFA_CONTROL_TRANS_HIGH)) { if (!(opts.control & CONTROL_DFA_TRANS_HIGH)) {
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) { for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i != dfa.nonmatching && *i != dfa.start) { if (*i != dfa.nonmatching && *i != dfa.start) {
insert_state(free_list, *i, dfa); insert_state(free_list, *i, dfa);
@@ -124,7 +125,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny); accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny);
num.insert(make_pair(*i, num.size())); num.insert(make_pair(*i, num.size()));
} }
if (flags & (DFA_DUMP_TRANS_PROGRESS)) { if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
count++; count++;
if (count % 100 == 0) if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r", fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
@@ -141,7 +142,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny); accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny);
num.insert(make_pair(i->second, num.size())); num.insert(make_pair(i->second, num.size()));
} }
if (flags & (DFA_DUMP_TRANS_PROGRESS)) { if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
count++; count++;
if (count % 100 == 0) if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r", fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
@@ -150,7 +151,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
} }
} }
if (flags & (DFA_DUMP_TRANS_STATS | DFA_DUMP_TRANS_PROGRESS)) { if (opts.dump & (DUMP_DFA_TRANS_STATS | DUMP_DFA_TRANS_PROGRESS)) {
ssize_t size = 4 * next_check.size() + 6 * dfa.states.size(); ssize_t size = 4 * next_check.size() + 6 * dfa.states.size();
fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n", fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n",
dfa.states.size(), next_check.size(), optimal, dfa.states.size(), next_check.size(), optimal,

View File

@@ -37,7 +37,7 @@ class CHFA {
typedef vector<pair<const State *, size_t> > DefaultBase; typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck; typedef vector<pair<const State *, const State *> > NextCheck;
public: public:
CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags); CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts);
void dump(ostream & os); void dump(ostream & os);
void flex_table(ostream &os, const char *name); void flex_table(ostream &os, const char *name);
void init_free_list(vector<pair<size_t, size_t> > &free_list, void init_free_list(vector<pair<size_t, size_t> > &free_list,

View File

@@ -575,12 +575,12 @@ static void count_tree_nodes(Node *t, struct node_counts *counts)
// simplification passes. Simplification may exit sooner if no changes // simplification passes. Simplification may exit sooner if no changes
// are made. // are made.
#define MAX_PASSES 1 #define MAX_PASSES 1
Node *simplify_tree(Node *t, dfaflags_t flags) Node *simplify_tree(Node *t, optflags const &opts)
{ {
bool update = true; bool update = true;
int i; int i;
if (flags & DFA_DUMP_TREE_STATS) { if (opts.dump & DUMP_DFA_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts); count_tree_nodes(t, &counts);
fprintf(stderr, fprintf(stderr,
@@ -598,25 +598,25 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
// the dfa having about 7 thousands states, // the dfa having about 7 thousands states,
// and it having about 1.25 million states // and it having about 1.25 million states
int dir = 1; int dir = 1;
if (flags & DFA_CONTROL_TREE_LEFT) if (opts.control & CONTROL_DFA_TREE_LEFT)
dir = 0; dir = 0;
for (int count = 0; count < 2; count++) { for (int count = 0; count < 2; count++) {
bool modified; bool modified;
do { do {
modified = false; modified = false;
if (flags & DFA_CONTROL_TREE_NORMAL) if (opts.control & CONTROL_DFA_TREE_NORMAL)
t->normalize(dir); t->normalize(dir);
t = simplify_tree_base(t, dir, modified); t = simplify_tree_base(t, dir, modified);
if (modified) if (modified)
update = true; update = true;
} while (modified); } while (modified);
if (flags & DFA_CONTROL_TREE_LEFT) if (opts.control & CONTROL_DFA_TREE_LEFT)
dir++; dir++;
else else
dir--; dir--;
} }
} }
if (flags & DFA_DUMP_TREE_STATS) { if (opts.dump & DUMP_DFA_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts); count_tree_nodes(t, &counts);
fprintf(stderr, fprintf(stderr,

View File

@@ -958,7 +958,7 @@ struct node_counts {
extern EpsNode epsnode; extern EpsNode epsnode;
int debug_tree(Node *t); int debug_tree(Node *t);
Node *simplify_tree(Node *t, dfaflags_t flags); Node *simplify_tree(Node *t, optflags const &opts);
void label_nodes(Node *root); void label_nodes(Node *root);
unsigned long hash_NodeSet(NodeSet *ns); unsigned long hash_NodeSet(NodeSet *ns);
void flip_tree(Node *node); void flip_tree(Node *node);

View File

@@ -391,12 +391,12 @@ void DFA::dump_node_to_dfa(void)
cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n"; cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n";
} }
void DFA::process_work_queue(const char *header, dfaflags_t flags) void DFA::process_work_queue(const char *header, optflags const &opts)
{ {
int i = 0; int i = 0;
while (!work_queue.empty()) { while (!work_queue.empty()) {
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS)) { if (i % 1000 == 0 && (opts.dump & DUMP_DFA_PROGRESS)) {
cerr << "\033[2K" << header << ": queue " cerr << "\033[2K" << header << ": queue "
<< work_queue.size() << work_queue.size()
<< "\tstates " << "\tstates "
@@ -420,7 +420,7 @@ void DFA::process_work_queue(const char *header, dfaflags_t flags)
/** /**
* Construct a DFA from a syntax tree. * Construct a DFA from a syntax tree.
*/ */
DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(buildfiledfa) DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filedfa(buildfiledfa)
{ {
diffcount = 0; /* set by diff_encode */ diffcount = 0; /* set by diff_encode */
max_range = 256; max_range = 256;
@@ -428,7 +428,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
oob_range = 0; oob_range = 0;
ord_range = 8; ord_range = 8;
if (flags & DFA_DUMP_PROGRESS) if (opts.dump & DUMP_DFA_PROGRESS)
fprintf(stderr, "Creating dfa:\r"); fprintf(stderr, "Creating dfa:\r");
for (depth_first_traversal i(root); i; i++) { for (depth_first_traversal i(root); i; i++) {
@@ -437,7 +437,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
(*i)->compute_lastpos(); (*i)->compute_lastpos();
} }
if (flags & DFA_DUMP_PROGRESS) if (opts.dump & DUMP_DFA_PROGRESS)
fprintf(stderr, "Creating dfa: followpos\r"); fprintf(stderr, "Creating dfa: followpos\r");
for (depth_first_traversal i(root); i; i++) { for (depth_first_traversal i(root); i; i++) {
(*i)->compute_followpos(); (*i)->compute_followpos();
@@ -457,7 +457,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
* work_queue at any given time, thus reducing peak memory use. * work_queue at any given time, thus reducing peak memory use.
*/ */
work_queue.push_back(start); work_queue.push_back(start);
process_work_queue("Creating dfa", flags); process_work_queue("Creating dfa", opts);
max_range += oob_range; max_range += oob_range;
/* if oob_range is ever greater than 256 need to move to computing this */ /* if oob_range is ever greater than 256 need to move to computing this */
if (oob_range) if (oob_range)
@@ -471,10 +471,10 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
(*i)->followpos.clear(); (*i)->followpos.clear();
} }
if (flags & DFA_DUMP_NODE_TO_DFA) if (opts.dump & DUMP_DFA_NODE_TO_DFA)
dump_node_to_dfa(); dump_node_to_dfa();
if (flags & (DFA_DUMP_STATS)) { if (opts.dump & (DUMP_DFA_STATS)) {
cerr << "\033[2KCreated dfa: states " cerr << "\033[2KCreated dfa: states "
<< states.size() << states.size()
<< " proto { " << " proto { "
@@ -540,7 +540,7 @@ void DFA::dump_uniq_perms(const char *s)
} }
/* Remove dead or unreachable states */ /* Remove dead or unreachable states */
void DFA::remove_unreachable(dfaflags_t flags) void DFA::remove_unreachable(optflags const &opts)
{ {
set<State *> reachable; set<State *> reachable;
@@ -571,7 +571,7 @@ void DFA::remove_unreachable(dfaflags_t flags)
next = i; next = i;
next++; next++;
if (reachable.find(*i) == reachable.end()) { if (reachable.find(*i) == reachable.end()) {
if (flags & DFA_DUMP_UNREACHABLE) { if (opts.dump & DUMP_DFA_UNREACHABLE) {
cerr << "unreachable: " << **i; cerr << "unreachable: " << **i;
if (*i == start) if (*i == start)
cerr << " <=="; cerr << " <==";
@@ -586,7 +586,7 @@ void DFA::remove_unreachable(dfaflags_t flags)
} }
} }
if (count && (flags & DFA_DUMP_STATS)) if (count && (opts.dump & DUMP_DFA_STATS))
cerr << "DFA: states " << states.size() << " removed " cerr << "DFA: states " << states.size() << " removed "
<< count << " unreachable states\n"; << count << " unreachable states\n";
} }
@@ -645,7 +645,7 @@ int DFA::apply_and_clear_deny(void)
} }
/* minimize the number of dfa states */ /* minimize the number of dfa states */
void DFA::minimize(dfaflags_t flags) void DFA::minimize(optflags const &opts)
{ {
map<pair<uint64_t, size_t>, Partition *> perm_map; map<pair<uint64_t, size_t>, Partition *> perm_map;
list<Partition *> partitions; list<Partition *> partitions;
@@ -680,7 +680,7 @@ void DFA::minimize(dfaflags_t flags)
p->second->push_back(*i); p->second->push_back(*i);
} }
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 1000 == 0)) if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 1000 == 0))
cerr << "\033[2KMinimize dfa: partitions " cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit " << partitions.size() << partitions.size() << "\tinit " << partitions.size()
<< " (accept " << accept_count << ")\r"; << " (accept " << accept_count << ")\r";
@@ -692,7 +692,7 @@ void DFA::minimize(dfaflags_t flags)
perm_map.clear(); perm_map.clear();
int init_count = partitions.size(); int init_count = partitions.size();
if (flags & DFA_DUMP_PROGRESS) if (opts.dump & DUMP_DFA_PROGRESS)
cerr << "\033[2KMinimize dfa: partitions " << partitions.size() cerr << "\033[2KMinimize dfa: partitions " << partitions.size()
<< "\tinit " << init_count << " (accept " << "\tinit " << init_count << " (accept "
<< accept_count << ")\r"; << accept_count << ")\r";
@@ -734,7 +734,7 @@ void DFA::minimize(dfaflags_t flags)
(*m)->partition = new_part; (*m)->partition = new_part;
} }
} }
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 100 == 0)) if ((opts.dump & DUMP_DFA_PROGRESS) && (partitions.size() % 100 == 0))
cerr << "\033[2KMinimize dfa: partitions " cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit " << partitions.size() << "\tinit "
<< init_count << " (accept " << init_count << " (accept "
@@ -743,7 +743,7 @@ void DFA::minimize(dfaflags_t flags)
} while (new_part_count); } while (new_part_count);
if (partitions.size() == states.size()) { if (partitions.size() == states.size()) {
if (flags & DFA_DUMP_STATS) if (opts.dump & DUMP_DFA_STATS)
cerr << "\033[2KDfa minimization no states removed: partitions " cerr << "\033[2KDfa minimization no states removed: partitions "
<< partitions.size() << "\tinit " << init_count << partitions.size() << "\tinit " << init_count
<< " (accept " << accept_count << ")\n"; << " (accept " << accept_count << ")\n";
@@ -757,13 +757,13 @@ void DFA::minimize(dfaflags_t flags)
* to states within the same partitions, however this can slow * to states within the same partitions, however this can slow
* down compressed dfa compression as there are more states, * down compressed dfa compression as there are more states,
*/ */
if (flags & DFA_DUMP_MIN_PARTS) if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << "Partitions after minimization\n"; cerr << "Partitions after minimization\n";
for (list<Partition *>::iterator p = partitions.begin(); for (list<Partition *>::iterator p = partitions.begin();
p != partitions.end(); p++) { p != partitions.end(); p++) {
/* representative state for this partition */ /* representative state for this partition */
State *rep = *((*p)->begin()); State *rep = *((*p)->begin());
if (flags & DFA_DUMP_MIN_PARTS) if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << *rep << " : "; cerr << *rep << " : ";
/* update representative state's transitions */ /* update representative state's transitions */
@@ -782,17 +782,17 @@ void DFA::minimize(dfaflags_t flags)
/* clear the state label for all non representative states, /* clear the state label for all non representative states,
* and accumulate permissions */ * and accumulate permissions */
for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) { for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) {
if (flags & DFA_DUMP_MIN_PARTS) if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << **i << ", "; cerr << **i << ", ";
(*i)->label = -1; (*i)->label = -1;
rep->perms.add((*i)->perms, filedfa); rep->perms.add((*i)->perms, filedfa);
} }
if (rep->perms.is_accept()) if (rep->perms.is_accept())
final_accept++; final_accept++;
if (flags & DFA_DUMP_MIN_PARTS) if (opts.dump & DUMP_DFA_MIN_PARTS)
cerr << "\n"; cerr << "\n";
} }
if (flags & DFA_DUMP_STATS) if (opts.dump & DUMP_DFA_STATS)
cerr << "\033[2KMinimized dfa: final partitions " cerr << "\033[2KMinimized dfa: final partitions "
<< partitions.size() << " (accept " << final_accept << partitions.size() << " (accept " << final_accept
<< ")" << "\tinit " << init_count << " (accept " << ")" << "\tinit " << init_count << " (accept "
@@ -875,7 +875,7 @@ static int diff_partition(State *state, Partition &part, int max_range, int uppe
/** /**
* diff_encode - compress dfa by differentially encoding state transitions * diff_encode - compress dfa by differentially encoding state transitions
* @dfa_flags: flags controlling dfa creation * @opts: flags controlling dfa creation
* *
* This function reduces the number of transitions that need to be stored * This function reduces the number of transitions that need to be stored
* by encoding transitions as the difference between the state and a * by encoding transitions as the difference between the state and a
@@ -910,7 +910,7 @@ static int diff_partition(State *state, Partition &part, int max_range, int uppe
* the state transition at most will only move 1 deeper into the DAG so for * the state transition at most will only move 1 deeper into the DAG so for
* the next state the maximum number of states traversed is 2*7. * the next state the maximum number of states traversed is 2*7.
*/ */
void DFA::diff_encode(dfaflags_t flags) void DFA::diff_encode(optflags const &opts)
{ {
DiffDag *dag; DiffDag *dag;
unsigned int xcount = 0, xweight = 0, transitions = 0, depth = 0; unsigned int xcount = 0, xweight = 0, transitions = 0, depth = 0;
@@ -965,7 +965,7 @@ void DFA::diff_encode(dfaflags_t flags)
} }
} }
if ((flags & DFA_DUMP_DIFF_PROGRESS) && (i % 100 == 0)) if ((opts.dump & DUMP_DFA_DIFF_PROGRESS) && (i % 100 == 0))
cerr << "\033[2KDiff Encode: " << i << " of " cerr << "\033[2KDiff Encode: " << i << " of "
<< tail << ". Diff states " << xcount << tail << ". Diff states " << xcount
<< " Savings " << xweight << "\r"; << " Savings " << xweight << "\r";
@@ -992,7 +992,7 @@ void DFA::diff_encode(dfaflags_t flags)
} }
} }
if (flags & DFA_DUMP_DIFF_STATS) if (opts.dump & DUMP_DFA_DIFF_STATS)
cerr << "Diff encode states: " << diffcount << " of " cerr << "Diff encode states: " << diffcount << " of "
<< tail << " reached @ depth " << depth << ". " << tail << " reached @ depth " << depth << ". "
<< aweight << " trans removed\n"; << aweight << " trans removed\n";
@@ -1194,7 +1194,7 @@ void DFA::dump_dot_graph(ostream & os)
* Compute character equivalence classes in the DFA to save space in the * Compute character equivalence classes in the DFA to save space in the
* transition table. * transition table.
*/ */
map<transchar, transchar> DFA::equivalence_classes(dfaflags_t flags) map<transchar, transchar> DFA::equivalence_classes(optflags const &opts)
{ {
map<transchar, transchar> classes; map<transchar, transchar> classes;
transchar next_class = 1; transchar next_class = 1;
@@ -1251,7 +1251,7 @@ map<transchar, transchar> DFA::equivalence_classes(dfaflags_t flags)
} }
} }
if (flags & DFA_DUMP_EQUIV_STATS) if (opts.dump & DUMP_DFA_EQUIV_STATS)
fprintf(stderr, "Equiv class reduces to %d classes\n", fprintf(stderr, "Equiv class reduces to %d classes\n",
next_class.c - 1); next_class.c - 1);
return classes; return classes;

View File

@@ -305,7 +305,7 @@ class DFA {
State *add_new_state(NodeSet *nodes, State *other); State *add_new_state(NodeSet *nodes, State *other);
State *add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other); State *add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other);
void update_state_transitions(State *state); void update_state_transitions(State *state);
void process_work_queue(const char *header, dfaflags_t); void process_work_queue(const char *header, optflags const &);
void dump_diff_chain(ostream &os, map<State *, Partition> &relmap, void dump_diff_chain(ostream &os, map<State *, Partition> &relmap,
Partition &chain, State *state, Partition &chain, State *state,
unsigned int &count, unsigned int &total, unsigned int &count, unsigned int &total,
@@ -318,19 +318,19 @@ class DFA {
list<State *> work_queue; list<State *> work_queue;
public: public:
DFA(Node *root, dfaflags_t flags, bool filedfa); DFA(Node *root, optflags const &flags, bool filedfa);
virtual ~DFA(); virtual ~DFA();
State *match_len(State *state, const char *str, size_t len); State *match_len(State *state, const char *str, size_t len);
State *match_until(State *state, const char *str, const char term); State *match_until(State *state, const char *str, const char term);
State *match(const char *str); State *match(const char *str);
void remove_unreachable(dfaflags_t flags); void remove_unreachable(optflags const &flags);
bool same_mappings(State *s1, State *s2); bool same_mappings(State *s1, State *s2);
void minimize(dfaflags_t flags); void minimize(optflags const &flags);
int apply_and_clear_deny(void); int apply_and_clear_deny(void);
void diff_encode(dfaflags_t flags); void diff_encode(optflags const &flags);
void undiff_encode(void); void undiff_encode(void);
void dump_diff_encode(ostream &os); void dump_diff_encode(ostream &os);
@@ -338,7 +338,7 @@ public:
void dump_dot_graph(ostream &os); void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s); void dump_uniq_perms(const char *s);
map<transchar, transchar> equivalence_classes(dfaflags_t flags); map<transchar, transchar> equivalence_classes(optflags const &flags);
void apply_equivalence_classes(map<transchar, transchar> &eq); void apply_equivalence_classes(map<transchar, transchar> &eq);
unsigned int diffcount; unsigned int diffcount;

View File

@@ -632,6 +632,51 @@ int mnt_rule::expand_variables(void)
return 0; return 0;
} }
static int cmp_vec_int(std::vector<unsigned int> const &lhs,
std::vector<unsigned int> const &rhs)
{
int res = lhs.size() - rhs.size();
if (res)
return res;
for (unsigned long i = 0; i < lhs.size(); i++) {
res = lhs[i] - rhs[i];
if (res)
return res;
}
return 0;
}
int mnt_rule::cmp(rule_t const &rhs) const {
// for now don't do merging of perms, only exact match
int res = perms_rule_t::cmp(rhs);
if (res != 0)
return res;
mnt_rule const &rhs_mnt = rule_cast<mnt_rule const &>(rhs);
res = null_strcmp(mnt_point, rhs_mnt.mnt_point);
if (res)
return res;
res = null_strcmp(device, rhs_mnt.device);
if (res)
return res;
res = null_strcmp(trans, rhs_mnt.trans);
if (res)
return res;
res = cmp_value_list(dev_type, rhs_mnt.dev_type);
if (res)
return res;
res = cmp_value_list(opts, rhs_mnt.opts);
if (res)
return res;
res = cmp_vec_int(flagsv, rhs_mnt.flagsv);
if (res)
return res;
return cmp_vec_int(opt_flagsv, rhs_mnt.opt_flagsv);
}
static int build_mnt_flags(char *buffer, int size, unsigned int flags, static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int opt_flags) unsigned int opt_flags)
{ {
@@ -753,7 +798,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
* else it has full perms * else it has full perms
*/ */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
vec, dfaflags, false)) vec, parseopts, false))
goto fail; goto fail;
count++; count++;
@@ -765,7 +810,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
vec[4] = optsbuf.c_str(); vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0), (audit == AUDIT_FORCE ? perms : 0),
5, vec, dfaflags, false)) 5, vec, parseopts, false))
goto fail; goto fail;
count++; count++;
} }
@@ -807,7 +852,7 @@ int mnt_rule::gen_policy_bind_mount(Profile &prof, int &count,
vec[3] = flagsbuf; vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec, 4, vec,
dfaflags, false)) parseopts, false))
goto fail; goto fail;
count++; count++;
@@ -864,7 +909,7 @@ int mnt_rule::gen_policy_change_mount_type(Profile &prof, int &count,
vec[3] = flagsbuf; vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec, 4, vec,
dfaflags, false)) parseopts, false))
goto fail; goto fail;
count++; count++;
@@ -907,7 +952,7 @@ int mnt_rule::gen_policy_move_mount(Profile &prof, int &count,
vec[3] = flagsbuf; vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
4, vec, 4, vec,
dfaflags, false)) parseopts, false))
goto fail; goto fail;
count++; count++;
@@ -958,7 +1003,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
} }
/* rule for match without required data || data MATCH_CONT */ /* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
vec, dfaflags, false)) vec, parseopts, false))
goto fail; goto fail;
count++; count++;
@@ -970,7 +1015,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
vec[4] = optsbuf.c_str(); vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0, audit == AUDIT_FORCE ? perms : 0,
5, vec, dfaflags, false)) 5, vec, parseopts, false))
goto fail; goto fail;
count++; count++;
} }
@@ -1062,7 +1107,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
vec[0] = mntbuf.c_str(); vec[0] = mntbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0), 1, vec, (audit == AUDIT_FORCE ? perms : 0), 1, vec,
dfaflags, false)) parseopts, false))
goto fail; goto fail;
count++; count++;
} }
@@ -1077,7 +1122,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
vec[1] = devbuf.c_str(); vec[1] = devbuf.c_str();
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
(audit == AUDIT_FORCE ? perms : 0), 2, vec, (audit == AUDIT_FORCE ? perms : 0), 2, vec,
dfaflags, false)) parseopts, false))
goto fail; goto fail;
count++; count++;
} }

View File

@@ -21,6 +21,7 @@
#include <ostream> #include <ostream>
#include <vector> #include <vector>
#include <algorithm>
#include "parser.h" #include "parser.h"
#include "rule.h" #include "rule.h"
@@ -173,6 +174,11 @@ public:
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual void post_parse_profile(Profile &prof unused); virtual void post_parse_profile(Profile &prof unused);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const;
// for now use default merge/dedup
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

View File

@@ -231,10 +231,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
/* store perms at name match so label doesn't need /* store perms at name match so label doesn't need
* to be checked * to be checked
*/ */
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, dfaflags, false)) if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, parseopts, false))
goto fail; goto fail;
/* also provide label match with perm */ /* also provide label match with perm */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, dfaflags, false)) if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, parseopts, false))
goto fail; goto fail;
} }
} }
@@ -266,10 +266,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
} }
if (perms & AA_VALID_SYSV_MQ_PERMS) { if (perms & AA_VALID_SYSV_MQ_PERMS) {
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, dfaflags, false)) if (!label && !prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, 1, vec, parseopts, false))
goto fail; goto fail;
/* also provide label match with perm */ /* also provide label match with perm */
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, dfaflags, false)) if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, size, vec, parseopts, false))
goto fail; goto fail;
} }
} }

View File

@@ -107,6 +107,22 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
mqueue_rule const &trhs = rule_cast<mqueue_rule const &>(rhs);
res = qtype - trhs.qtype;
if (res)
return res;
res = null_strcmp(qname, trhs.qname);
if (res)
return res;
return null_strcmp(label, trhs.label);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
void validate_qname(void); void validate_qname(void);

View File

@@ -82,9 +82,6 @@ extern int parser_token;
WARN_UNEXPECTED | WARN_FORMAT | WARN_MISSING | \ WARN_UNEXPECTED | WARN_FORMAT | WARN_MISSING | \
WARN_OVERRIDE | WARN_INCLUDE) WARN_OVERRIDE | WARN_INCLUDE)
extern dfaflags_t warnflags;
extern dfaflags_t werrflags;
typedef enum pattern_t pattern_t; typedef enum pattern_t pattern_t;
@@ -99,6 +96,8 @@ struct value_list {
struct value_list *next; struct value_list *next;
}; };
int cmp_value_list(value_list *lhs, value_list *rhs);
struct cond_entry { struct cond_entry {
char *name; char *name;
int eq; /* where equals was used in specifying list */ int eq; /* where equals was used in specifying list */
@@ -360,7 +359,6 @@ extern int conf_quiet;
extern int names_only; extern int names_only;
extern int option; extern int option;
extern int current_lineno; extern int current_lineno;
extern dfaflags_t dfaflags;
extern const char *progname; extern const char *progname;
extern char *profilename; extern char *profilename;
extern char *profile_ns; extern char *profile_ns;
@@ -372,7 +370,7 @@ extern IncludeCache_t *g_includecache;
extern void pwarnf(bool werr, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3))); extern void pwarnf(bool werr, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
extern void common_warn_once(const char *name, const char *msg, const char **warned_name); extern void common_warn_once(const char *name, const char *msg, const char **warned_name);
#define pwarn(F, args...) do { if (warnflags & (F)) pwarnf((werrflags & (F)), ## args); } while (0) #define pwarn(F, args...) do { if (parseopts.warn & (F)) pwarnf((parseopts.Werror & (F)), ## args); } while (0)
/* from parser_main (cannot be used in tst builds) */ /* from parser_main (cannot be used in tst builds) */
extern int force_complain; extern int force_complain;
@@ -454,6 +452,8 @@ extern struct cod_entry *new_entry(char *id, perms_t perms, char *link_id);
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */ /* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
extern int str_to_boolean(const char* str); extern int str_to_boolean(const char* str);
extern int null_strcmp(const char *s1, const char *s2);
extern bool strcomp (const char *lhs, const char *rhs);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod); extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list); extern void free_cod_entries(struct cod_entry *list);
void debug_cod_entries(struct cod_entry *list); void debug_cod_entries(struct cod_entry *list);

View File

@@ -89,9 +89,6 @@ int names_only = 0;
int current_lineno = 1; int current_lineno = 1;
int option = OPTION_ADD; int option = OPTION_ADD;
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_DIFF_ENCODE);
dfaflags_t warnflags = DEFAULT_WARNINGS;
dfaflags_t werrflags = 0;
const char *progname = __FILE__; const char *progname = __FILE__;
char *profile_ns = NULL; char *profile_ns = NULL;
@@ -102,6 +99,14 @@ FILE *ofile = NULL;
IncludeCache_t *g_includecache; IncludeCache_t *g_includecache;
optflags parseopts = {
.control = (optflags_t)(CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_DIFF_ENCODE | CONTROL_RULE_MERGE),
.dump = 0,
.warn = DEFAULT_WARNINGS,
.Werror = 0
};
#ifdef FORCE_READ_IMPLIES_EXEC #ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1; int read_implies_exec = 1;
#else #else
@@ -140,8 +145,8 @@ void pwarnf(bool werr, const char *fmt, ...)
/* do we want to warn once/profile or just once per compile?? */ /* do we want to warn once/profile or just once per compile?? */
void common_warn_once(const char *name, const char *msg, const char **warned_name) void common_warn_once(const char *name, const char *msg, const char **warned_name)
{ {
if ((warnflags & WARN_RULE_NOT_ENFORCED) && *warned_name != name) { if ((parseopts.warn & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
if (werrflags & WARN_RULE_NOT_ENFORCED) if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
cerr << "Warning converted to Error"; cerr << "Warning converted to Error";
else else
cerr << "Warning"; cerr << "Warning";
@@ -154,6 +159,6 @@ void common_warn_once(const char *name, const char *msg, const char **warned_nam
*warned_name = name; *warned_name = name;
} }
if (werrflags & WARN_RULE_NOT_ENFORCED) if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
exit(1); exit(1);
} }

View File

@@ -82,6 +82,9 @@ int abort_on_error = 0; /* stop processing profiles if error */
int skip_bad_cache_rebuild = 0; int skip_bad_cache_rebuild = 0;
int mru_skip_cache = 1; int mru_skip_cache = 1;
bool O_rule_merge = true;
bool D_rule_merge = false;
/* for jobs_max and jobs /* for jobs_max and jobs
* LONG_MAX : no limit * LONG_MAX : no limit
* LONG_MIN : auto = detect system processing cores * LONG_MIN : auto = detect system processing cores
@@ -274,6 +277,7 @@ optflag_table_t warnflag_table[] = {
{ 0, NULL, NULL, 0 }, { 0, NULL, NULL, 0 },
}; };
/* Parse comma separated cachelocations. Commas can be escaped by \, */ /* Parse comma separated cachelocations. Commas can be escaped by \, */
static int parse_cacheloc(const char *arg, const char **cacheloc, int max_size) static int parse_cacheloc(const char *arg, const char **cacheloc, int max_size)
{ {
@@ -497,7 +501,7 @@ static int process_arg(int c, char *optarg)
} else if (strcmp(optarg, "Optimize") == 0 || } else if (strcmp(optarg, "Optimize") == 0 ||
strcmp(optarg, "optimize") == 0 || strcmp(optarg, "optimize") == 0 ||
strcmp(optarg, "O") == 0) { strcmp(optarg, "O") == 0) {
flagtable_help("-O ", "", progname, optflag_table); flagtable_help("-O ", "", progname, dfaoptflag_table);
} else if (strcmp(optarg, "warn") == 0) { } else if (strcmp(optarg, "warn") == 0) {
flagtable_help("--warn=", "", progname, warnflag_table); flagtable_help("--warn=", "", progname, warnflag_table);
} else if (strcmp(optarg, "Werror") == 0) { } else if (strcmp(optarg, "Werror") == 0) {
@@ -568,13 +572,13 @@ static int process_arg(int c, char *optarg)
if (!optarg) { if (!optarg) {
dump_vars = 1; dump_vars = 1;
} else if (strcmp(optarg, "show") == 0) { } else if (strcmp(optarg, "show") == 0) {
print_flags("dump", dumpflag_table, dfaflags); print_flags("dump", dumpflag_table, parseopts.dump);
} else if (strcmp(optarg, "variables") == 0) { } else if (strcmp(optarg, "variables") == 0) {
dump_vars = 1; dump_vars = 1;
} else if (strcmp(optarg, "expanded-variables") == 0) { } else if (strcmp(optarg, "expanded-variables") == 0) {
dump_expanded_vars = 1; dump_expanded_vars = 1;
} else if (!handle_flag_table(dumpflag_table, optarg, } else if (!handle_flag_table(dumpflag_table, optarg,
&dfaflags)) { &parseopts.dump)) {
PERROR("%s: Invalid --Dump option %s\n", PERROR("%s: Invalid --Dump option %s\n",
progname, optarg); progname, optarg);
exit(1); exit(1);
@@ -582,9 +586,9 @@ static int process_arg(int c, char *optarg)
break; break;
case 'O': case 'O':
if (strcmp(optarg, "show") == 0) { if (strcmp(optarg, "show") == 0) {
print_flags("Optimize", optflag_table, dfaflags); print_flags("Optimize", dfaoptflag_table, parseopts.control);
} else if (!handle_flag_table(optflag_table, optarg, } else if (!handle_flag_table(dfaoptflag_table, optarg,
&dfaflags)) { &parseopts.control)) {
PERROR("%s: Invalid --Optimize option %s\n", PERROR("%s: Invalid --Optimize option %s\n",
progname, optarg); progname, optarg);
exit(1); exit(1);
@@ -665,7 +669,7 @@ static int process_arg(int c, char *optarg)
case 'q': case 'q':
conf_verbose = 0; conf_verbose = 0;
conf_quiet = 1; conf_quiet = 1;
warnflags = 0; parseopts.warn = 0;
break; break;
case 'v': case 'v':
conf_verbose = 1; conf_verbose = 1;
@@ -723,9 +727,9 @@ static int process_arg(int c, char *optarg)
break; break;
case ARG_WARN: case ARG_WARN:
if (strcmp(optarg, "show") == 0) { if (strcmp(optarg, "show") == 0) {
print_flags("warn", warnflag_table, warnflags); print_flags("warn", warnflag_table, parseopts.warn);
} else if (!handle_flag_table(warnflag_table, optarg, } else if (!handle_flag_table(warnflag_table, optarg,
&warnflags)) { &parseopts.warn)) {
PERROR("%s: Invalid --warn option %s\n", PERROR("%s: Invalid --warn option %s\n",
progname, optarg); progname, optarg);
exit(1); exit(1);
@@ -733,18 +737,18 @@ static int process_arg(int c, char *optarg)
break; break;
case ARG_WERROR: case ARG_WERROR:
if (!optarg) { if (!optarg) {
werrflags = -1; parseopts.Werror = -1;
} else if (strcmp(optarg, "show") == 0) { } else if (strcmp(optarg, "show") == 0) {
print_flags("Werror", warnflag_table, werrflags); print_flags("Werror", warnflag_table, parseopts.Werror);
} else if (optarg && !handle_flag_table(warnflag_table, optarg, } else if (optarg && !handle_flag_table(warnflag_table, optarg,
&werrflags)) { &parseopts.Werror)) {
PERROR("%s: Invalid --Werror option %s\n", PERROR("%s: Invalid --Werror option %s\n",
progname, optarg); progname, optarg);
exit(1); exit(1);
} }
break; break;
case ARG_DEBUG_CACHE: case ARG_DEBUG_CACHE:
warnflags |= WARN_DEBUG_CACHE; parseopts.warn |= WARN_DEBUG_CACHE;
break; break;
case 'j': case 'j':
jobs = process_jobs_arg("-j", optarg); jobs = process_jobs_arg("-j", optarg);
@@ -1530,7 +1534,7 @@ static bool get_kernel_features(struct aa_features **features)
if (!kernel_supports_diff_encode) if (!kernel_supports_diff_encode)
/* clear diff_encode because it is not supported */ /* clear diff_encode because it is not supported */
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE; parseopts.control &= ~CONTROL_DFA_DIFF_ENCODE;
return true; return true;
} }

View File

@@ -72,7 +72,7 @@ static int process_file_entries(Profile *prof)
table = (struct cod_entry **) malloc(sizeof(struct cod_entry *) * (count + 1)); table = (struct cod_entry **) malloc(sizeof(struct cod_entry *) * (count + 1));
if (!table) { if (!table) {
PERROR(_("Couldn't merge entries. Out of Memory\n")); PERROR(_("Couldn't merge entries. Out of Memory\n"));
return ENOMEM; return -ENOMEM;
} }
for (cur = prof->entries, n = 0; cur; cur = cur->next, n++) for (cur = prof->entries, n = 0; cur; cur = cur->next, n++)
@@ -84,6 +84,7 @@ static int process_file_entries(Profile *prof)
prof->entries = table[0]; prof->entries = table[0];
free(table); free(table);
count = 0;
/* walk the sorted table merging similar entries */ /* walk the sorted table merging similar entries */
for (cur = prof->entries, next = cur->next; next; next = cur->next) { for (cur = prof->entries, next = cur->next; next; next = cur->next) {
if (file_comp(&cur, &next) != 0) { if (file_comp(&cur, &next) != 0) {
@@ -102,12 +103,24 @@ static int process_file_entries(Profile *prof)
next->next = NULL; next->next = NULL;
free_cod_entries(next); free_cod_entries(next);
count++;
} }
return 0; return count;
} }
int profile_merge_rules(Profile *prof) int profile_merge_rules(Profile *prof)
{ {
return process_file_entries(prof); if (!(parseopts.control & CONTROL_RULE_MERGE))
return 0;
int res, tmp = process_file_entries(prof);
if (tmp < 0)
return -tmp;
res = prof->merge_rules();
if (res < 0)
return -res;
if (parseopts.dump & DUMP_RULE_MERGE)
fprintf(stderr, "RULE MERGE: deleted %d file rules, %d rules\n", tmp, res);
return 0;
} }

View File

@@ -34,6 +34,8 @@
#include <sys/apparmor.h> #include <sys/apparmor.h>
#include <sys/apparmor_private.h> #include <sys/apparmor_private.h>
#include <algorithm>
#include "capability.h" #include "capability.h"
#include "lib.h" #include "lib.h"
#include "parser.h" #include "parser.h"
@@ -271,6 +273,25 @@ static const char *strn_token(const char *str, size_t &len)
return start; return start;
} }
int null_strcmp(const char *s1, const char *s2)
{
if (s1) {
if (s2)
return strcmp(s1, s2);
return 1;
} else if (s2) {
return -1;
}
// both null
return 0;
}
bool strcomp (const char *lhs, const char *rhs)
{
return null_strcmp(lhs, rhs) < 0;
}
/* /*
* Returns: -1: error * Returns: -1: error
* 0: no change - capability already in table * 0: no change - capability already in table
@@ -1065,6 +1086,50 @@ void debug_cod_entries(struct cod_entry *list)
} }
} }
// these need to move to stl
int ordered_cmp_value_list(value_list *lhs, value_list *rhs)
{
std::vector<const char *> lhstable;
std::vector<const char *> rhstable;
struct value_list *entry;
list_for_each(lhs, entry) {
lhstable.push_back(entry->value);
}
list_for_each(rhs, entry) {
rhstable.push_back(entry->value);
}
int res = lhstable.size() - rhstable.size();
if (res)
return res;
std::sort(lhstable.begin(), lhstable.end(), strcomp);
std::sort(rhstable.begin(), rhstable.end(), strcomp);
for (unsigned long i = 0; i < lhstable.size(); i++) {
res = null_strcmp(lhstable[i], rhstable[i]);
if (res)
return res;
}
return 0;
}
int cmp_value_list(value_list *lhs, value_list *rhs)
{
if (lhs) {
if (rhs) {
return ordered_cmp_value_list(lhs, rhs);
}
return 1;
} else if (rhs) {
return -1;
}
return 0;
}
struct value_list *new_value_list(char *value) struct value_list *new_value_list(char *value)
{ {
struct value_list *val = (struct value_list *) calloc(1, sizeof(struct value_list)); struct value_list *val = (struct value_list *) calloc(1, sizeof(struct value_list));

View File

@@ -28,7 +28,7 @@
/* #define DEBUG */ /* #define DEBUG */
#include "common_optarg.h"
#include "lib.h" #include "lib.h"
#include "parser.h" #include "parser.h"
#include "profile.h" #include "profile.h"
@@ -128,7 +128,7 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
sptr = aare; sptr = aare;
if (dfaflags & DFA_DUMP_RULE_EXPR) if (parseopts.dump & DUMP_DFA_RULE_EXPR)
fprintf(stderr, "aare: %s -> ", aare); fprintf(stderr, "aare: %s -> ", aare);
if (anchor) if (anchor)
@@ -427,7 +427,7 @@ out:
if (ret == FALSE) if (ret == FALSE)
ptype = ePatternInvalid; ptype = ePatternInvalid;
if (dfaflags & DFA_DUMP_RULE_EXPR) if (parseopts.dump & DUMP_DFA_RULE_EXPR)
fprintf(stderr, "%s\n", pcre.c_str()); fprintf(stderr, "%s\n", pcre.c_str());
return ptype; return ptype;
@@ -507,7 +507,7 @@ static int process_profile_name_xmatch(Profile *prof)
aare_rules *rules = new aare_rules(); aare_rules *rules = new aare_rules();
if (!rules) if (!rules)
return FALSE; return FALSE;
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) { if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
delete rules; delete rules;
return FALSE; return FALSE;
} }
@@ -520,7 +520,7 @@ static int process_profile_name_xmatch(Profile *prof)
ptype = convert_aaregex_to_pcre(alt->name, 0, ptype = convert_aaregex_to_pcre(alt->name, 0,
glob_default, glob_default,
tbuf, &len); tbuf, &len);
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) { if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
delete rules; delete rules;
return FALSE; return FALSE;
} }
@@ -562,14 +562,14 @@ static int process_profile_name_xmatch(Profile *prof)
convert_aaregex_to_pcre(xattr_value, 0, convert_aaregex_to_pcre(xattr_value, 0,
glob_null, tbuf, glob_null, tbuf,
&len); &len);
if (!rules->append_rule(tbuf.c_str(), true, true, dfaflags)) { if (!rules->append_rule(tbuf.c_str(), true, true, parseopts)) {
delete rules; delete rules;
return FALSE; return FALSE;
} }
} }
} }
build: build:
prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, dfaflags, true); prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, parseopts, true);
delete rules; delete rules;
if (!prof->xmatch) if (!prof->xmatch)
return FALSE; return FALSE;
@@ -638,13 +638,13 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
!dfarules->add_rule(tbuf.c_str(), entry->rule_mode == RULE_DENY, !dfarules->add_rule(tbuf.c_str(), entry->rule_mode == RULE_DENY,
entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE), entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0, entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0,
dfaflags)) parseopts))
return FALSE; return FALSE;
} else if (!is_change_profile_perms(entry->perms)) { } else if (!is_change_profile_perms(entry->perms)) {
if (!dfarules->add_rule(tbuf.c_str(), if (!dfarules->add_rule(tbuf.c_str(),
entry->rule_mode == RULE_DENY, entry->perms, entry->rule_mode == RULE_DENY, entry->perms,
entry->audit == AUDIT_FORCE ? entry->perms : 0, entry->audit == AUDIT_FORCE ? entry->perms : 0,
dfaflags)) parseopts))
return FALSE; return FALSE;
} }
@@ -667,7 +667,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
perms |= LINK_TO_LINK_SUBSET(perms); perms |= LINK_TO_LINK_SUBSET(perms);
vec[1] = "/[^/].*"; vec[1] = "/[^/].*";
} }
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, perms, entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, 2, vec, dfaflags, false)) if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, perms, entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0, 2, vec, parseopts, false))
return FALSE; return FALSE;
} }
if (is_change_profile_perms(entry->perms)) { if (is_change_profile_perms(entry->perms)) {
@@ -678,7 +678,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
int index = 1; int index = 1;
uint32_t onexec_perms = AA_ONEXEC; uint32_t onexec_perms = AA_ONEXEC;
if ((warnflags & WARN_RULE_DOWNGRADED) && entry->audit == AUDIT_FORCE && warn_change_profile) { if ((parseopts.warn & WARN_RULE_DOWNGRADED) && entry->audit == AUDIT_FORCE && warn_change_profile) {
/* don't have profile name here, so until this code /* don't have profile name here, so until this code
* gets refactored just throw out a generic warning * gets refactored just throw out a generic warning
*/ */
@@ -720,12 +720,12 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
/* regular change_profile rule */ /* regular change_profile rule */
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY,
AA_CHANGE_PROFILE | onexec_perms, AA_CHANGE_PROFILE | onexec_perms,
0, index - 1, &vec[1], dfaflags, false)) 0, index - 1, &vec[1], parseopts, false))
return FALSE; return FALSE;
/* onexec rules - both rules are needed for onexec */ /* onexec rules - both rules are needed for onexec */
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms, if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms,
0, 1, vec, dfaflags, false)) 0, 1, vec, parseopts, false))
return FALSE; return FALSE;
/** /**
@@ -734,7 +734,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
*/ */
onexec_perms |= (entry->perms & (AA_EXEC_BITS | ALL_AA_EXEC_UNSAFE)); onexec_perms |= (entry->perms & (AA_EXEC_BITS | ALL_AA_EXEC_UNSAFE));
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms, if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms,
0, index, vec, dfaflags, false)) 0, index, vec, parseopts, false))
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
@@ -770,7 +770,7 @@ int process_profile_regex(Profile *prof)
if (prof->dfa.rules->rule_count > 0) { if (prof->dfa.rules->rule_count > 0) {
int xmatch_len = 0; int xmatch_len = 0;
prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size, prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size,
&xmatch_len, dfaflags, true); &xmatch_len, parseopts, true);
delete prof->dfa.rules; delete prof->dfa.rules;
prof->dfa.rules = NULL; prof->dfa.rules = NULL;
if (!prof->dfa.dfa) if (!prof->dfa.dfa)
@@ -848,7 +848,7 @@ int clear_and_convert_entry(std::string& buffer, char *entry)
int post_process_policydb_ents(Profile *prof) int post_process_policydb_ents(Profile *prof)
{ {
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) { for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED) if ((*i)->skip())
continue; continue;
if ((*i)->gen_policy_re(*prof) == RULE_ERROR) if ((*i)->gen_policy_re(*prof) == RULE_ERROR)
return FALSE; return FALSE;
@@ -875,7 +875,7 @@ static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask,
buf = buffer.str(); buf = buffer.str();
if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS), if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS),
audit ? map_perms(AA_VALID_NET_PERMS) : 0, audit ? map_perms(AA_VALID_NET_PERMS) : 0,
dfaflags)) parseopts))
return false; return false;
return true; return true;
@@ -969,44 +969,44 @@ int process_profile_policydb(Profile *prof)
/* note: this activates fs based unix domain sockets mediation on connect */ /* note: this activates fs based unix domain sockets mediation on connect */
if (kernel_abi_version > 5 && if (kernel_abi_version > 5 &&
!prof->policy.rules->add_rule(mediates_file, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_file, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_mount && if (features_supports_mount &&
!prof->policy.rules->add_rule(mediates_mount, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_mount, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_dbus && if (features_supports_dbus &&
!prof->policy.rules->add_rule(mediates_dbus, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_dbus, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_signal && if (features_supports_signal &&
!prof->policy.rules->add_rule(mediates_signal, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_signal, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_ptrace && if (features_supports_ptrace &&
!prof->policy.rules->add_rule(mediates_ptrace, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_ptrace, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_networkv8 && if (features_supports_networkv8 &&
!prof->policy.rules->add_rule(mediates_netv8, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_netv8, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_unix && if (features_supports_unix &&
(!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, dfaflags) || (!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, parseopts) ||
!prof->policy.rules->add_rule(mediates_net_unix, 0, AA_MAY_READ, 0, dfaflags))) !prof->policy.rules->add_rule(mediates_net_unix, 0, AA_MAY_READ, 0, parseopts)))
goto out; goto out;
if (features_supports_userns && if (features_supports_userns &&
!prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_posix_mqueue && if (features_supports_posix_mqueue &&
!prof->policy.rules->add_rule(mediates_posix_mqueue, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_posix_mqueue, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_sysv_mqueue && if (features_supports_sysv_mqueue &&
!prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (features_supports_io_uring && if (features_supports_io_uring &&
!prof->policy.rules->add_rule(mediates_io_uring, 0, AA_MAY_READ, 0, dfaflags)) !prof->policy.rules->add_rule(mediates_io_uring, 0, AA_MAY_READ, 0, parseopts))
goto out; goto out;
if (prof->policy.rules->rule_count > 0) { if (prof->policy.rules->rule_count > 0) {
int xmatch_len = 0; int xmatch_len = 0;
prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size, prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size,
&xmatch_len, dfaflags, false); &xmatch_len, parseopts, false);
delete prof->policy.rules; delete prof->policy.rules;
prof->policy.rules = NULL; prof->policy.rules = NULL;

View File

@@ -267,7 +267,7 @@ static int process_variables_in_entries(struct cod_entry *entry_list)
static int process_variables_in_rules(Profile &prof) static int process_variables_in_rules(Profile &prof)
{ {
for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) { for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED) if ((*i)->skip())
continue; continue;
int error = (*i)->expand_variables(); int error = (*i)->expand_variables();
if (error) if (error)

View File

@@ -121,38 +121,39 @@ Profile::~Profile()
free(net.quiet); free(net.quiet);
} }
static bool comp (rule_t *lhs, rule_t *rhs) { return (*lhs < *rhs); } static bool comp (rule_t *lhs, rule_t *rhs)
{
return (*lhs < *rhs);
}
bool Profile::merge_rules(void) // TODO: move to block rule
// returns number of rules merged
// returns negative number on error
int Profile::merge_rules(void)
{ {
int count = 0; int count = 0;
std::vector<rule_t *> table;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); ) { for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
if ((*i)->is_mergeable()) if ((*i)->is_mergeable() && !(*i)->skip())
count++; table.push_back(*i);
} }
if (count < 2) if (table.size() < 2)
return 0; return 0;
std::vector<rule_t *> table(count);
int n = 0;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); ) {
if ((*i)->is_mergeable())
table[n++] = *i;
}
std::sort(table.begin(), table.end(), comp); std::sort(table.begin(), table.end(), comp);
unsigned long n = table.size();
for (int i = 0, j = 1; j < count; j++) { for (unsigned long i = 0, j = 1; j < n; j++) {
if (table[j]->skip())
continue;
if (table[i]->cmp(*table[j]) == 0) { if (table[i]->cmp(*table[j]) == 0) {
if (!table[i]->merge(*table[j])) if (table[i]->merge(*table[j]))
return false; count++;
continue; continue;
} }
i = j; i = j;
} }
return true; return count;
} }
@@ -318,7 +319,7 @@ void post_process_file_entries(Profile *prof)
void post_process_rule_entries(Profile *prof) void post_process_rule_entries(Profile *prof)
{ {
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) { for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
if ((*i)->flags & RULE_FLAG_DELETED) if ((*i)->skip())
continue; continue;
(*i)->post_parse_profile(*prof); (*i)->post_parse_profile(*prof);
} }

View File

@@ -249,7 +249,7 @@ public:
* Requires the merged rules have customized methods * Requires the merged rules have customized methods
* cmp(), is_mergeable() and merge() * cmp(), is_mergeable() and merge()
*/ */
virtual bool merge_rules(void); virtual int merge_rules(void);
void dump(void) void dump(void)
{ {

View File

@@ -134,7 +134,7 @@ int ptrace_rule::gen_policy_re(Profile &prof)
buf = buffer.str(); buf = buffer.str();
if (perms & AA_VALID_PTRACE_PERMS) { if (perms & AA_VALID_PTRACE_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags)) parseopts))
goto fail; goto fail;
} }

View File

@@ -52,6 +52,16 @@ public:
return true; return true;
}; };
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
return null_strcmp(peer_label,
(rule_cast<ptrace_rule const &>(rhs)).peer_label);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

View File

@@ -38,6 +38,10 @@ class Profile;
// RULE_TYPE_CLASS needs to be last because various class follow it // RULE_TYPE_CLASS needs to be last because various class follow it
#define RULE_TYPE_CLASS 3 #define RULE_TYPE_CLASS 3
// rule_cast should only be used after a comparison of rule_type to ensure
// that it is valid. Change to dynamic_cast for debugging
//#define rule_cast dynamic_cast
#define rule_cast static_cast
typedef enum { RULE_FLAG_NONE = 0, typedef enum { RULE_FLAG_NONE = 0,
RULE_FLAG_DELETED = 1, // rule deleted - skip RULE_FLAG_DELETED = 1, // rule deleted - skip
@@ -48,16 +52,39 @@ typedef enum { RULE_FLAG_NONE = 0,
// added because it is implied // added because it is implied
} rule_flags_t; } rule_flags_t;
inline rule_flags_t operator|(rule_flags_t a, rule_flags_t b)
{
return static_cast<rule_flags_t>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
}
inline rule_flags_t operator&(rule_flags_t a, rule_flags_t b)
{
return static_cast<rule_flags_t>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
}
inline rule_flags_t& operator|=(rule_flags_t &a, const rule_flags_t &b)
{
a = a | b;
return a;
}
class rule_t { class rule_t {
public: public:
int rule_type; int rule_type;
rule_flags_t flags; rule_flags_t flags;
rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE) { } rule_t *removed_by;
rule_t(int t): rule_type(t), flags(RULE_FLAG_NONE), removed_by(NULL) { }
virtual ~rule_t() { }; virtual ~rule_t() { };
bool is_type(int type) { return rule_type == type; } bool is_type(int type) { return rule_type == type; }
// rule has been marked as should be skipped by regular processing
bool skip()
{
return (flags & RULE_FLAG_DELETED);
}
//virtual bool operator<(rule_t const &rhs)const = 0; //virtual bool operator<(rule_t const &rhs)const = 0;
virtual std::ostream &dump(std::ostream &os) = 0; virtual std::ostream &dump(std::ostream &os) = 0;
@@ -75,15 +102,37 @@ public:
// to support expansion in include names and profile names // to support expansion in include names and profile names
virtual int expand_variables(void) = 0; virtual int expand_variables(void) = 0;
// called by duplicate rule merge/elimination after final expand_vars
virtual bool is_mergeable(void) { return false; }
virtual int cmp(rule_t const &rhs) const { virtual int cmp(rule_t const &rhs) const {
return rule_type < rhs.rule_type; return rule_type - rhs.rule_type;
} }
virtual bool operator<(rule_t const &rhs) const { virtual bool operator<(rule_t const &rhs) const {
return cmp(rhs) < 0; return cmp(rhs) < 0;
} }
virtual bool merge(rule_t &rhs __attribute__ ((unused))) { return false; }; // called by duplicate rule merge/elimination after final expand_vars
// to get default rule dedup
// child object need to provide
// - cmp, operator<
// - is_mergeable() returning true
// if a child object wants to provide merging of permissions,
// it needs to provide a custom cmp fn that doesn't include
// permissions and a merge routine that does more than flagging
// as dup as below
virtual bool is_mergeable(void) { return false; }
// returns true if merged
virtual bool merge(rule_t &rhs)
{
if (rule_type != rhs.rule_type)
return false;
if (skip() || rhs.skip())
return false;
// default merge is just dedup
flags |= RULE_FLAG_MERGED;
rhs.flags |= (RULE_FLAG_MERGED | RULE_FLAG_DELETED);
rhs.removed_by = this;
return true;
};
// called late frontend to generate data for regex backend // called late frontend to generate data for regex backend
virtual int gen_policy_re(Profile &prof) = 0; virtual int gen_policy_re(Profile &prof) = 0;
@@ -156,6 +205,32 @@ public:
return os; return os;
} }
int cmp(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit)
return -1;
if ((uint) audit > (uint) rhs.audit)
return 1;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return -1;
if ((uint) rule_mode > (uint) rhs.rule_mode)
return 1;
if (owner < rhs.owner)
return -1;
if (owner > rhs.owner)
return 1;
return 0;
}
bool operator<(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit)
return true;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return true;
if (owner < rhs.owner)
return true;
return false;
}
}; };
class prefix_rule_t: public rule_t, public prefixes { class prefix_rule_t: public rule_t, public prefixes {
@@ -215,21 +290,32 @@ public:
return true; return true;
} }
virtual bool operator<(prefixes const &rhs) const { int cmp(prefixes const &rhs) const {
if ((uint) audit < (uint) rhs.audit) return prefixes::cmp(rhs);
return true;
if ((uint) rule_mode < (uint) rhs.rule_mode)
return true;
if (owner < rhs.owner)
return true;
return false;
} }
virtual bool operator<(prefix_rule_t const &rhs) const {
virtual bool operator<(prefixes const &rhs) const {
const prefixes *ptr = this;
return *ptr < rhs;
}
virtual int cmp(rule_t const &rhs) const {
int res = rule_t::cmp(rhs);
if (res)
return res;
prefix_rule_t const &pr = rule_cast<prefix_rule_t const &>(rhs);
const prefixes *lhsptr = this, *rhsptr = &pr;
return lhsptr->cmp(*rhsptr);
}
virtual bool operator<(rule_t const &rhs) const {
if (rule_type < rhs.rule_type) if (rule_type < rhs.rule_type)
return true; return true;
if (rhs.rule_type < rule_type) if (rhs.rule_type < rule_type)
return false; return false;
return *this < (prefixes const &)rhs; prefix_rule_t const &pr = rule_cast<prefix_rule_t const &>(rhs);
const prefixes *rhsptr = &pr;
return *this < *rhsptr;
} }
virtual ostream &dump(ostream &os) { virtual ostream &dump(ostream &os) {
@@ -247,6 +333,16 @@ public:
int aa_class(void) { return rule_type - RULE_TYPE_CLASS; } int aa_class(void) { return rule_type - RULE_TYPE_CLASS; }
/* inherit cmp */
/* we do not inherit operator< from so class_rules children
* can in herit the generic one that redirects to cmp()
* that does get overriden
*/
virtual bool operator<(rule_t const &rhs) const {
return cmp(rhs) < 0;
}
virtual ostream &dump(ostream &os) { virtual ostream &dump(ostream &os) {
prefix_rule_t::dump(os); prefix_rule_t::dump(os);
@@ -261,6 +357,13 @@ class perms_rule_t: public class_rule_t {
public: public:
perms_rule_t(int c): class_rule_t(c), perms(0) { }; perms_rule_t(int c): class_rule_t(c), perms(0) { };
virtual int cmp(rule_t const &rhs) const {
int res = class_rule_t::cmp(rhs);
if (res)
return res;
return perms - (rule_cast<perms_rule_t const &>(rhs)).perms;
}
/* defaut perms, override/mask off if none default used */ /* defaut perms, override/mask off if none default used */
virtual ostream &dump(ostream &os) { virtual ostream &dump(ostream &os) {

View File

@@ -230,6 +230,35 @@ int signal_rule::expand_variables(void)
return expand_entry_variables(&peer_label); return expand_entry_variables(&peer_label);
} }
static int cmp_set_int(Signals const &lhs, Signals const &rhs)
{
int res = lhs.size() - rhs.size();
if (res)
return res;
for (Signals::iterator i = lhs.begin(),
j = rhs.begin();
i != lhs.end(); i++, j++) {
res = *i - *j;
if (res)
return res;
}
return 0;
}
int signal_rule::cmp(rule_t const &rhs) const
{
int res = perms_rule_t::cmp(rhs);
if (res)
return res;
signal_rule const &trhs = rule_cast<signal_rule const &>(rhs);
res = null_strcmp(peer_label, trhs.peer_label);
if (res)
return res;
return cmp_set_int(signals, trhs.signals);
}
void signal_rule::warn_once(const char *name) void signal_rule::warn_once(const char *name)
{ {
rule_t::warn_once(name, "signal rules not enforced"); rule_t::warn_once(name, "signal rules not enforced");
@@ -288,7 +317,7 @@ int signal_rule::gen_policy_re(Profile &prof)
buf = buffer.str(); buf = buffer.str();
if (perms & (AA_MAY_SEND | AA_MAY_RECEIVE)) { if (perms & (AA_MAY_SEND | AA_MAY_RECEIVE)) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
dfaflags)) parseopts))
goto fail; goto fail;
} }

View File

@@ -57,6 +57,9 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const;
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };

View File

@@ -97,7 +97,7 @@ int userns_rule::gen_policy_re(Profile &prof)
if (perms & AA_VALID_USERNS_PERMS) { if (perms & AA_VALID_USERNS_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0, audit == AUDIT_FORCE ? perms : 0,
dfaflags)) parseopts))
goto fail; goto fail;
} }

View File

@@ -42,6 +42,12 @@ public:
virtual int expand_variables(void); virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof); virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
return perms_rule_t::cmp(rhs);
};
protected: protected:
virtual void warn_once(const char *name) override; virtual void warn_once(const char *name) override;
}; };