Add multiuser support

This commit is contained in:
topjohnwu 2017-05-27 02:40:12 +08:00
parent 371db886b4
commit 94c2fc80d2
3 changed files with 87 additions and 172 deletions

View File

@ -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
};

53
su.c
View File

@ -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);

91
su.h
View File

@ -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;