From 84ca8e1f3e23c70e232acddb54464208637c7f75 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Oct 2017 16:03:39 +0800 Subject: [PATCH] Support changing requester in DB --- activity.c | 88 +++++++++++++++++++------------------------------ db.c | 95 ++++++++++++++++++++++++++++++++++++++++------------- su.c | 25 ++++---------- su.h | 14 +++----- su_daemon.c | 83 ++++++++++++++-------------------------------- su_socket.c | 11 ++++--- 6 files changed, 147 insertions(+), 169 deletions(-) diff --git a/activity.c b/activity.c index 507d197fc..6071b21ab 100644 --- a/activity.c +++ b/activity.c @@ -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); diff --git a/db.c b/db.c index 1f65a3cca..dd392df43 100644 --- a/db.c +++ b/db.c @@ -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); + } } diff --git a/su.c b/su.c index 3dbdfa5ca..afd3a3b9a 100644 --- a/su.c +++ b/su.c @@ -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) diff --git a/su.h b/su.h index 731100431..ab9782971 100644 --- a/su.h +++ b/su.h @@ -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 diff --git a/su_daemon.c b/su_daemon.c index a0f34d765..3f6cfb6a0 100644 --- a/su_daemon.c +++ b/su_daemon.c @@ -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); } diff --git a/su_socket.c b/su_socket.c index 99c2a3e75..13f71c9e6 100644 --- a/su_socket.c +++ b/su_socket.c @@ -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; }