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" #include "su.h"
/* intent actions */ /* intent actions */
#define ACTION_REQUEST "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".RequestActivity" #define ACTION_REQUEST "%s/" REQUESTOR_PREFIX ".RequestActivity"
#define ACTION_NOTIFY "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".NotifyActivity" #define ACTION_RESULT "%s/" REQUESTOR_PREFIX ".SuReceiver"
#define ACTION_RESULT "broadcast", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".SuReceiver"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am" #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]; char user[16];
int notify = setup_user(ctx, user); int notify = setup_user(ctx, user);
char activity[128];
sprintf(activity, ACTION_RESULT, ctx->path.pkg_name);
// Send notice to manager, enable logging // Send notice to manager, enable logging
char *result_command[] = { char *result_command[] = {
AM_PATH, AM_PATH, "broadcast", "-n",
ACTION_RESULT, activity,
"--user", "--user", user,
user, "--ei", "mode", "0",
"--ei", "--ei", "from.uid", fromUid,
"mode", "--ei", "to.uid", toUid,
"0", "--ei", "pid", pid,
"--ei", "--es", "command", get_command(&ctx->to),
"from.uid", "--es", "action", policy == ALLOW ? "allow" : "deny",
fromUid,
"--ei",
"to.uid",
toUid,
"--ei",
"pid",
pid,
"--es",
"command",
get_command(&ctx->to),
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
NULL NULL
}; };
silent_run(result_command); silent_run(result_command);
@ -105,19 +94,12 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
if (notify) { if (notify) {
sprintf(user, "%d", notify); sprintf(user, "%d", notify);
char *notify_command[] = { char *notify_command[] = {
AM_PATH, AM_PATH, "broadcast", "-n",
ACTION_RESULT, activity,
"--user", "--user", user,
user, "--ei", "mode", "1",
"--ei", "--ei", "from.uid", fromUid,
"mode", "--es", "action", policy == ALLOW ? "allow" : "deny",
"1",
"--ei",
"from.uid",
fromUid,
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
NULL NULL
}; };
silent_run(notify_command); silent_run(notify_command);
@ -128,17 +110,15 @@ void app_send_request(struct su_context *ctx) {
char user[16]; char user[16];
int notify = setup_user(ctx, user); int notify = setup_user(ctx, user);
char activity[128];
sprintf(activity, ACTION_REQUEST, ctx->path.pkg_name);
char *request_command[] = { char *request_command[] = {
AM_PATH, AM_PATH, "start", "-n",
ACTION_REQUEST, activity,
"--user", "--user", user,
user, "--es", "socket", ctx->path.sock_path,
"--es", "--ez", "timeout", notify ? "false" : "true",
"socket",
ctx->path.sock_path,
"--ez",
"timeout",
notify ? "false" : "true",
NULL NULL
}; };
silent_run(request_command); 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 // Send notice to user to tell them root is managed by owner
if (notify) { if (notify) {
sprintf(user, "%d", notify); sprintf(user, "%d", notify);
sprintf(activity, ACTION_RESULT, ctx->path.pkg_name);
char *notify_command[] = { char *notify_command[] = {
AM_PATH, AM_PATH, "broadcast", "-n",
ACTION_RESULT, activity,
"--user", "--user", user,
user, "--ei", "mode", "2",
"--ei",
"mode",
"2",
NULL NULL
}; };
silent_run(notify_command); 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; 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) { void database_check(struct su_context *ctx) {
sqlite3 *db = NULL; sqlite3 *db = NULL;
int ret; int ret;
char query[512], *err = NULL; char buffer[PATH_MAX], *err = NULL;
// Set default values // Set default values
ctx->info->root_access = ROOT_ACCESS_APPS_AND_ADB; ctx->info->root_access = ROOT_ACCESS_APPS_AND_ADB;
ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY; ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY;
ctx->info->mnt_ns = NAMESPACE_MODE_REQUESTER; 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 // 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) { if (ret) {
LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret)); LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
sqlite3_close(db); sqlite3_close(db);
return; goto stat_requester;
} }
// Check multiuser mode settings // Check multiuser mode settings
snprintf(query, sizeof(query), "SELECT key, value FROM settings WHERE key='%s'", MULTIUSER_MODE_ENTRY); sprintf(buffer, "SELECT key, value FROM settings WHERE key='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, query, settings_callback, ctx, &err); sqlite3_exec(db, buffer, settings_callback, ctx, &err);
if (err != NULL)
LOGE("sqlite3_exec: %s\n", err);
err = NULL; 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); sqlite3_close(db);
sprintf(buffer, "%s/magisk.db", ctx->path.multiuser_path);
// Open database LOGD("su_db: open %s", buffer);
ret = sqlite3_open_v2(ctx->path.multiuser_db, &db, SQLITE_OPEN_READONLY, NULL); ret = sqlite3_open_v2(buffer, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) { if (ret) {
LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret)); LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
sqlite3_close(db); sqlite3_close(db);
return; goto stat_requester;
} }
} }
// Query for policy // Read PKG name from DB
snprintf(query, sizeof(query), "SELECT policy, until FROM policies WHERE uid=%d", ctx->info->uid % 100000); strcpy(buffer, "SELECT key, value FROM strings");
sqlite3_exec(db, query, policy_callback, ctx, &err); sqlite3_exec(db, buffer, strings_callback, ctx, &err);
if (err != NULL) { if (err != NULL)
LOGE("sqlite3_exec: %s\n", err); 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; err = NULL;
// Query for settings // Query for settings
snprintf(query, sizeof(query), "SELECT key, value FROM settings WHERE key!='%s'", MULTIUSER_MODE_ENTRY); sprintf(buffer, "SELECT key, value FROM settings WHERE key!='%s'", MULTIUSER_MODE_ENTRY);
sqlite3_exec(db, query, settings_callback, ctx, &err); sqlite3_exec(db, buffer, settings_callback, ctx, &err);
if (err != NULL) { if (err != NULL)
LOGE("sqlite3_exec: %s\n", err); LOGE("sqlite3_exec: %s\n", err);
return;
}
sqlite3_close(db); 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) { __attribute__ ((noreturn)) void exit2(int status) {
// Handle the pipe, or the daemon will get stuck // Handle the pipe, or the daemon will get stuck
if (su_ctx->info->policy == QUERY) { if (su_ctx->info->policy == QUERY) {
xwrite(pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy)); xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(pipefd[0]); close(su_ctx->pipefd[0]);
close(pipefd[1]); close(su_ctx->pipefd[1]);
} }
exit(status); exit(status);
} }
@ -352,19 +352,6 @@ int su_daemon_main(int argc, char **argv) {
// New request or no db exist, notify user for response // New request or no db exist, notify user for response
if (su_ctx->info->policy == QUERY) { 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)); socket_serv_fd = socket_create_temp(su_ctx->path.sock_path, sizeof(su_ctx->path.sock_path));
setup_sighandlers(cleanup_signal); setup_sighandlers(cleanup_signal);
@ -387,9 +374,9 @@ int su_daemon_main(int argc, char **argv) {
su_ctx->info->policy = DENY; su_ctx->info->policy = DENY;
// Report the policy to main daemon // Report the policy to main daemon
xwrite(pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy)); xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
close(pipefd[0]); close(su_ctx->pipefd[0]);
close(pipefd[1]); close(su_ctx->pipefd[1]);
} }
if (su_ctx->info->policy == ALLOW) if (su_ctx->info->policy == ALLOW)

14
su.h
View File

@ -31,17 +31,14 @@
#define NAMESPACE_MODE_REQUESTER 1 #define NAMESPACE_MODE_REQUESTER 1
#define NAMESPACE_MODE_ISOLATE 2 #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 // 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"
// 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 // 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_CACHE_PATH "/dev/" REQUESTOR
#define DEFAULT_SHELL "/system/bin/sh" #define DEFAULT_SHELL "/system/bin/sh"
@ -80,9 +77,8 @@ struct su_request {
struct su_path { struct su_path {
char base_path[PATH_MAX]; char base_path[PATH_MAX];
char base_db[PATH_MAX];
char multiuser_path[PATH_MAX]; char multiuser_path[PATH_MAX];
char multiuser_db[PATH_MAX]; char pkg_name[PATH_MAX];
char sock_path[PATH_MAX]; char sock_path[PATH_MAX];
}; };
@ -95,10 +91,10 @@ struct su_context {
mode_t umask; mode_t umask;
char *cwd; char *cwd;
struct stat st; struct stat st;
int pipefd[2];
}; };
extern struct su_context *su_ctx; extern struct su_context *su_ctx;
extern int pipefd[2];
// su.c // 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) { void su_daemon_receiver(int client) {
LOGD("su: request from client: %d\n", client); LOGD("su: request from client: %d\n", client);
@ -170,16 +148,6 @@ void su_daemon_receiver(int client) {
.umask = 022, .umask = 022,
.notify = new_request, .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 before the policy is determined
LOCK_UID(); LOCK_UID();
@ -187,41 +155,38 @@ void su_daemon_receiver(int client) {
// Not cached, do the checks // Not cached, do the checks
if (info->policy == QUERY) { if (info->policy == QUERY) {
// Get data from database // Get data from database
database_check(su_ctx); database_check(&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;
}
}
// Handle multiuser denies // Handle multiuser denies
if (su_ctx->info->uid / 100000 && if (info->uid / 100000 &&
su_ctx->info->multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) { info->multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) {
info->policy = DENY; info->policy = DENY;
su_ctx->notify = 0; ctx.notify = 0;
} }
// always allow if this is Magisk Manager // Check requester
if (info->policy == QUERY && (info->uid % 100000) == (su_ctx->st.st_uid % 100000)) { if (info->policy == QUERY) {
info->policy = ALLOW; if (ctx.st.st_gid != ctx.st.st_uid) {
info->root_access = ROOT_ACCESS_APPS_AND_ADB; LOGE("Bad uid/gid %d/%d for Superuser Requestor", ctx.st.st_uid, ctx.st.st_gid);
su_ctx->notify = 0; 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 // always allow if it's root
if (info->uid == UID_ROOT) { if (info->uid == UID_ROOT) {
info->policy = ALLOW; info->policy = ALLOW;
info->root_access = ROOT_ACCESS_APPS_AND_ADB; 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 still not determined, open a pipe and wait for results
if (info->policy == QUERY) if (info->policy == QUERY)
xpipe2(pipefd, O_CLOEXEC); xpipe2(ctx.pipefd, O_CLOEXEC);
} }
// Fork a new process, the child process will need to setsid, // Fork a new process, the child process will need to setsid,
@ -235,9 +200,9 @@ void su_daemon_receiver(int client) {
if (child) { if (child) {
// Wait for results // Wait for results
if (info->policy == QUERY) { if (info->policy == QUERY) {
xxread(pipefd[0], &info->policy, sizeof(info->policy)); xxread(ctx.pipefd[0], &info->policy, sizeof(info->policy));
close(pipefd[0]); close(ctx.pipefd[0]);
close(pipefd[1]); close(ctx.pipefd[1]);
} }
// The policy is determined, unlock // The policy is determined, unlock
@ -308,8 +273,8 @@ void su_daemon_receiver(int client) {
} }
// Get cwd // Get cwd
su_ctx->cwd = read_string(client); ctx.cwd = read_string(client);
LOGD("su: cwd=[%s]\n", su_ctx->cwd); LOGD("su: cwd=[%s]\n", ctx.cwd);
// Get pts_slave // Get pts_slave
char *pts_slave = read_string(client); char *pts_slave = read_string(client);
@ -330,7 +295,7 @@ void su_daemon_receiver(int client) {
xstat(pts_slave, &stbuf); xstat(pts_slave, &stbuf);
//If caller is not root, ensure the owner of pts_slave is the caller //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"); LOGE("su: Wrong permission of pts_slave");
exit2(1); exit2(1);
} }
@ -363,6 +328,8 @@ void su_daemon_receiver(int client) {
close(ptsfd); close(ptsfd);
// Give main the reference
su_ctx = &ctx;
su_daemon_main(argc, argv); 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)); memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_LOCAL; sun.sun_family = AF_LOCAL;
snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, (int) syscall(SYS_gettid)); snprintf(path, len, "/dev/.socket%d", (int) syscall(SYS_gettid));
snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path); strcpy(sun.sun_path, path);
/* /*
* Delete the socket to protect from situations when * Delete the socket to protect from situations when
* something bad occured previously and the kernel reused pid from that process. * something bad occured previously and the kernel reused pid from that process.
* Small probability, isn't it. * Small probability, isn't it.
*/ */
unlink(sun.sun_path); unlink(path);
xbind(fd, (struct sockaddr*) &sun, sizeof(sun)); xbind(fd, (struct sockaddr*) &sun, sizeof(sun));
xlisten(fd, 1); xlisten(fd, 1);
// Set context to su_device, so apps can access it // Set attributes so requester can access it
setfilecon(sun.sun_path, "u:object_r:su_device:s0"); setfilecon(path, "u:object_r:su_device:s0");
chown(path, su_ctx->st.st_uid, su_ctx->st.st_gid);
return fd; return fd;
} }