Support dumping sepolicy rules

This commit is contained in:
topjohnwu 2023-08-01 18:03:54 -07:00
parent 6089cc36de
commit c83e141a1c
4 changed files with 259 additions and 7 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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)

View File

@ -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);
}
}
}
}