From f0240b1f06b4139f82d26781f8010599be15417c Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 15 Mar 2019 06:17:37 -0400 Subject: [PATCH] Support Android Q new split sepolicy setup --- native/jni/Android.mk | 1 + native/jni/core/init.cpp | 109 ++++++------ native/jni/include/magisk.h | 17 +- native/jni/magiskhide/hide_utils.cpp | 1 + native/jni/magiskpolicy/magiskpolicy.cpp | 16 +- native/jni/magiskpolicy/magiskpolicy.h | 14 +- native/jni/magiskpolicy/policydb.cpp | 201 +++++++++++++++++++++++ native/jni/magiskpolicy/sepolicy.c | 142 ---------------- native/jni/utils/include/selinux.h | 18 ++ 9 files changed, 289 insertions(+), 230 deletions(-) create mode 100644 native/jni/magiskpolicy/policydb.cpp diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 4b018db81..42a3c6e72 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -83,6 +83,7 @@ LOCAL_SRC_FILES := \ magiskpolicy/api.cpp \ magiskpolicy/magiskpolicy.cpp \ magiskpolicy/rules.cpp \ + magiskpolicy/policydb.cpp \ magiskpolicy/sepolicy.c LOCAL_LDFLAGS := -static diff --git a/native/jni/core/init.cpp b/native/jni/core/init.cpp index 2943468df..300cb72a1 100644 --- a/native/jni/core/init.cpp +++ b/native/jni/core/init.cpp @@ -57,6 +57,8 @@ int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_ma static bool mnt_system = false; static bool mnt_vendor = false; +static bool mnt_product = false; +static bool mnt_odm = false; static bool kirin = false; static void *self, *config; @@ -196,13 +198,18 @@ static bool setup_block(struct device *dev, const char *partname) { return true; } +static inline bool is_lnk(const char *name) { + struct stat st; + if (lstat(name, &st)) + return false; + return S_ISLNK(st.st_mode); +} + static bool read_fstab_dt(const struct cmdline *cmd, const char *mnt_point, char *partname, char *partfs) { char buf[128]; struct stat st; sprintf(buf, "/%s", mnt_point); - lstat(buf, &st); - // Don't early mount if the mount point is symlink - if (S_ISLNK(st.st_mode)) + if (is_lnk(buf)) return false; int fd; sprintf(buf, "%s/fstab/%s/dev", cmd->dt_dir, mnt_point); @@ -222,68 +229,29 @@ static bool read_fstab_dt(const struct cmdline *cmd, const char *mnt_point, char return false; } -static bool verify_precompiled() { - DIR *dir; - struct dirent *entry; - int fd; - char sys_sha[64], ven_sha[64]; - - // init the strings with different value - sys_sha[0] = 0; - ven_sha[0] = 1; - - dir = opendir(NONPLAT_POLICY_DIR); - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if (strend(entry->d_name, ".sha256") == 0) { - fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC); - read(fd, ven_sha, sizeof(ven_sha)); - close(fd); - break; - } - } - closedir(dir); - dir = opendir(PLAT_POLICY_DIR); - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if (strend(entry->d_name, ".sha256") == 0) { - fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC); - read(fd, sys_sha, sizeof(sys_sha)); - close(fd); - break; - } - } - closedir(dir); - LOGD("sys_sha[%.*s]\nven_sha[%.*s]\n", sizeof(sys_sha), sys_sha, sizeof(ven_sha), ven_sha); - return memcmp(sys_sha, ven_sha, sizeof(sys_sha)) == 0; -} - static bool patch_sepolicy() { bool patch_init = false; - if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled()) { - patch_init = true; - load_policydb(SPLIT_PRECOMPILE); - } else if (access(SPLIT_PLAT_CIL, R_OK) == 0) { - patch_init = true; - compile_split_cil(); - } else if (access("/sepolicy", R_OK) == 0) { - load_policydb("/sepolicy"); - } else { - // No selinux in this ROM - return false; - } + + if (access(SPLIT_PLAT_CIL, R_OK) == 0) + patch_init = true; /* Split sepolicy */ + else if (access("/sepolicy", R_OK) == 0) + load_policydb("/sepolicy"); /* Monolithic sepolicy */ + else + return false; /* No SELinux */ + + // Mount selinuxfs to communicate with kernel + xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); + + if (patch_init) + load_split_cil(); sepol_magisk_rules(); sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); dump_policydb("/sepolicy"); - if (!kirin) { - // Load policy to kernel so we can label rootfs - xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); + // Load policy to kernel so we can label rootfs + if (!kirin) dump_policydb(SELINUX_LOAD); - } // Remove OnePlus stupid debug sepolicy and use our own if (access("/sepolicy_debug", F_OK) == 0) { @@ -453,6 +421,10 @@ static void setup_overlay() { umount("/system"); if (mnt_vendor) umount("/vendor"); + if (mnt_product) + umount("/product"); + if (mnt_odm) + umount("/odm"); execv("/init", argv); exit(1); @@ -537,6 +509,15 @@ int main(int argc, char *argv[]) { xmount(dev.path, "/system_root", "ext4", MS_RDONLY, nullptr); xmkdir("/system", 0755); xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr); + + // Copy if these partitions are symlinks + if (is_lnk("/system_root/vendor")) + cp_afc("/system_root/vendor", "/vendor"); + if (is_lnk("/system_root/product")) + cp_afc("/system_root/product", "/product"); + if (is_lnk("/system_root/odm")) + cp_afc("/system_root/odm", "/odm"); + } else if (read_fstab_dt(&cmd, "system", partname, partfs)) { setup_block(&dev, partname); xmount(dev.path, "/system", partfs, MS_RDONLY, nullptr); @@ -550,6 +531,20 @@ int main(int argc, char *argv[]) { mnt_vendor = true; } + if (read_fstab_dt(&cmd, "product", partname, partfs)) { + setup_block(&dev, partname); + xmkdir("/product", 0755); + xmount(dev.path, "/product", partfs, MS_RDONLY, nullptr); + mnt_product = true; + } + + if (read_fstab_dt(&cmd, "odm", partname, partfs)) { + setup_block(&dev, partname); + xmkdir("/odm", 0755); + xmount(dev.path, "/odm", partfs, MS_RDONLY, nullptr); + mnt_odm = true; + } + /*************** * Setup Rootfs ***************/ diff --git a/native/jni/include/magisk.h b/native/jni/include/magisk.h index e680ae1a8..74bedcd1c 100644 --- a/native/jni/include/magisk.h +++ b/native/jni/include/magisk.h @@ -1,8 +1,4 @@ -/* magisk.h - Top header - */ - -#ifndef _MAGISK_H_ -#define _MAGISK_H_ +#pragma once #include @@ -30,15 +26,6 @@ // Legacy crap #define LEGACYCORE MODULEROOT "/.core" -// selinux consts -#define SELINUX_MNT "/sys/fs/selinux" -#define SELINUX_ENFORCE SELINUX_MNT "/enforce" -#define SELINUX_POLICY SELINUX_MNT "/policy" -#define SELINUX_LOAD SELINUX_MNT "/load" -#define SELINUX_CONTEXT SELINUX_MNT "/context" -#define SEPOL_PROC_DOMAIN "magisk" -#define SEPOL_FILE_DOMAIN "magisk_file" - extern int SDK_INT; constexpr const char *applet_names[] = { "magisk", "su", "resetprop", "magiskhide", nullptr }; @@ -50,5 +37,3 @@ int magiskhide_main(int argc, char *argv[]); int magiskpolicy_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]); - -#endif diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index e104390be..4166847c8 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "magiskhide.h" diff --git a/native/jni/magiskpolicy/magiskpolicy.cpp b/native/jni/magiskpolicy/magiskpolicy.cpp index bad32ca10..606eb44c4 100644 --- a/native/jni/magiskpolicy/magiskpolicy.cpp +++ b/native/jni/magiskpolicy/magiskpolicy.cpp @@ -92,8 +92,9 @@ static const char *type_msg_6 = "Options:\n" " --help show help message for policy statements\n" " --load FILE load policies from FILE\n" - " --compile-split compile and load split cil policies\n" - " from system and vendor just like init\n" + " --load-split load from preloaded sepolicy or compile\n" + " split policies\n" + " --compile-split compile split cil policies\n" " --save FILE save policies to FILE\n" " --live directly apply sepolicy live\n" " --magisk inject built-in rules for a minimal\n" @@ -460,6 +461,11 @@ int magiskpolicy_main(int argc, char *argv[]) { return 1; } ++i; + } else if (strcmp(argv[i] + 2, "load-split") == 0) { + if (load_split_cil()) { + fprintf(stderr, "Cannot load split cil\n"); + return 1; + } } else if (strcmp(argv[i] + 2, "compile-split") == 0) { if (compile_split_cil()) { fprintf(stderr, "Cannot compile split cil\n"); @@ -486,12 +492,12 @@ int magiskpolicy_main(int argc, char *argv[]) { return 1; } - for (; i < argc; ++i) - parse_statement(argv[i]); - if (magisk) sepol_magisk_rules(); + for (; i < argc; ++i) + parse_statement(argv[i]); + if (live && dump_policydb(SELINUX_LOAD)) { fprintf(stderr, "Cannot apply policy\n"); return 1; diff --git a/native/jni/magiskpolicy/magiskpolicy.h b/native/jni/magiskpolicy/magiskpolicy.h index 9a09573a8..171e414c6 100644 --- a/native/jni/magiskpolicy/magiskpolicy.h +++ b/native/jni/magiskpolicy/magiskpolicy.h @@ -5,25 +5,19 @@ #define _MAGISKPOLICY_H #include +#include #define ALL NULL -// split policy paths -#define PLAT_POLICY_DIR "/system/etc/selinux/" -#define NONPLAT_POLICY_DIR "/vendor/etc/selinux/" -#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil" -#define SPLIT_PLAT_MAPPING PLAT_POLICY_DIR "mapping/%s.cil" -#define SPLIT_PRECOMPILE NONPLAT_POLICY_DIR "precompiled_sepolicy" -#define SPLIT_NONPLAT_VER NONPLAT_POLICY_DIR "plat_sepolicy_vers.txt" - #ifdef __cplusplus extern "C" { #endif // policydb functions -int load_policydb(const char *filename); +int load_policydb(const char *file); +int load_split_cil(); int compile_split_cil(); -int dump_policydb(const char *filename); +int dump_policydb(const char *file); void destroy_policydb(); // Handy functions diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp new file mode 100644 index 000000000..c212ec464 --- /dev/null +++ b/native/jni/magiskpolicy/policydb.cpp @@ -0,0 +1,201 @@ +#include +#include +#include +#include + +#include + +#include +#include + +#include "magiskpolicy.h" +#include "sepolicy.h" + +policydb_t *policydb = nullptr; + +int load_policydb(const char *file) { + if (policydb) + destroy_policydb(); + + struct policy_file pf; + policy_file_init(&pf); + pf.fp = xfopen(file, "re"); + pf.type = PF_USE_STDIO; + + policydb = new policydb_t(); + if (policydb_init(policydb) || policydb_read(policydb, &pf, 0)) + return 1; + + fclose(pf.fp); + + return 0; +} + +#define SHALEN 64 +static bool cmp_sha256(const char *a, const char *b) { + char id_a[SHALEN] = {0}; + char id_b[SHALEN] = {0}; + if (int fd = xopen(a, O_RDONLY | O_CLOEXEC); fd >= 0) { + xread(fd, id_a, SHALEN); + close(fd); + } else { + return false; + } + + if (int fd = xopen(b, O_RDONLY | O_CLOEXEC); fd >= 0) { + xread(fd, id_b, SHALEN); + close(fd); + } else { + return false; + } + LOGD("%s=[%.*s]\n%s=[%.*s]\n", a, SHALEN, id_a, b, SHALEN, id_b); + return memcmp(id_a, id_b, SHALEN) == 0; +} + +static bool check_precompiled(const char *precompiled) { + bool ok = false; + const char *actual_sha; + char compiled_sha[128]; + + actual_sha = PLAT_POLICY_DIR "plat_and_mapping_sepolicy.cil.sha256"; + if (access(actual_sha, R_OK) == 0) { + ok = true; + sprintf(compiled_sha, "%s.plat_and_mapping.sha256", precompiled); + if (!cmp_sha256(actual_sha, compiled_sha)) + return false; + } + + actual_sha = PLAT_POLICY_DIR "plat_sepolicy_and_mapping.sha256"; + if (access(actual_sha, R_OK) == 0) { + ok = true; + sprintf(compiled_sha, "%s.plat_sepolicy_and_mapping.sha256", precompiled); + if (!cmp_sha256(actual_sha, compiled_sha)) + return false; + } + + actual_sha = PROD_POLICY_DIR "product_sepolicy_and_mapping.sha256"; + if (access(actual_sha, R_OK) == 0) { + ok = true; + sprintf(compiled_sha, "%s.product_sepolicy_and_mapping.sha256", precompiled); + if (!cmp_sha256(actual_sha, compiled_sha) != 0) + return false; + } + + return ok; +} + +int load_split_cil() { + const char *odm_pre = ODM_POLICY_DIR "precompiled_sepolicy"; + const char *vend_pre = VEND_POLICY_DIR "precompiled_sepolicy"; + if (access(odm_pre, R_OK) == 0 && check_precompiled(odm_pre)) + return load_policydb(odm_pre); + else if (access(vend_pre, R_OK) == 0 && check_precompiled(vend_pre)) + return load_policydb(vend_pre); + else + return compile_split_cil(); +} + +static void load_cil(struct cil_db *db, const char *file) { + char *addr; + size_t size; + mmap_ro(file, addr, size); + cil_add_file(db, (char *) file, addr, size); + LOGD("cil_add[%s]\n", file); + munmap(addr, size); +} + +int compile_split_cil() { + char path[128], plat_ver[10]; + struct cil_db *db = nullptr; + sepol_policydb_t *pdb = nullptr; + FILE *f; + int policy_ver; + const char *cil_file; + + cil_db_init(&db); + cil_set_mls(db, 1); + cil_set_multiple_decls(db, 1); + cil_set_disable_neverallow(db, 1); + cil_set_target_platform(db, SEPOL_TARGET_SELINUX); + cil_set_attrs_expand_generated(db, 0); + + f = xfopen(SELINUX_VERSION, "re"); + fscanf(f, "%d", &policy_ver); + fclose(f); + cil_set_policy_version(db, policy_ver); + + // Get mapping version + f = xfopen(VEND_POLICY_DIR "plat_sepolicy_vers.txt", "re"); + fscanf(f, "%s", plat_ver); + fclose(f); + + // plat + load_cil(db, SPLIT_PLAT_CIL); + + sprintf(path, PLAT_POLICY_DIR "mapping/%s.cil", plat_ver); + load_cil(db, path); + + // product + sprintf(path, PROD_POLICY_DIR "mapping/%s.cil", plat_ver); + if (access(path, R_OK) == 0) + load_cil(db, path); + + cil_file = PROD_POLICY_DIR "product_sepolicy.cil"; + if (access(cil_file, R_OK) == 0) + load_cil(db, cil_file); + + // vendor + cil_file = VEND_POLICY_DIR "nonplat_sepolicy.cil"; + if (access(cil_file, R_OK) == 0) + load_cil(db, cil_file); + + cil_file = VEND_POLICY_DIR "plat_pub_versioned.cil"; + if (access(cil_file, R_OK) == 0) + load_cil(db, cil_file); + + cil_file = VEND_POLICY_DIR "vendor_sepolicy.cil"; + if (access(cil_file, R_OK) == 0) + load_cil(db, cil_file); + + // odm + cil_file = ODM_POLICY_DIR "odm_sepolicy.cil"; + if (access(cil_file, R_OK) == 0) + load_cil(db, cil_file); + + if (cil_compile(db)) + return 1; + if (cil_build_policydb(db, &pdb)) + return 1; + + cil_db_destroy(&db); + policydb = &pdb->p; + return 0; +} + +int dump_policydb(const char *file) { + int fd, ret; + void *data = nullptr; + size_t len; + policydb_to_image(nullptr, policydb, &data, &len); + if (data == nullptr) { + LOGE("Fail to dump policy image!\n"); + return 1; + } + + fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd < 0) + return 1; + ret = xwrite(fd, data, len); + close(fd); + if (ret < 0) + return 1; + return 0; +} + +void destroy_policydb() { + if (policydb) { + policydb_destroy(policydb); + delete policydb; + policydb = nullptr; + } +} \ No newline at end of file diff --git a/native/jni/magiskpolicy/sepolicy.c b/native/jni/magiskpolicy/sepolicy.c index 3b3007d6a..ebf58ed73 100644 --- a/native/jni/magiskpolicy/sepolicy.c +++ b/native/jni/magiskpolicy/sepolicy.c @@ -1,23 +1,5 @@ -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include #include #include @@ -25,7 +7,6 @@ #include "magiskpolicy.h" #include "sepolicy.h" -policydb_t *policydb = NULL; extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p); static int get_attr(const char *type, int value) { @@ -235,129 +216,6 @@ static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum return ret; } -int load_policydb(const char *filename) { - struct policy_file pf; - void *map; - size_t size; - int ret; - - if (policydb) - destroy_policydb(); - - policydb = xcalloc(sizeof(*policydb), 1); - - mmap_ro(filename, &map, &size); - - policy_file_init(&pf); - pf.type = PF_USE_MEMORY; - pf.data = map; - pf.len = size; - if (policydb_init(policydb)) { - LOGE("policydb_init: Out of memory!\n"); - return 1; - } - ret = policydb_read(policydb, &pf, 0); - if (ret) { - LOGE("error(s) encountered while parsing configuration\n"); - return 1; - } - - munmap(map, size); - - return 0; -} - -int compile_split_cil() { - DIR *dir; - struct dirent *entry; - char path[128]; - - struct cil_db *db = NULL; - sepol_policydb_t *pdb = NULL; - void *addr; - size_t size; - - cil_db_init(&db); - cil_set_mls(db, 1); - cil_set_multiple_decls(db, 1); - cil_set_disable_neverallow(db, 1); - cil_set_target_platform(db, SEPOL_TARGET_SELINUX); - cil_set_policy_version(db, POLICYDB_VERSION_XPERMS_IOCTL); - cil_set_attrs_expand_generated(db, 0); - - // plat - mmap_ro(SPLIT_PLAT_CIL, &addr, &size); - if (cil_add_file(db, SPLIT_PLAT_CIL, addr, size)) - return 1; - LOGD("cil_add[%s]\n", SPLIT_PLAT_CIL); - munmap(addr, size); - - // mapping - char plat[10]; - int fd = open(SPLIT_NONPLAT_VER, O_RDONLY | O_CLOEXEC); - plat[read(fd, plat, sizeof(plat)) - 1] = '\0'; - sprintf(path, SPLIT_PLAT_MAPPING, plat); - mmap_ro(path, &addr, &size); - if (cil_add_file(db, path, addr, size)) - return 1; - LOGD("cil_add[%s]\n", path); - munmap(addr, size); - close(fd); - - // nonplat - dir = opendir(NONPLAT_POLICY_DIR); - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if (strend(entry->d_name, ".cil") == 0) { - sprintf(path, NONPLAT_POLICY_DIR "%s", entry->d_name); - mmap_ro(path, &addr, &size); - if (cil_add_file(db, path, addr, size)) - return 1; - LOGD("cil_add[%s]\n", path); - munmap(addr, size); - } - } - closedir(dir); - - if (cil_compile(db)) - return 1; - if (cil_build_policydb(db, &pdb)) - return 1; - - cil_db_destroy(&db); - policydb = &pdb->p; - return 0; -} - -int dump_policydb(const char *filename) { - int fd, ret; - void *data = NULL; - size_t len; - policydb_to_image(NULL, policydb, &data, &len); - if (data == NULL) { - LOGE("Fail to dump policy image!\n"); - return 1; - } - - fd = creat(filename, 0644); - if (fd < 0) { - LOGE("Can't open '%s': %s\n", filename, strerror(errno)); - return 1; - } - ret = xwrite(fd, data, len); - close(fd); - if (ret < 0) - return 1; - return 0; -} - -void destroy_policydb() { - policydb_destroy(policydb); - free(policydb); - policydb = NULL; -} - int create_domain(const char *d) { symtab_datum_t *src = hashtab_search(policydb->p_types.table, d); if(src) { diff --git a/native/jni/utils/include/selinux.h b/native/jni/utils/include/selinux.h index 2c29a383a..11801d813 100644 --- a/native/jni/utils/include/selinux.h +++ b/native/jni/utils/include/selinux.h @@ -1,5 +1,23 @@ #pragma once +// selinuxfs paths +#define SELINUX_MNT "/sys/fs/selinux" +#define SELINUX_ENFORCE SELINUX_MNT "/enforce" +#define SELINUX_POLICY SELINUX_MNT "/policy" +#define SELINUX_LOAD SELINUX_MNT "/load" +#define SELINUX_CONTEXT SELINUX_MNT "/context" +#define SELINUX_VERSION SELINUX_MNT "/policyvers" + +// sepolicy paths +#define PLAT_POLICY_DIR "/system/etc/selinux/" +#define VEND_POLICY_DIR "/vendor/etc/selinux/" +#define PROD_POLICY_DIR "/product/etc/selinux/" +#define ODM_POLICY_DIR "/odm/etc/selinux/" +#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil" + +#define SEPOL_PROC_DOMAIN "magisk" +#define SEPOL_FILE_DOMAIN "magisk_file" + #ifdef __cplusplus extern "C" { #endif