diff --git a/main.c b/main.c index 6f1ada936..205d9b5b7 100644 --- a/main.c +++ b/main.c @@ -2,12 +2,30 @@ static void usage(char *arg0) { fprintf(stderr, "%s [--live] [--minimal] [--load ] [--save ] [policystatement...]\n\n", arg0); + fprintf(stderr, " --live: directly load patched policy to device\n"); + fprintf(stderr, " --minimal: minimal patches for boot image to let Magisk live patch on boot\n\n"); fprintf(stderr, "Supported policy statements:\n\n"); - fprintf(stderr, "\"allow source-class target-class permission-class permission\"\n"); - fprintf(stderr, "\"deny source-class target-class permission-class permission\"\n"); - fprintf(stderr, "\"permissive class\"\n"); - fprintf(stderr, "\"enforcing class\"\n"); - fprintf(stderr, "\"attradd class attribute\"\n"); + fprintf(stderr, "\"allow #source-class #target-class permission-class #permission\"\n"); + fprintf(stderr, "\"deny #source-class #target-class permission-class #permission\"\n"); + fprintf(stderr, "\"create #class\"\n"); + fprintf(stderr, "\"permissive #class\"\n"); + fprintf(stderr, "\"enforcing #class\"\n"); + fprintf(stderr, "\"attradd #class #attribute\"\n"); + fprintf(stderr, "\"typetrans source-class target-class permission-class default-class (optional: object_name)\"\n"); + fprintf(stderr, "\nsource-class and target-class can be replaced with attributes (will patch the whole group)"); + fprintf(stderr, "Sections (except typetrans) can be replaced with \'*\'; it'll try to patch every possible matches\n"); + fprintf(stderr, "Sections marked with \'#\' can be replaced with collections in curly brackets\n"); + fprintf(stderr, "e.g.: allow { source1 source2 } { target1 target2 } permission-class { permission1 permission2 }\n"); + fprintf(stderr, "Will be expanded to:\n"); + fprintf(stderr, +"allow source1 target1 permission-class permission1\n\ +allow source1 target1 permission-class permission2\n\ +allow source1 target2 permission-class permission1\n\ +allow source1 target2 permission-class permission2\n\ +allow source2 target1 permission-class permission1\n\ +allow source2 target1 permission-class permission2\n\ +allow source2 target2 permission-class permission1\n\ +allow source2 target2 permission-class permission2\n"); fprintf(stderr, "\n"); exit(1); } @@ -15,12 +33,12 @@ static void usage(char *arg0) { // Pattern 1: action { source } { target } class { permission } static int parse_pattern_1(int action, char* statement) { int state = 0, in_bracket = 0; - char *tok, *class; - struct vector source, target, permission, *temp; + char *tok, *class, *saveptr; + vector source, target, permission, *temp; vec_init(&source); vec_init(&target); vec_init(&permission); - tok = strtok(statement, " "); + tok = strtok_r(statement, " ", &saveptr); while (tok != NULL) { if (tok[0] == '{') { if (in_bracket || state == 2) return 1; @@ -58,7 +76,7 @@ static int parse_pattern_1(int action, char* statement) { vec_push_back(temp, tok); } if (!in_bracket) ++state; - tok = strtok(NULL, " "); + tok = strtok_r(NULL, " ", &saveptr); } if (state != 4) return 1; for(int i = 0; i < source.size; ++i) @@ -66,16 +84,20 @@ static int parse_pattern_1(int action, char* statement) { for (int k = 0; k < permission.size; ++k) switch (action) { case 0: - allow(source.data[i], target.data[j], class, permission.data[k]); + if (allow(source.data[i], target.data[j], class, permission.data[k])) + fprintf(stderr, "Error in: allow %s %s %s %s\n", source.data[i], target.data[j], class, permission.data[k]); break; case 1: - deny(source.data[i], target.data[j], class, permission.data[k]); + if (deny(source.data[i], target.data[j], class, permission.data[k])) + fprintf(stderr, "Error in: deny %s %s %s %s\n", source.data[i], target.data[j], class, permission.data[k]); break; case 2: - auditallow(source.data[i], target.data[j], class, permission.data[k]); + if (auditallow(source.data[i], target.data[j], class, permission.data[k])) + fprintf(stderr, "Error in: auditallow %s %s %s %s\n", source.data[i], target.data[j], class, permission.data[k]); break; case 3: - auditdeny(source.data[i], target.data[j], class, permission.data[k]); + if (auditdeny(source.data[i], target.data[j], class, permission.data[k])) + fprintf(stderr, "Error in: auditdeny %s %s %s %s\n", source.data[i], target.data[j], class, permission.data[k]); break; default: return 1; @@ -86,8 +108,130 @@ static int parse_pattern_1(int action, char* statement) { return 0; } +// Pattern 2: action { class } { attribute } +int parse_pattern_2(int action, char* statement) { + int state = 0, in_bracket = 0; + char *tok, *saveptr; + vector class, attribute, *temp; + vec_init(&class); + vec_init(&attribute); + tok = strtok_r(statement, " ", &saveptr); + while (tok != NULL) { + if (tok[0] == '{') { + if (in_bracket) return 1; + in_bracket = 1; + if (tok[1]) { + ++tok; + continue; + } + } else if (tok[strlen(tok) - 1] == '}') { + if (!in_bracket) return 1; + in_bracket = 0; + if (strlen(tok) - 1) { + tok[strlen(tok) - 1] = '\0'; + continue; + } + } else { + if (tok[0] == '*') tok = ALL; + switch (state) { + case 0: + temp = &class; + break; + case 1: + temp = &attribute; + break; + default: + return 1; + } + vec_push_back(temp, tok); + } + if (!in_bracket) ++state; + tok = strtok_r(NULL, " ", &saveptr); + } + if (state != 2) return 1; + for(int i = 0; i < class.size; ++i) + for (int j = 0; j < attribute.size; ++j) + switch (action) { + case 0: + if (attradd(class.data[i], attribute.data[j])) + fprintf(stderr, "Error in: attradd %s %s\n", class.data[i], attribute.data[j]); + break; + default: + return 1; + } + vec_destroy(&class); + vec_destroy(&attribute); + return 0; +} + +// Pattern 3: action { type } +int parse_pattern_3(int action, char* statement) { + char *tok, *saveptr; + vector classes; + vec_init(&classes); + tok = strtok_r(statement, " {}", &saveptr); + while (tok != NULL) { + if (tok[0] == '*') tok = ALL; + vec_push_back(&classes, tok); + tok = strtok_r(NULL, " {}", &saveptr); + } + for (int i = 0; i < classes.size; ++i) { + switch (action) { + case 0: + if (create(classes.data[i])) + fprintf(stderr, "Error in: create %s", classes.data[i]); + break; + case 1: + if (permissive(classes.data[i])) + fprintf(stderr, "Error in: permissive %s", classes.data[i]); + break; + case 2: + if (enforce(classes.data[i])) + fprintf(stderr, "Error in: enforce %s", classes.data[i]); + break; + } + } + vec_destroy(&classes); + return 0; +} + +// Pattern 4: action source target class default (filename) +int parse_pattern_4(int action, char* statement) { + int state = 0; + char *tok, *saveptr; + char *source, *target, *class, *def, *filename = NULL; + tok = strtok_r(statement, " ", &saveptr); + while (tok != NULL) { + switch(state) { + case 0: + source = tok; + break; + case 1: + target = tok; + break; + case 2: + class = tok; + break; + case 3: + def = tok; + break; + case 4: + filename = tok; + break; + default: + return 1; + } + tok = strtok_r(NULL, " ", &saveptr); + ++state; + } + if (state < 4) return 1; + if (typetrans(source, target, class, def, filename)) + fprintf(stderr, "Error in: typetrans %s %s %s %s %s\n", source, target, class, def, filename ? filename : ""); + return 0; +} + int main(int argc, char *argv[]) { - char *infile = NULL, *outfile, *tok, cpy[ARG_MAX]; + char *infile = NULL, *outfile, *tok, *saveptr, cpy[ARG_MAX]; int live = 0, minimal = 0; struct vector rules; @@ -141,7 +285,7 @@ int main(int argc, char *argv[]) { for (int i = 0; i < rules.size; ++i) { // Since strtok will modify the origin string, copy the policy for error messages strcpy(cpy, rules.data[i]); - tok = strtok(rules.data[i], " "); + tok = strtok_r(rules.data[i], " ", &saveptr); if (strcmp(tok, "allow") == 0) { if (parse_pattern_1(0, rules.data[i] + strlen(tok) + 1)) printf("Syntax error in \"%s\"\n", cpy); @@ -154,6 +298,23 @@ int main(int argc, char *argv[]) { } else if (strcmp(tok, "auditdeny") == 0) { if (parse_pattern_1(3, rules.data[i] + strlen(tok) + 1)) printf("Syntax error in \"%s\"\n", cpy); + } else if (strcmp(tok, "attradd") == 0) { + if (parse_pattern_2(0, rules.data[i] + strlen(tok) + 1)) + printf("Syntax error in \"%s\"\n", cpy); + } else if (strcmp(tok, "create") == 0) { + if (parse_pattern_3(0, rules.data[i] + strlen(tok) + 1)) + printf("Syntax error in \"%s\"\n", cpy); + } else if (strcmp(tok, "permissive") == 0) { + if (parse_pattern_3(1, rules.data[i] + strlen(tok) + 1)) + printf("Syntax error in \"%s\"\n", cpy); + } else if (strcmp(tok, "enforce") == 0) { + if (parse_pattern_3(2, rules.data[i] + strlen(tok) + 1)) + printf("Syntax error in \"%s\"\n", cpy); + } else if (strcmp(tok, "typetrans") == 0) { + if (parse_pattern_4(0, rules.data[i] + strlen(tok) + 1)) + printf("Syntax error in \"%s\"\n", cpy); + } else { + usage(argv[0]); } } diff --git a/rules.c b/rules.c index b40778ad4..2ae8a5b2a 100644 --- a/rules.c +++ b/rules.c @@ -136,15 +136,19 @@ void su_rules() { samsung(); // Create domains if they don't exist + if (!exists("su")) + create("su"); permissive("su"); + if (!exists("su_device")) + create("su_device"); enforce("su_device"); // Patch su to everything allow("su", ALL, ALL, ALL); // Autotransition su's socket to su_device - add_transition("su", "device", "su_device", "file"); - add_transition("su", "device", "su_device", "dir"); + add_transition("su", "device", "file", "su_device"); + add_transition("su", "device", "dir", "su_device"); allow("su_device", "tmpfs", "filesystem", "associate"); // Transition from untrusted_app to su_client @@ -176,6 +180,8 @@ void su_rules() { // Minimal to run Magisk script before live patching void min_rules() { + if (!exists("su")) + create("su"); permissive("su"); permissive("init"); diff --git a/sepolicy-inject.h b/sepolicy-inject.h index 7db1ed149..c5c492178 100644 --- a/sepolicy-inject.h +++ b/sepolicy-inject.h @@ -32,22 +32,23 @@ policydb_t *policy; // sepolicy manipulation functions int load_policy(char *filename, policydb_t *policydb, struct policy_file *pf); -void create_domain(char *d); +int create_domain(char *d); int set_domain_state(char* s, int state); -int add_file_transition(char *srcS, char *origS, char *tgtS, char *c, char* filename); -int add_transition(char *srcS, char *origS, char *tgtS, char *c); -int add_typeattribute(char *domainS, char *typeS); +int add_transition(char *s, char *t, char *c, char *d); +int add_file_transition(char *s, char *t, char *c, char *d, char* filename); +int add_typeattribute(char *domainS, char *attr); int add_rule(char *s, char *t, char *c, char *p, int effect, int not); -int add_typerule(char *s, char *targetAttribute, char **minusses, char *c, char *p, int effect, int not); // Handy functions -void allow(char *s, char *t, char *c, char *p); -void deny(char *s, char *t, char *c, char *p); -void auditallow(char *s, char *t, char *c, char *p); -void auditdeny(char *s, char *t, char *c, char *p); -void permissive(char *s); -void enforce(char *s); -void attradd(char *s, char *a); +int allow(char *s, char *t, char *c, char *p); +int deny(char *s, char *t, char *c, char *p); +int auditallow(char *s, char *t, char *c, char *p); +int auditdeny(char *s, char *t, char *c, char *p); +int typetrans(char *s, char *t, char *c, char *d, char *o); +int create(char *s); +int permissive(char *s); +int enforce(char *s); +int attradd(char *s, char *a); int exists(char *source); // Vector of char* diff --git a/sepolicy.c b/sepolicy.c index 7a6b8599e..7d8413e7d 100644 --- a/sepolicy.c +++ b/sepolicy.c @@ -12,10 +12,10 @@ static void *cmalloc(size_t s) { static int get_attr(char *type, int value) { type_datum_t *attr = hashtab_search(policy->p_types.table, type); if (!attr) - exit(1); + return 1; if (attr->flavor != TYPE_ATTRIB) - exit(1); + return 1; return !! ebitmap_get_bit(&policy->attr_type_map[attr->s.value-1], value-1); //return !! ebitmap_get_bit(&policy->type_attr_map[value-1], attr->s.value-1); @@ -24,10 +24,10 @@ static int get_attr(char *type, int value) { static int get_attr_id(char *type) { type_datum_t *attr = hashtab_search(policy->p_types.table, type); if (!attr) - exit(1); + return 1; if (attr->flavor != TYPE_ATTRIB) - exit(1); + return 1; return attr->s.value; } @@ -35,15 +35,15 @@ static int get_attr_id(char *type) { static int set_attr(char *type, int value) { type_datum_t *attr = hashtab_search(policy->p_types.table, type); if (!attr) - exit(1); + return 1; if (attr->flavor != TYPE_ATTRIB) - exit(1); + return 1; if(ebitmap_set_bit(&policy->type_attr_map[value-1], attr->s.value-1, 1)) - exit(1); + return 1; if(ebitmap_set_bit(&policy->attr_type_map[attr->s.value-1], value-1, 1)) - exit(1); + return 1; return 0; } @@ -179,10 +179,10 @@ int load_policy(char *filename, policydb_t *policydb, struct policy_file *pf) { return 0; } -void create_domain(char *d) { +int create_domain(char *d) { symtab_datum_t *src = hashtab_search(policy->p_types.table, d); if(src) - return; + return 1; type_datum_t *typdatum = (type_datum_t *) malloc(sizeof(type_datum_t)); type_datum_init(typdatum); @@ -193,9 +193,8 @@ void create_domain(char *d) { int r = symtab_insert(policy, SYM_TYPES, strdup(d), typdatum, SCOPE_DECL, 1, &value); typdatum->s.value = value; - fprintf(stderr, "source type %s does not exist: %d,%d\n", d, r, value); if (ebitmap_set_bit(&policy->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) { - exit(1); + return 1; } policy->type_attr_map = realloc(policy->type_attr_map, sizeof(ebitmap_t)*policy->p_types.nprim); @@ -215,52 +214,62 @@ void create_domain(char *d) { src = hashtab_search(policy->p_types.table, d); if(!src) - exit(1); + return 1; extern int policydb_index_decls(policydb_t * p); if(policydb_index_decls(policy)) - exit(1); + return 1; if(policydb_index_classes(policy)) - exit(1); + return 1; if(policydb_index_others(NULL, policy, 0)) - exit(1); + return 1; - set_attr("domain", value); + return set_attr("domain", value); } int set_domain_state(char* s, int state) { type_datum_t *type; - if (!exists(s)) - create_domain(s); - type = hashtab_search(policy->p_types.table, s); - if (type == NULL) { - fprintf(stderr, "type %s does not exist\n", s); + hashtab_ptr_t cur; + if (s == NULL) { + hashtab_for_each(policy->p_types.table, &cur) { + type = cur->datum; + if (ebitmap_set_bit(&policy->permissive_map, type->s.value, state)) { + fprintf(stderr, "Could not set bit in permissive map\n"); + return 1; + } + } + } else { + type = hashtab_search(policy->p_types.table, s); + if (type == NULL) { + fprintf(stderr, "type %s does not exist\n", s); + return 1; + } + if (ebitmap_set_bit(&policy->permissive_map, type->s.value, state)) { + fprintf(stderr, "Could not set bit in permissive map\n"); return 1; + } } - if (ebitmap_set_bit(&policy->permissive_map, type->s.value, state)) { - fprintf(stderr, "Could not set bit in permissive map\n"); - return 1; - } + return 0; } -int add_transition(char *srcS, char *origS, char *tgtS, char *c) { - type_datum_t *src, *tgt, *orig; +int add_transition(char *s, char *t, char *c, char *d) { + type_datum_t *src, *tgt, *def; class_datum_t *cls; avtab_datum_t *av; avtab_key_t key; - src = hashtab_search(policy->p_types.table, srcS); + src = hashtab_search(policy->p_types.table, s); if (src == NULL) { - fprintf(stderr, "source type %s does not exist\n", srcS); + fprintf(stderr, "source type %s does not exist\n", s); return 1; } - tgt = hashtab_search(policy->p_types.table, tgtS); + tgt = hashtab_search(policy->p_types.table, t); if (tgt == NULL) { - fprintf(stderr, "target type %s does not exist\n", tgtS); + fprintf(stderr, "target type %s does not exist\n", t); return 1; } cls = hashtab_search(policy->p_classes.table, c); @@ -268,21 +277,21 @@ int add_transition(char *srcS, char *origS, char *tgtS, char *c) { fprintf(stderr, "class %s does not exist\n", c); return 1; } - orig = hashtab_search(policy->p_types.table, origS); - if (cls == NULL) { - fprintf(stderr, "class %s does not exist\n", origS); + def = hashtab_search(policy->p_types.table, d); + if (def == NULL) { + fprintf(stderr, "default type %s does not exist\n", d); return 1; } key.source_type = src->s.value; - key.target_type = orig->s.value; + key.target_type = tgt->s.value; key.target_class = cls->s.value; key.specified = AVTAB_TRANSITION; av = avtab_search(&policy->te_avtab, &key); if (av == NULL) { av = cmalloc(sizeof(*av)); - av->data = tgt->s.value; + av->data = def->s.value; int ret = avtab_insert(&policy->te_avtab, &key, av); if (ret) { fprintf(stderr, "Error inserting into avtab\n"); @@ -290,24 +299,24 @@ int add_transition(char *srcS, char *origS, char *tgtS, char *c) { } } else { fprintf(stderr, "Warning, rule already defined! Won't override.\n"); - fprintf(stderr, "Previous value = %d, wanted value = %d\n", av->data, tgt->s.value); + fprintf(stderr, "Previous value = %d, wanted value = %d\n", av->data, def->s.value); } return 0; } -int add_file_transition(char *srcS, char *origS, char *tgtS, char *c, char* filename) { - type_datum_t *src, *tgt, *orig; +int add_file_transition(char *s, char *t, char *c, char *d, char* filename) { + type_datum_t *src, *tgt, *def; class_datum_t *cls; - src = hashtab_search(policy->p_types.table, srcS); + src = hashtab_search(policy->p_types.table, s); if (src == NULL) { - fprintf(stderr, "source type %s does not exist\n", srcS); + fprintf(stderr, "source type %s does not exist\n", s); return 1; } - tgt = hashtab_search(policy->p_types.table, tgtS); + tgt = hashtab_search(policy->p_types.table, t); if (tgt == NULL) { - fprintf(stderr, "target type %s does not exist\n", tgtS); + fprintf(stderr, "target type %s does not exist\n", t); return 1; } cls = hashtab_search(policy->p_classes.table, c); @@ -315,17 +324,17 @@ int add_file_transition(char *srcS, char *origS, char *tgtS, char *c, char* file fprintf(stderr, "class %s does not exist\n", c); return 1; } - orig = hashtab_search(policy->p_types.table, origS); - if (cls == NULL) { - fprintf(stderr, "class %s does not exist\n", origS); + def = hashtab_search(policy->p_types.table, d); + if (def == NULL) { + fprintf(stderr, "default type %s does not exist\n", d); return 1; } filename_trans_t *new_transition = cmalloc(sizeof(*new_transition)); new_transition->stype = src->s.value; - new_transition->ttype = orig->s.value; + new_transition->ttype = tgt->s.value; new_transition->tclass = cls->s.value; - new_transition->otype = tgt->s.value; + new_transition->otype = def->s.value; new_transition->name = strdup(filename); new_transition->next = policy->filename_trans; @@ -334,7 +343,7 @@ int add_file_transition(char *srcS, char *origS, char *tgtS, char *c, char* file return 0; } -int add_typeattribute(char *domainS, char *typeS) { +int add_typeattribute(char *domainS, char *attr) { type_datum_t *domain; domain = hashtab_search(policy->p_types.table, domainS); @@ -343,9 +352,9 @@ int add_typeattribute(char *domainS, char *typeS) { return 1; } - set_attr(typeS, domain->s.value); + set_attr(attr, domain->s.value); - int typeId = get_attr_id(typeS); + int typeId = get_attr_id(attr); //Now let's update all constraints! //(kernel doesn't support (yet?) type_names rules) for(int i=0; ip_classes.nprim; ++i) { diff --git a/utils.c b/utils.c index fdf55f2c7..e5e740000 100644 --- a/utils.c +++ b/utils.c @@ -22,32 +22,43 @@ void vec_destroy(vector *v) { free(v->data); } -void allow(char *s, char *t, char *c, char *p) { - add_rule(s, t, c, p, AVTAB_ALLOWED, 0); +int allow(char *s, char *t, char *c, char *p) { + return add_rule(s, t, c, p, AVTAB_ALLOWED, 0); } -void deny(char *s, char *t, char *c, char *p) { - add_rule(s, t, c, p, AVTAB_ALLOWED, 1); +int deny(char *s, char *t, char *c, char *p) { + return add_rule(s, t, c, p, AVTAB_ALLOWED, 1); } -void auditallow(char *s, char *t, char *c, char *p) { - add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0); +int auditallow(char *s, char *t, char *c, char *p) { + return add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0); } -void auditdeny(char *s, char *t, char *c, char *p) { - add_rule(s, t, c, p, AVTAB_AUDITDENY, 0); +int auditdeny(char *s, char *t, char *c, char *p) { + return add_rule(s, t, c, p, AVTAB_AUDITDENY, 0); } -void permissive(char *s) { - set_domain_state(s, 1); +int typetrans(char *s, char *t, char *c, char *d, char *o) { + if (o == NULL) + return add_transition(s, t, c, d); + else + return add_file_transition(s, t, c, d, o); } -void enforce(char *s) { - set_domain_state(s, 0); +int permissive(char *s) { + return set_domain_state(s, 1); } -void attradd(char *s, char *a) { - add_typeattribute(s, a); +int enforce(char *s) { + return set_domain_state(s, 0); +} + +int create(char *s) { + return create_domain(s); +} + +int attradd(char *s, char *a) { + return add_typeattribute(s, a); } int exists(char* source) {