mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-24 18:45:28 +00:00
Add multiuser support
This commit is contained in:
parent
371db886b4
commit
94c2fc80d2
115
activity.c
115
activity.c
@ -26,15 +26,8 @@
|
|||||||
|
|
||||||
static void silent_run(char* const args[]) {
|
static void silent_run(char* const args[]) {
|
||||||
set_identity(0);
|
set_identity(0);
|
||||||
pid_t pid;
|
if (fork())
|
||||||
pid = fork();
|
|
||||||
/* Parent */
|
|
||||||
if (pid < 0) {
|
|
||||||
PLOGE("fork");
|
|
||||||
}
|
|
||||||
else if (pid > 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
|
int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
|
||||||
dup2(zero, 0);
|
dup2(zero, 0);
|
||||||
int null = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
int null = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||||
@ -46,79 +39,37 @@ static void silent_run(char* const args[]) {
|
|||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_owner_login_user_args(struct su_context *ctx, char* user, int user_len) {
|
static void setup_user(struct su_context *ctx, char* user) {
|
||||||
int needs_owner_login_prompt = 0;
|
switch (ctx->user.multiuser_mode) {
|
||||||
|
case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */
|
||||||
if (ctx->user.multiuser_mode == MULTIUSER_MODE_OWNER_MANAGED) {
|
case MULTIUSER_MODE_OWNER_MANAGED:
|
||||||
if (0 != ctx->user.android_user_id) {
|
sprintf(user, "%d", 0);
|
||||||
needs_owner_login_prompt = 1;
|
break;
|
||||||
}
|
case MULTIUSER_MODE_USER:
|
||||||
snprintf(user, user_len, "0");
|
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) {
|
void app_send_result(struct su_context *ctx, policy_t policy) {
|
||||||
// char binary_version[256];
|
char fromUid[16];
|
||||||
// sprintf(binary_version, "%d", VERSION_CODE);
|
sprintf(fromUid, "%d", ctx->from.uid);
|
||||||
|
|
||||||
char uid[256];
|
char toUid[16];
|
||||||
sprintf(uid, "%d", ctx->from.uid);
|
|
||||||
|
|
||||||
char toUid[256];
|
|
||||||
sprintf(toUid, "%d", ctx->to.uid);
|
sprintf(toUid, "%d", ctx->to.uid);
|
||||||
|
|
||||||
char pid[256];
|
char pid[16];
|
||||||
sprintf(pid, "%d", ctx->from.pid);
|
sprintf(pid, "%d", ctx->from.pid);
|
||||||
|
|
||||||
char user[64];
|
char user[16];
|
||||||
get_owner_login_user_args(ctx, user, sizeof(user));
|
setup_user(ctx, 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 *result_command[] = {
|
char *result_command[] = {
|
||||||
AM_PATH,
|
AM_PATH,
|
||||||
ACTION_RESULT,
|
ACTION_RESULT,
|
||||||
"--ei",
|
"--ei",
|
||||||
"from.uid",
|
"from.uid",
|
||||||
uid,
|
fromUid,
|
||||||
"--ei",
|
"--ei",
|
||||||
"to.uid",
|
"to.uid",
|
||||||
toUid,
|
toUid,
|
||||||
@ -131,7 +82,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
|
|||||||
"--es",
|
"--es",
|
||||||
"action",
|
"action",
|
||||||
policy == ALLOW ? "allow" : "deny",
|
policy == ALLOW ? "allow" : "deny",
|
||||||
user[0] ? "--user" : NULL,
|
"--user",
|
||||||
user,
|
user,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
@ -139,34 +90,8 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void app_send_request(struct su_context *ctx) {
|
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];
|
char user[64];
|
||||||
int needs_owner_login_prompt = get_owner_login_user_args(ctx, user, sizeof(user));
|
setup_user(ctx, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *request_command[] = {
|
char *request_command[] = {
|
||||||
AM_PATH,
|
AM_PATH,
|
||||||
@ -174,7 +99,7 @@ void app_send_request(struct su_context *ctx) {
|
|||||||
"--es",
|
"--es",
|
||||||
"socket",
|
"socket",
|
||||||
ctx->sock_path,
|
ctx->sock_path,
|
||||||
user[0] ? "--user" : NULL,
|
"--user",
|
||||||
user,
|
user,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
53
su.c
53
su.c
@ -62,8 +62,13 @@ static char *concat_commands(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int get_multiuser_mode() {
|
static int get_multiuser_mode() {
|
||||||
// TODO: Multiuser support
|
char *prop = getprop(MULTIUSER_MODE_PROP);
|
||||||
return MULTIUSER_MODE_NONE;
|
if (prop) {
|
||||||
|
int ret = atoi(prop);
|
||||||
|
free(prop);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return MULTIUSER_MODE_OWNER_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void populate_environment(const struct su_context *ctx) {
|
static void populate_environment(const struct su_context *ctx) {
|
||||||
@ -212,8 +217,8 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
.user = {
|
.user = {
|
||||||
.android_user_id = 0,
|
.android_user_id = 0,
|
||||||
.multiuser_mode = get_multiuser_mode(),
|
.multiuser_mode = get_multiuser_mode(),
|
||||||
.database_path = APPLICATION_DATA_PATH REQUESTOR_DATABASE_PATH,
|
.database_path = APP_DATA_PATH REQUESTOR_DATABASE_PATH,
|
||||||
.base_path = APPLICATION_DATA_PATH REQUESTOR
|
.base_path = APP_DATA_PATH REQUESTOR
|
||||||
},
|
},
|
||||||
.umask = 022,
|
.umask = 022,
|
||||||
};
|
};
|
||||||
@ -262,16 +267,13 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
case 'u':
|
case 'u':
|
||||||
switch (ctx.user.multiuser_mode) {
|
switch (ctx.user.multiuser_mode) {
|
||||||
case MULTIUSER_MODE_USER:
|
case MULTIUSER_MODE_USER:
|
||||||
printf("%s\n", MULTIUSER_VALUE_USER);
|
printf("Owner only: Only owner has root access\n");
|
||||||
break;
|
break;
|
||||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
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;
|
break;
|
||||||
case MULTIUSER_MODE_OWNER_ONLY:
|
case MULTIUSER_MODE_OWNER_ONLY:
|
||||||
printf("%s\n", MULTIUSER_VALUE_OWNER_ONLY);
|
printf("User independent: The user has its own separate root rules\n");
|
||||||
break;
|
|
||||||
case MULTIUSER_MODE_NONE:
|
|
||||||
printf("%s\n", MULTIUSER_VALUE_NONE);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -318,15 +320,20 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
// It's in multiuser mode
|
// It's in multiuser mode
|
||||||
if (ctx.from.uid > 99999) {
|
if (ctx.from.uid > 99999) {
|
||||||
ctx.user.android_user_id = ctx.from.uid / 100000;
|
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",
|
snprintf(ctx.user.database_path, PATH_MAX, "%s/%d/%s",
|
||||||
USER_DATA_PATH, ctx.user.android_user_id, REQUESTOR_DATABASE_PATH);
|
USER_DATA_PATH, ctx.user.android_user_id, REQUESTOR_DATABASE_PATH);
|
||||||
snprintf(ctx.user.base_path, PATH_MAX, "%s/%d/%s",
|
snprintf(ctx.user.base_path, PATH_MAX, "%s/%d/%s",
|
||||||
USER_DATA_PATH, ctx.user.android_user_id, REQUESTOR);
|
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);
|
xstat(ctx.user.base_path, &st);
|
||||||
|
|
||||||
// odd perms on superuser data dir
|
// odd perms on superuser data dir
|
||||||
@ -336,12 +343,6 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
deny();
|
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
|
// Check property of root configuration
|
||||||
char *root_prop = getprop(ROOT_ACCESS_PROP);
|
char *root_prop = getprop(ROOT_ACCESS_PROP);
|
||||||
if (root_prop) {
|
if (root_prop) {
|
||||||
@ -361,21 +362,23 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
free(root_prop);
|
||||||
} else {
|
} 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
|
// Allow root to start root
|
||||||
if (ctx.from.uid == UID_ROOT) {
|
if (ctx.from.uid == UID_ROOT) {
|
||||||
allow();
|
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);
|
mkdir(REQUESTOR_CACHE_PATH, 0770);
|
||||||
if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) {
|
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);
|
PLOGE("chown (%s, %u, %u)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid);
|
||||||
|
91
su.h
91
su.h
@ -10,16 +10,22 @@
|
|||||||
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
|
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
|
||||||
|
|
||||||
// Property check for root access
|
// 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_DISABLED 0
|
||||||
#define ROOT_ACCESS_APPS_ONLY 1
|
#define ROOT_ACCESS_APPS_ONLY 1
|
||||||
#define ROOT_ACCESS_ADB_ONLY 2
|
#define ROOT_ACCESS_ADB_ONLY 2
|
||||||
#define ROOT_ACCESS_APPS_AND_ADB 3
|
#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
|
// DO NOT CHANGE LINE BELOW, java package name will always be the same
|
||||||
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
|
#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"
|
#define USER_DATA_PATH "/data/user"
|
||||||
|
|
||||||
// If --rename-manifest-package is used in AAPT, this
|
// If --rename-manifest-package is used in AAPT, this
|
||||||
@ -29,77 +35,58 @@
|
|||||||
// This is used if wrapping the fragment classes and activities
|
// This is used if wrapping the fragment classes and activities
|
||||||
// with classes in another package.
|
// with classes in another package.
|
||||||
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser"
|
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser"
|
||||||
#define REQUESTOR_FILES_PATH APPLICATION_DATA_PATH REQUESTOR "/files"
|
|
||||||
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
|
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
|
||||||
|
|
||||||
// there's no guarantee that the db or files are actually created named as such by
|
// 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.
|
// SQLiteOpenHelper, etc. Though that is the behavior as of current.
|
||||||
// it is up to the Android application to symlink as appropriate.
|
// it is up to the Android application to symlink as appropriate.
|
||||||
#define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.db"
|
#define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.db"
|
||||||
#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode"
|
|
||||||
|
|
||||||
#define DEFAULT_SHELL "/system/bin/sh"
|
#define DEFAULT_SHELL "/system/bin/sh"
|
||||||
|
|
||||||
struct su_initiator {
|
struct su_initiator {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
unsigned uid;
|
unsigned uid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct su_request {
|
struct su_request {
|
||||||
unsigned uid;
|
unsigned uid;
|
||||||
int login;
|
int login;
|
||||||
int keepenv;
|
int keepenv;
|
||||||
char *shell;
|
char *shell;
|
||||||
char *command;
|
char *command;
|
||||||
char **argv;
|
char **argv;
|
||||||
int argc;
|
int argc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct su_user_info {
|
struct su_user_info {
|
||||||
// the user in android userspace (multiuser)
|
// the user in android userspace (multiuser)
|
||||||
// that invoked this action.
|
// that invoked this action.
|
||||||
unsigned android_user_id;
|
unsigned android_user_id;
|
||||||
// how su behaves with multiuser. see enum below.
|
// how su behaves with multiuser. see enum below.
|
||||||
int multiuser_mode;
|
int multiuser_mode;
|
||||||
// path to superuser directory. this is populated according
|
// path to superuser directory. this is populated according
|
||||||
// to the multiuser mode.
|
// to the multiuser mode.
|
||||||
// this is used to check uid/gid for protecting socket.
|
// this is used to check uid/gid for protecting socket.
|
||||||
// this is used instead of database, as it is more likely
|
// this is used instead of database, as it is more likely
|
||||||
// to exist. db will not exist if su has never launched.
|
// to exist. db will not exist if su has never launched.
|
||||||
char base_path[PATH_MAX];
|
char base_path[PATH_MAX];
|
||||||
// path to su database. this is populated according
|
// path to su database. this is populated according
|
||||||
// to the multiuser mode.
|
// to the multiuser mode.
|
||||||
char database_path[PATH_MAX];
|
char database_path[PATH_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct su_context {
|
struct su_context {
|
||||||
struct su_initiator from;
|
struct su_initiator from;
|
||||||
struct su_request to;
|
struct su_request to;
|
||||||
struct su_user_info user;
|
struct su_user_info user;
|
||||||
mode_t umask;
|
mode_t umask;
|
||||||
char sock_path[PATH_MAX];
|
char sock_path[PATH_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
// multiuser su behavior
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// only owner can su
|
INTERACTIVE = 0,
|
||||||
MULTIUSER_MODE_OWNER_ONLY = 0,
|
DENY = 1,
|
||||||
// owner gets a su prompt
|
ALLOW = 2,
|
||||||
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,
|
|
||||||
} policy_t;
|
} policy_t;
|
||||||
|
|
||||||
extern struct ucred su_credentials;
|
extern struct ucred su_credentials;
|
||||||
|
Loading…
Reference in New Issue
Block a user