From c9608af0c82ebbee151b61fa0f3cf1f710d23044 Mon Sep 17 00:00:00 2001 From: weishu Date: Sun, 4 Jun 2023 16:11:41 +0800 Subject: [PATCH] manager: fix capabilities and namespace save/load --- manager/app/src/main/cpp/jni.cc | 52 +++++++++++++++---- manager/app/src/main/cpp/ksu.h | 10 +++- .../main/java/me/weishu/kernelsu/Natives.kt | 5 +- .../ui/component/profile/RootProfileConfig.kt | 27 ++++++---- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/manager/app/src/main/cpp/jni.cc b/manager/app/src/main/cpp/jni.cc index fc1857e8..ce1777ba 100644 --- a/manager/app/src/main/cpp/jni.cc +++ b/manager/app/src/main/cpp/jni.cc @@ -46,7 +46,7 @@ Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) { return is_safe_mode(); } -static void fillIntArray(JNIEnv* env, jobject list, int *data, int count) { +static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) { auto cls = env->GetObjectClass(list); auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z"); auto integerCls = env->FindClass("java/lang/Integer"); @@ -57,13 +57,42 @@ static void fillIntArray(JNIEnv* env, jobject list, int *data, int count) { } } +static void addIntToList(JNIEnv *env, jobject list, int ele) { + auto cls = env->GetObjectClass(list); + auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z"); + auto integerCls = env->FindClass("java/lang/Integer"); + auto constructor = env->GetMethodID(integerCls, "", "(I)V"); + auto integer = env->NewObject(integerCls, constructor, ele); + env->CallBooleanMethod(list, add, integer); +} + +static uint64_t capListToBits(JNIEnv *env, jobject list) { + auto cls = env->GetObjectClass(list); + auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;"); + auto size = env->GetMethodID(cls, "size", "()I"); + auto listSize = env->CallIntMethod(list, size); + auto integerCls = env->FindClass("java/lang/Integer"); + auto intValue = env->GetMethodID(integerCls, "intValue", "()I"); + uint64_t result = 0; + for (int i = 0; i < listSize; ++i) { + auto integer = env->CallObjectMethod(list, get, i); + int data = env->CallIntMethod(integer, intValue); + + if (cap_valid(data)) { + result |= (1ULL << data); + } + } + + return result; +} + static int getListSize(JNIEnv *env, jobject list) { auto cls = env->GetObjectClass(list); auto size = env->GetMethodID(cls, "size", "()I"); return env->CallIntMethod(list, size); } -static void fillArrayWithList(JNIEnv* env, jobject list, int *data, int count) { +static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) { auto cls = env->GetObjectClass(list); auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;"); auto integerCls = env->FindClass("java/lang/Integer"); @@ -115,7 +144,7 @@ Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;"); auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;"); auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;"); - // auto namespacesField = env->GetFieldID(cls, "namespace", "I"); + auto namespacesField = env->GetFieldID(cls, "namespace", "I"); auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z"); auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z"); @@ -136,14 +165,19 @@ Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, env->SetIntField(obj, gidField, profile.root_profile.gid); jobject groupList = env->GetObjectField(obj, groupsField); - fillIntArray(env, groupList, profile.root_profile.groups, profile.root_profile.groups_count); + fillIntArray(env, groupList, profile.root_profile.groups, + profile.root_profile.groups_count); jobject capList = env->GetObjectField(obj, capabilitiesField); -// fillIntArray(env, capList, profile.root_profile.capabilities, 2); + for (int i = 0; i <= CAP_LAST_CAP; i++) { + if (profile.root_profile.caps.effective & (1ULL << i)) { + addIntToList(env, capList, i); + } + } env->SetObjectField(obj, domainField, env->NewStringUTF(profile.root_profile.selinux_domain)); - // env->SetIntField(obj, namespacesField, profile.root_profile.namespaces); + env->SetIntField(obj, namespacesField, profile.root_profile.namespaces); env->SetBooleanField(obj, allowSuField, profile.allow_su); } else { env->SetBooleanField(obj, nonRootUseDefaultField, @@ -173,7 +207,7 @@ Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobjec auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;"); auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;"); auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;"); -// auto namespacesField = env->GetFieldID(cls, "namespaces", "I"); + auto namespacesField = env->GetFieldID(cls, "namespace", "I"); auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z"); auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z"); @@ -224,13 +258,13 @@ Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobjec p.root_profile.groups_count = groups_count; fillArrayWithList(env, groups, p.root_profile.groups, groups_count); -// fillArrayWithList(env, capabilities, p.root_profile.capabilities, 2); + p.root_profile.caps.effective = capListToBits(env, capabilities); auto cdomain = env->GetStringUTFChars((jstring) domain, nullptr); strcpy(p.root_profile.selinux_domain, cdomain); env->ReleaseStringUTFChars((jstring) domain, cdomain); - // p.root_profile.namespaces = env->GetIntField(profile, namespacesField); + p.root_profile.namespaces = env->GetIntField(profile, namespacesField); } else { p.non_root_profile.use_default = env->GetBooleanField(profile, nonRootUseDefaultField); p.non_root_profile.umount_modules = umountModules; diff --git a/manager/app/src/main/cpp/ksu.h b/manager/app/src/main/cpp/ksu.h index 0fea38c0..3085f1ea 100644 --- a/manager/app/src/main/cpp/ksu.h +++ b/manager/app/src/main/cpp/ksu.h @@ -5,6 +5,8 @@ #ifndef KERNELSU_KSU_H #define KERNELSU_KSU_H +#include + bool become_manager(const char *); int get_version(); @@ -42,8 +44,12 @@ struct app_profile { int32_t groups[KSU_MAX_GROUPS]; int32_t groups_count; - // kernel_cap_t is u32[2] - int32_t capabilities[2]; + struct { + // kernel_cap_t is u32[2], we use u64 here to avoid alignment issues. + uint64_t effective; + uint64_t permitted; + uint64_t inheritable; + } caps; char selinux_domain[KSU_SELINUX_DOMAIN]; int32_t namespaces; diff --git a/manager/app/src/main/java/me/weishu/kernelsu/Natives.kt b/manager/app/src/main/java/me/weishu/kernelsu/Natives.kt index 13de8598..aae64a1d 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/Natives.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/Natives.kt @@ -13,7 +13,8 @@ object Natives { // minimal supported kernel version // 10915: allowlist breaking change, add app profile // 10931: app profile struct add 'version' field - const val MINIMAL_SUPPORTED_KERNEL = 10931 + // 10946: add capabilities + const val MINIMAL_SUPPORTED_KERNEL = 10946 init { System.loadLibrary("kernelsu") @@ -87,7 +88,7 @@ object Natives { val groups: List = mutableListOf(), val capabilities: List = mutableListOf(), val context: String = "su", - val namespace: Namespace = Namespace.Inherited, + val namespace: Int = Namespace.Inherited.ordinal, val nonRootUseDefault: Boolean = true, val umountModules: Boolean = true, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt index cc6a64b9..ab2040ca 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt @@ -62,9 +62,10 @@ fun RootProfileConfig( var expanded by remember { mutableStateOf(false) } val currentNamespace = when (profile.namespace) { - Natives.Profile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited) - Natives.Profile.Namespace.Global -> stringResource(R.string.profile_namespace_global) - Natives.Profile.Namespace.Individual -> stringResource(R.string.profile_namespace_individual) + Natives.Profile.Namespace.Inherited.ordinal -> stringResource(R.string.profile_namespace_inherited) + Natives.Profile.Namespace.Global.ordinal -> stringResource(R.string.profile_namespace_global) + Natives.Profile.Namespace.Individual.ordinal -> stringResource(R.string.profile_namespace_individual) + else -> stringResource(R.string.profile_namespace_inherited) } ListItem(headlineContent = { ExposedDropdownMenuBox( @@ -91,21 +92,21 @@ fun RootProfileConfig( DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_inherited)) }, onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited)) + onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited.ordinal)) expanded = false }, ) DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_global)) }, onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global)) + onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global.ordinal)) expanded = false }, ) DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_individual)) }, onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual)) + onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual.ordinal)) expanded = false }, ) @@ -164,9 +165,9 @@ fun RootProfileConfig( ) }) - val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { - it.mapNotNull { id -> - Groups.values().find { it.gid == id } + val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e -> + e.mapNotNull { g -> + Groups.values().find { it.gid == g } } } GroupsPanel(selectedGroups) { @@ -178,8 +179,12 @@ fun RootProfileConfig( ) } - val selectedCaps = profile.capabilities.mapNotNull { id -> - Capabilities.values().find { it.cap == id } + val selectedCaps = profile.capabilities.ifEmpty { + Capabilities.values().toList() + }.let { e -> + e.mapNotNull { cap -> + Capabilities.values().find { it.cap == cap } + } } CapsPanel(selectedCaps) { onProfileChange(