diff --git a/native/src/Android.mk b/native/src/Android.mk index ef8b57e01..c0df67078 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -19,7 +19,6 @@ LOCAL_SRC_FILES := \ core/magisk.cpp \ core/daemon.cpp \ core/scripting.cpp \ - core/selinux.cpp \ core/sqlite.cpp \ core/module.cpp \ core/thread.cpp \ diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index 69116951c..e35e44256 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -85,6 +85,7 @@ pub trait Utf8CStrBuf: Write + AsRef + Deref { fn capacity(&self) -> usize; fn clear(&mut self); fn as_mut_ptr(&mut self) -> *mut c_char; + fn truncate(&mut self, new_len: usize); #[inline(always)] fn is_empty(&self) -> bool { @@ -183,6 +184,11 @@ impl Utf8CStrBuf for Utf8CString { fn as_mut_ptr(&mut self) -> *mut c_char { self.0.as_mut_ptr().cast() } + + fn truncate(&mut self, new_len: usize) { + self.0.truncate(new_len); + self.0.nul_terminate(); + } } impl From for Utf8CString { @@ -337,6 +343,12 @@ impl ToOwned for Utf8CStr { } } +impl AsRef for Utf8CStr { + fn as_ref(&self) -> &Utf8CStr { + self + } +} + // Notice that we only implement ExternType on Utf8CStr *reference* unsafe impl ExternType for &Utf8CStr { type Id = type_id!("rust::Utf8CStr"); @@ -449,7 +461,7 @@ macro_rules! impl_cstr_misc { self == other.as_cstr() } } - impl, $($g)*> PartialEq for $t { + impl + ?Sized, $($g)*> PartialEq for $t { #[inline(always)] fn eq(&self, other: &T) -> bool { self.as_bytes_with_nul() == other.as_ref().as_bytes_with_nul() @@ -521,6 +533,13 @@ macro_rules! impl_cstr_buf { fn as_mut_ptr(&mut self) -> *mut c_char { self.buf.as_mut_ptr().cast() } + fn truncate(&mut self, new_len: usize) { + if self.used <= new_len { + return; + } + self.buf[new_len] = b'\0'; + self.used = new_len; + } } )*} } diff --git a/native/src/core/applet_stub.cpp b/native/src/core/applet_stub.cpp index 291abb962..271834c6a 100644 --- a/native/src/core/applet_stub.cpp +++ b/native/src/core/applet_stub.cpp @@ -1,7 +1,6 @@ #include #include -#include #include int main(int argc, char *argv[]) { diff --git a/native/src/core/applets.cpp b/native/src/core/applets.cpp index 0b6d0b537..80a5f6211 100644 --- a/native/src/core/applets.cpp +++ b/native/src/core/applets.cpp @@ -3,7 +3,6 @@ #include #include -#include #include using namespace std; diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index 937a35e6f..9c0098c24 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include using namespace std; @@ -339,6 +338,16 @@ static void switch_cgroup(const char *cgroup, int pid) { close(fd); } +static int setcon(const char *con) { + int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC); + if (fd < 0) + return fd; + size_t len = strlen(con) + 1; + int rc = write(fd, con, len); + close(fd); + return rc != len; +} + static void daemon_entry() { android_logging(); @@ -383,8 +392,6 @@ static void daemon_entry() { set_prop("ro.vendor.mtk_model", "0"); } - restore_tmpcon(); - // Cleanups const char *tmp = get_magisk_tmp(); char path[64]; diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 743c1d216..d83d78259 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -8,6 +8,7 @@ use crate::get_prop; use crate::logging::{magisk_logging, setup_logfile, start_log_daemon}; use crate::mount::{clean_mounts, setup_mounts}; use crate::package::ManagerInfo; +use crate::selinux::restore_tmpcon; use crate::su::SuInfo; use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, cstr, error, info, libc}; @@ -259,6 +260,8 @@ pub fn daemon_entry() { } info!("* Device API level: {}", sdk_int); + restore_tmpcon().log_ok(); + let magiskd = MagiskD { sdk_int, is_emulator, diff --git a/native/src/core/deny/utils.cpp b/native/src/core/deny/utils.cpp index 8173934ed..016d12aa2 100644 --- a/native/src/core/deny/utils.cpp +++ b/native/src/core/deny/utils.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "deny.hpp" @@ -109,10 +108,10 @@ static bool proc_name_match(int pid, string_view name) { bool proc_context_match(int pid, string_view context) { char buf[PATH_MAX]; - char con[1024]; + char con[1024] = {0}; sprintf(buf, "/proc/%d", pid); - if (lgetfilecon(buf, { con, sizeof(con) }) >= 0) { + if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) { return str_starts(con, context); } return false; diff --git a/native/src/core/include/selinux.hpp b/native/src/core/include/selinux.hpp deleted file mode 100644 index 3b59a928e..000000000 --- a/native/src/core/include/selinux.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -int setcon(const char *con); -int getfilecon(const char *path, byte_data con); -int lgetfilecon(const char *path, byte_data con); -int fgetfilecon(int fd, byte_data con); -int setfilecon(const char *path, const char *con); -int lsetfilecon(const char *path, const char *con); -int fsetfilecon(int fd, const char *con); -int getfilecon_at(int dirfd, const char *name, byte_data con); -void setfilecon_at(int dirfd, const char *name, const char *con); - -void restorecon(); -void restore_tmpcon(); diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 1d132bc10..810386571 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -15,6 +15,7 @@ use derive::Decodable; use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; use mount::{find_preinit_device, revert_unmount}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; +use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon}; use socket::{recv_fd, recv_fds, send_fd, send_fds}; use std::fs::File; use std::mem::ManuallyDrop; @@ -31,6 +32,7 @@ mod logging; mod mount; mod package; mod resetprop; +mod selinux; mod socket; mod su; mod zygisk; @@ -205,13 +207,16 @@ pub mod ffi { fn recv_fd(socket: i32) -> i32; fn recv_fds(socket: i32) -> Vec; unsafe fn write_to_fd(self: &SuRequest, fd: i32); - - #[namespace = "rust"] - fn daemon_entry(); - fn pump_tty(infd: i32, outfd: i32); fn get_pty_num(fd: i32) -> i32; fn restore_stdin() -> bool; + fn restorecon(); + fn lgetfilecon(path: Utf8CStrRef, con: &mut [u8]) -> bool; + fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool; + fn lsetfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool; + + #[namespace = "rust"] + fn daemon_entry(); } // Default constructors diff --git a/native/src/core/magisk.cpp b/native/src/core/magisk.cpp index 3dd810d9e..e262d6e55 100644 --- a/native/src/core/magisk.cpp +++ b/native/src/core/magisk.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include using namespace std; diff --git a/native/src/core/module.cpp b/native/src/core/module.cpp index d1fe8ee82..90b6d4178 100644 --- a/native/src/core/module.cpp +++ b/native/src/core/module.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "node.hpp" diff --git a/native/src/core/scripting.cpp b/native/src/core/scripting.cpp index 06101eb0b..462be09e4 100644 --- a/native/src/core/scripting.cpp +++ b/native/src/core/scripting.cpp @@ -4,7 +4,6 @@ #include #include -#include #include using namespace std; diff --git a/native/src/core/selinux.cpp b/native/src/core/selinux.cpp deleted file mode 100644 index 5dfb240a5..000000000 --- a/native/src/core/selinux.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace std; - -int setcon(const char *con) { - int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC); - if (fd < 0) - return fd; - size_t len = strlen(con) + 1; - int rc = write(fd, con, len); - close(fd); - return rc != len; -} - -int getfilecon(const char *path, byte_data con) { - return syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, con.buf(), con.sz()); -} - -int lgetfilecon(const char *path, byte_data con) { - return syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, con.buf(), con.sz()); -} - -int fgetfilecon(int fd, byte_data con) { - return syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, con.buf(), con.sz()); -} - -int setfilecon(const char *path, const char *con) { - return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0); -} - -int lsetfilecon(const char *path, const char *con) { - return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0); -} - -int fsetfilecon(int fd, const char *con) { - return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, con, strlen(con) + 1, 0); -} - -int getfilecon_at(int dirfd, const char *name, byte_data con) { - char path[4096]; - fd_pathat(dirfd, name, path, sizeof(path)); - return lgetfilecon(path, con); -} - -void setfilecon_at(int dirfd, const char *name, const char *con) { - char path[4096]; - fd_pathat(dirfd, name, path, sizeof(path)); - lsetfilecon(path, con); -} - -#define UNLABEL_CON "u:object_r:unlabeled:s0" -#define SYSTEM_CON "u:object_r:system_file:s0" -#define ADB_CON "u:object_r:adb_data_file:s0" -#define ROOT_CON "u:object_r:rootfs:s0" - -static void restore_syscon_from_null(int dirfd) { - struct dirent *entry; - char con[1024]; - - if (fgetfilecon(dirfd, { con, sizeof(con) }) >= 0) { - if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0) - fsetfilecon(dirfd, SYSTEM_CON); - } - - auto dir = xopen_dir(dirfd); - while ((entry = xreaddir(dir.get()))) { - int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); - if (entry->d_type == DT_DIR) { - restore_syscon_from_null(fd); - continue; - } else if (entry->d_type == DT_REG) { - if (fgetfilecon(fd, { con, sizeof(con) }) >= 0) { - if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0) - fsetfilecon(fd, SYSTEM_CON); - } - } else if (entry->d_type == DT_LNK) { - if (getfilecon_at(dirfd, entry->d_name, { con, sizeof(con) }) >= 0) { - if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0) - setfilecon_at(dirfd, entry->d_name, SYSTEM_CON); - } - } - close(fd); - } -} - -static void restore_syscon(int dirfd) { - struct dirent *entry; - - fsetfilecon(dirfd, SYSTEM_CON); - fchown(dirfd, 0, 0); - - auto dir = xopen_dir(dirfd); - while ((entry = xreaddir(dir.get()))) { - int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); - if (entry->d_type == DT_DIR) { - restore_syscon(fd); - continue; - } else if (entry->d_type) { - fsetfilecon(fd, SYSTEM_CON); - fchown(fd, 0, 0); - } - close(fd); - } -} - -void restorecon() { - int fd = xopen("/sys/fs/selinux/context", O_WRONLY | O_CLOEXEC); - if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0) - lsetfilecon(SECURE_DIR, ADB_CON); - close(fd); - lsetfilecon(MODULEROOT, SYSTEM_CON); - restore_syscon_from_null(xopen(MODULEROOT, O_RDONLY | O_CLOEXEC)); - restore_syscon(xopen(DATABIN, O_RDONLY | O_CLOEXEC)); -} - -void restore_tmpcon() { - const char *tmp = get_magisk_tmp(); - if (tmp == "/sbin"sv) - setfilecon(tmp, ROOT_CON); - else - chmod(tmp, 0711); - - auto dir = xopen_dir(tmp); - int dfd = dirfd(dir.get()); - - for (dirent *entry; (entry = xreaddir(dir.get()));) - setfilecon_at(dfd, entry->d_name, SYSTEM_CON); - - string logd = tmp + "/"s LOG_PIPE; - setfilecon(logd.data(), MAGISK_LOG_CON); -} diff --git a/native/src/core/selinux.rs b/native/src/core/selinux.rs new file mode 100644 index 000000000..58a7ae326 --- /dev/null +++ b/native/src/core/selinux.rs @@ -0,0 +1,108 @@ +use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MODULEROOT, SECURE_DIR}; +use crate::ffi::get_magisk_tmp; +use base::libc::{O_CLOEXEC, O_WRONLY}; +use base::{Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, libc}; +use std::io::Write; + +const UNLABEL_CON: &Utf8CStr = cstr!("u:object_r:unlabeled:s0"); +const SYSTEM_CON: &Utf8CStr = cstr!("u:object_r:system_file:s0"); +const ADB_CON: &Utf8CStr = cstr!("u:object_r:adb_data_file:s0"); +const ROOT_CON: &Utf8CStr = cstr!("u:object_r:rootfs:s0"); + +fn restore_syscon_from_unlabeled( + path: &mut dyn Utf8CStrBuf, + con: &mut dyn Utf8CStrBuf, +) -> LoggedResult<()> { + let dir_path_len = path.len(); + if path.get_secontext(con).log().is_ok() && con.as_str() == UNLABEL_CON { + path.set_secontext(SYSTEM_CON)?; + } + let mut dir = Directory::open(path)?; + while let Some(ref e) = dir.read()? { + path.truncate(dir_path_len); + path.append_path(e.name()); + if e.is_dir() { + restore_syscon_from_unlabeled(path, con)?; + } else if (e.is_file() || e.is_symlink()) + && path.get_secontext(con).log().is_ok() + && con.as_str() == UNLABEL_CON + { + path.set_secontext(SYSTEM_CON)?; + } + } + Ok(()) +} + +fn restore_syscon(path: &mut dyn Utf8CStrBuf) -> LoggedResult<()> { + let dir_path_len = path.len(); + path.set_secontext(SYSTEM_CON)?; + unsafe { libc::lchown(path.as_ptr(), 0, 0) }; + let mut dir = Directory::open(path)?; + while let Some(ref e) = dir.read()? { + path.truncate(dir_path_len); + path.append_path(e.name()); + if e.is_dir() { + restore_syscon(path)?; + } else if e.is_file() || e.is_symlink() { + path.set_secontext(SYSTEM_CON)?; + unsafe { libc::lchown(path.as_ptr(), 0, 0) }; + } + } + Ok(()) +} + +pub(crate) fn restorecon() { + if let Ok(mut file) = cstr!("/sys/fs/selinux/context") + .open(O_WRONLY | O_CLOEXEC) + .log() + { + if file.write_all(ADB_CON.as_bytes_with_nul()).is_ok() { + cstr!(SECURE_DIR).set_secontext(ADB_CON).log_ok(); + } + } + + let mut path = cstr::buf::default(); + let mut con = cstr::buf::new::<1024>(); + path.push_str(MODULEROOT); + path.set_secontext(SYSTEM_CON).log_ok(); + restore_syscon_from_unlabeled(&mut path, &mut con).log_ok(); + + path.clear(); + path.push_str(DATABIN); + restore_syscon(&mut path).log_ok(); +} + +pub(crate) fn restore_tmpcon() -> LoggedResult<()> { + let tmp = get_magisk_tmp(); + if tmp == "/sbin" { + tmp.set_secontext(ROOT_CON)?; + } else { + unsafe { libc::chmod(tmp.as_ptr(), 0o711) }; + } + + let mut path = cstr::buf::default(); + let mut dir = Directory::open(tmp)?; + while let Some(ref e) = dir.read()? { + e.resolve_path(&mut path)?; + path.set_secontext(SYSTEM_CON)?; + } + + path.clear(); + path.append_path(tmp).append_path(LOG_PIPE); + path.set_secontext(cstr!(MAGISK_LOG_CON))?; + + Ok(()) +} + +pub(crate) fn lgetfilecon(path: &Utf8CStr, con: &mut [u8]) -> bool { + let mut con = cstr::buf::wrap(con); + path.get_secontext(&mut con).is_ok() +} + +pub(crate) fn setfilecon(path: &Utf8CStr, con: &Utf8CStr) -> bool { + path.follow_link().set_secontext(con).is_ok() +} + +pub(crate) fn lsetfilecon(path: &Utf8CStr, con: &Utf8CStr) -> bool { + path.set_secontext(con).is_ok() +} diff --git a/native/src/core/su/connect.cpp b/native/src/core/su/connect.cpp index f891b8539..fe1762ad9 100644 --- a/native/src/core/su/connect.cpp +++ b/native/src/core/su/connect.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index 0f57a4216..a72672b58 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "zygisk.hpp" diff --git a/native/src/include/consts.rs b/native/src/include/consts.rs index 05870c6af..42305eb6c 100644 --- a/native/src/include/consts.rs +++ b/native/src/include/consts.rs @@ -15,16 +15,25 @@ pub const LOGFILE: &str = "/cache/magisk.log"; // data paths pub const SECURE_DIR: &str = "/data/adb"; pub const MODULEROOT: &str = concatcp!(SECURE_DIR, "/modules"); +pub const DATABIN: &str = concatcp!(SECURE_DIR, "/magisk"); // tmpfs paths const INTERNAL_DIR: &str = ".magisk"; -pub const LOG_PIPE: &str = concatcp!(INTERNAL_DIR, "/device/log"); pub const MAIN_CONFIG: &str = concatcp!(INTERNAL_DIR, "/config"); pub const PREINITMIRR: &str = concatcp!(INTERNAL_DIR, "/preinit"); pub const MODULEMNT: &str = concatcp!(INTERNAL_DIR, "/modules"); pub const WORKERDIR: &str = concatcp!(INTERNAL_DIR, "/worker"); pub const DEVICEDIR: &str = concatcp!(INTERNAL_DIR, "/device"); pub const PREINITDEV: &str = concatcp!(DEVICEDIR, "/preinit"); +pub const LOG_PIPE: &str = concatcp!(DEVICEDIR, "/log"); pub const ROOTOVL: &str = concatcp!(INTERNAL_DIR, "/rootdir"); pub const ROOTMNT: &str = concatcp!(ROOTOVL, "/.mount_list"); pub const SELINUXMOCK: &str = concatcp!(INTERNAL_DIR, "/selinux"); + +// Unconstrained domain the daemon and root processes run in +pub const SEPOL_PROC_DOMAIN: &str = "magisk"; +// Unconstrained file type that anyone can access +pub const SEPOL_FILE_TYPE: &str = "magisk_file"; +// Log pipe that only root and zygote can open +pub const SEPOL_LOG_TYPE: &str = "magisk_log_file"; +pub const MAGISK_LOG_CON: &str = concatcp!("u:object_r:", SEPOL_LOG_TYPE, ":s0"); diff --git a/native/src/sepolicy/lib.rs b/native/src/sepolicy/lib.rs index 5a95e8fe5..75868b41f 100644 --- a/native/src/sepolicy/lib.rs +++ b/native/src/sepolicy/lib.rs @@ -6,6 +6,9 @@ use std::fmt::Write; use crate::ffi::SePolicy; +#[path = "../include/consts.rs"] +mod consts; + #[cfg(feature = "main")] mod cli; mod rules; diff --git a/native/src/sepolicy/rules.rs b/native/src/sepolicy/rules.rs index 5120118b4..b55d23678 100644 --- a/native/src/sepolicy/rules.rs +++ b/native/src/sepolicy/rules.rs @@ -1,3 +1,4 @@ +use crate::consts::{SEPOL_FILE_TYPE, SEPOL_LOG_TYPE, SEPOL_PROC_DOMAIN}; use crate::{SePolicy, ffi::Xperm}; use base::{LogLevel, set_log_level_state}; @@ -12,22 +13,22 @@ macro_rules! rules { vec!["servicemanager", "vndservicemanager", "hwservicemanager"] }; (@args [proc]) => { - vec!["magisk"] + vec![SEPOL_PROC_DOMAIN] }; (@args [file]) => { - vec!["magisk_file"] + vec![SEPOL_FILE_TYPE] }; (@args [log]) => { - vec!["magisk_log_file"] + vec![SEPOL_LOG_TYPE] }; (@args proc) => { - "magisk" + SEPOL_PROC_DOMAIN }; (@args file) => { - "magisk_file" + SEPOL_FILE_TYPE }; (@args log) => { - "magisk_log_file" + SEPOL_LOG_TYPE }; (@args [$($arg:tt)*]) => { vec![$($arg)*]