Magisk/native/jni/core/daemon.cpp
topjohnwu a5d7c41d20 Support Safe Mode detection
When detecting device is booting as Safe Mode, disable all modules and
MagiskHide and skip all operations. The only thing that'll be available
in this state is root (Magisk Manager will also be disabled by system).

Since the next normal boot will also have all modules disabled, this can
be used to rescue a device in the case when a rogue module causes
bootloop and no custom recovery is available (or recoveries without
the ability to decrypt data).
2020-05-08 00:45:11 -07:00

225 lines
4.9 KiB
C++

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <libgen.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <magisk.hpp>
#include <utils.hpp>
#include <daemon.hpp>
#include <selinux.hpp>
#include <db.hpp>
#include <resetprop.hpp>
#include <flags.h>
using namespace std;
int SDK_INT = -1;
bool RECOVERY_MODE = false;
string MAGISKTMP;
static struct stat self_st;
static void verify_client(int client, pid_t pid) {
// Verify caller is the same as server
char path[32];
sprintf(path, "/proc/%d/exe", pid);
struct stat st;
if (stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino) {
close(client);
pthread_exit(nullptr);
}
}
static void *request_handler(void *args) {
int client = reinterpret_cast<intptr_t>(args);
struct ucred credential;
get_client_cred(client, &credential);
if (credential.uid != 0)
verify_client(client, credential.pid);
int req = read_int(client);
switch (req) {
case MAGISKHIDE:
case POST_FS_DATA:
case LATE_START:
case BOOT_COMPLETE:
case SQLITE_CMD:
case GET_PATH:
if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
return nullptr;
}
break;
case REMOVE_MODULES:
if (credential.uid != UID_SHELL && credential.uid != UID_ROOT) {
write_int(client, 1);
close(client);
return nullptr;
}
break;
default:
break;
}
switch (req) {
case MAGISKHIDE:
magiskhide_handler(client);
break;
case SUPERUSER:
su_daemon_handler(client, &credential);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VERSION ":MAGISK");
close(client);
break;
case CHECK_VERSION_CODE:
write_int(client, MAGISK_VER_CODE);
close(client);
break;
case POST_FS_DATA:
post_fs_data(client);
break;
case LATE_START:
late_start(client);
break;
case BOOT_COMPLETE:
boot_complete(client);
break;
case SQLITE_CMD:
exec_sql(client);
break;
case REMOVE_MODULES:
foreach_modules("remove");
write_int(client, 0);
close(client);
reboot();
break;
case GET_PATH:
write_string(client, MAGISKTMP.data());
close(client);
break;
default:
close(client);
break;
}
return nullptr;
}
static void daemon_entry(int ppid) {
android_logging();
int fd = xopen("/dev/null", O_WRONLY);
xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
close(fd);
fd = xopen("/dev/zero", O_RDONLY);
xdup2(fd, STDIN_FILENO);
if (fd > STDERR_FILENO)
close(fd);
setsid();
setcon("u:r:" SEPOL_PROC_DOMAIN ":s0");
// Make sure ppid is not in acct
char src[64], dest[64];
sprintf(src, "/acct/uid_0/pid_%d", ppid);
sprintf(dest, "/acct/uid_0/pid_%d", getpid());
rename(src, dest);
// Get self stat
xreadlink("/proc/self/exe", src, sizeof(src));
MAGISKTMP = dirname(src);
xstat("/proc/self/exe", &self_st);
restore_tmpcon();
// SAR cleanups
auto mount_list = MAGISKTMP + "/" ROOTMNT;
if (access(mount_list.data(), F_OK) == 0) {
file_readline(true, mount_list.data(), [](string_view line) -> bool {
umount2(line.data(), MNT_DETACH);
return true;
});
}
unlink("/dev/.se");
LOGI(NAME_WITH_VER(Magisk) " daemon started\n");
// Get API level
parse_prop_file("/system/build.prop", [](auto key, auto val) -> bool {
if (key == "ro.build.version.sdk") {
SDK_INT = parse_int(val);
return false;
}
return true;
});
if (SDK_INT < 0) {
// In case some devices do not store this info in build.prop, fallback to getprop
auto sdk = getprop("ro.build.version.sdk");
if (!sdk.empty()) {
SDK_INT = parse_int(sdk);
}
}
LOGI("* Device API level: %d\n", SDK_INT);
// Load config status
auto config = MAGISKTMP + "/" INTLROOT "/config";
parse_prop_file(config.data(), [](auto key, auto val) -> bool {
if (key == "RECOVERYMODE" && val == "true")
RECOVERY_MODE = true;
return true;
});
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (xbind(fd, (struct sockaddr*) &sun, len))
exit(1);
xlisten(fd, 10);
// Change process name
set_nice_name("magiskd");
// Block all signals
sigset_t block_set;
sigfillset(&block_set);
pthread_sigmask(SIG_SETMASK, &block_set, nullptr);
// Loop forever to listen for requests
for (;;) {
int client = xaccept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
new_daemon_thread(request_handler, reinterpret_cast<void*>(client));
}
}
int connect_daemon(bool create) {
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(fd, (struct sockaddr*) &sun, len)) {
if (!create || getuid() != UID_ROOT || getgid() != UID_ROOT) {
LOGE("No daemon is currently running!\n");
exit(1);
}
int ppid = getpid();
LOGD("client: launching new main daemon process\n");
if (fork_dont_care() == 0) {
close(fd);
daemon_entry(ppid);
}
while (connect(fd, (struct sockaddr*) &sun, len))
usleep(10000);
}
return fd;
}