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[]) {
|
||||
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
53
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);
|
||||
|
91
su.h
91
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;
|
||||
|
Loading…
Reference in New Issue
Block a user