Support changing requester in DB

This commit is contained in:
topjohnwu 2017-10-28 16:03:39 +08:00
parent 6b7b71b1f8
commit 84ca8e1f3e
6 changed files with 147 additions and 169 deletions

View File

@ -18,9 +18,8 @@
#include "su.h"
/* intent actions */
#define ACTION_REQUEST "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".RequestActivity"
#define ACTION_NOTIFY "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".NotifyActivity"
#define ACTION_RESULT "broadcast", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".SuReceiver"
#define ACTION_REQUEST "%s/" REQUESTOR_PREFIX ".RequestActivity"
#define ACTION_RESULT "%s/" REQUESTOR_PREFIX ".SuReceiver"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
@ -73,30 +72,20 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
char user[16];
int notify = setup_user(ctx, user);
char activity[128];
sprintf(activity, ACTION_RESULT, ctx->path.pkg_name);
// Send notice to manager, enable logging
char *result_command[] = {
AM_PATH,
ACTION_RESULT,
"--user",
user,
"--ei",
"mode",
"0",
"--ei",
"from.uid",
fromUid,
"--ei",
"to.uid",
toUid,
"--ei",
"pid",
pid,
"--es",
"command",
get_command(&ctx->to),
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
AM_PATH, "broadcast", "-n",
activity,
"--user", user,
"--ei", "mode", "0",
"--ei", "from.uid", fromUid,
"--ei", "to.uid", toUid,
"--ei", "pid", pid,
"--es", "command", get_command(&ctx->to),
"--es", "action", policy == ALLOW ? "allow" : "deny",
NULL
};
silent_run(result_command);
@ -105,19 +94,12 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
if (notify) {
sprintf(user, "%d", notify);
char *notify_command[] = {
AM_PATH,
ACTION_RESULT,
"--user",
user,
"--ei",
"mode",
"1",
"--ei",
"from.uid",
fromUid,
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
AM_PATH, "broadcast", "-n",
activity,
"--user", user,
"--ei", "mode", "1",
"--ei", "from.uid", fromUid,
"--es", "action", policy == ALLOW ? "allow" : "deny",
NULL
};
silent_run(notify_command);
@ -128,17 +110,15 @@ void app_send_request(struct su_context *ctx) {
char user[16];
int notify = setup_user(ctx, user);
char activity[128];
sprintf(activity, ACTION_REQUEST, ctx->path.pkg_name);
char *request_command[] = {
AM_PATH,
ACTION_REQUEST,
"--user",
user,
"--es",
"socket",
ctx->path.sock_path,
"--ez",
"timeout",
notify ? "false" : "true",
AM_PATH, "start", "-n",
activity,
"--user", user,
"--es", "socket", ctx->path.sock_path,
"--ez", "timeout", notify ? "false" : "true",
NULL
};
silent_run(request_command);
@ -146,14 +126,12 @@ void app_send_request(struct su_context *ctx) {
// Send notice to user to tell them root is managed by owner
if (notify) {
sprintf(user, "%d", notify);
sprintf(activity, ACTION_RESULT, ctx->path.pkg_name);
char *notify_command[] = {
AM_PATH,
ACTION_RESULT,
"--user",
user,
"--ei",
"mode",
"2",
AM_PATH, "broadcast", "-n",
activity,
"--user", user,
"--ei", "mode", "2",
NULL
};
silent_run(notify_command);

95
db.c
View File

@ -58,60 +58,109 @@ static int settings_callback(void *v, int argc, char **argv, char **azColName) {
return 0;
}
static int strings_callback(void *v, int argc, char **argv, char **azColName) {
struct su_context *ctx = (struct su_context *) v;
char *entry, *target, *value;
for (int i = 0; i < argc; ++i) {
if (strcmp(azColName[i], "key") == 0) {
if (strcmp(argv[i], REQUESTER_ENTRY) == 0)
target = ctx->path.pkg_name;
entry = argv[i];
} else if (strcmp(azColName[i], "value") == 0) {
value = argv[i];
}
}
LOGD("su_db: query %s=[%s]\n", entry, value);
strcpy(target, value);
return 0;
}
#define BASE_FMT "/data/user%s/%d"
#define USE_MULTI(info) (info->uid / 100000 != 0 && info->multiuser_mode == MULTIUSER_MODE_USER)
void database_check(struct su_context *ctx) {
sqlite3 *db = NULL;
int ret;
char query[512], *err = NULL;
char buffer[PATH_MAX], *err = NULL;
// Set default values
ctx->info->root_access = ROOT_ACCESS_APPS_AND_ADB;
ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY;
ctx->info->mnt_ns = NAMESPACE_MODE_REQUESTER;
ctx->info->policy = QUERY;
strcpy(ctx->path.pkg_name, "???"); /* bad string so it doesn't exist */
// Populate paths
sprintf(ctx->path.base_path, BASE_FMT, "_de", 0);
if (access(ctx->path.base_path, R_OK) == -1)
sprintf(ctx->path.base_path, BASE_FMT, "", 0);
sprintf(ctx->path.multiuser_path, BASE_FMT, "_de", ctx->info->uid / 100000);
if (access(ctx->path.multiuser_path, R_OK) == -1)
sprintf(ctx->path.multiuser_path, BASE_FMT, "", ctx->info->uid / 100000);
// Open database
ret = sqlite3_open_v2(ctx->path.base_db, &db, SQLITE_OPEN_READONLY, NULL);
sprintf(buffer, "%s/magisk.db", ctx->path.base_path);
LOGD("su_db: open %s", buffer);
ret = sqlite3_open_v2(buffer, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) {
LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
return;
goto stat_requester;
}
// Check multiuser mode settings
snprintf(query, sizeof(query), "SELECT key, value FROM settings WHERE key='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, query, settings_callback, ctx, &err);
sprintf(buffer, "SELECT key, value FROM settings WHERE key='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, buffer, settings_callback, ctx, &err);
if (err != NULL)
LOGE("sqlite3_exec: %s\n", err);
err = NULL;
if (ctx->info->uid / 100000 != 0 && ctx->info->multiuser_mode == MULTIUSER_MODE_USER) {
// Open database based on multiuser settings
if (USE_MULTI(ctx->info)) {
sqlite3_close(db);
// Open database
ret = sqlite3_open_v2(ctx->path.multiuser_db, &db, SQLITE_OPEN_READONLY, NULL);
sprintf(buffer, "%s/magisk.db", ctx->path.multiuser_path);
LOGD("su_db: open %s", buffer);
ret = sqlite3_open_v2(buffer, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) {
LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
sqlite3_close(db);
return;
goto stat_requester;
}
}
// Query for policy
snprintf(query, sizeof(query), "SELECT policy, until FROM policies WHERE uid=%d", ctx->info->uid % 100000);
sqlite3_exec(db, query, policy_callback, ctx, &err);
if (err != NULL) {
// Read PKG name from DB
strcpy(buffer, "SELECT key, value FROM strings");
sqlite3_exec(db, buffer, strings_callback, ctx, &err);
if (err != NULL)
LOGE("sqlite3_exec: %s\n", err);
return;
}
err = NULL;
// Query for policy
sprintf(buffer, "SELECT policy, until FROM policies WHERE uid=%d", ctx->info->uid % 100000);
sqlite3_exec(db, buffer, policy_callback, ctx, &err);
if (err != NULL)
LOGE("sqlite3_exec: %s\n", err);
err = NULL;
// Query for settings
snprintf(query, sizeof(query), "SELECT key, value FROM settings WHERE key!='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, query, settings_callback, ctx, &err);
if (err != NULL) {
sprintf(buffer, "SELECT key, value FROM settings WHERE key!='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, buffer, settings_callback, ctx, &err);
if (err != NULL)
LOGE("sqlite3_exec: %s\n", err);
return;
}
sqlite3_close(db);
stat_requester:
// We prefer the orignal name
sprintf(buffer, "%s/%s", USE_MULTI(ctx->info) ? ctx->path.multiuser_path : ctx->path.base_path, JAVA_PACKAGE_NAME);
if (stat(buffer, &ctx->st) == -1) {
sprintf(buffer, "%s/%s", USE_MULTI(ctx->info) ? ctx->path.multiuser_path : ctx->path.base_path, ctx->path.pkg_name);
if (stat(buffer, &ctx->st) == -1) {
LOGE("su: cannot find requester");
ctx->info->policy = DENY;
ctx->notify = 0;
}
} else {
strcpy(ctx->path.pkg_name, JAVA_PACKAGE_NAME);
}
}

25
su.c
View File

@ -162,9 +162,9 @@ static void cleanup_signal(int sig) {
__attribute__ ((noreturn)) void exit2(int status) {
// Handle the pipe, or the daemon will get stuck
if (su_ctx->info->policy == QUERY) {
xwrite(pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(pipefd[0]);
close(pipefd[1]);
xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(su_ctx->pipefd[0]);
close(su_ctx->pipefd[1]);
}
exit(status);
}
@ -352,19 +352,6 @@ int su_daemon_main(int argc, char **argv) {
// New request or no db exist, notify user for response
if (su_ctx->info->policy == QUERY) {
mkdir(REQUESTOR_CACHE_PATH, 0770);
if (chown(REQUESTOR_CACHE_PATH, su_ctx->st.st_uid, su_ctx->st.st_gid))
PLOGE("chown (%s, %u, %u)", REQUESTOR_CACHE_PATH, su_ctx->st.st_uid, su_ctx->st.st_gid);
if (setgroups(0, NULL))
PLOGE("setgroups");
if (setegid(su_ctx->st.st_gid))
PLOGE("setegid (%u)", su_ctx->st.st_gid);
if (seteuid(su_ctx->st.st_uid))
PLOGE("seteuid (%u)", su_ctx->st.st_uid);
socket_serv_fd = socket_create_temp(su_ctx->path.sock_path, sizeof(su_ctx->path.sock_path));
setup_sighandlers(cleanup_signal);
@ -387,9 +374,9 @@ int su_daemon_main(int argc, char **argv) {
su_ctx->info->policy = DENY;
// Report the policy to main daemon
xwrite(pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(pipefd[0]);
close(pipefd[1]);
xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(su_ctx->pipefd[0]);
close(su_ctx->pipefd[1]);
}
if (su_ctx->info->policy == ALLOW)

14
su.h
View File

@ -31,17 +31,14 @@
#define NAMESPACE_MODE_REQUESTER 1
#define NAMESPACE_MODE_ISOLATE 2
// DB entry for requester
#define REQUESTER_ENTRY "requester"
// DO NOT CHANGE LINE BELOW, java package name will always be the same
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"
// If --rename-manifest-package is used in AAPT, this
// must be changed to correspond to the new APK package name
// See the two Android.mk files for more details.
#define REQUESTOR JAVA_PACKAGE_NAME
// This is used if wrapping the fragment classes and activities
// with classes in another package.
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser"
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
#define DEFAULT_SHELL "/system/bin/sh"
@ -80,9 +77,8 @@ struct su_request {
struct su_path {
char base_path[PATH_MAX];
char base_db[PATH_MAX];
char multiuser_path[PATH_MAX];
char multiuser_db[PATH_MAX];
char pkg_name[PATH_MAX];
char sock_path[PATH_MAX];
};
@ -95,10 +91,10 @@ struct su_context {
mode_t umask;
char *cwd;
struct stat st;
int pipefd[2];
};
extern struct su_context *su_ctx;
extern int pipefd[2];
// su.c

View File

@ -90,28 +90,6 @@ static void *collector(void *args) {
}
}
#define BASE_FMT "/data/user%s/%d/" REQUESTOR
#define DB_FMT BASE_FMT "/databases/su.db"
#define CAT_PATH(dest, fmt, ...) snprintf(dest, sizeof(dest), fmt, __VA_ARGS__)
static void populate_paths(struct su_context *ctx) {
CAT_PATH(ctx->path.base_path, BASE_FMT, "_de", 0);
if (access(ctx->path.base_path, R_OK) == -1)
CAT_PATH(ctx->path.base_path, BASE_FMT, "", 0);
CAT_PATH(ctx->path.base_db, DB_FMT, "_de", 0);
if (access(ctx->path.base_db, R_OK) == -1)
CAT_PATH(ctx->path.base_db, DB_FMT, "", 0);
CAT_PATH(ctx->path.multiuser_path, BASE_FMT, "_de", ctx->info->uid / 100000);
if (access(ctx->path.multiuser_path, R_OK) == -1)
CAT_PATH(ctx->path.multiuser_path, BASE_FMT, "", ctx->info->uid / 100000);
CAT_PATH(ctx->path.multiuser_db, DB_FMT, "_de", ctx->info->uid / 100000);
if (access(ctx->path.multiuser_db, R_OK) == -1)
CAT_PATH(ctx->path.multiuser_db, DB_FMT, "", ctx->info->uid / 100000);
}
void su_daemon_receiver(int client) {
LOGD("su: request from client: %d\n", client);
@ -170,16 +148,6 @@ void su_daemon_receiver(int client) {
.umask = 022,
.notify = new_request,
};
su_ctx = &ctx;
populate_paths(su_ctx);
// Check main Magisk Manager
xstat(su_ctx->path.base_path, &su_ctx->st);
if (su_ctx->st.st_gid != su_ctx->st.st_uid) {
LOGE("Bad uid/gid %d/%d for Superuser Requestor application", su_ctx->st.st_uid, su_ctx->st.st_gid);
info->policy = DENY;
}
// Lock before the policy is determined
LOCK_UID();
@ -187,41 +155,38 @@ void su_daemon_receiver(int client) {
// Not cached, do the checks
if (info->policy == QUERY) {
// Get data from database
database_check(su_ctx);
if (su_ctx->info->multiuser_mode == MULTIUSER_MODE_USER) {
// Check the user installed Magisk Manager
xstat(su_ctx->path.multiuser_path, &su_ctx->st);
if (su_ctx->st.st_gid != su_ctx->st.st_uid) {
LOGE("Bad uid/gid %d/%d for Superuser Requestor application", su_ctx->st.st_uid, su_ctx->st.st_gid);
info->policy = DENY;
}
}
database_check(&ctx);
// Handle multiuser denies
if (su_ctx->info->uid / 100000 &&
su_ctx->info->multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) {
if (info->uid / 100000 &&
info->multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) {
info->policy = DENY;
su_ctx->notify = 0;
ctx.notify = 0;
}
// always allow if this is Magisk Manager
if (info->policy == QUERY && (info->uid % 100000) == (su_ctx->st.st_uid % 100000)) {
info->policy = ALLOW;
info->root_access = ROOT_ACCESS_APPS_AND_ADB;
su_ctx->notify = 0;
// Check requester
if (info->policy == QUERY) {
if (ctx.st.st_gid != ctx.st.st_uid) {
LOGE("Bad uid/gid %d/%d for Superuser Requestor", ctx.st.st_uid, ctx.st.st_gid);
info->policy = DENY;
ctx.notify = 0;
} else if ((info->uid % 100000) == (ctx.st.st_uid % 100000)) {
info->policy = ALLOW;
info->root_access = ROOT_ACCESS_APPS_AND_ADB;
ctx.notify = 0;
}
}
// always allow if it's root
if (info->uid == UID_ROOT) {
info->policy = ALLOW;
info->root_access = ROOT_ACCESS_APPS_AND_ADB;
su_ctx->notify = 0;
ctx.notify = 0;
}
// If still not determined, open a pipe and wait for results
if (info->policy == QUERY)
xpipe2(pipefd, O_CLOEXEC);
xpipe2(ctx.pipefd, O_CLOEXEC);
}
// Fork a new process, the child process will need to setsid,
@ -235,9 +200,9 @@ void su_daemon_receiver(int client) {
if (child) {
// Wait for results
if (info->policy == QUERY) {
xxread(pipefd[0], &info->policy, sizeof(info->policy));
close(pipefd[0]);
close(pipefd[1]);
xxread(ctx.pipefd[0], &info->policy, sizeof(info->policy));
close(ctx.pipefd[0]);
close(ctx.pipefd[1]);
}
// The policy is determined, unlock
@ -308,8 +273,8 @@ void su_daemon_receiver(int client) {
}
// Get cwd
su_ctx->cwd = read_string(client);
LOGD("su: cwd=[%s]\n", su_ctx->cwd);
ctx.cwd = read_string(client);
LOGD("su: cwd=[%s]\n", ctx.cwd);
// Get pts_slave
char *pts_slave = read_string(client);
@ -330,7 +295,7 @@ void su_daemon_receiver(int client) {
xstat(pts_slave, &stbuf);
//If caller is not root, ensure the owner of pts_slave is the caller
if(stbuf.st_uid != credential.uid && credential.uid != 0) {
if(stbuf.st_uid != info->uid && info->uid != 0) {
LOGE("su: Wrong permission of pts_slave");
exit2(1);
}
@ -363,6 +328,8 @@ void su_daemon_receiver(int client) {
close(ptsfd);
// Give main the reference
su_ctx = &ctx;
su_daemon_main(argc, argv);
}

View File

@ -29,21 +29,22 @@ int socket_create_temp(char *path, size_t len) {
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_LOCAL;
snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, (int) syscall(SYS_gettid));
snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path);
snprintf(path, len, "/dev/.socket%d", (int) syscall(SYS_gettid));
strcpy(sun.sun_path, path);
/*
* Delete the socket to protect from situations when
* something bad occured previously and the kernel reused pid from that process.
* Small probability, isn't it.
*/
unlink(sun.sun_path);
unlink(path);
xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
xlisten(fd, 1);
// Set context to su_device, so apps can access it
setfilecon(sun.sun_path, "u:object_r:su_device:s0");
// Set attributes so requester can access it
setfilecon(path, "u:object_r:su_device:s0");
chown(path, su_ctx->st.st_uid, su_ctx->st.st_gid);
return fd;
}