diff --git a/parser/libapparmor_re/Makefile b/parser/libapparmor_re/Makefile index 4cf11a6a9..620dd297e 100644 --- a/parser/libapparmor_re/Makefile +++ b/parser/libapparmor_re/Makefile @@ -22,17 +22,19 @@ all : ${TARGET} UNITTESTS = tst_parse -libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o +libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o policy_compat.o ${AR} ${ARFLAGS} $@ $^ expr-tree.o: expr-tree.cc expr-tree.h -hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h +hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h policy_compat.h aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h chfa.h parse.h ../immunix.h chfa.o: chfa.cc chfa.h ../immunix.h +policy_compat.o: policy_compat.cc policy_compat.h ../perms.h ../immunix.h + parse.o : parse.cc apparmor_re.h expr-tree.h parse.cc : parse.y parse.h flex-tables.h ../immunix.h diff --git a/parser/libapparmor_re/aare_rules.cc b/parser/libapparmor_re/aare_rules.cc index 2223906f7..f48d858fb 100644 --- a/parser/libapparmor_re/aare_rules.cc +++ b/parser/libapparmor_re/aare_rules.cc @@ -194,8 +194,10 @@ 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, optflags const &opts, - bool filedfa) +void *aare_rules::create_dfa(size_t *size, int *min_match_len, + vector &perms_table, + optflags const &opts, + bool filedfa, bool extended_perms) { char *buffer = NULL; @@ -304,7 +306,12 @@ void *aare_rules::create_dfa(size_t *size, int *min_match_len, optflags const &o dfa.dump_diff_encode(cerr); } - CHFA chfa(dfa, eq, opts, false); + //cerr << "Checking extended perms " << extended_perms << "\n"; + if (extended_perms) { + //cerr << "creating permstable\n"; + dfa.compute_perms_table(perms_table); + } + CHFA chfa(dfa, eq, opts, extended_perms); if (opts.dump & DUMP_DFA_TRANS_TABLE) chfa.dump(cerr); chfa.flex_table(stream); diff --git a/parser/libapparmor_re/aare_rules.h b/parser/libapparmor_re/aare_rules.h index 71087b887..5e9104036 100644 --- a/parser/libapparmor_re/aare_rules.h +++ b/parser/libapparmor_re/aare_rules.h @@ -21,11 +21,15 @@ #ifndef __LIBAA_RE_RULES_H #define __LIBAA_RE_RULES_H +#include + #include #include "../common_optarg.h" #include "apparmor_re.h" #include "expr-tree.h" +#include "../immunix.h" +#include "../perms.h" class UniquePerm { public: @@ -106,8 +110,10 @@ class aare_rules { bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count, const char **rulev, optflags const &opts, bool oob); bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts); - void *create_dfa(size_t *size, int *min_match_len, optflags const &opts, - bool filedfa); + void *create_dfa(size_t *size, int *min_match_len, + vector &perms_table, + optflags const &opts, + bool filedfa, bool extended_perms); }; #endif /* __LIBAA_RE_RULES_H */ diff --git a/parser/libapparmor_re/chfa.cc b/parser/libapparmor_re/chfa.cc index a29d858fc..f5aa97929 100644 --- a/parser/libapparmor_re/chfa.cc +++ b/parser/libapparmor_re/chfa.cc @@ -105,20 +105,23 @@ CHFA::CHFA(DFA &dfa, map &eq, optflags const &opts, num.insert(make_pair(dfa.nonmatching, num.size())); accept.resize(max(dfa.states.size(), (size_t) 2)); - if (!permindex) { + if (permindex) { + accept[0] = dfa.nonmatching->idx; + accept[1] = dfa.start->idx; + } else { accept2.resize(max(dfa.states.size(), (size_t) 2)); - accept2[0] = 0; - accept2[1] = 0; + dfa.nonmatching->map_perms_to_accept(accept[0], + accept2[0]); + dfa.start->map_perms_to_accept(accept[1], + accept2[1]); } next_check.resize(max(optimal, (size_t) dfa.max_range)); free_list.resize(next_check.size()); - accept[0] = 0; first_free = 1; init_free_list(free_list, 0, 1); insert_state(free_list, dfa.start, dfa); - accept[1] = 0; num.insert(make_pair(dfa.start, num.size())); int count = 2; @@ -127,8 +130,11 @@ CHFA::CHFA(DFA &dfa, map &eq, optflags const &opts, 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); - (*i)->map_perms_to_accept(accept[num.size()], - accept2[num.size()]); + if (permindex) + accept[num.size()] = (*i)->idx; + else + (*i)->map_perms_to_accept(accept[num.size()], + accept2[num.size()]); num.insert(make_pair(*i, num.size())); } if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) { @@ -144,8 +150,11 @@ CHFA::CHFA(DFA &dfa, map &eq, optflags const &opts, if (i->second != dfa.nonmatching && i->second != dfa.start) { insert_state(free_list, i->second, dfa); - i->second->map_perms_to_accept(accept[num.size()], - accept2[num.size()]); + if (permindex) + accept[num.size()] = i->second->idx; + else + i->second->map_perms_to_accept(accept[num.size()], + accept2[num.size()]); num.insert(make_pair(i->second, num.size())); } if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) { diff --git a/parser/libapparmor_re/chfa.h b/parser/libapparmor_re/chfa.h index 3976f413e..b1028add9 100644 --- a/parser/libapparmor_re/chfa.h +++ b/parser/libapparmor_re/chfa.h @@ -16,7 +16,7 @@ * along with this program. If not, see . * * - * Create a compressed hfa (chfa) from and hfa + * Create a compressed hfa (chfa) from an hfa */ #ifndef __LIBAA_RE_CHFA_H #define __LIBAA_RE_CHFA_H diff --git a/parser/libapparmor_re/hfa.cc b/parser/libapparmor_re/hfa.cc index 9c2f54dd3..323f3236a 100644 --- a/parser/libapparmor_re/hfa.cc +++ b/parser/libapparmor_re/hfa.cc @@ -34,8 +34,9 @@ #include "expr-tree.h" #include "hfa.h" +#include "policy_compat.h" #include "../immunix.h" - +#include "../perms.h" ostream &operator<<(ostream &os, const CacheStats &cache) { @@ -1300,6 +1301,44 @@ void DFA::apply_equivalence_classes(map &eq) } } +void DFA::compute_perms_table_ent(State *state, size_t pos, + vector &perms_table) +{ + uint32_t accept1, accept2; + + state->map_perms_to_accept(accept1, accept2); + if (filedfa) { + state->idx = pos * 2; + perms_table[pos*2] = compute_fperms_user(accept1, accept2); + perms_table[pos*2 + 1] = compute_fperms_other(accept1, accept2); + } else { + state->idx = pos; + perms_table[pos] = compute_perms_entry(accept1, accept2); + } +} + +void DFA::compute_perms_table(vector &perms_table) +{ + size_t mult = filedfa ? 2 : 1; + size_t pos = 2; + + assert(states.size() >= 2); + perms_table.resize(states.size() * mult); + + // nonmatching and start need to be 0 and 1 so handle outside of loop + if (filedfa) + compute_perms_table_ent(nonmatching, 0, perms_table); + compute_perms_table_ent(start, 1, perms_table); + + for (Partition::iterator i = states.begin(); i != states.end(); i++) { + if (*i == nonmatching || *i == start) + continue; + compute_perms_table_ent(*i, pos, perms_table); + pos++; + } +} + + #if 0 typedef set AcceptNodes; map dominance(DFA & dfa) diff --git a/parser/libapparmor_re/hfa.h b/parser/libapparmor_re/hfa.h index 51d02c7a8..530c0c086 100644 --- a/parser/libapparmor_re/hfa.h +++ b/parser/libapparmor_re/hfa.h @@ -32,6 +32,7 @@ #include #include "expr-tree.h" +#include "policy_compat.h" #define DiffEncodeFlag 1 @@ -198,7 +199,7 @@ struct DiffDag { class State { public: State(int l, ProtoState &n, State *other, bool filedfa): - label(l), flags(0), perms(), trans() + label(l), flags(0), idx(0), perms(), trans() { int error; @@ -256,6 +257,7 @@ public: int label; int flags; + int idx; perms_t perms; StateTrans trans; State *otherwise; @@ -303,7 +305,6 @@ public: } }; - /* Transitions in the DFA. */ class DFA { void dump_node_to_dfa(void); @@ -346,6 +347,10 @@ public: map equivalence_classes(optflags const &flags); void apply_equivalence_classes(map &eq); + void compute_perms_table_ent(State *state, size_t pos, + vector &perms_table); + void compute_perms_table(vector &perms_table); + unsigned int diffcount; int oob_range; int max_range; @@ -354,6 +359,7 @@ public: Node *root; State *nonmatching, *start; Partition states; + //vector perms_table; bool filedfa; }; diff --git a/parser/libapparmor_re/policy_compat.cc b/parser/libapparmor_re/policy_compat.cc new file mode 100644 index 000000000..8213109a8 --- /dev/null +++ b/parser/libapparmor_re/policy_compat.cc @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2022 + * 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. + */ +/* + * This is a set of functions to provide convertion from old style permission + * mappings, to new style kernel mappings. It is based on the kernel to + * as the kernel needs this for backwards compatibility. This allows the + * userspace to convert to the new permission mapping without reworking + * the internal dfa permission tracking. + * + * In the future this code will be converted to go the reverse direction + * i.e. new mappings into old, which the parser will need for backwards + * compat with old kernels. + */ + +#include +#include + +#include "policy_compat.h" +#include "../perms.h" + + +/* remap old accept table embedded permissions to separate permission table */ +static uint32_t dfa_map_xindex(uint16_t mask) +{ + uint16_t old_index = (mask >> 10) & 0xf; + uint32_t index = 0; + + if (mask & 0x100) + index |= AA_X_UNSAFE; + if (mask & 0x200) + index |= AA_X_INHERIT; + if (mask & 0x80) + index |= AA_X_UNCONFINED; + + if (old_index == 1) { + index |= AA_X_UNCONFINED; + } else if (old_index == 2) { + index |= AA_X_NAME; + } else if (old_index == 3) { + index |= AA_X_NAME | AA_X_CHILD; + } else if (old_index) { + index |= AA_X_TABLE; + index |= old_index - 4; + } + + return index; +} + +/* + * map old dfa inline permissions to new format + */ +#define dfa_user_allow(accept1, accept2) (((accept1) & 0x7f) | \ + ((accept1) & 0x80000000)) +#define dfa_user_xbits(accept1, accept2) (((accept1) >> 7) & 0x7f) +#define dfa_user_audit(accept1, accept2) ((accept2) & 0x7f) +#define dfa_user_quiet(accept1, accept2) (((accept2) >> 7) & 0x7f) +#define dfa_user_xindex(accept1, accept2) \ + (dfa_map_xindex(accept1 & 0x3fff)) + +#define dfa_other_allow(accept1, accept2) ((((accept1) >> 14) & \ + 0x7f) | \ + ((accept1) & 0x80000000)) +#define dfa_other_xbits(accept1, accept2) \ + ((((accept1) >> 7) >> 14) & 0x7f) +#define dfa_other_audit(accept1, accept2) (((accept2) >> 14) & 0x7f) +#define dfa_other_quiet(accept1, accept2) \ + ((((accept2) >> 7) >> 14) & 0x7f) +#define dfa_other_xindex(accept1, accept2) \ + dfa_map_xindex((accept1 >> 14) & 0x3fff) + +/** + * map_old_perms - map old file perms layout to the new layout + * @old: permission set in old mapping + * + * Returns: new permission mapping + */ +static uint32_t map_old_perms(uint32_t old) +{ + uint32_t perm = old & 0xf; + + if (old & AA_MAY_READ) + perm |= AA_MAY_GETATTR | AA_MAY_OPEN; + if (old & AA_MAY_WRITE) + perm |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE | + AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN; + if (old & 0x10) + perm |= AA_MAY_LINK; + /* the old mapping lock and link_subset flags where overlaid + * and use was determined by part of a pair that they were in + */ + if (old & 0x20) + perm |= AA_MAY_LOCK | AA_LINK_SUBSET; + if (old & 0x40) /* AA_EXEC_MMAP */ + perm |= AA_EXEC_MMAP; + + return perm; +} + +static void compute_fperms_allow(struct aa_perms *perms, uint32_t accept1) +{ + perms->allow |= AA_MAY_GETATTR; + + /* change_profile wasn't determined by ownership in old mapping */ + if (accept1 & 0x80000000) + perms->allow |= AA_MAY_CHANGE_PROFILE; + if (accept1 & 0x40000000) + perms->allow |= AA_MAY_ONEXEC; +} + +struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2) +{ + struct aa_perms perms = { }; + + perms.allow = map_old_perms(dfa_user_allow(accept1, accept2)); + perms.audit = map_old_perms(dfa_user_audit(accept1, accept2)); + perms.quiet = map_old_perms(dfa_user_quiet(accept1, accept2)); + perms.xindex = dfa_user_xindex(accept1, accept2); + + compute_fperms_allow(&perms, accept1); + // prompt being carried as audit need to change + perms.allow &= ~perms.prompt; + if (perms.allow & perms.prompt) { + //std::cerr << "user allow & prompt\n"; + } + return perms; +} + +struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2) +{ + struct aa_perms perms = { }; + + perms.allow = map_old_perms(dfa_other_allow(accept1, accept2)); + perms.audit = map_old_perms(dfa_other_audit(accept1, accept2)); + perms.quiet = map_old_perms(dfa_other_quiet(accept1, accept2)); + perms.xindex = dfa_other_xindex(accept1, accept2); + + compute_fperms_allow(&perms, accept1); + // prompt being carried as audit need to change + perms.allow &= ~perms.prompt; + if (perms.allow & perms.prompt) { + std::cerr << "other allow & prompt\n"; + } + return perms; +} + +static uint32_t map_other(uint32_t x) +{ + return ((x & 0x3) << 8) | /* SETATTR/GETATTR */ + ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */ + ((x & 0x60) << 19); /* SETOPT/GETOPT */ +} + +static uint32_t map_xbits(uint32_t x) +{ + return ((x & 0x1) << 7) | + ((x & 0x7e) << 9); +} + +struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2) +// don't need to worry about version internally within the parser +// uint32_t version) +{ + struct aa_perms perms = { }; + + perms.allow = dfa_user_allow(accept1, accept2); + perms.audit = dfa_user_audit(accept1, accept2); + perms.quiet = dfa_user_quiet(accept1, accept2); + + /* + * This mapping is convulated due to history. + * v1-v4: only file perms, which are handled by compute_fperms + * v5: added policydb which dropped user conditional to gain new + * perm bits, but had to map around the xbits because the + * userspace compiler was still munging them. + * v9: adds using the xbits in policydb because the compiler now + * supports treating policydb permission bits different. + * Unfortunately there is no way to force auditing on the + * perms represented by the xbits + */ + perms.allow |= map_other(dfa_other_allow(accept1, accept2)); + // v9 encoding never rolled out. AA_MAY_LOCK needed to fix + // non fs unix locking see kernel commit + // 1cf26c3d2c4c apparmor: fix apparmor mediating locking non-fs unix sockets + //if (VERSION_LE(version, v8)) + perms.allow |= AA_MAY_LOCK; + //else + // perms.allow |= map_xbits(dfa_user_xbits(dfa, state)); + + /* + * for v5-v9 perm mapping in the policydb, the other set is used + * to extend the general perm set + */ + perms.audit |= map_other(dfa_other_audit(accept1, accept2)); + perms.quiet |= map_other(dfa_other_quiet(accept1, accept2)); + //if (VERSION_GT(version, v8)) + // perms.quiet |= map_xbits(dfa_other_xbits(dfa, state)); + + return perms; +} + diff --git a/parser/libapparmor_re/policy_compat.h b/parser/libapparmor_re/policy_compat.h new file mode 100644 index 000000000..f19c14b20 --- /dev/null +++ b/parser/libapparmor_re/policy_compat.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 + * 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_POLICY_COMPAT_H +#define __AA_POLICY_COMPAT_H + +struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2); +struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2); +struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2); + +#endif /* __AA_POLICY_COMPAT_H */ diff --git a/parser/parser.h b/parser/parser.h index 0722e5609..27bac1849 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -359,6 +359,7 @@ extern int features_supports_flag_interruptible; extern int features_supports_flag_signal; extern int features_supports_flag_error; extern int kernel_supports_oob; +extern int kernel_supports_permstable32; extern int conf_verbose; extern int conf_quiet; extern int names_only; diff --git a/parser/parser_common.c b/parser/parser_common.c index b43a4a075..b041fa05c 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -87,6 +87,7 @@ int features_supports_flag_interruptible = 0; int features_supports_flag_signal = 0; int features_supports_flag_error = 0; int kernel_supports_oob = 0; /* out of band transitions */ +int kernel_supports_permstable32 = 0; /* extended permissions */ int conf_verbose = 0; int conf_quiet = 0; int names_only = 0; diff --git a/parser/parser_interface.c b/parser/parser_interface.c index 647902be4..dca49b224 100644 --- a/parser/parser_interface.c +++ b/parser/parser_interface.c @@ -323,10 +323,49 @@ static inline void sd_write_listend(std::ostringstream &buf) sd_write8(buf, SD_LISTEND); } -void sd_serialize_dfa(std::ostringstream &buf, void *dfa, size_t size) +void sd_serialize_perm(std::ostringstream &buf, aa_perms &perms) { - if (dfa) + sd_write_uint32(buf, 0); /* reserved */ + sd_write_uint32(buf, perms.allow); + sd_write_uint32(buf, perms.deny); + sd_write_uint32(buf, perms.subtree); + sd_write_uint32(buf, perms.cond); + sd_write_uint32(buf, perms.kill); + sd_write_uint32(buf, perms.complain); + sd_write_uint32(buf, perms.prompt); + sd_write_uint32(buf, perms.audit); + sd_write_uint32(buf, perms.quiet); + sd_write_uint32(buf, perms.hide); + sd_write_uint32(buf, perms.xindex); + sd_write_uint32(buf, perms.tag); + sd_write_uint32(buf, perms.label); +} + +void sd_serialize_permstable(std::ostringstream &buf, vector &perms_table) +{ + sd_write_struct(buf, "perms"); + sd_write_name(buf, "version"); + sd_write_uint32(buf, 1); + sd_write_array(buf, NULL, perms_table.size()); + for (size_t i = 0; i < perms_table.size(); i++) { + sd_serialize_perm(buf, perms_table[i]); + } + sd_write_arrayend(buf); + sd_write_structend(buf); +} + +void sd_serialize_dfa(std::ostringstream &buf, void *dfa, size_t size, + vector &perms_table) +{ + if (dfa) { + if (kernel_supports_permstable32 && perms_table.size() > 0) { + //fprintf(stderr, "writing perms table %d\n", size); + sd_serialize_permstable(buf, perms_table); + } else { + //fprintf(stderr, "skipping permtable32 %d, size %d\n", kernel_supports_permstable32, perms_table.size()); + } sd_write_aligned_blob(buf, dfa, size, "aadfa"); + } } void sd_serialize_rlimits(std::ostringstream &buf, struct aa_rlimits *limits) @@ -344,10 +383,13 @@ void sd_serialize_rlimits(std::ostringstream &buf, struct aa_rlimits *limits) sd_write_structend(buf); } -void sd_serialize_xtable(std::ostringstream &buf, char **table) +void sd_serialize_xtable(std::ostringstream &buf, char **table, + size_t min_size) { - int count; - if (!table[4]) + size_t count; + size_t size; + + if (!table[4] && min_size == 0) return; sd_write_struct(buf, "xtable"); count = 0; @@ -356,9 +398,11 @@ void sd_serialize_xtable(std::ostringstream &buf, char **table) count++; } - sd_write_array(buf, NULL, count); - for (int i = 4; i < count + 4; i++) { - int len = strlen(table[i]) + 1; + size = max(min_size, count); + + sd_write_array(buf, NULL, size); + for (size_t i = 4; i < count + 4; i++) { + size_t len = strlen(table[i]) + 1; /* if its a namespace make sure the second : is overwritten * with 0, so that the namespace and name are \0 separated @@ -369,6 +413,14 @@ void sd_serialize_xtable(std::ostringstream &buf, char **table) } sd_write_strn(buf, table[i], len, NULL); } + if (min_size > count) { + //fprintf(stderr, "Adding padding to xtable count %lu, min %lu\n", count, min_size); + for (; count < min_size; count++) { + /* fill with null strings */ + sd_write_strn(buf, "", 1, NULL); + } + } + sd_write_arrayend(buf); sd_write_structend(buf); } @@ -411,7 +463,7 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile, /* only emit this if current kernel at least supports "create" */ if (perms_create) { if (profile->xmatch) { - sd_serialize_dfa(buf, profile->xmatch, profile->xmatch_size); + sd_serialize_dfa(buf, profile->xmatch, profile->xmatch_size, profile->xmatch_perms_table); sd_write_uint32(buf, profile->xmatch_len); } } @@ -491,14 +543,27 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile, if (profile->policy.dfa) { sd_write_struct(buf, "policydb"); - sd_serialize_dfa(buf, profile->policy.dfa, profile->policy.size); + sd_serialize_dfa(buf, profile->policy.dfa, profile->policy.size, + profile->policy.perms_table); + if (profile->policy.dfa) { + // fprintf(stderr, "profile %s: policy xtable\n", profile->name); + // TODO: this is dummy exec make dependent on V1 + sd_serialize_xtable(buf, profile->exec_table, + //??? work around + profile->policy.perms_table.size()); + } sd_write_structend(buf); } /* either have a single dfa or lists of different entry types */ - sd_serialize_dfa(buf, profile->dfa.dfa, profile->dfa.size); - sd_serialize_xtable(buf, profile->exec_table); - + sd_serialize_dfa(buf, profile->dfa.dfa, profile->dfa.size, + profile->dfa.perms_table); + if (profile->dfa.dfa) { + // fprintf(stderr, "profile %s: dfa xtable\n", profile->name); + sd_serialize_xtable(buf, profile->exec_table, + //??? work around + profile->dfa.perms_table.size()); + } sd_write_structend(buf); } diff --git a/parser/parser_main.c b/parser/parser_main.c index 0adb8a563..4186c7269 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -1544,6 +1544,10 @@ static bool get_kernel_features(struct aa_features **features) else if (aa_features_supports(*features, "policy/versions/v6")) kernel_abi_version = 6; + kernel_supports_permstable32 = aa_features_supports(*features, "policy/permstable32"); + if (kernel_supports_permstable32) { + //fprintf(stderr, "kernel supports prompt\n"); + } if (!kernel_supports_diff_encode) /* clear diff_encode because it is not supported */ parseopts.control &= ~CONTROL_DFA_DIFF_ENCODE; diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 1af5eaf58..de38c7773 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -569,7 +569,11 @@ static int process_profile_name_xmatch(Profile *prof) } } build: - prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, parseopts, true); + /* xmatch doesn't use file dfa exec mode bits NOT the owner + * conditional and for just MAY_EXEC can be processed as + * none file perms + */ + prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, prof->xmatch_perms_table, parseopts, false, kernel_supports_permstable32); delete rules; if (!prof->xmatch) return FALSE; @@ -769,8 +773,9 @@ int process_profile_regex(Profile *prof) if (prof->dfa.rules->rule_count > 0) { int xmatch_len = 0; + //fprintf(stderr, "Creating file DFA %d\n", kernel_supports_permstable32); prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size, - &xmatch_len, parseopts, true); + &xmatch_len, prof->dfa.perms_table, parseopts, true, kernel_supports_permstable32); delete prof->dfa.rules; prof->dfa.rules = NULL; if (!prof->dfa.dfa) @@ -1044,7 +1049,7 @@ int process_profile_policydb(Profile *prof) if (prof->policy.rules->rule_count > 0) { int xmatch_len = 0; prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size, - &xmatch_len, parseopts, false); + &xmatch_len, prof->policy.perms_table, parseopts, false, kernel_supports_permstable32); delete prof->policy.rules; prof->policy.rules = NULL; diff --git a/parser/perms.h b/parser/perms.h new file mode 100644 index 000000000..2759b7225 --- /dev/null +++ b/parser/perms.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 + * 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_PERM_H +#define __AA_PERM_H + +#include + +/* same as in immunix.h - make it so they can both be included or used alone */ +#ifndef AA_MAY_EXEC +#define AA_MAY_EXEC 1 +#define AA_MAY_WRITE 2 +#define AA_MAY_READ 4 +#define AA_MAY_APPEND 8 +#endif + +#ifndef AA_MAY_CREATE +// these are in apparmor.h +#define AA_MAY_CREATE 0x0010 +#define AA_MAY_DELETE 0x0020 +#define AA_MAY_OPEN 0x0040 +#define AA_MAY_RENAME 0x0080 /* pair */ + +#define AA_MAY_SETATTR 0x0100 /* meta write */ +#define AA_MAY_GETATTR 0x0200 /* meta read */ +#define AA_MAY_SETCRED 0x0400 /* security cred/attr */ +#define AA_MAY_GETCRED 0x0800 + +#define AA_MAY_CHMOD 0x1000 /* pair */ +#define AA_MAY_CHOWN 0x2000 /* pair */ +#define AA_MAY_CHGRP 0x4000 /* pair */ +#define AA_MAY_LOCK 0x8000 /* LINK_SUBSET overlaid */ + +#define AA_EXEC_MMAP 0x00010000 +#define AA_MAY_MPROT 0x00020000 /* extend conditions */ +#define AA_MAY_LINK 0x00040000 /* pair */ +#endif +#define AA_MAY_SNAPSHOT 0x00080000 /* pair */ + +#define AA_MAY_DELEGATE +#define AA_CONT_MATCH 0x08000000 + +#define AA_MAY_STACK 0x10000000 +#define AA_MAY_ONEXEC 0x20000000 /* either stack or change_profile */ +#define AA_MAY_CHANGE_PROFILE 0x40000000 +#define AA_MAY_CHANGEHAT 0x80000000 + +#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ + + +/* + * The xindex is broken into 3 parts + * - index - an index into either the exec name table or the variable table + * - exec type - which determines how the executable name and index are used + * - flags - which modify how the destination name is applied + */ +#define AA_X_INDEX_MASK AA_INDEX_MASK + +#define AA_X_TYPE_MASK 0x0c000000 +#define AA_X_NONE AA_INDEX_NONE +#define AA_X_NAME 0x04000000 /* use executable name px */ +#define AA_X_TABLE 0x08000000 /* use a specified name ->n# */ + +#define AA_X_UNSAFE 0x10000000 +#define AA_X_CHILD 0x20000000 +#define AA_X_INHERIT 0x40000000 +#define AA_X_UNCONFINED 0x80000000 + +struct aa_perms { + uint32_t allow; + uint32_t deny; /* explicit deny, or conflict if allow also set */ + + uint32_t subtree; /* allow perm on full subtree only when allow is set */ + uint32_t cond; /* set only when ~allow and ~deny */ + + uint32_t kill; /* set only when ~allow | deny */ + uint32_t complain; /* accumulates only used when ~allow & ~deny */ + uint32_t prompt; /* accumulates only used when ~allow & ~deny */ + + uint32_t audit; /* set only when allow is set */ + uint32_t quiet; /* set only when ~allow | deny */ + uint32_t hide; /* set only when ~allow | deny */ + + + uint32_t xindex; + uint32_t tag; /* tag string index, if present */ + uint32_t label; /* label string index, if present */ +}; + +#endif /* __AA_PERM_H */ diff --git a/parser/profile.h b/parser/profile.h index 8286208d8..fa4ceadb9 100644 --- a/parser/profile.h +++ b/parser/profile.h @@ -15,6 +15,7 @@ #define __AA_PROFILE_H #include +#include #include #include @@ -24,6 +25,8 @@ #include "libapparmor_re/aare_rules.h" #include "network.h" #include "signal.h" +#include "immunix.h" +#include "perms.h" class Profile; @@ -336,7 +339,7 @@ struct dfa_stuff { aare_rules *rules; void *dfa; size_t size; - + vector perms_table; dfa_stuff(void): rules(NULL), dfa(NULL), size(0) { } }; @@ -349,7 +352,7 @@ public: void *xmatch; size_t xmatch_size; int xmatch_len; - + vector xmatch_perms_table; struct cond_entry_list xattrs; /* char *sub_name; */ /* subdomain name or NULL */