From 1ddd7468620e9e405b1392847a42b57c10139802 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 1 Jun 2017 03:19:45 +0800 Subject: [PATCH] Switch to DB based su config --- activity.c | 4 +-- db.c | 72 ++++++++++++++++++++++++++++++++++++++--------------- su.c | 46 ++++++++++++++-------------------- su.h | 14 +++++------ su_daemon.c | 37 ++++++++++----------------- 5 files changed, 93 insertions(+), 80 deletions(-) diff --git a/activity.c b/activity.c index b9890709b..aba453579 100644 --- a/activity.c +++ b/activity.c @@ -40,7 +40,7 @@ static void silent_run(char* const args[]) { } static int setup_user(struct su_context *ctx, char* user) { - switch (ctx->user.multiuser_mode) { + switch (ctx->info->multiuser_mode) { case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */ case MULTIUSER_MODE_OWNER_MANAGED: sprintf(user, "%d", 0); @@ -60,7 +60,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) { sprintf(toUid, "%d", ctx->to.uid); char pid[16]; - sprintf(pid, "%d", ctx->info->pid); + sprintf(pid, "%d", ctx->pid); char user[16]; int notify = setup_user(ctx, user); diff --git a/db.c b/db.c index 8cf60b6d3..e3f5cb9c7 100644 --- a/db.c +++ b/db.c @@ -1,4 +1,5 @@ /* +** Copyright 2017, John Wu (@topjohnwu) ** Copyright 2013, Koushik Dutta (@koush) ** */ @@ -14,20 +15,15 @@ #include "magisk.h" #include "su.h" -static int database_callback(void *v, int argc, char **argv, char **azColName) { +static int policy_callback(void *v, int argc, char **argv, char **azColName) { struct su_context *ctx = (struct su_context *) v; policy_t policy = QUERY; time_t until = 0; - for(int i = 0; i < argc; i++) { - if (strcmp(azColName[i], "policy") == 0) { - if (argv[i] != NULL) { - policy = atoi(argv[i]); - } - } else if (strcmp(azColName[i], "until") == 0) { - if (argv[i] != NULL) { - until = atol(argv[i]); - } - } + for (int i = 0; i < argc; i++) { + if (strcmp(azColName[i], "policy") == 0) + policy = atoi(argv[i]); + else if (strcmp(azColName[i], "until") == 0) + until = atol(argv[i]); } if (policy == DENY) @@ -35,18 +31,44 @@ static int database_callback(void *v, int argc, char **argv, char **azColName) { else if (policy == ALLOW && (until == 0 || until > time(NULL))) ctx->info->policy = ALLOW; + LOGD("su_db: query policy=[%d]\n", ctx->info->policy); + + return 0; +} + +static int settings_callback(void *v, int argc, char **argv, char **azColName) { + struct su_context *ctx = (struct su_context *) v; + int *target, value; + char *entry; + for (int i = 0; i < argc; ++i) { + if (strcmp(azColName[i], "key") == 0) { + if (strcmp(argv[i], ROOT_ACCESS_ENTRY) == 0) + target = &ctx->info->root_access; + else if (strcmp(argv[i], MULTIUSER_MODE_ENTRY) == 0) + target = &ctx->info->multiuser_mode; + entry = argv[i]; + } else if (strcmp(azColName[i], "value") == 0) { + value = atoi(argv[i]); + } + } + LOGD("su_db: query %s=[%d]\n", entry, value); + *target = value; return 0; } void database_check(struct su_context *ctx) { sqlite3 *db = NULL; + // Set default values + ctx->info->root_access = ROOT_ACCESS_APPS_AND_ADB; + ctx->info->multiuser_mode = MULTIUSER_MODE_OWNER_ONLY; + ctx->info->policy = QUERY; + // Check if file is readable if (access(ctx->user.database_path, R_OK) == -1) return; - - char query[512]; - snprintf(query, sizeof(query), "SELECT policy, until FROM policies WHERE uid=%d", ctx->info->uid % 100000); + + // Open database int ret = sqlite3_open_v2(ctx->user.database_path, &db, SQLITE_OPEN_READONLY, NULL); if (ret) { LOGD("sqlite3 open failure: %s\n", sqlite3_errstr(ret)); @@ -54,11 +76,21 @@ void database_check(struct su_context *ctx) { return; } - char *err = NULL; - ret = sqlite3_exec(db, query, database_callback, ctx, &err); - sqlite3_close(db); - if (err != NULL) { + char query[512], *err = NULL; + + // 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) LOGE("sqlite3_exec: %s\n", err); - ctx->info->policy = DENY; - } + + err = NULL; + + // Query for settings + snprintf(query, sizeof(query), "SELECT key, value FROM settings"); + sqlite3_exec(db, query, settings_callback, ctx, &err); + if (err != NULL) + LOGE("sqlite3_exec: %s\n", err); + + sqlite3_close(db); } diff --git a/su.c b/su.c index 02021eed8..f99bca040 100644 --- a/su.c +++ b/su.c @@ -237,7 +237,7 @@ int su_daemon_main(int argc, char **argv) { printf("%s\n", MAGISKSU_VER_STR); exit2(EXIT_SUCCESS); case 'u': - switch (su_ctx->user.multiuser_mode) { + switch (su_ctx->info->multiuser_mode) { case MULTIUSER_MODE_USER: printf("Owner only: Only owner has root access\n"); break; @@ -293,34 +293,26 @@ int su_daemon_main(int argc, char **argv) { // Setup done, now every error leads to deny err_handler = deny; - // Check property of root configuration - char *root_prop = getprop(ROOT_ACCESS_PROP); - if (root_prop) { - int prop_status = atoi(root_prop); - switch (prop_status) { - case ROOT_ACCESS_DISABLED: - LOGE("Root access is disabled!\n"); + // Check root_access configuration + switch (su_ctx->info->root_access) { + case ROOT_ACCESS_DISABLED: + LOGE("Root access is disabled!\n"); + exit(EXIT_FAILURE); + case ROOT_ACCESS_APPS_ONLY: + if (su_ctx->info->uid == UID_SHELL) { + LOGE("Root access is disabled for ADB!\n"); exit(EXIT_FAILURE); - case ROOT_ACCESS_APPS_ONLY: - if (su_ctx->info->uid == UID_SHELL) { - LOGE("Root access is disabled for ADB!\n"); - exit(EXIT_FAILURE); - } - break; - case ROOT_ACCESS_ADB_ONLY: - if (su_ctx->info->uid != UID_SHELL) { - LOGE("Root access limited to ADB only!\n"); - exit(EXIT_FAILURE); - } - break; - case ROOT_ACCESS_APPS_AND_ADB: - default: - break; } - free(root_prop); - } else { - // Not initialized yet, set the prop to allow everything by default - setprop(ROOT_ACCESS_PROP, xstr(ROOT_ACCESS_APPS_AND_ADB)); + break; + case ROOT_ACCESS_ADB_ONLY: + if (su_ctx->info->uid != UID_SHELL) { + LOGE("Root access limited to ADB only!\n"); + exit(EXIT_FAILURE); + } + break; + case ROOT_ACCESS_APPS_AND_ADB: + default: + break; } // New request or no db exist, notify user for response diff --git a/su.h b/su.h index 4614ef77d..b815acd8a 100644 --- a/su.h +++ b/su.h @@ -12,14 +12,14 @@ #define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)" // Property check for root access -#define ROOT_ACCESS_PROP "persist.magisk.root" +#define ROOT_ACCESS_ENTRY "root_access" #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_ENTRY "multiuser_mode" #define MULTIUSER_MODE_OWNER_ONLY 0 #define MULTIUSER_MODE_OWNER_MANAGED 1 #define MULTIUSER_MODE_USER 2 @@ -51,13 +51,14 @@ typedef enum { ALLOW = 2, } policy_t; -struct su_initiator { - pid_t pid; +struct su_info { unsigned uid; policy_t policy; pthread_mutex_t lock; int count; int clock; + int multiuser_mode; + int root_access; struct list_head pos; }; @@ -75,8 +76,6 @@ 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. @@ -89,9 +88,10 @@ struct su_user_info { }; struct su_context { - struct su_initiator *info; + struct su_info *info; struct su_request to; struct su_user_info user; + pid_t pid; int notify; mode_t umask; char sock_path[PATH_MAX]; diff --git a/su_daemon.c b/su_daemon.c index 39917fa89..ece0b50bb 100644 --- a/su_daemon.c +++ b/su_daemon.c @@ -63,27 +63,16 @@ static void sigpipe_handler(int sig) { LOGD("su: Client killed unexpectedly\n"); } -static int get_multiuser_mode() { - char *prop = getprop(MULTIUSER_MODE_PROP); - if (prop) { - int ret = atoi(prop); - free(prop); - return ret; - } - return MULTIUSER_MODE_OWNER_ONLY; -} - - // Maintain the lists periodically static void *collector(void *args) { LOGD("su: collector started\n"); struct list_head *pos, *temp; - struct su_initiator *node; + struct su_info *node; while(1) { sleep(1); pthread_mutex_lock(&list_lock); list_for_each(pos, &active_list) { - node = list_entry(pos, struct su_initiator, pos); + node = list_entry(pos, struct su_info, pos); --node->clock; // Timeout, move to waiting list if (node->clock == 0) { @@ -94,7 +83,7 @@ static void *collector(void *args) { } } list_for_each(pos, &waiting_list) { - node = list_entry(pos, struct su_initiator, pos); + node = list_entry(pos, struct su_info, pos); // Nothing is using the info, remove it if (node->count == 0) { temp = pos; @@ -111,7 +100,7 @@ static void *collector(void *args) { void su_daemon_receiver(int client) { LOGD("su: request from client: %d\n", client); - struct su_initiator *info = NULL, *node; + struct su_info *info = NULL, *node; struct list_head *pos; int new_request = 0; @@ -129,7 +118,7 @@ void su_daemon_receiver(int client) { // Search for existing in the active list list_for_each(pos, &active_list) { - node = list_entry(pos, struct su_initiator, pos); + node = list_entry(pos, struct su_info, pos); if (node->uid == credential.uid) info = node; } @@ -154,8 +143,6 @@ void su_daemon_receiver(int client) { // Lock before the policy is determined pthread_mutex_lock(&info->lock); - info->pid = credential.pid; - // Default values struct su_context ctx = { .info = info, @@ -168,8 +155,8 @@ void su_daemon_receiver(int client) { }, .user = { .android_user_id = info->uid / 100000, - .multiuser_mode = get_multiuser_mode(), }, + .pid = credential.pid, .umask = 022, .notify = new_request, }; @@ -191,8 +178,12 @@ void su_daemon_receiver(int client) { // Not cached, do the checks if (info->policy == QUERY) { + // Get data from database + database_check(su_ctx); + + // Handle multiuser denies if (su_ctx->user.android_user_id && - su_ctx->user.multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) { + su_ctx->info->multiuser_mode == MULTIUSER_MODE_OWNER_ONLY) { info->policy = DENY; su_ctx->notify = 0; } @@ -200,19 +191,17 @@ void su_daemon_receiver(int client) { // always allow if this is Magisk Manager if (info->policy == QUERY && (info->uid % 100000) == (st.st_uid % 100000)) { info->policy = ALLOW; + info->root_access = ROOT_ACCESS_APPS_AND_ADB; su_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; } - // If not determined, check database - if (info->policy == QUERY) - database_check(su_ctx); - // If still not determined, open a pipe and wait for results if (info->policy == QUERY) pipe2(pipefd, O_CLOEXEC);