mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-07-29 09:33:49 +00:00
su: support drop capabilities
This commit is contained in:
parent
37a9724a54
commit
ff18cb8e70
@ -4,15 +4,16 @@ import com.topjohnwu.magisk.core.data.magiskdb.MagiskDB
|
||||
|
||||
class SuPolicy(
|
||||
val uid: Int,
|
||||
var policy: Int = INTERACTIVE,
|
||||
var policy: Int = QUERY,
|
||||
var remain: Long = -1L,
|
||||
var logging: Boolean = true,
|
||||
var notification: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
const val INTERACTIVE = 0
|
||||
const val QUERY = 0
|
||||
const val DENY = 1
|
||||
const val ALLOW = 2
|
||||
const val RESTRICT = 3
|
||||
}
|
||||
|
||||
fun toMap(): MutableMap<String, Any> {
|
||||
|
@ -184,6 +184,10 @@ int parse_int(string_view s) {
|
||||
return parse_num<int, 10>(s);
|
||||
}
|
||||
|
||||
uint32_t parse_uint32_hex(string_view s) {
|
||||
return parse_num<uint32_t, 16>(s);
|
||||
}
|
||||
|
||||
int switch_mnt_ns(int pid) {
|
||||
int ret = -1;
|
||||
int fd = syscall(__NR_pidfd_open, pid, 0);
|
||||
|
@ -175,6 +175,7 @@ rust::Vec<size_t> mut_u8_patch(
|
||||
rust::Slice<const uint8_t> from,
|
||||
rust::Slice<const uint8_t> to);
|
||||
|
||||
uint32_t parse_uint32_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
|
@ -88,6 +88,7 @@ pub mod ffi {
|
||||
Query,
|
||||
Deny,
|
||||
Allow,
|
||||
Restrict,
|
||||
}
|
||||
|
||||
struct ModuleInfo {
|
||||
@ -117,6 +118,7 @@ pub mod ffi {
|
||||
target_pid: i32,
|
||||
login: bool,
|
||||
keep_env: bool,
|
||||
drop_cap: bool,
|
||||
shell: String,
|
||||
command: String,
|
||||
context: String,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MODULEROOT, SECURE_DIR};
|
||||
use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MAGISKDB, 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};
|
||||
@ -69,6 +69,7 @@ pub(crate) fn restorecon() {
|
||||
path.clear();
|
||||
path.push_str(DATABIN);
|
||||
restore_syscon(&mut path).log_ok();
|
||||
unsafe { libc::chmod(cstr!(MAGISKDB).as_ptr(), 0o000) };
|
||||
}
|
||||
|
||||
pub(crate) fn restore_tmpcon() -> LoggedResult<()> {
|
||||
|
@ -22,6 +22,7 @@ impl Default for SuRequest {
|
||||
target_pid: -1,
|
||||
login: false,
|
||||
keep_env: false,
|
||||
drop_cap: false,
|
||||
shell: DEFAULT_SHELL.to_string(),
|
||||
command: "".to_string(),
|
||||
context: "".to_string(),
|
||||
@ -168,7 +169,10 @@ impl MagiskD {
|
||||
// Before unlocking, refresh the timestamp
|
||||
access.refresh();
|
||||
|
||||
// Fail fast
|
||||
if access.settings.policy == SuPolicy::Restrict {
|
||||
req.drop_cap = true;
|
||||
}
|
||||
|
||||
if access.settings.policy == SuPolicy::Deny {
|
||||
warn!("su: request rejected ({})", info.uid);
|
||||
client.write_pod(&SuPolicy::Deny.repr).ok();
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <linux/securebits.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -46,6 +49,7 @@ int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGI
|
||||
" as a primary group if the option -g is not specified\n"
|
||||
" -Z, --context CONTEXT Change SELinux context\n"
|
||||
" -t, --target PID PID to take mount namespace from\n"
|
||||
" -d, --drop-cap Drop all Linux capabilities\n"
|
||||
" -h, --help Display this help message and exit\n"
|
||||
" -, -l, --login Pretend the shell to be a login shell\n"
|
||||
" -m, -p,\n"
|
||||
@ -105,6 +109,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
{ "group", required_argument, nullptr, 'g' },
|
||||
{ "supp-group", required_argument, nullptr, 'G' },
|
||||
{ "interactive", no_argument, nullptr, 'i' },
|
||||
{ "drop-cap", no_argument, nullptr, 'd' },
|
||||
{ nullptr, 0, nullptr, 0 },
|
||||
};
|
||||
|
||||
@ -121,7 +126,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
|
||||
bool interactive = false;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:hlimps:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'c': {
|
||||
string command;
|
||||
@ -146,6 +151,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
case 'p':
|
||||
req.keep_env = true;
|
||||
break;
|
||||
case 'd':
|
||||
req.drop_cap = true;
|
||||
break;
|
||||
case 's':
|
||||
req.shell = optarg;
|
||||
break;
|
||||
@ -259,11 +267,66 @@ int su_client_main(int argc, char *argv[]) {
|
||||
return code;
|
||||
}
|
||||
|
||||
// Set effective uid back to root, otherwise setres[ug]id will fail if uid isn't root
|
||||
static void set_identity(int uid, const rust::Vec<gid_t> &groups) {
|
||||
if (seteuid(0)) {
|
||||
PLOGE("seteuid (root)");
|
||||
static void drop_caps() {
|
||||
static auto last_valid_cap = []() {
|
||||
uint32_t cap = CAP_WAKE_ALARM;
|
||||
while (prctl(PR_CAPBSET_READ, cap) >= 0) {
|
||||
cap++;
|
||||
}
|
||||
return cap - 1;
|
||||
}();
|
||||
// Drop bounding set
|
||||
for (uint32_t cap = 0; cap <= last_valid_cap; cap++) {
|
||||
if (cap != CAP_SETUID) {
|
||||
prctl(PR_CAPBSET_DROP, cap);
|
||||
}
|
||||
}
|
||||
// Clean inheritable set
|
||||
__user_cap_header_struct header = {.version = _LINUX_CAPABILITY_VERSION_3};
|
||||
__user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3] = {};
|
||||
if (capget(&header, &data[0]) == 0) {
|
||||
for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
|
||||
data[i].inheritable = 0;
|
||||
}
|
||||
capset(&header, &data[0]);
|
||||
}
|
||||
// All capabilities will be lost after exec
|
||||
prctl(PR_SET_SECUREBITS, SECBIT_NOROOT);
|
||||
// Except CAP_SETUID in bounding set, it is a marker for restricted process
|
||||
}
|
||||
|
||||
static bool proc_is_restricted(pid_t pid) {
|
||||
char buf[32] = {};
|
||||
auto bnd = "CapBnd:"sv;
|
||||
uint32_t data[_LINUX_CAPABILITY_U32S_3] = {};
|
||||
ssprintf(buf, sizeof(buf), "/proc/%d/status", pid);
|
||||
file_readline(buf, [&](string_view line) -> bool {
|
||||
if (line.starts_with(bnd)) {
|
||||
auto p = line.begin();
|
||||
advance(p, bnd.size());
|
||||
while (isspace(*p)) advance(p, 1);
|
||||
line.remove_prefix(distance(line.begin(), p));
|
||||
for (int i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
|
||||
auto cap = line.substr((_LINUX_CAPABILITY_U32S_3 - 1 - i) * 8, 8);
|
||||
data[i] = parse_uint32_hex(cap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
bool equal = true;
|
||||
for (int i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
|
||||
if (i == CAP_TO_INDEX(CAP_SETUID)) {
|
||||
if (data[i] != CAP_TO_MASK(CAP_SETUID)) equal = false;
|
||||
} else {
|
||||
if (data[i] != 0) equal = false;
|
||||
}
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
static void set_identity(int uid, const rust::Vec<gid_t> &groups) {
|
||||
gid_t gid;
|
||||
if (!groups.empty()) {
|
||||
if (setgroups(groups.size(), groups.data())) {
|
||||
@ -404,15 +467,21 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
// Unblock all signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigprocmask(SIG_SETMASK, &block_set, nullptr);
|
||||
// Config privileges
|
||||
if (!req.context.empty()) {
|
||||
auto f = xopen_file("/proc/self/attr/exec", "we");
|
||||
if (f) fprintf(f.get(), "%s", req.context.c_str());
|
||||
}
|
||||
if (req.target_uid != AID_ROOT || req.drop_cap || proc_is_restricted(pid))
|
||||
drop_caps();
|
||||
if (req.target_uid != AID_ROOT || req.gids.size() > 0)
|
||||
set_identity(req.target_uid, req.gids);
|
||||
|
||||
// Unblock all signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigprocmask(SIG_SETMASK, &block_set, nullptr);
|
||||
|
||||
execvp(req.shell.c_str(), (char **) argv);
|
||||
fprintf(stderr, "Cannot execute %s: %s\n", req.shell.c_str(), strerror(errno));
|
||||
PLOGE("exec");
|
||||
|
@ -16,6 +16,7 @@ pub const LOGFILE: &str = "/cache/magisk.log";
|
||||
pub const SECURE_DIR: &str = "/data/adb";
|
||||
pub const MODULEROOT: &str = concatcp!(SECURE_DIR, "/modules");
|
||||
pub const DATABIN: &str = concatcp!(SECURE_DIR, "/magisk");
|
||||
pub const MAGISKDB: &str = concatcp!(SECURE_DIR, "/magisk.db");
|
||||
|
||||
// tmpfs paths
|
||||
const INTERNAL_DIR: &str = ".magisk";
|
||||
|
Loading…
x
Reference in New Issue
Block a user