From 5aeb67e3cdbc8ccdb4496b8d87ec7cfb2d6fb8ad Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 10 Dec 2017 13:29:30 +0100 Subject: [PATCH] Switch to Xposed Installer's UID/GID in logcat/app service These two access files written by Xposed Installer. If they're running with the same permissions as Xposed Installer, the files and directories don't need to be world-readable/writable. Note that even before, those processes didn't have any special permissions, despite UID 0. That's because they don't have the DAC_OVERRIDE capability, which would ignore file permissions. So instead of being able to read/write root's files, we can now read/write Xposed Installer's file (plus files readable/writable by anyone). One problem is that in order to determine the UID/GID of the Xposed Installer, the process needs to run in app context. But from this context, SELinux doesn't allow changing the UID anymore. Therefore, the UID/GID needs to be determined in some other way. This is done via a separate process which stats the directory and returns the result via shared memory. The processes which want to change their UID/GID can then do this before switching their context. This conflicts with single-process service mode (which was used as an optimization when SELinux is disabled) because only processes with special UIDs can add services. Therefore, we always need to use two services, one running as root in system context and one running with Xposed Installer's permissions in app context. The latter is actually processing requests, the first one is used for registering the services. It also requires that the logcat process runs with Xposed Installer's permission, but having AID_LOG as secondary group is sufficient to read all logcat entries. --- xposed.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++- xposed.h | 2 ++ xposed_logcat.cpp | 17 +++++++---- xposed_service.cpp | 10 +++++-- xposed_shared.h | 2 ++ 5 files changed, 93 insertions(+), 9 deletions(-) diff --git a/xposed.cpp b/xposed.cpp index 2a66869f4..e7ce7be75 100644 --- a/xposed.cpp +++ b/xposed.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #if PLATFORM_SDK_VERSION >= 18 #include @@ -110,7 +112,7 @@ bool initialize(bool zygote, bool startSystemServer, const char* className, int printRomInfo(); if (startSystemServer) { - if (!xposed::service::startAll()) { + if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) { return false; } xposed::logcat::start(); @@ -425,6 +427,73 @@ void setProcessName(const char* name) { set_process_name(name); } +/** Determine the UID/GID of Xposed Installer. */ +bool determineXposedInstallerUidGid() { + if (xposed->isSELinuxEnabled) { + struct stat* st = (struct stat*) mmap(NULL, sizeof(struct stat), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (st == MAP_FAILED) { + ALOGE("Could not allocate memory in determineXposedInstallerUidGid(): %s", strerror(errno)); + return false; + } + + pid_t pid; + if ((pid = fork()) < 0) { + ALOGE("Fork in determineXposedInstallerUidGid() failed: %s", strerror(errno)); + munmap(st, sizeof(struct stat)); + return false; + } else if (pid == 0) { + // Child. +#if XPOSED_WITH_SELINUX + if (setcon(ctx_app) != 0) { + ALOGE("Could not switch to %s context", ctx_app); + exit(EXIT_FAILURE); + } +#endif // XPOSED_WITH_SELINUX + + if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, st)) != 0) { + ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno)); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + } + + // Parent. + int status; + if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + munmap(st, sizeof(struct stat)); + return false; + } + + xposed->installer_uid = st->st_uid; + xposed->installer_gid = st->st_gid; + munmap(st, sizeof(struct stat)); + return true; + } else { + struct stat st; + if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, &st)) != 0) { + ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno)); + return false; + } + + xposed->installer_uid = st.st_uid; + xposed->installer_gid = st.st_gid; + return true; + } +} + +/** Switch UID/GID to the ones of Xposed Installer. */ +bool switchToXposedInstallerUidGid() { + if (setresgid(xposed->installer_gid, xposed->installer_gid, xposed->installer_gid) != 0) { + ALOGE("Could not setgid(%d): %s", xposed->installer_gid, strerror(errno)); + return false; + } + if (setresuid(xposed->installer_uid, xposed->installer_uid, xposed->installer_uid) != 0) { + ALOGE("Could not setuid(%d): %s", xposed->installer_uid, strerror(errno)); + return false; + } + return true; +} /** Drop all capabilities except for the mentioned ones */ void dropCapabilities(int8_t keep[]) { diff --git a/xposed.h b/xposed.h index 70357fbc4..9faf78c73 100644 --- a/xposed.h +++ b/xposed.h @@ -46,6 +46,8 @@ namespace xposed { bool addJarToClasspath(); void onVmCreated(JNIEnv* env); void setProcessName(const char* name); + bool determineXposedInstallerUidGid(); + bool switchToXposedInstallerUidGid(); void dropCapabilities(int8_t keep[] = NULL); bool isMinimalFramework(); diff --git a/xposed_logcat.cpp b/xposed_logcat.cpp index c5f262d96..f7dc776fc 100644 --- a/xposed_logcat.cpp +++ b/xposed_logcat.cpp @@ -33,12 +33,6 @@ char marker[50]; //////////////////////////////////////////////////////////// static void execLogcat() { - // Ensure that we're allowed to read all log entries - if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { - ALOGE("Failed to keep capabilities: %s", strerror(errno)); - } - setresgid(AID_LOG, AID_LOG, AID_LOG); - setresuid(AID_LOG, AID_LOG, AID_LOG); int8_t keep[] = { CAP_SYSLOG, -1 }; xposed::dropCapabilities(keep); @@ -140,6 +134,16 @@ void start() { return; } + // Ensure that we're allowed to read all log entries + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { + ALOGE("Failed to keep capabilities: %s", strerror(errno)); + } + const gid_t groups[] = { AID_LOG }; + setgroups(1, groups); + if (!xposed::switchToXposedInstallerUidGid()) { + exit(EXIT_FAILURE); + } + #if XPOSED_WITH_SELINUX if (xposed->isSELinuxEnabled) { if (setcon(ctx_app) != 0) { @@ -152,6 +156,7 @@ void start() { int err = rename(XPOSEDLOG, XPOSEDLOG_OLD); if (err < 0 && errno != ENOENT) { ALOGE("%s while renaming log file %s -> %s", strerror(errno), XPOSEDLOG, XPOSEDLOG_OLD); + exit(EXIT_FAILURE); } int pipeFds[2]; diff --git a/xposed_service.cpp b/xposed_service.cpp index 6727a8535..94ec3a292 100644 --- a/xposed_service.cpp +++ b/xposed_service.cpp @@ -641,7 +641,7 @@ int XposedService::test() const { status_t XposedService::addService(const String16& name, const sp& service, bool allowIsolated) const { uid_t uid = IPCThreadState::self()->getCallingUid(); - if (!isSystem || (uid != 0)) { + if (!isSystem || (uid != xposed->installer_uid)) { ALOGE("Permission denied, not adding service %s", String8(name).string()); errno = EPERM; return -1; @@ -820,6 +820,9 @@ static void systemService() { static void appService(bool useSingleProcess) { xposed::setProcessName(useSingleProcess ? "xposed_service" : "xposed_service_app"); + if (!xposed::switchToXposedInstallerUidGid()) { + exit(EXIT_FAILURE); + } xposed::dropCapabilities(); #if XPOSED_WITH_SELINUX @@ -894,7 +897,7 @@ bool checkMembasedRunning() { } bool startAll() { - bool useSingleProcess = !xposed->isSELinuxEnabled; + bool useSingleProcess = false; if (xposed->isSELinuxEnabled && !membased::init()) { return false; } @@ -945,6 +948,9 @@ bool startMembased() { return false; } else if (pid == 0) { xposed::setProcessName("xposed_zygote_service"); + if (!xposed::switchToXposedInstallerUidGid()) { + exit(EXIT_FAILURE); + } xposed::dropCapabilities(); if (setcon(ctx_app) != 0) { ALOGE("Could not switch to %s context", ctx_app); diff --git a/xposed_shared.h b/xposed_shared.h index 63721bc85..73443c0cf 100644 --- a/xposed_shared.h +++ b/xposed_shared.h @@ -35,6 +35,8 @@ struct XposedShared { uint32_t xposedVersionInt; bool isSELinuxEnabled; bool isSELinuxEnforcing; + uid_t installer_uid; + gid_t installer_gid; // Provided by runtime-specific library, used by executable void (*onVmCreated)(JNIEnv* env);