Magisk/native/jni/core/scripting.cpp
topjohnwu ec3705f2ed Redesign of MagiskSU's sepolicy model
Introduce new domain `magisk_client` and new file type `magisk_exec`.

Connection to magiskd's always-on socket is restricted to magisk_client
only. Whitelisted process domains can transit to magisk_client through
executing files labelled magisk_exec. The main magisk binary shall be
the only file labelled as magisk_exec throughout the whole system.
All processes thus are no longer allowed to connect to magiskd directly
without going through the proper magisk binary.

Connection failures are silenced from audit logs with dontaudit rules,
so crazy processes which traverse through all unix domain sockets to try
connection can no longer check logcat to know the actual reason behind
EACCES, leaking the denied process policy (which is u:r:magisk:s0).

This also allows us to remove many rules that open up holes in
untrusted_app domains that were used to make remote shell work properly.
Since all processes establishing the remote shell are now restricted to
the magisk_client domain, all these rules are moved to magisk_client.
This makes Magisk require fewer compromises in Android's security model.

Note: as of this commit, requesting new root access via Magisk Manager
will stop working as Magisk Manager can no longer communicate with
magiskd directly. This will be addressed in a future commit that
involves changes in both native and application side.
2020-06-03 23:29:42 -07:00

95 lines
2.2 KiB
C++

#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <magisk.hpp>
#include <utils.hpp>
#include <selinux.hpp>
using namespace std;
#define BBEXEC_CMD DATABIN "/busybox", "sh"
static void set_standalone() {
setenv("ASH_STANDALONE", "1", 1);
};
void exec_script(const char *script) {
exec_t exec {
.pre_exec = set_standalone,
.fork = fork_no_zombie
};
exec_command_sync(exec, BBEXEC_CMD, script);
}
void exec_common_scripts(const char *stage) {
LOGI("* Running %s.d scripts\n", stage);
char path[4096];
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage);
auto dir = xopen_dir(path);
if (!dir)
return;
int dfd = dirfd(dir.get());
bool pfs = stage == "post-fs-data"sv;
*(name++) = '/';
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_REG) {
if (faccessat(dfd, entry->d_name, X_OK, 0) != 0)
continue;
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
strcpy(name, entry->d_name);
exec_t exec {
.pre_exec = set_standalone,
.fork = pfs ? fork_no_zombie : fork_dont_care
};
if (pfs)
exec_command_sync(exec, BBEXEC_CMD, path);
else
exec_command(exec, BBEXEC_CMD, path);
}
}
}
void exec_module_scripts(const char *stage, const vector<string> &module_list) {
LOGI("* Running module %s scripts\n", stage);
char path[4096];
bool pfs = stage == "post-fs-data"sv;
for (auto &m : module_list) {
const char* module = m.c_str();
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
if (access(path, F_OK) == -1)
continue;
LOGI("%s: exec [%s.sh]\n", module, stage);
exec_t exec {
.pre_exec = set_standalone,
.fork = pfs ? fork_no_zombie : fork_dont_care
};
if (pfs)
exec_command_sync(exec, BBEXEC_CMD, path);
else
exec_command(exec, BBEXEC_CMD, path);
}
}
constexpr char install_script[] = R"EOF(
APK=%s
log -t Magisk "apk_install: $APK"
log -t Magisk "apk_install: `pm install -r $APK 2>&1`"
rm -f $APK
)EOF";
void install_apk(const char *apk) {
setfilecon(apk, "u:object_r:" SEPOL_FILE_TYPE ":s0");
exec_t exec {
.pre_exec = set_standalone,
.fork = fork_no_zombie
};
char cmds[sizeof(install_script) + 4096];
sprintf(cmds, install_script, apk);
exec_command_sync(exec, BBEXEC_CMD, "-c", cmds);
}