Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
Switch to Xposed Installer's UID/GID in logcat/app service
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rovo89 committed Dec 17, 2017
1 parent ab86a12 commit 5aeb67e
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 9 deletions.
71 changes: 70 additions & 1 deletion xposed.cpp
Expand Up @@ -18,6 +18,8 @@
#include <fcntl.h>
#include <inttypes.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>

#if PLATFORM_SDK_VERSION >= 18
#include <sys/capability.h>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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[]) {
Expand Down
2 changes: 2 additions & 0 deletions xposed.h
Expand Up @@ -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();

Expand Down
17 changes: 11 additions & 6 deletions xposed_logcat.cpp
Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand All @@ -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];
Expand Down
10 changes: 8 additions & 2 deletions xposed_service.cpp
Expand Up @@ -641,7 +641,7 @@ int XposedService::test() const {
status_t XposedService::addService(const String16& name, const sp<IBinder>& 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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -894,7 +897,7 @@ bool checkMembasedRunning() {
}

bool startAll() {
bool useSingleProcess = !xposed->isSELinuxEnabled;
bool useSingleProcess = false;
if (xposed->isSELinuxEnabled && !membased::init()) {
return false;
}
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions xposed_shared.h
Expand Up @@ -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);
Expand Down

0 comments on commit 5aeb67e

Please sign in to comment.