Compare commits
348 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5be402710 | ||
|
|
809b74a5f3 | ||
|
|
a98a718d61 | ||
|
|
45ea8455fc | ||
|
|
f81023246f | ||
|
|
8b06df3468 | ||
|
|
46cfd936a0 | ||
|
|
de04ea9db0 | ||
|
|
3853928305 | ||
|
|
5e64eee624 | ||
|
|
3a97e6580f | ||
|
|
228b6b1273 | ||
|
|
314fbe8cf7 | ||
|
|
ccb38061ee | ||
|
|
b631344e7c | ||
|
|
85d739a153 | ||
|
|
9817724a10 | ||
|
|
7c7e72f111 | ||
|
|
c5d473c815 | ||
|
|
f2de18bc26 | ||
|
|
61f5785729 | ||
|
|
a7713f0445 | ||
|
|
0109723187 | ||
|
|
15fe454b6d | ||
|
|
5c80febdbd | ||
|
|
39f4a5991a | ||
|
|
b2565fda08 | ||
|
|
923ba8c213 | ||
|
|
c94608a2eb | ||
|
|
ccb59cb7ca | ||
|
|
36d93501c8 | ||
|
|
27f6db889a | ||
|
|
6898d82daf | ||
|
|
8d8d0180ae | ||
|
|
f7b875fc16 | ||
|
|
0d73908d1b | ||
|
|
3dd210cfec | ||
|
|
18c65c8495 | ||
|
|
4f9b745cd0 | ||
|
|
a585989a03 | ||
|
|
ba6f29557e | ||
|
|
79b78e35ba | ||
|
|
932fabd35c | ||
|
|
4ea5c8f450 | ||
|
|
c6b184793e | ||
|
|
e3ef521de5 | ||
|
|
3d4e0e48b4 | ||
|
|
ff3071ca08 | ||
|
|
dd969eac22 | ||
|
|
9f2e5f513d | ||
|
|
385f4ab2c5 | ||
|
|
6826406494 | ||
|
|
6465e7a874 | ||
|
|
c753dd1345 | ||
|
|
06c8580788 | ||
|
|
5f228f1896 | ||
|
|
2368c5afd5 | ||
|
|
16ec695b63 | ||
|
|
404352b536 | ||
|
|
8e7f1f1cc7 | ||
|
|
d2a6fa4513 | ||
|
|
9574409955 | ||
|
|
9c2924de78 | ||
|
|
d7878ddd45 | ||
|
|
bc3399fd1b | ||
|
|
ba1aaaa160 | ||
|
|
a4e5a571bd | ||
|
|
3c501295b7 | ||
|
|
a8acea9180 | ||
|
|
4f79c94ab9 | ||
|
|
a14551b3ec | ||
|
|
2ea748dac1 | ||
|
|
429874b4d6 | ||
|
|
cd86589ad3 | ||
|
|
22cb7596a7 | ||
|
|
029ae8d389 | ||
|
|
58c8289890 | ||
|
|
94fa1e360a | ||
|
|
1d1ce396d3 | ||
|
|
99d58c8cfd | ||
|
|
ad2a23f55e | ||
|
|
f48d2e6cac | ||
|
|
d45676f059 | ||
|
|
0439a00f1d | ||
|
|
cbcaa07fd5 | ||
|
|
c4d8c49e5c | ||
|
|
c8020b2066 | ||
|
|
8442ebcb7a | ||
|
|
25fbc22f66 | ||
|
|
a4a9df3a25 | ||
|
|
19a67fb76c | ||
|
|
3a2d55237d | ||
|
|
98e617f1bd | ||
|
|
d84a88f059 | ||
|
|
ae7b4dcbed | ||
|
|
684a5d1ccd | ||
|
|
35b02e3c73 | ||
|
|
db6a59ec4e | ||
|
|
46846dce91 | ||
|
|
cd21af6728 | ||
|
|
92a483d222 | ||
|
|
04ca981e4d | ||
|
|
83209a5259 | ||
|
|
3cfc6d6a31 | ||
|
|
01ac06c3fd | ||
|
|
906c4bdb01 | ||
|
|
c17d7b38eb | ||
|
|
fa57ccccf4 | ||
|
|
7e7713ee4a | ||
|
|
7afcdb3059 | ||
|
|
5df821ed41 | ||
|
|
12fc2e6d5e | ||
|
|
9f869090d2 | ||
|
|
bb8b991110 | ||
|
|
10548f9243 | ||
|
|
96d33e62bb | ||
|
|
7be8c15b85 | ||
|
|
14bb6afd0b | ||
|
|
54dad4ceb2 | ||
|
|
a60395ba35 | ||
|
|
095385f814 | ||
|
|
7e595e1730 | ||
|
|
ded31c2043 | ||
|
|
a7f840d811 | ||
|
|
05cca26075 | ||
|
|
5ce6c210c4 | ||
|
|
8f49898155 | ||
|
|
fd3a22360a | ||
|
|
ef36a36e9a | ||
|
|
7a1a08064b | ||
|
|
36862d6175 | ||
|
|
cda7e4c6c0 | ||
|
|
0b63cc445c | ||
|
|
2433ced81a | ||
|
|
1f04f13e44 | ||
|
|
184467c691 | ||
|
|
05ed1a3714 | ||
|
|
163531fcd2 | ||
|
|
049956aaa9 | ||
|
|
6530d06710 | ||
|
|
88135d8363 | ||
|
|
548258f922 | ||
|
|
46b9f5fb4b | ||
|
|
413e9ab8a9 | ||
|
|
c3644da85b | ||
|
|
7b4b5b431f | ||
|
|
7479c0b81b | ||
|
|
0381d12be2 | ||
|
|
4425c88d5a | ||
|
|
7828c5c107 | ||
|
|
76046c84cd | ||
|
|
623dd15cbf | ||
|
|
ab13ed5c16 | ||
|
|
e171ca15cb | ||
|
|
4fc369a059 | ||
|
|
18ad2afadb | ||
|
|
3badbcd4bc | ||
|
|
1b5b235bd9 | ||
|
|
fdf5e7104e | ||
|
|
ed6b2e0a8e | ||
|
|
704f7cba32 | ||
|
|
860bdce295 | ||
|
|
d8a8ef6458 | ||
|
|
d37a78ea2d | ||
|
|
6c9bf69718 | ||
|
|
776bcc4d5d | ||
|
|
bf5cb885b5 | ||
|
|
a533a490bd | ||
|
|
c6d9f76c7b | ||
|
|
66032391af | ||
|
|
da0e16bd26 | ||
|
|
53d763cdf9 | ||
|
|
9ebddde0d5 | ||
|
|
03a164ebb7 | ||
|
|
4769065cfc | ||
|
|
9b209765c4 | ||
|
|
d7c101e244 | ||
|
|
a32f89403b | ||
|
|
2cd673d776 | ||
|
|
14fea6f8a3 | ||
|
|
02f1aec6e9 | ||
|
|
826661dffb | ||
|
|
f86c71efc5 | ||
|
|
06018a2f03 | ||
|
|
a2193841d5 | ||
|
|
1324a7f54e | ||
|
|
5df9431a22 | ||
|
|
e54989e51a | ||
|
|
cf50be122e | ||
|
|
7f9048724f | ||
|
|
3dde6d9a25 | ||
|
|
132e9ef8ed | ||
|
|
e6436b340c | ||
|
|
9cdf98782d | ||
|
|
dece57cacf | ||
|
|
3f07ea29ae | ||
|
|
c8e103062a | ||
|
|
91312effba | ||
|
|
fd60cda3b3 | ||
|
|
5323a500dd | ||
|
|
0ce7bc2627 | ||
|
|
c9c62b25d2 | ||
|
|
f8904b1b02 | ||
|
|
89ce65e8ba | ||
|
|
994fdfddf2 | ||
|
|
557e7f8153 | ||
|
|
9e9bb685f0 | ||
|
|
99bec0e439 | ||
|
|
0400b94674 | ||
|
|
247f7d4aee | ||
|
|
088ce97697 | ||
|
|
c0a86544d8 | ||
|
|
47bd84f3d1 | ||
|
|
06e714b4e7 | ||
|
|
1439e486a1 | ||
|
|
801bcb0e1f | ||
|
|
54b5fb5fdb | ||
|
|
1cc9fce2c6 | ||
|
|
46fefc299c | ||
|
|
23cc0ceff1 | ||
|
|
4a610af452 | ||
|
|
8177afa81e | ||
|
|
3588282b43 | ||
|
|
257f0ca6de | ||
|
|
c863ff6f49 | ||
|
|
e99a14290f | ||
|
|
18e60ededa | ||
|
|
22e4b69231 | ||
|
|
1853d9decf | ||
|
|
d286f49e11 | ||
|
|
7103779a11 | ||
|
|
4350d309da | ||
|
|
7051b22536 | ||
|
|
e0bce04e79 | ||
|
|
c75b041c40 | ||
|
|
d2a3f0fcad | ||
|
|
696c3059b6 | ||
|
|
ab8e966b7f | ||
|
|
7ece40bb2c | ||
|
|
d1aa6c8beb | ||
|
|
bfed2d700a | ||
|
|
59339b806a | ||
|
|
2433d64b6b | ||
|
|
a622657092 | ||
|
|
b4e682148a | ||
|
|
02474a5953 | ||
|
|
450dbf14fc | ||
|
|
d89eab2c34 | ||
|
|
a6b86a4f99 | ||
|
|
33d1f18395 | ||
|
|
8ebe60ca04 | ||
|
|
980613c6a9 | ||
|
|
47bcc956a3 | ||
|
|
00de4e1c64 | ||
|
|
97ec718fea | ||
|
|
5c96f951b5 | ||
|
|
7e446efac4 | ||
|
|
c06d694ebc | ||
|
|
bd0b07cba9 | ||
|
|
e54339cf4e | ||
|
|
320e08b8fb | ||
|
|
0da8ecb071 | ||
|
|
cc54abd273 | ||
|
|
ee781cf959 | ||
|
|
a21b18c5de | ||
|
|
1644f22c98 | ||
|
|
7a338b1b43 | ||
|
|
324dc0844f | ||
|
|
f7fe0cf748 | ||
|
|
bf1a45963b | ||
|
|
64ee09fd12 | ||
|
|
b9a84a15bc | ||
|
|
6a1e1d788b | ||
|
|
4b86989bf9 | ||
|
|
d3f8c128da | ||
|
|
bbb2748494 | ||
|
|
b9e6246d65 | ||
|
|
7bf13fbfca | ||
|
|
e9ee2304d3 | ||
|
|
f7b4b4b82d | ||
|
|
15b9c4dbbf | ||
|
|
3b966c536b | ||
|
|
b5e5be2572 | ||
|
|
253df1ea47 | ||
|
|
134684a139 | ||
|
|
9c07fa6889 | ||
|
|
c3f66e15e9 | ||
|
|
b6e2fa383a | ||
|
|
61f85a029e | ||
|
|
b1564b77a2 | ||
|
|
cc0a3590ce | ||
|
|
e793219c2b | ||
|
|
776a753206 | ||
|
|
7b6470cc79 | ||
|
|
eb5fdbbf3f | ||
|
|
8db55f56a9 | ||
|
|
62635879e0 | ||
|
|
86634aac3d | ||
|
|
af25f8d49e | ||
|
|
cd78c2693a | ||
|
|
8ff9fab414 | ||
|
|
b8eebcda5a | ||
|
|
85291de02a | ||
|
|
cb7abc88dd | ||
|
|
918e7ae0b7 | ||
|
|
0a804ba170 | ||
|
|
4c512dc7ff | ||
|
|
fcb7c3e99d | ||
|
|
b827360ac6 | ||
|
|
ca7b53370e | ||
|
|
230ca54d63 | ||
|
|
2f43ad4f76 | ||
|
|
9c1ff635e3 | ||
|
|
ef97f0e4d9 | ||
|
|
2e394903cc | ||
|
|
7978cbafa5 | ||
|
|
c89a3dbcd9 | ||
|
|
13c7912320 | ||
|
|
abbe385382 | ||
|
|
0b80137f17 | ||
|
|
c4ff89c13d | ||
|
|
ce3a7ec189 | ||
|
|
2bb789212a | ||
|
|
7ef9230d66 | ||
|
|
fbaa69f3fb | ||
|
|
e78ee720b5 | ||
|
|
04b603394a | ||
|
|
c9c7a5f4e3 | ||
|
|
044b4a2f9c | ||
|
|
59cd8d1c3b | ||
|
|
36617bf0a1 | ||
|
|
5b49054055 | ||
|
|
a17a220745 | ||
|
|
0729066a6f | ||
|
|
d4dcf610c9 | ||
|
|
78e0dc6da2 | ||
|
|
a9a10466b3 | ||
|
|
65d5d6a494 | ||
|
|
e552163d9e | ||
|
|
c950705044 | ||
|
|
9e7aabf3f7 | ||
|
|
a20a89da03 | ||
|
|
9551ca4fe8 | ||
|
|
a2431d50ce | ||
|
|
8b74f7d466 | ||
|
|
b5d9607e8e | ||
|
|
475b3998a1 | ||
|
|
23ed4384e6 |
71
.cursor/rules/general.mdc
Normal file
71
.cursor/rules/general.mdc
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
description: "Project-wide coding standards and tooling rules for Kotlin, Rust, C++, C, and Java (Gradle)."
|
||||||
|
globs:
|
||||||
|
- "**/*.kt"
|
||||||
|
- "**/*.rs"
|
||||||
|
- "**/*.cpp"
|
||||||
|
- "**/*.c"
|
||||||
|
- "**/*.java"
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Project Coding Standards
|
||||||
|
|
||||||
|
## Universal Rules
|
||||||
|
- Role: You are an expert senior engineer. Always generate maintainable, secure, performant, and idiomatic code.
|
||||||
|
- Prefer readability over cleverness. Keep changes focused and minimal.
|
||||||
|
- Follow SOLID, clean architecture, immutability-first, modular design.
|
||||||
|
- Remove unused imports, dead code, commented-out blocks, TODOs, debug logs.
|
||||||
|
- Use descriptive naming; avoid magic numbers.
|
||||||
|
- Document non-obvious logic and unsafe operations.
|
||||||
|
|
||||||
|
## Formatting & Tooling
|
||||||
|
- Formatters to apply automatically on save or pre-commit: `ktlint`, `detekt`, `rustfmt`, `cargo clippy`, `clang-format`, `clang-tidy`, `spotless`.
|
||||||
|
- Pre-commit hooks should run: `ktlint --format`, `cargo fmt`, `clang-format -i`, `gradle spotlessApply`.
|
||||||
|
- CI must enforce formatting and linting; any violation should fail the build.
|
||||||
|
|
||||||
|
## Kotlin (files matching `**/*.kt`)
|
||||||
|
- Target Kotlin version 1.9.
|
||||||
|
- Use `val` over `var` by default.
|
||||||
|
- Use data classes and sealed classes for value types.
|
||||||
|
- Use structured concurrency with coroutines; avoid `GlobalScope`.
|
||||||
|
- Avoid forced unwraps (`!!`); prefer safe calls (`?.`) and Elvis (`?:`) operators.
|
||||||
|
- Prefer extension functions for utilities.
|
||||||
|
- Tests must be written using JUnit 5.
|
||||||
|
|
||||||
|
## Rust (files matching `**/*.rs`)
|
||||||
|
- Use Rust edition 2024.
|
||||||
|
- Replace `unwrap()` and `expect()` in production code with `Result` + `?` propagation.
|
||||||
|
- Avoid unnecessary `.clone()`; use borrowing and lifetimes properly.
|
||||||
|
- Avoid `unsafe` blocks unless strictly necessary, and document why.
|
||||||
|
- Always run `cargo fmt` and `cargo clippy`.
|
||||||
|
|
||||||
|
## C++ (files matching `**/*.cpp`, `**/*.hpp`)
|
||||||
|
- Use C++23 standard.
|
||||||
|
- Avoid raw pointers; prefer `std::unique_ptr`, `std::shared_ptr`, `std::optional`.
|
||||||
|
- Use RAII for resource management.
|
||||||
|
- Prefer STL algorithms over manual loops.
|
||||||
|
- Avoid macros and global mutable state.
|
||||||
|
- Tests should be written using Google Test or equivalent.
|
||||||
|
|
||||||
|
## C (files matching `**/*.c`, `**/*.h`)
|
||||||
|
- Use C23 standard where possible.
|
||||||
|
- Check return values of all system/library calls.
|
||||||
|
- Avoid global mutable state.
|
||||||
|
- Use `const` correctness in declarations.
|
||||||
|
- Leverage static analysis tools: `cppcheck`, `valgrind`.
|
||||||
|
- Use header/implementation separation properly.
|
||||||
|
|
||||||
|
## Java + Gradle (files matching `build.gradle.kts`, `**/*.java`)
|
||||||
|
- Target Java version 21 (or higher if project permits).
|
||||||
|
- Use Gradle Kotlin DSL for build scripts.
|
||||||
|
- Prefer `record` for simple data carriers.
|
||||||
|
- Use `Optional` and `Stream` responsibly; prefer immutability.
|
||||||
|
- Build must be reproducible and minimal: avoid unnecessary plugins.
|
||||||
|
- Use `spotless` for formatting and `checkstyle` for static analysis.
|
||||||
|
|
||||||
|
## Testing Rules
|
||||||
|
- For each new module, write unit tests.
|
||||||
|
- Test naming convention: `should<Behavior>_when<Condition>`.
|
||||||
|
- Tests must be fast, deterministic, not rely on network/external I/O (use mocks as needed).
|
||||||
|
- Aim for coverage: critical paths ≥ 90%, overall modules ≥ 70%.
|
||||||
137
.github/workflows/avd-kernel.yml
vendored
137
.github/workflows/avd-kernel.yml
vendored
@@ -1,137 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
arch:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Build arch: aarch64/x86_64
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
manifest_name:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Local repo manifest xml path,
|
|
||||||
typically for AVD kernel build.
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Maximize build space
|
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 8192
|
|
||||||
temp-reserve-mb: 2048
|
|
||||||
remove-dotnet: 'true'
|
|
||||||
remove-android: 'true'
|
|
||||||
remove-haskell: 'true'
|
|
||||||
remove-codeql: 'true'
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: KernelSU
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup need_upload
|
|
||||||
id: need_upload
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup kernel source
|
|
||||||
run: |
|
|
||||||
echo "Free space:"
|
|
||||||
df -h
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
sudo apt-get install repo -y
|
|
||||||
mkdir android-kernel && cd android-kernel
|
|
||||||
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
|
||||||
repo --version
|
|
||||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Setup KernelSU
|
|
||||||
env:
|
|
||||||
PATCH_PATH: ${{ inputs.patch_path }}
|
|
||||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/android-kernel
|
|
||||||
echo "[+] KernelSU setup"
|
|
||||||
GKI_ROOT=$(pwd)
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
|
||||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
|
||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
|
||||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
|
||||||
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
|
||||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
|
||||||
echo "[+] Apply KernelSU patches"
|
|
||||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
|
||||||
echo "[+] Enable debug features for kernel"
|
|
||||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
|
||||||
fi
|
|
||||||
repo status
|
|
||||||
echo "[+] KernelSU setup done."
|
|
||||||
cd $GITHUB_WORKSPACE/KernelSU
|
|
||||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
|
||||||
echo "VERSION: $VERSION"
|
|
||||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
|
||||||
git config --global user.email "bot@kernelsu.org"
|
|
||||||
git config --global user.name "KernelSUBot"
|
|
||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
|
||||||
repo status
|
|
||||||
|
|
||||||
- name: Build kernel
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
|
||||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
|
||||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
|
||||||
fi
|
|
||||||
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
|
|
||||||
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
TARGET_IMAGE=dist/bzImage
|
|
||||||
if [ ! -e $TARGET_IMAGE ]; then
|
|
||||||
TARGET_IMAGE=dist/Image
|
|
||||||
fi
|
|
||||||
mv $TARGET_IMAGE $NAME
|
|
||||||
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload Kernel
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
path: "${{ env.file_path }}"
|
|
||||||
20
.github/workflows/build-gki-image.yml
vendored
Normal file
20
.github/workflows/build-gki-image.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Build Android GKI Image
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-a12-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a12.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a13-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a13.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a14-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a14.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a15-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a15.yml
|
||||||
|
secrets: inherit
|
||||||
|
build-a16-kernel:
|
||||||
|
uses: ./.github/workflows/build-kernel-a16.yml
|
||||||
|
secrets: inherit
|
||||||
111
.github/workflows/build-kernel-a12.yml
vendored
Normal file
111
.github/workflows/build-kernel-a12.yml
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
name: Build Kernel - Android 12
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a12.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a12.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- sub_level: 218
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- sub_level: 226
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- sub_level: 233
|
||||||
|
os_patch_level: 2025-02
|
||||||
|
- sub_level: 236
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android12-5.10
|
||||||
|
version_name: android12-5.10.${{ matrix.sub_level }}
|
||||||
|
tag: android12-5.10-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: "5.10"
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
upload-artifacts:
|
||||||
|
needs: build-kernel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||||
|
env:
|
||||||
|
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||||
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
|
pip3 install telethon
|
||||||
|
|
||||||
|
- name: Set boot sign key
|
||||||
|
env:
|
||||||
|
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||||
|
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build boot images
|
||||||
|
run: |
|
||||||
|
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||||
|
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||||
|
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||||
|
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||||
|
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||||
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
|
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
|
echo "VERSION: $VERSION"
|
||||||
|
cd -
|
||||||
|
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh
|
||||||
|
|
||||||
|
- name: Display structure of boot files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload images artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android12
|
||||||
|
path: Image-android12*/*.img.gz
|
||||||
146
.github/workflows/build-kernel-a13.yml
vendored
Normal file
146
.github/workflows/build-kernel-a13.yml
vendored
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
name: Build Kernel - Android 13
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a13.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 210
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 214
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 218
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 223
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 228
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.10"
|
||||||
|
sub_level: 234
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 149
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 151
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 153
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 167
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 170
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android13-${{ matrix.version }}
|
||||||
|
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
upload-artifacts:
|
||||||
|
needs: build-kernel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||||
|
env:
|
||||||
|
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||||
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
|
pip3 install telethon
|
||||||
|
|
||||||
|
- name: Set boot sign key
|
||||||
|
env:
|
||||||
|
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||||
|
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build boot images
|
||||||
|
run: |
|
||||||
|
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||||
|
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||||
|
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||||
|
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||||
|
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||||
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
|
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
|
echo "VERSION: $VERSION"
|
||||||
|
cd -
|
||||||
|
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||||
|
|
||||||
|
- name: Display structure of boot files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload images artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android13
|
||||||
|
path: Image-android13*/*.img.gz
|
||||||
158
.github/workflows/build-kernel-a14.yml
vendored
Normal file
158
.github/workflows/build-kernel-a14.yml
vendored
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
name: Build Kernel - Android 14
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a14.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 149
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 153
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 158
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 164
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 167
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 170
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 178
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 78
|
||||||
|
os_patch_level: 2024-06
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 84
|
||||||
|
os_patch_level: 2024-07
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 90
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 93
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 99
|
||||||
|
os_patch_level: 2024-10
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 112
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 115
|
||||||
|
os_patch_level: 2024-12
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 118
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 128
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 134
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android14-${{ matrix.version }}
|
||||||
|
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
upload-artifacts:
|
||||||
|
needs: build-kernel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||||
|
env:
|
||||||
|
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||||
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
|
pip3 install telethon
|
||||||
|
|
||||||
|
- name: Set boot sign key
|
||||||
|
env:
|
||||||
|
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||||
|
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build boot images
|
||||||
|
run: |
|
||||||
|
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||||
|
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||||
|
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||||
|
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||||
|
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||||
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
|
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
|
echo "VERSION: $VERSION"
|
||||||
|
cd -
|
||||||
|
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||||
|
|
||||||
|
- name: Display structure of boot files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload images artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android14
|
||||||
|
path: Image-android14*/*.img.gz
|
||||||
131
.github/workflows/build-kernel-a15.yml
vendored
Normal file
131
.github/workflows/build-kernel-a15.yml
vendored
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
name: Build Kernel - Android 15
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a15.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 30
|
||||||
|
os_patch_level: 2024-08
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 46
|
||||||
|
os_patch_level: 2024-09
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 50
|
||||||
|
os_patch_level: 2024-10
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 56
|
||||||
|
os_patch_level: 2024-11
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 57
|
||||||
|
os_patch_level: 2024-12
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 58
|
||||||
|
os_patch_level: 2025-01
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 66
|
||||||
|
os_patch_level: 2025-02
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 77
|
||||||
|
os_patch_level: 2025-03
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 82
|
||||||
|
os_patch_level: 2025-04
|
||||||
|
- version: "6.6"
|
||||||
|
sub_level: 87
|
||||||
|
os_patch_level: 2025-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android15-${{ matrix.version }}
|
||||||
|
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
upload-artifacts:
|
||||||
|
needs: build-kernel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||||
|
env:
|
||||||
|
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||||
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
|
pip3 install telethon
|
||||||
|
|
||||||
|
- name: Set boot sign key
|
||||||
|
env:
|
||||||
|
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||||
|
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build boot images
|
||||||
|
run: |
|
||||||
|
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||||
|
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||||
|
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||||
|
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||||
|
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||||
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
|
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
|
echo "VERSION: $VERSION"
|
||||||
|
cd -
|
||||||
|
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||||
|
|
||||||
|
- name: Display structure of boot files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload images artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: boot-images-android15
|
||||||
|
path: Image-android15*/*.img.gz
|
||||||
104
.github/workflows/build-kernel-a16.yml
vendored
Normal file
104
.github/workflows/build-kernel-a16.yml
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
name: Build Kernel - Android 16
|
||||||
|
on:
|
||||||
|
# push:
|
||||||
|
# branches: ["main", "ci", "checkci"]
|
||||||
|
# paths:
|
||||||
|
# - ".github/workflows/deps/gki/build-kernel-a16.yml"
|
||||||
|
# - ".github/workflows/deps/gki/gki-kernel.yml"
|
||||||
|
# - ".github/scripts/build_a13.sh"
|
||||||
|
# - "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
debug:
|
||||||
|
description: 'Build debug kernel'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "6.12"
|
||||||
|
sub_level: 38
|
||||||
|
os_patch_level: 2025-08
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
version: android16-${{ matrix.version }}
|
||||||
|
version_name: android16-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android16-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: ${{ inputs.debug || false }}
|
||||||
|
|
||||||
|
upload-artifacts:
|
||||||
|
needs: build-kernel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||||
|
env:
|
||||||
|
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||||
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||||
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v6
|
||||||
|
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: |
|
||||||
|
tree
|
||||||
|
|
||||||
|
- name: Download prebuilt toolchain
|
||||||
|
run: |
|
||||||
|
AOSP_MIRROR=https://android.googlesource.com
|
||||||
|
BRANCH=main-kernel-build-2024
|
||||||
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
|
pip3 install telethon
|
||||||
|
|
||||||
|
- name: Set boot sign key
|
||||||
|
env:
|
||||||
|
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
|
||||||
|
run: |
|
||||||
|
if [ ! -z "$BOOT_SIGN_KEY" ]; then
|
||||||
|
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build boot images
|
||||||
|
run: |
|
||||||
|
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||||
|
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||||
|
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||||
|
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||||
|
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||||
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
|
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
|
echo "VERSION: $VERSION"
|
||||||
|
cd -
|
||||||
|
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||||
|
|
||||||
|
- name: Display structure of boot files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: Upload images artifact
|
||||||
|
uses: actions/upload-artifact@v5
|
||||||
|
with:
|
||||||
|
name: boot-images-android16
|
||||||
|
path: Image-android16*/*.img.gz
|
||||||
74
.github/workflows/build-lkm-local.yml
vendored
74
.github/workflows/build-lkm-local.yml
vendored
@@ -1,74 +0,0 @@
|
|||||||
name: Build LKM for KernelSU Local
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
secrets:
|
|
||||||
# username:github_pat
|
|
||||||
TOKEN:
|
|
||||||
required: true
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
jobs:
|
|
||||||
build-lkm:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- version: "android12-5.10"
|
|
||||||
sub_level: 236
|
|
||||||
os_patch_level: 2025-05
|
|
||||||
- version: "android13-5.10"
|
|
||||||
sub_level: 234
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android13-5.15"
|
|
||||||
sub_level: 178
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android14-5.15"
|
|
||||||
sub_level: 178
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android14-6.1"
|
|
||||||
sub_level: 134
|
|
||||||
os_patch_level: 2025-05
|
|
||||||
- version: "android15-6.6"
|
|
||||||
sub_level: 87
|
|
||||||
os_patch_level: 2025-05
|
|
||||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
|
||||||
uses: ./.github/workflows/gki-kernel-local.yml
|
|
||||||
with:
|
|
||||||
version: ${{ matrix.version }}
|
|
||||||
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
|
||||||
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
|
||||||
build_lkm: true
|
|
||||||
|
|
||||||
push-to-branch:
|
|
||||||
needs: [build-lkm]
|
|
||||||
runs-on: self-hosted
|
|
||||||
if: ${{ inputs.upload }}
|
|
||||||
steps:
|
|
||||||
- name: Download all workflow run artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: bin/
|
|
||||||
merge-multiple: true
|
|
||||||
- name: Push to branch LKM
|
|
||||||
run: |
|
|
||||||
cd bin
|
|
||||||
git config --global init.defaultBranch lkm
|
|
||||||
git init
|
|
||||||
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
find . -type f
|
|
||||||
git add .
|
|
||||||
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
||||||
git push --force --set-upstream origin lkm
|
|
||||||
77
.github/workflows/build-lkm.yml
vendored
77
.github/workflows/build-lkm.yml
vendored
@@ -1,74 +1,21 @@
|
|||||||
name: Build LKM for KernelSU
|
name: Build LKM for KernelSU
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
secrets:
|
|
||||||
# username:github_pat
|
|
||||||
TOKEN:
|
|
||||||
required: true
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
jobs:
|
jobs:
|
||||||
build-lkm:
|
build-lkm:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
kmi:
|
||||||
- version: "android12-5.10"
|
- android12-5.10
|
||||||
sub_level: 240
|
- android13-5.10
|
||||||
os_patch_level: 2025-09
|
- android13-5.15
|
||||||
- version: "android13-5.10"
|
- android14-5.15
|
||||||
sub_level: 238
|
- android14-6.1
|
||||||
os_patch_level: 2025-07
|
- android15-6.6
|
||||||
- version: "android13-5.15"
|
- android16-6.12
|
||||||
sub_level: 189
|
uses: ./.github/workflows/ddk-lkm.yml
|
||||||
os_patch_level: 2025-09
|
|
||||||
- version: "android14-5.15"
|
|
||||||
sub_level: 185
|
|
||||||
os_patch_level: 2025-07
|
|
||||||
- version: "android14-6.1"
|
|
||||||
sub_level: 145
|
|
||||||
os_patch_level: 2025-09
|
|
||||||
- version: "android15-6.6"
|
|
||||||
sub_level: 98
|
|
||||||
os_patch_level: 2025-09
|
|
||||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
|
||||||
with:
|
with:
|
||||||
version: ${{ matrix.version }}
|
kmi: ${{ matrix.kmi }}
|
||||||
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
ddk_release: '20251104'
|
||||||
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
|
||||||
build_lkm: true
|
|
||||||
|
|
||||||
push-to-branch:
|
|
||||||
needs: [build-lkm]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ inputs.upload }}
|
|
||||||
steps:
|
|
||||||
- name: Download all workflow run artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: bin/
|
|
||||||
merge-multiple: true
|
|
||||||
- name: Push to branch LKM
|
|
||||||
run: |
|
|
||||||
cd bin
|
|
||||||
git config --global init.defaultBranch lkm
|
|
||||||
git init
|
|
||||||
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
find . -type f
|
|
||||||
git add .
|
|
||||||
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
||||||
git push --force --set-upstream origin lkm
|
|
||||||
|
|||||||
245
.github/workflows/build-manager-manual.yml
vendored
245
.github/workflows/build-manager-manual.yml
vendored
@@ -1,245 +0,0 @@
|
|||||||
name: Build Manager Manual
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
build_lkm:
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
default: "auto"
|
|
||||||
options:
|
|
||||||
- "true"
|
|
||||||
- "false"
|
|
||||||
- "auto"
|
|
||||||
description: "Whether to build lkm"
|
|
||||||
upload_lkm:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload lkm"
|
|
||||||
jobs:
|
|
||||||
check-build-lkm:
|
|
||||||
runs-on: self-hosted
|
|
||||||
outputs:
|
|
||||||
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
|
|
||||||
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
|
|
||||||
steps:
|
|
||||||
- name: check build
|
|
||||||
id: check-build
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
|
|
||||||
kernel_changed="${{ inputs.build_lkm }}"
|
|
||||||
else
|
|
||||||
kernel_changed=true
|
|
||||||
mkdir tmp
|
|
||||||
cd tmp
|
|
||||||
git config --global init.defaultBranch bot
|
|
||||||
git config --global user.name 'Bot'
|
|
||||||
git config --global user.email 'bot@github.shirkneko.io'
|
|
||||||
git init .
|
|
||||||
git remote add origin https://github.com/${{ github.repository }}
|
|
||||||
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
|
|
||||||
git fetch origin $CURRENT_COMMIT --depth=1
|
|
||||||
git fetch origin lkm --depth=1
|
|
||||||
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
|
|
||||||
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
|
|
||||||
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
|
|
||||||
echo "LKM_COMMIT=$LKM_COMMIT"
|
|
||||||
git fetch origin "$LKM_COMMIT" --depth=1
|
|
||||||
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm-local.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
|
|
||||||
cd ..
|
|
||||||
rm -rf tmp
|
|
||||||
fi
|
|
||||||
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
|
|
||||||
need_upload=true
|
|
||||||
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
||||||
need_upload="${{ inputs.upload_lkm }}"
|
|
||||||
else
|
|
||||||
need_upload=false
|
|
||||||
fi
|
|
||||||
echo "kernel changed: $kernel_changed"
|
|
||||||
echo "need upload: $need_upload"
|
|
||||||
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
build-lkm:
|
|
||||||
needs: check-build-lkm
|
|
||||||
uses: ./.github/workflows/build-lkm-local.yml
|
|
||||||
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
|
|
||||||
with:
|
|
||||||
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
|
|
||||||
secrets: inherit
|
|
||||||
build-susfs:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/susfs.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
|
|
||||||
build-kpmmgr:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/kpmmgr.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
|
|
||||||
build-ksud:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
- target: x86_64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
- target: armv7-linux-androideabi
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/ksud.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
pack_lkm: true
|
|
||||||
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
|
|
||||||
|
|
||||||
build-manager:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: build-ksud
|
|
||||||
runs-on: self-hosted
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./manager
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup need_upload
|
|
||||||
id: need_upload
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Write key
|
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
|
||||||
{
|
|
||||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
|
|
||||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
|
|
||||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
|
|
||||||
echo KEYSTORE_FILE='key.jks'
|
|
||||||
} >> gradle.properties
|
|
||||||
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download arm64 susfs
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: susfs-aarch64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm64 kpmmgr
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kpmmgr-aarch64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm64 ksud
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ksud-aarch64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download x86_64 ksud
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ksud-x86_64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm ksud
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ksud-armv7-linux-androideabi
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Copy ksud to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
mkdir -p app/src/main/jniLibs/x86_64
|
|
||||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
|
||||||
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
|
|
||||||
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
|
|
||||||
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
|
|
||||||
|
|
||||||
- name: Copy kpmmgr to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
|
||||||
|
|
||||||
- name: Copy susfs to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: |
|
|
||||||
export ANDROID_HOME=/root/.android/sdk
|
|
||||||
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
|
|
||||||
./gradlew clean assembleRelease
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
|
||||||
with:
|
|
||||||
name: manager
|
|
||||||
path: manager/app/build/outputs/apk/release/*.apk
|
|
||||||
|
|
||||||
- name: Upload mappings
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
|
||||||
with:
|
|
||||||
name: "mappings"
|
|
||||||
path: "manager/app/build/outputs/mapping/release/"
|
|
||||||
|
|
||||||
- name: Bot session cache
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
|
|
||||||
- name: Upload to telegram
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
env:
|
|
||||||
CHAT_ID: ${{ vars.CHAT_ID }}
|
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
|
||||||
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
|
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
TITLE: Manager
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
export VERSION=$(git rev-list --count HEAD)
|
|
||||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
|
||||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
|
||||||
fi
|
|
||||||
195
.github/workflows/build-manager.yml
vendored
195
.github/workflows/build-manager.yml
vendored
@@ -2,117 +2,31 @@ name: Build Manager
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "ci" ]
|
branches: [ "main", "dev", "ci", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-manager.yml'
|
- '.github/workflows/build-manager.yml'
|
||||||
|
- '.github/workflows/build-lkm.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
- 'kernel/**'
|
- 'kernel/**'
|
||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
- 'userspace/susfs/**'
|
|
||||||
- 'userspace/kpmmgr/**'
|
|
||||||
- 'userspace/user_scanner/**'
|
- 'userspace/user_scanner/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "dev", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
|
- '.github/workflows/build-manager.yml'
|
||||||
|
- '.github/workflows/build-lkm.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
|
- 'kernel/**'
|
||||||
|
- 'userspace/ksud/**'
|
||||||
|
- 'userspace/user_scanner/**'
|
||||||
workflow_call:
|
workflow_call:
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
build_lkm:
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
default: "auto"
|
|
||||||
options:
|
|
||||||
- "true"
|
|
||||||
- "false"
|
|
||||||
- "auto"
|
|
||||||
description: "Whether to build lkm"
|
|
||||||
upload_lkm:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
description: "Whether to upload lkm"
|
|
||||||
jobs:
|
jobs:
|
||||||
check-build-lkm:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
|
|
||||||
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
|
|
||||||
steps:
|
|
||||||
- name: check build
|
|
||||||
id: check-build
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
|
|
||||||
kernel_changed="${{ inputs.build_lkm }}"
|
|
||||||
else
|
|
||||||
kernel_changed=true
|
|
||||||
mkdir tmp
|
|
||||||
cd tmp
|
|
||||||
git config --global init.defaultBranch bot
|
|
||||||
git config --global user.name 'Bot'
|
|
||||||
git config --global user.email 'bot@github.shirkneko.io'
|
|
||||||
git init .
|
|
||||||
git remote add origin https://github.com/${{ github.repository }}
|
|
||||||
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
|
|
||||||
git fetch origin $CURRENT_COMMIT --depth=1
|
|
||||||
git fetch origin lkm --depth=1
|
|
||||||
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
|
|
||||||
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
|
|
||||||
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
|
|
||||||
echo "LKM_COMMIT=$LKM_COMMIT"
|
|
||||||
git fetch origin "$LKM_COMMIT" --depth=1
|
|
||||||
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
|
|
||||||
cd ..
|
|
||||||
rm -rf tmp
|
|
||||||
fi
|
|
||||||
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
|
|
||||||
need_upload=true
|
|
||||||
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
||||||
need_upload="${{ inputs.upload_lkm }}"
|
|
||||||
else
|
|
||||||
need_upload=false
|
|
||||||
fi
|
|
||||||
echo "kernel changed: $kernel_changed"
|
|
||||||
echo "need upload: $need_upload"
|
|
||||||
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
build-lkm:
|
build-lkm:
|
||||||
needs: check-build-lkm
|
|
||||||
uses: ./.github/workflows/build-lkm.yml
|
uses: ./.github/workflows/build-lkm.yml
|
||||||
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
|
|
||||||
with:
|
|
||||||
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
|
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
build-susfs:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/susfs.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
|
|
||||||
build-kpmmgr:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
os: ubuntu-latest
|
|
||||||
uses: ./.github/workflows/kpmmgr.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
|
|
||||||
build-user_scanner:
|
build-user_scanner:
|
||||||
if: ${{ always() }}
|
needs: build-lkm
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -124,8 +38,7 @@ jobs:
|
|||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-ksud:
|
build-ksud:
|
||||||
if: ${{ always() }}
|
needs: build-lkm
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -139,13 +52,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
pack_lkm: true
|
|
||||||
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
|
|
||||||
|
|
||||||
build-manager:
|
build-manager:
|
||||||
if: ${{ always() }}
|
|
||||||
needs: build-ksud
|
needs: build-ksud
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
spoofed: ["true","false"]
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./manager
|
working-directory: ./manager
|
||||||
@@ -165,8 +78,34 @@ jobs:
|
|||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
- name: Determine manager variant for telegram bot
|
||||||
|
id: determine
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.ref_name }}" == "miuix" ] && [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
|
echo "SKIP=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${{ github.ref_name }}" == "miuix" ]; then
|
||||||
|
echo "title=Manager" >> $GITHUB_OUTPUT
|
||||||
|
echo "topicid=${{ vars.MESSAGE_MIUIX_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
|
echo "title=Spoofed-Manager" >> $GITHUB_OUTPUT
|
||||||
|
# maybe need a new var
|
||||||
|
echo "topicid=${{ vars.MESSAGE_SPOOFED_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "title=Manager" >> $GITHUB_OUTPUT
|
||||||
|
echo "topicid=${{ vars.MESSAGE_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run randomizer
|
||||||
|
if: ${{ matrix.spoofed == 'true' && steps.determine.outputs.SKIP != 'true' }}
|
||||||
|
run: |
|
||||||
|
chmod +x randomizer
|
||||||
|
./randomizer
|
||||||
|
|
||||||
- name: Write key
|
- name: Write key
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||||
{
|
{
|
||||||
@@ -196,18 +135,6 @@ jobs:
|
|||||||
name: userscanner-all-linux-android
|
name: userscanner-all-linux-android
|
||||||
path: .
|
path: .
|
||||||
|
|
||||||
- name: Download arm64 susfs
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: susfs-aarch64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm64 kpmmgr
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kpmmgr-aarch64-linux-android
|
|
||||||
path: .
|
|
||||||
|
|
||||||
- name: Download arm64 ksud
|
- name: Download arm64 ksud
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -231,19 +158,9 @@ jobs:
|
|||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
mkdir -p app/src/main/jniLibs/x86_64
|
mkdir -p app/src/main/jniLibs/x86_64
|
||||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||||
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
|
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||||
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
|
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
|
||||||
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
|
cp -f ../armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud.so
|
||||||
|
|
||||||
- name: Copy kpmmgr to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
|
||||||
|
|
||||||
- name: Copy susfs to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
|
||||||
|
|
||||||
- name: Copy user_scanner to app jniLibs
|
- name: Copy user_scanner to app jniLibs
|
||||||
run: |
|
run: |
|
||||||
@@ -255,40 +172,34 @@ jobs:
|
|||||||
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
|
if: ${{ steps.determine.outputs.SKIP != 'true' }}
|
||||||
run: ./gradlew clean assembleRelease
|
run: ./gradlew clean assembleRelease
|
||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: manager
|
name: ${{ steps.determine.outputs.title }}
|
||||||
path: manager/app/build/outputs/apk/release/*.apk
|
path: manager/app/build/outputs/apk/release/*.apk
|
||||||
|
|
||||||
- name: Upload mappings
|
- name: Upload mappings
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: "mappings"
|
name: "${{ steps.determine.outputs.title }}-mappings"
|
||||||
path: "manager/app/build/outputs/mapping/release/"
|
path: "manager/app/build/outputs/mapping/release/"
|
||||||
|
|
||||||
- name: Bot session cache
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
|
|
||||||
- name: Upload to telegram
|
- name: Upload to telegram
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' && steps.determine.outputs.SKIP != 'true'
|
||||||
env:
|
env:
|
||||||
CHAT_ID: ${{ vars.CHAT_ID }}
|
CHAT_ID: ${{ vars.CHAT_ID }}
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
|
MESSAGE_THREAD_ID: ${{ steps.determine.outputs.topicid }}
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
TITLE: Manager
|
TITLE: ${{ steps.determine.outputs.title }}
|
||||||
|
BRANCH: ${{ github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||||
export VERSION=$(git rev-list --count HEAD)
|
export VERSION=$(git rev-list --count HEAD)
|
||||||
|
|||||||
2
.github/workflows/clippy.yml
vendored
2
.github/workflows/clippy.yml
vendored
@@ -34,4 +34,4 @@ jobs:
|
|||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: |
|
run: |
|
||||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
||||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||||
53
.github/workflows/ddk-lkm.yml
vendored
Normal file
53
.github/workflows/ddk-lkm.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Build KernelSU Kernel Module
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
kmi:
|
||||||
|
description: 'KMI version'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
ddk_release:
|
||||||
|
description: 'DDK release version'
|
||||||
|
required: false
|
||||||
|
default: '20251104'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-kernelsu-ko:
|
||||||
|
name: Build kernelsu.ko for ${{ inputs.kmi }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/ylarod/ddk:${{ inputs.kmi }}-${{ inputs.ddk_release }}
|
||||||
|
options: --privileged
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build kernelsu.ko
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/SukiSU-Ultra/SukiSU-Ultra
|
||||||
|
cd kernel
|
||||||
|
|
||||||
|
echo "=== Building kernelsu.ko for KMI: ${{ inputs.kmi }} ==="
|
||||||
|
CONFIG_KSU=m CONFIG_KSU_MANUAL_SU=y make
|
||||||
|
|
||||||
|
echo "=== Build completed ==="
|
||||||
|
# Create output directory in GitHub workspace
|
||||||
|
mkdir -p /github/workspace/out
|
||||||
|
# Copy with KMI-specific naming
|
||||||
|
OUTPUT_NAME="${{ inputs.kmi }}_kernelsu.ko"
|
||||||
|
cp kernelsu.ko "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
|
||||||
|
echo "Copied to: /github/workspace/out/$OUTPUT_NAME"
|
||||||
|
ls -la "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
echo "Size: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
|
||||||
|
llvm-strip -d "/github/workspace/out/$OUTPUT_NAME"
|
||||||
|
echo "Size after stripping: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
|
||||||
|
|
||||||
|
- name: Upload kernelsu.ko artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.kmi }}-lkm
|
||||||
|
path: /github/workspace/out/${{ inputs.kmi }}_kernelsu.ko
|
||||||
67
.github/workflows/deploy-website.yml
vendored
Normal file
67
.github/workflows/deploy-website.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Deploy Website
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- website
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/deploy-website.yml'
|
||||||
|
- 'website/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||||
|
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||||
|
concurrency:
|
||||||
|
group: pages
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build job
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./website
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Not needed if lastUpdated is not enabled
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: latest
|
||||||
|
cache: yarn # or pnpm / yarn
|
||||||
|
cache-dependency-path: website/yarn.lock
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v5
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
- name: Build with VitePress
|
||||||
|
run: |
|
||||||
|
yarn docs:build
|
||||||
|
touch docs/.vitepress/dist/.nojekyll
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: website/docs/.vitepress/dist
|
||||||
|
|
||||||
|
# Deployment job
|
||||||
|
deploy:
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Deploy
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
252
.github/workflows/gki-kernel-local.yml
vendored
252
.github/workflows/gki-kernel-local.yml
vendored
@@ -1,252 +0,0 @@
|
|||||||
name: GKI Kernel Build Local
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Output directory of gki,
|
|
||||||
for example: android12-5.10
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
tag:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Part of branch name of common kernel manifest,
|
|
||||||
for example: android12-5.10-2021-11
|
|
||||||
os_patch_level:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Patch level of common kernel manifest,
|
|
||||||
for example: 2021-11
|
|
||||||
default: 2022-05
|
|
||||||
patch_path:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Directory name of .github/patches/<patch_path>
|
|
||||||
for example: 5.10
|
|
||||||
use_cache:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
embed_ksud:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ksud-aarch64-linux-android
|
|
||||||
description: >
|
|
||||||
Artifact name of prebuilt ksud to be embedded
|
|
||||||
for example: ksud-aarch64-linux-android
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: self-hosted
|
|
||||||
env:
|
|
||||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
|
||||||
CCACHE_NOHASHDIR: "true"
|
|
||||||
CCACHE_HARDLINK: "true"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: KernelSU
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup need_upload
|
|
||||||
id: need_upload
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup kernel source
|
|
||||||
run: |
|
|
||||||
echo "Free space:"
|
|
||||||
df -h
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
sudo apt-get install repo -y
|
|
||||||
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
|
|
||||||
mkdir android-kernel && cd android-kernel
|
|
||||||
repo init --depth=1 --u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
|
|
||||||
REMOTE_BRANCH=$(git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/common ${{ inputs.tag }})
|
|
||||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
|
||||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
|
||||||
echo "Found deprecated branch: ${{ inputs.tag }}"
|
|
||||||
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
|
|
||||||
cat $DEFAULT_MANIFEST_PATH
|
|
||||||
fi
|
|
||||||
repo --version
|
|
||||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Setup KernelSU
|
|
||||||
env:
|
|
||||||
PATCH_PATH: ${{ inputs.patch_path }}
|
|
||||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/android-kernel
|
|
||||||
echo "[+] KernelSU setup"
|
|
||||||
GKI_ROOT=$(pwd)
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
|
||||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
|
||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
|
||||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
|
||||||
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
|
||||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
|
||||||
echo "[+] Apply Compilation Patches"
|
|
||||||
if [ ! -e build/build.sh ]; then
|
|
||||||
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
|
|
||||||
echo "GLIBC_VERSION: $GLIBC_VERSION"
|
|
||||||
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
|
|
||||||
echo "Patching resolve_btfids/Makefile"
|
|
||||||
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
|
||||||
echo "[+] Enable debug features for kernel"
|
|
||||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
|
||||||
fi
|
|
||||||
repo status
|
|
||||||
echo "[+] KernelSU setup done."
|
|
||||||
|
|
||||||
- name: Symbol magic
|
|
||||||
run: |
|
|
||||||
echo "[+] Export all symbol from abi_gki_aarch64.xml"
|
|
||||||
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
|
|
||||||
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
|
|
||||||
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
|
|
||||||
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
|
|
||||||
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
|
|
||||||
echo "[+] Add KernelSU symbols"
|
|
||||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
|
||||||
|
|
||||||
- name: Setup ccache
|
|
||||||
if: inputs.use_cache == true
|
|
||||||
uses: hendrikmuhs/ccache-action@v1
|
|
||||||
with:
|
|
||||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
|
||||||
max-size: 2G
|
|
||||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Setup for LKM
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
pip install ast-grep-cli
|
|
||||||
sudo apt-get install llvm-15 -y
|
|
||||||
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
|
|
||||||
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
|
|
||||||
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
|
|
||||||
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
|
|
||||||
|
|
||||||
# bazel build, android14-5.15, android14-6.1 use bazel
|
|
||||||
if [ ! -e build/build.sh ]; then
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
|
||||||
if [ -e common/modules.bzl ]; then
|
|
||||||
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
TARGET_FILE="build/kernel/build.sh"
|
|
||||||
if [ ! -e "$TARGET_FILE" ]; then
|
|
||||||
TARGET_FILE="build/build.sh"
|
|
||||||
fi
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
|
|
||||||
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
|
|
||||||
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
if [ -e common/BUILD.bazel ]; then
|
|
||||||
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
|
||||||
fi
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
|
||||||
git config --global user.email "bot@kernelsu.org"
|
|
||||||
git config --global user.name "KernelSUBot"
|
|
||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
|
||||||
repo status
|
|
||||||
|
|
||||||
- name: Build Kernel/LKM
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
|
||||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
|
||||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
|
||||||
fi
|
|
||||||
if [ -e build/build.sh ]; then
|
|
||||||
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
|
||||||
else
|
|
||||||
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Prepare artifacts
|
|
||||||
id: prepareArtifacts
|
|
||||||
run: |
|
|
||||||
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
|
|
||||||
if [ ! -e $OUTDIR ]; then
|
|
||||||
OUTDIR=android-kernel/dist
|
|
||||||
fi
|
|
||||||
mkdir output
|
|
||||||
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
|
||||||
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
|
||||||
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
|
||||||
else
|
|
||||||
cp $OUTDIR/Image ./output/
|
|
||||||
cp $OUTDIR/Image.lz4 ./output/
|
|
||||||
git clone https://github.com/Kernel-SU/AnyKernel3
|
|
||||||
rm -rf ./AnyKernel3/.git
|
|
||||||
cp $OUTDIR/Image ./AnyKernel3/
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload Image and Image.gz
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == false }}
|
|
||||||
with:
|
|
||||||
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
|
||||||
path: ./output/*
|
|
||||||
|
|
||||||
- name: Upload AnyKernel3
|
|
||||||
if: ${{ inputs.build_lkm == false }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
|
||||||
path: ./AnyKernel3/*
|
|
||||||
|
|
||||||
- name: Upload LKM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.version }}-lkm
|
|
||||||
path: ./output/*_kernelsu.ko
|
|
||||||
79
.github/workflows/gki-kernel-mock.yml
vendored
79
.github/workflows/gki-kernel-mock.yml
vendored
@@ -1,79 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Output directory of gki,
|
|
||||||
for example: android12-5.10
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
tag:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Part of branch name of common kernel manifest,
|
|
||||||
for example: android12-5.10-2021-11
|
|
||||||
os_patch_level:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Patch level of common kernel manifest,
|
|
||||||
for example: 2021-11
|
|
||||||
default: 2022-05
|
|
||||||
patch_path:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Directory name of .github/patches/<patch_path>
|
|
||||||
for example: 5.10
|
|
||||||
use_cache:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
embed_ksud:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ksud-aarch64-linux-android
|
|
||||||
description: >
|
|
||||||
Artifact name of prebuilt ksud to be embedded
|
|
||||||
for example: ksud-aarch64-linux-android
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
mock_build:
|
|
||||||
name: Mock build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Create mocking ko
|
|
||||||
run: |
|
|
||||||
echo "${{ inputs.version }}_kernelsu.ko" > ${{ inputs.version }}_kernelsu.ko
|
|
||||||
- name: Upload LKM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.version }}-lkm
|
|
||||||
path: ./*_kernelsu.ko
|
|
||||||
45
.github/workflows/gki-kernel.yml
vendored
45
.github/workflows/gki-kernel.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
root-reserve-mb: 8192
|
root-reserve-mb: 8192
|
||||||
temp-reserve-mb: 2048
|
temp-reserve-mb: 2048
|
||||||
remove-dotnet: 'true'
|
remove-dotnet: "true"
|
||||||
remove-android: 'true'
|
remove-android: "true"
|
||||||
remove-haskell: 'true'
|
remove-haskell: "true"
|
||||||
remove-codeql: 'true'
|
remove-codeql: "true"
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
sudo apt-get install repo -y
|
sudo apt-get install repo -y
|
||||||
mkdir android-kernel && cd android-kernel
|
mkdir android-kernel && cd android-kernel
|
||||||
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
|
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.16
|
||||||
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
||||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
||||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
||||||
@@ -195,12 +195,35 @@ jobs:
|
|||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Append ashmem exports if missing
|
||||||
|
if: startsWith(inputs.version, 'android16-6.12')
|
||||||
|
working-directory: android-kernel
|
||||||
|
run: |
|
||||||
|
FILE=common/drivers/staging/android/ashmem.c
|
||||||
|
if [[ -f "$FILE" ]] && ! grep -q 'is_ashmem_file' "$FILE"; then
|
||||||
|
cat >>"$FILE" <<'EOF'
|
||||||
|
|
||||||
|
bool is_ashmem_file(struct file *file) { return false; }
|
||||||
|
int ashmem_area_name(struct file *file, char *name) { return 0; }
|
||||||
|
long ashmem_area_size(struct file *file) { return 0; }
|
||||||
|
struct file *ashmem_area_vmfile(struct file *file) { return NULL; }
|
||||||
|
EXPORT_SYMBOL_GPL(is_ashmem_file);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_name);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_size);
|
||||||
|
EXPORT_SYMBOL_GPL(ashmem_area_vmfile);
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -i -E 's/\$\(CONFIG_ANDROID_BINDER_IPC_RUST\)/m/g' common/drivers/android/Makefile
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
- name: Make working directory clean to avoid dirty
|
||||||
working-directory: android-kernel
|
working-directory: android-kernel
|
||||||
run: |
|
run: |
|
||||||
if [ -e common/BUILD.bazel ]; then
|
# Fix bazel build error
|
||||||
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
if [ -f common/BUILD.bazel ]; then
|
||||||
|
[ -f android/abi_gki_protected_exports_aarch64 ] || sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||||
git config --global user.email "bot@kernelsu.org"
|
git config --global user.email "bot@kernelsu.org"
|
||||||
git config --global user.name "KernelSUBot"
|
git config --global user.name "KernelSUBot"
|
||||||
@@ -217,7 +240,11 @@ jobs:
|
|||||||
if [ -e build/build.sh ]; then
|
if [ -e build/build.sh ]; then
|
||||||
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
||||||
else
|
else
|
||||||
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
if [ "${{ inputs.version }}" == "android16-6.12" ]; then
|
||||||
|
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --destdir=dist
|
||||||
|
else
|
||||||
|
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
@@ -228,7 +255,7 @@ jobs:
|
|||||||
OUTDIR=android-kernel/dist
|
OUTDIR=android-kernel/dist
|
||||||
fi
|
fi
|
||||||
mkdir output
|
mkdir output
|
||||||
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
||||||
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
||||||
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
||||||
else
|
else
|
||||||
|
|||||||
40
.github/workflows/kpmmgr.yml
vendored
40
.github/workflows/kpmmgr.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: Build kpmmgr
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "mian" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/kpmmgr.yml'
|
|
||||||
- 'userspace/kpmmgr/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
os:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: self-hosted
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-susfs:
|
|
||||||
name: Build userspace kpmmgr
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Build kpmmgr
|
|
||||||
working-directory: ./userspace/kpmmgr
|
|
||||||
run: |
|
|
||||||
$ANDROID_NDK_HOME/ndk-build
|
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kpmmgr-aarch64-linux-android
|
|
||||||
path: ./userspace/kpmmgr/libs
|
|
||||||
35
.github/workflows/ksud.yml
vendored
35
.github/workflows/ksud.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: ubuntu-latest
|
default: ubuntu-latest
|
||||||
pull_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
pack_lkm:
|
pack_lkm:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -21,6 +17,8 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ inputs.os }}
|
runs-on: ${{ inputs.os }}
|
||||||
@@ -29,36 +27,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Pull lkms from branch
|
- name: Download artifacts
|
||||||
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: lkm
|
|
||||||
path: lkm
|
|
||||||
|
|
||||||
- name: Download lkms from artifacts
|
|
||||||
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
- name: Prepare LKM files
|
- name: Prepare LKM fies
|
||||||
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
|
if: ${{ inputs.pack_lkm }}
|
||||||
run: |
|
|
||||||
cp lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
|
||||||
|
|
||||||
- name: Prepare LKM files
|
|
||||||
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
|
|
||||||
run: |
|
run: |
|
||||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
||||||
|
|
||||||
- name: Setup rustup
|
- name: Setup rustup
|
||||||
run: |
|
run: |
|
||||||
rustup update stable
|
rustup update stable
|
||||||
rustup target add x86_64-apple-darwin
|
rustup target add x86_64-apple-darwin
|
||||||
rustup target add aarch64-apple-darwin
|
rustup target add aarch64-apple-darwin
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: userspace/ksud
|
|
||||||
cache-targets: false
|
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
run: |
|
run: |
|
||||||
@@ -71,4 +52,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ksud-${{ inputs.target }}
|
name: ksud-${{ inputs.target }}
|
||||||
path: userspace/ksud/target/**/release/zakozako*
|
path: userspace/ksud/target/**/release/ksud*
|
||||||
|
|||||||
2
.github/workflows/shellcheck.yml
vendored
2
.github/workflows/shellcheck.yml
vendored
@@ -16,7 +16,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
shellcheck:
|
shellcheck:
|
||||||
runs-on: self-hosted
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
40
.github/workflows/susfs.yml
vendored
40
.github/workflows/susfs.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: Build susfs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "mian" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/susfs.yml'
|
|
||||||
- 'userspace/susfs/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
os:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: self-hosted
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-susfs:
|
|
||||||
name: Build userspace susfs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Build susfs
|
|
||||||
working-directory: ./userspace/susfs
|
|
||||||
run: |
|
|
||||||
$ANDROID_NDK_HOME/ndk-build
|
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: susfs-aarch64-linux-android
|
|
||||||
path: ./userspace/susfs/libs
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
CLAUDE.md
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/e
|
|||||||
.ptr.compat = __envp,
|
.ptr.compat = __envp,
|
||||||
};
|
};
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0); // 32-bit su and 32-on-64 support
|
||||||
+#endif
|
+#endif
|
||||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
}
|
}
|
||||||
@@ -237,34 +237,3 @@ Need to modify the `input_event` method in `drivers/input/input.c`, not `input_h
|
|||||||
|
|
||||||
spin_lock_irqsave(&dev->event_lock, flags);
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
```
|
```
|
||||||
|
|
||||||
### devpts Hook (`pty.c`)
|
|
||||||
|
|
||||||
Need to modify the `pts_unix98_lookup` method in `drivers/tty/pty.c`
|
|
||||||
|
|
||||||
```patch
|
|
||||||
--- a/drivers/tty/pty.c
|
|
||||||
+++ b/drivers/tty/pty.c
|
|
||||||
@@ -31,6 +31,10 @@
|
|
||||||
#include <linux/compat.h>
|
|
||||||
#include "tty.h"
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#undef TTY_DEBUG_HANGUP
|
|
||||||
#ifdef TTY_DEBUG_HANGUP
|
|
||||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
|
||||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
|
||||||
{
|
|
||||||
struct tty_struct *tty;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
mutex_lock(&devpts_mutex);
|
|
||||||
tty = devpts_get_priv(file->f_path.dentry);
|
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
.ptr.compat = __envp,
|
.ptr.compat = __envp,
|
||||||
};
|
};
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0)); // 32-bit su and 32-on-64 support
|
||||||
+#endif
|
+#endif
|
||||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||||
}
|
}
|
||||||
@@ -236,35 +236,4 @@
|
|||||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->event_lock, flags);
|
spin_lock_irqsave(&dev->event_lock, flags);
|
||||||
```
|
```
|
||||||
|
|
||||||
### devpts 钩子 (`pty.c`)
|
|
||||||
|
|
||||||
需要修改 `drivers/tty/pty.c` 的 `pts_unix98_lookup` 方法
|
|
||||||
|
|
||||||
```patch
|
|
||||||
--- a/drivers/tty/pty.c
|
|
||||||
+++ b/drivers/tty/pty.c
|
|
||||||
@@ -31,6 +31,10 @@
|
|
||||||
#include <linux/compat.h>
|
|
||||||
#include "tty.h"
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#undef TTY_DEBUG_HANGUP
|
|
||||||
#ifdef TTY_DEBUG_HANGUP
|
|
||||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
|
||||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
|
||||||
{
|
|
||||||
struct tty_struct *tty;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
mutex_lock(&devpts_mutex);
|
|
||||||
tty = devpts_get_priv(file->f_path.dentry);
|
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
```
|
|
||||||
@@ -56,8 +56,8 @@ ColumnLimit: 80
|
|||||||
CommentPragmas: '^ IWYU pragma:'
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
ConstructorInitializerIndentWidth: 8
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: false
|
Cpp11BracedListStyle: false
|
||||||
DerivePointerAlignment: false
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
@@ -501,7 +501,7 @@ IncludeCategories:
|
|||||||
IncludeIsMainRegex: '(Test)?$'
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||||
IndentWidth: 8
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
|
|||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||||
ObjCBlockIndentWidth: 8
|
ObjCBlockIndentWidth: 4
|
||||||
ObjCSpaceAfterProperty: true
|
ObjCSpaceAfterProperty: true
|
||||||
ObjCSpaceBeforeProtocolList: true
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
|
|||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Cpp03
|
Standard: Cpp03
|
||||||
TabWidth: 8
|
TabWidth: 4
|
||||||
UseTab: Always
|
UseTab: Never
|
||||||
...
|
...
|
||||||
|
|||||||
22
kernel/.gitignore
vendored
Normal file
22
kernel/.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.cache/
|
||||||
|
.thinlto-cache/
|
||||||
|
compile_commands.json
|
||||||
|
*.ko
|
||||||
|
*.o
|
||||||
|
*.mod
|
||||||
|
*.lds
|
||||||
|
*.mod.o
|
||||||
|
.*.o*
|
||||||
|
.*.mod*
|
||||||
|
*.ko*
|
||||||
|
*.mod.c
|
||||||
|
*.symvers*
|
||||||
|
*.order
|
||||||
|
.*.ko.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
libs/
|
||||||
|
obj/
|
||||||
|
|
||||||
|
CLAUDE.md
|
||||||
|
.ddk-version
|
||||||
|
.vscode/settings.json
|
||||||
11
kernel/.vscode/c_cpp_properties.json
vendored
Normal file
11
kernel/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"intelliSenseMode": "gcc-arm64",
|
||||||
|
"compileCommands": "${workspaceFolder}/compile_commands.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
92
kernel/.vscode/generate_compdb.py
vendored
Normal file
92
kernel/.vscode/generate_compdb.py
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from __future__ import print_function, division
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import fnmatch
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
CMD_VAR_RE = re.compile(r'^\s*(?:saved)?cmd_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
|
||||||
|
SOURCE_VAR_RE = re.compile(r'^\s*source_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def print_progress_bar(progress):
|
||||||
|
progress_bar = '[' + '|' * int(50 * progress) + '-' * int(50 * (1.0 - progress)) + ']'
|
||||||
|
print('\r', progress_bar, "{0:.1%}".format(progress), end='\r', file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_cmd_file(out_dir, cmdfile_path):
|
||||||
|
with open(cmdfile_path, 'r') as cmdfile:
|
||||||
|
cmdfile_content = cmdfile.read()
|
||||||
|
|
||||||
|
commands = { match.group(1): match.group(2) for match in CMD_VAR_RE.finditer(cmdfile_content) }
|
||||||
|
sources = { match.group(1): match.group(2) for match in SOURCE_VAR_RE.finditer(cmdfile_content) }
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'directory': out_dir,
|
||||||
|
'command': commands[o_file_name],
|
||||||
|
'file': source,
|
||||||
|
'output': o_file_name
|
||||||
|
} for o_file_name, source in sources.items()]
|
||||||
|
|
||||||
|
|
||||||
|
def gen_compile_commands(cmd_file_search_path, out_dir):
|
||||||
|
print("Building *.o.cmd file list...", file=sys.stderr)
|
||||||
|
|
||||||
|
out_dir = os.path.abspath(out_dir)
|
||||||
|
|
||||||
|
if not cmd_file_search_path:
|
||||||
|
cmd_file_search_path = [out_dir]
|
||||||
|
|
||||||
|
cmd_files = []
|
||||||
|
for search_path in cmd_file_search_path:
|
||||||
|
if (os.path.isdir(search_path)):
|
||||||
|
for cur_dir, subdir, files in os.walk(search_path):
|
||||||
|
cmd_files.extend(os.path.join(cur_dir, cmdfile_name) for cmdfile_name in fnmatch.filter(files, '*.o.cmd'))
|
||||||
|
else:
|
||||||
|
cmd_files.extend(search_path)
|
||||||
|
|
||||||
|
if not cmd_files:
|
||||||
|
print("No *.o.cmd files found in", ", ".join(cmd_file_search_path), file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Parsing *.o.cmd files...", file=sys.stderr)
|
||||||
|
|
||||||
|
n_processed = 0
|
||||||
|
print_progress_bar(0)
|
||||||
|
|
||||||
|
compdb = []
|
||||||
|
pool = multiprocessing.Pool()
|
||||||
|
try:
|
||||||
|
for compdb_chunk in pool.imap_unordered(functools.partial(parse_cmd_file, out_dir), cmd_files, chunksize=int(math.sqrt(len(cmd_files)))):
|
||||||
|
compdb.extend(compdb_chunk)
|
||||||
|
n_processed += 1
|
||||||
|
print_progress_bar(n_processed / len(cmd_files))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
pool.terminate()
|
||||||
|
pool.join()
|
||||||
|
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Writing compile_commands.json...", file=sys.stderr)
|
||||||
|
|
||||||
|
with open('compile_commands.json', 'w') as compdb_file:
|
||||||
|
json.dump(compdb, compdb_file, indent=1)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cmd_parser = argparse.ArgumentParser()
|
||||||
|
cmd_parser.add_argument('-O', '--out-dir', type=str, default=os.getcwd(), help="Build output directory")
|
||||||
|
cmd_parser.add_argument('cmd_file_search_path', nargs='*', help="*.cmd file search path")
|
||||||
|
gen_compile_commands(**vars(cmd_parser.parse_args()))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
16
kernel/.vscode/tasks.json
vendored
Normal file
16
kernel/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Generate compile_commands.json",
|
||||||
|
"type": "process",
|
||||||
|
"command": "python",
|
||||||
|
"args": [
|
||||||
|
"${workspaceRoot}/.vscode/generate_compdb.py"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,57 +1,42 @@
|
|||||||
menu "KernelSU"
|
menu "KernelSU"
|
||||||
|
|
||||||
config KSU
|
config KSU
|
||||||
tristate "KernelSU function support"
|
tristate "KernelSU function support"
|
||||||
depends on OVERLAY_FS
|
default y
|
||||||
default y
|
help
|
||||||
help
|
Enable kernel-level root privileges on Android System.
|
||||||
Enable kernel-level root privileges on Android System.
|
To compile as a module, choose M here: the
|
||||||
To compile as a module, choose M here: the
|
module will be called kernelsu.
|
||||||
module will be called kernelsu.
|
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable KernelSU debug mode.
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
|
config KSU_MANUAL_SU
|
||||||
|
bool "Use manual su"
|
||||||
|
depends on KSU
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Use manual su and authorize the corresponding command line and application via prctl
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable SukiSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
depends on KSU && 64BIT
|
depends on KSU && 64BIT
|
||||||
default n
|
default n
|
||||||
help
|
|
||||||
Enabling this option will activate the KPM feature of SukiSU.
|
|
||||||
This option is suitable for scenarios where you need to force KPM to be enabled.
|
|
||||||
but it may affect system stability.
|
|
||||||
select KALLSYMS
|
|
||||||
select KALLSYMS_ALL
|
|
||||||
|
|
||||||
choice
|
|
||||||
prompt "KernelSU hook type"
|
|
||||||
depends on KSU
|
|
||||||
default KSU_KPROBES_HOOK
|
|
||||||
help
|
help
|
||||||
Hook type for KernelSU
|
Enabling this option will activate the KPM feature of SukiSU.
|
||||||
|
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||||
config KSU_KPROBES_HOOK
|
but it may affect system stability.
|
||||||
bool "Hook KernelSU with Kprobes"
|
select KALLSYMS
|
||||||
depends on KPROBES
|
select KALLSYMS_ALL
|
||||||
help
|
|
||||||
If enabled, Hook required KernelSU syscalls with Kernel-probe.
|
|
||||||
|
|
||||||
config KSU_TRACEPOINT_HOOK
|
|
||||||
bool "Hook KernelSU with Tracepoint"
|
|
||||||
depends on TRACEPOINTS
|
|
||||||
help
|
|
||||||
If enabled, Hook required KernelSU syscalls with Tracepoint.
|
|
||||||
|
|
||||||
config KSU_MANUAL_HOOK
|
config KSU_MANUAL_HOOK
|
||||||
bool "Hook KernelSU manually"
|
bool "Hook KernelSU manually"
|
||||||
depends on KSU != m
|
depends on KSU != m
|
||||||
help
|
help
|
||||||
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
||||||
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
kernelsu-objs := ksu.o
|
kernelsu-objs := ksu.o
|
||||||
kernelsu-objs += allowlist.o
|
kernelsu-objs += allowlist.o
|
||||||
|
kernelsu-objs += app_profile.o
|
||||||
kernelsu-objs += dynamic_manager.o
|
kernelsu-objs += dynamic_manager.o
|
||||||
kernelsu-objs += apk_sign.o
|
kernelsu-objs += apk_sign.o
|
||||||
kernelsu-objs += sucompat.o
|
kernelsu-objs += sucompat.o
|
||||||
|
kernelsu-objs += syscall_hook_manager.o
|
||||||
kernelsu-objs += throne_tracker.o
|
kernelsu-objs += throne_tracker.o
|
||||||
kernelsu-objs += core_hook.o
|
kernelsu-objs += pkg_observer.o
|
||||||
|
kernelsu-objs += throne_tracker.o
|
||||||
|
kernelsu-objs += umount_manager.o
|
||||||
|
kernelsu-objs += setuid_hook.o
|
||||||
|
kernelsu-objs += kernel_umount.o
|
||||||
|
kernelsu-objs += supercalls.o
|
||||||
|
kernelsu-objs += feature.o
|
||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += seccomp_cache.o
|
||||||
|
kernelsu-objs += file_wrapper.o
|
||||||
|
kernelsu-objs += util.o
|
||||||
kernelsu-objs += throne_comm.o
|
kernelsu-objs += throne_comm.o
|
||||||
|
kernelsu-objs += sulog.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
||||||
kernelsu-objs += ksu_trace.o
|
ccflags-y += -DCONFIG_KSU_MANUAL_SU
|
||||||
|
kernelsu-objs += manual_su.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
kernelsu-objs += selinux/selinux.o
|
kernelsu-objs += selinux/selinux.o
|
||||||
@@ -21,46 +33,62 @@ ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
|||||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|
||||||
obj-$(CONFIG_KSU) += kernelsu.o
|
obj-$(CONFIG_KSU) += kernelsu.o
|
||||||
obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_KPM) += kpm/
|
obj-$(CONFIG_KPM) += kpm/
|
||||||
|
|
||||||
|
|
||||||
REPO_OWNER := SukiSU-Ultra
|
REPO_OWNER := SukiSU-Ultra
|
||||||
REPO_NAME := SukiSU-Ultra
|
REPO_NAME := SukiSU-Ultra
|
||||||
REPO_BRANCH := main
|
REPO_BRANCH := miuix
|
||||||
KSU_VERSION_API := 3.2.0
|
KSU_VERSION_API := 4.0.0
|
||||||
|
|
||||||
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||||
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||||
|
|
||||||
|
KDIR := $(KDIR)
|
||||||
|
MDIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||||
|
|
||||||
|
ifneq ($(KDIR),)
|
||||||
|
$(info -- KDIR: $(KDIR))
|
||||||
|
$(info -- MDIR: $(MDIR))
|
||||||
|
endif
|
||||||
|
|
||||||
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -s "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -s "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
|
||||||
KSU_GITHUB_VERSION_COMMIT := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
|
KSU_GITHUB_VERSION_COMMIT := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
|
||||||
|
|
||||||
LOCAL_GIT_EXISTS := $(shell test -e $(srctree)/$(src)/../.git && echo 1 || echo 0)
|
ifeq ($(findstring $(srctree),$(src)),$(srctree))
|
||||||
|
KSU_SRC := $(src)
|
||||||
|
else
|
||||||
|
KSU_SRC := $(srctree)/$(src)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(shell test -e $(KSU_SRC)/../.git && echo "in-tree"),in-tree)
|
||||||
|
KSU_SRC := $(MDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
|
||||||
|
|
||||||
define get_ksu_version_full
|
define get_ksu_version_full
|
||||||
v$1-$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
|
v$1-$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
|
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
|
||||||
ifeq ($(LOCAL_GIT_EXISTS),1)
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
|
KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
|
||||||
KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 700)
|
KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
|
||||||
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
|
||||||
else
|
else
|
||||||
KSU_VERSION := 13000
|
KSU_VERSION := 13000
|
||||||
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION_COMMIT) + 700)
|
KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
|
||||||
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
|
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(KSU_GITHUB_VERSION),)
|
ifeq ($(KSU_GITHUB_VERSION),)
|
||||||
ifeq ($(LOCAL_GIT_EXISTS),1)
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
|
||||||
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
|
||||||
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
|
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
|
||||||
@@ -69,7 +97,7 @@ ifeq ($(KSU_GITHUB_VERSION),)
|
|||||||
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
|
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
|
||||||
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
|
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
|
||||||
endif
|
endif
|
||||||
@@ -93,14 +121,13 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
|||||||
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
||||||
|
ccflags-y += -DKSU_MANUAL_HOOK
|
||||||
ifeq ($(CONFIG_KSU_KPROBES_HOOK), y)
|
$(info -- SukiSU: KSU_MANUAL_HOOK Temporarily discontinued))
|
||||||
$(info -- SukiSU: CONFIG_KSU_KPROBES_HOOK)
|
else
|
||||||
else ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
ccflags-y += -DKSU_KPROBES_HOOK
|
||||||
$(info -- SukiSU: CONFIG_KSU_TRACEPOINT_HOOK)
|
ccflags-y += -DKSU_TP_HOOK
|
||||||
else ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
$(info -- SukiSU: KSU_TRACEPOINT_HOOK)
|
||||||
$(info -- SukiSU: CONFIG_KSU_MANUAL_HOOK)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||||
@@ -117,14 +144,30 @@ endif
|
|||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||||
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
||||||
|
|
||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
|
||||||
ifeq ($(CONFIG_KPM), y)
|
ifeq ($(CONFIG_KPM), y)
|
||||||
$(info -- KPM is enabled)
|
$(info -- KPM is enabled)
|
||||||
else
|
else
|
||||||
$(info -- KPM is disabled)
|
$(info -- KPM is disabled)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
# Check new vfs_getattr()
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Function proc_ops check
|
||||||
|
ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
|
||||||
|
endif
|
||||||
|
|
||||||
|
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes
|
||||||
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C $(KDIR) M=$(MDIR) modules
|
||||||
|
compdb:
|
||||||
|
python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR)
|
||||||
|
clean:
|
||||||
|
make -C $(KDIR) M=$(MDIR) clean
|
||||||
|
|
||||||
# Keep a new line here!! Because someone may append config
|
# Keep a new line here!! Because someone may append config
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
@@ -8,14 +10,16 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
#include <linux/compiler_types.h>
|
#include <linux/compiler_types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||||
#define FILE_FORMAT_VERSION 3 // u32
|
#define FILE_FORMAT_VERSION 3 // u32
|
||||||
@@ -29,58 +33,61 @@ static DEFINE_MUTEX(allowlist_mutex);
|
|||||||
static struct root_profile default_root_profile;
|
static struct root_profile default_root_profile;
|
||||||
static struct non_root_profile default_non_root_profile;
|
static struct non_root_profile default_non_root_profile;
|
||||||
|
|
||||||
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
void persistent_allow_list(void);
|
||||||
|
|
||||||
|
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly
|
||||||
|
__aligned(PAGE_SIZE);
|
||||||
static int allow_list_pointer __read_mostly = 0;
|
static int allow_list_pointer __read_mostly = 0;
|
||||||
|
|
||||||
static void remove_uid_from_arr(uid_t uid)
|
static void remove_uid_from_arr(uid_t uid)
|
||||||
{
|
{
|
||||||
int *temp_arr;
|
int *temp_arr;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (allow_list_pointer == 0)
|
if (allow_list_pointer == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
if (temp_arr == NULL) {
|
if (temp_arr == NULL) {
|
||||||
pr_err("%s: unable to allocate memory\n", __func__);
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||||
if (allow_list_arr[i] == uid)
|
if (allow_list_arr[i] == uid)
|
||||||
continue;
|
continue;
|
||||||
temp_arr[j++] = allow_list_arr[i];
|
temp_arr[j++] = allow_list_arr[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_list_pointer = j;
|
allow_list_pointer = j;
|
||||||
|
|
||||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||||
temp_arr[j] = -1;
|
temp_arr[j] = -1;
|
||||||
|
|
||||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||||
kfree(temp_arr);
|
kfree(temp_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_default_profiles()
|
static void init_default_profiles(void)
|
||||||
{
|
{
|
||||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||||
|
|
||||||
default_root_profile.uid = 0;
|
default_root_profile.uid = 0;
|
||||||
default_root_profile.gid = 0;
|
default_root_profile.gid = 0;
|
||||||
default_root_profile.groups_count = 1;
|
default_root_profile.groups_count = 1;
|
||||||
default_root_profile.groups[0] = 0;
|
default_root_profile.groups[0] = 0;
|
||||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||||
sizeof(default_root_profile.capabilities.effective));
|
sizeof(default_root_profile.capabilities.effective));
|
||||||
default_root_profile.namespaces = 0;
|
default_root_profile.namespaces = 0;
|
||||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
// This means that we will umount modules by default!
|
// This means that we will umount modules by default!
|
||||||
default_non_root_profile.umount_modules = true;
|
default_non_root_profile.umount_modules = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perm_data {
|
struct perm_data {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct list_head allow_list;
|
static struct list_head allow_list;
|
||||||
@@ -90,437 +97,535 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
|||||||
|
|
||||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||||
|
|
||||||
static struct work_struct ksu_save_work;
|
|
||||||
static struct work_struct ksu_load_work;
|
|
||||||
|
|
||||||
bool persistent_allow_list(void);
|
|
||||||
|
|
||||||
void ksu_show_allow_list(void)
|
void ksu_show_allow_list(void)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
pr_info("ksu_show_allow_list\n");
|
pr_info("ksu_show_allow_list\n");
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||||
p->profile.allow_su);
|
p->profile.allow_su);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
static void ksu_grant_root_to_shell()
|
static void ksu_grant_root_to_shell(void)
|
||||||
{
|
{
|
||||||
struct app_profile profile = {
|
struct app_profile profile = {
|
||||||
.version = KSU_APP_PROFILE_VER,
|
.version = KSU_APP_PROFILE_VER,
|
||||||
.allow_su = true,
|
.allow_su = true,
|
||||||
.current_uid = 2000,
|
.current_uid = 2000,
|
||||||
};
|
};
|
||||||
strcpy(profile.key, "com.android.shell");
|
strcpy(profile.key, "com.android.shell");
|
||||||
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(profile.rp_config.profile.selinux_domain,
|
||||||
ksu_set_app_profile(&profile, false);
|
KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ksu_get_app_profile(struct app_profile *profile)
|
bool ksu_get_app_profile(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
bool uid_match = profile->current_uid == p->profile.current_uid;
|
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||||
if (uid_match) {
|
if (uid_match) {
|
||||||
// found it, override it with ours
|
// found it, override it with ours
|
||||||
memcpy(profile, &p->profile, sizeof(*profile));
|
memcpy(profile, &p->profile, sizeof(*profile));
|
||||||
found = true;
|
found = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool forbid_system_uid(uid_t uid) {
|
static inline bool forbid_system_uid(uid_t uid)
|
||||||
#define SHELL_UID 2000
|
{
|
||||||
#define SYSTEM_UID 1000
|
#define SHELL_UID 2000
|
||||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
#define SYSTEM_UID 1000
|
||||||
|
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool profile_valid(struct app_profile *profile)
|
static bool profile_valid(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (!profile_valid(profile)) {
|
if (!profile_valid(profile)) {
|
||||||
pr_err("Failed to set app profile: invalid profile!\n");
|
pr_err("Failed to set app profile: invalid profile!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
// both uid and package must match, otherwise it will break multiple package with different user id
|
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||||
if (profile->current_uid == p->profile.current_uid &&
|
if (profile->current_uid == p->profile.current_uid &&
|
||||||
!strcmp(profile->key, p->profile.key)) {
|
!strcmp(profile->key, p->profile.key)) {
|
||||||
// found it, just override it all!
|
// found it, just override it all!
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
result = true;
|
result = true;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found, alloc a new node!
|
// not found, alloc a new node!
|
||||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
pr_err("ksu_set_app_profile alloc failed\n");
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->rp_config.profile.gid,
|
profile->rp_config.profile.gid,
|
||||||
profile->rp_config.profile.selinux_domain);
|
profile->rp_config.profile.selinux_domain);
|
||||||
} else {
|
} else {
|
||||||
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->nrp_config.profile.umount_modules);
|
profile->nrp_config.profile.umount_modules);
|
||||||
}
|
}
|
||||||
list_add_tail(&p->list, &allow_list);
|
list_add_tail(&p->list, &allow_list);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||||
if (profile->allow_su)
|
if (profile->allow_su)
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |=
|
||||||
else
|
1 << (profile->current_uid % BITS_PER_BYTE);
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
else
|
||||||
} else {
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &=
|
||||||
if (profile->allow_su) {
|
~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||||
/*
|
} else {
|
||||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
if (profile->allow_su) {
|
||||||
* registered to request superuser?
|
/*
|
||||||
*/
|
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
* registered to request superuser?
|
||||||
pr_err("too many apps registered\n");
|
*/
|
||||||
WARN_ON(1);
|
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||||
return false;
|
pr_err("too many apps registered\n");
|
||||||
}
|
WARN_ON(1);
|
||||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
return false;
|
||||||
} else {
|
}
|
||||||
remove_uid_from_arr(profile->current_uid);
|
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||||
}
|
} else {
|
||||||
}
|
remove_uid_from_arr(profile->current_uid);
|
||||||
result = true;
|
}
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
|
||||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||||
// set default non root profile
|
// set default non root profile
|
||||||
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||||
sizeof(default_non_root_profile));
|
sizeof(default_non_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!strcmp(profile->key, "#"))) {
|
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||||
// set default root profile
|
// set default root profile
|
||||||
memcpy(&default_root_profile, &profile->rp_config.profile,
|
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||||
sizeof(default_root_profile));
|
sizeof(default_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (persist)
|
if (persist) {
|
||||||
persistent_allow_list();
|
persistent_allow_list();
|
||||||
|
// FIXME: use a new flag
|
||||||
|
ksu_mark_running_process();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __ksu_is_allow_uid(uid_t uid)
|
bool __ksu_is_allow_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(uid == 0)) {
|
if (forbid_system_uid(uid)) {
|
||||||
// already root, but only allow our domain.
|
// do not bother going through the list if it's system
|
||||||
return is_ksu_domain();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forbid_system_uid(uid)) {
|
if (likely(ksu_is_manager_uid_valid()) &&
|
||||||
// do not bother going through the list if it's system
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
return false;
|
// manager is always allowed!
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
// manager is always allowed!
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
|
||||||
return true;
|
(1 << (uid % BITS_PER_BYTE)));
|
||||||
}
|
} else {
|
||||||
|
for (i = 0; i < allow_list_pointer; i++) {
|
||||||
|
if (allow_list_arr[i] == uid)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
return false;
|
||||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
}
|
||||||
} else {
|
|
||||||
for (i = 0; i < allow_list_pointer; i++) {
|
|
||||||
if (allow_list_arr[i] == uid)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
bool __ksu_is_allow_uid_for_current(uid_t uid)
|
||||||
|
{
|
||||||
|
if (unlikely(uid == 0)) {
|
||||||
|
// already root, but only allow our domain.
|
||||||
|
return is_ksu_domain();
|
||||||
|
}
|
||||||
|
return __ksu_is_allow_uid(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid)
|
bool ksu_uid_should_umount(uid_t uid)
|
||||||
{
|
{
|
||||||
struct app_profile profile = { .current_uid = uid };
|
struct app_profile profile = { .current_uid = uid };
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(ksu_is_manager_uid_valid()) &&
|
||||||
// we should not umount on manager!
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
return false;
|
// we should not umount on manager!
|
||||||
}
|
return false;
|
||||||
bool found = ksu_get_app_profile(&profile);
|
}
|
||||||
if (!found) {
|
bool found = ksu_get_app_profile(&profile);
|
||||||
// no app profile found, it must be non root app
|
if (!found) {
|
||||||
return default_non_root_profile.umount_modules;
|
// no app profile found, it must be non root app
|
||||||
}
|
return default_non_root_profile.umount_modules;
|
||||||
if (profile.allow_su) {
|
}
|
||||||
// if found and it is granted to su, we shouldn't umount for it
|
if (profile.allow_su) {
|
||||||
return false;
|
// if found and it is granted to su, we shouldn't umount for it
|
||||||
} else {
|
return false;
|
||||||
// found an app profile
|
} else {
|
||||||
if (profile.nrp_config.use_default) {
|
// found an app profile
|
||||||
return default_non_root_profile.umount_modules;
|
if (profile.nrp_config.use_default) {
|
||||||
} else {
|
return default_non_root_profile.umount_modules;
|
||||||
return profile.nrp_config.profile.umount_modules;
|
} else {
|
||||||
}
|
return profile.nrp_config.profile.umount_modules;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid)
|
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||||
if (!p->profile.rp_config.use_default) {
|
if (!p->profile.rp_config.use_default) {
|
||||||
return &p->profile.rp_config.profile;
|
return &p->profile.rp_config.profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default profile
|
// use default profile
|
||||||
return &default_root_profile;
|
return &default_root_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||||
if (p->profile.allow_su == allow) {
|
if (p->profile.allow_su == allow) {
|
||||||
array[i++] = p->profile.current_uid;
|
array[i++] = p->profile.current_uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*length = i;
|
*length = i;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_save_allow_list(struct work_struct *work)
|
static void do_persistent_allow_list(struct callback_head *_cb)
|
||||||
{
|
{
|
||||||
u32 magic = FILE_MAGIC;
|
u32 magic = FILE_MAGIC;
|
||||||
u32 version = FILE_FORMAT_VERSION;
|
u32 version = FILE_FORMAT_VERSION;
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
|
|
||||||
struct file *fp =
|
mutex_lock(&allowlist_mutex);
|
||||||
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
struct file *fp =
|
||||||
if (IS_ERR(fp)) {
|
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
if (IS_ERR(fp)) {
|
||||||
return;
|
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||||
}
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
// store magic and version
|
// store magic and version
|
||||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||||
sizeof(magic)) {
|
pr_err("save_allow_list write magic failed.\n");
|
||||||
pr_err("save_allow_list write magic failed.\n");
|
goto close_file;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
sizeof(version)) {
|
pr_err("save_allow_list write version failed.\n");
|
||||||
pr_err("save_allow_list write version failed.\n");
|
goto close_file;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
||||||
p->profile.key, p->profile.current_uid,
|
p->profile.key, p->profile.current_uid, p->profile.allow_su);
|
||||||
p->profile.allow_su);
|
|
||||||
|
|
||||||
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
kernel_write(fp, &p->profile, sizeof(p->profile), &off);
|
||||||
&off);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
close_file:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
kfree(_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_load_allow_list(struct work_struct *work)
|
void persistent_allow_list()
|
||||||
{
|
{
|
||||||
loff_t off = 0;
|
struct task_struct *tsk;
|
||||||
ssize_t ret = 0;
|
|
||||||
struct file *fp = NULL;
|
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
|
||||||
u32 magic;
|
if (!tsk) {
|
||||||
u32 version;
|
pr_err("save_allow_list find init task err\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct callback_head *cb =
|
||||||
|
kzalloc(sizeof(struct callback_head), GFP_KERNEL);
|
||||||
|
if (!cb) {
|
||||||
|
pr_err("save_allow_list alloc cb err\b");
|
||||||
|
goto put_task;
|
||||||
|
}
|
||||||
|
cb->func = do_persistent_allow_list;
|
||||||
|
task_work_add(tsk, cb, TWA_RESUME);
|
||||||
|
|
||||||
|
put_task:
|
||||||
|
put_task_struct(tsk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_load_allow_list()
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
struct file *fp = NULL;
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
// always allow adb shell by default
|
// always allow adb shell by default
|
||||||
ksu_grant_root_to_shell();
|
ksu_grant_root_to_shell();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load allowlist now!
|
// load allowlist now!
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify magic
|
// verify magic
|
||||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||||
sizeof(magic) ||
|
magic != FILE_MAGIC) {
|
||||||
magic != FILE_MAGIC) {
|
pr_err("allowlist file invalid: %d!\n", magic);
|
||||||
pr_err("allowlist file invalid: %d!\n", magic);
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
sizeof(version)) {
|
pr_err("allowlist read version: %d failed\n", version);
|
||||||
pr_err("allowlist read version: %d failed\n", version);
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("allowlist version: %d\n", version);
|
pr_info("allowlist version: %d\n", version);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
|
|
||||||
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
ret = kernel_read(fp, &profile, sizeof(profile), &off);
|
||||||
&off);
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
pr_info("load_allow_list read err: %zd\n", ret);
|
pr_info("load_allow_list read err: %zd\n", ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", profile.key,
|
||||||
profile.key, profile.current_uid, profile.allow_su);
|
profile.current_uid, profile.allow_su);
|
||||||
ksu_set_app_profile(&profile, false);
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
ksu_show_allow_list();
|
ksu_show_allow_list();
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
|
||||||
|
void *data)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
bool modified = false;
|
if (!ksu_boot_completed) {
|
||||||
// TODO: use RCU!
|
pr_info("boot not completed, skip prune\n");
|
||||||
mutex_lock(&allowlist_mutex);
|
return;
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
}
|
||||||
uid_t uid = np->profile.current_uid;
|
|
||||||
char *package = np->profile.key;
|
|
||||||
// we use this uid for special cases, don't prune it!
|
|
||||||
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
|
||||||
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
|
||||||
modified = true;
|
|
||||||
pr_info("prune uid: %d, package: %s\n", uid, package);
|
|
||||||
list_del(&np->list);
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
|
||||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
|
||||||
}
|
|
||||||
remove_uid_from_arr(uid);
|
|
||||||
smp_mb();
|
|
||||||
kfree(np);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&allowlist_mutex);
|
|
||||||
|
|
||||||
if (modified) {
|
bool modified = false;
|
||||||
persistent_allow_list();
|
// TODO: use RCU!
|
||||||
}
|
mutex_lock(&allowlist_mutex);
|
||||||
}
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
uid_t uid = np->profile.current_uid;
|
||||||
|
char *package = np->profile.key;
|
||||||
|
// we use this uid for special cases, don't prune it!
|
||||||
|
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||||
|
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
||||||
|
modified = true;
|
||||||
|
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||||
|
list_del(&np->list);
|
||||||
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
|
allow_list_bitmap[uid / BITS_PER_BYTE] &=
|
||||||
|
~(1 << (uid % BITS_PER_BYTE));
|
||||||
|
}
|
||||||
|
remove_uid_from_arr(uid);
|
||||||
|
smp_mb();
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
|
||||||
// make sure allow list works cross boot
|
if (modified) {
|
||||||
bool persistent_allow_list(void)
|
persistent_allow_list();
|
||||||
{
|
}
|
||||||
return ksu_queue_work(&ksu_save_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ksu_load_allow_list(void)
|
|
||||||
{
|
|
||||||
return ksu_queue_work(&ksu_load_work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_init(void)
|
void ksu_allowlist_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||||
allow_list_arr[i] = -1;
|
allow_list_arr[i] = -1;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&allow_list);
|
INIT_LIST_HEAD(&allow_list);
|
||||||
|
|
||||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
init_default_profiles();
|
||||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
|
||||||
|
|
||||||
init_default_profiles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_exit(void)
|
void ksu_allowlist_exit(void)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
do_save_allow_list(NULL);
|
// free allowlist
|
||||||
|
mutex_lock(&allowlist_mutex);
|
||||||
// free allowlist
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
mutex_lock(&allowlist_mutex);
|
list_del(&np->list);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
kfree(np);
|
||||||
list_del(&np->list);
|
}
|
||||||
kfree(np);
|
mutex_unlock(&allowlist_mutex);
|
||||||
}
|
|
||||||
mutex_unlock(&allowlist_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
bool ksu_temp_grant_root_once(uid_t uid)
|
||||||
|
{
|
||||||
|
struct app_profile profile = {
|
||||||
|
.version = KSU_APP_PROFILE_VER,
|
||||||
|
.allow_su = true,
|
||||||
|
.current_uid = uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *default_key = "com.temp.once";
|
||||||
|
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
if (p->profile.current_uid == uid) {
|
||||||
|
strcpy(profile.key, p->profile.key);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
strcpy(profile.key, default_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.rp_config.profile.uid = default_root_profile.uid;
|
||||||
|
profile.rp_config.profile.gid = default_root_profile.gid;
|
||||||
|
profile.rp_config.profile.groups_count = default_root_profile.groups_count;
|
||||||
|
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
|
||||||
|
memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities));
|
||||||
|
profile.rp_config.profile.namespaces = default_root_profile.namespaces;
|
||||||
|
strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain);
|
||||||
|
|
||||||
|
bool ok = ksu_set_app_profile(&profile, false);
|
||||||
|
if (ok)
|
||||||
|
pr_info("pending_root: UID=%d granted and persisted\n", uid);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_temp_revoke_root_once(uid_t uid)
|
||||||
|
{
|
||||||
|
struct app_profile profile = {
|
||||||
|
.version = KSU_APP_PROFILE_VER,
|
||||||
|
.allow_su = false,
|
||||||
|
.current_uid = uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *default_key = "com.temp.once";
|
||||||
|
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
if (p->profile.current_uid == uid) {
|
||||||
|
strcpy(profile.key, p->profile.key);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
strcpy(profile.key, default_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules;
|
||||||
|
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
|
persistent_allow_list();
|
||||||
|
pr_info("pending_root: UID=%d removed and persist updated\n", uid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -2,19 +2,31 @@
|
|||||||
#define __KSU_H_ALLOWLIST
|
#define __KSU_H_ALLOWLIST
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "ksu.h"
|
#include <linux/uidgid.h>
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
#define PER_USER_RANGE 100000
|
||||||
|
#define FIRST_APPLICATION_UID 10000
|
||||||
|
#define LAST_APPLICATION_UID 19999
|
||||||
|
#define FIRST_ISOLATED_UID 99000
|
||||||
|
#define LAST_ISOLATED_UID 99999
|
||||||
|
|
||||||
void ksu_allowlist_init(void);
|
void ksu_allowlist_init(void);
|
||||||
|
|
||||||
void ksu_allowlist_exit(void);
|
void ksu_allowlist_exit(void);
|
||||||
|
|
||||||
bool ksu_load_allow_list(void);
|
void ksu_load_allow_list(void);
|
||||||
|
|
||||||
void ksu_show_allow_list(void);
|
void ksu_show_allow_list(void);
|
||||||
|
|
||||||
|
// Check if the uid is in allow list
|
||||||
bool __ksu_is_allow_uid(uid_t uid);
|
bool __ksu_is_allow_uid(uid_t uid);
|
||||||
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||||
|
|
||||||
|
// Check if the uid is in allow list, or current is ksu domain root
|
||||||
|
bool __ksu_is_allow_uid_for_current(uid_t uid);
|
||||||
|
#define ksu_is_allow_uid_for_current(uid) unlikely(__ksu_is_allow_uid_for_current(uid))
|
||||||
|
|
||||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||||
|
|
||||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
||||||
@@ -24,4 +36,22 @@ bool ksu_set_app_profile(struct app_profile *, bool persist);
|
|||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid);
|
bool ksu_uid_should_umount(uid_t uid);
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid);
|
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||||
|
|
||||||
|
static inline bool is_appuid(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_isolated_process(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
bool ksu_temp_grant_root_once(uid_t uid);
|
||||||
|
void ksu_temp_revoke_root_once(uid_t uid);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,69 +17,65 @@
|
|||||||
#include "apk_sign.h"
|
#include "apk_sign.h"
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "manager_sign.h"
|
#include "manager_sign.h"
|
||||||
|
|
||||||
struct sdesc {
|
struct sdesc {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
char ctx[];
|
char ctx[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct apk_sign_key {
|
static apk_sign_key_t apk_sign_keys[] = {
|
||||||
unsigned size;
|
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
|
||||||
const char *sha256;
|
|
||||||
} apk_sign_keys[] = {
|
|
||||||
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
|
|
||||||
#ifdef EXPECTED_SIZE
|
#ifdef EXPECTED_SIZE
|
||||||
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
sdesc = kmalloc(size, GFP_KERNEL);
|
sdesc = kzalloc(size, GFP_KERNEL);
|
||||||
if (!sdesc)
|
if (!sdesc)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
sdesc->shash.tfm = alg;
|
sdesc->shash.tfm = alg;
|
||||||
return sdesc;
|
return sdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
||||||
unsigned int datalen, unsigned char *digest)
|
unsigned int datalen, unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sdesc = init_sdesc(alg);
|
sdesc = init_sdesc(alg);
|
||||||
if (IS_ERR(sdesc)) {
|
if (IS_ERR(sdesc)) {
|
||||||
pr_info("can't alloc sdesc\n");
|
pr_info("can't alloc sdesc\n");
|
||||||
return PTR_ERR(sdesc);
|
return PTR_ERR(sdesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
||||||
kfree(sdesc);
|
kfree(sdesc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||||
unsigned char *digest)
|
unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct crypto_shash *alg;
|
struct crypto_shash *alg;
|
||||||
char *hash_alg_name = "sha256";
|
char *hash_alg_name = "sha256";
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
||||||
if (IS_ERR(alg)) {
|
if (IS_ERR(alg)) {
|
||||||
pr_info("can't alloc alg %s\n", hash_alg_name);
|
pr_info("can't alloc alg %s\n", hash_alg_name);
|
||||||
return PTR_ERR(alg);
|
return PTR_ERR(alg);
|
||||||
}
|
}
|
||||||
ret = calc_hash(alg, data, datalen, digest);
|
ret = calc_hash(alg, data, datalen, digest);
|
||||||
crypto_free_shash(alg);
|
crypto_free_shash(alg);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -87,304 +83,307 @@ static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
|
|||||||
|
|
||||||
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
||||||
{
|
{
|
||||||
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
||||||
|
|
||||||
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
||||||
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
||||||
current_dynamic_key.size, current_dynamic_key.hash);
|
current_dynamic_key.size, current_dynamic_key.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size4 != current_dynamic_key.size) {
|
if (size4 != current_dynamic_key.size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
char cert[CERT_MAX_LENGTH];
|
char cert[CERT_MAX_LENGTH];
|
||||||
if (size4 > CERT_MAX_LENGTH) {
|
if (size4 > CERT_MAX_LENGTH) {
|
||||||
pr_info("cert length overlimit\n");
|
pr_info("cert length overlimit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, cert, size4, pos);
|
kernel_read(fp, cert, size4, pos);
|
||||||
|
|
||||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
if (ksu_sha256(cert, size4, digest) < 0) {
|
if (ksu_sha256(cert, size4, digest) < 0) {
|
||||||
pr_info("sha256 error\n");
|
pr_info("sha256 error\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
|
||||||
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
||||||
|
|
||||||
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
||||||
if (matched_index) {
|
if (matched_index) {
|
||||||
*matched_index = DYNAMIC_SIGN_INDEX;
|
*matched_index = DYNAMIC_SIGN_INDEX;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct apk_sign_key sign_key;
|
apk_sign_key_t sign_key;
|
||||||
bool signature_valid = false;
|
bool signature_valid = false;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
kernel_read(fp, size4, 0x4, pos); // signer-sequence length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
kernel_read(fp, size4, 0x4, pos); // signer length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
kernel_read(fp, size4, 0x4, pos); // signed data length
|
||||||
|
|
||||||
*offset += 0x4 * 3;
|
*offset += 0x4 * 3;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
kernel_read(fp, size4, 0x4, pos); // digests-sequence length
|
||||||
|
|
||||||
*pos += *size4;
|
*pos += *size4;
|
||||||
*offset += 0x4 + *size4;
|
*offset += 0x4 + *size4;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
kernel_read(fp, size4, 0x4, pos); // certificates length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
kernel_read(fp, size4, 0x4, pos); // certificate length
|
||||||
*offset += 0x4 * 2;
|
*offset += 0x4 * 2;
|
||||||
|
|
||||||
if (ksu_is_dynamic_manager_enabled()) {
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
loff_t temp_pos = *pos;
|
loff_t temp_pos = *pos;
|
||||||
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
||||||
*pos = temp_pos;
|
*pos = temp_pos;
|
||||||
*offset += *size4;
|
*offset += *size4;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||||
sign_key = apk_sign_keys[i];
|
sign_key = apk_sign_keys[i];
|
||||||
|
|
||||||
if (*size4 != sign_key.size)
|
if (*size4 != sign_key.size)
|
||||||
continue;
|
continue;
|
||||||
*offset += *size4;
|
*offset += *size4;
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
char cert[CERT_MAX_LENGTH];
|
char cert[CERT_MAX_LENGTH];
|
||||||
if (*size4 > CERT_MAX_LENGTH) {
|
if (*size4 > CERT_MAX_LENGTH) {
|
||||||
pr_info("cert length overlimit\n");
|
pr_info("cert length overlimit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
kernel_read(fp, cert, *size4, pos);
|
||||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
if (ksu_sha256(cert, *size4, digest) < 0 ) {
|
||||||
pr_info("sha256 error\n");
|
pr_info("sha256 error\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
|
||||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
||||||
|
|
||||||
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||||
signature_valid = true;
|
signature_valid = true;
|
||||||
if (matched_index) {
|
if (matched_index) {
|
||||||
*matched_index = i;
|
*matched_index = i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return signature_valid;
|
return signature_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct zip_entry_header {
|
struct zip_entry_header {
|
||||||
uint32_t signature;
|
uint32_t signature;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint16_t compression;
|
uint16_t compression;
|
||||||
uint16_t mod_time;
|
uint16_t mod_time;
|
||||||
uint16_t mod_date;
|
uint16_t mod_date;
|
||||||
uint32_t crc32;
|
uint32_t crc32;
|
||||||
uint32_t compressed_size;
|
uint32_t compressed_size;
|
||||||
uint32_t uncompressed_size;
|
uint32_t uncompressed_size;
|
||||||
uint16_t file_name_length;
|
uint16_t file_name_length;
|
||||||
uint16_t extra_field_length;
|
uint16_t extra_field_length;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
// This is a necessary but not sufficient condition, but it is enough for us
|
// This is a necessary but not sufficient condition, but it is enough for us
|
||||||
static bool has_v1_signature_file(struct file *fp)
|
static bool has_v1_signature_file(struct file *fp)
|
||||||
{
|
{
|
||||||
struct zip_entry_header header;
|
struct zip_entry_header header;
|
||||||
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
||||||
|
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
|
||||||
while (ksu_kernel_read_compat(fp, &header,
|
while (kernel_read(fp, &header,
|
||||||
sizeof(struct zip_entry_header), &pos) ==
|
sizeof(struct zip_entry_header), &pos) ==
|
||||||
sizeof(struct zip_entry_header)) {
|
sizeof(struct zip_entry_header)) {
|
||||||
if (header.signature != 0x04034b50) {
|
if (header.signature != 0x04034b50) {
|
||||||
// ZIP magic: 'PK'
|
// ZIP magic: 'PK'
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Read the entry file name
|
// Read the entry file name
|
||||||
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
||||||
char fileName[sizeof(MANIFEST)];
|
char fileName[sizeof(MANIFEST)];
|
||||||
ksu_kernel_read_compat(fp, fileName,
|
kernel_read(fp, fileName,
|
||||||
header.file_name_length, &pos);
|
header.file_name_length, &pos);
|
||||||
fileName[header.file_name_length] = '\0';
|
fileName[header.file_name_length] = '\0';
|
||||||
|
|
||||||
// Check if the entry matches META-INF/MANIFEST.MF
|
// Check if the entry matches META-INF/MANIFEST.MF
|
||||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) {
|
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||||
return true;
|
0) {
|
||||||
}
|
return true;
|
||||||
} else {
|
}
|
||||||
// Skip the entry file name
|
} else {
|
||||||
pos += header.file_name_length;
|
// Skip the entry file name
|
||||||
}
|
pos += header.file_name_length;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip to the next entry
|
// Skip to the next entry
|
||||||
pos += header.extra_field_length + header.compressed_size;
|
pos += header.extra_field_length + header.compressed_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||||
{
|
{
|
||||||
unsigned char buffer[0x11] = { 0 };
|
unsigned char buffer[0x11] = { 0 };
|
||||||
u32 size4;
|
u32 size4;
|
||||||
u64 size8, size_of_block;
|
u64 size8, size_of_block;
|
||||||
loff_t pos;
|
|
||||||
bool v2_signing_valid = false;
|
|
||||||
int v2_signing_blocks = 0;
|
|
||||||
bool v3_signing_exist = false;
|
|
||||||
bool v3_1_signing_exist = false;
|
|
||||||
int matched_index = -1;
|
|
||||||
int i;
|
|
||||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
|
||||||
if (IS_ERR(fp)) {
|
|
||||||
pr_err("open %s error.\n", path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
loff_t pos;
|
||||||
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
|
||||||
filp_close(fp, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable inotify for this file
|
bool v2_signing_valid = false;
|
||||||
fp->f_mode |= FMODE_NONOTIFY;
|
int v2_signing_blocks = 0;
|
||||||
|
bool v3_signing_exist = false;
|
||||||
|
bool v3_1_signing_exist = false;
|
||||||
|
int matched_index = -1;
|
||||||
|
int i;
|
||||||
|
struct file *fp = filp_open(path, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("open %s error.\n", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
||||||
for (i = 0;; ++i) {
|
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
||||||
unsigned short n;
|
filp_close(fp, 0);
|
||||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
return 0;
|
||||||
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
}
|
||||||
if (n == i) {
|
|
||||||
pos -= 22;
|
|
||||||
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
|
||||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == 0xffff) {
|
|
||||||
pr_info("error: cannot find eocd\n");
|
|
||||||
goto clean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += 12;
|
// disable inotify for this file
|
||||||
// offset
|
fp->f_mode |= FMODE_NONOTIFY;
|
||||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
|
||||||
pos = size4 - 0x18;
|
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||||
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
for (i = 0;; ++i) {
|
||||||
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
unsigned short n;
|
||||||
goto clean;
|
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||||
}
|
kernel_read(fp, &n, 2, &pos);
|
||||||
|
if (n == i) {
|
||||||
|
pos -= 22;
|
||||||
|
kernel_read(fp, &size4, 4, &pos);
|
||||||
|
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 0xffff) {
|
||||||
|
pr_info("error: cannot find eocd\n");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pos = size4 - (size8 + 0x8);
|
pos += 12;
|
||||||
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
// offset
|
||||||
if (size_of_block != size8) {
|
kernel_read(fp, &size4, 0x4, &pos);
|
||||||
goto clean;
|
pos = size4 - 0x18;
|
||||||
}
|
|
||||||
|
|
||||||
int loop_count = 0;
|
kernel_read(fp, &size8, 0x8, &pos);
|
||||||
while (loop_count++ < 10) {
|
kernel_read(fp, buffer, 0x10, &pos);
|
||||||
uint32_t id;
|
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||||
uint32_t offset;
|
goto clean;
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8,
|
}
|
||||||
&pos); // sequence length
|
|
||||||
if (size8 == size_of_block) {
|
pos = size4 - (size8 + 0x8);
|
||||||
break;
|
kernel_read(fp, &size_of_block, 0x8, &pos);
|
||||||
}
|
if (size_of_block != size8) {
|
||||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
goto clean;
|
||||||
offset = 4;
|
}
|
||||||
if (id == 0x7109871au) {
|
|
||||||
v2_signing_blocks++;
|
int loop_count = 0;
|
||||||
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
while (loop_count++ < 10) {
|
||||||
if (result) {
|
uint32_t id;
|
||||||
v2_signing_valid = true;
|
uint32_t offset;
|
||||||
}
|
kernel_read(fp, &size8, 0x8,
|
||||||
} else if (id == 0xf05368c0u) {
|
&pos); // sequence length
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
if (size8 == size_of_block) {
|
||||||
v3_signing_exist = true;
|
break;
|
||||||
} else if (id == 0x1b93ad61u) {
|
}
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
kernel_read(fp, &id, 0x4, &pos); // id
|
||||||
v3_1_signing_exist = true;
|
offset = 4;
|
||||||
} else {
|
if (id == 0x7109871au) {
|
||||||
|
v2_signing_blocks++;
|
||||||
|
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
||||||
|
if (result) {
|
||||||
|
v2_signing_valid = true;
|
||||||
|
}
|
||||||
|
} else if (id == 0xf05368c0u) {
|
||||||
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||||
|
v3_signing_exist = true;
|
||||||
|
} else if (id == 0x1b93ad61u) {
|
||||||
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||||
|
v3_1_signing_exist = true;
|
||||||
|
} else {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("Unknown id: 0x%08x\n", id);
|
pr_info("Unknown id: 0x%08x\n", id);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pos += (size8 - offset);
|
pos += (size8 - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_blocks != 1) {
|
if (v2_signing_blocks != 1) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v2 signature count: %d\n",
|
pr_err("Unexpected v2 signature count: %d\n",
|
||||||
v2_signing_blocks);
|
v2_signing_blocks);
|
||||||
#endif
|
#endif
|
||||||
v2_signing_valid = false;
|
v2_signing_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_valid) {
|
if (v2_signing_valid) {
|
||||||
int has_v1_signing = has_v1_signature_file(fp);
|
int has_v1_signing = has_v1_signature_file(fp);
|
||||||
if (has_v1_signing) {
|
if (has_v1_signing) {
|
||||||
pr_err("Unexpected v1 signature scheme found!\n");
|
pr_err("Unexpected v1 signature scheme found!\n");
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean:
|
clean:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
|
||||||
if (v3_signing_exist || v3_1_signing_exist) {
|
if (v3_signing_exist || v3_1_signing_exist) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v3 signature scheme found!\n");
|
pr_err("Unexpected v3 signature scheme found!\n");
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_valid) {
|
if (v2_signing_valid) {
|
||||||
if (signature_index) {
|
if (signature_index) {
|
||||||
*signature_index = matched_index;
|
*signature_index = matched_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_multi_manager) {
|
if (check_multi_manager) {
|
||||||
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
||||||
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||||
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Common manager check: any valid signature will do
|
// Common manager check: any valid signature will do
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
@@ -395,19 +394,19 @@ int ksu_debug_manager_uid = -1;
|
|||||||
|
|
||||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||||
{
|
{
|
||||||
int rv = param_set_uint(val, kp);
|
int rv = param_set_uint(val, kp);
|
||||||
ksu_set_manager_uid(ksu_debug_manager_uid);
|
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||||
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kernel_param_ops expected_size_ops = {
|
static struct kernel_param_ops expected_size_ops = {
|
||||||
.set = set_expected_size,
|
.set = set_expected_size,
|
||||||
.get = param_get_uint,
|
.get = param_get_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||||
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
303
kernel/app_profile.c
Normal file
303
kernel/app_profile.c
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "objsec.h"
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
|
||||||
|
static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), };
|
||||||
|
#else
|
||||||
|
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||||
|
{
|
||||||
|
if (profile->groups_count > KSU_MAX_GROUPS) {
|
||||||
|
pr_warn("Failed to setgroups, too large group: %d!\n",
|
||||||
|
profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
||||||
|
// setgroup to root and return early.
|
||||||
|
if (cred->group_info)
|
||||||
|
put_group_info(cred->group_info);
|
||||||
|
cred->group_info = get_group_info(&root_groups);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ngroups = profile->groups_count;
|
||||||
|
struct group_info *group_info = groups_alloc(ngroups);
|
||||||
|
if (!group_info) {
|
||||||
|
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ngroups; i++) {
|
||||||
|
gid_t gid = profile->groups[i];
|
||||||
|
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
||||||
|
if (!gid_valid(kgid)) {
|
||||||
|
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
||||||
|
put_group_info(group_info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group_info->gid[i] = kgid;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups_sort(group_info);
|
||||||
|
set_groups(cred, group_info);
|
||||||
|
put_group_info(group_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_seccomp(void)
|
||||||
|
{
|
||||||
|
assert_spin_locked(¤t->sighand->siglock);
|
||||||
|
// disable seccomp
|
||||||
|
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||||
|
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
clear_syscall_work(SECCOMP);
|
||||||
|
#else
|
||||||
|
clear_thread_flag(TIF_SECCOMP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
current->seccomp.mode = 0;
|
||||||
|
current->seccomp.filter = NULL;
|
||||||
|
atomic_set(¤t->seccomp.filter_count, 0);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_with_root_profile(void)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
struct task_struct *p = current;
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
cred = prepare_creds();
|
||||||
|
if (!cred) {
|
||||||
|
pr_warn("prepare_creds failed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred->euid.val == 0) {
|
||||||
|
pr_warn("Already root, don't escape!\n");
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed");
|
||||||
|
#endif
|
||||||
|
abort_creds(cred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||||
|
|
||||||
|
cred->uid.val = profile->uid;
|
||||||
|
cred->suid.val = profile->uid;
|
||||||
|
cred->euid.val = profile->uid;
|
||||||
|
cred->fsuid.val = profile->uid;
|
||||||
|
|
||||||
|
cred->gid.val = profile->gid;
|
||||||
|
cred->fsgid.val = profile->gid;
|
||||||
|
cred->sgid.val = profile->gid;
|
||||||
|
cred->egid.val = profile->gid;
|
||||||
|
cred->securebits = 0;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||||
|
sizeof(kernel_cap_t));
|
||||||
|
|
||||||
|
// setup capabilities
|
||||||
|
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
||||||
|
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
||||||
|
u64 cap_for_ksud =
|
||||||
|
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||||
|
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||||
|
sizeof(cred->cap_effective));
|
||||||
|
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_permitted));
|
||||||
|
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_bset));
|
||||||
|
|
||||||
|
setup_groups(profile, cred);
|
||||||
|
|
||||||
|
commit_creds(cred);
|
||||||
|
|
||||||
|
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||||
|
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
disable_seccomp();
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
|
||||||
|
#include "ksud.h"
|
||||||
|
|
||||||
|
#ifndef DEVPTS_SUPER_MAGIC
|
||||||
|
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int __manual_su_handle_devpts(struct inode *inode)
|
||||||
|
{
|
||||||
|
if (!current->mm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_t uid = current_uid().val;
|
||||||
|
if (uid % 100000 < 10000) {
|
||||||
|
// not untrusted_app, ignore it
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(!ksu_is_allow_uid_for_current(uid)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
|
||||||
|
struct inode_security_struct *sec = selinux_inode(inode);
|
||||||
|
#else
|
||||||
|
struct inode_security_struct *sec =
|
||||||
|
(struct inode_security_struct *)inode->i_security;
|
||||||
|
#endif
|
||||||
|
if (ksu_file_sid && sec)
|
||||||
|
sec->sid = ksu_file_sid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disable_seccomp_for_task(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
assert_spin_locked(&tsk->sighand->siglock);
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
clear_tsk_thread_flag(tsk, TIF_SECCOMP);
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
tsk->seccomp.mode = SECCOMP_MODE_DISABLED;
|
||||||
|
if (tsk->seccomp.filter) {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
seccomp_filter_release(tsk);
|
||||||
|
#else
|
||||||
|
put_seccomp_filter(tsk);
|
||||||
|
tsk->seccomp.filter = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
|
||||||
|
{
|
||||||
|
struct cred *newcreds;
|
||||||
|
struct task_struct *target_task;
|
||||||
|
unsigned long flags;
|
||||||
|
struct task_struct *p = current;
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
|
||||||
|
|
||||||
|
// Find target task by PID
|
||||||
|
rcu_read_lock();
|
||||||
|
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
|
||||||
|
if (!target_task) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_task_struct(target_task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (task_uid(target_task).val == 0) {
|
||||||
|
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
|
||||||
|
put_task_struct(target_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newcreds = prepare_kernel_cred(target_task);
|
||||||
|
if (newcreds == NULL) {
|
||||||
|
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
|
||||||
|
#endif
|
||||||
|
put_task_struct(target_task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *profile = ksu_get_root_profile(target_uid);
|
||||||
|
|
||||||
|
newcreds->uid.val = profile->uid;
|
||||||
|
newcreds->suid.val = profile->uid;
|
||||||
|
newcreds->euid.val = profile->uid;
|
||||||
|
newcreds->fsuid.val = profile->uid;
|
||||||
|
|
||||||
|
newcreds->gid.val = profile->gid;
|
||||||
|
newcreds->fsgid.val = profile->gid;
|
||||||
|
newcreds->sgid.val = profile->gid;
|
||||||
|
newcreds->egid.val = profile->gid;
|
||||||
|
newcreds->securebits = 0;
|
||||||
|
|
||||||
|
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
|
||||||
|
memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
|
||||||
|
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
|
||||||
|
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
|
||||||
|
|
||||||
|
setup_groups(profile, newcreds);
|
||||||
|
task_lock(target_task);
|
||||||
|
|
||||||
|
const struct cred *old_creds = get_task_cred(target_task);
|
||||||
|
|
||||||
|
rcu_assign_pointer(target_task->real_cred, newcreds);
|
||||||
|
rcu_assign_pointer(target_task->cred, get_cred(newcreds));
|
||||||
|
task_unlock(target_task);
|
||||||
|
|
||||||
|
if (target_task->sighand) {
|
||||||
|
spin_lock_irqsave(&target_task->sighand->siglock, flags);
|
||||||
|
disable_seccomp_for_task(target_task);
|
||||||
|
spin_unlock_irqrestore(&target_task->sighand->siglock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
put_cred(old_creds);
|
||||||
|
wake_up_process(target_task);
|
||||||
|
|
||||||
|
if (target_task->signal->tty) {
|
||||||
|
struct inode *inode = target_task->signal->tty->driver_data;
|
||||||
|
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
|
||||||
|
__manual_su_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put_task_struct(target_task);
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
|
||||||
|
#endif
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
70
kernel/app_profile.h
Normal file
70
kernel/app_profile.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef __KSU_H_APP_PROFILE
|
||||||
|
#define __KSU_H_APP_PROFILE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct cred;
|
||||||
|
|
||||||
|
#define KSU_APP_PROFILE_VER 2
|
||||||
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
|
#define KSU_MAX_GROUPS 32
|
||||||
|
#define KSU_SELINUX_DOMAIN 64
|
||||||
|
|
||||||
|
struct root_profile {
|
||||||
|
int32_t uid;
|
||||||
|
int32_t gid;
|
||||||
|
|
||||||
|
int32_t groups_count;
|
||||||
|
int32_t groups[KSU_MAX_GROUPS];
|
||||||
|
|
||||||
|
// kernel_cap_t is u32[2] for capabilities v3
|
||||||
|
struct {
|
||||||
|
u64 effective;
|
||||||
|
u64 permitted;
|
||||||
|
u64 inheritable;
|
||||||
|
} capabilities;
|
||||||
|
|
||||||
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
|
int32_t namespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct non_root_profile {
|
||||||
|
bool umount_modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct app_profile {
|
||||||
|
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
|
char key[KSU_MAX_PACKAGE_NAME];
|
||||||
|
int32_t current_uid;
|
||||||
|
bool allow_su;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
|
|
||||||
|
struct root_profile profile;
|
||||||
|
} rp_config;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
|
||||||
|
struct non_root_profile profile;
|
||||||
|
} nrp_config;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Escalate current process to root with the appropriate profile
|
||||||
|
void escape_with_root_profile(void);
|
||||||
|
|
||||||
|
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid);
|
||||||
|
|
||||||
|
void disable_seccomp(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -18,10 +18,8 @@
|
|||||||
#define __PT_SP_REG sp
|
#define __PT_SP_REG sp
|
||||||
#define __PT_IP_REG pc
|
#define __PT_IP_REG pc
|
||||||
|
|
||||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
@@ -39,10 +37,8 @@
|
|||||||
#define __PT_RC_REG ax
|
#define __PT_RC_REG ax
|
||||||
#define __PT_SP_REG sp
|
#define __PT_SP_REG sp
|
||||||
#define __PT_IP_REG ip
|
#define __PT_IP_REG ip
|
||||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
1092
kernel/core_hook.c
1092
kernel/core_hook.c
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
|||||||
#ifndef __KSU_H_KSU_CORE
|
|
||||||
#define __KSU_H_KSU_CORE
|
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include "apk_sign.h"
|
|
||||||
|
|
||||||
void __init ksu_core_init(void);
|
|
||||||
void ksu_core_exit(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
#define MAX_MANAGERS 2
|
#define MAX_MANAGERS 2
|
||||||
@@ -233,23 +232,23 @@ static void do_save_dynamic_manager(struct work_struct *work)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||||
pr_err("save_dynamic_manager write magic failed.\n");
|
pr_err("save_dynamic_manager write magic failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
pr_err("save_dynamic_manager write version failed.\n");
|
pr_err("save_dynamic_manager write version failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
|
if (kernel_write(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
|
||||||
pr_err("save_dynamic_manager write config failed.\n");
|
pr_err("save_dynamic_manager write config failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -271,7 +270,7 @@ static void do_load_dynamic_manager(struct work_struct *work)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
if (PTR_ERR(fp) == -ENOENT) {
|
if (PTR_ERR(fp) == -ENOENT) {
|
||||||
pr_info("No saved dynamic manager config found\n");
|
pr_info("No saved dynamic manager config found\n");
|
||||||
@@ -281,20 +280,20 @@ static void do_load_dynamic_manager(struct work_struct *work)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||||
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
|
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
|
||||||
pr_err("dynamic manager file invalid magic: %x!\n", magic);
|
pr_err("dynamic manager file invalid magic: %x!\n", magic);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
pr_err("dynamic manager read version failed\n");
|
pr_err("dynamic manager read version failed\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("dynamic manager file version: %d\n", version);
|
pr_info("dynamic manager file version: %d\n", version);
|
||||||
|
|
||||||
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
|
ret = kernel_read(fp, &loaded_config, sizeof(loaded_config), &off);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
pr_info("load_dynamic_manager read err: %zd\n", ret);
|
pr_info("load_dynamic_manager read err: %zd\n", ret);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -348,14 +347,14 @@ static void do_clear_dynamic_manager(struct work_struct *work)
|
|||||||
|
|
||||||
memset(zero_buffer, 0, sizeof(zero_buffer));
|
memset(zero_buffer, 0, sizeof(zero_buffer));
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write null bytes to overwrite the file content
|
// Write null bytes to overwrite the file content
|
||||||
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
|
if (kernel_write(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
|
||||||
pr_err("clear_dynamic_manager write null bytes failed.\n");
|
pr_err("clear_dynamic_manager write null bytes failed.\n");
|
||||||
} else {
|
} else {
|
||||||
pr_info("Dynamic sign config file cleared successfully\n");
|
pr_info("Dynamic sign config file cleared successfully\n");
|
||||||
|
|||||||
173
kernel/feature.c
Normal file
173
kernel/feature.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX];
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(feature_mutex);
|
||||||
|
|
||||||
|
int ksu_register_feature_handler(const struct ksu_feature_handler *handler)
|
||||||
|
{
|
||||||
|
if (!handler) {
|
||||||
|
pr_err("feature: register handler is NULL\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler->feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", handler->feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->get_handler && !handler->set_handler) {
|
||||||
|
pr_err("feature: no handler provided for feature %u\n", handler->feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
if (feature_handlers[handler->feature_id]) {
|
||||||
|
pr_warn("feature: handler for %u already registered, overwriting\n",
|
||||||
|
handler->feature_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_handlers[handler->feature_id] = handler;
|
||||||
|
|
||||||
|
pr_info("feature: registered handler for %s (id=%u)\n",
|
||||||
|
handler->name ? handler->name : "unknown", handler->feature_id);
|
||||||
|
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_unregister_feature_handler(u32 feature_id)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
if (!feature_handlers[feature_id]) {
|
||||||
|
pr_warn("feature: no handler registered for %u\n", feature_id);
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_handlers[feature_id] = NULL;
|
||||||
|
|
||||||
|
pr_info("feature: unregistered handler for id=%u\n", feature_id);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct ksu_feature_handler *handler;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value || !supported) {
|
||||||
|
pr_err("feature: invalid parameters\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
handler = feature_handlers[feature_id];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
*supported = false;
|
||||||
|
*value = 0;
|
||||||
|
pr_debug("feature: feature %u not supported\n", feature_id);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*supported = true;
|
||||||
|
|
||||||
|
if (!handler->get_handler) {
|
||||||
|
pr_warn("feature: no get_handler for feature %u\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = handler->get_handler(value);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_set_feature(u32 feature_id, u64 value)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct ksu_feature_handler *handler;
|
||||||
|
|
||||||
|
if (feature_id >= KSU_FEATURE_MAX) {
|
||||||
|
pr_err("feature: invalid feature_id %u\n", feature_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
handler = feature_handlers[feature_id];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
pr_err("feature: feature %u not registered\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->set_handler) {
|
||||||
|
pr_warn("feature: no set_handler for feature %u\n", feature_id);
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = handler->set_handler(value);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_feature_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_FEATURE_MAX; i++) {
|
||||||
|
feature_handlers[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("feature: feature management initialized\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_feature_exit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&feature_mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_FEATURE_MAX; i++) {
|
||||||
|
feature_handlers[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&feature_mutex);
|
||||||
|
|
||||||
|
pr_info("feature: feature management cleaned up\n");
|
||||||
|
}
|
||||||
37
kernel/feature.h
Normal file
37
kernel/feature.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef __KSU_H_FEATURE
|
||||||
|
#define __KSU_H_FEATURE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
enum ksu_feature_id {
|
||||||
|
KSU_FEATURE_SU_COMPAT = 0,
|
||||||
|
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||||
|
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||||
|
KSU_FEATURE_SULOG = 3,
|
||||||
|
|
||||||
|
KSU_FEATURE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*ksu_feature_get_t)(u64 *value);
|
||||||
|
typedef int (*ksu_feature_set_t)(u64 value);
|
||||||
|
|
||||||
|
struct ksu_feature_handler {
|
||||||
|
u32 feature_id;
|
||||||
|
const char *name;
|
||||||
|
ksu_feature_get_t get_handler;
|
||||||
|
ksu_feature_set_t set_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_register_feature_handler(const struct ksu_feature_handler *handler);
|
||||||
|
|
||||||
|
int ksu_unregister_feature_handler(u32 feature_id);
|
||||||
|
|
||||||
|
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported);
|
||||||
|
|
||||||
|
int ksu_set_feature(u32 feature_id, u64 value);
|
||||||
|
|
||||||
|
void ksu_feature_init(void);
|
||||||
|
|
||||||
|
void ksu_feature_exit(void);
|
||||||
|
|
||||||
|
#endif // __KSU_H_FEATURE
|
||||||
341
kernel/file_wrapper.c
Normal file
341
kernel/file_wrapper.c
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/anon_inodes.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
|
||||||
|
#include "file_wrapper.h"
|
||||||
|
|
||||||
|
static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->llseek(data->orig, off, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->read(orig, ptr, sz, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->write(orig, ptr, sz, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
iocb->ki_filp = orig;
|
||||||
|
return orig->f_op->read_iter(iocb, iovi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
iocb->ki_filp = orig;
|
||||||
|
return orig->f_op->write_iter(iocb, iovi);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
|
||||||
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
kiocb->ki_filp = orig;
|
||||||
|
return orig->f_op->iopoll(kiocb, icb, v);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
|
||||||
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
kiocb->ki_filp = orig;
|
||||||
|
return orig->f_op->iopoll(kiocb, spin);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->iterate(orig, dc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->iterate_shared(orig, dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __poll_t ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->poll(orig, pts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->compat_ioctl(orig, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->mmap(orig, vma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static unsigned long mmap_supported_flags {}
|
||||||
|
|
||||||
|
static int ksu_wrapper_open(struct inode *ino, struct file *fp) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
struct inode *orig_ino = file_inode(orig);
|
||||||
|
return orig->f_op->open(orig_ino, orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->flush(orig, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->fsync(orig, off1, off2, datasync);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->fasync(arg, orig, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->lock(orig, arg1, fl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->sendpage) {
|
||||||
|
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->get_unmapped_area) {
|
||||||
|
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static int ksu_wrapper_check_flags(int arg) {}
|
||||||
|
|
||||||
|
static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->flock) {
|
||||||
|
return orig->f_op->flock(orig, arg1, fl);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_write) {
|
||||||
|
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_read) {
|
||||||
|
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
void ksu_wrapper_splice_eof(struct file *fp) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->splice_eof) {
|
||||||
|
return orig->f_op->splice_eof(orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->setlease) {
|
||||||
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->fallocate) {
|
||||||
|
return orig->f_op->fallocate(orig, mode, offset, len);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
|
||||||
|
struct ksu_file_wrapper* data = f->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->show_fdinfo) {
|
||||||
|
orig->f_op->show_fdinfo(m, orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1, struct file *f2,
|
||||||
|
loff_t off2, size_t sz, unsigned int flags) {
|
||||||
|
// TODO: determine which file to use
|
||||||
|
struct ksu_file_wrapper* data = f1->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->copy_file_range) {
|
||||||
|
return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
|
||||||
|
struct file *file_out, loff_t pos_out,
|
||||||
|
loff_t len, unsigned int remap_flags) {
|
||||||
|
// TODO: determine which file to use
|
||||||
|
struct ksu_file_wrapper* data = file_in->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->remap_file_range) {
|
||||||
|
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
if (orig->f_op->fadvise) {
|
||||||
|
return orig->f_op->fadvise(orig, off1, off2, flags);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
|
||||||
|
ksu_delete_file_wrapper(filp->private_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {
|
||||||
|
struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_file(fp);
|
||||||
|
|
||||||
|
p->orig = fp;
|
||||||
|
p->ops.owner = THIS_MODULE;
|
||||||
|
p->ops.llseek = fp->f_op->llseek ? ksu_wrapper_llseek : NULL;
|
||||||
|
p->ops.read = fp->f_op->read ? ksu_wrapper_read : NULL;
|
||||||
|
p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
|
||||||
|
p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
|
||||||
|
p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
|
||||||
|
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
|
||||||
|
#endif
|
||||||
|
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
|
||||||
|
p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
|
||||||
|
p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
|
||||||
|
p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
|
||||||
|
p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
|
p->ops.fop_flags = fp->f_op->fop_flags;
|
||||||
|
#else
|
||||||
|
p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags;
|
||||||
|
#endif
|
||||||
|
p->ops.open = fp->f_op->open ? ksu_wrapper_open : NULL;
|
||||||
|
p->ops.flush = fp->f_op->flush ? ksu_wrapper_flush : NULL;
|
||||||
|
p->ops.release = ksu_wrapper_release;
|
||||||
|
p->ops.fsync = fp->f_op->fsync ? ksu_wrapper_fsync : NULL;
|
||||||
|
p->ops.fasync = fp->f_op->fasync ? ksu_wrapper_fasync : NULL;
|
||||||
|
p->ops.lock = fp->f_op->lock ? ksu_wrapper_lock : NULL;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
|
||||||
|
#endif
|
||||||
|
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL;
|
||||||
|
p->ops.check_flags = fp->f_op->check_flags;
|
||||||
|
p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
|
||||||
|
p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
|
||||||
|
p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
|
||||||
|
p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
|
||||||
|
p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
|
||||||
|
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
|
||||||
|
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
|
||||||
|
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
|
||||||
|
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
|
||||||
|
fput((struct file*) data->orig);
|
||||||
|
kfree(data);
|
||||||
|
}
|
||||||
14
kernel/file_wrapper.h
Normal file
14
kernel/file_wrapper.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef KSU_FILE_WRAPPER_H
|
||||||
|
#define KSU_FILE_WRAPPER_H
|
||||||
|
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
|
||||||
|
struct ksu_file_wrapper {
|
||||||
|
struct file* orig;
|
||||||
|
struct file_operations ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp);
|
||||||
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data);
|
||||||
|
#endif // KSU_FILE_WRAPPER_H
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef __KSU_H_KSHOOK
|
|
||||||
#define __KSU_H_KSHOOK
|
|
||||||
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
// For sucompat
|
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
|
||||||
int *flags);
|
|
||||||
|
|
||||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
|
||||||
|
|
||||||
// For ksud
|
|
||||||
|
|
||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|
||||||
size_t *count_ptr, loff_t **pos);
|
|
||||||
|
|
||||||
// For ksud and sucompat
|
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|
||||||
void *envp, int *flags);
|
|
||||||
|
|
||||||
// For volume button
|
|
||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
|
||||||
int *value);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#include <linux/version.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/nsproxy.h>
|
|
||||||
#include <linux/sched/task.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
|
|
||||||
extern struct task_struct init_task;
|
|
||||||
|
|
||||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
|
||||||
struct ksu_ns_fs_saved {
|
|
||||||
struct nsproxy *ns;
|
|
||||||
struct fs_struct *fs;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
|
||||||
{
|
|
||||||
ns_fs_saved->ns = current->nsproxy;
|
|
||||||
ns_fs_saved->fs = current->fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
|
||||||
{
|
|
||||||
current->nsproxy = ns_fs_saved->ns;
|
|
||||||
current->fs = ns_fs_saved->fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool android_context_saved_checked = false;
|
|
||||||
static bool android_context_saved_enabled = false;
|
|
||||||
static struct ksu_ns_fs_saved android_context_saved;
|
|
||||||
|
|
||||||
void ksu_android_ns_fs_check()
|
|
||||||
{
|
|
||||||
if (android_context_saved_checked)
|
|
||||||
return;
|
|
||||||
android_context_saved_checked = true;
|
|
||||||
task_lock(current);
|
|
||||||
if (current->nsproxy && current->fs &&
|
|
||||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
|
||||||
android_context_saved_enabled = true;
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
|
||||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
|
||||||
#endif
|
|
||||||
ksu_save_ns_fs(&android_context_saved);
|
|
||||||
} else {
|
|
||||||
pr_info("android context saved disabled\n");
|
|
||||||
}
|
|
||||||
task_unlock(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
|
||||||
{
|
|
||||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
|
||||||
struct ksu_ns_fs_saved saved;
|
|
||||||
if (android_context_saved_enabled) {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("start switch current nsproxy and fs to android context\n");
|
|
||||||
#endif
|
|
||||||
task_lock(current);
|
|
||||||
ksu_save_ns_fs(&saved);
|
|
||||||
ksu_load_ns_fs(&android_context_saved);
|
|
||||||
task_unlock(current);
|
|
||||||
}
|
|
||||||
struct file *fp = filp_open(filename, flags, mode);
|
|
||||||
if (android_context_saved_enabled) {
|
|
||||||
task_lock(current);
|
|
||||||
ksu_load_ns_fs(&saved);
|
|
||||||
task_unlock(current);
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
|
||||||
loff_t *pos)
|
|
||||||
{
|
|
||||||
return kernel_read(p, buf, count, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
|
||||||
loff_t *pos)
|
|
||||||
{
|
|
||||||
return kernel_write(p, buf, count, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
|
||||||
long count)
|
|
||||||
{
|
|
||||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
|
||||||
}
|
|
||||||
@@ -3,63 +3,7 @@
|
|||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include "ss/policydb.h"
|
|
||||||
#include "linux/key.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* list_count_nodes - count the number of nodes in a list
|
|
||||||
* @head: the head of the list
|
|
||||||
*
|
|
||||||
* This function iterates over the list starting from @head and counts
|
|
||||||
* the number of nodes in the list. It does not modify the list.
|
|
||||||
*
|
|
||||||
* Context: Any context. The function is safe to call in any context,
|
|
||||||
* including interrupt context, as it does not sleep or allocate
|
|
||||||
* memory.
|
|
||||||
*
|
|
||||||
* Return: the number of nodes in the list (excluding the head)
|
|
||||||
*/
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
|
||||||
static inline __maybe_unused size_t list_count_nodes(const struct list_head *head)
|
|
||||||
{
|
|
||||||
const struct list_head *pos;
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
if (!head)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
list_for_each(pos, head)
|
|
||||||
count++;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
|
||||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
|
||||||
* From ss/ebitmap.h
|
|
||||||
*/
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
|
||||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
|
||||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
|
||||||
#define CONFIG_IS_HW_HISI
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
|
||||||
const void __user *unsafe_addr,
|
|
||||||
long count);
|
|
||||||
|
|
||||||
extern void ksu_android_ns_fs_check();
|
|
||||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
|
||||||
umode_t mode);
|
|
||||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
|
||||||
loff_t *pos);
|
|
||||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
|
||||||
size_t count, loff_t *pos);
|
|
||||||
/*
|
/*
|
||||||
* ksu_copy_from_user_retry
|
* ksu_copy_from_user_retry
|
||||||
* try nofault copy first, if it fails, try with plain
|
* try nofault copy first, if it fails, try with plain
|
||||||
@@ -67,14 +11,14 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
|||||||
* 0 = success
|
* 0 = success
|
||||||
*/
|
*/
|
||||||
static long ksu_copy_from_user_retry(void *to,
|
static long ksu_copy_from_user_retry(void *to,
|
||||||
const void __user *from, unsigned long count)
|
const void __user *from, unsigned long count)
|
||||||
{
|
{
|
||||||
long ret = copy_from_user_nofault(to, from, count);
|
long ret = copy_from_user_nofault(to, from, count);
|
||||||
if (likely(!ret))
|
if (likely(!ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// we faulted! fallback to slow path
|
// we faulted! fallback to slow path
|
||||||
return copy_from_user(to, from, count);
|
return copy_from_user(to, from, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
181
kernel/kernel_umount.c
Normal file
181
kernel/kernel_umount.c
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
|
||||||
|
#include "umount_manager.h"
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
|
static bool ksu_kernel_umount_enabled = true;
|
||||||
|
|
||||||
|
static int kernel_umount_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_kernel_umount_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kernel_umount_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_kernel_umount_enabled = enable;
|
||||||
|
pr_info("kernel_umount: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler kernel_umount_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
|
||||||
|
.name = "kernel_umount",
|
||||||
|
.get_handler = kernel_umount_feature_get,
|
||||||
|
.set_handler = kernel_umount_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int path_umount(struct path *path, int flags);
|
||||||
|
|
||||||
|
static void ksu_umount_mnt(struct path *path, int flags)
|
||||||
|
{
|
||||||
|
int err = path_umount(path, flags);
|
||||||
|
if (err) {
|
||||||
|
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void try_umount(const char *mnt, int flags)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path(mnt, 0, &path);
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.dentry != path.mnt->mnt_root) {
|
||||||
|
// it is not root mountpoint, maybe umounted by others already.
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_umount_mnt(&path, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct umount_tw {
|
||||||
|
struct callback_head cb;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void umount_tw_func(struct callback_head *cb)
|
||||||
|
{
|
||||||
|
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
||||||
|
const struct cred *saved = NULL;
|
||||||
|
if (tw->old_cred) {
|
||||||
|
saved = override_creds(tw->old_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mount_entry *entry;
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
list_for_each_entry(entry, &mount_list, list) {
|
||||||
|
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
|
||||||
|
try_umount(entry->umountable, entry->flags);
|
||||||
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
ksu_umount_manager_execute_all(tw->old_cred);
|
||||||
|
|
||||||
|
if (saved)
|
||||||
|
revert_creds(saved);
|
||||||
|
|
||||||
|
if (tw->old_cred)
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
||||||
|
{
|
||||||
|
struct umount_tw *tw;
|
||||||
|
|
||||||
|
// if there isn't any module mounted, just ignore it!
|
||||||
|
if (!ksu_module_mounted) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_kernel_umount_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are 5 scenarios:
|
||||||
|
// 1. Normal app: zygote -> appuid
|
||||||
|
// 2. Isolated process forked from zygote: zygote -> isolated_process
|
||||||
|
// 3. App zygote forked from zygote: zygote -> appuid
|
||||||
|
// 4. Isolated process froked from app zygote: appuid -> isolated_process (already handled by 3)
|
||||||
|
// 5. Isolated process froked from webview zygote (no need to handle, app cannot run custom code)
|
||||||
|
if (!is_appuid(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_uid_should_umount(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check old process's selinux context, if it is not zygote, ignore it!
|
||||||
|
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||||
|
// when we umount for such process, that is a disaster!
|
||||||
|
// also handle case 4 and 5
|
||||||
|
bool is_zygote_child = is_zygote(get_current_cred());
|
||||||
|
if (!is_zygote_child) {
|
||||||
|
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
|
||||||
|
#endif
|
||||||
|
// umount the target mnt
|
||||||
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
||||||
|
|
||||||
|
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
|
if (!tw)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tw->old_cred = get_current_cred();
|
||||||
|
tw->cb.func = umount_tw_func;
|
||||||
|
|
||||||
|
int err = task_work_add(current, &tw->cb, TWA_RESUME);
|
||||||
|
if (err) {
|
||||||
|
if (tw->old_cred) {
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
}
|
||||||
|
kfree(tw);
|
||||||
|
pr_warn("unmount add task_work failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
rc = ksu_umount_manager_init();
|
||||||
|
if (rc) {
|
||||||
|
pr_err("Failed to initialize umount manager: %d\n", rc);
|
||||||
|
}
|
||||||
|
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||||
|
pr_err("Failed to register kernel_umount feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_exit(void)
|
||||||
|
{
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
||||||
|
}
|
||||||
25
kernel/kernel_umount.h
Normal file
25
kernel/kernel_umount.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef __KSU_H_KERNEL_UMOUNT
|
||||||
|
#define __KSU_H_KERNEL_UMOUNT
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/rwsem.h>
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void);
|
||||||
|
void ksu_kernel_umount_exit(void);
|
||||||
|
|
||||||
|
void try_umount(const char *mnt, int flags);
|
||||||
|
|
||||||
|
// Handler function to be called from setresuid hook
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
||||||
|
|
||||||
|
// for the umount list
|
||||||
|
struct mount_entry {
|
||||||
|
char *umountable;
|
||||||
|
unsigned int flags;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
extern struct list_head mount_list;
|
||||||
|
extern struct rw_semaphore mount_list_lock;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
static int sukisu_is_su_allow_uid(uid_t uid)
|
static int sukisu_is_su_allow_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
return ksu_is_allow_uid(uid) ? 1 : 0;
|
return ksu_is_allow_uid_for_current(uid) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sukisu_get_ap_mod_exclude(uid_t uid)
|
static int sukisu_get_ap_mod_exclude(uid_t uid)
|
||||||
|
|||||||
273
kernel/kpm/kpm.c
273
kernel/kpm/kpm.c
@@ -9,13 +9,10 @@
|
|||||||
* 并参照KernelPatch的标准KPM格式实现加载和控制
|
* 并参照KernelPatch的标准KPM格式实现加载和控制
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/export.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kernfs.h>
|
#include <linux/kernfs.h>
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
@@ -25,26 +22,25 @@
|
|||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h>
|
#include <asm/elf.h>
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/set_memory.h>
|
#include <linux/set_memory.h>
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <linux/kprobes.h>
|
#include <linux/kprobes.h>
|
||||||
#include <linux/stacktrace.h>
|
#include <linux/stacktrace.h>
|
||||||
#include <linux/kallsyms.h>
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
|
||||||
#include <linux/moduleloader.h>
|
#include <linux/moduleloader.h>
|
||||||
#endif
|
#endif
|
||||||
#include "kpm.h"
|
#include "kpm.h"
|
||||||
#include "compact.h"
|
#include "compact.h"
|
||||||
|
|
||||||
|
#define KPM_NAME_LEN 32
|
||||||
|
#define KPM_ARGS_LEN 1024
|
||||||
|
|
||||||
#ifndef NO_OPTIMIZE
|
#ifndef NO_OPTIMIZE
|
||||||
#if defined(__GNUC__) && !defined(__clang__)
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
#define NO_OPTIMIZE __attribute__((optimize("O0")))
|
#define NO_OPTIMIZE __attribute__((optimize("O0")))
|
||||||
@@ -56,156 +52,231 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
|
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
|
||||||
const char *args, void *ptr, void __user *result)
|
const char *args, void *ptr, int *result)
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
|
||||||
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). "
|
|
||||||
"path=%s args=%s ptr=%p\n", path, args, ptr);
|
"path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||||
|
|
||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
|
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
|
||||||
void *ptr, void __user *result)
|
void *ptr, int *result)
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
|
||||||
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). "
|
|
||||||
"name=%s ptr=%p\n", name, ptr);
|
"name=%s ptr=%p\n", name, ptr);
|
||||||
|
|
||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_num(void __user *result)
|
noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
|
||||||
{
|
{
|
||||||
int res = 0;
|
pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
|
||||||
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
|
||||||
|
|
||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_num);
|
EXPORT_SYMBOL(sukisu_kpm_num);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, void __user *out,
|
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
|
||||||
void __user *result)
|
int *size)
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_info). "
|
||||||
|
"name=%s buffer=%p\n", name, buf);
|
||||||
printk("KPM: Stub function called (sukisu_kpm_info). "
|
|
||||||
"name=%s buffer=%p\n", name, out);
|
|
||||||
|
|
||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_info);
|
EXPORT_SYMBOL(sukisu_kpm_info);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_list(void __user *out, unsigned int bufferSize,
|
noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
|
||||||
void __user *result)
|
int *result)
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_list). "
|
||||||
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_list). "
|
|
||||||
"buffer=%p size=%d\n", out, bufferSize);
|
"buffer=%p size=%d\n", out, bufferSize);
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_list);
|
EXPORT_SYMBOL(sukisu_kpm_list);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_control(void __user *name, void __user *args,
|
noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
|
||||||
void __user *result)
|
int *result)
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_control). "
|
||||||
|
"name=%p args=%p arg_len=%ld\n", name, args, arg_len);
|
||||||
printk("KPM: Stub function called (sukisu_kpm_control). "
|
|
||||||
"name=%p args=%p\n", name, args);
|
|
||||||
|
|
||||||
__asm__ volatile("nop");
|
__asm__ volatile("nop");
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_control);
|
EXPORT_SYMBOL(sukisu_kpm_control);
|
||||||
|
|
||||||
noinline NO_OPTIMIZE void sukisu_kpm_version(void __user *out, unsigned int bufferSize,
|
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
|
||||||
void __user *result)
|
|
||||||
{
|
{
|
||||||
int res = -1;
|
pr_info("kpm: Stub function called (sukisu_kpm_version). "
|
||||||
|
"buffer=%p\n", buf);
|
||||||
printk("KPM: Stub function called (sukisu_kpm_version). "
|
|
||||||
"buffer=%p size=%d\n", out, bufferSize);
|
|
||||||
|
|
||||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
|
||||||
printk("KPM: Copy to user failed.");
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_version);
|
EXPORT_SYMBOL(sukisu_kpm_version);
|
||||||
|
|
||||||
noinline int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
|
||||||
unsigned long arg5)
|
unsigned long result_code)
|
||||||
{
|
{
|
||||||
if (arg2 == SUKISU_KPM_LOAD) {
|
int res = -1;
|
||||||
char kernel_load_path[256] = { 0 };
|
if (control_code == SUKISU_KPM_LOAD) {
|
||||||
char kernel_args_buffer[256] = { 0 };
|
char kernel_load_path[256];
|
||||||
|
char kernel_args_buffer[256];
|
||||||
|
|
||||||
if (arg3 == 0)
|
if (arg1 == 0) {
|
||||||
return -1;
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg1, 255)) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy_from_user((char *)&kernel_load_path, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, 255);
|
||||||
|
|
||||||
if (arg4 != 0)
|
if (arg2 != 0) {
|
||||||
strncpy_from_user((char *)&kernel_args_buffer, (const char __user *)arg4, 255);
|
if (!access_ok(arg2, 255)) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, 255);
|
||||||
|
}
|
||||||
|
|
||||||
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
|
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
|
||||||
(const char *)&kernel_args_buffer, NULL, (void __user *)arg5);
|
(const char *)&kernel_args_buffer, NULL, &res);
|
||||||
} else if (arg2 == SUKISU_KPM_UNLOAD) {
|
} else if (control_code == SUKISU_KPM_UNLOAD) {
|
||||||
char kernel_name_buffer[256] = { 0 };
|
char kernel_name_buffer[256];
|
||||||
|
|
||||||
if (arg3 == 0)
|
if (arg1 == 0) {
|
||||||
return -1;
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
|
}
|
||||||
|
|
||||||
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL,
|
|
||||||
(void __user *)arg5);
|
|
||||||
} else if (arg2 == SUKISU_KPM_NUM) {
|
|
||||||
sukisu_kpm_num((void __user *)arg5);
|
|
||||||
} else if (arg2 == SUKISU_KPM_INFO) {
|
|
||||||
char kernel_name_buffer[256] = { 0 };
|
|
||||||
|
|
||||||
if (arg3 == 0 || arg4 == 0)
|
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
|
||||||
return -1;
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
|
||||||
|
|
||||||
sukisu_kpm_info((const char *)&kernel_name_buffer, (char __user *)arg4,
|
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
|
||||||
(void __user *)arg5);
|
} else if (control_code == SUKISU_KPM_NUM) {
|
||||||
} else if (arg2 == SUKISU_KPM_LIST) {
|
sukisu_kpm_num(&res);
|
||||||
sukisu_kpm_list((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
|
} else if (control_code == SUKISU_KPM_INFO) {
|
||||||
} else if (arg2 == SUKISU_KPM_CONTROL) {
|
char kernel_name_buffer[256];
|
||||||
sukisu_kpm_control((char __user *)arg3, (char __user *)arg4, (void __user *)arg5);
|
char buf[256];
|
||||||
} else if (arg2 == SUKISU_KPM_VERSION) {
|
int size;
|
||||||
sukisu_kpm_version((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
|
|
||||||
|
if (arg1 == 0 || arg2 == 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
|
||||||
|
|
||||||
|
sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
|
||||||
|
|
||||||
|
if (!access_ok(arg2, size)) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = copy_to_user(arg2, &buf, size);
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_LIST) {
|
||||||
|
char buf[1024];
|
||||||
|
int len = (int) arg2;
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg2, len)) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
sukisu_kpm_list((char *)&buf, sizeof(buf), &res);
|
||||||
|
|
||||||
|
if (res > len) {
|
||||||
|
res = -ENOBUFS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg1, &buf, len) != 0)
|
||||||
|
pr_info("kpm: Copy to user failed.");
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_CONTROL) {
|
||||||
|
char kpm_name[KPM_NAME_LEN] = { 0 };
|
||||||
|
char kpm_args[KPM_ARGS_LEN] = { 0 };
|
||||||
|
|
||||||
|
if (!access_ok(arg1, sizeof(kpm_name))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(arg2, sizeof(kpm_args))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name));
|
||||||
|
if (name_len <= 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args));
|
||||||
|
|
||||||
|
sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res);
|
||||||
|
|
||||||
|
} else if (control_code == SUKISU_KPM_VERSION) {
|
||||||
|
char buffer[256] = {0};
|
||||||
|
|
||||||
|
sukisu_kpm_version((char*) &buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
unsigned int outlen = (unsigned int) arg2;
|
||||||
|
int len = strlen(buffer);
|
||||||
|
if (len >= outlen) len = outlen - 1;
|
||||||
|
|
||||||
|
res = copy_to_user(arg1, &buffer, len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (copy_to_user(result_code, &res, sizeof(res)) != 0)
|
||||||
|
pr_info("kpm: Copy to user failed.");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
invalid_arg:
|
||||||
|
pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
|
||||||
|
res = -EFAULT;
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_handle_kpm);
|
EXPORT_SYMBOL(sukisu_handle_kpm);
|
||||||
|
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2) {
|
int sukisu_is_kpm_control_code(unsigned long control_code) {
|
||||||
return (arg2 >= CMD_KPM_CONTROL &&
|
return (control_code >= CMD_KPM_CONTROL &&
|
||||||
arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_kpm(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_kpm_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("kpm: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(cmd.control_code, sizeof(int))) {
|
||||||
|
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access_ok(cmd.result_code, sizeof(int))) {
|
||||||
|
pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,70 @@
|
|||||||
#ifndef __SUKISU_KPM_H
|
#ifndef __SUKISU_KPM_H
|
||||||
#define __SUKISU_KPM_H
|
#define __SUKISU_KPM_H
|
||||||
|
|
||||||
extern int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
#include <linux/types.h>
|
||||||
unsigned long arg5);
|
#include <linux/ioctl.h>
|
||||||
extern int sukisu_is_kpm_control_code(unsigned long arg2);
|
|
||||||
|
struct ksu_kpm_cmd {
|
||||||
|
__aligned_u64 __user control_code;
|
||||||
|
__aligned_u64 __user arg1;
|
||||||
|
__aligned_u64 __user arg2;
|
||||||
|
__aligned_u64 __user result_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg3, unsigned long arg4, unsigned long result_code);
|
||||||
|
int sukisu_is_kpm_control_code(unsigned long control_code);
|
||||||
|
int do_kpm(void __user *arg);
|
||||||
|
|
||||||
|
#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
|
||||||
|
|
||||||
/* KPM Control Code */
|
/* KPM Control Code */
|
||||||
#define CMD_KPM_CONTROL 28
|
#define CMD_KPM_CONTROL 1
|
||||||
#define CMD_KPM_CONTROL_MAX 35
|
#define CMD_KPM_CONTROL_MAX 10
|
||||||
|
|
||||||
/* Control Code */
|
/* Control Code */
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 28, "PATH", "ARGS")
|
* prctl(xxx, 1, "PATH", "ARGS")
|
||||||
* success return 0, error return -N
|
* success return 0, error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_LOAD 28
|
#define SUKISU_KPM_LOAD 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 29, "NAME")
|
* prctl(xxx, 2, "NAME")
|
||||||
* success return 0, error return -N
|
* success return 0, error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_UNLOAD 29
|
#define SUKISU_KPM_UNLOAD 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* num = prctl(xxx, 30)
|
* num = prctl(xxx, 3)
|
||||||
* error return -N
|
* error return -N
|
||||||
* success return +num or 0
|
* success return +num or 0
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_NUM 30
|
#define SUKISU_KPM_NUM 3
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 31, Buffer, BufferSize)
|
* prctl(xxx, 4, Buffer, BufferSize)
|
||||||
* success return +out, error return -N
|
* success return +out, error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_LIST 31
|
#define SUKISU_KPM_LIST 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 32, "NAME", Buffer[256])
|
* prctl(xxx, 5, "NAME", Buffer[256])
|
||||||
* success return +out, error return -N
|
* success return +out, error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_INFO 32
|
#define SUKISU_KPM_INFO 5
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 33, "NAME", "ARGS")
|
* prctl(xxx, 6, "NAME", "ARGS")
|
||||||
* success return KPM's result value
|
* success return KPM's result value
|
||||||
* error return -N
|
* error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_CONTROL 33
|
#define SUKISU_KPM_CONTROL 6
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prctl(xxx, 34, buffer, bufferSize)
|
* prctl(xxx, 7, buffer, bufferSize)
|
||||||
* success return KPM's result value
|
* success return KPM's result value
|
||||||
* error return -N
|
* error return -N
|
||||||
*/
|
*/
|
||||||
#define SUKISU_KPM_VERSION 34
|
#define SUKISU_KPM_VERSION 7
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
107
kernel/ksu.c
107
kernel/ksu.c
@@ -3,89 +3,103 @@
|
|||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "feature.h"
|
||||||
#include "core_hook.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "supercalls.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
|
||||||
static struct workqueue_struct *ksu_workqueue;
|
static struct workqueue_struct *ksu_workqueue;
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work)
|
bool ksu_queue_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
return queue_work(ksu_workqueue, work);
|
return queue_work(ksu_workqueue, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
void sukisu_custom_config_init(void)
|
||||||
void *argv, void *envp, int *flags);
|
{
|
||||||
|
}
|
||||||
|
|
||||||
extern void ksu_sucompat_init();
|
void sukisu_custom_config_exit(void)
|
||||||
extern void ksu_sucompat_exit();
|
{
|
||||||
extern void ksu_ksud_init();
|
ksu_uid_exit();
|
||||||
extern void ksu_ksud_exit();
|
ksu_throne_comm_exit();
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
ksu_dynamic_manager_exit();
|
||||||
extern void ksu_trace_register();
|
#if __SULOG_GATE
|
||||||
extern void ksu_trace_unregister();
|
ksu_sulog_exit();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** You are running KernelSU in DEBUG mode **");
|
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_init();
|
ksu_feature_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_supercalls_init();
|
||||||
|
|
||||||
ksu_allowlist_init();
|
sukisu_custom_config_init();
|
||||||
|
|
||||||
ksu_throne_tracker_init();
|
ksu_syscall_hook_manager_init();
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
|
||||||
ksu_sucompat_init();
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
ksu_ksud_init();
|
|
||||||
|
ksu_allowlist_init();
|
||||||
|
|
||||||
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
|
#ifdef KSU_KPROBES_HOOK
|
||||||
|
ksu_ksud_init();
|
||||||
#else
|
#else
|
||||||
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
|
||||||
ksu_trace_register();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
#ifndef CONFIG_KSU_DEBUG
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
kobject_del(&THIS_MODULE->mkobj.kobj);
|
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void ksu_observer_exit(void);
|
||||||
void kernelsu_exit(void)
|
void kernelsu_exit(void)
|
||||||
{
|
{
|
||||||
ksu_allowlist_exit();
|
ksu_allowlist_exit();
|
||||||
|
|
||||||
ksu_throne_tracker_exit();
|
ksu_observer_exit();
|
||||||
|
|
||||||
destroy_workqueue(ksu_workqueue);
|
ksu_throne_tracker_exit();
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
destroy_workqueue(ksu_workqueue);
|
||||||
ksu_ksud_exit();
|
|
||||||
ksu_sucompat_exit();
|
#ifdef KSU_KPROBES_HOOK
|
||||||
|
ksu_ksud_exit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
ksu_syscall_hook_manager_exit();
|
||||||
ksu_trace_unregister();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ksu_core_exit();
|
sukisu_custom_config_exit();
|
||||||
|
|
||||||
|
ksu_supercalls_exit();
|
||||||
|
|
||||||
|
ksu_feature_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(kernelsu_init);
|
module_init(kernelsu_init);
|
||||||
@@ -94,4 +108,9 @@ module_exit(kernelsu_exit);
|
|||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("weishu");
|
MODULE_AUTHOR("weishu");
|
||||||
MODULE_DESCRIPTION("Android KernelSU");
|
MODULE_DESCRIPTION("Android KernelSU");
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
|
||||||
|
MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
|
||||||
|
#else
|
||||||
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||||
|
#endif
|
||||||
|
|||||||
96
kernel/ksu.h
96
kernel/ksu.h
@@ -7,40 +7,12 @@
|
|||||||
#define KERNEL_SU_VERSION KSU_VERSION
|
#define KERNEL_SU_VERSION KSU_VERSION
|
||||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||||
|
|
||||||
#define CMD_GRANT_ROOT 0
|
extern bool ksu_uid_scanner_enabled;
|
||||||
#define CMD_BECOME_MANAGER 1
|
|
||||||
#define CMD_GET_VERSION 2
|
|
||||||
#define CMD_ALLOW_SU 3
|
|
||||||
#define CMD_DENY_SU 4
|
|
||||||
#define CMD_GET_ALLOW_LIST 5
|
|
||||||
#define CMD_GET_DENY_LIST 6
|
|
||||||
#define CMD_REPORT_EVENT 7
|
|
||||||
#define CMD_SET_SEPOLICY 8
|
|
||||||
#define CMD_CHECK_SAFEMODE 9
|
|
||||||
#define CMD_GET_APP_PROFILE 10
|
|
||||||
#define CMD_SET_APP_PROFILE 11
|
|
||||||
#define CMD_UID_GRANTED_ROOT 12
|
|
||||||
#define CMD_UID_SHOULD_UMOUNT 13
|
|
||||||
#define CMD_IS_SU_ENABLED 14
|
|
||||||
#define CMD_ENABLE_SU 15
|
|
||||||
|
|
||||||
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
|
|
||||||
|
|
||||||
#define CMD_ENABLE_KPM 100
|
|
||||||
#define CMD_HOOK_TYPE 101
|
|
||||||
#define CMD_DYNAMIC_MANAGER 103
|
|
||||||
#define CMD_GET_MANAGERS 104
|
|
||||||
|
|
||||||
#define EVENT_POST_FS_DATA 1
|
#define EVENT_POST_FS_DATA 1
|
||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
#define EVENT_MODULE_MOUNTED 3
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
|
|
||||||
#define KSU_APP_PROFILE_VER 2
|
|
||||||
#define KSU_MAX_PACKAGE_NAME 256
|
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
|
||||||
#define KSU_MAX_GROUPS 32
|
|
||||||
#define KSU_SELINUX_DOMAIN 64
|
|
||||||
|
|
||||||
// SukiSU Ultra kernel su version full strings
|
// SukiSU Ultra kernel su version full strings
|
||||||
#ifndef KSU_VERSION_FULL
|
#ifndef KSU_VERSION_FULL
|
||||||
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
||||||
@@ -51,6 +23,10 @@
|
|||||||
#define DYNAMIC_MANAGER_OP_GET 1
|
#define DYNAMIC_MANAGER_OP_GET 1
|
||||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||||
|
|
||||||
|
#define UID_SCANNER_OP_GET_STATUS 0
|
||||||
|
#define UID_SCANNER_OP_TOGGLE 1
|
||||||
|
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||||
|
|
||||||
struct dynamic_manager_user_config {
|
struct dynamic_manager_user_config {
|
||||||
unsigned int operation;
|
unsigned int operation;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
@@ -65,68 +41,22 @@ struct manager_list_info {
|
|||||||
} managers[2];
|
} managers[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct root_profile {
|
|
||||||
int32_t uid;
|
|
||||||
int32_t gid;
|
|
||||||
|
|
||||||
int32_t groups_count;
|
|
||||||
int32_t groups[KSU_MAX_GROUPS];
|
|
||||||
|
|
||||||
// kernel_cap_t is u32[2] for capabilities v3
|
|
||||||
struct {
|
|
||||||
u64 effective;
|
|
||||||
u64 permitted;
|
|
||||||
u64 inheritable;
|
|
||||||
} capabilities;
|
|
||||||
|
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
|
||||||
|
|
||||||
int32_t namespaces;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct non_root_profile {
|
|
||||||
bool umount_modules;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct app_profile {
|
|
||||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
|
||||||
u32 version;
|
|
||||||
|
|
||||||
// this is usually the package of the app, but can be other value for special apps
|
|
||||||
char key[KSU_MAX_PACKAGE_NAME];
|
|
||||||
int32_t current_uid;
|
|
||||||
bool allow_su;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
|
||||||
|
|
||||||
struct root_profile profile;
|
|
||||||
} rp_config;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
|
|
||||||
struct non_root_profile profile;
|
|
||||||
} nrp_config;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work);
|
bool ksu_queue_work(struct work_struct *work);
|
||||||
|
|
||||||
|
#if 0
|
||||||
static inline int startswith(char *s, char *prefix)
|
static inline int startswith(char *s, char *prefix)
|
||||||
{
|
{
|
||||||
return strncmp(s, prefix, strlen(prefix));
|
return strncmp(s, prefix, strlen(prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int endswith(const char *s, const char *t)
|
static inline int endswith(const char *s, const char *t)
|
||||||
{
|
{
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t tlen = strlen(t);
|
size_t tlen = strlen(t);
|
||||||
if (tlen > slen)
|
if (tlen > slen)
|
||||||
return 1;
|
return 1;
|
||||||
return strcmp(s + slen - tlen, t);
|
return strcmp(s + slen - tlen, t);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
#include "ksu_trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
// extern kernelsu functions
|
|
||||||
extern bool ksu_vfs_read_hook __read_mostly;
|
|
||||||
extern bool ksu_input_hook __read_mostly;
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags);
|
|
||||||
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags);
|
|
||||||
extern int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, size_t *count_ptr);
|
|
||||||
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
|
||||||
extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
|
||||||
// end kernelsu functions
|
|
||||||
|
|
||||||
|
|
||||||
// tracepoint callback functions
|
|
||||||
void ksu_trace_execveat_sucompat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
|
||||||
void *argv, void *envp, int *flags)
|
|
||||||
{
|
|
||||||
ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
|
||||||
int *mode, int *flags)
|
|
||||||
{
|
|
||||||
ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
|
|
||||||
size_t *count_ptr)
|
|
||||||
{
|
|
||||||
if (unlikely(ksu_vfs_read_hook))
|
|
||||||
ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
|
||||||
int *flags)
|
|
||||||
{
|
|
||||||
ksu_handle_stat(dfd, filename_user, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
|
|
||||||
int *value)
|
|
||||||
{
|
|
||||||
if (unlikely(ksu_input_hook))
|
|
||||||
ksu_handle_input_handle_event(type, code, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// end tracepoint callback functions
|
|
||||||
|
|
||||||
|
|
||||||
// register tracepoint callback functions
|
|
||||||
void ksu_trace_register(void)
|
|
||||||
{
|
|
||||||
register_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
|
||||||
register_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
|
||||||
register_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
|
||||||
register_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
|
||||||
register_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister tracepoint callback functions
|
|
||||||
void ksu_trace_unregister(void)
|
|
||||||
{
|
|
||||||
unregister_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
|
||||||
unregister_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
|
||||||
unregister_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
|
||||||
unregister_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
|
||||||
unregister_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#undef TRACE_SYSTEM
|
|
||||||
#define TRACE_SYSTEM ksu_trace
|
|
||||||
|
|
||||||
#if !defined(_KSU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
|
||||||
#define _KSU_TRACE_H
|
|
||||||
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/tracepoint.h>
|
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_execveat_sucompat_hook,
|
|
||||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
|
||||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_faccessat_hook,
|
|
||||||
TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
|
|
||||||
TP_ARGS(dfd, filename_user, mode, flags));
|
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_sys_read_hook,
|
|
||||||
TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
|
|
||||||
TP_ARGS(fd, buf_ptr, count_ptr));
|
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_stat_hook,
|
|
||||||
TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
|
|
||||||
TP_ARGS(dfd, filename_user, flags));
|
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_input_hook,
|
|
||||||
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
|
|
||||||
TP_ARGS(type, code, value));
|
|
||||||
|
|
||||||
#endif /* _KSU_TRACE_H */
|
|
||||||
|
|
||||||
#undef TRACE_INCLUDE_PATH
|
|
||||||
#define TRACE_INCLUDE_PATH .
|
|
||||||
#undef TRACE_INCLUDE_FILE
|
|
||||||
#define TRACE_INCLUDE_FILE ksu_trace
|
|
||||||
|
|
||||||
#include <trace/define_trace.h>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#define CREATE_TRACE_POINTS
|
|
||||||
#include "ksu_trace.h"
|
|
||||||
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_sucompat_hook);
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_faccessat_hook);
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_sys_read_hook);
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_stat_hook);
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_input_hook);
|
|
||||||
936
kernel/ksud.c
936
kernel/ksud.c
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,19 @@
|
|||||||
|
|
||||||
#define KSUD_PATH "/data/adb/ksud"
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
|
void ksu_ksud_init();
|
||||||
|
void ksu_ksud_exit();
|
||||||
|
|
||||||
void on_post_fs_data(void);
|
void on_post_fs_data(void);
|
||||||
|
void on_module_mounted(void);
|
||||||
|
void on_boot_completed(void);
|
||||||
|
|
||||||
bool ksu_is_safe_mode(void);
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
extern u32 ksu_devpts_sid;
|
int nuke_ext4_sysfs(const char* mnt);
|
||||||
|
|
||||||
extern bool ksu_execveat_hook __read_mostly;
|
extern u32 ksu_file_sid;
|
||||||
extern int ksu_handle_pre_ksud(const char *filename);
|
extern bool ksu_module_mounted;
|
||||||
|
extern bool ksu_boot_completed;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,30 +13,31 @@ extern void ksu_add_manager(uid_t uid, int signature_index);
|
|||||||
extern void ksu_remove_manager(uid_t uid);
|
extern void ksu_remove_manager(uid_t uid);
|
||||||
extern int ksu_get_manager_signature_index(uid_t uid);
|
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||||
|
|
||||||
static inline bool ksu_is_manager_uid_valid()
|
static inline bool ksu_is_manager_uid_valid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid != KSU_INVALID_UID;
|
return ksu_manager_uid != KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_manager()
|
static inline bool is_manager(void)
|
||||||
{
|
{
|
||||||
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
||||||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uid_t ksu_get_manager_uid()
|
static inline uid_t ksu_get_manager_uid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid;
|
return ksu_manager_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_set_manager_uid(uid_t uid)
|
static inline void ksu_set_manager_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = uid;
|
ksu_manager_uid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_invalidate_manager_uid()
|
static inline void ksu_invalidate_manager_uid(void)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = KSU_INVALID_UID;
|
ksu_manager_uid = KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
int ksu_observer_init(void);
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -9,5 +9,9 @@
|
|||||||
#define EXPECTED_SIZE_OTHER 0x300
|
#define EXPECTED_SIZE_OTHER 0x300
|
||||||
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned size;
|
||||||
|
const char *sha256;
|
||||||
|
} apk_sign_key_t;
|
||||||
|
|
||||||
#endif /* MANAGER_SIGN_H */
|
#endif /* MANAGER_SIGN_H */
|
||||||
|
|||||||
357
kernel/manual_su.c
Normal file
357
kernel/manual_su.c
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
|
||||||
|
#include "manual_su.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
static bool current_verified = false;
|
||||||
|
static void ksu_cleanup_expired_tokens(void);
|
||||||
|
static bool is_current_verified(void);
|
||||||
|
static void add_pending_root(uid_t uid);
|
||||||
|
|
||||||
|
static struct pending_uid pending_uids[MAX_PENDING] = {0};
|
||||||
|
static int pending_cnt = 0;
|
||||||
|
static struct ksu_token_entry auth_tokens[MAX_TOKENS] = {0};
|
||||||
|
static int token_count = 0;
|
||||||
|
static DEFINE_SPINLOCK(token_lock);
|
||||||
|
|
||||||
|
static char* get_token_from_envp(void)
|
||||||
|
{
|
||||||
|
struct mm_struct *mm;
|
||||||
|
char *envp_start, *envp_end;
|
||||||
|
char *env_ptr, *token = NULL;
|
||||||
|
unsigned long env_len;
|
||||||
|
char *env_copy = NULL;
|
||||||
|
|
||||||
|
if (!current->mm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mm = current->mm;
|
||||||
|
|
||||||
|
down_read(&mm->mmap_lock);
|
||||||
|
|
||||||
|
envp_start = (char *)mm->env_start;
|
||||||
|
envp_end = (char *)mm->env_end;
|
||||||
|
env_len = envp_end - envp_start;
|
||||||
|
|
||||||
|
if (env_len <= 0 || env_len > PAGE_SIZE * 32) {
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
env_copy = kzalloc(env_len + 1, GFP_KERNEL);
|
||||||
|
if (!env_copy) {
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_from_user(env_copy, envp_start, env_len)) {
|
||||||
|
kfree(env_copy);
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
up_read(&mm->mmap_lock);
|
||||||
|
|
||||||
|
env_copy[env_len] = '\0';
|
||||||
|
env_ptr = env_copy;
|
||||||
|
|
||||||
|
while (env_ptr < env_copy + env_len) {
|
||||||
|
if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) {
|
||||||
|
char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1;
|
||||||
|
char *token_end = strchr(token_start, '\0');
|
||||||
|
|
||||||
|
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
|
||||||
|
token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
|
||||||
|
if (token) {
|
||||||
|
memcpy(token, token_start, KSU_TOKEN_LENGTH);
|
||||||
|
token[KSU_TOKEN_LENGTH] = '\0';
|
||||||
|
pr_info("manual_su: found auth token in environment\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
env_ptr += strlen(env_ptr) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(env_copy);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* ksu_generate_auth_token(void)
|
||||||
|
{
|
||||||
|
static char token_buffer[KSU_TOKEN_LENGTH + 1];
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ksu_cleanup_expired_tokens();
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
if (token_count >= MAX_TOKENS) {
|
||||||
|
for (i = 0; i < MAX_TOKENS - 1; i++) {
|
||||||
|
auth_tokens[i] = auth_tokens[i + 1];
|
||||||
|
}
|
||||||
|
token_count = MAX_TOKENS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < KSU_TOKEN_LENGTH; i++) {
|
||||||
|
u8 rand_byte;
|
||||||
|
get_random_bytes(&rand_byte, 1);
|
||||||
|
int char_type = rand_byte % 3;
|
||||||
|
if (char_type == 0) {
|
||||||
|
token_buffer[i] = 'A' + (rand_byte % 26);
|
||||||
|
} else if (char_type == 1) {
|
||||||
|
token_buffer[i] = 'a' + (rand_byte % 26);
|
||||||
|
} else {
|
||||||
|
token_buffer[i] = '0' + (rand_byte % 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#else
|
||||||
|
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#endif
|
||||||
|
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
|
||||||
|
auth_tokens[token_count].used = false;
|
||||||
|
token_count++;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
|
||||||
|
pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME);
|
||||||
|
return token_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ksu_verify_auth_token(const char *token)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool valid = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!token || strlen(token) != KSU_TOKEN_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < token_count; i++) {
|
||||||
|
if (!auth_tokens[i].used &&
|
||||||
|
time_before(jiffies, auth_tokens[i].expire_time) &&
|
||||||
|
strcmp(auth_tokens[i].token, token) == 0) {
|
||||||
|
|
||||||
|
auth_tokens[i].used = true;
|
||||||
|
valid = true;
|
||||||
|
pr_info("manual_su: auth token verified successfully\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
pr_warn("manual_su: invalid or expired auth token\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_cleanup_expired_tokens(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&token_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < token_count; ) {
|
||||||
|
if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) {
|
||||||
|
for (j = i; j < token_count - 1; j++) {
|
||||||
|
auth_tokens[j] = auth_tokens[j + 1];
|
||||||
|
}
|
||||||
|
token_count--;
|
||||||
|
pr_debug("manual_su: cleaned up expired/used token\n");
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&token_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_token_generation(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
if (current_uid().val > 2000) {
|
||||||
|
pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *new_token = ksu_generate_auth_token();
|
||||||
|
if (!new_token) {
|
||||||
|
pr_err("manual_su: failed to generate token\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#else
|
||||||
|
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pr_info("manual_su: auth token generated successfully\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_escalation_request(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
uid_t target_uid = request->target_uid;
|
||||||
|
pid_t target_pid = request->target_pid;
|
||||||
|
struct task_struct *tsk;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID);
|
||||||
|
if (!tsk || ksu_task_is_dead(tsk)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_err("cmd_su: PID %d is invalid or dead\n", target_pid);
|
||||||
|
return -ESRCH;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val))
|
||||||
|
goto allowed;
|
||||||
|
|
||||||
|
char *env_token = get_token_from_envp();
|
||||||
|
if (!env_token) {
|
||||||
|
pr_warn("manual_su: no auth token found in environment\n");
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool token_valid = ksu_verify_auth_token(env_token);
|
||||||
|
kfree(env_token);
|
||||||
|
|
||||||
|
if (!token_valid) {
|
||||||
|
pr_warn("manual_su: token verification failed\n");
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed:
|
||||||
|
current_verified = true;
|
||||||
|
escape_to_root_for_cmd_su(target_uid, target_pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_add_pending_request(struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
uid_t target_uid = request->target_uid;
|
||||||
|
|
||||||
|
if (!is_current_verified()) {
|
||||||
|
pr_warn("manual_su: add_pending denied, not verified\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_pending_root(target_uid);
|
||||||
|
current_verified = false;
|
||||||
|
pr_info("manual_su: pending root added for UID %d\n", target_uid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_manual_su_request(int option, struct manual_su_request *request)
|
||||||
|
{
|
||||||
|
if (!request) {
|
||||||
|
pr_err("manual_su: invalid request pointer\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case MANUAL_SU_OP_GENERATE_TOKEN:
|
||||||
|
pr_info("manual_su: handling token generation request\n");
|
||||||
|
return handle_token_generation(request);
|
||||||
|
|
||||||
|
case MANUAL_SU_OP_ESCALATE:
|
||||||
|
pr_info("manual_su: handling escalation request for UID %d, PID %d\n",
|
||||||
|
request->target_uid, request->target_pid);
|
||||||
|
return handle_escalation_request(request);
|
||||||
|
|
||||||
|
case MANUAL_SU_OP_ADD_PENDING:
|
||||||
|
pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid);
|
||||||
|
return handle_add_pending_request(request);
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("manual_su: unknown option %d\n", option);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_current_verified(void)
|
||||||
|
{
|
||||||
|
return current_verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].use_count++;
|
||||||
|
pending_uids[i].remove_calls++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].remove_calls++;
|
||||||
|
|
||||||
|
if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) {
|
||||||
|
pending_uids[i] = pending_uids[--pending_cnt];
|
||||||
|
pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS);
|
||||||
|
ksu_temp_revoke_root_once(uid);
|
||||||
|
} else {
|
||||||
|
pr_info("pending_root: UID %d remove_call=%d (<%d)\n",
|
||||||
|
uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_pending_root(uid_t uid)
|
||||||
|
{
|
||||||
|
if (pending_cnt >= MAX_PENDING) {
|
||||||
|
pr_warn("pending_root: cache full\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < pending_cnt; i++) {
|
||||||
|
if (pending_uids[i].uid == uid) {
|
||||||
|
pending_uids[i].use_count = 0;
|
||||||
|
pending_uids[i].remove_calls = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pending_uids[pending_cnt++] = (struct pending_uid){uid, 0};
|
||||||
|
ksu_temp_grant_root_once(uid);
|
||||||
|
pr_info("pending_root: cached UID %d\n", uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_try_escalate_for_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
if (!is_pending_root(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
pr_info("pending_root: UID=%d temporarily allowed\n", uid);
|
||||||
|
remove_pending_root(uid);
|
||||||
|
}
|
||||||
49
kernel/manual_su.h
Normal file
49
kernel/manual_su.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef __KSU_MANUAL_SU_H
|
||||||
|
#define __KSU_MANUAL_SU_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
|
||||||
|
#define mmap_lock mmap_sem
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ksu_task_is_dead(t) ((t)->exit_state != 0)
|
||||||
|
|
||||||
|
#define MAX_PENDING 16
|
||||||
|
#define REMOVE_DELAY_CALLS 150
|
||||||
|
#define MAX_TOKENS 10
|
||||||
|
|
||||||
|
#define KSU_SU_VERIFIED_BIT (1UL << 0)
|
||||||
|
#define KSU_TOKEN_LENGTH 32
|
||||||
|
#define KSU_TOKEN_ENV_NAME "KSU_AUTH_TOKEN"
|
||||||
|
#define KSU_TOKEN_EXPIRE_TIME 150
|
||||||
|
|
||||||
|
#define MANUAL_SU_OP_GENERATE_TOKEN 0
|
||||||
|
#define MANUAL_SU_OP_ESCALATE 1
|
||||||
|
#define MANUAL_SU_OP_ADD_PENDING 2
|
||||||
|
|
||||||
|
struct pending_uid {
|
||||||
|
uid_t uid;
|
||||||
|
int use_count;
|
||||||
|
int remove_calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct manual_su_request {
|
||||||
|
uid_t target_uid;
|
||||||
|
pid_t target_pid;
|
||||||
|
char token_buffer[KSU_TOKEN_LENGTH + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_token_entry {
|
||||||
|
char token[KSU_TOKEN_LENGTH + 1];
|
||||||
|
unsigned long expire_time;
|
||||||
|
bool used;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_handle_manual_su_request(int option, struct manual_su_request *request);
|
||||||
|
bool is_pending_root(uid_t uid);
|
||||||
|
void remove_pending_root(uid_t uid);
|
||||||
|
void ksu_try_escalate_for_uid(uid_t uid);
|
||||||
|
#endif
|
||||||
133
kernel/pkg_observer.c
Normal file
133
kernel/pkg_observer.c
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/fsnotify_backend.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/rculist.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
|
||||||
|
#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
|
||||||
|
|
||||||
|
struct watch_dir {
|
||||||
|
const char *path;
|
||||||
|
u32 mask;
|
||||||
|
struct path kpath;
|
||||||
|
struct inode *inode;
|
||||||
|
struct fsnotify_mark *mark;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fsnotify_group *g;
|
||||||
|
|
||||||
|
static int ksu_handle_inode_event(struct fsnotify_mark *mark, u32 mask,
|
||||||
|
struct inode *inode, struct inode *dir,
|
||||||
|
const struct qstr *file_name, u32 cookie)
|
||||||
|
{
|
||||||
|
if (!file_name)
|
||||||
|
return 0;
|
||||||
|
if (mask & FS_ISDIR)
|
||||||
|
return 0;
|
||||||
|
if (file_name->len == 13 &&
|
||||||
|
!memcmp(file_name->name, "packages.list", 13)) {
|
||||||
|
pr_info("packages.list detected: %d\n", mask);
|
||||||
|
if (ksu_uid_scanner_enabled) {
|
||||||
|
ksu_request_userspace_scan();
|
||||||
|
}
|
||||||
|
track_throne(false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fsnotify_ops ksu_ops = {
|
||||||
|
.handle_inode_event = ksu_handle_inode_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int add_mark_on_inode(struct inode *inode, u32 mask,
|
||||||
|
struct fsnotify_mark **out)
|
||||||
|
{
|
||||||
|
struct fsnotify_mark *m;
|
||||||
|
|
||||||
|
m = kzalloc(sizeof(*m), GFP_KERNEL);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
fsnotify_init_mark(m, g);
|
||||||
|
m->mask = mask;
|
||||||
|
|
||||||
|
if (fsnotify_add_inode_mark(m, inode, 0)) {
|
||||||
|
fsnotify_put_mark(m);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*out = m;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int watch_one_dir(struct watch_dir *wd)
|
||||||
|
{
|
||||||
|
int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
|
||||||
|
if (ret) {
|
||||||
|
pr_info("path not ready: %s (%d)\n", wd->path, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
wd->inode = d_inode(wd->kpath.dentry);
|
||||||
|
ihold(wd->inode);
|
||||||
|
|
||||||
|
ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
|
||||||
|
path_put(&wd->kpath);
|
||||||
|
iput(wd->inode);
|
||||||
|
wd->inode = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pr_info("watching %s\n", wd->path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unwatch_one_dir(struct watch_dir *wd)
|
||||||
|
{
|
||||||
|
if (wd->mark) {
|
||||||
|
fsnotify_destroy_mark(wd->mark, g);
|
||||||
|
fsnotify_put_mark(wd->mark);
|
||||||
|
wd->mark = NULL;
|
||||||
|
}
|
||||||
|
if (wd->inode) {
|
||||||
|
iput(wd->inode);
|
||||||
|
wd->inode = NULL;
|
||||||
|
}
|
||||||
|
if (wd->kpath.dentry) {
|
||||||
|
path_put(&wd->kpath);
|
||||||
|
memset(&wd->kpath, 0, sizeof(wd->kpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct watch_dir g_watch = { .path = "/data/system",
|
||||||
|
.mask = MASK_SYSTEM };
|
||||||
|
|
||||||
|
int ksu_observer_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
|
||||||
|
g = fsnotify_alloc_group(&ksu_ops, 0);
|
||||||
|
#else
|
||||||
|
g = fsnotify_alloc_group(&ksu_ops);
|
||||||
|
#endif
|
||||||
|
if (IS_ERR(g))
|
||||||
|
return PTR_ERR(g);
|
||||||
|
|
||||||
|
ret = watch_one_dir(&g_watch);
|
||||||
|
pr_info("observer init done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_observer_exit(void)
|
||||||
|
{
|
||||||
|
unwatch_one_dir(&g_watch);
|
||||||
|
fsnotify_put_group(g);
|
||||||
|
pr_info("observer exit done\n");
|
||||||
|
}
|
||||||
69
kernel/seccomp_cache.c
Normal file
69
kernel/seccomp_cache.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
|
||||||
|
struct action_cache {
|
||||||
|
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct seccomp_filter {
|
||||||
|
refcount_t refs;
|
||||||
|
refcount_t users;
|
||||||
|
bool log;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
bool wait_killable_recv;
|
||||||
|
#endif
|
||||||
|
struct action_cache cache;
|
||||||
|
struct seccomp_filter *prev;
|
||||||
|
struct bpf_prog *prog;
|
||||||
|
struct notification *notif;
|
||||||
|
struct mutex notify_lock;
|
||||||
|
wait_queue_head_t wqh;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
12
kernel/seccomp_cache.h
Normal file
12
kernel/seccomp_cache.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __KSU_H_SECCOMP_CACHE
|
||||||
|
#define __KSU_H_SECCOMP_CACHE
|
||||||
|
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
|
||||||
|
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,15 +2,7 @@ obj-y += selinux.o
|
|||||||
obj-y += sepolicy.o
|
obj-y += sepolicy.o
|
||||||
obj-y += rules.o
|
obj-y += rules.o
|
||||||
|
|
||||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion
|
||||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
|
||||||
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
|
||||||
endif
|
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
#include "sepolicy.h"
|
#include "sepolicy.h"
|
||||||
#include "ss/services.h"
|
#include "ss/services.h"
|
||||||
#include "linux/lsm_audit.h"
|
#include "linux/lsm_audit.h" // IWYU pragma: keep
|
||||||
#include "xfrm.h"
|
#include "xfrm.h"
|
||||||
|
|
||||||
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
@@ -18,119 +18,119 @@
|
|||||||
|
|
||||||
static struct policydb *get_policydb(void)
|
static struct policydb *get_policydb(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
struct selinux_policy *policy = selinux_state.policy;
|
struct selinux_policy *policy = selinux_state.policy;
|
||||||
db = &policy->policydb;
|
db = &policy->policydb;
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFINE_MUTEX(ksu_rules);
|
static DEFINE_MUTEX(ksu_rules);
|
||||||
|
|
||||||
void apply_kernelsu_rules()
|
void apply_kernelsu_rules()
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!getenforce()) {
|
||||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&ksu_rules);
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||||
|
|
||||||
// Create unconstrained file type
|
// Create unconstrained file type
|
||||||
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||||
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||||
|
|
||||||
// allow all!
|
// allow all!
|
||||||
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||||
|
|
||||||
// allow us do any ioctl
|
// allow us do any ioctl
|
||||||
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to save allowlist in /data/adb/ksu
|
// we need to save allowlist in /data/adb/ksu
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||||
// we need to search /data/app
|
// we need to search /data/app
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||||
// we may need to do mount on shell
|
// we may need to do mount on shell
|
||||||
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||||
// we need to read /data/system/packages.list
|
// we need to read /data/system/packages.list
|
||||||
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
||||||
// Android 10+:
|
// Android 10+:
|
||||||
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
||||||
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
||||||
// Kernel 4.4
|
// Kernel 4.4
|
||||||
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
||||||
// Android 9-:
|
// Android 9-:
|
||||||
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
||||||
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
||||||
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
||||||
// our ksud triggered by init
|
// our ksud triggered by init
|
||||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||||
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||||
// we need to umount modules in zygote
|
// we need to umount modules in zygote
|
||||||
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||||
|
|
||||||
// copied from Magisk rules
|
// copied from Magisk rules
|
||||||
// suRights
|
// suRights
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||||
|
|
||||||
// allowLog
|
// allowLog
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||||
|
|
||||||
// dumpsys
|
// dumpsys
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||||
|
|
||||||
// bootctl
|
// bootctl
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||||
"getattr");
|
"getattr");
|
||||||
|
|
||||||
// For mounting loop devices, mirrors, tmpfs
|
// For mounting loop devices, mirrors, tmpfs
|
||||||
ksu_allow(db, "kernel", ALL, "file", "read");
|
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||||
ksu_allow(db, "kernel", ALL, "file", "write");
|
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||||
|
|
||||||
// Allow all binder transactions
|
// Allow all binder transactions
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||||
|
|
||||||
// Allow system server kill su process
|
// Allow system server kill su process
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|
||||||
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
||||||
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
||||||
|
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_SEPOL_LEN 128
|
#define MAX_SEPOL_LEN 128
|
||||||
@@ -145,401 +145,333 @@ void apply_kernelsu_rules()
|
|||||||
#define CMD_TYPE_CHANGE 8
|
#define CMD_TYPE_CHANGE 8
|
||||||
#define CMD_GENFSCON 9
|
#define CMD_GENFSCON 9
|
||||||
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
u32 subcmd;
|
u32 subcmd;
|
||||||
u64 field_sepol1;
|
char __user *sepol1;
|
||||||
u64 field_sepol2;
|
char __user *sepol2;
|
||||||
u64 field_sepol3;
|
char __user *sepol3;
|
||||||
u64 field_sepol4;
|
char __user *sepol4;
|
||||||
u64 field_sepol5;
|
char __user *sepol5;
|
||||||
u64 field_sepol6;
|
char __user *sepol6;
|
||||||
u64 field_sepol7;
|
char __user *sepol7;
|
||||||
};
|
};
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
extern bool ksu_is_compat __read_mostly;
|
|
||||||
struct sepol_compat_data {
|
|
||||||
u32 cmd;
|
|
||||||
u32 subcmd;
|
|
||||||
u32 field_sepol1;
|
|
||||||
u32 field_sepol2;
|
|
||||||
u32 field_sepol3;
|
|
||||||
u32 field_sepol4;
|
|
||||||
u32 field_sepol5;
|
|
||||||
u32 field_sepol6;
|
|
||||||
u32 field_sepol7;
|
|
||||||
};
|
|
||||||
#endif // CONFIG_COMPAT
|
|
||||||
#else
|
|
||||||
struct sepol_data {
|
|
||||||
u32 cmd;
|
|
||||||
u32 subcmd;
|
|
||||||
u32 field_sepol1;
|
|
||||||
u32 field_sepol2;
|
|
||||||
u32 field_sepol3;
|
|
||||||
u32 field_sepol4;
|
|
||||||
u32 field_sepol5;
|
|
||||||
u32 field_sepol6;
|
|
||||||
u32 field_sepol7;
|
|
||||||
};
|
|
||||||
#endif // CONFIG_64BIT
|
|
||||||
|
|
||||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||||
char **object)
|
char **object)
|
||||||
{
|
{
|
||||||
if (!user_object) {
|
if (!user_object) {
|
||||||
*object = ALL;
|
*object = ALL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*object = buf;
|
*object = buf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
||||||
|
extern int avc_ss_reset(u32 seqno);
|
||||||
|
#else
|
||||||
|
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
|
||||||
|
#endif
|
||||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||||
static void reset_avc_cache()
|
static void reset_avc_cache()
|
||||||
{
|
{
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
||||||
avc_ss_reset(0);
|
avc_ss_reset(0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(0);
|
selinux_status_update_policyload(0);
|
||||||
#else
|
#else
|
||||||
struct selinux_avc *avc = selinux_state.avc;
|
struct selinux_avc *avc = selinux_state.avc;
|
||||||
avc_ss_reset(avc, 0);
|
avc_ss_reset(avc, 0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(&selinux_state, 0);
|
selinux_status_update_policyload(&selinux_state, 0);
|
||||||
#endif
|
#endif
|
||||||
selinux_xfrm_notify_policyload();
|
selinux_xfrm_notify_policyload();
|
||||||
}
|
}
|
||||||
|
|
||||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
|
|
||||||
if (!arg4) {
|
if (!arg4) {
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!getenforce()) {
|
||||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 cmd, subcmd;
|
|
||||||
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
|
||||||
|
|
||||||
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
|
struct sepol_data data;
|
||||||
if (unlikely(ksu_is_compat)) {
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
struct sepol_compat_data compat_data;
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) {
|
return -EINVAL;
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
}
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sepol1 = compat_ptr(compat_data.field_sepol1);
|
|
||||||
sepol2 = compat_ptr(compat_data.field_sepol2);
|
|
||||||
sepol3 = compat_ptr(compat_data.field_sepol3);
|
|
||||||
sepol4 = compat_ptr(compat_data.field_sepol4);
|
|
||||||
sepol5 = compat_ptr(compat_data.field_sepol5);
|
|
||||||
sepol6 = compat_ptr(compat_data.field_sepol6);
|
|
||||||
sepol7 = compat_ptr(compat_data.field_sepol7);
|
|
||||||
cmd = compat_data.cmd;
|
|
||||||
subcmd = compat_data.subcmd;
|
|
||||||
} else {
|
|
||||||
struct sepol_data data;
|
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sepol1 = data.field_sepol1;
|
|
||||||
sepol2 = data.field_sepol2;
|
|
||||||
sepol3 = data.field_sepol3;
|
|
||||||
sepol4 = data.field_sepol4;
|
|
||||||
sepol5 = data.field_sepol5;
|
|
||||||
sepol6 = data.field_sepol6;
|
|
||||||
sepol7 = data.field_sepol7;
|
|
||||||
cmd = data.cmd;
|
|
||||||
subcmd = data.subcmd;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n)
|
|
||||||
struct sepol_data data;
|
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sepol1 = data.field_sepol1;
|
|
||||||
sepol2 = data.field_sepol2;
|
|
||||||
sepol3 = data.field_sepol3;
|
|
||||||
sepol4 = data.field_sepol4;
|
|
||||||
sepol5 = data.field_sepol5;
|
|
||||||
sepol6 = data.field_sepol6;
|
|
||||||
sepol7 = data.field_sepol7;
|
|
||||||
cmd = data.cmd;
|
|
||||||
subcmd = data.subcmd;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mutex_lock(&ksu_rules);
|
u32 cmd = data.cmd;
|
||||||
|
u32 subcmd = data.subcmd;
|
||||||
|
|
||||||
db = get_policydb();
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
int ret = -1;
|
db = get_policydb();
|
||||||
if (cmd == CMD_NORMAL_PERM) {
|
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
|
||||||
char perm_buf[MAX_SEPOL_LEN];
|
|
||||||
|
|
||||||
char *s, *t, *c, *p;
|
int ret = -EINVAL;
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
if (cmd == CMD_NORMAL_PERM) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
}
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
char *s, *t, *c, *p;
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
goto exit;
|
pr_err("sepol: copy src failed.\n");
|
||||||
}
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
0) {
|
pr_err("sepol: copy cls failed.\n");
|
||||||
pr_err("sepol: copy perm failed.\n");
|
goto exit;
|
||||||
goto exit;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
||||||
if (subcmd == 1) {
|
0) {
|
||||||
success = ksu_allow(db, s, t, c, p);
|
pr_err("sepol: copy perm failed.\n");
|
||||||
} else if (subcmd == 2) {
|
goto exit;
|
||||||
success = ksu_deny(db, s, t, c, p);
|
}
|
||||||
} else if (subcmd == 3) {
|
|
||||||
success = ksu_auditallow(db, s, t, c, p);
|
|
||||||
} else if (subcmd == 4) {
|
|
||||||
success = ksu_dontaudit(db, s, t, c, p);
|
|
||||||
} else {
|
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
|
||||||
}
|
|
||||||
ret = success ? 0 : -1;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_XPERM) {
|
bool success = false;
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
if (subcmd == 1) {
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
success = ksu_allow(db, s, t, c, p);
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_deny(db, s, t, c, p);
|
||||||
|
} else if (subcmd == 3) {
|
||||||
|
success = ksu_auditallow(db, s, t, c, p);
|
||||||
|
} else if (subcmd == 4) {
|
||||||
|
success = ksu_dontaudit(db, s, t, c, p);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -EINVAL;
|
||||||
|
|
||||||
char __maybe_unused
|
} else if (cmd == CMD_XPERM) {
|
||||||
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char perm_set[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c;
|
char __maybe_unused
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||||
pr_err("sepol: copy src failed.\n");
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
|
||||||
pr_err("sepol: copy cls failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(operation, sepol4,
|
|
||||||
sizeof(operation)) < 0) {
|
|
||||||
pr_err("sepol: copy operation failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
|
||||||
0) {
|
|
||||||
pr_err("sepol: copy perm_set failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
char *s, *t, *c;
|
||||||
if (subcmd == 1) {
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
success = ksu_allowxperm(db, s, t, c, perm_set);
|
pr_err("sepol: copy src failed.\n");
|
||||||
} else if (subcmd == 2) {
|
goto exit;
|
||||||
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
}
|
||||||
} else if (subcmd == 3) {
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
} else {
|
goto exit;
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
}
|
||||||
}
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
ret = success ? 0 : -1;
|
pr_err("sepol: copy cls failed.\n");
|
||||||
} else if (cmd == CMD_TYPE_STATE) {
|
goto exit;
|
||||||
char src[MAX_SEPOL_LEN];
|
}
|
||||||
|
if (strncpy_from_user(operation, data.sepol4,
|
||||||
|
sizeof(operation)) < 0) {
|
||||||
|
pr_err("sepol: copy operation failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
|
||||||
|
0) {
|
||||||
|
pr_err("sepol: copy perm_set failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
bool success = false;
|
||||||
pr_err("sepol: copy src failed.\n");
|
if (subcmd == 1) {
|
||||||
goto exit;
|
success = ksu_allowxperm(db, s, t, c, perm_set);
|
||||||
}
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
||||||
|
} else if (subcmd == 3) {
|
||||||
|
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -EINVAL;
|
||||||
|
} else if (cmd == CMD_TYPE_STATE) {
|
||||||
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
bool success = false;
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
if (subcmd == 1) {
|
pr_err("sepol: copy src failed.\n");
|
||||||
success = ksu_permissive(db, src);
|
goto exit;
|
||||||
} else if (subcmd == 2) {
|
}
|
||||||
success = ksu_enforce(db, src);
|
|
||||||
} else {
|
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
|
||||||
}
|
|
||||||
if (success)
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
bool success = false;
|
||||||
char type[MAX_SEPOL_LEN];
|
if (subcmd == 1) {
|
||||||
char attr[MAX_SEPOL_LEN];
|
success = ksu_permissive(db, src);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_enforce(db, src);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||||
pr_err("sepol: copy type failed.\n");
|
char type[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char attr[MAX_SEPOL_LEN];
|
||||||
}
|
|
||||||
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
|
||||||
pr_err("sepol: copy attr failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
||||||
if (cmd == CMD_TYPE) {
|
pr_err("sepol: copy type failed.\n");
|
||||||
success = ksu_type(db, type, attr);
|
goto exit;
|
||||||
} else {
|
}
|
||||||
success = ksu_typeattribute(db, type, attr);
|
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
||||||
}
|
pr_err("sepol: copy attr failed.\n");
|
||||||
if (!success) {
|
goto exit;
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
}
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_ATTR) {
|
bool success = false;
|
||||||
char attr[MAX_SEPOL_LEN];
|
if (cmd == CMD_TYPE) {
|
||||||
|
success = ksu_type(db, type, attr);
|
||||||
|
} else {
|
||||||
|
success = ksu_typeattribute(db, type, attr);
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
} else if (cmd == CMD_ATTR) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
char attr[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (!ksu_attribute(db, attr)) {
|
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
||||||
char src[MAX_SEPOL_LEN];
|
pr_err("sepol: copy attr failed.\n");
|
||||||
char tgt[MAX_SEPOL_LEN];
|
goto exit;
|
||||||
char cls[MAX_SEPOL_LEN];
|
}
|
||||||
char default_type[MAX_SEPOL_LEN];
|
if (!ksu_attribute(db, attr)) {
|
||||||
char object[MAX_SEPOL_LEN];
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt[MAX_SEPOL_LEN];
|
||||||
}
|
char cls[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
char default_type[MAX_SEPOL_LEN];
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
char object[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
|
||||||
pr_err("sepol: copy cls failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
|
||||||
sizeof(default_type)) < 0) {
|
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
char *real_object;
|
|
||||||
if (sepol5 == NULL) {
|
|
||||||
real_object = NULL;
|
|
||||||
} else {
|
|
||||||
if (strncpy_from_user(object, sepol5,
|
|
||||||
sizeof(object)) < 0) {
|
|
||||||
pr_err("sepol: copy object failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
real_object = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = ksu_type_transition(db, src, tgt, cls,
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
default_type, real_object);
|
pr_err("sepol: copy src failed.\n");
|
||||||
if (success)
|
goto exit;
|
||||||
ret = 0;
|
}
|
||||||
|
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||||
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(default_type, data.sepol4,
|
||||||
|
sizeof(default_type)) < 0) {
|
||||||
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
char *real_object;
|
||||||
|
if (data.sepol5 == NULL) {
|
||||||
|
real_object = NULL;
|
||||||
|
} else {
|
||||||
|
if (strncpy_from_user(object, data.sepol5,
|
||||||
|
sizeof(object)) < 0) {
|
||||||
|
pr_err("sepol: copy object failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
real_object = object;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||||
char src[MAX_SEPOL_LEN];
|
default_type, real_object);
|
||||||
char tgt[MAX_SEPOL_LEN];
|
if (success)
|
||||||
char cls[MAX_SEPOL_LEN];
|
ret = 0;
|
||||||
char default_type[MAX_SEPOL_LEN];
|
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
char src[MAX_SEPOL_LEN];
|
||||||
goto exit;
|
char tgt[MAX_SEPOL_LEN];
|
||||||
}
|
char cls[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
char default_type[MAX_SEPOL_LEN];
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
|
||||||
pr_err("sepol: copy cls failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
|
||||||
sizeof(default_type)) < 0) {
|
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
bool success = false;
|
|
||||||
if (subcmd == 1) {
|
|
||||||
success = ksu_type_change(db, src, tgt, cls,
|
|
||||||
default_type);
|
|
||||||
} else if (subcmd == 2) {
|
|
||||||
success = ksu_type_member(db, src, tgt, cls,
|
|
||||||
default_type);
|
|
||||||
} else {
|
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
|
||||||
}
|
|
||||||
if (success)
|
|
||||||
ret = 0;
|
|
||||||
} else if (cmd == CMD_GENFSCON) {
|
|
||||||
char name[MAX_SEPOL_LEN];
|
|
||||||
char path[MAX_SEPOL_LEN];
|
|
||||||
char context[MAX_SEPOL_LEN];
|
|
||||||
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
|
||||||
pr_err("sepol: copy name failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
|
||||||
pr_err("sepol: copy path failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
|
||||||
0) {
|
|
||||||
pr_err("sepol: copy context failed.\n");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ksu_genfscon(db, name, path, context)) {
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||||
} else {
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
goto exit;
|
||||||
}
|
}
|
||||||
|
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(default_type, data.sepol4,
|
||||||
|
sizeof(default_type)) < 0) {
|
||||||
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
if (subcmd == 1) {
|
||||||
|
success = ksu_type_change(db, src, tgt, cls,
|
||||||
|
default_type);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_type_member(db, src, tgt, cls,
|
||||||
|
default_type);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
} else if (cmd == CMD_GENFSCON) {
|
||||||
|
char name[MAX_SEPOL_LEN];
|
||||||
|
char path[MAX_SEPOL_LEN];
|
||||||
|
char context[MAX_SEPOL_LEN];
|
||||||
|
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
|
||||||
|
pr_err("sepol: copy name failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
|
||||||
|
pr_err("sepol: copy path failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
|
||||||
|
0) {
|
||||||
|
pr_err("sepol: copy context failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_genfscon(db, name, path, context)) {
|
||||||
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
|
|
||||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||||
// we are in atomic context. so we just reset it every time.
|
// we are in atomic context. so we just reset it every time.
|
||||||
reset_avc_cache();
|
reset_avc_cache();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
#include "linux/sched.h"
|
||||||
#include "objsec.h"
|
#include "objsec.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
#include "../klog.h" // IWYU pragma: keep
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
@@ -7,124 +9,146 @@
|
|||||||
|
|
||||||
static int transive_to_domain(const char *domain)
|
static int transive_to_domain(const char *domain)
|
||||||
{
|
{
|
||||||
struct cred *cred;
|
struct cred *cred;
|
||||||
struct task_security_struct *tsec;
|
struct task_security_struct *tsec;
|
||||||
u32 sid;
|
u32 sid;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
cred = (struct cred *)__task_cred(current);
|
cred = (struct cred *)__task_cred(current);
|
||||||
|
|
||||||
tsec = cred->security;
|
tsec = cred->security;
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
pr_err("tsec == NULL!\n");
|
pr_err("tsec == NULL!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||||
if (error) {
|
if (error) {
|
||||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||||
domain, sid, error);
|
domain, sid, error);
|
||||||
}
|
}
|
||||||
if (!error) {
|
if (!error) {
|
||||||
tsec->sid = sid;
|
tsec->sid = sid;
|
||||||
tsec->create_sid = 0;
|
tsec->create_sid = 0;
|
||||||
tsec->keycreate_sid = 0;
|
tsec->keycreate_sid = 0;
|
||||||
tsec->sockcreate_sid = 0;
|
tsec->sockcreate_sid = 0;
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_selinux(const char *domain)
|
void setup_selinux(const char *domain)
|
||||||
{
|
{
|
||||||
if (transive_to_domain(domain)) {
|
if (transive_to_domain(domain)) {
|
||||||
pr_err("transive domain failed.\n");
|
pr_err("transive domain failed.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we didn't need this now, we have change selinux rules when boot!
|
|
||||||
if (!is_domain_permissive) {
|
|
||||||
if (set_domain_permissive() == 0) {
|
|
||||||
is_domain_permissive = true;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setenforce(bool enforce)
|
void setenforce(bool enforce)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
selinux_state.enforcing = enforce;
|
selinux_state.enforcing = enforce;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getenforce()
|
bool getenforce()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||||
if (selinux_state.disabled) {
|
if (selinux_state.disabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
return selinux_state.enforcing;
|
return selinux_state.enforcing;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
|
||||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
struct lsm_context {
|
||||||
/*
|
char *context;
|
||||||
* get the subjective security ID of the current task
|
u32 len;
|
||||||
*/
|
};
|
||||||
static inline u32 current_sid(void)
|
|
||||||
|
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
|
||||||
{
|
{
|
||||||
const struct task_security_struct *tsec = current_security();
|
return security_secid_to_secctx(secid, &cp->context, &cp->len);
|
||||||
|
|
||||||
return tsec->sid;
|
|
||||||
}
|
}
|
||||||
|
static void __security_release_secctx(struct lsm_context *cp)
|
||||||
|
{
|
||||||
|
return security_release_secctx(cp->context, cp->len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define __security_secid_to_secctx security_secid_to_secctx
|
||||||
|
#define __security_release_secctx security_release_secctx
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred* cred)
|
||||||
|
{
|
||||||
|
struct lsm_context ctx;
|
||||||
|
bool result;
|
||||||
|
if (!cred) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const struct task_security_struct *tsec = selinux_cred(cred);
|
||||||
|
if (!tsec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||||
|
__security_release_secctx(&ctx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_ksu_domain()
|
bool is_ksu_domain()
|
||||||
{
|
{
|
||||||
char *domain;
|
current_sid();
|
||||||
u32 seclen;
|
return is_task_ksu_domain(current_cred());
|
||||||
bool result;
|
|
||||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
|
||||||
if (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
|
||||||
security_release_secctx(domain, seclen);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_zygote(void *sec)
|
bool is_context(const struct cred* cred, const char* context)
|
||||||
{
|
{
|
||||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
if (!cred) {
|
||||||
if (!tsec) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
const struct task_security_struct * tsec = selinux_cred(cred);
|
||||||
char *domain;
|
if (!tsec) {
|
||||||
u32 seclen;
|
return false;
|
||||||
bool result;
|
}
|
||||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
struct lsm_context ctx;
|
||||||
if (err) {
|
bool result;
|
||||||
return false;
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
}
|
if (err) {
|
||||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
return false;
|
||||||
security_release_secctx(domain, seclen);
|
}
|
||||||
return result;
|
result = strncmp(context, ctx.context, ctx.len) == 0;
|
||||||
|
__security_release_secctx(&ctx);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
bool is_zygote(const struct cred* cred)
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid()
|
|
||||||
{
|
{
|
||||||
u32 devpts_sid = 0;
|
return is_context(cred, "u:r:zygote:s0");
|
||||||
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
}
|
||||||
&devpts_sid);
|
|
||||||
if (err) {
|
bool is_init(const struct cred* cred) {
|
||||||
pr_info("get devpts sid err %d\n", err);
|
return is_context(cred, "u:r:init:s0");
|
||||||
}
|
}
|
||||||
return devpts_sid;
|
|
||||||
|
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
|
||||||
|
|
||||||
|
u32 ksu_get_ksu_file_sid()
|
||||||
|
{
|
||||||
|
u32 ksu_file_sid = 0;
|
||||||
|
int err = security_secctx_to_secid(KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN),
|
||||||
|
&ksu_file_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_info("get ksufile sid err %d\n", err);
|
||||||
|
}
|
||||||
|
return ksu_file_sid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "linux/types.h"
|
#include "linux/types.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
|
||||||
void setup_selinux(const char *);
|
void setup_selinux(const char *);
|
||||||
|
|
||||||
@@ -10,12 +11,18 @@ void setenforce(bool);
|
|||||||
|
|
||||||
bool getenforce();
|
bool getenforce();
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred* cred);
|
||||||
|
|
||||||
bool is_ksu_domain();
|
bool is_ksu_domain();
|
||||||
|
|
||||||
bool is_zygote(void *cred);
|
bool is_zygote(const struct cred* cred);
|
||||||
|
|
||||||
|
bool is_init(const struct cred* cred);
|
||||||
|
|
||||||
void apply_kernelsu_rules();
|
void apply_kernelsu_rules();
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid();
|
u32 ksu_get_ksu_file_sid();
|
||||||
|
|
||||||
|
int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type);
|
|||||||
|
|
||||||
// Access vector rules
|
// Access vector rules
|
||||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
|
|
||||||
// Extended permissions access vector rules
|
// Extended permissions access vector rules
|
||||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
|
|
||||||
// Type rules
|
// Type rules
|
||||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def, const char *obj);
|
const char *cls, const char *def, const char *obj);
|
||||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
|
|
||||||
// File system labeling
|
// File system labeling
|
||||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||||
const char *ctx);
|
const char *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
171
kernel/setuid_hook.c
Normal file
171
kernel/setuid_hook.c
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/init_task.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/security.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "manager.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
#include "supercalls.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
static bool ksu_enhanced_security_enabled = false;
|
||||||
|
|
||||||
|
static int enhanced_security_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_enhanced_security_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int enhanced_security_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_enhanced_security_enabled = enable;
|
||||||
|
pr_info("enhanced_security: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler enhanced_security_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_ENHANCED_SECURITY,
|
||||||
|
.name = "enhanced_security",
|
||||||
|
.get_handler = enhanced_security_feature_get,
|
||||||
|
.set_handler = enhanced_security_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool is_allow_su()
|
||||||
|
{
|
||||||
|
if (is_manager()) {
|
||||||
|
// we are manager, allow!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||||
|
{
|
||||||
|
uid_t new_uid = ruid;
|
||||||
|
uid_t old_uid = current_uid().val;
|
||||||
|
|
||||||
|
pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);
|
||||||
|
|
||||||
|
// if old process is root, ignore it.
|
||||||
|
if (old_uid != 0 && ksu_enhanced_security_enabled) {
|
||||||
|
// disallow any non-ksu domain escalation from non-root to root!
|
||||||
|
// euid is what we care about here as it controls permission
|
||||||
|
if (unlikely(euid == 0)) {
|
||||||
|
if (!is_ksu_domain()) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid, new_uid);
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// disallow appuid decrease to any other uid if it is not allowed to su
|
||||||
|
if (is_appuid(old_uid)) {
|
||||||
|
if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid, new_uid);
|
||||||
|
force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if on private space, see if its possibly the manager
|
||||||
|
if (new_uid > PER_USER_RANGE && new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
|
||||||
|
ksu_set_manager_uid(new_uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for manager: %d\n", new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
|
||||||
|
current->seccomp.filter) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
}
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
disable_seccomp();
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for ksu manager(uid=%d)\n",
|
||||||
|
new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle kernel umount
|
||||||
|
ksu_handle_umount(old_uid, new_uid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void)
|
||||||
|
{
|
||||||
|
ksu_kernel_umount_init();
|
||||||
|
if (ksu_register_feature_handler(&enhanced_security_handler)) {
|
||||||
|
pr_err("Failed to register enhanced security feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setuid_hook_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("ksu_core_exit\n");
|
||||||
|
ksu_kernel_umount_exit();
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
|
||||||
|
}
|
||||||
14
kernel/setuid_hook.h
Normal file
14
kernel/setuid_hook.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __KSU_H_KSU_CORE
|
||||||
|
#define __KSU_H_KSU_CORE
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "apk_sign.h"
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void);
|
||||||
|
void ksu_setuid_hook_exit(void);
|
||||||
|
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,337 +1,198 @@
|
|||||||
#include <linux/dcache.h>
|
#include <linux/compiler_types.h>
|
||||||
#include <linux/security.h>
|
#include <linux/preempt.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/pgtable.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
|
||||||
#include "objsec.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
#include "sucompat.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
#define SU_PATH "/system/bin/su"
|
#define SU_PATH "/system/bin/su"
|
||||||
#define SH_PATH "/system/bin/sh"
|
#define SH_PATH "/system/bin/sh"
|
||||||
|
|
||||||
extern void escape_to_root();
|
bool ksu_su_compat_enabled __read_mostly = true;
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
static int su_compat_feature_get(u64 *value)
|
||||||
static bool ksu_sucompat_hook_state __read_mostly = true;
|
{
|
||||||
#endif
|
*value = ksu_su_compat_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int su_compat_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_su_compat_enabled = enable;
|
||||||
|
pr_info("su_compat: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler su_compat_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_SU_COMPAT,
|
||||||
|
.name = "su_compat",
|
||||||
|
.get_handler = su_compat_feature_get,
|
||||||
|
.set_handler = su_compat_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||||
{
|
{
|
||||||
/* To avoid having to mmap a page in userspace, just write below the stack
|
// To avoid having to mmap a page in userspace, just write below the stack
|
||||||
* pointer. */
|
// pointer.
|
||||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||||
|
|
||||||
return copy_to_user(p, d, len) ? NULL : p;
|
return copy_to_user(p, d, len) ? NULL : p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char __user *sh_user_path(void)
|
static char __user *sh_user_path(void)
|
||||||
{
|
{
|
||||||
static const char sh_path[] = "/system/bin/sh";
|
static const char sh_path[] = "/system/bin/sh";
|
||||||
|
|
||||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static char __user *ksud_user_path(void)
|
static char __user *ksud_user_path(void)
|
||||||
{
|
{
|
||||||
static const char ksud_path[] = KSUD_PATH;
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
|
|
||||||
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
int *__unused_flags)
|
int *__unused_flags)
|
||||||
{
|
{
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
if (!ksu_sucompat_hook_state) {
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
|
||||||
#endif
|
#endif
|
||||||
|
pr_info("faccessat su->sh!\n");
|
||||||
|
*filename_user = sh_user_path();
|
||||||
|
}
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
|
||||||
memset(path, 0, sizeof(path));
|
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
|
||||||
pr_info("faccessat su->sh!\n");
|
|
||||||
*filename_user = sh_user_path();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||||
{
|
{
|
||||||
// const char sh[] = SH_PATH;
|
// const char sh[] = SH_PATH;
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
if (!ksu_sucompat_hook_state) {
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
if (unlikely(!filename_user)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
|
#if __SULOG_GATE
|
||||||
|
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
|
||||||
#endif
|
#endif
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
pr_info("newfstatat su->sh!\n");
|
||||||
return 0;
|
*filename_user = sh_user_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!filename_user)) {
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
int ksu_handle_execve_sucompat(const char __user **filename_user,
|
||||||
memset(path, 0, sizeof(path));
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
int *__never_use_flags)
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
{
|
||||||
// it becomes a `struct filename *` after 5.18
|
const char su[] = SU_PATH;
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
const char __user *fn;
|
||||||
const char sh[] = SH_PATH;
|
char path[sizeof(su) + 1];
|
||||||
struct filename *filename = *((struct filename **)filename_user);
|
long ret;
|
||||||
if (IS_ERR(filename)) {
|
unsigned long addr;
|
||||||
return 0;
|
|
||||||
}
|
if (unlikely(!filename_user))
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
return 0;
|
||||||
return 0;
|
|
||||||
pr_info("vfs_statx su->sh!\n");
|
#if __SULOG_GATE
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
|
||||||
|
|
||||||
|
if (!is_allowed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
|
||||||
#else
|
#else
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
||||||
|
return 0;
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
}
|
||||||
pr_info("newfstatat su->sh!\n");
|
|
||||||
*filename_user = sh_user_path();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
addr = untagged_addr((unsigned long)*filename_user);
|
||||||
|
fn = (const char __user *)addr;
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
|
||||||
|
|
||||||
|
if (ret < 0 && try_set_access_flag(addr)) {
|
||||||
|
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0 && preempt_count()) {
|
||||||
|
/* This is crazy, but we know what we are doing:
|
||||||
|
* Temporarily exit atomic context to handle page faults, then restore it */
|
||||||
|
pr_info("Access filename failed, try rescue..\n");
|
||||||
|
preempt_enable_no_resched_notrace();
|
||||||
|
ret = strncpy_from_user(path, fn, sizeof(path));
|
||||||
|
preempt_disable_notrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_warn("Access filename when execve failed: %ld", ret);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("sys_execve su found\n");
|
||||||
|
*filename_user = ksud_user_path();
|
||||||
|
|
||||||
|
escape_with_root_profile();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
// sucompat: permitted process can execute 'su' to gain root access.
|
||||||
void *envp, int *flags)
|
|
||||||
{
|
|
||||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
|
||||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
|
||||||
int *__never_use_flags)
|
|
||||||
{
|
|
||||||
struct filename *filename;
|
|
||||||
const char sh[] = KSUD_PATH;
|
|
||||||
const char su[] = SU_PATH;
|
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
|
||||||
if (!ksu_sucompat_hook_state) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (unlikely(!filename_ptr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
filename = *filename_ptr;
|
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("do_execveat_common su found\n");
|
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
|
|
||||||
escape_to_root();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
|
||||||
int *__never_use_flags)
|
|
||||||
{
|
|
||||||
const char su[] = SU_PATH;
|
|
||||||
char path[sizeof(su) + 1];
|
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
|
||||||
if (!ksu_sucompat_hook_state){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (unlikely(!filename_user))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memset(path, 0, sizeof(path));
|
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
|
||||||
|
|
||||||
if (likely(memcmp(path, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pr_info("sys_execve su found\n");
|
|
||||||
*filename_user = ksud_user_path();
|
|
||||||
|
|
||||||
escape_to_root();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dummified
|
|
||||||
int ksu_handle_devpts(struct inode *inode)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int __ksu_handle_devpts(struct inode *inode)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
|
||||||
if (!ksu_sucompat_hook_state)
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!current->mm) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uid_t uid = current_uid().val;
|
|
||||||
if (uid % 100000 < 10000) {
|
|
||||||
// not untrusted_app, ignore it
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(!ksu_is_allow_uid(uid)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
struct inode_security_struct *sec = selinux_inode(inode);
|
|
||||||
|
|
||||||
if (ksu_devpts_sid && sec)
|
|
||||||
sec->sid = ksu_devpts_sid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
|
||||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
|
||||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
|
||||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_stat(dfd, filename_user, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char **)&PT_REGS_PARM1(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe *su_kps[4];
|
|
||||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct inode *inode;
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
|
||||||
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
|
||||||
inode = file->f_path.dentry->d_inode;
|
|
||||||
#else
|
|
||||||
inode = (struct inode *)PT_REGS_PARM2(regs);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ksu_handle_devpts(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe *init_kprobe(const char *name,
|
|
||||||
kprobe_pre_handler_t handler)
|
|
||||||
{
|
|
||||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
|
||||||
if (!kp)
|
|
||||||
return NULL;
|
|
||||||
kp->symbol_name = name;
|
|
||||||
kp->pre_handler = handler;
|
|
||||||
|
|
||||||
int ret = register_kprobe(kp);
|
|
||||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
|
||||||
if (ret) {
|
|
||||||
kfree(kp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
|
||||||
{
|
|
||||||
struct kprobe *kp = *kp_ptr;
|
|
||||||
if (!kp)
|
|
||||||
return;
|
|
||||||
unregister_kprobe(kp);
|
|
||||||
synchronize_rcu();
|
|
||||||
kfree(kp);
|
|
||||||
*kp_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
|
||||||
void ksu_sucompat_init()
|
void ksu_sucompat_init()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
pr_err("Failed to register su_compat feature handler\n");
|
||||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
}
|
||||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
|
||||||
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
|
||||||
#else
|
|
||||||
ksu_sucompat_hook_state = true;
|
|
||||||
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sucompat_exit()
|
void ksu_sucompat_exit()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
||||||
int i;
|
}
|
||||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
|
||||||
destroy_kprobe(&su_kps[i]);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
ksu_sucompat_hook_state = false;
|
|
||||||
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
18
kernel/sucompat.h
Normal file
18
kernel/sucompat.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __KSU_H_SUCOMPAT
|
||||||
|
#define __KSU_H_SUCOMPAT
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
extern bool ksu_su_compat_enabled;
|
||||||
|
|
||||||
|
void ksu_sucompat_init(void);
|
||||||
|
void ksu_sucompat_exit(void);
|
||||||
|
|
||||||
|
// Handler functions exported for hook_manager
|
||||||
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
|
||||||
|
int *mode, int *__unused_flags);
|
||||||
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||||
|
int ksu_handle_execve_sucompat(const char __user **filename_user,
|
||||||
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags);
|
||||||
|
|
||||||
|
#endif
|
||||||
369
kernel/sulog.c
Normal file
369
kernel/sulog.c
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
|
||||||
|
#include "sulog.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "feature.h"
|
||||||
|
|
||||||
|
#if __SULOG_GATE
|
||||||
|
|
||||||
|
struct dedup_entry dedup_tbl[SULOG_COMM_LEN];
|
||||||
|
static DEFINE_SPINLOCK(dedup_lock);
|
||||||
|
static LIST_HEAD(sulog_queue);
|
||||||
|
static struct workqueue_struct *sulog_workqueue;
|
||||||
|
static struct work_struct sulog_work;
|
||||||
|
static bool sulog_enabled __read_mostly = true;
|
||||||
|
|
||||||
|
static int sulog_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = sulog_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sulog_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
sulog_enabled = enable;
|
||||||
|
pr_info("sulog: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler sulog_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_SULOG,
|
||||||
|
.name = "sulog",
|
||||||
|
.get_handler = sulog_feature_get,
|
||||||
|
.set_handler = sulog_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void get_timestamp(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct timespec64 ts;
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
ktime_get_real_ts64(&ts);
|
||||||
|
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
|
||||||
|
|
||||||
|
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
|
||||||
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
|
||||||
|
{
|
||||||
|
if (!full_comm || buf_len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (comm && strlen(comm) > 0) {
|
||||||
|
KSU_STRSCPY(full_comm, comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_atomic() || in_interrupt() || irqs_disabled()) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current->mm) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = get_cmdline(current, full_comm, buf_len);
|
||||||
|
if (n <= 0) {
|
||||||
|
KSU_STRSCPY(full_comm, current->comm, buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < n && i < buf_len - 1; i++) {
|
||||||
|
if (full_comm[i] == '\0')
|
||||||
|
full_comm[i] = ' ';
|
||||||
|
}
|
||||||
|
full_comm[n < buf_len ? n : buf_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sanitize_string(char *str, size_t len)
|
||||||
|
{
|
||||||
|
if (!str || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t read_pos = 0, write_pos = 0;
|
||||||
|
|
||||||
|
while (read_pos < len && str[read_pos] != '\0') {
|
||||||
|
char c = str[read_pos];
|
||||||
|
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
read_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') {
|
||||||
|
read_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[write_pos++] = c;
|
||||||
|
read_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[write_pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len)
|
||||||
|
{
|
||||||
|
struct dedup_key key = {
|
||||||
|
.crc = dedup_calc_hash(content, len),
|
||||||
|
.uid = uid,
|
||||||
|
.type = type,
|
||||||
|
};
|
||||||
|
u64 now = ktime_get_ns();
|
||||||
|
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
|
||||||
|
|
||||||
|
u32 idx = key.crc & (SULOG_COMM_LEN - 1);
|
||||||
|
spin_lock(&dedup_lock);
|
||||||
|
|
||||||
|
struct dedup_entry *e = &dedup_tbl[idx];
|
||||||
|
if (e->key.crc == key.crc &&
|
||||||
|
e->key.uid == key.uid &&
|
||||||
|
e->key.type == key.type &&
|
||||||
|
(now - e->ts_ns) < delta_ns) {
|
||||||
|
spin_unlock(&dedup_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e->key = key;
|
||||||
|
e->ts_ns = now;
|
||||||
|
spin_unlock(&dedup_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_work_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
LIST_HEAD(local_queue);
|
||||||
|
loff_t pos = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_splice_init(&sulog_queue, &local_queue);
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
if (list_empty(&local_queue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = filp_open(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
|
||||||
|
if (vfs_truncate(&fp->f_path, 0))
|
||||||
|
pr_err("sulog: failed to truncate log file\n");
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
pos = fp->f_inode->i_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &local_queue, list)
|
||||||
|
kernel_write(fp, entry->content, strlen(entry->content), &pos);
|
||||||
|
|
||||||
|
vfs_fsync(fp, 0);
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!sulog_enabled || !log_buf || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!dedup_should_print(uid, dedup_type, log_buf, len))
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_add_tail(&entry->list, &sulog_queue);
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
if (sulog_workqueue)
|
||||||
|
queue_work(sulog_workqueue, &sulog_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, method ? method : "unknown", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, target_path ? target_path : "unknown",
|
||||||
|
success ? "SUCCESS" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, NULL, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
|
||||||
|
timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args)
|
||||||
|
{
|
||||||
|
char log_buf[SULOG_ENTRY_MAX_LEN];
|
||||||
|
char timestamp[32];
|
||||||
|
char full_comm[SULOG_COMM_LEN];
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_timestamp(timestamp, sizeof(timestamp));
|
||||||
|
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
sanitize_string(full_comm, sizeof(full_comm));
|
||||||
|
|
||||||
|
snprintf(log_buf, sizeof(log_buf),
|
||||||
|
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm, syscall ? syscall : "unknown",
|
||||||
|
args ? args : "none", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_sulog_init(void)
|
||||||
|
{
|
||||||
|
if (ksu_register_feature_handler(&sulog_handler)) {
|
||||||
|
pr_err("Failed to register sulog feature handler\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
||||||
|
if (!sulog_workqueue) {
|
||||||
|
pr_err("sulog: failed to create workqueue\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&sulog_work, sulog_work_handler);
|
||||||
|
pr_info("sulog: initialized successfully\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_exit(void)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_SULOG);
|
||||||
|
|
||||||
|
sulog_enabled = false;
|
||||||
|
|
||||||
|
if (sulog_workqueue) {
|
||||||
|
flush_workqueue(sulog_workqueue);
|
||||||
|
destroy_workqueue(sulog_workqueue);
|
||||||
|
sulog_workqueue = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dedup_lock, flags);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dedup_lock, flags);
|
||||||
|
|
||||||
|
pr_info("sulog: cleaned up successfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __SULOG_GATE
|
||||||
93
kernel/sulog.h
Normal file
93
kernel/sulog.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#ifndef __KSU_SULOG_H
|
||||||
|
#define __KSU_SULOG_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/crc32.h> // needed for function dedup_calc_hash
|
||||||
|
|
||||||
|
#define __SULOG_GATE 1
|
||||||
|
|
||||||
|
#if __SULOG_GATE
|
||||||
|
|
||||||
|
extern struct timezone sys_tz;
|
||||||
|
|
||||||
|
#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
|
||||||
|
#define SULOG_MAX_SIZE (32 * 1024 * 1024) // 128MB
|
||||||
|
#define SULOG_ENTRY_MAX_LEN 512
|
||||||
|
#define SULOG_COMM_LEN 256
|
||||||
|
#define DEDUP_SECS 10
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
|
||||||
|
static inline size_t strlcpy(char *dest, const char *src, size_t size)
|
||||||
|
{
|
||||||
|
return strscpy(dest, src, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KSU_STRSCPY(dst, src, size) \
|
||||||
|
do { \
|
||||||
|
if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) { \
|
||||||
|
strscpy(dst, src, size); \
|
||||||
|
} else { \
|
||||||
|
strlcpy(dst, src, size); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
|
||||||
|
{
|
||||||
|
struct rtc_time rtc_tm;
|
||||||
|
rtc_time64_to_tm(totalsecs, &rtc_tm);
|
||||||
|
|
||||||
|
result->tm_sec = rtc_tm.tm_sec;
|
||||||
|
result->tm_min = rtc_tm.tm_min;
|
||||||
|
result->tm_hour = rtc_tm.tm_hour;
|
||||||
|
result->tm_mday = rtc_tm.tm_mday;
|
||||||
|
result->tm_mon = rtc_tm.tm_mon;
|
||||||
|
result->tm_year = rtc_tm.tm_year;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct dedup_key {
|
||||||
|
u32 crc;
|
||||||
|
uid_t uid;
|
||||||
|
u8 type;
|
||||||
|
u8 _pad[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dedup_entry {
|
||||||
|
struct dedup_key key;
|
||||||
|
u64 ts_ns;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DEDUP_SU_GRANT = 0,
|
||||||
|
DEDUP_SU_ATTEMPT,
|
||||||
|
DEDUP_PERM_CHECK,
|
||||||
|
DEDUP_MANAGER_OP,
|
||||||
|
DEDUP_SYSCALL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u32 dedup_calc_hash(const char *content, size_t len)
|
||||||
|
{
|
||||||
|
return crc32(0, content, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sulog_entry {
|
||||||
|
struct list_head list;
|
||||||
|
char content[SULOG_ENTRY_MAX_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
|
||||||
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success);
|
||||||
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed);
|
||||||
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid);
|
||||||
|
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args);
|
||||||
|
|
||||||
|
int ksu_sulog_init(void);
|
||||||
|
void ksu_sulog_exit(void);
|
||||||
|
#endif // __SULOG_GATE
|
||||||
|
|
||||||
|
#endif /* __KSU_SULOG_H */
|
||||||
1072
kernel/supercalls.c
Normal file
1072
kernel/supercalls.c
Normal file
File diff suppressed because it is too large
Load Diff
197
kernel/supercalls.h
Normal file
197
kernel/supercalls.h
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#ifndef __KSU_H_SUPERCALLS
|
||||||
|
#define __KSU_H_SUPERCALLS
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ioctl.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPM
|
||||||
|
#include "kpm/kpm.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Magic numbers for reboot hook to install fd
|
||||||
|
#define KSU_INSTALL_MAGIC1 0xDEADBEEF
|
||||||
|
#define KSU_INSTALL_MAGIC2 0xCAFEBABE
|
||||||
|
|
||||||
|
// Command structures for ioctl
|
||||||
|
|
||||||
|
struct ksu_become_daemon_cmd {
|
||||||
|
__u8 token[65]; // Input: daemon token (null-terminated)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_info_cmd {
|
||||||
|
__u32 version; // Output: KERNEL_SU_VERSION
|
||||||
|
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
||||||
|
__u32 features; // Output: max feature ID supported
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_report_event_cmd {
|
||||||
|
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_set_sepolicy_cmd {
|
||||||
|
__u64 cmd; // Input: sepolicy command
|
||||||
|
__aligned_u64 arg; // Input: sepolicy argument pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_check_safemode_cmd {
|
||||||
|
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_allow_list_cmd {
|
||||||
|
__u32 uids[128]; // Output: array of allowed/denied UIDs
|
||||||
|
__u32 count; // Output: number of UIDs in array
|
||||||
|
__u8 allow; // Input: true for allow list, false for deny list
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_uid_granted_root_cmd {
|
||||||
|
__u32 uid; // Input: target UID to check
|
||||||
|
__u8 granted; // Output: true if granted, false otherwise
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_uid_should_umount_cmd {
|
||||||
|
__u32 uid; // Input: target UID to check
|
||||||
|
__u8 should_umount; // Output: true if should umount, false otherwise
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_manager_uid_cmd {
|
||||||
|
__u32 uid; // Output: manager UID
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_app_profile_cmd {
|
||||||
|
struct app_profile profile; // Input/Output: app profile structure
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_set_app_profile_cmd {
|
||||||
|
struct app_profile profile; // Input: app profile structure
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_feature_cmd {
|
||||||
|
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
|
||||||
|
__u64 value; // Output: feature value/state
|
||||||
|
__u8 supported; // Output: true if feature is supported, false otherwise
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_set_feature_cmd {
|
||||||
|
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
|
||||||
|
__u64 value; // Input: feature value/state to set
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_wrapper_fd_cmd {
|
||||||
|
__u32 fd; // Input: userspace fd
|
||||||
|
__u32 flags; // Input: flags of userspace fd
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_manage_mark_cmd {
|
||||||
|
__u32 operation; // Input: KSU_MARK_*
|
||||||
|
__s32 pid; // Input: target pid (0 for all processes)
|
||||||
|
__u32 result; // Output: for get operation - mark status or reg_count
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KSU_MARK_GET 1
|
||||||
|
#define KSU_MARK_MARK 2
|
||||||
|
#define KSU_MARK_UNMARK 3
|
||||||
|
#define KSU_MARK_REFRESH 4
|
||||||
|
|
||||||
|
struct ksu_nuke_ext4_sysfs_cmd {
|
||||||
|
__aligned_u64 arg; // Input: mnt pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_add_try_umount_cmd {
|
||||||
|
__aligned_u64 arg; // char ptr, this is the mountpoint
|
||||||
|
__u32 flags; // this is the flag we use for it
|
||||||
|
__u8 mode; // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KSU_UMOUNT_WIPE 0 // ignore everything and wipe list
|
||||||
|
#define KSU_UMOUNT_ADD 1 // add entry (path + flags)
|
||||||
|
#define KSU_UMOUNT_DEL 2 // delete entry, strcmp
|
||||||
|
|
||||||
|
|
||||||
|
// Other command structures
|
||||||
|
struct ksu_get_full_version_cmd {
|
||||||
|
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_hook_type_cmd {
|
||||||
|
char hook_type[32]; // Output: hook type string
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_enable_kpm_cmd {
|
||||||
|
__u8 enabled; // Output: true if KPM is enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_dynamic_manager_cmd {
|
||||||
|
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_get_managers_cmd {
|
||||||
|
struct manager_list_info manager_info; // Output: manager list information
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_enable_uid_scanner_cmd {
|
||||||
|
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
|
||||||
|
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
|
||||||
|
void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
struct ksu_manual_su_cmd {
|
||||||
|
__u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING)
|
||||||
|
__u32 target_uid; // Input: target UID
|
||||||
|
__u32 target_pid; // Input: target PID
|
||||||
|
char token_buffer[33]; // Input/Output: token buffer
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// IOCTL command definitions
|
||||||
|
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
|
||||||
|
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
|
||||||
|
#define KSU_IOCTL_REPORT_EVENT _IOC(_IOC_WRITE, 'K', 3, 0)
|
||||||
|
#define KSU_IOCTL_SET_SEPOLICY _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0)
|
||||||
|
#define KSU_IOCTL_CHECK_SAFEMODE _IOC(_IOC_READ, 'K', 5, 0)
|
||||||
|
#define KSU_IOCTL_GET_ALLOW_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 6, 0)
|
||||||
|
#define KSU_IOCTL_GET_DENY_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 7, 0)
|
||||||
|
#define KSU_IOCTL_UID_GRANTED_ROOT _IOC(_IOC_READ|_IOC_WRITE, 'K', 8, 0)
|
||||||
|
#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOC(_IOC_READ|_IOC_WRITE, 'K', 9, 0)
|
||||||
|
#define KSU_IOCTL_GET_MANAGER_UID _IOC(_IOC_READ, 'K', 10, 0)
|
||||||
|
#define KSU_IOCTL_GET_APP_PROFILE _IOC(_IOC_READ|_IOC_WRITE, 'K', 11, 0)
|
||||||
|
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
|
||||||
|
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
|
||||||
|
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
|
||||||
|
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
|
||||||
|
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
|
||||||
|
#define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0)
|
||||||
|
#define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0)
|
||||||
|
// Other IOCTL command definitions
|
||||||
|
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
||||||
|
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
||||||
|
#define KSU_IOCTL_ENABLE_KPM _IOC(_IOC_READ, 'K', 102, 0)
|
||||||
|
#define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0)
|
||||||
|
#define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0)
|
||||||
|
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0)
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
#define KSU_IOCTL_MANUAL_SU _IOC(_IOC_READ|_IOC_WRITE, 'K', 106, 0)
|
||||||
|
#endif
|
||||||
|
#define KSU_IOCTL_UMOUNT_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 107, 0)
|
||||||
|
|
||||||
|
// IOCTL handler types
|
||||||
|
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
|
||||||
|
typedef bool (*ksu_perm_check_t)(void);
|
||||||
|
|
||||||
|
// IOCTL command mapping
|
||||||
|
struct ksu_ioctl_cmd_map {
|
||||||
|
unsigned int cmd;
|
||||||
|
const char *name;
|
||||||
|
ksu_ioctl_handler_t handler;
|
||||||
|
ksu_perm_check_t perm_check; // Permission check function
|
||||||
|
};
|
||||||
|
|
||||||
|
// Install KSU fd to current process
|
||||||
|
int ksu_install_fd(void);
|
||||||
|
|
||||||
|
void ksu_supercalls_init(void);
|
||||||
|
void ksu_supercalls_exit(void);
|
||||||
|
|
||||||
|
#endif // __KSU_H_SUPERCALLS
|
||||||
385
kernel/syscall_hook_manager.c
Normal file
385
kernel/syscall_hook_manager.c
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
#include "linux/compiler.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
#include "linux/printk.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
#include <asm/syscall.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <trace/events/syscalls.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// Tracepoint registration count management
|
||||||
|
// == 1: just us
|
||||||
|
// > 1: someone else is also using syscall tracepoint e.g. ftrace
|
||||||
|
static int tracepoint_reg_count = 0;
|
||||||
|
static DEFINE_SPINLOCK(tracepoint_reg_lock);
|
||||||
|
|
||||||
|
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count <= 1) {
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process marking management
|
||||||
|
static void handle_process_mark(bool mark)
|
||||||
|
{
|
||||||
|
struct task_struct *p, *t;
|
||||||
|
read_lock(&tasklist_lock);
|
||||||
|
for_each_process_thread(p, t) {
|
||||||
|
if (mark)
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
else
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
read_unlock(&tasklist_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_mark_all_process(void)
|
||||||
|
{
|
||||||
|
handle_process_mark(true);
|
||||||
|
pr_info("hook_manager: mark all user process done!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_unmark_all_process(void)
|
||||||
|
{
|
||||||
|
handle_process_mark(false);
|
||||||
|
pr_info("hook_manager: unmark all user process done!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_mark_running_process_locked()
|
||||||
|
{
|
||||||
|
struct task_struct *p, *t;
|
||||||
|
read_lock(&tasklist_lock);
|
||||||
|
for_each_process_thread (p, t) {
|
||||||
|
if (!t->mm) { // only user processes
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int uid = task_uid(t).val;
|
||||||
|
const struct cred *cred = get_task_cred(t);
|
||||||
|
bool ksu_root_process =
|
||||||
|
uid == 0 && is_task_ksu_domain(cred);
|
||||||
|
bool is_zygote_process = is_zygote(cred);
|
||||||
|
bool is_shell = uid == 2000;
|
||||||
|
// before boot completed, we shall mark init for marking zygote
|
||||||
|
bool is_init = t->pid == 1;
|
||||||
|
if (ksu_root_process || is_zygote_process || is_shell || is_init
|
||||||
|
|| ksu_is_allow_uid(uid)) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
|
||||||
|
t->pid, uid, t->comm);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n",
|
||||||
|
t->pid, uid, t->comm);
|
||||||
|
}
|
||||||
|
put_cred(cred);
|
||||||
|
}
|
||||||
|
read_unlock(&tasklist_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_mark_running_process()
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count <= 1) {
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
} else {
|
||||||
|
pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n");
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get task mark status
|
||||||
|
// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found
|
||||||
|
int ksu_get_task_mark(pid_t pid)
|
||||||
|
{
|
||||||
|
struct task_struct *task;
|
||||||
|
int marked = -ESRCH;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
task = find_task_by_vpid(pid);
|
||||||
|
if (task) {
|
||||||
|
get_task_struct(task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 : 0;
|
||||||
|
#else
|
||||||
|
marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ? 1 : 0;
|
||||||
|
#endif
|
||||||
|
put_task_struct(task);
|
||||||
|
} else {
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return marked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set task mark status
|
||||||
|
// Returns: 0 on success, -ESRCH if task not found
|
||||||
|
int ksu_set_task_mark(pid_t pid, bool mark)
|
||||||
|
{
|
||||||
|
struct task_struct *task;
|
||||||
|
int ret = -ESRCH;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
task = find_task_by_vpid(pid);
|
||||||
|
if (task) {
|
||||||
|
get_task_struct(task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (mark) {
|
||||||
|
ksu_set_task_tracepoint_flag(task);
|
||||||
|
pr_info("hook_manager: marked task pid=%d comm=%s\n", pid, task->comm);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag(task);
|
||||||
|
pr_info("hook_manager: unmarked task pid=%d comm=%s\n", pid, task->comm);
|
||||||
|
}
|
||||||
|
put_task_struct(task);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
|
||||||
|
static struct kretprobe *init_kretprobe(const char *name,
|
||||||
|
kretprobe_handler_t handler)
|
||||||
|
{
|
||||||
|
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
|
||||||
|
if (!rp)
|
||||||
|
return NULL;
|
||||||
|
rp->kp.symbol_name = name;
|
||||||
|
rp->handler = handler;
|
||||||
|
rp->data_size = 0;
|
||||||
|
rp->maxactive = 0;
|
||||||
|
|
||||||
|
int ret = register_kretprobe(rp);
|
||||||
|
pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret);
|
||||||
|
if (ret) {
|
||||||
|
kfree(rp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_kretprobe(struct kretprobe **rp_ptr)
|
||||||
|
{
|
||||||
|
struct kretprobe *rp = *rp_ptr;
|
||||||
|
if (!rp)
|
||||||
|
return;
|
||||||
|
unregister_kretprobe(rp);
|
||||||
|
synchronize_rcu();
|
||||||
|
kfree(rp);
|
||||||
|
*rp_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int syscall_regfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count < 1) {
|
||||||
|
// while install our tracepoint, mark our processes
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
} else if (tracepoint_reg_count == 1) {
|
||||||
|
// while other tracepoint first added, mark all processes
|
||||||
|
ksu_mark_all_process();
|
||||||
|
}
|
||||||
|
tracepoint_reg_count++;
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int syscall_unregfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
tracepoint_reg_count--;
|
||||||
|
if (tracepoint_reg_count <= 0) {
|
||||||
|
// while no tracepoint left, unmark all processes
|
||||||
|
ksu_unmark_all_process();
|
||||||
|
} else if (tracepoint_reg_count == 1) {
|
||||||
|
// while just our tracepoint left, unmark disallowed processes
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kretprobe *syscall_regfunc_rp = NULL;
|
||||||
|
static struct kretprobe *syscall_unregfunc_rp = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline bool check_syscall_fastpath(int nr)
|
||||||
|
{
|
||||||
|
switch (nr) {
|
||||||
|
case __NR_newfstatat:
|
||||||
|
case __NR_faccessat:
|
||||||
|
case __NR_execve:
|
||||||
|
case __NR_setresuid:
|
||||||
|
case __NR_clone:
|
||||||
|
case __NR_clone3:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmark init's child that are not zygote, adbd or ksud
|
||||||
|
int ksu_handle_init_mark_tracker(const char __user **filename_user)
|
||||||
|
{
|
||||||
|
char path[64];
|
||||||
|
unsigned long addr;
|
||||||
|
const char __user *fn;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
addr = untagged_addr((unsigned long)*filename_user);
|
||||||
|
fn = (const char __user *)addr;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
|
||||||
|
if (ret < 0 && try_set_access_flag(addr)) {
|
||||||
|
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
|
||||||
|
pr_info("ksu_handle_init_mark_tracker: %ld\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(strstr(path, "/app_process") == NULL && strstr(path, "/adbd") == NULL && strstr(path, "/ksud") == NULL)) {
|
||||||
|
pr_info("hook_manager: unmark %d exec %s", current->pid, path);
|
||||||
|
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
#include "manual_su.h"
|
||||||
|
static inline void ksu_handle_task_alloc(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
ksu_try_escalate_for_uid(current_uid().val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
// Generic sys_enter handler that dispatches to specific handlers
|
||||||
|
static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
|
||||||
|
{
|
||||||
|
if (unlikely(check_syscall_fastpath(id))) {
|
||||||
|
#ifdef KSU_TP_HOOK
|
||||||
|
if (ksu_su_compat_enabled) {
|
||||||
|
// Handle newfstatat
|
||||||
|
if (id == __NR_newfstatat) {
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM2(regs);
|
||||||
|
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
|
||||||
|
ksu_handle_stat(dfd, filename_user, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle faccessat
|
||||||
|
if (id == __NR_faccessat) {
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM2(regs);
|
||||||
|
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||||
|
ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle execve
|
||||||
|
if (id == __NR_execve) {
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM1(regs);
|
||||||
|
if (current->pid != 1 && is_init(get_current_cred())) {
|
||||||
|
ksu_handle_init_mark_tracker(filename_user);
|
||||||
|
} else {
|
||||||
|
ksu_handle_execve_sucompat(filename_user, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle setresuid
|
||||||
|
if (id == __NR_setresuid) {
|
||||||
|
uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
|
||||||
|
uid_t euid = (uid_t)PT_REGS_PARM2(regs);
|
||||||
|
uid_t suid = (uid_t)PT_REGS_PARM3(regs);
|
||||||
|
ksu_handle_setresuid(ruid, euid, suid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
// Handle task_alloc via clone/fork
|
||||||
|
if (id == __NR_clone || id == __NR_clone3)
|
||||||
|
return ksu_handle_task_alloc(regs);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_init called\n");
|
||||||
|
|
||||||
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
// Register kretprobe for syscall_regfunc
|
||||||
|
syscall_regfunc_rp = init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
|
||||||
|
// Register kretprobe for syscall_unregfunc
|
||||||
|
syscall_unregfunc_rp = init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
#ifndef CONFIG_KRETPROBES
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
#endif
|
||||||
|
if (ret) {
|
||||||
|
pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n", ret);
|
||||||
|
} else {
|
||||||
|
pr_info("hook_manager: sys_enter tracepoint registered\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_setuid_hook_init();
|
||||||
|
ksu_sucompat_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_exit called\n");
|
||||||
|
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
tracepoint_synchronize_unregister();
|
||||||
|
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KRETPROBES
|
||||||
|
destroy_kretprobe(&syscall_regfunc_rp);
|
||||||
|
destroy_kretprobe(&syscall_unregfunc_rp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
ksu_setuid_hook_exit();
|
||||||
|
}
|
||||||
47
kernel/syscall_hook_manager.h
Normal file
47
kernel/syscall_hook_manager.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef __KSU_H_HOOK_MANAGER
|
||||||
|
#define __KSU_H_HOOK_MANAGER
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
|
||||||
|
// Hook manager initialization and cleanup
|
||||||
|
void ksu_syscall_hook_manager_init(void);
|
||||||
|
void ksu_syscall_hook_manager_exit(void);
|
||||||
|
|
||||||
|
// Process marking for tracepoint
|
||||||
|
void ksu_mark_all_process(void);
|
||||||
|
void ksu_unmark_all_process(void);
|
||||||
|
void ksu_mark_running_process(void);
|
||||||
|
|
||||||
|
// Per-task mark operations
|
||||||
|
int ksu_get_task_mark(pid_t pid);
|
||||||
|
int ksu_set_task_mark(pid_t pid, bool mark);
|
||||||
|
|
||||||
|
|
||||||
|
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
||||||
|
#else
|
||||||
|
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
||||||
|
#else
|
||||||
|
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,119 +7,208 @@
|
|||||||
|
|
||||||
#include "klog.h"
|
#include "klog.h"
|
||||||
#include "throne_comm.h"
|
#include "throne_comm.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
||||||
|
#define UID_SCANNER_STATE_FILE "/data/adb/ksu/.uid_scanner"
|
||||||
|
|
||||||
static struct proc_dir_entry *proc_entry = NULL;
|
static struct proc_dir_entry *proc_entry = NULL;
|
||||||
static struct workqueue_struct *scanner_wq = NULL;
|
static struct workqueue_struct *scanner_wq = NULL;
|
||||||
static struct work_struct scan_work;
|
static struct work_struct scan_work;
|
||||||
|
static struct work_struct ksu_state_save_work;
|
||||||
|
static struct work_struct ksu_state_load_work;
|
||||||
|
|
||||||
|
|
||||||
// Signal userspace to rescan
|
// Signal userspace to rescan
|
||||||
static bool need_rescan = false;
|
static bool need_rescan = false;
|
||||||
|
|
||||||
static void rescan_work_fn(struct work_struct *work)
|
static void rescan_work_fn(struct work_struct *work)
|
||||||
{
|
{
|
||||||
// Signal userspace through proc interface
|
// Signal userspace through proc interface
|
||||||
need_rescan = true;
|
need_rescan = true;
|
||||||
pr_info("requested userspace uid rescan\n");
|
pr_info("requested userspace uid rescan\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_request_userspace_scan(void)
|
void ksu_request_userspace_scan(void)
|
||||||
{
|
{
|
||||||
if (scanner_wq) {
|
if (scanner_wq) {
|
||||||
queue_work(scanner_wq, &scan_work);
|
queue_work(scanner_wq, &scan_work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_handle_userspace_update(void)
|
void ksu_handle_userspace_update(void)
|
||||||
{
|
{
|
||||||
// Called when userspace notifies update complete
|
// Called when userspace notifies update complete
|
||||||
need_rescan = false;
|
need_rescan = false;
|
||||||
pr_info("userspace uid list updated\n");
|
pr_info("userspace uid list updated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_save_throne_state(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
char state_char = ksu_uid_scanner_enabled ? '1' : '0';
|
||||||
|
loff_t off = 0;
|
||||||
|
|
||||||
|
fp = filp_open(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernel_write(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
|
||||||
|
pr_err("save_throne_state write failed\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_load_throne_state(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
char state_char;
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
fp = filp_open(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_info("throne state file not found, using default: disabled\n");
|
||||||
|
ksu_uid_scanner_enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kernel_read(fp, &state_char, sizeof(state_char), &off);
|
||||||
|
if (ret != sizeof(state_char)) {
|
||||||
|
pr_err("load_throne_state read err: %zd\n", ret);
|
||||||
|
ksu_uid_scanner_enabled = false;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_uid_scanner_enabled = (state_char == '1');
|
||||||
|
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_throne_comm_load_state(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&ksu_state_load_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_comm_save_state(void)
|
||||||
|
{
|
||||||
|
ksu_queue_work(&ksu_state_save_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uid_scanner_show(struct seq_file *m, void *v)
|
static int uid_scanner_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
if (need_rescan) {
|
if (need_rescan) {
|
||||||
seq_puts(m, "RESCAN\n");
|
seq_puts(m, "RESCAN\n");
|
||||||
} else {
|
} else {
|
||||||
seq_puts(m, "OK\n");
|
seq_puts(m, "OK\n");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uid_scanner_open(struct inode *inode, struct file *file)
|
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
return single_open(file, uid_scanner_show, NULL);
|
return single_open(file, uid_scanner_show, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||||
size_t count, loff_t *pos)
|
size_t count, loff_t *pos)
|
||||||
{
|
{
|
||||||
char cmd[16];
|
char cmd[16];
|
||||||
|
|
||||||
if (count >= sizeof(cmd))
|
if (count >= sizeof(cmd))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (copy_from_user(cmd, buffer, count))
|
if (copy_from_user(cmd, buffer, count))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
cmd[count] = '\0';
|
cmd[count] = '\0';
|
||||||
|
|
||||||
// Remove newline if present
|
// Remove newline if present
|
||||||
if (count > 0 && cmd[count-1] == '\n')
|
if (count > 0 && cmd[count-1] == '\n')
|
||||||
cmd[count-1] = '\0';
|
cmd[count-1] = '\0';
|
||||||
|
|
||||||
if (strcmp(cmd, "UPDATED") == 0) {
|
if (strcmp(cmd, "UPDATED") == 0) {
|
||||||
ksu_handle_userspace_update();
|
ksu_handle_userspace_update();
|
||||||
pr_info("received userspace update notification\n");
|
pr_info("received userspace update notification\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
||||||
static const struct proc_ops uid_scanner_proc_ops = {
|
static const struct proc_ops uid_scanner_proc_ops = {
|
||||||
.proc_open = uid_scanner_open,
|
.proc_open = uid_scanner_open,
|
||||||
.proc_read = seq_read,
|
.proc_read = seq_read,
|
||||||
.proc_write = uid_scanner_write,
|
.proc_write = uid_scanner_write,
|
||||||
.proc_lseek = seq_lseek,
|
.proc_lseek = seq_lseek,
|
||||||
.proc_release = single_release,
|
.proc_release = single_release,
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
static const struct file_operations uid_scanner_proc_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = uid_scanner_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.write = uid_scanner_write,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
int ksu_throne_comm_init(void)
|
int ksu_throne_comm_init(void)
|
||||||
{
|
{
|
||||||
// Create workqueue
|
// Create workqueue
|
||||||
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||||
if (!scanner_wq) {
|
if (!scanner_wq) {
|
||||||
pr_err("failed to create scanner workqueue\n");
|
pr_err("failed to create scanner workqueue\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&scan_work, rescan_work_fn);
|
INIT_WORK(&scan_work, rescan_work_fn);
|
||||||
|
|
||||||
// Create proc entry
|
// Create proc entry
|
||||||
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||||
if (!proc_entry) {
|
if (!proc_entry) {
|
||||||
pr_err("failed to create proc entry\n");
|
pr_err("failed to create proc entry\n");
|
||||||
destroy_workqueue(scanner_wq);
|
destroy_workqueue(scanner_wq);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("throne communication initialized\n");
|
pr_info("throne communication initialized\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_throne_comm_exit(void)
|
void ksu_throne_comm_exit(void)
|
||||||
{
|
{
|
||||||
if (proc_entry) {
|
if (proc_entry) {
|
||||||
proc_remove(proc_entry);
|
proc_remove(proc_entry);
|
||||||
proc_entry = NULL;
|
proc_entry = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanner_wq) {
|
if (scanner_wq) {
|
||||||
destroy_workqueue(scanner_wq);
|
destroy_workqueue(scanner_wq);
|
||||||
scanner_wq = NULL;
|
scanner_wq = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("throne communication cleaned up\n");
|
pr_info("throne communication cleaned up\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_uid_init(void)
|
||||||
|
{
|
||||||
|
INIT_WORK(&ksu_state_save_work, do_save_throne_state);
|
||||||
|
INIT_WORK(&ksu_state_load_work, do_load_throne_state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_uid_exit(void)
|
||||||
|
{
|
||||||
|
do_save_throne_state(NULL);
|
||||||
}
|
}
|
||||||
@@ -9,4 +9,14 @@ int ksu_throne_comm_init(void);
|
|||||||
|
|
||||||
void ksu_throne_comm_exit(void);
|
void ksu_throne_comm_exit(void);
|
||||||
|
|
||||||
|
int ksu_uid_init(void);
|
||||||
|
|
||||||
|
void ksu_uid_exit(void);
|
||||||
|
|
||||||
|
bool ksu_throne_comm_load_state(void);
|
||||||
|
|
||||||
|
void ksu_throne_comm_save_state(void);
|
||||||
|
|
||||||
|
void do_load_throne_state(struct work_struct *work);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,6 @@ void ksu_throne_tracker_init();
|
|||||||
|
|
||||||
void ksu_throne_tracker_exit();
|
void ksu_throne_tracker_exit();
|
||||||
|
|
||||||
void track_throne();
|
void track_throne(bool prune_only);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
353
kernel/umount_manager.c
Normal file
353
kernel/umount_manager.c
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "umount_manager.h"
|
||||||
|
|
||||||
|
static struct umount_manager g_umount_mgr = {
|
||||||
|
.entry_count = 0,
|
||||||
|
.max_entries = 512,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void try_umount_path(struct umount_entry *entry)
|
||||||
|
{
|
||||||
|
try_umount(entry->path, entry->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct umount_entry *find_entry_locked(const char *path)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (strcmp(entry->path, path) == 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_path_in_mount_list(const char *path)
|
||||||
|
{
|
||||||
|
struct mount_entry *entry;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
list_for_each_entry(entry, &mount_list, list) {
|
||||||
|
if (entry->umountable && strcmp(entry->umountable, path) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_mount_entry_to_user(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 idx, const char *path, int flags)
|
||||||
|
{
|
||||||
|
struct ksu_umount_entry_info info;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
strncpy(info.path, path, sizeof(info.path) - 1);
|
||||||
|
info.path[sizeof(info.path) - 1] = '\0';
|
||||||
|
info.flags = flags;
|
||||||
|
info.is_default = 1;
|
||||||
|
info.state = UMOUNT_STATE_IDLE;
|
||||||
|
info.ref_count = 0;
|
||||||
|
|
||||||
|
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_umount_entry_to_user(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 idx, struct umount_entry *entry)
|
||||||
|
{
|
||||||
|
struct ksu_umount_entry_info info;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
strncpy(info.path, entry->path, sizeof(info.path) - 1);
|
||||||
|
info.path[sizeof(info.path) - 1] = '\0';
|
||||||
|
info.flags = entry->flags;
|
||||||
|
info.is_default = entry->is_default;
|
||||||
|
info.state = entry->state;
|
||||||
|
info.ref_count = entry->ref_count;
|
||||||
|
|
||||||
|
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_mount_list_entries(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 max_count, u32 *out_idx)
|
||||||
|
{
|
||||||
|
struct mount_entry *mount_entry;
|
||||||
|
u32 idx = 0;
|
||||||
|
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
list_for_each_entry(mount_entry, &mount_list, list) {
|
||||||
|
if (idx >= max_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mount_entry->umountable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_mount_entry_to_user(entries, idx, mount_entry->umountable,
|
||||||
|
mount_entry->flags)) {
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
*out_idx = idx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_umount_manager_entries(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 start_idx, u32 max_count, u32 *out_idx)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 idx = start_idx;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (idx >= max_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_path_in_mount_list(entry->path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
if (copy_umount_entry_to_user(entries, idx, entry)) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
*out_idx = idx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_umount_manager_init(void)
|
||||||
|
{
|
||||||
|
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
|
||||||
|
spin_lock_init(&g_umount_mgr.lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_umount_manager_exit(void)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry, *tmp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(entry, tmp, &g_umount_mgr.entry_list, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
g_umount_mgr.entry_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
pr_info("Umount manager cleaned up\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_umount_manager_add(const char *path, int flags, bool is_default)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
unsigned long irqflags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (flags == -1)
|
||||||
|
flags = MNT_DETACH;
|
||||||
|
|
||||||
|
if (!path || strlen(path) == 0 || strlen(path) >= 256) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_path_in_mount_list(path)) {
|
||||||
|
pr_warn("Umount manager: path already exists in mount_list: %s\n", path);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, irqflags);
|
||||||
|
|
||||||
|
if (g_umount_mgr.entry_count >= g_umount_mgr.max_entries) {
|
||||||
|
pr_err("Umount manager: max entries reached\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_entry_locked(path)) {
|
||||||
|
pr_warn("Umount manager: path already exists: %s\n", path);
|
||||||
|
ret = -EEXIST;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
|
if (!entry) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(entry->path, path, sizeof(entry->path) - 1);
|
||||||
|
entry->flags = flags;
|
||||||
|
entry->state = UMOUNT_STATE_IDLE;
|
||||||
|
entry->is_default = is_default;
|
||||||
|
entry->ref_count = 0;
|
||||||
|
|
||||||
|
list_add_tail(&entry->list, &g_umount_mgr.entry_list);
|
||||||
|
g_umount_mgr.entry_count++;
|
||||||
|
|
||||||
|
pr_info("Umount manager: added %s entry: %s\n",
|
||||||
|
is_default ? "default" : "custom", path);
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, irqflags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_umount_manager_remove(const char *path)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!path) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
entry = find_entry_locked(path);
|
||||||
|
if (!entry) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->is_default) {
|
||||||
|
pr_err("Umount manager: cannot remove default entry: %s\n", path);
|
||||||
|
ret = -EPERM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->state == UMOUNT_STATE_BUSY || entry->ref_count > 0) {
|
||||||
|
pr_err("Umount manager: entry is busy: %s\n", path);
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del(&entry->list);
|
||||||
|
g_umount_mgr.entry_count--;
|
||||||
|
kfree(entry);
|
||||||
|
|
||||||
|
pr_info("Umount manager: removed entry: %s\n", path);
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_umount_manager_execute_all(const struct cred *cred)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (entry->state == UMOUNT_STATE_IDLE) {
|
||||||
|
entry->ref_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (entry->ref_count > 0 && entry->state == UMOUNT_STATE_IDLE) {
|
||||||
|
try_umount_path(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (entry->ref_count > 0) {
|
||||||
|
entry->ref_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count)
|
||||||
|
{
|
||||||
|
u32 max_count = *count;
|
||||||
|
u32 idx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = collect_mount_list_entries(entries, max_count, &idx);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx < max_count) {
|
||||||
|
ret = collect_umount_manager_entries(entries, idx, max_count, &idx);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*count = idx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_umount_manager_clear_custom(void)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry, *tmp;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 cleared = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(entry, tmp, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (!entry->is_default && entry->state == UMOUNT_STATE_IDLE && entry->ref_count == 0) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
g_umount_mgr.entry_count--;
|
||||||
|
cleared++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
pr_info("Umount manager: cleared %u custom entries\n", cleared);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
63
kernel/umount_manager.h
Normal file
63
kernel/umount_manager.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef __KSU_H_UMOUNT_MANAGER
|
||||||
|
#define __KSU_H_UMOUNT_MANAGER
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
struct cred;
|
||||||
|
|
||||||
|
enum umount_entry_state {
|
||||||
|
UMOUNT_STATE_IDLE = 0,
|
||||||
|
UMOUNT_STATE_ACTIVE = 1,
|
||||||
|
UMOUNT_STATE_BUSY = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct umount_entry {
|
||||||
|
struct list_head list;
|
||||||
|
char path[256];
|
||||||
|
int flags;
|
||||||
|
enum umount_entry_state state;
|
||||||
|
bool is_default;
|
||||||
|
u32 ref_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct umount_manager {
|
||||||
|
struct list_head entry_list;
|
||||||
|
spinlock_t lock;
|
||||||
|
u32 entry_count;
|
||||||
|
u32 max_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum umount_manager_op {
|
||||||
|
UMOUNT_OP_ADD = 0,
|
||||||
|
UMOUNT_OP_REMOVE = 1,
|
||||||
|
UMOUNT_OP_LIST = 2,
|
||||||
|
UMOUNT_OP_CLEAR_CUSTOM = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_umount_manager_cmd {
|
||||||
|
__u32 operation;
|
||||||
|
char path[256];
|
||||||
|
__s32 flags;
|
||||||
|
__u32 count;
|
||||||
|
__aligned_u64 entries_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ksu_umount_entry_info {
|
||||||
|
char path[256];
|
||||||
|
__s32 flags;
|
||||||
|
__u8 is_default;
|
||||||
|
__u32 state;
|
||||||
|
__u32 ref_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_umount_manager_init(void);
|
||||||
|
void ksu_umount_manager_exit(void);
|
||||||
|
int ksu_umount_manager_add(const char *path, int flags, bool is_default);
|
||||||
|
int ksu_umount_manager_remove(const char *path);
|
||||||
|
void ksu_umount_manager_execute_all(const struct cred *cred);
|
||||||
|
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count);
|
||||||
|
int ksu_umount_manager_clear_custom(void);
|
||||||
|
|
||||||
|
#endif // __KSU_H_UMOUNT_MANAGER
|
||||||
76
kernel/util.c
Normal file
76
kernel/util.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/pgtable.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <asm/current.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
bool try_set_access_flag(unsigned long addr)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_ARM64
|
||||||
|
struct mm_struct *mm = current->mm;
|
||||||
|
struct vm_area_struct *vma;
|
||||||
|
pgd_t *pgd;
|
||||||
|
p4d_t *p4d;
|
||||||
|
pud_t *pud;
|
||||||
|
pmd_t *pmd;
|
||||||
|
pte_t *ptep, pte;
|
||||||
|
spinlock_t *ptl;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (!mm)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!mmap_read_trylock(mm))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vma = find_vma(mm, addr);
|
||||||
|
if (!vma || addr < vma->vm_start)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
pgd = pgd_offset(mm, addr);
|
||||||
|
if (!pgd_present(*pgd))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
p4d = p4d_offset(pgd, addr);
|
||||||
|
if (!p4d_present(*p4d))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
pud = pud_offset(p4d, addr);
|
||||||
|
if (!pud_present(*pud))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
pmd = pmd_offset(pud, addr);
|
||||||
|
if (!pmd_present(*pmd))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
if (pmd_trans_huge(*pmd))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
|
||||||
|
if (!ptep)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
pte = *ptep;
|
||||||
|
|
||||||
|
if (!pte_present(pte))
|
||||||
|
goto out_pte_unlock;
|
||||||
|
|
||||||
|
if (pte_young(pte)) {
|
||||||
|
ret = true;
|
||||||
|
goto out_pte_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptep_set_access_flags(vma, addr, ptep, pte_mkyoung(pte), 0);
|
||||||
|
pr_info("set AF for addr %lx\n", addr);
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out_pte_unlock:
|
||||||
|
pte_unmap_unlock(ptep, ptl);
|
||||||
|
out_unlock:
|
||||||
|
mmap_read_unlock(mm);
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
24
kernel/util.h
Normal file
24
kernel/util.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef __KSU_UTIL_H
|
||||||
|
#define __KSU_UTIL_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#ifndef preempt_enable_no_resched_notrace
|
||||||
|
#define preempt_enable_no_resched_notrace() \
|
||||||
|
do { \
|
||||||
|
barrier(); \
|
||||||
|
__preempt_count_dec(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef preempt_disable_notrace
|
||||||
|
#define preempt_disable_notrace() \
|
||||||
|
do { \
|
||||||
|
__preempt_count_inc(); \
|
||||||
|
barrier(); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool try_set_access_flag(unsigned long addr);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.agp.app)
|
alias(libs.plugins.agp.app)
|
||||||
@@ -11,12 +10,11 @@ plugins {
|
|||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.lsplugin.apksign)
|
alias(libs.plugins.lsplugin.apksign)
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val managerVersionCode: Int by rootProject.extra
|
val managerVersionCode: Int by rootProject.extra
|
||||||
val managerVersionName: String by rootProject.extra
|
val managerVersionName: String by rootProject.extra
|
||||||
|
val androidCmakeVersion: String by rootProject.extra
|
||||||
|
|
||||||
apksign {
|
apksign {
|
||||||
storeFileProperty = "KEYSTORE_FILE"
|
storeFileProperty = "KEYSTORE_FILE"
|
||||||
@@ -25,7 +23,6 @@ apksign {
|
|||||||
keyPasswordProperty = "KEY_PASSWORD"
|
keyPasswordProperty = "KEY_PASSWORD"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
/**signingConfigs {
|
/**signingConfigs {
|
||||||
@@ -51,15 +48,12 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
aidl = true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
compose = true
|
compose = true
|
||||||
prefab = true
|
prefab = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(21)
|
|
||||||
}
|
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
jniLibs {
|
jniLibs {
|
||||||
useLegacyPackaging = true
|
useLegacyPackaging = true
|
||||||
@@ -77,7 +71,8 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path("src/main/cpp/CMakeLists.txt")
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
|
version = androidCmakeVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,11 +114,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
implementation(libs.androidx.compose.material.icons.extended)
|
implementation(libs.androidx.compose.material.icons.extended)
|
||||||
implementation(libs.androidx.compose.material)
|
|
||||||
implementation(libs.androidx.compose.material3)
|
|
||||||
implementation(libs.androidx.compose.ui)
|
implementation(libs.androidx.compose.ui)
|
||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.foundation)
|
|
||||||
implementation(libs.androidx.documentfile)
|
implementation(libs.androidx.documentfile)
|
||||||
|
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
@@ -146,24 +138,14 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
|
||||||
implementation(libs.me.zhanghai.android.appiconloader.coil)
|
|
||||||
|
|
||||||
implementation(libs.sheet.compose.dialogs.core)
|
|
||||||
implementation(libs.sheet.compose.dialogs.list)
|
|
||||||
implementation(libs.sheet.compose.dialogs.input)
|
|
||||||
|
|
||||||
implementation(libs.markdown)
|
implementation(libs.markdown)
|
||||||
|
implementation(libs.markdown.ext.tables)
|
||||||
|
|
||||||
implementation(libs.androidx.webkit)
|
implementation(libs.androidx.webkit)
|
||||||
|
|
||||||
implementation(libs.lsposed.cxx)
|
implementation(libs.lsposed.cxx)
|
||||||
|
|
||||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
implementation(libs.miuix)
|
||||||
|
implementation(libs.haze)
|
||||||
implementation(libs.mmrl.platform)
|
implementation(libs.capsule)
|
||||||
compileOnly(libs.mmrl.hidden.api)
|
}
|
||||||
implementation(libs.mmrl.webui)
|
|
||||||
implementation(libs.mmrl.ui)
|
|
||||||
|
|
||||||
implementation(libs.accompanist.drawablepainter)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
46
manager/app/proguard-rules.pro
vendored
46
manager/app/proguard-rules.pro
vendored
@@ -1,46 +0,0 @@
|
|||||||
-verbose
|
|
||||||
-optimizationpasses 5
|
|
||||||
|
|
||||||
-dontwarn org.conscrypt.**
|
|
||||||
-dontwarn kotlinx.serialization.**
|
|
||||||
|
|
||||||
# Please add these rules to your existing keep rules in order to suppress warnings.
|
|
||||||
# This is generated automatically by the Android Gradle plugin.
|
|
||||||
-dontwarn com.google.auto.service.AutoService
|
|
||||||
-dontwarn com.google.j2objc.annotations.RetainedWith
|
|
||||||
-dontwarn javax.lang.model.SourceVersion
|
|
||||||
-dontwarn javax.lang.model.element.AnnotationMirror
|
|
||||||
-dontwarn javax.lang.model.element.AnnotationValue
|
|
||||||
-dontwarn javax.lang.model.element.Element
|
|
||||||
-dontwarn javax.lang.model.element.ElementKind
|
|
||||||
-dontwarn javax.lang.model.element.ElementVisitor
|
|
||||||
-dontwarn javax.lang.model.element.ExecutableElement
|
|
||||||
-dontwarn javax.lang.model.element.Modifier
|
|
||||||
-dontwarn javax.lang.model.element.Name
|
|
||||||
-dontwarn javax.lang.model.element.PackageElement
|
|
||||||
-dontwarn javax.lang.model.element.TypeElement
|
|
||||||
-dontwarn javax.lang.model.element.TypeParameterElement
|
|
||||||
-dontwarn javax.lang.model.element.VariableElement
|
|
||||||
-dontwarn javax.lang.model.type.ArrayType
|
|
||||||
-dontwarn javax.lang.model.type.DeclaredType
|
|
||||||
-dontwarn javax.lang.model.type.ExecutableType
|
|
||||||
-dontwarn javax.lang.model.type.TypeKind
|
|
||||||
-dontwarn javax.lang.model.type.TypeMirror
|
|
||||||
-dontwarn javax.lang.model.type.TypeVariable
|
|
||||||
-dontwarn javax.lang.model.type.TypeVisitor
|
|
||||||
-dontwarn javax.lang.model.util.AbstractAnnotationValueVisitor8
|
|
||||||
-dontwarn javax.lang.model.util.AbstractTypeVisitor8
|
|
||||||
-dontwarn javax.lang.model.util.ElementFilter
|
|
||||||
-dontwarn javax.lang.model.util.Elements
|
|
||||||
-dontwarn javax.lang.model.util.SimpleElementVisitor8
|
|
||||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor7
|
|
||||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
|
||||||
-dontwarn javax.lang.model.util.Types
|
|
||||||
-dontwarn javax.tools.Diagnostic$Kind
|
|
||||||
|
|
||||||
|
|
||||||
# MMRL:webui reflection
|
|
||||||
-keep class com.dergoogler.mmrl.webui.interfaces.** { *; }
|
|
||||||
-keep class com.sukisu.ultra.ui.webui.WebViewInterface { *; }
|
|
||||||
|
|
||||||
-keep,allowobfuscation class * extends com.dergoogler.mmrl.platform.content.IService { *; }
|
|
||||||
@@ -3,48 +3,50 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
||||||
tools:ignore="ScopedStorage" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
tools:ignore="ScopedStorage" />
|
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".KernelSUApplication"
|
android:name=".KernelSUApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="false"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.KernelSU"
|
android:theme="@style/Theme.KernelSU"
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
tools:targetApi="34">
|
tools:targetApi="34">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.KernelSU">
|
android:theme="@style/Theme.KernelSU"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:mimeType="application/zip" />
|
||||||
|
<data android:mimeType="application/vnd.android.package-archive" />
|
||||||
|
<data android:scheme="content" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="application/zip" />
|
||||||
|
<data android:mimeType="application/vnd.android.package-archive" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="application/zip" />
|
||||||
|
<data android:mimeType="application/vnd.android.package-archive" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity-alias
|
|
||||||
android:name=".ui.MainActivityAlias"
|
|
||||||
android:exported="true"
|
|
||||||
android:enabled="false"
|
|
||||||
android:icon="@mipmap/ic_launcher_alt"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_alt_round"
|
|
||||||
android:targetActivity=".ui.MainActivity">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity-alias>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.webui.WebUIActivity"
|
android:name=".ui.webui.WebUIActivity"
|
||||||
android:autoRemoveFromRecents="true"
|
android:autoRemoveFromRecents="true"
|
||||||
@@ -52,13 +54,6 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Theme.KernelSU.WebUI" />
|
android:theme="@style/Theme.KernelSU.WebUI" />
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.webui.WebUIXActivity"
|
|
||||||
android:autoRemoveFromRecents="true"
|
|
||||||
android:documentLaunchMode="intoExisting"
|
|
||||||
android:exported="false"
|
|
||||||
android:theme="@style/Theme.KernelSU.WebUI" />
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IKsuInterface.aidl
|
||||||
|
package com.sukisu.zako;
|
||||||
|
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import rikka.parcelablelist.ParcelableListSlice;
|
||||||
|
|
||||||
|
interface IKsuInterface {
|
||||||
|
ParcelableListSlice<PackageInfo> getPackages(int flags);
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user