mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01: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:
commit
1279f85e4a
40 changed files with 730 additions and 295 deletions
|
@ -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 \
|
||||
file_cache.h immunix.h lib.h mount.h network.h parser.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
|
||||
GENERATED_HDRS = af_names.h generated_af_names.h \
|
||||
|
|
|
@ -76,6 +76,35 @@ public:
|
|||
virtual ostream &dump(ostream &os);
|
||||
virtual int expand_variables(void);
|
||||
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 */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "common_optarg.h"
|
||||
#include "network.h"
|
||||
#include "parser.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
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
@ -321,7 +322,7 @@ int unix_rule::gen_policy_re(Profile &prof)
|
|||
if (features_supports_network || features_supports_networkv8) {
|
||||
/* only warn if we are building against a kernel
|
||||
* 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");
|
||||
/* TODO: add ability to abort instead of downgrade */
|
||||
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,
|
||||
map_perms(AA_NET_CREATE),
|
||||
map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0),
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
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,
|
||||
map_perms(AA_NET_BIND),
|
||||
map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0),
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
/* clear if auto, else generic need to generate addr below */
|
||||
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,
|
||||
map_perms(mask & local_mask),
|
||||
map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0),
|
||||
dfaflags))
|
||||
parseopts))
|
||||
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,
|
||||
map_perms(AA_NET_LISTEN),
|
||||
map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0),
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
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,
|
||||
map_perms(AA_NET_OPT),
|
||||
map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0),
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
mask &= ~AA_LOCAL_NET_PERMS | AA_NET_ACCEPT;
|
||||
|
@ -432,7 +433,7 @@ int unix_rule::gen_policy_re(Profile &prof)
|
|||
goto fail;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,19 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
33
parser/common_flags.h
Normal file
33
parser/common_flags.h
Normal 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 */
|
|
@ -29,80 +29,83 @@
|
|||
|
||||
optflag_table_t dumpflag_table[] = {
|
||||
{ 1, "rule-exprs", "Dump rule to expr tree conversions",
|
||||
DFA_DUMP_RULE_EXPR },
|
||||
{ 1, "expr-stats", "Dump stats on expr tree", DFA_DUMP_TREE_STATS },
|
||||
{ 1, "expr-tree", "Dump expression tree", DFA_DUMP_TREE },
|
||||
DUMP_DFA_RULE_EXPR },
|
||||
{ 1, "expr-stats", "Dump stats on expr tree", DUMP_DFA_TREE_STATS },
|
||||
{ 1, "expr-tree", "Dump expression tree", DUMP_DFA_TREE },
|
||||
{ 1, "expr-simplified", "Dump simplified expression tree",
|
||||
DFA_DUMP_SIMPLE_TREE },
|
||||
DUMP_DFA_SIMPLE_TREE },
|
||||
{ 1, "stats", "Dump all compile stats",
|
||||
DFA_DUMP_TREE_STATS | DFA_DUMP_STATS | DFA_DUMP_TRANS_STATS |
|
||||
DFA_DUMP_EQUIV_STATS | DFA_DUMP_DIFF_STATS },
|
||||
DUMP_DFA_TREE_STATS | DUMP_DFA_STATS | DUMP_DFA_TRANS_STATS |
|
||||
DUMP_DFA_EQUIV_STATS | DUMP_DFA_DIFF_STATS },
|
||||
{ 1, "progress", "Dump progress for all compile phases",
|
||||
DFA_DUMP_PROGRESS | DFA_DUMP_STATS | DFA_DUMP_TRANS_PROGRESS |
|
||||
DFA_DUMP_TRANS_STATS | DFA_DUMP_DIFF_PROGRESS | DFA_DUMP_DIFF_STATS },
|
||||
DUMP_DFA_PROGRESS | DUMP_DFA_STATS | DUMP_DFA_TRANS_PROGRESS |
|
||||
DUMP_DFA_TRANS_STATS | DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
|
||||
{ 1, "dfa-progress", "Dump dfa creation as in progress",
|
||||
DFA_DUMP_PROGRESS | DFA_DUMP_STATS },
|
||||
{ 1, "dfa-stats", "Dump dfa creation stats", DFA_DUMP_STATS },
|
||||
{ 1, "dfa-states", "Dump dfa state diagram", DFA_DUMP_STATES },
|
||||
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DFA_DUMP_GRAPH },
|
||||
{ 1, "dfa-minimize", "Dump dfa minimization", DFA_DUMP_MINIMIZE },
|
||||
DUMP_DFA_PROGRESS | DUMP_DFA_STATS },
|
||||
{ 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS },
|
||||
{ 1, "dfa-states", "Dump dfa state diagram", DUMP_DFA_STATES },
|
||||
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DUMP_DFA_GRAPH },
|
||||
{ 1, "dfa-minimize", "Dump dfa minimization", DUMP_DFA_MINIMIZE },
|
||||
{ 1, "dfa-unreachable", "Dump dfa unreachable states",
|
||||
DFA_DUMP_UNREACHABLE },
|
||||
DUMP_DFA_UNREACHABLE },
|
||||
{ 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",
|
||||
DFA_DUMP_UNIQ_PERMS },
|
||||
DUMP_DFA_UNIQ_PERMS },
|
||||
{ 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",
|
||||
DFA_DUMP_MIN_PARTS },
|
||||
DUMP_DFA_MIN_PARTS },
|
||||
{ 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",
|
||||
DFA_DUMP_TRANS_STATS },
|
||||
{ 1, "compressed-dfa", "Dump compressed dfa", DFA_DUMP_TRANS_TABLE },
|
||||
DUMP_DFA_TRANS_STATS },
|
||||
{ 1, "compressed-dfa", "Dump compressed dfa", DUMP_DFA_TRANS_TABLE },
|
||||
{ 1, "equiv-stats", "Dump equivalence class stats",
|
||||
DFA_DUMP_EQUIV_STATS },
|
||||
{ 1, "equiv", "Dump equivalence class", DFA_DUMP_EQUIV },
|
||||
DUMP_DFA_EQUIV_STATS },
|
||||
{ 1, "equiv", "Dump equivalence class", DUMP_DFA_EQUIV },
|
||||
{ 1, "diff-encode", "Dump differential encoding",
|
||||
DFA_DUMP_DIFF_ENCODE },
|
||||
DUMP_DFA_DIFF_ENCODE },
|
||||
{ 1, "diff-stats", "Dump differential encoding stats",
|
||||
DFA_DUMP_DIFF_STATS },
|
||||
DUMP_DFA_DIFF_STATS },
|
||||
{ 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 },
|
||||
};
|
||||
|
||||
optflag_table_t optflag_table[] = {
|
||||
optflag_table_t dfaoptflag_table[] = {
|
||||
{ 2, "0", "no optimizations",
|
||||
DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE |
|
||||
DFA_CONTROL_MINIMIZE | DFA_CONTROL_REMOVE_UNREACHABLE |
|
||||
DFA_CONTROL_DIFF_ENCODE
|
||||
CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE |
|
||||
CONTROL_DFA_MINIMIZE | CONTROL_DFA_REMOVE_UNREACHABLE |
|
||||
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",
|
||||
DFA_CONTROL_TREE_NORMAL },
|
||||
CONTROL_DFA_TREE_NORMAL },
|
||||
{ 1, "expr-simplify", "expression tree simplification",
|
||||
DFA_CONTROL_TREE_SIMPLE },
|
||||
CONTROL_DFA_TREE_SIMPLE },
|
||||
{ 0, "expr-left-simplify", "left simplification first",
|
||||
DFA_CONTROL_TREE_LEFT },
|
||||
CONTROL_DFA_TREE_LEFT },
|
||||
{ 2, "expr-right-simplify", "right simplification first",
|
||||
DFA_CONTROL_TREE_LEFT },
|
||||
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
|
||||
CONTROL_DFA_TREE_LEFT },
|
||||
{ 1, "minimize", "dfa state minimization", CONTROL_DFA_MINIMIZE },
|
||||
{ 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",
|
||||
DFA_CONTROL_REMOVE_UNREACHABLE },
|
||||
CONTROL_DFA_REMOVE_UNREACHABLE },
|
||||
{ 0, "compress-small",
|
||||
"do slower dfa transition table compression",
|
||||
DFA_CONTROL_TRANS_HIGH },
|
||||
CONTROL_DFA_TRANS_HIGH },
|
||||
{ 2, "compress-fast", "do faster dfa transition table compression",
|
||||
DFA_CONTROL_TRANS_HIGH },
|
||||
CONTROL_DFA_TRANS_HIGH },
|
||||
{ 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 },
|
||||
};
|
||||
|
||||
|
||||
void print_flag_table(optflag_table_t *table)
|
||||
{
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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,
|
||||
dfaflags_t *flags)
|
||||
optflags_t *flags)
|
||||
{
|
||||
const char *arg = optarg;
|
||||
int i, invert = 0;
|
||||
|
|
|
@ -21,25 +21,31 @@
|
|||
#ifndef __AA_COMMON_OPTARG_H
|
||||
#define __AA_COMMON_OPTARG_H
|
||||
|
||||
#include "common_flags.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
|
||||
|
||||
/*
|
||||
* flag: 1 - allow no- inversion
|
||||
* flag: 2 - flags specified should be masked off
|
||||
*/
|
||||
#define OPT_FLAG_CONTROL_PREFIX_NO 1
|
||||
#define OPT_FLAG_CONTROL_MASK 2
|
||||
typedef struct {
|
||||
int control;
|
||||
const char *option;
|
||||
const char *desc;
|
||||
dfaflags_t flags;
|
||||
optflags_t flags;
|
||||
} optflag_table_t;
|
||||
|
||||
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,
|
||||
dfaflags_t *flags);
|
||||
optflags_t *flags);
|
||||
void flagtable_help(const char *name, const char *header, const char *command,
|
||||
optflag_table_t *table);
|
||||
|
||||
|
|
|
@ -276,21 +276,21 @@ int dbus_rule::gen_policy_re(Profile &prof)
|
|||
if (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,
|
||||
2, vec, dfaflags, false))
|
||||
2, vec, parseopts, false))
|
||||
goto fail;
|
||||
}
|
||||
if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) {
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
|
||||
perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
|
||||
audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0,
|
||||
6, vec, dfaflags, false))
|
||||
6, vec, parseopts, false))
|
||||
goto fail;
|
||||
}
|
||||
if (perms & AA_DBUS_EAVESDROP) {
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
|
||||
perms & AA_DBUS_EAVESDROP,
|
||||
audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0,
|
||||
1, vec, dfaflags, false))
|
||||
1, vec, parseopts, false))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,32 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* along with this program; if not, contact or Canonical Ltd.
|
||||
*/
|
||||
|
||||
#include "common_optarg.h"
|
||||
#include "parser.h"
|
||||
#include "profile.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 (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
|
||||
audit == AUDIT_FORCE ? perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
|
||||
if (perms & AA_IO_URING_OVERRIDE_CREDS) {
|
||||
buf = buffer.str(); /* update buf to have label */
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
|
||||
perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,16 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
|
@ -45,9 +45,9 @@ aare_rules::~aare_rules(void)
|
|||
}
|
||||
|
||||
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)
|
||||
|
@ -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,
|
||||
int count, const char **rulev, dfaflags_t flags,
|
||||
int count, const char **rulev, optflags const &opts,
|
||||
bool oob)
|
||||
{
|
||||
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);
|
||||
|
||||
if (flags & DFA_DUMP_RULE_EXPR) {
|
||||
if (opts.dump & DUMP_DFA_RULE_EXPR) {
|
||||
const char *separator;
|
||||
if (oob)
|
||||
separator = "\\-x01";
|
||||
|
@ -152,13 +152,13 @@ err:
|
|||
* advanced by a null character for each xattr.
|
||||
*/
|
||||
bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
|
||||
dfaflags_t flags)
|
||||
optflags const &opts)
|
||||
{
|
||||
Node *tree = NULL;
|
||||
if (regex_parse(&tree, rule))
|
||||
return false;
|
||||
|
||||
if (flags & DFA_DUMP_RULE_EXPR) {
|
||||
if (opts.dump & DUMP_DFA_RULE_EXPR) {
|
||||
cerr << "rule: ";
|
||||
cerr << rule;
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
|
@ -204,15 +204,15 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
|
|||
* set nodes */
|
||||
PermExprMap::iterator i = expr_map.begin();
|
||||
if (i != expr_map.end()) {
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
Node *tmp = simplify_tree(i->second, flags);
|
||||
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
|
||||
Node *tmp = simplify_tree(i->second, opts);
|
||||
root = new CatNode(tmp, i->first);
|
||||
} else
|
||||
root = new CatNode(i->second, i->first);
|
||||
for (i++; i != expr_map.end(); i++) {
|
||||
Node *tmp;
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
tmp = simplify_tree(i->second, flags);
|
||||
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
|
||||
tmp = simplify_tree(i->second, opts);
|
||||
} else
|
||||
tmp = i->second;
|
||||
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.
|
||||
*/
|
||||
label_nodes(root);
|
||||
if (flags & DFA_DUMP_TREE) {
|
||||
if (opts.dump & DUMP_DFA_TREE) {
|
||||
cerr << "\nDFA: Expression Tree\n";
|
||||
root->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
||||
if (opts.control & CONTROL_DFA_TREE_SIMPLE) {
|
||||
/* This is old total tree, simplification point
|
||||
* For now just do simplification up front. It gets most
|
||||
* of the benefit running on the smaller chains, and is
|
||||
* overall faster because there are less nodes. Reevaluate
|
||||
* 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";
|
||||
root->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
|
@ -250,19 +250,19 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
|
|||
|
||||
stringstream stream;
|
||||
try {
|
||||
DFA dfa(root, flags, filedfa);
|
||||
if (flags & DFA_DUMP_UNIQ_PERMS)
|
||||
DFA dfa(root, opts, filedfa);
|
||||
if (opts.dump & DUMP_DFA_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("dfa");
|
||||
|
||||
if (flags & DFA_CONTROL_MINIMIZE) {
|
||||
dfa.minimize(flags);
|
||||
if (opts.control & CONTROL_DFA_MINIMIZE) {
|
||||
dfa.minimize(opts);
|
||||
|
||||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_FILTER_DENY &&
|
||||
flags & DFA_CONTROL_MINIMIZE &&
|
||||
if (opts.control & CONTROL_DFA_FILTER_DENY &&
|
||||
opts.control & CONTROL_DFA_MINIMIZE &&
|
||||
dfa.apply_and_clear_deny()) {
|
||||
/* Do a second minimization pass as removal of deny
|
||||
* information has moved some states from accepting
|
||||
|
@ -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
|
||||
* 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");
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
|
||||
dfa.remove_unreachable(flags);
|
||||
if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE)
|
||||
dfa.remove_unreachable(opts);
|
||||
|
||||
if (flags & DFA_DUMP_STATES)
|
||||
if (opts.dump & DUMP_DFA_STATES)
|
||||
dfa.dump(cerr);
|
||||
|
||||
if (flags & DFA_DUMP_GRAPH)
|
||||
if (opts.dump & DUMP_DFA_GRAPH)
|
||||
dfa.dump_dot_graph(cerr);
|
||||
|
||||
map<transchar, transchar> eq;
|
||||
if (flags & DFA_CONTROL_EQUIV) {
|
||||
eq = dfa.equivalence_classes(flags);
|
||||
if (opts.control & CONTROL_DFA_EQUIV) {
|
||||
eq = dfa.equivalence_classes(opts);
|
||||
dfa.apply_equivalence_classes(eq);
|
||||
|
||||
if (flags & DFA_DUMP_EQUIV) {
|
||||
if (opts.dump & DUMP_DFA_EQUIV) {
|
||||
cerr << "\nDFA equivalence class\n";
|
||||
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";
|
||||
|
||||
if (flags & DFA_CONTROL_DIFF_ENCODE) {
|
||||
dfa.diff_encode(flags);
|
||||
if (opts.control & CONTROL_DFA_DIFF_ENCODE) {
|
||||
dfa.diff_encode(opts);
|
||||
|
||||
if (flags & DFA_DUMP_DIFF_ENCODE)
|
||||
if (opts.dump & DUMP_DFA_DIFF_ENCODE)
|
||||
dfa.dump_diff_encode(cerr);
|
||||
}
|
||||
|
||||
CHFA chfa(dfa, eq, flags);
|
||||
if (flags & DFA_DUMP_TRANS_TABLE)
|
||||
CHFA chfa(dfa, eq, opts);
|
||||
if (opts.dump & DUMP_DFA_TRANS_TABLE)
|
||||
chfa.dump(cerr);
|
||||
chfa.flex_table(stream, "");
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../common_optarg.h"
|
||||
#include "apparmor_re.h"
|
||||
#include "expr-tree.h"
|
||||
|
||||
|
@ -101,11 +102,11 @@ class aare_rules {
|
|||
~aare_rules();
|
||||
|
||||
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,
|
||||
const char **rulev, dfaflags_t flags, bool oob);
|
||||
bool append_rule(const char *rule, bool oob, bool with_perm, dfaflags_t flags);
|
||||
void *create_dfa(size_t *size, int *min_match_len, dfaflags_t flags,
|
||||
const char **rulev, optflags const &opts, bool oob);
|
||||
bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts);
|
||||
void *create_dfa(size_t *size, int *min_match_len, optflags const &opts,
|
||||
bool filedfa);
|
||||
};
|
||||
|
||||
|
|
|
@ -19,40 +19,42 @@
|
|||
#ifndef 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 DFA_CONTROL_TREE_NORMAL (1 << 1)
|
||||
#define DFA_CONTROL_TREE_SIMPLE (1 << 2)
|
||||
#define DFA_CONTROL_TREE_LEFT (1 << 3)
|
||||
#define DFA_CONTROL_MINIMIZE (1 << 4)
|
||||
#define DFA_CONTROL_FILTER_DENY (1 << 6)
|
||||
#define DFA_CONTROL_REMOVE_UNREACHABLE (1 << 7)
|
||||
#define DFA_CONTROL_TRANS_HIGH (1 << 8)
|
||||
#define DFA_CONTROL_DIFF_ENCODE (1 << 9)
|
||||
|
||||
#define DFA_DUMP_DIFF_PROGRESS (1 << 10)
|
||||
#define DFA_DUMP_DIFF_ENCODE (1 << 11)
|
||||
#define DFA_DUMP_DIFF_STATS (1 << 12)
|
||||
#define DFA_DUMP_MIN_PARTS (1 << 13)
|
||||
#define DFA_DUMP_UNIQ_PERMS (1 << 14)
|
||||
#define DFA_DUMP_MIN_UNIQ_PERMS (1 << 15)
|
||||
#define DFA_DUMP_TREE_STATS (1 << 16)
|
||||
#define DFA_DUMP_TREE (1 << 17)
|
||||
#define DFA_DUMP_SIMPLE_TREE (1 << 18)
|
||||
#define DFA_DUMP_PROGRESS (1 << 19)
|
||||
#define DFA_DUMP_STATS (1 << 20)
|
||||
#define DFA_DUMP_STATES (1 << 21)
|
||||
#define DFA_DUMP_GRAPH (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)
|
||||
#define DUMP_DFA_DIFF_PROGRESS (1 << 0)
|
||||
#define DUMP_DFA_DIFF_ENCODE (1 << 1)
|
||||
#define DUMP_DFA_DIFF_STATS (1 << 2)
|
||||
#define DUMP_DFA_MIN_PARTS (1 << 3)
|
||||
#define DUMP_DFA_UNIQ_PERMS (1 << 4)
|
||||
#define DUMP_DFA_MIN_UNIQ_PERMS (1 << 5)
|
||||
#define DUMP_DFA_TREE_STATS (1 << 6)
|
||||
#define DUMP_DFA_TREE (1 << 7)
|
||||
#define DUMP_DFA_SIMPLE_TREE (1 << 8)
|
||||
#define DUMP_DFA_PROGRESS (1 << 9)
|
||||
#define DUMP_DFA_STATS (1 << 10)
|
||||
#define DUMP_DFA_STATES (1 << 11)
|
||||
#define DUMP_DFA_GRAPH (1 << 12)
|
||||
#define DUMP_DFA_TRANS_PROGRESS (1 << 13)
|
||||
#define DUMP_DFA_TRANS_STATS (1 << 14)
|
||||
#define DUMP_DFA_TRANS_TABLE (1 << 15)
|
||||
#define DUMP_DFA_EQUIV (1 << 16)
|
||||
#define DUMP_DFA_EQUIV_STATS (1 << 17)
|
||||
#define DUMP_DFA_MINIMIZE (1 << 18)
|
||||
#define DUMP_DFA_UNREACHABLE (1 << 19)
|
||||
#define DUMP_DFA_RULE_EXPR (1 << 20)
|
||||
#define DUMP_DFA_NODE_TO_DFA (1 << 21)
|
||||
#define DUMP_RULE_MERGE (1 << 22)
|
||||
|
||||
#endif /* APPARMOR_RE_H */
|
||||
|
|
|
@ -49,9 +49,10 @@ void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
|
|||
/**
|
||||
* 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");
|
||||
|
||||
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)
|
||||
continue;
|
||||
optimal += (*i)->trans.size();
|
||||
if (flags & DFA_CONTROL_TRANS_HIGH) {
|
||||
if (opts.control & CONTROL_DFA_TRANS_HIGH) {
|
||||
size_t range = 0;
|
||||
if ((*i)->trans.size())
|
||||
range =
|
||||
|
@ -116,7 +117,7 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, dfaflags_t flags): eq(eq)
|
|||
|
||||
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++) {
|
||||
if (*i != dfa.nonmatching && *i != dfa.start) {
|
||||
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);
|
||||
num.insert(make_pair(*i, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
|
||||
count++;
|
||||
if (count % 100 == 0)
|
||||
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);
|
||||
num.insert(make_pair(i->second, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
|
||||
count++;
|
||||
if (count % 100 == 0)
|
||||
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();
|
||||
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,
|
||||
|
|
|
@ -37,7 +37,7 @@ class CHFA {
|
|||
typedef vector<pair<const State *, size_t> > DefaultBase;
|
||||
typedef vector<pair<const State *, const State *> > NextCheck;
|
||||
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 flex_table(ostream &os, const char *name);
|
||||
void init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
|
|
|
@ -575,12 +575,12 @@ static void count_tree_nodes(Node *t, struct node_counts *counts)
|
|||
// simplification passes. Simplification may exit sooner if no changes
|
||||
// are made.
|
||||
#define MAX_PASSES 1
|
||||
Node *simplify_tree(Node *t, dfaflags_t flags)
|
||||
Node *simplify_tree(Node *t, optflags const &opts)
|
||||
{
|
||||
bool update = true;
|
||||
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 };
|
||||
count_tree_nodes(t, &counts);
|
||||
fprintf(stderr,
|
||||
|
@ -598,25 +598,25 @@ Node *simplify_tree(Node *t, dfaflags_t flags)
|
|||
// the dfa having about 7 thousands states,
|
||||
// and it having about 1.25 million states
|
||||
int dir = 1;
|
||||
if (flags & DFA_CONTROL_TREE_LEFT)
|
||||
if (opts.control & CONTROL_DFA_TREE_LEFT)
|
||||
dir = 0;
|
||||
for (int count = 0; count < 2; count++) {
|
||||
bool modified;
|
||||
do {
|
||||
modified = false;
|
||||
if (flags & DFA_CONTROL_TREE_NORMAL)
|
||||
if (opts.control & CONTROL_DFA_TREE_NORMAL)
|
||||
t->normalize(dir);
|
||||
t = simplify_tree_base(t, dir, modified);
|
||||
if (modified)
|
||||
update = true;
|
||||
} while (modified);
|
||||
if (flags & DFA_CONTROL_TREE_LEFT)
|
||||
if (opts.control & CONTROL_DFA_TREE_LEFT)
|
||||
dir++;
|
||||
else
|
||||
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 };
|
||||
count_tree_nodes(t, &counts);
|
||||
fprintf(stderr,
|
||||
|
|
|
@ -958,7 +958,7 @@ struct node_counts {
|
|||
extern EpsNode epsnode;
|
||||
|
||||
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);
|
||||
unsigned long hash_NodeSet(NodeSet *ns);
|
||||
void flip_tree(Node *node);
|
||||
|
|
|
@ -391,12 +391,12 @@ void DFA::dump_node_to_dfa(void)
|
|||
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;
|
||||
|
||||
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 "
|
||||
<< work_queue.size()
|
||||
<< "\tstates "
|
||||
|
@ -420,7 +420,7 @@ void DFA::process_work_queue(const char *header, dfaflags_t flags)
|
|||
/**
|
||||
* 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 */
|
||||
max_range = 256;
|
||||
|
@ -428,7 +428,7 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
|
|||
oob_range = 0;
|
||||
ord_range = 8;
|
||||
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
if (opts.dump & DUMP_DFA_PROGRESS)
|
||||
fprintf(stderr, "Creating dfa:\r");
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
if (opts.dump & DUMP_DFA_PROGRESS)
|
||||
fprintf(stderr, "Creating dfa: followpos\r");
|
||||
for (depth_first_traversal i(root); i; i++) {
|
||||
(*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.push_back(start);
|
||||
process_work_queue("Creating dfa", flags);
|
||||
process_work_queue("Creating dfa", opts);
|
||||
max_range += oob_range;
|
||||
/* if oob_range is ever greater than 256 need to move to computing this */
|
||||
if (oob_range)
|
||||
|
@ -471,10 +471,10 @@ DFA::DFA(Node *root, dfaflags_t flags, bool buildfiledfa): root(root), filedfa(b
|
|||
(*i)->followpos.clear();
|
||||
}
|
||||
|
||||
if (flags & DFA_DUMP_NODE_TO_DFA)
|
||||
if (opts.dump & DUMP_DFA_NODE_TO_DFA)
|
||||
dump_node_to_dfa();
|
||||
|
||||
if (flags & (DFA_DUMP_STATS)) {
|
||||
if (opts.dump & (DUMP_DFA_STATS)) {
|
||||
cerr << "\033[2KCreated dfa: states "
|
||||
<< states.size()
|
||||
<< " proto { "
|
||||
|
@ -540,7 +540,7 @@ void DFA::dump_uniq_perms(const char *s)
|
|||
}
|
||||
|
||||
/* Remove dead or unreachable states */
|
||||
void DFA::remove_unreachable(dfaflags_t flags)
|
||||
void DFA::remove_unreachable(optflags const &opts)
|
||||
{
|
||||
set<State *> reachable;
|
||||
|
||||
|
@ -571,7 +571,7 @@ void DFA::remove_unreachable(dfaflags_t flags)
|
|||
next = i;
|
||||
next++;
|
||||
if (reachable.find(*i) == reachable.end()) {
|
||||
if (flags & DFA_DUMP_UNREACHABLE) {
|
||||
if (opts.dump & DUMP_DFA_UNREACHABLE) {
|
||||
cerr << "unreachable: " << **i;
|
||||
if (*i == start)
|
||||
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 "
|
||||
<< count << " unreachable states\n";
|
||||
}
|
||||
|
@ -645,7 +645,7 @@ int DFA::apply_and_clear_deny(void)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
list<Partition *> partitions;
|
||||
|
@ -680,7 +680,7 @@ void DFA::minimize(dfaflags_t flags)
|
|||
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 "
|
||||
<< partitions.size() << "\tinit " << partitions.size()
|
||||
<< " (accept " << accept_count << ")\r";
|
||||
|
@ -692,7 +692,7 @@ void DFA::minimize(dfaflags_t flags)
|
|||
perm_map.clear();
|
||||
|
||||
int init_count = partitions.size();
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
if (opts.dump & DUMP_DFA_PROGRESS)
|
||||
cerr << "\033[2KMinimize dfa: partitions " << partitions.size()
|
||||
<< "\tinit " << init_count << " (accept "
|
||||
<< accept_count << ")\r";
|
||||
|
@ -734,7 +734,7 @@ void DFA::minimize(dfaflags_t flags)
|
|||
(*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 "
|
||||
<< partitions.size() << "\tinit "
|
||||
<< init_count << " (accept "
|
||||
|
@ -743,7 +743,7 @@ void DFA::minimize(dfaflags_t flags)
|
|||
} while (new_part_count);
|
||||
|
||||
if (partitions.size() == states.size()) {
|
||||
if (flags & DFA_DUMP_STATS)
|
||||
if (opts.dump & DUMP_DFA_STATS)
|
||||
cerr << "\033[2KDfa minimization no states removed: partitions "
|
||||
<< partitions.size() << "\tinit " << init_count
|
||||
<< " (accept " << accept_count << ")\n";
|
||||
|
@ -757,13 +757,13 @@ void DFA::minimize(dfaflags_t flags)
|
|||
* to states within the same partitions, however this can slow
|
||||
* 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";
|
||||
for (list<Partition *>::iterator p = partitions.begin();
|
||||
p != partitions.end(); p++) {
|
||||
/* representative state for this partition */
|
||||
State *rep = *((*p)->begin());
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
if (opts.dump & DUMP_DFA_MIN_PARTS)
|
||||
cerr << *rep << " : ";
|
||||
|
||||
/* update representative state's transitions */
|
||||
|
@ -782,17 +782,17 @@ void DFA::minimize(dfaflags_t flags)
|
|||
/* clear the state label for all non representative states,
|
||||
* and accumulate permissions */
|
||||
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 << ", ";
|
||||
(*i)->label = -1;
|
||||
rep->perms.add((*i)->perms, filedfa);
|
||||
}
|
||||
if (rep->perms.is_accept())
|
||||
final_accept++;
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
if (opts.dump & DUMP_DFA_MIN_PARTS)
|
||||
cerr << "\n";
|
||||
}
|
||||
if (flags & DFA_DUMP_STATS)
|
||||
if (opts.dump & DUMP_DFA_STATS)
|
||||
cerr << "\033[2KMinimized dfa: final partitions "
|
||||
<< partitions.size() << " (accept " << final_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
|
||||
* @dfa_flags: flags controlling dfa creation
|
||||
* @opts: flags controlling dfa creation
|
||||
*
|
||||
* This function reduces the number of transitions that need to be stored
|
||||
* 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 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;
|
||||
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 "
|
||||
<< tail << ". Diff states " << xcount
|
||||
<< " 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 "
|
||||
<< tail << " reached @ depth " << depth << ". "
|
||||
<< 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
|
||||
* transition table.
|
||||
*/
|
||||
map<transchar, transchar> DFA::equivalence_classes(dfaflags_t flags)
|
||||
map<transchar, transchar> DFA::equivalence_classes(optflags const &opts)
|
||||
{
|
||||
map<transchar, transchar> classes;
|
||||
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",
|
||||
next_class.c - 1);
|
||||
return classes;
|
||||
|
|
|
@ -305,7 +305,7 @@ class DFA {
|
|||
State *add_new_state(NodeSet *nodes, State *other);
|
||||
State *add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other);
|
||||
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,
|
||||
Partition &chain, State *state,
|
||||
unsigned int &count, unsigned int &total,
|
||||
|
@ -318,19 +318,19 @@ class DFA {
|
|||
list<State *> work_queue;
|
||||
|
||||
public:
|
||||
DFA(Node *root, dfaflags_t flags, bool filedfa);
|
||||
DFA(Node *root, optflags const &flags, bool filedfa);
|
||||
virtual ~DFA();
|
||||
|
||||
State *match_len(State *state, const char *str, size_t len);
|
||||
State *match_until(State *state, const char *str, const char term);
|
||||
State *match(const char *str);
|
||||
|
||||
void remove_unreachable(dfaflags_t flags);
|
||||
void remove_unreachable(optflags const &flags);
|
||||
bool same_mappings(State *s1, State *s2);
|
||||
void minimize(dfaflags_t flags);
|
||||
void minimize(optflags const &flags);
|
||||
int apply_and_clear_deny(void);
|
||||
|
||||
void diff_encode(dfaflags_t flags);
|
||||
void diff_encode(optflags const &flags);
|
||||
void undiff_encode(void);
|
||||
void dump_diff_encode(ostream &os);
|
||||
|
||||
|
@ -338,7 +338,7 @@ public:
|
|||
void dump_dot_graph(ostream &os);
|
||||
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);
|
||||
|
||||
unsigned int diffcount;
|
||||
|
|
|
@ -632,6 +632,51 @@ int mnt_rule::expand_variables(void)
|
|||
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,
|
||||
unsigned int opt_flags)
|
||||
{
|
||||
|
@ -753,7 +798,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
|
|||
* else it has full perms
|
||||
*/
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
|
||||
vec, dfaflags, false))
|
||||
vec, parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
|
||||
|
@ -765,7 +810,7 @@ int mnt_rule::gen_policy_remount(Profile &prof, int &count,
|
|||
vec[4] = optsbuf.c_str();
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
|
||||
(audit == AUDIT_FORCE ? perms : 0),
|
||||
5, vec, dfaflags, false))
|
||||
5, vec, parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
|
@ -807,7 +852,7 @@ int mnt_rule::gen_policy_bind_mount(Profile &prof, int &count,
|
|||
vec[3] = flagsbuf;
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
4, vec,
|
||||
dfaflags, false))
|
||||
parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
|
||||
|
@ -864,7 +909,7 @@ int mnt_rule::gen_policy_change_mount_type(Profile &prof, int &count,
|
|||
vec[3] = flagsbuf;
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
4, vec,
|
||||
dfaflags, false))
|
||||
parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
|
||||
|
@ -907,7 +952,7 @@ int mnt_rule::gen_policy_move_mount(Profile &prof, int &count,
|
|||
vec[3] = flagsbuf;
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
4, vec,
|
||||
dfaflags, false))
|
||||
parseopts, false))
|
||||
goto fail;
|
||||
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 */
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, tmpperms, tmpaudit, 4,
|
||||
vec, dfaflags, false))
|
||||
vec, parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
|
||||
|
@ -970,7 +1015,7 @@ int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
|
|||
vec[4] = optsbuf.c_str();
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
|
||||
audit == AUDIT_FORCE ? perms : 0,
|
||||
5, vec, dfaflags, false))
|
||||
5, vec, parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
|
@ -1062,7 +1107,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
|
|||
vec[0] = mntbuf.c_str();
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
|
||||
(audit == AUDIT_FORCE ? perms : 0), 1, vec,
|
||||
dfaflags, false))
|
||||
parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
|
@ -1077,7 +1122,7 @@ int mnt_rule::gen_policy_re(Profile &prof)
|
|||
vec[1] = devbuf.c_str();
|
||||
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms,
|
||||
(audit == AUDIT_FORCE ? perms : 0), 2, vec,
|
||||
dfaflags, false))
|
||||
parseopts, false))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "parser.h"
|
||||
#include "rule.h"
|
||||
|
@ -173,6 +174,11 @@ public:
|
|||
virtual int gen_policy_re(Profile &prof);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
|
@ -231,10 +231,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
|
|||
/* store perms at name match so label doesn't need
|
||||
* to be checked
|
||||
*/
|
||||
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == 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;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
@ -266,10 +266,10 @@ int mqueue_rule::gen_policy_re(Profile &prof)
|
|||
}
|
||||
|
||||
if (perms & AA_VALID_SYSV_MQ_PERMS) {
|
||||
if (!label && !prof.policy.rules->add_rule_vec(rule_mode == 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;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,22 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
void validate_qname(void);
|
||||
|
|
|
@ -82,9 +82,6 @@ extern int parser_token;
|
|||
WARN_UNEXPECTED | WARN_FORMAT | WARN_MISSING | \
|
||||
WARN_OVERRIDE | WARN_INCLUDE)
|
||||
|
||||
extern dfaflags_t warnflags;
|
||||
extern dfaflags_t werrflags;
|
||||
|
||||
|
||||
typedef enum pattern_t pattern_t;
|
||||
|
||||
|
@ -99,6 +96,8 @@ struct value_list {
|
|||
struct value_list *next;
|
||||
};
|
||||
|
||||
int cmp_value_list(value_list *lhs, value_list *rhs);
|
||||
|
||||
struct cond_entry {
|
||||
char *name;
|
||||
int eq; /* where equals was used in specifying list */
|
||||
|
@ -360,7 +359,6 @@ extern int conf_quiet;
|
|||
extern int names_only;
|
||||
extern int option;
|
||||
extern int current_lineno;
|
||||
extern dfaflags_t dfaflags;
|
||||
extern const char *progname;
|
||||
extern char *profilename;
|
||||
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 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) */
|
||||
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 */
|
||||
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 void free_cod_entries(struct cod_entry *list);
|
||||
void debug_cod_entries(struct cod_entry *list);
|
||||
|
|
|
@ -89,9 +89,6 @@ int names_only = 0;
|
|||
int current_lineno = 1;
|
||||
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__;
|
||||
char *profile_ns = NULL;
|
||||
|
@ -102,6 +99,14 @@ FILE *ofile = NULL;
|
|||
|
||||
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
|
||||
int read_implies_exec = 1;
|
||||
#else
|
||||
|
@ -140,8 +145,8 @@ void pwarnf(bool werr, const char *fmt, ...)
|
|||
/* 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)
|
||||
{
|
||||
if ((warnflags & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
|
||||
if (werrflags & WARN_RULE_NOT_ENFORCED)
|
||||
if ((parseopts.warn & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
|
||||
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
|
||||
cerr << "Warning converted to Error";
|
||||
else
|
||||
cerr << "Warning";
|
||||
|
@ -154,6 +159,6 @@ void common_warn_once(const char *name, const char *msg, const char **warned_nam
|
|||
*warned_name = name;
|
||||
}
|
||||
|
||||
if (werrflags & WARN_RULE_NOT_ENFORCED)
|
||||
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,9 @@ int abort_on_error = 0; /* stop processing profiles if error */
|
|||
int skip_bad_cache_rebuild = 0;
|
||||
int mru_skip_cache = 1;
|
||||
|
||||
bool O_rule_merge = true;
|
||||
bool D_rule_merge = false;
|
||||
|
||||
/* for jobs_max and jobs
|
||||
* LONG_MAX : no limit
|
||||
* LONG_MIN : auto = detect system processing cores
|
||||
|
@ -274,6 +277,7 @@ optflag_table_t warnflag_table[] = {
|
|||
{ 0, NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
|
||||
/* Parse comma separated cachelocations. Commas can be escaped by \, */
|
||||
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 ||
|
||||
strcmp(optarg, "optimize") == 0 ||
|
||||
strcmp(optarg, "O") == 0) {
|
||||
flagtable_help("-O ", "", progname, optflag_table);
|
||||
flagtable_help("-O ", "", progname, dfaoptflag_table);
|
||||
} else if (strcmp(optarg, "warn") == 0) {
|
||||
flagtable_help("--warn=", "", progname, warnflag_table);
|
||||
} else if (strcmp(optarg, "Werror") == 0) {
|
||||
|
@ -568,13 +572,13 @@ static int process_arg(int c, char *optarg)
|
|||
if (!optarg) {
|
||||
dump_vars = 1;
|
||||
} 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) {
|
||||
dump_vars = 1;
|
||||
} else if (strcmp(optarg, "expanded-variables") == 0) {
|
||||
dump_expanded_vars = 1;
|
||||
} else if (!handle_flag_table(dumpflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
&parseopts.dump)) {
|
||||
PERROR("%s: Invalid --Dump option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
|
@ -582,9 +586,9 @@ static int process_arg(int c, char *optarg)
|
|||
break;
|
||||
case 'O':
|
||||
if (strcmp(optarg, "show") == 0) {
|
||||
print_flags("Optimize", optflag_table, dfaflags);
|
||||
} else if (!handle_flag_table(optflag_table, optarg,
|
||||
&dfaflags)) {
|
||||
print_flags("Optimize", dfaoptflag_table, parseopts.control);
|
||||
} else if (!handle_flag_table(dfaoptflag_table, optarg,
|
||||
&parseopts.control)) {
|
||||
PERROR("%s: Invalid --Optimize option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
|
@ -665,7 +669,7 @@ static int process_arg(int c, char *optarg)
|
|||
case 'q':
|
||||
conf_verbose = 0;
|
||||
conf_quiet = 1;
|
||||
warnflags = 0;
|
||||
parseopts.warn = 0;
|
||||
break;
|
||||
case 'v':
|
||||
conf_verbose = 1;
|
||||
|
@ -723,9 +727,9 @@ static int process_arg(int c, char *optarg)
|
|||
break;
|
||||
case ARG_WARN:
|
||||
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,
|
||||
&warnflags)) {
|
||||
&parseopts.warn)) {
|
||||
PERROR("%s: Invalid --warn option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
|
@ -733,18 +737,18 @@ static int process_arg(int c, char *optarg)
|
|||
break;
|
||||
case ARG_WERROR:
|
||||
if (!optarg) {
|
||||
werrflags = -1;
|
||||
parseopts.Werror = -1;
|
||||
} 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,
|
||||
&werrflags)) {
|
||||
&parseopts.Werror)) {
|
||||
PERROR("%s: Invalid --Werror option %s\n",
|
||||
progname, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case ARG_DEBUG_CACHE:
|
||||
warnflags |= WARN_DEBUG_CACHE;
|
||||
parseopts.warn |= WARN_DEBUG_CACHE;
|
||||
break;
|
||||
case 'j':
|
||||
jobs = process_jobs_arg("-j", optarg);
|
||||
|
@ -1530,7 +1534,7 @@ static bool get_kernel_features(struct aa_features **features)
|
|||
|
||||
if (!kernel_supports_diff_encode)
|
||||
/* clear diff_encode because it is not supported */
|
||||
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
|
||||
parseopts.control &= ~CONTROL_DFA_DIFF_ENCODE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ static int process_file_entries(Profile *prof)
|
|||
table = (struct cod_entry **) malloc(sizeof(struct cod_entry *) * (count + 1));
|
||||
if (!table) {
|
||||
PERROR(_("Couldn't merge entries. Out of Memory\n"));
|
||||
return ENOMEM;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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];
|
||||
free(table);
|
||||
|
||||
count = 0;
|
||||
/* walk the sorted table merging similar entries */
|
||||
for (cur = prof->entries, next = cur->next; next; next = cur->next) {
|
||||
if (file_comp(&cur, &next) != 0) {
|
||||
|
@ -102,12 +103,24 @@ static int process_file_entries(Profile *prof)
|
|||
|
||||
next->next = NULL;
|
||||
free_cod_entries(next);
|
||||
count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include <sys/apparmor.h>
|
||||
#include <sys/apparmor_private.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "capability.h"
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
|
@ -271,6 +273,25 @@ static const char *strn_token(const char *str, size_t &len)
|
|||
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
|
||||
* 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 *val = (struct value_list *) calloc(1, sizeof(struct value_list));
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#include "common_optarg.h"
|
||||
#include "lib.h"
|
||||
#include "parser.h"
|
||||
#include "profile.h"
|
||||
|
@ -128,7 +128,7 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
|
|||
|
||||
sptr = aare;
|
||||
|
||||
if (dfaflags & DFA_DUMP_RULE_EXPR)
|
||||
if (parseopts.dump & DUMP_DFA_RULE_EXPR)
|
||||
fprintf(stderr, "aare: %s -> ", aare);
|
||||
|
||||
if (anchor)
|
||||
|
@ -427,7 +427,7 @@ out:
|
|||
if (ret == FALSE)
|
||||
ptype = ePatternInvalid;
|
||||
|
||||
if (dfaflags & DFA_DUMP_RULE_EXPR)
|
||||
if (parseopts.dump & DUMP_DFA_RULE_EXPR)
|
||||
fprintf(stderr, "%s\n", pcre.c_str());
|
||||
|
||||
return ptype;
|
||||
|
@ -507,7 +507,7 @@ static int process_profile_name_xmatch(Profile *prof)
|
|||
aare_rules *rules = new aare_rules();
|
||||
if (!rules)
|
||||
return FALSE;
|
||||
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) {
|
||||
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
|
||||
delete rules;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ static int process_profile_name_xmatch(Profile *prof)
|
|||
ptype = convert_aaregex_to_pcre(alt->name, 0,
|
||||
glob_default,
|
||||
tbuf, &len);
|
||||
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) {
|
||||
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, parseopts)) {
|
||||
delete rules;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -562,14 +562,14 @@ static int process_profile_name_xmatch(Profile *prof)
|
|||
convert_aaregex_to_pcre(xattr_value, 0,
|
||||
glob_null, tbuf,
|
||||
&len);
|
||||
if (!rules->append_rule(tbuf.c_str(), true, true, dfaflags)) {
|
||||
if (!rules->append_rule(tbuf.c_str(), true, true, parseopts)) {
|
||||
delete rules;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
if (!prof->xmatch)
|
||||
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,
|
||||
entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
|
||||
entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
return FALSE;
|
||||
} else if (!is_change_profile_perms(entry->perms)) {
|
||||
if (!dfarules->add_rule(tbuf.c_str(),
|
||||
entry->rule_mode == RULE_DENY, entry->perms,
|
||||
entry->audit == AUDIT_FORCE ? entry->perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -667,7 +667,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
|
|||
perms |= LINK_TO_LINK_SUBSET(perms);
|
||||
vec[1] = "/[^/].*";
|
||||
}
|
||||
if (!dfarules->add_rule_vec(entry->rule_mode == 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;
|
||||
}
|
||||
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;
|
||||
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
|
||||
* 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 */
|
||||
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY,
|
||||
AA_CHANGE_PROFILE | onexec_perms,
|
||||
0, index - 1, &vec[1], dfaflags, false))
|
||||
0, index - 1, &vec[1], parseopts, false))
|
||||
return FALSE;
|
||||
|
||||
/* onexec rules - both rules are needed for onexec */
|
||||
if (!dfarules->add_rule_vec(entry->rule_mode == RULE_DENY, onexec_perms,
|
||||
0, 1, vec, dfaflags, false))
|
||||
0, 1, vec, parseopts, 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));
|
||||
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 TRUE;
|
||||
|
@ -770,7 +770,7 @@ int process_profile_regex(Profile *prof)
|
|||
if (prof->dfa.rules->rule_count > 0) {
|
||||
int xmatch_len = 0;
|
||||
prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size,
|
||||
&xmatch_len, dfaflags, true);
|
||||
&xmatch_len, parseopts, true);
|
||||
delete prof->dfa.rules;
|
||||
prof->dfa.rules = NULL;
|
||||
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)
|
||||
{
|
||||
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
|
||||
if ((*i)->flags & RULE_FLAG_DELETED)
|
||||
if ((*i)->skip())
|
||||
continue;
|
||||
if ((*i)->gen_policy_re(*prof) == RULE_ERROR)
|
||||
return FALSE;
|
||||
|
@ -875,7 +875,7 @@ static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask,
|
|||
buf = buffer.str();
|
||||
if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS),
|
||||
audit ? map_perms(AA_VALID_NET_PERMS) : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -969,44 +969,44 @@ int process_profile_policydb(Profile *prof)
|
|||
|
||||
/* note: this activates fs based unix domain sockets mediation on connect */
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
if (features_supports_unix &&
|
||||
(!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, dfaflags) ||
|
||||
!prof->policy.rules->add_rule(mediates_net_unix, 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, parseopts)))
|
||||
goto out;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
if (prof->policy.rules->rule_count > 0) {
|
||||
int xmatch_len = 0;
|
||||
prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size,
|
||||
&xmatch_len, dfaflags, false);
|
||||
&xmatch_len, parseopts, false);
|
||||
delete prof->policy.rules;
|
||||
|
||||
prof->policy.rules = NULL;
|
||||
|
|
|
@ -267,7 +267,7 @@ static int process_variables_in_entries(struct cod_entry *entry_list)
|
|||
static int process_variables_in_rules(Profile &prof)
|
||||
{
|
||||
for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) {
|
||||
if ((*i)->flags & RULE_FLAG_DELETED)
|
||||
if ((*i)->skip())
|
||||
continue;
|
||||
int error = (*i)->expand_variables();
|
||||
if (error)
|
||||
|
|
|
@ -121,38 +121,39 @@ Profile::~Profile()
|
|||
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;
|
||||
std::vector<rule_t *> table;
|
||||
|
||||
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); ) {
|
||||
if ((*i)->is_mergeable())
|
||||
count++;
|
||||
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
|
||||
if ((*i)->is_mergeable() && !(*i)->skip())
|
||||
table.push_back(*i);
|
||||
}
|
||||
if (count < 2)
|
||||
if (table.size() < 2)
|
||||
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);
|
||||
|
||||
for (int i = 0, j = 1; j < count; j++) {
|
||||
unsigned long n = table.size();
|
||||
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]->merge(*table[j]))
|
||||
return false;
|
||||
if (table[i]->merge(*table[j]))
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
|
||||
return true;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,7 +319,7 @@ void post_process_file_entries(Profile *prof)
|
|||
void post_process_rule_entries(Profile *prof)
|
||||
{
|
||||
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
|
||||
if ((*i)->flags & RULE_FLAG_DELETED)
|
||||
if ((*i)->skip())
|
||||
continue;
|
||||
(*i)->post_parse_profile(*prof);
|
||||
}
|
||||
|
|
|
@ -249,7 +249,7 @@ public:
|
|||
* Requires the merged rules have customized methods
|
||||
* cmp(), is_mergeable() and merge()
|
||||
*/
|
||||
virtual bool merge_rules(void);
|
||||
virtual int merge_rules(void);
|
||||
|
||||
void dump(void)
|
||||
{
|
||||
|
|
|
@ -134,7 +134,7 @@ int ptrace_rule::gen_policy_re(Profile &prof)
|
|||
buf = buffer.str();
|
||||
if (perms & AA_VALID_PTRACE_PERMS) {
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,16 @@ public:
|
|||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
133
parser/rule.h
133
parser/rule.h
|
@ -38,6 +38,10 @@ class Profile;
|
|||
// RULE_TYPE_CLASS needs to be last because various class follow it
|
||||
#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,
|
||||
RULE_FLAG_DELETED = 1, // rule deleted - skip
|
||||
|
@ -48,16 +52,39 @@ typedef enum { RULE_FLAG_NONE = 0,
|
|||
// added because it is implied
|
||||
} 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 {
|
||||
public:
|
||||
int rule_type;
|
||||
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() { };
|
||||
|
||||
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 std::ostream &dump(std::ostream &os) = 0;
|
||||
|
||||
|
@ -75,15 +102,37 @@ public:
|
|||
// to support expansion in include names and profile names
|
||||
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 {
|
||||
return rule_type < rhs.rule_type;
|
||||
return rule_type - rhs.rule_type;
|
||||
}
|
||||
virtual bool operator<(rule_t const &rhs) const {
|
||||
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
|
||||
virtual int gen_policy_re(Profile &prof) = 0;
|
||||
|
@ -156,6 +205,32 @@ public:
|
|||
|
||||
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 {
|
||||
|
@ -215,21 +290,32 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
virtual 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;
|
||||
int cmp(prefixes const &rhs) const {
|
||||
return prefixes::cmp(rhs);
|
||||
}
|
||||
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 = ≺
|
||||
return lhsptr->cmp(*rhsptr);
|
||||
}
|
||||
|
||||
virtual bool operator<(rule_t const &rhs) const {
|
||||
if (rule_type < rhs.rule_type)
|
||||
return true;
|
||||
if (rhs.rule_type < rule_type)
|
||||
return false;
|
||||
return *this < (prefixes const &)rhs;
|
||||
prefix_rule_t const &pr = rule_cast<prefix_rule_t const &>(rhs);
|
||||
const prefixes *rhsptr = ≺
|
||||
return *this < *rhsptr;
|
||||
}
|
||||
|
||||
virtual ostream &dump(ostream &os) {
|
||||
|
@ -247,6 +333,16 @@ public:
|
|||
|
||||
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) {
|
||||
prefix_rule_t::dump(os);
|
||||
|
||||
|
@ -261,6 +357,13 @@ class perms_rule_t: public class_rule_t {
|
|||
public:
|
||||
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 */
|
||||
virtual ostream &dump(ostream &os) {
|
||||
|
||||
|
|
|
@ -230,6 +230,35 @@ int signal_rule::expand_variables(void)
|
|||
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)
|
||||
{
|
||||
rule_t::warn_once(name, "signal rules not enforced");
|
||||
|
@ -288,7 +317,7 @@ int signal_rule::gen_policy_re(Profile &prof)
|
|||
buf = buffer.str();
|
||||
if (perms & (AA_MAY_SEND | AA_MAY_RECEIVE)) {
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,9 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
virtual int gen_policy_re(Profile &prof);
|
||||
|
||||
virtual bool is_mergeable(void) { return true; }
|
||||
virtual int cmp(rule_t const &rhs) const;
|
||||
|
||||
protected:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
|
@ -97,7 +97,7 @@ int userns_rule::gen_policy_re(Profile &prof)
|
|||
if (perms & AA_VALID_USERNS_PERMS) {
|
||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
|
||||
audit == AUDIT_FORCE ? perms : 0,
|
||||
dfaflags))
|
||||
parseopts))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,12 @@ public:
|
|||
virtual int expand_variables(void);
|
||||
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:
|
||||
virtual void warn_once(const char *name) override;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue