mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 10:37:39 +00:00
Move database logic outside of MagiskSU
This commit is contained in:
parent
7e2ba41c64
commit
b2f719989d
10
activity.c
10
activity.c
@ -45,7 +45,7 @@ static void silent_run(char* const args[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int setup_user(struct su_context *ctx, char* user) {
|
static int setup_user(struct su_context *ctx, char* user) {
|
||||||
switch (ctx->info->multiuser_mode) {
|
switch (ctx->info->dbs.v[SU_MULTIUSER_MODE]) {
|
||||||
case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */
|
case MULTIUSER_MODE_OWNER_ONLY: /* Should already be denied if not owner */
|
||||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
case MULTIUSER_MODE_OWNER_MANAGED:
|
||||||
sprintf(user, "%d", 0);
|
sprintf(user, "%d", 0);
|
||||||
@ -59,7 +59,7 @@ static int setup_user(struct su_context *ctx, char* user) {
|
|||||||
|
|
||||||
void app_send_result(struct su_context *ctx, policy_t policy) {
|
void app_send_result(struct su_context *ctx, policy_t policy) {
|
||||||
char fromUid[16];
|
char fromUid[16];
|
||||||
if (ctx->info->multiuser_mode == MULTIUSER_MODE_OWNER_MANAGED)
|
if (ctx->info->dbs.v[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED)
|
||||||
sprintf(fromUid, "%d", ctx->info->uid % 100000);
|
sprintf(fromUid, "%d", ctx->info->uid % 100000);
|
||||||
else
|
else
|
||||||
sprintf(fromUid, "%d", ctx->info->uid);
|
sprintf(fromUid, "%d", ctx->info->uid);
|
||||||
@ -74,7 +74,7 @@ void app_send_result(struct su_context *ctx, policy_t policy) {
|
|||||||
int notify = setup_user(ctx, user);
|
int notify = setup_user(ctx, user);
|
||||||
|
|
||||||
char activity[128];
|
char activity[128];
|
||||||
sprintf(activity, ACTION_RESULT, ctx->info->pkg_name);
|
sprintf(activity, ACTION_RESULT, ctx->info->str.s[SU_REQUESTER]);
|
||||||
|
|
||||||
// Send notice to manager, enable logging
|
// Send notice to manager, enable logging
|
||||||
char *result_command[] = {
|
char *result_command[] = {
|
||||||
@ -113,7 +113,7 @@ void app_send_request(struct su_context *ctx) {
|
|||||||
int notify = setup_user(ctx, user);
|
int notify = setup_user(ctx, user);
|
||||||
|
|
||||||
char activity[128];
|
char activity[128];
|
||||||
sprintf(activity, ACTION_REQUEST, ctx->info->pkg_name);
|
sprintf(activity, ACTION_REQUEST, ctx->info->str.s[SU_REQUESTER]);
|
||||||
|
|
||||||
char *request_command[] = {
|
char *request_command[] = {
|
||||||
AM_PATH, "start", "-n",
|
AM_PATH, "start", "-n",
|
||||||
@ -128,7 +128,7 @@ 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->info->pkg_name);
|
sprintf(activity, ACTION_RESULT, ctx->info->str.s[SU_REQUESTER]);
|
||||||
char *notify_command[] = {
|
char *notify_command[] = {
|
||||||
AM_PATH, "broadcast", "-n",
|
AM_PATH, "broadcast", "-n",
|
||||||
activity,
|
activity,
|
||||||
|
148
db.c
148
db.c
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
** Copyright 2017, John Wu (@topjohnwu)
|
|
||||||
** Copyright 2013, Koushik Dutta (@koush)
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "magisk.h"
|
|
||||||
#include "su.h"
|
|
||||||
|
|
||||||
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)
|
|
||||||
policy = atoi(argv[i]);
|
|
||||||
else if (strcmp(azColName[i], "until") == 0)
|
|
||||||
until = atol(argv[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (policy == DENY)
|
|
||||||
ctx->info->policy = DENY;
|
|
||||||
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;
|
|
||||||
else if (strcmp(argv[i], NAMESPACE_MODE_ENTRY) == 0)
|
|
||||||
target = &ctx->info->mnt_ns;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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->info->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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void database_check(struct su_context *ctx) {
|
|
||||||
sqlite3 *db = NULL;
|
|
||||||
int ret;
|
|
||||||
char buffer[PATH_MAX], *err = NULL;
|
|
||||||
const char *base = access("/data/user_de", F_OK) == 0 ? "/data/user_de" : "/data/user";
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
strcpy(ctx->info->pkg_name, "???"); /* bad string so it doesn't exist */
|
|
||||||
|
|
||||||
// Open database
|
|
||||||
ret = sqlite3_open_v2(MAGISKDB, &db, SQLITE_OPEN_READONLY, NULL);
|
|
||||||
if (ret) {
|
|
||||||
LOGE("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
|
|
||||||
sqlite3_close(db);
|
|
||||||
goto stat_requester;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query for strings
|
|
||||||
sqlite3_exec(db, "SELECT key, value FROM strings", strings_callback, ctx, &err);
|
|
||||||
if (err)
|
|
||||||
LOGE("sqlite3_exec: %s\n", err);
|
|
||||||
err = NULL;
|
|
||||||
|
|
||||||
// Query for settings
|
|
||||||
sqlite3_exec(db, "SELECT key, value FROM settings", settings_callback, ctx, &err);
|
|
||||||
if (err)
|
|
||||||
LOGE("sqlite3_exec: %s\n", err);
|
|
||||||
err = NULL;
|
|
||||||
|
|
||||||
// Query for policy
|
|
||||||
int uid = -1;
|
|
||||||
switch (ctx->info->multiuser_mode) {
|
|
||||||
case MULTIUSER_MODE_OWNER_ONLY:
|
|
||||||
if (ctx->info->uid / 100000) {
|
|
||||||
uid = -1;
|
|
||||||
ctx->info->policy = DENY;
|
|
||||||
ctx->notify = 0;
|
|
||||||
} else {
|
|
||||||
uid = ctx->info->uid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
|
||||||
uid = ctx->info->uid % 100000;
|
|
||||||
break;
|
|
||||||
case MULTIUSER_MODE_USER:
|
|
||||||
uid = ctx->info->uid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(buffer, "SELECT policy, until FROM policies WHERE uid=%d", uid);
|
|
||||||
sqlite3_exec(db, buffer, policy_callback, ctx, &err);
|
|
||||||
if (err)
|
|
||||||
LOGE("sqlite3_exec: %s\n", err);
|
|
||||||
|
|
||||||
sqlite3_close(db);
|
|
||||||
|
|
||||||
stat_requester:
|
|
||||||
// We prefer the original name
|
|
||||||
sprintf(buffer, "%s/0/" JAVA_PACKAGE_NAME, base);
|
|
||||||
if (stat(buffer, &ctx->info->st) == 0) {
|
|
||||||
strcpy(ctx->info->pkg_name, JAVA_PACKAGE_NAME);
|
|
||||||
} else {
|
|
||||||
sprintf(buffer, "%s/0/%s", base, ctx->info->pkg_name);
|
|
||||||
if (stat(buffer, &ctx->info->st) == -1) {
|
|
||||||
LOGE("su: cannot find requester");
|
|
||||||
memset(&ctx->info->st, 0, sizeof(ctx->info->st));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
48
su.c
48
su.c
@ -104,7 +104,7 @@ void set_identity(unsigned uid) {
|
|||||||
static __attribute__ ((noreturn)) void allow() {
|
static __attribute__ ((noreturn)) void allow() {
|
||||||
char* argv[] = { NULL, NULL, NULL, NULL };
|
char* argv[] = { NULL, NULL, NULL, NULL };
|
||||||
|
|
||||||
if (su_ctx->notify)
|
if (su_ctx->info->access.notify || su_ctx->info->access.log)
|
||||||
app_send_result(su_ctx, ALLOW);
|
app_send_result(su_ctx, ALLOW);
|
||||||
|
|
||||||
if (su_ctx->to.login)
|
if (su_ctx->to.login)
|
||||||
@ -129,7 +129,7 @@ static __attribute__ ((noreturn)) void allow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static __attribute__ ((noreturn)) void deny() {
|
static __attribute__ ((noreturn)) void deny() {
|
||||||
if (su_ctx->notify)
|
if (su_ctx->info->access.notify || su_ctx->info->access.log)
|
||||||
app_send_result(su_ctx, DENY);
|
app_send_result(su_ctx, DENY);
|
||||||
|
|
||||||
LOGW("su: request rejected (%u->%u)", su_ctx->info->uid, su_ctx->to.uid);
|
LOGW("su: request rejected (%u->%u)", su_ctx->info->uid, su_ctx->to.uid);
|
||||||
@ -140,7 +140,7 @@ static __attribute__ ((noreturn)) void deny() {
|
|||||||
static void socket_cleanup() {
|
static void socket_cleanup() {
|
||||||
if (su_ctx && su_ctx->sock_path[0]) {
|
if (su_ctx && su_ctx->sock_path[0]) {
|
||||||
unlink(su_ctx->sock_path);
|
unlink(su_ctx->sock_path);
|
||||||
su_ctx->sock_path[0] = 0;
|
su_ctx->sock_path[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,8 +151,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->pipefd[0] >= 0) {
|
||||||
xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
|
int i = DENY;
|
||||||
|
xwrite(su_ctx->pipefd[1], &i, sizeof(i));
|
||||||
close(su_ctx->pipefd[0]);
|
close(su_ctx->pipefd[0]);
|
||||||
close(su_ctx->pipefd[1]);
|
close(su_ctx->pipefd[1]);
|
||||||
}
|
}
|
||||||
@ -215,7 +216,6 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
case 'c':
|
case 'c':
|
||||||
su_ctx->to.command = concat_commands(argc, argv);
|
su_ctx->to.command = concat_commands(argc, argv);
|
||||||
optind = argc;
|
optind = argc;
|
||||||
su_ctx->notify = 1;
|
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(EXIT_SUCCESS);
|
usage(EXIT_SUCCESS);
|
||||||
@ -240,7 +240,7 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
// Do nothing, placed here for legacy support :)
|
// Do nothing, placed here for legacy support :)
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
su_ctx->info->mnt_ns = NAMESPACE_MODE_GLOBAL;
|
su_ctx->info->dbs.v[SU_MNT_NS] = NAMESPACE_MODE_GLOBAL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Bionic getopt_long doesn't terminate its error output by newline */
|
/* Bionic getopt_long doesn't terminate its error output by newline */
|
||||||
@ -265,7 +265,7 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle namespaces
|
// Handle namespaces
|
||||||
switch (su_ctx->info->mnt_ns) {
|
switch (su_ctx->info->dbs.v[SU_MNT_NS]) {
|
||||||
case NAMESPACE_MODE_GLOBAL:
|
case NAMESPACE_MODE_GLOBAL:
|
||||||
LOGD("su: use global namespace\n");
|
LOGD("su: use global namespace\n");
|
||||||
break;
|
break;
|
||||||
@ -285,30 +285,8 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
// Change directory to cwd
|
// Change directory to cwd
|
||||||
chdir(su_ctx->cwd);
|
chdir(su_ctx->cwd);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
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
|
// New request or no db exist, notify user for response
|
||||||
if (su_ctx->info->policy == QUERY && su_ctx->info->st.st_uid != 0) {
|
if (su_ctx->pipefd[0] >= 0) {
|
||||||
socket_serv_fd = socket_create_temp(su_ctx->sock_path, sizeof(su_ctx->sock_path));
|
socket_serv_fd = socket_create_temp(su_ctx->sock_path, sizeof(su_ctx->sock_path));
|
||||||
setup_sighandlers(cleanup_signal);
|
setup_sighandlers(cleanup_signal);
|
||||||
|
|
||||||
@ -326,17 +304,17 @@ int su_daemon_main(int argc, char **argv) {
|
|||||||
socket_cleanup();
|
socket_cleanup();
|
||||||
|
|
||||||
if (strcmp(result, "socket:ALLOW") == 0)
|
if (strcmp(result, "socket:ALLOW") == 0)
|
||||||
su_ctx->info->policy = ALLOW;
|
su_ctx->info->access.policy = ALLOW;
|
||||||
else
|
else
|
||||||
su_ctx->info->policy = DENY;
|
su_ctx->info->access.policy = DENY;
|
||||||
|
|
||||||
// Report the policy to main daemon
|
// Report the policy to main daemon
|
||||||
xwrite(su_ctx->pipefd[1], &su_ctx->info->policy, sizeof(su_ctx->info->policy));
|
xwrite(su_ctx->pipefd[1], &su_ctx->info->access.policy, sizeof(policy_t));
|
||||||
close(su_ctx->pipefd[0]);
|
close(su_ctx->pipefd[0]);
|
||||||
close(su_ctx->pipefd[1]);
|
close(su_ctx->pipefd[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (su_ctx->info->policy == ALLOW)
|
if (su_ctx->info->access.policy == ALLOW)
|
||||||
allow();
|
allow();
|
||||||
else
|
else
|
||||||
deny();
|
deny();
|
||||||
|
48
su.h
48
su.h
@ -8,57 +8,26 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "db.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
|
||||||
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
|
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
|
||||||
|
|
||||||
// DB settings for root access
|
|
||||||
#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
|
|
||||||
|
|
||||||
// DB settings for multiuser
|
|
||||||
#define MULTIUSER_MODE_ENTRY "multiuser_mode"
|
|
||||||
#define MULTIUSER_MODE_OWNER_ONLY 0
|
|
||||||
#define MULTIUSER_MODE_OWNER_MANAGED 1
|
|
||||||
#define MULTIUSER_MODE_USER 2
|
|
||||||
|
|
||||||
// DB settings for namespace seperation
|
|
||||||
#define NAMESPACE_MODE_ENTRY "mnt_ns"
|
|
||||||
#define NAMESPACE_MODE_GLOBAL 0
|
|
||||||
#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"
|
|
||||||
// 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 DEFAULT_SHELL "/system/bin/sh"
|
#define DEFAULT_SHELL "/system/bin/sh"
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
QUERY = 0,
|
|
||||||
DENY = 1,
|
|
||||||
ALLOW = 2,
|
|
||||||
} policy_t;
|
|
||||||
|
|
||||||
struct su_info {
|
struct su_info {
|
||||||
unsigned uid; /* Key to find su_info */
|
unsigned uid; /* Unique key to find su_info */
|
||||||
pthread_mutex_t lock; /* Internal lock */
|
pthread_mutex_t lock; /* Internal lock */
|
||||||
int count; /* Just a count for debugging purpose */
|
int count; /* Just a count for debugging purpose */
|
||||||
|
|
||||||
/* These values should be guarded with internal lock */
|
/* These values should be guarded with internal lock */
|
||||||
policy_t policy;
|
struct db_settings dbs;
|
||||||
int multiuser_mode;
|
struct db_strings str;
|
||||||
int root_access;
|
struct su_access access;
|
||||||
int mnt_ns;
|
|
||||||
char pkg_name[PATH_MAX];
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
/* These should be guarded with global list lock */
|
/* These should be guarded with global list lock */
|
||||||
@ -79,7 +48,6 @@ struct su_context {
|
|||||||
struct su_info *info;
|
struct su_info *info;
|
||||||
struct su_request to;
|
struct su_request to;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int notify;
|
|
||||||
char cwd[PATH_MAX];
|
char cwd[PATH_MAX];
|
||||||
char sock_path[PATH_MAX];
|
char sock_path[PATH_MAX];
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
@ -105,8 +73,4 @@ void socket_receive_result(int fd, char *result, ssize_t result_len);
|
|||||||
void app_send_result(struct su_context *ctx, policy_t policy);
|
void app_send_result(struct su_context *ctx, policy_t policy);
|
||||||
void app_send_request(struct su_context *ctx);
|
void app_send_request(struct su_context *ctx);
|
||||||
|
|
||||||
// db.c
|
|
||||||
|
|
||||||
void database_check(struct su_context *ctx);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
169
su_daemon.c
169
su_daemon.c
@ -31,7 +31,7 @@
|
|||||||
#define LOCK_LIST() pthread_mutex_lock(&list_lock)
|
#define LOCK_LIST() pthread_mutex_lock(&list_lock)
|
||||||
#define LOCK_UID() pthread_mutex_lock(&info->lock)
|
#define LOCK_UID() pthread_mutex_lock(&info->lock)
|
||||||
#define UNLOCK_LIST() pthread_mutex_unlock(&list_lock)
|
#define UNLOCK_LIST() pthread_mutex_unlock(&list_lock)
|
||||||
#define UNLOCK_UID() pthread_mutex_unlock(&info->lock)
|
#define UNLOCK_UID() pthread_mutex_unlock(&ctx.info->lock)
|
||||||
|
|
||||||
static struct list_head active_list, waiting_list;
|
static struct list_head active_list, waiting_list;
|
||||||
static pthread_t su_collector = 0;
|
static pthread_t su_collector = 0;
|
||||||
@ -88,11 +88,41 @@ static void *collector(void *args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void su_daemon_receiver(int client, struct ucred *credential) {
|
static void database_check(struct su_info *info) {
|
||||||
LOGD("su: request from client: %d\n", client);
|
int uid = info->uid;
|
||||||
|
sqlite3 *db = get_magiskdb();
|
||||||
|
if (db) {
|
||||||
|
get_db_settings(db, -1, &info->dbs);
|
||||||
|
get_db_strings(db, -1, &info->str);
|
||||||
|
|
||||||
|
// Check multiuser settings
|
||||||
|
switch (info->dbs.v[SU_MULTIUSER_MODE]) {
|
||||||
|
case MULTIUSER_MODE_OWNER_ONLY:
|
||||||
|
if (info->uid / 100000) {
|
||||||
|
uid = -1;
|
||||||
|
info->access = NO_SU_ACCESS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MULTIUSER_MODE_OWNER_MANAGED:
|
||||||
|
uid = info->uid % 100000;
|
||||||
|
break;
|
||||||
|
case MULTIUSER_MODE_USER:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid > 0)
|
||||||
|
get_uid_policy(db, uid, &info->access);
|
||||||
|
sqlite3_close(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check our manager
|
||||||
|
if (info->access.log || info->access.notify)
|
||||||
|
validate_manager(info->str.s[SU_REQUESTER], uid / 100000, &info->st);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct su_info *get_su_info(unsigned uid) {
|
||||||
struct su_info *info = NULL, *node;
|
struct su_info *info = NULL, *node;
|
||||||
int new_request = 0;
|
|
||||||
|
|
||||||
LOCK_LIST();
|
LOCK_LIST();
|
||||||
|
|
||||||
@ -104,7 +134,7 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
|
|
||||||
// Search for existing in the active list
|
// Search for existing in the active list
|
||||||
list_for_each(node, &active_list, struct su_info, pos) {
|
list_for_each(node, &active_list, struct su_info, pos) {
|
||||||
if (node->uid == credential->uid) {
|
if (node->uid == uid) {
|
||||||
info = node;
|
info = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -112,10 +142,11 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
|
|
||||||
// If no exist, create a new request
|
// If no exist, create a new request
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
new_request = 1;
|
|
||||||
info = malloc(sizeof(*info));
|
info = malloc(sizeof(*info));
|
||||||
info->uid = credential->uid;
|
info->uid = uid;
|
||||||
info->policy = QUERY;
|
info->dbs = DEFAULT_DB_SETTINGS;
|
||||||
|
info->access = DEFAULT_SU_ACCESS;
|
||||||
|
INIT_DB_STRINGS(&info->str);
|
||||||
info->ref = 0;
|
info->ref = 0;
|
||||||
info->count = 0;
|
info->count = 0;
|
||||||
pthread_mutex_init(&info->lock, NULL);
|
pthread_mutex_init(&info->lock, NULL);
|
||||||
@ -128,65 +159,85 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
|
|
||||||
LOGD("su: request from uid=[%d] (#%d)\n", info->uid, ++info->count);
|
LOGD("su: request from uid=[%d] (#%d)\n", info->uid, ++info->count);
|
||||||
|
|
||||||
// Default values
|
|
||||||
struct su_context ctx = {
|
|
||||||
.info = info,
|
|
||||||
.to = {
|
|
||||||
.uid = UID_ROOT,
|
|
||||||
.login = 0,
|
|
||||||
.keepenv = 0,
|
|
||||||
.shell = DEFAULT_SHELL,
|
|
||||||
.command = NULL,
|
|
||||||
},
|
|
||||||
.pid = credential->pid,
|
|
||||||
.notify = new_request,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Lock before the policy is determined
|
// Lock before the policy is determined
|
||||||
LOCK_UID();
|
LOCK_UID();
|
||||||
|
|
||||||
// Not cached, do the checks
|
if (info->access.policy == QUERY) {
|
||||||
if (info->policy == QUERY) {
|
// Not cached, get data from database
|
||||||
// Get data from database
|
database_check(info);
|
||||||
database_check(&ctx);
|
|
||||||
|
|
||||||
// Check requester
|
// Check su access settings
|
||||||
if (info->policy == QUERY) {
|
switch (info->dbs.v[ROOT_ACCESS]) {
|
||||||
if (info->st.st_gid != info->st.st_uid) {
|
case ROOT_ACCESS_DISABLED:
|
||||||
LOGE("Bad uid/gid %d/%d for Superuser Requestor", info->st.st_gid, info->st.st_uid);
|
LOGE("Root access is disabled!\n");
|
||||||
info->policy = DENY;
|
info->access = NO_SU_ACCESS;
|
||||||
ctx.notify = 0;
|
break;
|
||||||
} else if ((info->uid % 100000) == (info->st.st_uid % 100000)) {
|
case ROOT_ACCESS_ADB_ONLY:
|
||||||
info->policy = ALLOW;
|
if (info->uid != UID_SHELL) {
|
||||||
info->root_access = ROOT_ACCESS_APPS_AND_ADB;
|
LOGE("Root access is disabled for ADB!\n");
|
||||||
ctx.notify = 0;
|
info->access = NO_SU_ACCESS;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ROOT_ACCESS_APPS_ONLY:
|
||||||
|
if (info->uid == UID_SHELL) {
|
||||||
|
LOGE("Root access limited to ADB only!\n");
|
||||||
|
info->access = NO_SU_ACCESS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ROOT_ACCESS_APPS_AND_ADB:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// always allow if it's root
|
// If it's the manager, allow it silently
|
||||||
if (info->uid == UID_ROOT) {
|
if ((info->uid % 100000) == (info->st.st_uid % 100000))
|
||||||
info->policy = ALLOW;
|
info->access = SILENT_SU_ACCESS;
|
||||||
info->root_access = ROOT_ACCESS_APPS_AND_ADB;
|
|
||||||
ctx.notify = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If still not determined, open a pipe and wait for results
|
// Allow if it's root
|
||||||
if (info->policy == QUERY)
|
if (info->uid == UID_ROOT)
|
||||||
xpipe2(ctx.pipefd, O_CLOEXEC);
|
info->access = SILENT_SU_ACCESS;
|
||||||
|
|
||||||
|
// If still not determined, check if manager exists
|
||||||
|
if (info->access.policy == QUERY && info->str.s[SU_REQUESTER][0] == '\0')
|
||||||
|
info->access = NO_SU_ACCESS;
|
||||||
}
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
// Fork a new process, the child process will need to setsid,
|
void su_daemon_receiver(int client, struct ucred *credential) {
|
||||||
// open a pseudo-terminal if needed, and will eventually run exec
|
LOGD("su: request from client: %d\n", client);
|
||||||
// The parent process will wait for the result and
|
|
||||||
// send the return code back to our client
|
// Default values
|
||||||
|
struct su_context ctx = {
|
||||||
|
.info = get_su_info(credential->uid),
|
||||||
|
.to = {
|
||||||
|
.uid = UID_ROOT,
|
||||||
|
.login = 0,
|
||||||
|
.keepenv = 0,
|
||||||
|
.shell = DEFAULT_SHELL,
|
||||||
|
.command = NULL,
|
||||||
|
},
|
||||||
|
.pid = credential->pid,
|
||||||
|
.pipefd = { -1, -1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// If still not determined, open a pipe and wait for results
|
||||||
|
if (ctx.info->access.policy == QUERY)
|
||||||
|
xpipe2(ctx.pipefd, O_CLOEXEC);
|
||||||
|
|
||||||
|
/* Fork a new process, the child process will need to setsid,
|
||||||
|
* open a pseudo-terminal if needed, and will eventually run exec
|
||||||
|
* The parent process will wait for the result and
|
||||||
|
* send the return code back to our client
|
||||||
|
*/
|
||||||
int child = fork();
|
int child = fork();
|
||||||
if (child < 0)
|
if (child < 0)
|
||||||
PLOGE("fork");
|
PLOGE("fork");
|
||||||
|
|
||||||
if (child) {
|
if (child) {
|
||||||
// Wait for results
|
// Wait for results
|
||||||
if (info->policy == QUERY) {
|
if (ctx.pipefd[0] >= 0) {
|
||||||
xxread(ctx.pipefd[0], &info->policy, sizeof(info->policy));
|
xxread(ctx.pipefd[0], &ctx.info->access.policy, sizeof(policy_t));
|
||||||
close(ctx.pipefd[0]);
|
close(ctx.pipefd[0]);
|
||||||
close(ctx.pipefd[1]);
|
close(ctx.pipefd[1]);
|
||||||
}
|
}
|
||||||
@ -223,7 +274,7 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
|
|
||||||
// Decrement reference count
|
// Decrement reference count
|
||||||
LOCK_LIST();
|
LOCK_LIST();
|
||||||
--info->ref;
|
--ctx.info->ref;
|
||||||
UNLOCK_LIST();
|
UNLOCK_LIST();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -276,7 +327,7 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
char *pts_slave = read_string(client);
|
char *pts_slave = read_string(client);
|
||||||
LOGD("su: pts_slave=[%s]\n", pts_slave);
|
LOGD("su: pts_slave=[%s]\n", pts_slave);
|
||||||
|
|
||||||
// The the FDs for each of the streams
|
// The FDs for each of the streams
|
||||||
int infd = recv_fd(client);
|
int infd = recv_fd(client);
|
||||||
int outfd = recv_fd(client);
|
int outfd = recv_fd(client);
|
||||||
int errfd = recv_fd(client);
|
int errfd = recv_fd(client);
|
||||||
@ -286,12 +337,12 @@ void su_daemon_receiver(int client, struct ucred *credential) {
|
|||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
if (pts_slave[0]) {
|
if (pts_slave[0]) {
|
||||||
//Check pts_slave file is owned by daemon_from_uid
|
// Check pts_slave file is owned by daemon_from_uid
|
||||||
struct stat stbuf;
|
struct stat st;
|
||||||
xstat(pts_slave, &stbuf);
|
xstat(pts_slave, &st);
|
||||||
|
|
||||||
//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 != info->uid && info->uid != 0) {
|
if(st.st_uid != credential->uid && credential->uid != 0) {
|
||||||
LOGE("su: Wrong permission of pts_slave");
|
LOGE("su: Wrong permission of pts_slave");
|
||||||
exit2(1);
|
exit2(1);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user