diff --git a/native/jni/include/magiskpolicy.hpp b/native/jni/include/magiskpolicy.hpp index 616235b38..5c3d5d05a 100644 --- a/native/jni/include/magiskpolicy.hpp +++ b/native/jni/include/magiskpolicy.hpp @@ -23,7 +23,8 @@ public: void load_rule_file(c_str file); // Operation on types - bool create(c_str type); + bool type(c_str name, c_str attr); + bool attribute(c_str name); bool permissive(c_str type); bool enforce(c_str type); bool typeattribute(c_str type, c_str attr); @@ -51,6 +52,9 @@ public: // Magisk void magisk_rules(); + // Deprecate + bool create(c_str name) { return type(name, "domain"); } + protected: policydb *db; }; diff --git a/native/jni/magiskpolicy/sepolicy.cpp b/native/jni/magiskpolicy/sepolicy.cpp index e9e55584d..5ca75537c 100644 --- a/native/jni/magiskpolicy/sepolicy.cpp +++ b/native/jni/magiskpolicy/sepolicy.cpp @@ -513,7 +513,7 @@ bool sepol_impl::add_genfscon(const char *fs_name, const char *path, const char return true; } -bool sepol_impl::create_domain(const char *type_name) { +bool sepol_impl::add_type(const char *type_name, uint32_t flavor) { type_datum_t *type = hashtab_find(db->p_types.table, type_name); if (type) { LOGW("Type %s already exists\n", type_name); @@ -523,7 +523,7 @@ bool sepol_impl::create_domain(const char *type_name) { type = auto_cast(xmalloc(sizeof(type_datum_t))); type_datum_init(type); type->primary = 1; - type->flavor = TYPE_TYPE; + type->flavor = flavor; uint32_t value = 0; if (symtab_insert(db, SYM_TYPES, strdup(type_name), type, SCOPE_DECL, 1, &value)) @@ -551,22 +551,21 @@ bool sepol_impl::create_domain(const char *type_name) { type_set_expand(&db->role_val_to_struct[i]->types, &db->role_val_to_struct[i]->cache, db, 0); } - set_attr("domain", value); return true; } -bool sepol_impl::set_domain_state(const char *s, bool permissive) { +bool sepol_impl::set_type_state(const char *type_name, bool permissive) { type_datum_t *type; - if (s == nullptr) { + if (type_name == nullptr) { hashtab_for_each(db->p_types.table, [&](hashtab_ptr_t node) { type = auto_cast(node->datum); if (ebitmap_set_bit(&db->permissive_map, type->s.value, permissive)) LOGW("Could not set bit in permissive map\n"); }); } else { - type = hashtab_find(db->p_types.table, s); + type = hashtab_find(db->p_types.table, type_name); if (type == nullptr) { - LOGW("type %s does not exist\n", s); + LOGW("type %s does not exist\n", type_name); return false; } if (ebitmap_set_bit(&db->permissive_map, type->s.value, permissive)) { @@ -577,28 +576,43 @@ bool sepol_impl::set_domain_state(const char *s, bool permissive) { return true; } -bool sepol_impl::add_typeattribute(const char *type, const char *attr) { - type_datum_t *domain = hashtab_find(db->p_types.table, type); - if (domain == nullptr) { - LOGW("type %s does not exist\n", type); - return false; - } - - int attr_val = set_attr(attr, domain->s.value); - if (attr_val < 0) - return false; +void sepol_impl::add_typeattribute(type_datum_t *type, type_datum_t *attr) { + ebitmap_set_bit(&db->type_attr_map[type->s.value - 1], attr->s.value - 1, 1); + ebitmap_set_bit(&db->attr_type_map[attr->s.value - 1], type->s.value - 1, 1); 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) { if (e->expr_type == CEXPR_NAMES && - ebitmap_get_bit(&e->type_names->types, attr_val - 1)) { - ebitmap_set_bit(&e->names, domain->s.value - 1, 1); + ebitmap_get_bit(&e->type_names->types, attr->s.value - 1)) { + ebitmap_set_bit(&e->names, type->s.value - 1, 1); } } } }); +} + +bool sepol_impl::add_typeattribute(const char *type, const char *attr) { + type_datum_t *type_d = hashtab_find(db->p_types.table, type); + if (type_d == nullptr) { + LOGW("type %s does not exist\n", type); + return false; + } else if (type_d->flavor == TYPE_ATTRIB) { + LOGW("type %s is an attribute\n", attr); + return false; + } + + type_datum *attr_d = hashtab_find(db->p_types.table, attr); + if (attr_d == nullptr) { + LOGW("attribute %s does not exist\n", type); + return false; + } else if (attr_d->flavor != TYPE_ATTRIB) { + LOGW("type %s is not an attribute \n", attr); + return false; + } + + add_typeattribute(type_d, attr_d); return true; } @@ -666,17 +680,22 @@ bool sepolicy::type_transition(const char *s, const char *t, const char *c, cons bool sepolicy::permissive(const char *s) { dprint(__FUNCTION__, s); - return impl->set_domain_state(s, true); + return impl->set_type_state(s, true); } bool sepolicy::enforce(const char *s) { dprint(__FUNCTION__, s); - return impl->set_domain_state(s, false); + return impl->set_type_state(s, false); } -bool sepolicy::create(const char *s) { - dprint(__FUNCTION__, s); - return impl->create_domain(s); +bool sepolicy::type(const char *name, const char *attr) { + dprint(__FUNCTION__, name, attr); + return impl->add_type(name, TYPE_TYPE) && impl->add_typeattribute(name, attr); +} + +bool sepolicy::attribute(const char *name) { + dprint(__FUNCTION__, name); + return impl->add_type(name, TYPE_ATTRIB); } bool sepolicy::typeattribute(const char *type, const char *attr) { diff --git a/native/jni/magiskpolicy/sepolicy.hpp b/native/jni/magiskpolicy/sepolicy.hpp index bce5af10c..70a7ecfcd 100644 --- a/native/jni/magiskpolicy/sepolicy.hpp +++ b/native/jni/magiskpolicy/sepolicy.hpp @@ -16,8 +16,9 @@ struct sepol_impl : public sepolicy { bool add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect); bool add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o); bool add_genfscon(const char *fs_name, const char *path, const char *context); - bool create_domain(const char *type_name); - bool set_domain_state(const char *s, bool permissive); + bool add_type(const char *type_name, uint32_t flavor); + bool set_type_state(const char *type_name, bool permissive); + void add_typeattribute(type_datum_t *type, type_datum_t *attr); bool add_typeattribute(const char *type, const char *attr); void strip_dontaudit(); void allow_su_client(const char *type); diff --git a/native/jni/magiskpolicy/statement.cpp b/native/jni/magiskpolicy/statement.cpp index 983f3a167..ddc0a2f6f 100644 --- a/native/jni/magiskpolicy/statement.cpp +++ b/native/jni/magiskpolicy/statement.cpp @@ -11,45 +11,52 @@ using namespace std; static const char *type_msg_1 = -R"EOF(Type 1: -" ^source_type ^target_type ^class ^perm_set" -Rules: allow, deny, auditallow, dontaudit +R"EOF("allow *source_type *target_type *class *perm_set" +"deny *source_type *target_type *class *perm_set" +"auditallow *source_type *target_type *class *perm_set" +"dontaudit *source_type *target_type *class *perm_set" )EOF"; static const char *type_msg_2 = -R"EOF(Type 2: -" ^source_type ^target_type ^class operation xperm_set" -Rules: allowxperm, auditallowxperm, dontauditxperm -- The only supported operation is ioctl -- The only supported xperm_set format is range ([low-high]) +R"EOF("allowxperm *source_type *target_type *class operation xperm_set" +"auditallowxperm *source_type *target_type *class operation xperm_set" +"dontauditxperm *source_type *target_type *class operation xperm_set" +- The only supported operation is 'ioctl' +- xperm_set format is either 'low-high', 'value', or '*'. + '*' will be treated as '0x0000-0xFFFF'. + All values should be written in hexadecimal. )EOF"; static const char *type_msg_3 = -R"EOF(Type 3: -" ^type" -Rules: create, permissive, enforcing +R"EOF("permissive ^type" +"enforce ^type" )EOF"; static const char *type_msg_4 = -R"EOF(Type 4: -"typeattribute ^type ^attribute" +R"EOF("typeattribute ^type ^attribute" )EOF"; static const char *type_msg_5 = -R"EOF(Type 5: -" source_type target_type class default_type" -Rules: type_change, type_member +R"EOF("type type_name ^(attribute)" +- Argument 'attribute' is optional, default to 'domain' )EOF"; static const char *type_msg_6 = -R"EOF(Type 6: -"type_transition source_type target_type class default_type (object_name)" -- Entry 'object_name' is optional +R"EOF("attribute attribute_name" )EOF"; static const char *type_msg_7 = -R"EOF(Type 7: -"genfscon fs_name partial_path fs_context" +R"EOF("type_transition source_type target_type class default_type (object_name)" +- Argument 'object_name' is optional +)EOF"; + +static const char *type_msg_8 = +R"EOF("type_change source_type target_type class default_type" +"type_member source_type target_type class default_type" +)EOF"; + +static const char *type_msg_9 = +R"EOF("genfscon fs_name partial_path fs_context" )EOF"; void statement_help() { @@ -61,8 +68,8 @@ Multiple policy statements can be provided in a single command. Statements has a format of " [args...]". Arguments labeled with (^) can accept one or more entries. Multiple entries consist of a space separated list enclosed in braces ({}). -For args that support multiple entries, (*) can be used to -represent all valid matches. +Arguments labeled with (*) are the same as (^), but additionally +support the match-all operator (*). Example: "allow { s1 s2 } { t1 t2 } class *" Will be expanded to: @@ -81,15 +88,20 @@ Supported policy statements: %s %s %s -)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6, type_msg_7); +%s +%s +)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, +type_msg_5, type_msg_6, type_msg_7, type_msg_8, type_msg_9); exit(0); } -static bool tokenize_string(char *stmt, vector> &arr) { +using parsed_tokens = vector>; + +static bool tokenize_string(char *stmt, parsed_tokens &arr) { // cur is the pointer to where the top level is parsing char *cur = stmt; for (char *tok; (tok = strtok_r(nullptr, " ", &cur)) != nullptr;) { - vector token; + vector token; if (tok[0] == '{') { // cur could point to somewhere in the braces, restore the string if (cur) @@ -116,7 +128,7 @@ static bool tokenize_string(char *stmt, vector> &arr) { // Check array size and all args listed in 'ones' have size = 1 (no multiple entries) template -static bool check_tokens(vector> &arr) { +static bool check_tokens(parsed_tokens &arr) { if (arr.size() != size) return false; initializer_list list{ones...}; @@ -127,7 +139,7 @@ static bool check_tokens(vector> &arr) { } template -static bool tokenize_and_check(char *stmt, vector> &arr) { +static bool tokenize_and_check(char *stmt, parsed_tokens &arr) { return tokenize_string(stmt, arr) && check_tokens(arr); } @@ -143,85 +155,112 @@ static void run_and_check(const Func &fn, const char *action, Args ...args) { #define run_fn(...) run_and_check(fn, action, __VA_ARGS__) -// Pattern 1: action { source } { target } { class } { permission } +// Pattern 1: allow { source } { target } { class } { permission } template static bool parse_pattern_1(const Func &fn, const char *action, char *stmt) { - vector> arr; + parsed_tokens arr; if (!tokenize_and_check<4>(stmt, arr)) return false; - for (char *src : arr[0]) - for (char *tgt : arr[1]) - for (char *cls : arr[2]) - for (char *perm : arr[3]) + for (auto src : arr[0]) + for (auto tgt : arr[1]) + for (auto cls : arr[2]) + for (auto perm : arr[3]) run_fn(src, tgt, cls, perm); return true; } -// Pattern 2: action { source } { target } { class } ioctl range +// Pattern 2: allowxperm { source } { target } { class } ioctl range template static bool parse_pattern_2(const Func &fn, const char *action, char *stmt) { - vector> arr; + parsed_tokens arr; if (!tokenize_and_check<5, 3, 4>(stmt, arr) || arr[3][0] != "ioctl"sv) return false; - char *range = arr[4][0]; - for (char *src : arr[0]) - for (char *tgt : arr[1]) - for (char *cls : arr[2]) + auto range = arr[4][0]; + for (auto src : arr[0]) + for (auto tgt : arr[1]) + for (auto cls : arr[2]) run_fn(src, tgt, cls, range); return true; } -// Pattern 3: action { type } +// Pattern 3: permissive { type } template static bool parse_pattern_3(const Func &fn, const char *action, char *stmt) { - vector> arr; + parsed_tokens arr; if (!tokenize_and_check<1>(stmt, arr)) return false; - for (char *type : arr[0]) + for (auto type : arr[0]) run_fn(type); return true; } -// Pattern 4: action { type } { attribute } +// Pattern 4: typeattribute { type } { attribute } template static bool parse_pattern_4(const Func &fn, const char *action, char *stmt) { - vector> arr; + parsed_tokens arr; if (!tokenize_and_check<2>(stmt, arr)) return false; - for (char *type : arr[0]) - for (char *attr : arr[1]) + for (auto type : arr[0]) + for (auto attr : arr[1]) run_fn(type, attr); return true; } -// Pattern 5: action source target class default +// Pattern 5: type name { attribute } template static bool parse_pattern_5(const Func &fn, const char *action, char *stmt) { - vector> arr; - if (!tokenize_and_check<4, 0, 1, 2, 3>(stmt, arr)) + parsed_tokens arr; + string tmp_str; + if (!tokenize_string(stmt, arr)) return false; - run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0]); + if (arr.size() == 1) { + arr.emplace_back(initializer_list{ "domain" }); + } + if (!check_tokens<2, 0>(arr)) + return false; + for (auto attr : arr[1]) + run_fn(arr[0][0], attr); return true; } -// Pattern 6: action source target class default (filename) +// Pattern 6: attribute name template static bool parse_pattern_6(const Func &fn, const char *action, char *stmt) { - vector> arr; + parsed_tokens arr; + if (!tokenize_and_check<1, 0>(stmt, arr)) + return false; + run_fn(arr[0][1]); + return true; +} + +// Pattern 7: type_transition source target class default (filename) +template +static bool parse_pattern_7(const Func &fn, const char *action, char *stmt) { + parsed_tokens arr; if (!tokenize_string(stmt, arr)) return false; if (arr.size() == 4) - arr.emplace_back(initializer_list{nullptr}); + arr.emplace_back(initializer_list{nullptr}); if (!check_tokens<5, 0, 1, 2, 3, 4>(arr)) return false; run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0], arr[4][0]); return true; } -// Pattern 7: action name path context +// Pattern 8: type_change source target class default template -static bool parse_pattern_7(const Func &fn, const char *action, char *stmt) { - vector> arr; +static bool parse_pattern_8(const Func &fn, const char *action, char *stmt) { + parsed_tokens arr; + if (!tokenize_and_check<4, 0, 1, 2, 3>(stmt, arr)) + return false; + run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0]); + return true; +} + +// Pattern 9: genfscon name path context +template +static bool parse_pattern_9(const Func &fn, const char *action, char *stmt) { + parsed_tokens arr; if (!tokenize_and_check<3, 0, 1, 2>(stmt, arr)) return false; run_fn(arr[0][0], arr[1][0], arr[2][0]); @@ -256,18 +295,20 @@ void sepolicy::parse_statement(const char *stmt) { add_action(allowxperm, 2) add_action(auditallowxperm, 2) add_action(dontauditxperm, 2) - add_action(create, 3) add_action(permissive, 3) add_action(enforce, 3) add_action(typeattribute, 4) - add_action(type_change, 5) - add_action(type_member, 5) - add_action(type_transition, 6) - add_action(genfscon, 7) + add_action(type, 5) + add_action(attribute, 6) + add_action(type_transition, 7) + add_action(type_change, 8) + add_action(type_member, 8) + add_action(genfscon, 9) // Backwards compatible syntax + add_action(create, 3) add_action_func("attradd", 4, typeattribute) - add_action_func("name_transition", 6, type_transition) + add_action_func("name_transition", 7, type_transition) else { LOGW("Unknown action: '%s'\n\n", action); } }