mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Support dumping sepolicy rules
This commit is contained in:
parent
6089cc36de
commit
c83e141a1c
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
// Internal APIs, do not use directly
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
#include <sepol/policydb/policydb.h>
|
||||
#include <sepolicy.hpp>
|
||||
|
||||
@ -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<std::string_view, std::array<const char *, 32>> class_perm_names;
|
||||
};
|
||||
|
||||
#define impl reinterpret_cast<sepol_impl *>(this)
|
||||
|
@ -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 <typename T>
|
||||
@ -38,12 +43,20 @@ static auto hashtab_find(hashtab_t h, const_hashtab_key_t key) {
|
||||
return auto_cast(hashtab_search(h, key));
|
||||
}
|
||||
|
||||
template <class Node, class Func>
|
||||
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 <class Node, class Func>
|
||||
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<class_datum_t *>(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<pair<uint8_t, uint8_t>> 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<filename_trans_key_t *>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user