From c83e141a1c38f0b33732b7adc5749bdbce862298 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 1 Aug 2023 18:03:54 -0700 Subject: [PATCH] Support dumping sepolicy rules --- native/src/sepolicy/include/sepolicy.hpp | 1 + native/src/sepolicy/main.cpp | 9 + native/src/sepolicy/policy.hpp | 9 + native/src/sepolicy/sepolicy.cpp | 247 ++++++++++++++++++++++- 4 files changed, 259 insertions(+), 7 deletions(-) diff --git a/native/src/sepolicy/include/sepolicy.hpp b/native/src/sepolicy/include/sepolicy.hpp index 6447321c6..d0c727d9d 100644 --- a/native/src/sepolicy/include/sepolicy.hpp +++ b/native/src/sepolicy/include/sepolicy.hpp @@ -29,6 +29,7 @@ struct sepolicy { void parse_statement(c_str stmt) { parse_statement(stmt, strlen(stmt)); } void load_rules(const std::string &rules); void load_rule_file(c_str file); + void print_rules(); // Operation on types bool type(c_str name, c_str attr); diff --git a/native/src/sepolicy/main.cpp b/native/src/sepolicy/main.cpp index fec950256..170f1a38e 100644 --- a/native/src/sepolicy/main.cpp +++ b/native/src/sepolicy/main.cpp @@ -23,6 +23,7 @@ Options: --apply FILE apply rules from FILE, read and parsed line by line as policy statements (multiple --apply are allowed) + --print-rules print all rules in the loaded sepolicy If neither --load, --load-split, nor --compile-split is specified, it will load from current live policies (/sys/fs/selinux/policy) @@ -38,6 +39,7 @@ int main(int argc, char *argv[]) { sepolicy *sepol = nullptr; bool magisk = false; bool live = false; + bool print = false; if (argc < 2) usage(argv[0]); int i = 1; @@ -49,6 +51,8 @@ int main(int argc, char *argv[]) { live = true; else if (option == "magisk"sv) magisk = true; + else if (option == "print-rules"sv) + print = true; else if (option == "load"sv) { if (argv[i + 1] == nullptr) usage(argv[0]); @@ -96,6 +100,11 @@ int main(int argc, char *argv[]) { return 1; } + if (print) { + sepol->print_rules(); + return 0; + } + if (magisk) sepol->magisk_rules(); diff --git a/native/src/sepolicy/policy.hpp b/native/src/sepolicy/policy.hpp index 6f96a2050..84a8b1d25 100644 --- a/native/src/sepolicy/policy.hpp +++ b/native/src/sepolicy/policy.hpp @@ -2,6 +2,9 @@ // Internal APIs, do not use directly +#include +#include + #include #include @@ -11,6 +14,9 @@ struct sepol_impl : public sepolicy { avtab_ptr_t find_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms); avtab_ptr_t insert_avtab_node(avtab_key_t *key); avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms); + void print_type(FILE *fp, type_datum_t *type); + void print_avtab(FILE *fp, avtab_ptr_t node); + void print_filename_trans(FILE *fp, hashtab_ptr_t node); bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert); void add_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, perm_datum_t *perm, int effect, bool invert); @@ -29,6 +35,9 @@ struct sepol_impl : public sepolicy { ~sepol_impl(); policydb *db; + +private: + std::map> class_perm_names; }; #define impl reinterpret_cast(this) diff --git a/native/src/sepolicy/sepolicy.cpp b/native/src/sepolicy/sepolicy.cpp index ee42eb056..5d698d979 100644 --- a/native/src/sepolicy/sepolicy.cpp +++ b/native/src/sepolicy/sepolicy.cpp @@ -17,6 +17,11 @@ int context_from_string( const policydb_t * policydb, context_struct_t ** cptr, const char *con_str, size_t con_str_len); +int context_to_string( + sepol_handle_t * handle, + const policydb_t * policydb, + const context_struct_t * context, + char **result, size_t * result_len); __END_DECLS template @@ -38,12 +43,20 @@ static auto hashtab_find(hashtab_t h, const_hashtab_key_t key) { return auto_cast(hashtab_search(h, key)); } +template +static void list_for_each(Node *node_ptr, const Func &fn) { + auto cur = node_ptr; + while (cur) { + auto next = cur->next; + fn(cur); + cur = next; + } +} + template static void hash_for_each(Node **node_ptr, int n_slot, const Func &fn) { for (int i = 0; i < n_slot; ++i) { - for (Node *cur = node_ptr[i]; cur; cur = cur->next) { - fn(cur); - } + list_for_each(node_ptr[i], fn); } } @@ -644,14 +657,14 @@ void sepol_impl::add_typeattribute(type_datum_t *type, type_datum_t *attr) { hashtab_for_each(db->p_classes.table, [&](hashtab_ptr_t node){ auto cls = static_cast(node->datum); - for (constraint_node_t *n = cls->constraints; n ; n = n->next) { - for (constraint_expr_t *e = n->expr; e; e = e->next) { + list_for_each(cls->constraints, [&](constraint_node_t *n) { + list_for_each(n->expr, [&](constraint_expr_t *e) { if (e->expr_type == CEXPR_NAMES && ebitmap_get_bit(&e->type_names->types, attr->s.value - 1)) { ebitmap_set_bit(&e->names, type->s.value - 1, 1); } - } - } + }); + }); }); } @@ -684,3 +697,223 @@ void sepol_impl::strip_dontaudit() { avtab_remove_node(&db->te_avtab, node); }); } + +void sepolicy::print_rules() { + hashtab_for_each(impl->db->p_types.table, [&](hashtab_ptr_t node) { + type_datum_t *type = auto_cast(node->datum); + if (type->flavor == TYPE_ATTRIB) { + impl->print_type(stdout, type); + } + }); + hashtab_for_each(impl->db->p_types.table, [&](hashtab_ptr_t node) { + type_datum_t *type = auto_cast(node->datum); + if (type->flavor == TYPE_TYPE) { + impl->print_type(stdout, type); + } + }); + avtab_for_each(&impl->db->te_avtab, [&](avtab_ptr_t node) { + impl->print_avtab(stdout, node); + }); + hashtab_for_each(impl->db->filename_trans, [&](hashtab_ptr_t node) { + impl->print_filename_trans(stdout, node); + }); + list_for_each(impl->db->genfs, [&](genfs_t *genfs) { + list_for_each(genfs->head, [&](ocontext *context) { + char *ctx = nullptr; + size_t len = 0; + if (context_to_string(nullptr, impl->db, &context->context[0], &ctx, &len) == 0) { + fprintf(stdout, "genfscon %s %s %s\n", genfs->fstype, context->u.name, ctx); + free(ctx); + } + }); + }); +} + +void sepol_impl::print_type(FILE *fp, type_datum_t *type) { + if (type->flavor == TYPE_ATTRIB) { + if (const char *attr = db->p_type_val_to_name[type->s.value - 1]) { + fprintf(fp, "attribute %s\n", attr); + } + } else if (type->flavor == TYPE_TYPE) { + if (const char *name = db->p_type_val_to_name[type->s.value - 1]) { + bool first = true; + ebitmap_t *bitmap = &db->type_attr_map[type->s.value - 1]; + for (uint32_t i = 0; i <= bitmap->highbit; ++i) { + if (ebitmap_get_bit(bitmap, i)) { + auto attr_type = db->type_val_to_struct[i]; + if (attr_type->flavor == TYPE_ATTRIB) { + if (const char *attr = db->p_type_val_to_name[i]) { + if (first) { + fprintf(fp, "type %s {", name); + first = false; + } + fprintf(fp, " %s", attr); + } + } + } + } + if (!first) { + fprintf(fp, " }\n"); + } + } + } +} + +void sepol_impl::print_avtab(FILE *fp, avtab_ptr_t node) { + const char *src = db->p_type_val_to_name[node->key.source_type - 1]; + const char *tgt = db->p_type_val_to_name[node->key.target_type - 1]; + const char *cls = db->p_class_val_to_name[node->key.target_class - 1]; + if (src == nullptr || tgt == nullptr || cls == nullptr) + return; + + if (node->key.specified & AVTAB_AV) { + uint32_t data = node->datum.data; + const char *name; + switch (node->key.specified) { + case AVTAB_ALLOWED: + name = "allow"; + break; + case AVTAB_AUDITALLOW: + name = "auditallow"; + break; + case AVTAB_AUDITDENY: + name = "dontaudit"; + // Invert the rules for dontaudit + data = ~data; + break; + default: + return; + } + + class_datum_t *clz = db->class_val_to_struct[node->key.target_class - 1]; + if (clz == nullptr) + return; + + auto it = class_perm_names.find(cls); + if (it == class_perm_names.end()) { + it = class_perm_names.try_emplace(cls).first; + // Find all permission names and cache the value + hashtab_for_each(clz->permissions.table, [&](hashtab_ptr_t node) { + perm_datum_t *perm = auto_cast(node->datum); + it->second[perm->s.value - 1] = node->key; + }); + if (clz->comdatum) { + hashtab_for_each(clz->comdatum->permissions.table, [&](hashtab_ptr_t node) { + perm_datum_t *perm = auto_cast(node->datum); + it->second[perm->s.value - 1] = node->key; + }); + } + } + + bool first = true; + for (int i = 0; i < 32; ++i) { + if (data & (1u << i)) { + if (const char *perm = it->second[i]) { + if (first) { + fprintf(fp, "%s %s %s %s {", name, src, tgt, cls); + first = false; + } + fprintf(fp, " %s", perm); + } + } + } + if (!first) { + fprintf(fp, " }\n"); + } + } else if (node->key.specified & AVTAB_TYPE) { + const char *name; + switch (node->key.specified) { + case AVTAB_TRANSITION: + name = "type_transition"; + break; + case AVTAB_MEMBER: + name = "type_member"; + break; + case AVTAB_CHANGE: + name = "type_change"; + break; + default: + return; + } + if (const char *def = db->p_type_val_to_name[node->datum.data - 1]) { + fprintf(fp, "%s %s %s %s %s\n", name, src, tgt, cls, def); + } + } else if (node->key.specified & AVTAB_XPERMS) { + const char *name; + switch (node->key.specified) { + case AVTAB_XPERMS_ALLOWED: + name = "allowxperm"; + break; + case AVTAB_XPERMS_AUDITALLOW: + name = "auditallowxperm"; + break; + case AVTAB_XPERMS_DONTAUDIT: + name = "dontauditxperm"; + break; + default: + return; + } + avtab_extended_perms_t *xperms = node->datum.xperms; + if (xperms == nullptr) + return; + + vector> ranges; + { + int low = -1; + for (int i = 0; i < 256; ++i) { + if (xperm_test(i, xperms->perms)) { + if (low < 0) { + low = i; + } + if (i == 255) { + ranges.emplace_back(low, 255); + } + } else if (low >= 0) { + ranges.emplace_back(low, i - 1); + low = -1; + } + } + } + + auto to_value = [&](uint8_t val) -> uint16_t { + if (xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { + return (((uint16_t) xperms->driver) << 8) | val; + } else { + return ((uint16_t) val) << 8; + } + }; + + if (!ranges.empty()) { + fprintf(fp, "%s %s %s %s ioctl {", name, src, tgt, cls); + for (auto [l, h] : ranges) { + uint16_t low = to_value(l); + uint16_t high = to_value(h); + if (low == high) { + fprintf(fp, " 0x%04X", low); + } else { + fprintf(fp, " 0x%04X-0x%04X", low, high); + } + } + fprintf(fp, " }\n"); + } + } +} + +void sepol_impl::print_filename_trans(FILE *fp, hashtab_ptr_t node) { + auto key = reinterpret_cast(node->key); + filename_trans_datum_t *trans = auto_cast(node->datum); + + const char *tgt = db->p_type_val_to_name[key->ttype - 1]; + const char *cls = db->p_class_val_to_name[key->tclass - 1]; + const char *def = db->p_type_val_to_name[trans->otype - 1]; + if (tgt == nullptr || cls == nullptr || def == nullptr || key->name == nullptr) + return; + + for (uint32_t i = 0; i <= trans->stypes.highbit; ++i) { + if (ebitmap_get_bit(&trans->stypes, i)) { + if (const char *src = db->p_type_val_to_name[i]) { + fprintf(fp, "type_transition %s %s %s %s %s\n", src, tgt, cls, def, key->name); + } + } + } +}