From 94c2fc80d2b6c09b60e465216cce1b597c7bcf50 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 27 May 2017 02:40:12 +0800 Subject: [PATCH] Add multiuser support --- activity.c | 115 ++++++++++------------------------------------------- su.c | 53 ++++++++++++------------ su.h | 91 ++++++++++++++++++------------------------ 3 files changed, 87 insertions(+), 172 deletions(-) diff --git a/activity.c b/activity.c index 45c337993..800e371ef 100644 --- a/activity.c +++ b/activity.c @@ -26,15 +26,8 @@ static void silent_run(char* const args[]) { set_identity(0); - pid_t pid; - pid = fork(); - /* Parent */ - if (pid < 0) { - PLOGE("fork"); - } - else if (pid > 0) { + if (fork()) return; - } int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC); dup2(zero, 0); int null = open("/dev/null", O_WRONLY | O_CLOEXEC); @@ -46,79 +39,37 @@ static void silent_run(char* const args[]) { _exit(EXIT_FAILURE); } -static int get_owner_login_user_args(struct su_context *ctx, char* user, int user_len) { - int needs_owner_login_prompt = 0; - - if (ctx->user.multiuser_mode == MULTIUSER_MODE_OWNER_MANAGED) { - if (0 != ctx->user.android_user_id) { - needs_owner_login_prompt = 1; - } - snprintf(user, user_len, "0"); +static void setup_user(struct su_context *ctx, char* user) { + switch (ctx->user.multiuser_mode) { + case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */ + case MULTIUSER_MODE_OWNER_MANAGED: + sprintf(user, "%d", 0); + break; + case MULTIUSER_MODE_USER: + sprintf(user, "%d", ctx->user.android_user_id); + break; } - else if (ctx->user.multiuser_mode == MULTIUSER_MODE_USER) { - snprintf(user, user_len, "%d", ctx->user.android_user_id); - } - else if (ctx->user.multiuser_mode == MULTIUSER_MODE_NONE) { - user[0] = '\0'; - } - else { - snprintf(user, user_len, "0"); - } - - return needs_owner_login_prompt; } void app_send_result(struct su_context *ctx, policy_t policy) { - // char binary_version[256]; - // sprintf(binary_version, "%d", VERSION_CODE); + char fromUid[16]; + sprintf(fromUid, "%d", ctx->from.uid); - char uid[256]; - sprintf(uid, "%d", ctx->from.uid); - - char toUid[256]; + char toUid[16]; sprintf(toUid, "%d", ctx->to.uid); - char pid[256]; + char pid[16]; sprintf(pid, "%d", ctx->from.pid); - char user[64]; - get_owner_login_user_args(ctx, user, sizeof(user)); - - if (0 != ctx->user.android_user_id) { - char android_user_id[256]; - sprintf(android_user_id, "%d", ctx->user.android_user_id); - - char *user_result_command[] = { - AM_PATH, - ACTION_RESULT, - "--ei", - "from.uid", - uid, - "--ei", - "to.uid", - toUid, - "--ei", - "pid", - pid, - "--es", - "command", - get_command(&ctx->to), - "--es", - "action", - policy == ALLOW ? "allow" : "deny", - user[0] ? "--user" : NULL, - android_user_id, - NULL - }; - silent_run(user_result_command); - } + char user[16]; + setup_user(ctx, user); char *result_command[] = { AM_PATH, ACTION_RESULT, "--ei", "from.uid", - uid, + fromUid, "--ei", "to.uid", toUid, @@ -131,7 +82,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) { "--es", "action", policy == ALLOW ? "allow" : "deny", - user[0] ? "--user" : NULL, + "--user", user, NULL }; @@ -139,34 +90,8 @@ void app_send_result(struct su_context *ctx, policy_t policy) { } void app_send_request(struct su_context *ctx) { - // if su is operating in MULTIUSER_MODEL_OWNER, - // and the user requestor is not the owner, - // the owner needs to be notified of the request. - // so there will be two activities shown. char user[64]; - int needs_owner_login_prompt = get_owner_login_user_args(ctx, user, sizeof(user)); - - if (needs_owner_login_prompt) { - char uid[256]; - sprintf(uid, "%d", ctx->from.uid); - - char android_user_id[256]; - sprintf(android_user_id, "%d", ctx->user.android_user_id); - - // in multiuser mode, the owner gets the su prompt - char *notify_command[] = { - AM_PATH, - ACTION_NOTIFY, - "--ei", - "caller_uid", - uid, - "--user", - android_user_id, - NULL - }; - - silent_run(notify_command); - } + setup_user(ctx, user); char *request_command[] = { AM_PATH, @@ -174,7 +99,7 @@ void app_send_request(struct su_context *ctx) { "--es", "socket", ctx->sock_path, - user[0] ? "--user" : NULL, + "--user", user, NULL }; diff --git a/su.c b/su.c index 4068fbce8..1efcaa7a5 100644 --- a/su.c +++ b/su.c @@ -62,8 +62,13 @@ static char *concat_commands(int argc, char *argv[]) { } static int get_multiuser_mode() { - // TODO: Multiuser support - return MULTIUSER_MODE_NONE; + char *prop = getprop(MULTIUSER_MODE_PROP); + if (prop) { + int ret = atoi(prop); + free(prop); + return ret; + } + return MULTIUSER_MODE_OWNER_ONLY; } static void populate_environment(const struct su_context *ctx) { @@ -212,8 +217,8 @@ int su_daemon_main(int argc, char **argv) { .user = { .android_user_id = 0, .multiuser_mode = get_multiuser_mode(), - .database_path = APPLICATION_DATA_PATH REQUESTOR_DATABASE_PATH, - .base_path = APPLICATION_DATA_PATH REQUESTOR + .database_path = APP_DATA_PATH REQUESTOR_DATABASE_PATH, + .base_path = APP_DATA_PATH REQUESTOR }, .umask = 022, }; @@ -262,16 +267,13 @@ int su_daemon_main(int argc, char **argv) { case 'u': switch (ctx.user.multiuser_mode) { case MULTIUSER_MODE_USER: - printf("%s\n", MULTIUSER_VALUE_USER); + printf("Owner only: Only owner has root access\n"); break; case MULTIUSER_MODE_OWNER_MANAGED: - printf("%s\n", MULTIUSER_VALUE_OWNER_MANAGED); + printf("Owner managed: Only owner can manage root access and receive request prompts\n"); break; case MULTIUSER_MODE_OWNER_ONLY: - printf("%s\n", MULTIUSER_VALUE_OWNER_ONLY); - break; - case MULTIUSER_MODE_NONE: - printf("%s\n", MULTIUSER_VALUE_NONE); + printf("User independent: The user has its own separate root rules\n"); break; } exit(EXIT_SUCCESS); @@ -318,15 +320,20 @@ int su_daemon_main(int argc, char **argv) { // It's in multiuser mode if (ctx.from.uid > 99999) { ctx.user.android_user_id = ctx.from.uid / 100000; - if (ctx.user.multiuser_mode == MULTIUSER_MODE_USER) { + ctx.from.uid %= 100000; + switch (ctx.user.multiuser_mode) { + case MULTIUSER_MODE_OWNER_ONLY: + deny(); + case MULTIUSER_MODE_USER: snprintf(ctx.user.database_path, PATH_MAX, "%s/%d/%s", USER_DATA_PATH, ctx.user.android_user_id, REQUESTOR_DATABASE_PATH); snprintf(ctx.user.base_path, PATH_MAX, "%s/%d/%s", USER_DATA_PATH, ctx.user.android_user_id, REQUESTOR); + break; } } - // verify superuser is installed + // verify if Magisk Manager is installed xstat(ctx.user.base_path, &st); // odd perms on superuser data dir @@ -336,12 +343,6 @@ int su_daemon_main(int argc, char **argv) { deny(); } - // always allow if this is the superuser uid - // superuser needs to be able to reenable itself when disabled... - if (ctx.from.uid == st.st_uid) { - allow(); - } - // Check property of root configuration char *root_prop = getprop(ROOT_ACCESS_PROP); if (root_prop) { @@ -361,21 +362,23 @@ int su_daemon_main(int argc, char **argv) { default: break; } + free(root_prop); } else { - exit(EXIT_FAILURE); + // Not initialized yet, set the prop to allow everything by default + setprop(ROOT_ACCESS_PROP, xstr(ROOT_ACCESS_APPS_AND_ADB)); + } + + // always allow if this is the superuser uid + // superuser needs to be able to reenable itself when disabled... + if (ctx.from.uid == st.st_uid) { + allow(); } - free(root_prop); // Allow root to start root if (ctx.from.uid == UID_ROOT) { allow(); } - // deny if this is a non owner request and owner mode only - if (ctx.user.multiuser_mode == MULTIUSER_MODE_OWNER_ONLY && ctx.user.android_user_id != 0) { - deny(); - } - mkdir(REQUESTOR_CACHE_PATH, 0770); if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) { PLOGE("chown (%s, %u, %u)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid); diff --git a/su.h b/su.h index 995e47ebe..e4edad3a8 100644 --- a/su.h +++ b/su.h @@ -10,16 +10,22 @@ #define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)" // Property check for root access -#define ROOT_ACCESS_PROP "persist.sys.root_access" +#define ROOT_ACCESS_PROP "persist.magisk.root" #define ROOT_ACCESS_DISABLED 0 #define ROOT_ACCESS_APPS_ONLY 1 #define ROOT_ACCESS_ADB_ONLY 2 #define ROOT_ACCESS_APPS_AND_ADB 3 +// Property for multiuser +#define MULTIUSER_MODE_PROP "persist.magisk.multiuser" +#define MULTIUSER_MODE_OWNER_ONLY 0 +#define MULTIUSER_MODE_OWNER_MANAGED 1 +#define MULTIUSER_MODE_USER 2 + // DO NOT CHANGE LINE BELOW, java package name will always be the same #define JAVA_PACKAGE_NAME "com.topjohnwu.magisk" -#define APPLICATION_DATA_PATH "/data/data/" +#define APP_DATA_PATH "/data/data/" #define USER_DATA_PATH "/data/user" // If --rename-manifest-package is used in AAPT, this @@ -29,77 +35,58 @@ // This is used if wrapping the fragment classes and activities // with classes in another package. #define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser" -#define REQUESTOR_FILES_PATH APPLICATION_DATA_PATH REQUESTOR "/files" #define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR - // there's no guarantee that the db or files are actually created named as such by // SQLiteOpenHelper, etc. Though that is the behavior as of current. // it is up to the Android application to symlink as appropriate. #define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.db" -#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode" #define DEFAULT_SHELL "/system/bin/sh" struct su_initiator { - pid_t pid; - unsigned uid; + pid_t pid; + unsigned uid; }; struct su_request { - unsigned uid; - int login; - int keepenv; - char *shell; - char *command; - char **argv; - int argc; + unsigned uid; + int login; + int keepenv; + char *shell; + char *command; + char **argv; + int argc; }; struct su_user_info { - // the user in android userspace (multiuser) - // that invoked this action. - unsigned android_user_id; - // how su behaves with multiuser. see enum below. - int multiuser_mode; - // path to superuser directory. this is populated according - // to the multiuser mode. - // this is used to check uid/gid for protecting socket. - // this is used instead of database, as it is more likely - // to exist. db will not exist if su has never launched. - char base_path[PATH_MAX]; - // path to su database. this is populated according - // to the multiuser mode. - char database_path[PATH_MAX]; + // the user in android userspace (multiuser) + // that invoked this action. + unsigned android_user_id; + // how su behaves with multiuser. see enum below. + int multiuser_mode; + // path to superuser directory. this is populated according + // to the multiuser mode. + // this is used to check uid/gid for protecting socket. + // this is used instead of database, as it is more likely + // to exist. db will not exist if su has never launched. + char base_path[PATH_MAX]; + // path to su database. this is populated according + // to the multiuser mode. + char database_path[PATH_MAX]; }; struct su_context { - struct su_initiator from; - struct su_request to; - struct su_user_info user; - mode_t umask; - char sock_path[PATH_MAX]; + struct su_initiator from; + struct su_request to; + struct su_user_info user; + mode_t umask; + char sock_path[PATH_MAX]; }; -// multiuser su behavior typedef enum { - // only owner can su - MULTIUSER_MODE_OWNER_ONLY = 0, - // owner gets a su prompt - MULTIUSER_MODE_OWNER_MANAGED = 1, - // user gets a su prompt - MULTIUSER_MODE_USER = 2, - MULTIUSER_MODE_NONE = 3, -} multiuser_mode_t; - -#define MULTIUSER_VALUE_OWNER_ONLY "owner" -#define MULTIUSER_VALUE_OWNER_MANAGED "managed" -#define MULTIUSER_VALUE_USER "user" -#define MULTIUSER_VALUE_NONE "none" - -typedef enum { - INTERACTIVE = 0, - DENY = 1, - ALLOW = 2, + INTERACTIVE = 0, + DENY = 1, + ALLOW = 2, } policy_t; extern struct ucred su_credentials;