Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4301d1dc5d | ||
|
|
36d803a92a | ||
|
|
3a12bdead1 | ||
|
|
57e3c095be | ||
|
|
3510203fa6 | ||
|
|
2aa0034695 | ||
|
|
7782c00275 | ||
|
|
dc3de58aa6 | ||
|
|
83db28b262 | ||
|
|
6e44090e57 | ||
|
|
c93cf58f48 | ||
|
|
edeff936ce | ||
|
|
d918e016bc | ||
|
|
a4d642bf50 | ||
|
|
d36371580b | ||
|
|
a2211e2909 | ||
|
|
00ea078da7 | ||
|
|
25e5c0aacb | ||
|
|
d22f1bdcc4 | ||
|
|
a473707c10 | ||
|
|
cc0dfc44ac | ||
|
|
f87c4e077a | ||
|
|
76ca0050a7 | ||
|
|
2f2d6aeecf | ||
|
|
f6657fdbfd | ||
|
|
5782afe481 | ||
|
|
2ab373d146 | ||
|
|
eba286ef7e | ||
|
|
2a89159a3d | ||
|
|
29c6e2dbcc | ||
|
|
968c3f7d57 | ||
|
|
a8cdd014dc | ||
|
|
4d7dd32b11 | ||
|
|
97b57de3d0 | ||
|
|
c53cb0afb1 | ||
|
|
699852009c | ||
|
|
a8302120c4 | ||
|
|
8d535fa03a | ||
|
|
2b97c77a6d | ||
|
|
68f3be2cbe | ||
|
|
c55a918957 | ||
|
|
3cdb8fd057 | ||
|
|
2fdb933acc | ||
|
|
cd05d49a7a | ||
|
|
bcb38274f9 | ||
|
|
1f468f35f4 | ||
|
|
1d4d23d2c9 | ||
|
|
994999c8ce | ||
|
|
f05b20dbce | ||
|
|
6af2da13ae | ||
|
|
fd8e3c35bb | ||
|
|
04586ccb96 | ||
|
|
b537b957bd | ||
|
|
c02b42d7de | ||
|
|
3f4293e69a | ||
|
|
8943bab810 | ||
|
|
99898203a3 | ||
|
|
27fba0d48b | ||
|
|
07320d9e11 | ||
|
|
90611232ed | ||
|
|
868b1e655f | ||
|
|
f7d055c9e1 | ||
|
|
c63186cad2 | ||
|
|
86ccca18eb | ||
|
|
23dde3f863 | ||
|
|
4a5119a80c | ||
|
|
f0fec48050 | ||
|
|
b4bdd17e4e | ||
|
|
de92cc4bad | ||
|
|
d288b8f24f | ||
|
|
1b732f62e8 | ||
|
|
36742cc17e | ||
|
|
3a61da7f45 | ||
|
|
fb7901b7fe | ||
|
|
9b96f853e9 | ||
|
|
bcdbb1e877 | ||
|
|
cc8cf28cbc | ||
|
|
d0c3c2ada2 | ||
|
|
ed3536d5fd | ||
|
|
a7efaf6b93 | ||
|
|
856bbf79d0 | ||
|
|
c19b025767 | ||
|
|
1294bbe853 | ||
|
|
a670b82bb6 | ||
|
|
d17960d9ec | ||
|
|
7177a48678 | ||
|
|
06bf44de11 | ||
|
|
98d543e989 | ||
|
|
8ca2a25535 | ||
|
|
77c2ae72d6 | ||
|
|
e24c09acbd | ||
|
|
2ab242a209 | ||
|
|
605ef68b3a | ||
|
|
a184dcf165 | ||
|
|
ce58519e66 | ||
|
|
fe472057b1 | ||
|
|
a17cd29e7a | ||
|
|
210f61949f | ||
|
|
5c7241da31 | ||
|
|
2fb7bde2d9 | ||
|
|
14b3449af2 | ||
|
|
351dc15d08 | ||
|
|
ff6a68221f | ||
|
|
665091f37d | ||
|
|
963717e000 | ||
|
|
deed7a7903 | ||
|
|
68f2f5a0ae | ||
|
|
3a5bcb0e09 | ||
|
|
441d06b065 | ||
|
|
347ffa389e | ||
|
|
157df04c8b | ||
|
|
8eb2c79471 | ||
|
|
78a95ae82b | ||
|
|
a7b9b4c390 | ||
|
|
f63dbca3fa | ||
|
|
672041b4d6 | ||
|
|
0c87765958 | ||
|
|
39811e311f | ||
|
|
cf50966952 | ||
|
|
da477fd588 | ||
|
|
fc85270a35 | ||
|
|
a9e3c1cc8f | ||
|
|
dd4cf956dd | ||
|
|
ac9acf6c0a | ||
|
|
340a94ef30 | ||
|
|
6e0fb0b388 | ||
|
|
ed95981d09 | ||
|
|
942210459f | ||
|
|
828290059d | ||
|
|
6af65779d1 | ||
|
|
6bbb47bad4 | ||
|
|
b44dd7d020 | ||
|
|
67c4111bbd | ||
|
|
4908e3b633 | ||
|
|
0c3049ec03 | ||
|
|
f820b9aaa8 | ||
|
|
f1ba7127b8 | ||
|
|
2564dce9ed | ||
|
|
1a43244288 | ||
|
|
8752b82fdc | ||
|
|
ddea10e0d8 | ||
|
|
4c4dce98f4 | ||
|
|
aef862e91a | ||
|
|
a437f69586 | ||
|
|
ea7e2f4db6 | ||
|
|
ae475cba67 | ||
|
|
8987312fc1 | ||
|
|
2394fc67fc | ||
|
|
d13233f566 | ||
|
|
d582e619f0 | ||
|
|
722b5ab944 | ||
|
|
c1aa0690c5 | ||
|
|
8331ed2d74 | ||
|
|
a0fd27dc33 | ||
|
|
8359bc5890 | ||
|
|
02629db24b | ||
|
|
430a3504d4 | ||
|
|
71bb5a3d3b | ||
|
|
3e7cae8134 | ||
|
|
29de74c941 | ||
|
|
e4285fcb25 | ||
|
|
0144a888da | ||
|
|
99becea3a1 | ||
|
|
58c31cb726 | ||
|
|
43590fc350 | ||
|
|
1d1e0f1e7f | ||
|
|
aec76a388f | ||
|
|
3b8445cdaa | ||
|
|
6225985f6f | ||
|
|
d52fc57fc4 | ||
|
|
79c298cae1 | ||
|
|
ab3aa84173 | ||
|
|
b337fc869c | ||
|
|
08d0b2b048 | ||
|
|
622c681ffc | ||
|
|
98d25694dc | ||
|
|
d9f54a8e42 | ||
|
|
a3a847a885 | ||
|
|
bf06b92850 | ||
|
|
80e3c736d1 | ||
|
|
a16f150269 | ||
|
|
8d066b9ec5 | ||
|
|
db547eecf1 | ||
|
|
0973cd1ae0 | ||
|
|
8c6f50815a | ||
|
|
c98cf121dc | ||
|
|
037c5b6c73 | ||
|
|
9d920e7cc5 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
|||||||
*.bat eol=crlf
|
|
||||||
5
.github/FUNDING.yml
vendored
5
.github/FUNDING.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: tiann
|
|
||||||
patreon: weishu
|
|
||||||
custom: https://vxposed.com/donate.html
|
|
||||||
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
33
.github/ISSUE_TEMPLATE/add_device.yml
vendored
@@ -1,33 +0,0 @@
|
|||||||
name: Contribute to Unofficially Supported Device
|
|
||||||
description: Add your device kernel source to KernelSU's Unofficially Supported Device List
|
|
||||||
title: "[Add Device]: "
|
|
||||||
labels: ["add-device"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for supporting KernelSU!
|
|
||||||
- type: input
|
|
||||||
id: repo-url
|
|
||||||
attributes:
|
|
||||||
label: Repository URL
|
|
||||||
description: Your repository URL
|
|
||||||
placeholder: https://github.com/tiann/KernelSU
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: device
|
|
||||||
attributes:
|
|
||||||
label: Device
|
|
||||||
description: Please describe the device maintained by you.
|
|
||||||
placeholder: GKI 2.0 Device
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
id: terms
|
|
||||||
attributes:
|
|
||||||
label: Code of Conduct
|
|
||||||
description: By submitting this issue, you should be the maintainer of the repository.
|
|
||||||
options:
|
|
||||||
- label: I'm the maintainer of this repository
|
|
||||||
required: true
|
|
||||||
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
name: Bug report
|
|
||||||
description: Create a report to help us improve KernelSU
|
|
||||||
labels: [Bug]
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Please check before submitting an issue
|
|
||||||
options:
|
|
||||||
- label: I have searched the issues and haven't found anything relevant
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- label: I will upload bugreport file in KernelSU Manager - Settings - Report log
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- label: I know how to reproduce the issue which may not be specific to my device
|
|
||||||
required: false
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug
|
|
||||||
description: A clear and concise description of what the bug is
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: To Reproduce
|
|
||||||
description: Steps to reproduce the behaviour
|
|
||||||
placeholder: |
|
|
||||||
- 1. Go to '...'
|
|
||||||
- 2. Click on '....'
|
|
||||||
- 3. Scroll down to '....'
|
|
||||||
- 4. See error
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Expected behavior
|
|
||||||
description: A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Logs
|
|
||||||
description: If applicable, add crash or any other logs to help us figure out the problem.
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Device info
|
|
||||||
value: |
|
|
||||||
- Device:
|
|
||||||
- OS Version:
|
|
||||||
- KernelSU Version:
|
|
||||||
- Kernel Version:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add any other context about the problem here.
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Feature Request
|
|
||||||
url: https://github.com/tiann/KernelSU/issues/1705
|
|
||||||
about: "We do not accept external Feature Requests, see this link for more details."
|
|
||||||
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
name: Custom issue template
|
|
||||||
description: WARNING! If you are reporting a bug but use this template, the issue will be closed directly.
|
|
||||||
title: '[Custom]'
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: "Describe your problem."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
64
.github/scripts/build_a12.sh
vendored
64
.github/scripts/build_a12.sh
vendored
@@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
build_from_image() {
|
|
||||||
export TITLE
|
|
||||||
TITLE=kernel-aarch64-${1//Image-/}
|
|
||||||
echo "[+] title: $TITLE"
|
|
||||||
|
|
||||||
export PATCH_LEVEL
|
|
||||||
PATCH_LEVEL=$(echo "$1" | awk -F_ '{ print $2}')
|
|
||||||
echo "[+] patch level: $PATCH_LEVEL"
|
|
||||||
|
|
||||||
echo '[+] Download prebuilt ramdisk'
|
|
||||||
GKI_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
|
|
||||||
FALLBACK_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-2023-01_r1.zip
|
|
||||||
status=$(curl -sL -w "%{http_code}" "$GKI_URL" -o /dev/null)
|
|
||||||
if [ "$status" = "200" ]; then
|
|
||||||
curl -Lo gki-kernel.zip "$GKI_URL"
|
|
||||||
else
|
|
||||||
echo "[+] $GKI_URL not found, using $FALLBACK_URL"
|
|
||||||
curl -Lo gki-kernel.zip "$FALLBACK_URL"
|
|
||||||
fi
|
|
||||||
unzip gki-kernel.zip && rm gki-kernel.zip
|
|
||||||
|
|
||||||
echo '[+] Unpack prebuilt boot.img'
|
|
||||||
BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img")
|
|
||||||
$UNPACK_BOOTIMG --boot_img="$BOOT_IMG"
|
|
||||||
rm "$BOOT_IMG"
|
|
||||||
|
|
||||||
echo '[+] Building Image.gz'
|
|
||||||
$GZIP -n -k -f -9 Image >Image.gz
|
|
||||||
|
|
||||||
echo '[+] Building boot.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Building boot-gz.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Building boot-lz4.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Compress images'
|
|
||||||
for image in boot*.img; do
|
|
||||||
$GZIP -n -f -9 "$image"
|
|
||||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "[+] Images to upload"
|
|
||||||
find . -type f -name "*.gz"
|
|
||||||
|
|
||||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in Image*; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
echo "----- Building $dir -----"
|
|
||||||
cd "$dir"
|
|
||||||
build_from_image "$dir"
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
43
.github/scripts/build_a13.sh
vendored
43
.github/scripts/build_a13.sh
vendored
@@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
build_from_image() {
|
|
||||||
export TITLE
|
|
||||||
TITLE=kernel-aarch64-${1//Image-/}
|
|
||||||
|
|
||||||
echo "[+] title: $TITLE"
|
|
||||||
echo '[+] Building Image.gz'
|
|
||||||
$GZIP -n -k -f -9 Image >Image.gz
|
|
||||||
|
|
||||||
echo '[+] Building boot.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Building boot-gz.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Building boot-lz4.img'
|
|
||||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img
|
|
||||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
|
||||||
|
|
||||||
echo '[+] Compress images'
|
|
||||||
for image in boot*.img; do
|
|
||||||
$GZIP -n -f -9 "$image"
|
|
||||||
mv "$image".gz "${1//Image-/}"-"$image".gz
|
|
||||||
done
|
|
||||||
|
|
||||||
echo '[+] Images to upload'
|
|
||||||
find . -type f -name "*.gz"
|
|
||||||
|
|
||||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in Image*; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
echo "----- Building $dir -----"
|
|
||||||
cd "$dir"
|
|
||||||
build_from_image "$dir"
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
60
.github/workflows/add-device.yml
vendored
60
.github/workflows/add-device.yml
vendored
@@ -1,60 +0,0 @@
|
|||||||
name: handle-add-device-issue
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [labeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
handle-add-device:
|
|
||||||
if: github.event.label.name == 'add-device'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
ISSUE_CONTENT: ${{ github.event.issue.body }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Parse issue body
|
|
||||||
id: handle-add-device
|
|
||||||
run: |
|
|
||||||
python3 scripts/add_device_handler.py website/docs/repos.json || true
|
|
||||||
- name: Commit
|
|
||||||
if: steps.handle-add-device.outputs.success == 'true'
|
|
||||||
run: |
|
|
||||||
git config --local user.name "GitHub Actions"
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add website/docs/repos.json
|
|
||||||
git commit -m "add device: ${{ steps.handle-add-device.outputs.device }}"
|
|
||||||
- name: Make pull request
|
|
||||||
if: steps.handle-add-device.outputs.success == 'true'
|
|
||||||
id: cpr
|
|
||||||
uses: peter-evans/create-pull-request@v7
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
|
||||||
title: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
|
||||||
body: |
|
|
||||||
${{ steps.handle-add-device.outputs.device }} has been added to the website.
|
|
||||||
Related issue: ${{ github.event.issue.html_url }}
|
|
||||||
branch: "add-device-${{ github.event.issue.number }}"
|
|
||||||
labels: add-device
|
|
||||||
delete-branch: true
|
|
||||||
sign-commits: true
|
|
||||||
- name: Check outputs
|
|
||||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
|
||||||
run: |
|
|
||||||
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
|
|
||||||
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
|
||||||
- uses: Kernel-SU/actions-comment-on-issue@master
|
|
||||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
|
||||||
with:
|
|
||||||
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- uses: Kernel-SU/actions-comment-on-issue@master
|
|
||||||
if: steps.handle-add-device.outputs.success != 'true'
|
|
||||||
with:
|
|
||||||
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: close issue
|
|
||||||
uses: peter-evans/close-issue@v3
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.issue.number }}
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
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 }}"
|
|
||||||
74
.github/workflows/build-lkm.yml
vendored
74
.github/workflows/build-lkm.yml
vendored
@@ -1,74 +0,0 @@
|
|||||||
name: Build LKM for KernelSU
|
|
||||||
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: 233
|
|
||||||
os_patch_level: 2025-02
|
|
||||||
- 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: 129
|
|
||||||
os_patch_level: 2025-04
|
|
||||||
- version: "android15-6.6"
|
|
||||||
sub_level: 82
|
|
||||||
os_patch_level: 2025-04
|
|
||||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
|
||||||
uses: ./.github/workflows/gki-kernel.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: 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
|
|
||||||
266
.github/workflows/build-manager.yml
vendored
266
.github/workflows/build-manager.yml
vendored
@@ -1,266 +0,0 @@
|
|||||||
name: Build Manager
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main", "ci" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/build-manager.yml'
|
|
||||||
- 'manager/**'
|
|
||||||
- 'kernel/**'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
- 'userspace/susfs/**'
|
|
||||||
- 'userspace/kpmmgr/**'
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- 'manager/**'
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
needs: check-build-lkm
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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: ubuntu-latest
|
|
||||||
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: Setup Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
java-version: 21
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: android-actions/setup-android@v3
|
|
||||||
|
|
||||||
- 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: Copy ksud to app jniLibs
|
|
||||||
run: |
|
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
|
||||||
mkdir -p app/src/main/jniLibs/x86_64
|
|
||||||
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
|
|
||||||
|
|
||||||
- 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: |
|
|
||||||
{
|
|
||||||
echo 'org.gradle.parallel=true'
|
|
||||||
echo 'org.gradle.vfs.watch=true'
|
|
||||||
echo 'org.gradle.jvmargs=-Xmx2048m'
|
|
||||||
echo 'android.native.buildOutput=verbose'
|
|
||||||
} >> gradle.properties
|
|
||||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
|
||||||
./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")
|
|
||||||
pip3 install telethon
|
|
||||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
|
||||||
fi
|
|
||||||
36
.github/workflows/build-su.yml
vendored
36
.github/workflows/build-su.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Build SU
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main", "ci" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/build-su.yml'
|
|
||||||
- 'userspace/su/**'
|
|
||||||
- 'scripts/ksubot.py'
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- 'userspace/su/**'
|
|
||||||
jobs:
|
|
||||||
build-su:
|
|
||||||
name: Build userspace su
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- 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: Build su
|
|
||||||
working-directory: ./userspace/su
|
|
||||||
run: $ANDROID_NDK/ndk-build
|
|
||||||
- name: Upload a Build Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: su
|
|
||||||
path: ./userspace/su/libs
|
|
||||||
37
.github/workflows/clippy.yml
vendored
37
.github/workflows/clippy.yml
vendored
@@ -1,37 +0,0 @@
|
|||||||
name: Clippy check
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/clippy.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/clippy.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: '-Dwarnings'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- run: rustup update stable
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: userspace/ksud
|
|
||||||
|
|
||||||
- name: Install cross
|
|
||||||
run: |
|
|
||||||
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
|
||||||
|
|
||||||
- name: Run clippy
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
67
.github/workflows/deploy-website.yml
vendored
67
.github/workflows/deploy-website.yml
vendored
@@ -1,67 +0,0 @@
|
|||||||
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
|
|
||||||
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
|
|
||||||
261
.github/workflows/gki-kernel.yml
vendored
261
.github/workflows/gki-kernel.yml
vendored
@@ -1,261 +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:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
|
||||||
CCACHE_NOHASHDIR: "true"
|
|
||||||
CCACHE_HARDLINK: "true"
|
|
||||||
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 -b common-${{ inputs.tag }} --repo-rev=v2.35
|
|
||||||
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/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
|
|
||||||
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
|
|
||||||
74
.github/workflows/ksud.yml
vendored
74
.github/workflows/ksud.yml
vendored
@@ -1,74 +0,0 @@
|
|||||||
name: Build ksud
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
os:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ubuntu-latest
|
|
||||||
pull_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
pack_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
use_cache:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Pull lkms from branch
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Prepare LKM files
|
|
||||||
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
|
|
||||||
run: |
|
|
||||||
cp lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
|
||||||
|
|
||||||
- name: Prepare LKM files
|
|
||||||
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
|
|
||||||
run: |
|
|
||||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
|
||||||
|
|
||||||
- name: Setup rustup
|
|
||||||
run: |
|
|
||||||
rustup update stable
|
|
||||||
rustup target add x86_64-apple-darwin
|
|
||||||
rustup target add aarch64-apple-darwin
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: userspace/ksud
|
|
||||||
cache-targets: false
|
|
||||||
|
|
||||||
- name: Install cross
|
|
||||||
run: |
|
|
||||||
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
|
||||||
|
|
||||||
- name: Build ksud
|
|
||||||
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
|
|
||||||
- name: Upload ksud artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ksud-${{ inputs.target }}
|
|
||||||
path: userspace/ksud/target/**/release/zakozako*
|
|
||||||
33
.github/workflows/rustfmt.yml
vendored
33
.github/workflows/rustfmt.yml
vendored
@@ -1,33 +0,0 @@
|
|||||||
name: Rustfmt check
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/rustfmt.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/rustfmt.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
format:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
components: rustfmt
|
|
||||||
|
|
||||||
- uses: LoliGothick/rustfmt-check@master
|
|
||||||
with:
|
|
||||||
token: ${{ github.token }}
|
|
||||||
working-directory: userspace/ksud
|
|
||||||
27
.github/workflows/shellcheck.yml
vendored
27
.github/workflows/shellcheck.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
name: ShellCheck
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/shellcheck.yml'
|
|
||||||
- '**/*.sh'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/shellcheck.yml'
|
|
||||||
- '**/*.sh'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
shellcheck:
|
|
||||||
runs-on: self-hosted
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Run ShellCheck
|
|
||||||
uses: ludeeus/action-shellcheck@2.0.0
|
|
||||||
with:
|
|
||||||
ignore_names: gradlew
|
|
||||||
ignore_paths: ./userspace/ksud/src/installer.sh
|
|
||||||
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
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1 @@
|
|||||||
.idea
|
manager
|
||||||
.vscode
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Reporting Security Issues
|
|
||||||
|
|
||||||
The KernelSU team and community take security bugs in KernelSU seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
|
||||||
|
|
||||||
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/tiann/KernelSU/security/advisories/new) tab, or you can mailto [weishu](mailto:twsxtd@gmail.com) directly.
|
|
||||||
|
|
||||||
The KernelSU team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
# SukiSU Ultra
|
|
||||||
|
|
||||||
**English** | [简体中文](README.md) | [日本語](README-ja.md)
|
|
||||||
|
|
||||||
Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
|
|
||||||
|
|
||||||
**Experimental! Use at your own risk!** This solution is based on [KernelSU](https://github.com/tiann/KernelSU) and is experimental!
|
|
||||||
|
|
||||||
> This is an unofficial fork. All rights are reserved to [@tiann](https://github.com/tiann)
|
|
||||||
>
|
|
||||||
> However, we will be a separately maintained branch of KSU in the future
|
|
||||||
|
|
||||||
- Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only)
|
|
||||||
|
|
||||||
## How to add
|
|
||||||
|
|
||||||
Use the susfs-stable or susfs-dev branch (integrated susfs with support for non-GKI devices)
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the main branch
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
## How to use integrated susfs
|
|
||||||
|
|
||||||
1. Use the susfs-dev branch directly without any patching
|
|
||||||
|
|
||||||
## KPM support
|
|
||||||
|
|
||||||
- We have removed duplicate KSU functions based on KernelPatch and retained KPM support.
|
|
||||||
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
|
|
||||||
|
|
||||||
Open source address: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
|
||||||
|
|
||||||
KPM template address: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## More links
|
|
||||||
|
|
||||||
Projects compiled based on Sukisu and susfs
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## Hook method
|
|
||||||
- This method references the hook method from (https://github.com/rsuntk/KernelSU)
|
|
||||||
|
|
||||||
1. **KPROBES hook:**
|
|
||||||
- Also used for Loadable Kernel Module (LKM)
|
|
||||||
- Default hook method on GKI kernels.
|
|
||||||
- Need `CONFIG_KPROBES=y`
|
|
||||||
|
|
||||||
2. **Manual hook:**
|
|
||||||
- Standard KernelSU hook: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
|
||||||
- backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5
|
|
||||||
- Default hook method on Non-GKI kernels.
|
|
||||||
- Need `CONFIG_KSU_MANUAL_HOOK=y`
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### GKI
|
|
||||||
|
|
||||||
Please follow this guide.
|
|
||||||
|
|
||||||
https://kernelsu.org/guide/installation.html
|
|
||||||
|
|
||||||
|
|
||||||
### OnePlus
|
|
||||||
|
|
||||||
1. Use the link mentioned in the 'More Links' section to create a customized build with your device information, and then flash the zip file with the AnyKernel3 suffix.
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - You only need to fill in the first two parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
|
|
||||||
> - Please search for the processor codename by yourself, usually it is all English without numbers.
|
|
||||||
> - You can find the branch and configuration files from the OnePlus open-source kernel repository.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
1. Kernel-based `su` and root access management.
|
|
||||||
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system, but based on [Magic Mount](https://github.com/5ec1cff/KernelSU) from 5ec1cff
|
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
|
|
||||||
4. Bringing back non-GKI/GKI 1.0 support
|
|
||||||
5. More customization
|
|
||||||
6. Support for KPM kernel modules
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
|
|
||||||
- All other parts except the “kernel” directory are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
|
||||||
|
|
||||||
## Sponsorship list
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) Thanks for the 100 USDT Lao
|
|
||||||
|
|
||||||
If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU): Reintroduced the support of non-GKI devices using the kernel of this project
|
|
||||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):Used susfs file system
|
|
||||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
|
||||||
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit utilities.
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
# SukiSU Ultra
|
|
||||||
|
|
||||||
**日本語** | [简体中文](README.md) | [English](README-en.md)
|
|
||||||
|
|
||||||
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
|
||||||
|
|
||||||
**試験中なビルドです!自己責任で使用してください!**<br>
|
|
||||||
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
|
|
||||||
|
|
||||||
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
|
|
||||||
>
|
|
||||||
> ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
|
|
||||||
|
|
||||||
- GKI 非対応なデバイスに完全に適応 (susfs-dev と unsusfs-patched dev ブランチのみ)
|
|
||||||
|
|
||||||
## 追加方法
|
|
||||||
|
|
||||||
susfs-stable または susfs-dev ブランチ (GKI 非対応デバイスに対応する統合された susfs) 使用してください。
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
メインブランチを使用する場合
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
## 統合された susfs の使い方
|
|
||||||
|
|
||||||
1. パッチを当てずに susfs-dev ブランチを直接使用してください。
|
|
||||||
|
|
||||||
## KPM に対応
|
|
||||||
|
|
||||||
- KernelPatch に基づいて重複した KSU の機能を削除、KPM の対応を維持させています。
|
|
||||||
- KPM 機能の整合性を確保するために、APatch の互換機能を更に向上させる予定です。
|
|
||||||
|
|
||||||
オープンソースアドレス: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
|
||||||
|
|
||||||
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## その他のリンク
|
|
||||||
|
|
||||||
SukiSU と susfs をベースにコンパイルされたプロジェクトです。
|
|
||||||
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## フックの方式
|
|
||||||
|
|
||||||
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
|
||||||
|
|
||||||
1. **KPROBES フック:**
|
|
||||||
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
|
||||||
- GKI カーネルのデフォルトとなるフック方式
|
|
||||||
- `CONFIG_KPROBES=y` が必要です
|
|
||||||
|
|
||||||
2. **手動でフック:**
|
|
||||||
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
|
||||||
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
|
|
||||||
- 非 GKI カーネル用のデフォルトフッキングメソッド
|
|
||||||
- `CONFIG_KSU_MANUAL_HOOK=y` が必要です
|
|
||||||
|
|
||||||
## 使い方
|
|
||||||
|
|
||||||
### GKI
|
|
||||||
|
|
||||||
このガイドに従ってください。
|
|
||||||
|
|
||||||
https://kernelsu.org/ja_JP/guide/installation.html
|
|
||||||
|
|
||||||
### OnePlus
|
|
||||||
|
|
||||||
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
|
|
||||||
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
|
|
||||||
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
|
|
||||||
|
|
||||||
## 機能
|
|
||||||
|
|
||||||
1. カーネルベースな `su` および root アクセスの管理。
|
|
||||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) モジュールシステムではなく、 5ec1cff 氏の [Magic Mount](https://github.com/5ec1cff/KernelSU) に基づいています。
|
|
||||||
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージ内にロックします。
|
|
||||||
4. 非 GKI / GKI 1.0 の対応を復活
|
|
||||||
5. その他のカスタマイズ
|
|
||||||
6. KPM カーネルモジュールに対応
|
|
||||||
|
|
||||||
## ライセンス
|
|
||||||
|
|
||||||
- “kernel” ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
|
|
||||||
- “kernel” ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
|
|
||||||
|
|
||||||
## スポンサーシップの一覧
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) 応援をしてくれたことに感謝。
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) ミルクティーを買ってあげるのも良い考えですね。
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトを支援していただき、ありがとうございます。
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) どうもありがとう。
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) ラオウ100USDTありがとう!
|
|
||||||
|
|
||||||
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
|
|
||||||
|
|
||||||
## 貢献者
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクトです。
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクトです。
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用して非 GKI デバイスのサポートを追加しています。
|
|
||||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):使用している susfs ファイルシステムです。
|
|
||||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU について。
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ユーティリティです。
|
|
||||||
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証で使用しています。
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit ユーティリティを使用しています。
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装での重要な部分となります。
|
|
||||||
116
docs/README.md
116
docs/README.md
@@ -1,116 +0,0 @@
|
|||||||
# SukiSU Ultra
|
|
||||||
|
|
||||||
**简体中文** | [English](README-en.md) | [日本語](README-ja.md)
|
|
||||||
|
|
||||||
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
|
||||||
|
|
||||||
**实验性! 使用风险自负!**
|
|
||||||
|
|
||||||
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
|
|
||||||
>
|
|
||||||
> 但是,我们将会在未来成为一个单独维护的 KSU 分支
|
|
||||||
|
|
||||||
## 如何添加
|
|
||||||
|
|
||||||
在内核源码的根目录下执行以下命令:
|
|
||||||
|
|
||||||
使用 susfs-dev 分支(已集成 susfs,带非 GKI 设备的支持)
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
使用 main 分支
|
|
||||||
```
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
## 如何集成 susfs
|
|
||||||
|
|
||||||
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
|
|
||||||
|
|
||||||
## 钩子方法
|
|
||||||
|
|
||||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
|
||||||
|
|
||||||
1. **KPROBES 钩子:**
|
|
||||||
- 用于可加载内核模块 (LKM)
|
|
||||||
- GKI 2.0 内核的默认钩子方法
|
|
||||||
- 需要 `CONFIG_KPROBES=y`
|
|
||||||
|
|
||||||
2. **手动钩子:**
|
|
||||||
- 标准的 KernelSU 钩子:https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
|
||||||
- backslashxx 的 syscall 手动钩子:https://github.com/backslashxx/KernelSU/issues/5
|
|
||||||
- 非 GKI 内核的默认挂钩方法
|
|
||||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
|
||||||
|
|
||||||
## KPM 支持
|
|
||||||
|
|
||||||
- 我们基于 KernelPatch 去掉了和 KSU 重复的功能,仅保留了 KPM 支持
|
|
||||||
- 我们将会引入更多的兼容 APatch 的函数来确保 KPM 功能的完整性
|
|
||||||
|
|
||||||
开源地址: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
|
||||||
|
|
||||||
KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
|
|
||||||
|
|
||||||
## 更多链接
|
|
||||||
|
|
||||||
基于 SukiSU 和 susfs 编译的项目
|
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
- [一加](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 普适的 GKI
|
|
||||||
|
|
||||||
请**全部**参考 https://kernelsu.org/zh_CN/guide/installation.html
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> 1. 适用于如小米、红米、三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
|
||||||
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来,用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
|
|
||||||
> 3. 一般不带后缀的 .zip 压缩包是未压缩的,gz 后缀的为天玑机型所使用的压缩方式
|
|
||||||
|
|
||||||
|
|
||||||
### 一加
|
|
||||||
|
|
||||||
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
|
||||||
|
|
||||||
> [!Note]
|
|
||||||
> - 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
|
||||||
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
|
|
||||||
> - 分支和配置文件请自行到一加内核开源地址进行填写
|
|
||||||
|
|
||||||
## 特点
|
|
||||||
|
|
||||||
1. 基于内核的 `su` 和 root 访问管理
|
|
||||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
|
||||||
4. 恢复对非 GKI 2.0 内核的支持
|
|
||||||
5. 更多自定义功能
|
|
||||||
6. 对 KPM 内核模块的支持
|
|
||||||
|
|
||||||
## 许可证
|
|
||||||
|
|
||||||
- `kernel` 目录下的文件是 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
|
||||||
- 除 `kernel` 目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)。
|
|
||||||
|
|
||||||
## 赞助名单
|
|
||||||
|
|
||||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
|
||||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
|
||||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
|
||||||
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
|
||||||
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
|
|
||||||
|
|
||||||
如果以上名单没有你的名称,我会及时更新,再次感谢大家的支持
|
|
||||||
|
|
||||||
## 贡献
|
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU):原始项目
|
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU):使用的项目
|
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非GKI设备重新进行支持
|
|
||||||
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu):使用的 susfs 文件系统
|
|
||||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的构想
|
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
|
|
||||||
- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证
|
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
|
|
||||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch 是 APatch 实现内核模块的关键部分
|
|
||||||
121
js/README.md
121
js/README.md
@@ -1,121 +0,0 @@
|
|||||||
# Library for KernelSU's module WebUI
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn add kernelsu
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
### exec
|
|
||||||
|
|
||||||
Spawns a **root** shell and runs a command within that shell, returning a Promise that resolves with the `stdout` and `stderr` outputs upon completion.
|
|
||||||
|
|
||||||
- `command` `<string>` The command to run, with space-separated arguments.
|
|
||||||
- `options` `<Object>`
|
|
||||||
- `cwd` - Current working directory of the child process.
|
|
||||||
- `env` - Environment key-value pairs.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { exec } from 'kernelsu';
|
|
||||||
|
|
||||||
const { errno, stdout, stderr } = await exec('ls -l', { cwd: '/tmp' });
|
|
||||||
if (errno === 0) {
|
|
||||||
// success
|
|
||||||
console.log(stdout);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### spawn
|
|
||||||
|
|
||||||
Spawns a new process using the given `command` in **root** shell, with command-line arguments in `args`. If omitted, `args` defaults to an empty array.
|
|
||||||
|
|
||||||
Returns a `ChildProcess` instance. Instances of `ChildProcess` represent spawned child processes.
|
|
||||||
|
|
||||||
- `command` `<string>` The command to run.
|
|
||||||
- `args` `<string[]>` List of string arguments.
|
|
||||||
- `options` `<Object>`:
|
|
||||||
- `cwd` `<string>` - Current working directory of the child process.
|
|
||||||
- `env` `<Object>` - Environment key-value pairs.
|
|
||||||
|
|
||||||
Example of running `ls -lh /data`, capturing `stdout`, `stderr`, and the exit code:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { spawn } from 'kernelsu';
|
|
||||||
|
|
||||||
const ls = spawn('ls', ['-lh', '/data']);
|
|
||||||
|
|
||||||
ls.stdout.on('data', (data) => {
|
|
||||||
console.log(`stdout: ${data}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
ls.stderr.on('data', (data) => {
|
|
||||||
console.log(`stderr: ${data}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
ls.on('exit', (code) => {
|
|
||||||
console.log(`child process exited with code ${code}`);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ChildProcess
|
|
||||||
|
|
||||||
##### Event 'exit'
|
|
||||||
|
|
||||||
- `code` `<number>` The exit code if the child process exited on its own.
|
|
||||||
|
|
||||||
The `'exit'` event is emitted when the child process ends. If the process exits, `code` contains the final exit code; otherwise, it is null.
|
|
||||||
|
|
||||||
##### Event 'error'
|
|
||||||
|
|
||||||
- `err` `<Error>` The error.
|
|
||||||
|
|
||||||
The `'error'` event is emitted whenever:
|
|
||||||
|
|
||||||
- The process could not be spawned.
|
|
||||||
- The process could not be killed.
|
|
||||||
|
|
||||||
##### `stdout`
|
|
||||||
|
|
||||||
A `Readable Stream` that represents the child process's `stdout`.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const subprocess = spawn('ls');
|
|
||||||
|
|
||||||
subprocess.stdout.on('data', (data) => {
|
|
||||||
console.log(`Received chunk ${data}`);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `stderr`
|
|
||||||
|
|
||||||
A `Readable Stream` that represents the child process's `stderr`.
|
|
||||||
|
|
||||||
### fullScreen
|
|
||||||
|
|
||||||
Request the WebView enter/exit full screen.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { fullScreen } from 'kernelsu';
|
|
||||||
fullScreen(true);
|
|
||||||
```
|
|
||||||
|
|
||||||
### toast
|
|
||||||
|
|
||||||
Show a toast message.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { toast } from 'kernelsu';
|
|
||||||
toast('Hello, world!');
|
|
||||||
```
|
|
||||||
|
|
||||||
### moduleInfo
|
|
||||||
|
|
||||||
Get module info.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { moduleInfo } from 'kernelsu';
|
|
||||||
// print moduleId in console
|
|
||||||
console.log(moduleInfo());
|
|
||||||
```
|
|
||||||
48
js/index.d.ts
vendored
48
js/index.d.ts
vendored
@@ -1,48 +0,0 @@
|
|||||||
interface ExecOptions {
|
|
||||||
cwd?: string,
|
|
||||||
env?: { [key: string]: string }
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ExecResults {
|
|
||||||
errno: number,
|
|
||||||
stdout: string,
|
|
||||||
stderr: string
|
|
||||||
}
|
|
||||||
|
|
||||||
declare function exec(command: string): Promise<ExecResults>;
|
|
||||||
declare function exec(command: string, options: ExecOptions): Promise<ExecResults>;
|
|
||||||
|
|
||||||
interface SpawnOptions {
|
|
||||||
cwd?: string,
|
|
||||||
env?: { [key: string]: string }
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Stdio {
|
|
||||||
on(event: 'data', callback: (data: string) => void)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChildProcess {
|
|
||||||
stdout: Stdio,
|
|
||||||
stderr: Stdio,
|
|
||||||
on(event: 'exit', callback: (code: number) => void)
|
|
||||||
on(event: 'error', callback: (err: any) => void)
|
|
||||||
}
|
|
||||||
|
|
||||||
declare function spawn(command: string): ChildProcess;
|
|
||||||
declare function spawn(command: string, args: string[]): ChildProcess;
|
|
||||||
declare function spawn(command: string, options: SpawnOptions): ChildProcess;
|
|
||||||
declare function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
|
|
||||||
|
|
||||||
declare function fullScreen(isFullScreen: boolean);
|
|
||||||
|
|
||||||
declare function toast(message: string);
|
|
||||||
|
|
||||||
declare function moduleInfo(): string;
|
|
||||||
|
|
||||||
export {
|
|
||||||
exec,
|
|
||||||
spawn,
|
|
||||||
fullScreen,
|
|
||||||
toast,
|
|
||||||
moduleInfo
|
|
||||||
}
|
|
||||||
119
js/index.js
119
js/index.js
@@ -1,119 +0,0 @@
|
|||||||
let callbackCounter = 0;
|
|
||||||
function getUniqueCallbackName(prefix) {
|
|
||||||
return `${prefix}_callback_${Date.now()}_${callbackCounter++}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function exec(command, options) {
|
|
||||||
if (typeof options === "undefined") {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Generate a unique callback function name
|
|
||||||
const callbackFuncName = getUniqueCallbackName("exec");
|
|
||||||
|
|
||||||
// Define the success callback function
|
|
||||||
window[callbackFuncName] = (errno, stdout, stderr) => {
|
|
||||||
resolve({ errno, stdout, stderr });
|
|
||||||
cleanup(callbackFuncName);
|
|
||||||
};
|
|
||||||
|
|
||||||
function cleanup(successName) {
|
|
||||||
delete window[successName];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ksu.exec(command, JSON.stringify(options), callbackFuncName);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
cleanup(callbackFuncName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stdio() {
|
|
||||||
this.listeners = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Stdio.prototype.on = function (event, listener) {
|
|
||||||
if (!this.listeners[event]) {
|
|
||||||
this.listeners[event] = [];
|
|
||||||
}
|
|
||||||
this.listeners[event].push(listener);
|
|
||||||
};
|
|
||||||
|
|
||||||
Stdio.prototype.emit = function (event, ...args) {
|
|
||||||
if (this.listeners[event]) {
|
|
||||||
this.listeners[event].forEach((listener) => listener(...args));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ChildProcess() {
|
|
||||||
this.listeners = {};
|
|
||||||
this.stdin = new Stdio();
|
|
||||||
this.stdout = new Stdio();
|
|
||||||
this.stderr = new Stdio();
|
|
||||||
}
|
|
||||||
|
|
||||||
ChildProcess.prototype.on = function (event, listener) {
|
|
||||||
if (!this.listeners[event]) {
|
|
||||||
this.listeners[event] = [];
|
|
||||||
}
|
|
||||||
this.listeners[event].push(listener);
|
|
||||||
};
|
|
||||||
|
|
||||||
ChildProcess.prototype.emit = function (event, ...args) {
|
|
||||||
if (this.listeners[event]) {
|
|
||||||
this.listeners[event].forEach((listener) => listener(...args));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function spawn(command, args, options) {
|
|
||||||
if (typeof args === "undefined") {
|
|
||||||
args = [];
|
|
||||||
} else if (!(args instanceof Array)) {
|
|
||||||
// allow for (command, options) signature
|
|
||||||
options = args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof options === "undefined") {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const child = new ChildProcess();
|
|
||||||
const childCallbackName = getUniqueCallbackName("spawn");
|
|
||||||
window[childCallbackName] = child;
|
|
||||||
|
|
||||||
function cleanup(name) {
|
|
||||||
delete window[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
child.on("exit", code => {
|
|
||||||
cleanup(childCallbackName);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
ksu.spawn(
|
|
||||||
command,
|
|
||||||
JSON.stringify(args),
|
|
||||||
JSON.stringify(options),
|
|
||||||
childCallbackName
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
child.emit("error", error);
|
|
||||||
cleanup(childCallbackName);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fullScreen(isFullScreen) {
|
|
||||||
ksu.fullScreen(isFullScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toast(message) {
|
|
||||||
ksu.toast(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function moduleInfo() {
|
|
||||||
return ksu.moduleInfo();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "kernelsu",
|
|
||||||
"version": "1.0.7",
|
|
||||||
"description": "Library for KernelSU's module WebUI",
|
|
||||||
"main": "index.js",
|
|
||||||
"types": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"test": "npm run test"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/tiann/KernelSU.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"su",
|
|
||||||
"kernelsu",
|
|
||||||
"module",
|
|
||||||
"webui"
|
|
||||||
],
|
|
||||||
"author": "weishu",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/tiann/KernelSU/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/tiann/KernelSU#readme"
|
|
||||||
}
|
|
||||||
14
justfile
14
justfile
@@ -1,14 +0,0 @@
|
|||||||
alias bk := build_ksud
|
|
||||||
alias bm := build_manager
|
|
||||||
|
|
||||||
build_ksud:
|
|
||||||
cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
|
|
||||||
build_manager: build_ksud
|
|
||||||
cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
|
||||||
cd manager && ./gradlew aDebug
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
cargo fmt --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
|
|
||||||
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
|
||||||
@@ -2,13 +2,17 @@ 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.
|
||||||
|
|
||||||
|
# For easier extern ifdef handling
|
||||||
|
config RKSU
|
||||||
|
bool "RKSU compat, do not modify"
|
||||||
|
default y
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
@@ -16,20 +20,40 @@ config KSU_DEBUG
|
|||||||
help
|
help
|
||||||
Enable KernelSU debug mode.
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
config KSU_HOOK
|
config KSU_ALLOWLIST_WORKAROUND
|
||||||
bool "Enable KernelSU Hook"
|
bool "KernelSU allowlist workaround"
|
||||||
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This option enables the KernelSU Hook feature. If enabled, it will
|
Enable workaround for broken allowlist save
|
||||||
override the kernel version check and enable the hook functionality.
|
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable SukiSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
|
depends on KSU && 64BIT
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enabling this option will activate the KPM feature of SukiSU.
|
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.
|
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||||
but it may affect system stability.
|
but it may affect system stability.
|
||||||
|
select KALLSYMS
|
||||||
|
select KALLSYMS_ALL
|
||||||
|
|
||||||
|
config KSU_MANUAL_HOOK
|
||||||
|
bool "KernelSU manual hook mode."
|
||||||
|
depends on KSU && KSU != m
|
||||||
|
default y if !KPROBES
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable manual hook support.
|
||||||
|
|
||||||
|
config KSU_SHOULD_USE_NEW_TP
|
||||||
|
bool "KernelSU tracepoint+kretprobe hook"
|
||||||
|
depends on KSU && !KSU_MANUAL_HOOK
|
||||||
|
depends on KRETPROBES && KPROBES && HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
default y if KPROBES && KRETPROBES && HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable KPROBES, KRETPROBES and TRACEPOINT hook for KernelSU core.
|
||||||
|
This should not be used on kernel below 5.10.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
187
kernel/Makefile
187
kernel/Makefile
@@ -1,43 +1,185 @@
|
|||||||
kernelsu-objs := ksu.o
|
kernelsu-objs := ksu.o
|
||||||
kernelsu-objs += allowlist.o
|
kernelsu-objs += allowlist.o
|
||||||
|
kernelsu-objs += dynamic_manager.o
|
||||||
|
kernelsu-objs += app_profile.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 += pkg_observer.o
|
||||||
|
kernelsu-objs += setuid_hook.o
|
||||||
|
kernelsu-objs += lsm_hooks.o
|
||||||
|
kernelsu-objs += kernel_compat.o
|
||||||
|
kernelsu-objs += kernel_umount.o
|
||||||
|
kernelsu-objs += supercalls.o
|
||||||
|
kernelsu-objs += feature.o
|
||||||
kernelsu-objs += throne_tracker.o
|
kernelsu-objs += throne_tracker.o
|
||||||
kernelsu-objs += core_hook.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 += throne_comm.o
|
||||||
|
|
||||||
kernelsu-objs += selinux/selinux.o
|
kernelsu-objs += selinux/selinux.o
|
||||||
kernelsu-objs += selinux/sepolicy.o
|
kernelsu-objs += selinux/sepolicy.o
|
||||||
kernelsu-objs += selinux/rules.o
|
kernelsu-objs += selinux/rules.o
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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_NAME := SukiSU-Ultra
|
||||||
|
REPO_BRANCH := main
|
||||||
|
KSU_VERSION_API := 4.0.0
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
ifeq ($(findstring $(srctree),$(src)),$(srctree))
|
||||||
|
KSU_SRC := $(src)
|
||||||
|
else
|
||||||
|
KSU_SRC := $(srctree)/$(src)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
|
||||||
|
|
||||||
|
define get_ksu_version_full
|
||||||
|
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
|
||||||
|
|
||||||
|
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
|
||||||
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
|
||||||
|
KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
|
||||||
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
|
||||||
|
else
|
||||||
|
KSU_VERSION := 13000
|
||||||
|
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
|
||||||
|
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(KSU_GITHUB_VERSION),)
|
||||||
|
ifeq ($(LOCAL_GIT_EXISTS),1)
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
|
||||||
|
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
|
||||||
|
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
|
||||||
|
else
|
||||||
|
KSU_VERSION_FULL := v$(KSU_VERSION_API)-$(REPO_NAME)-unknown@unknown
|
||||||
|
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||||
|
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
|
||||||
|
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
|
||||||
|
endif
|
||||||
|
|
||||||
# .git is a text file while the module is imported by 'git submodule add'.
|
|
||||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
|
||||||
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
|
||||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count main)
|
|
||||||
# ksu_version: major * 10000 + git version + 606 for historical reasons
|
|
||||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
|
|
||||||
$(info -- KernelSU version: $(KSU_VERSION))
|
|
||||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
else # If there is no .git file, the default version will be passed.
|
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
|
||||||
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!")
|
|
||||||
ccflags-y += -DKSU_VERSION=16
|
# RKSU: checks for available hook
|
||||||
|
## Logic flipped for HAVE_KSU_HOOK: 0 is success, 1 is failure, but not with KSU_DRY_RUN
|
||||||
|
HAVE_KSU_HOOK ?= 1
|
||||||
|
KSU_DRY_RUN ?= 0
|
||||||
|
|
||||||
|
# Checks hooks state
|
||||||
|
ifeq ($(CONFIG_KSU_SHOULD_USE_NEW_TP), y)
|
||||||
|
$(info -- KernelSU: SHOULD_USE_NEW_TP)
|
||||||
|
ccflags-y += -DKSU_SHOULD_USE_NEW_TP
|
||||||
|
# Let's make it 0, so it would pass.
|
||||||
|
HAVE_KSU_HOOK := 0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_SIZE
|
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
||||||
KSU_EXPECTED_SIZE := 0x35c
|
HAVE_KSU_HOOK := $(shell grep -q "ksu_handle_faccessat" $(srctree)/fs/open.c; echo $$?)
|
||||||
|
ifeq ($(HAVE_KSU_HOOK),0)
|
||||||
|
$(info -- KernelSU: CONFIG_KSU_MANUAL_HOOK)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_HASH
|
ifeq ($(KSU_DRY_RUN),0)
|
||||||
KSU_EXPECTED_HASH := 947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef
|
ifneq ($(HAVE_KSU_HOOK),0)
|
||||||
|
$(error -- KernelSU: No hooks were defined, bail!)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
$(info -- KernelSU in dry run mode, skip hook checks)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# SELinux drivers check
|
||||||
|
ifeq ($(shell grep -q "current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
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
|
||||||
|
|
||||||
|
# Handle optional backports
|
||||||
|
ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_STRNCPY
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_KERNEL_READ
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_HAS_PATH_UMOUNT
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED
|
||||||
|
endif
|
||||||
|
# seccomp_types.h were added on 6.7
|
||||||
|
ifeq ($(shell grep -q "atomic_t\s\+filter_count" $(srctree)/include/linux/seccomp.h $(srctree)/include/linux/seccomp_types.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_SECCOMP_FILTER_CNT
|
||||||
|
endif
|
||||||
|
# some old kernel backport this, let's check if put_seccomp_filter still exist
|
||||||
|
ifneq ($(shell grep -wq "put_seccomp_filter" $(srctree)/kernel/seccomp.c $(srctree)/include/linux/seccomp.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_OPTIONAL_SECCOMP_FILTER_RELEASE
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_HAS_GETFD_SECURE
|
||||||
|
endif
|
||||||
|
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
|
||||||
|
|
||||||
|
# Checks Samsung
|
||||||
|
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
|
||||||
|
ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST
|
||||||
|
endif
|
||||||
|
ifeq ($(shell grep -q "SEC_SELINUX_PORTING_COMMON" $(srctree)/security/selinux/avc.c; echo $$?),0)
|
||||||
|
ccflags-y += -DSAMSUNG_SELINUX_PORTING
|
||||||
|
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
|
||||||
|
|
||||||
|
# Custom Signs
|
||||||
|
ifdef KSU_EXPECTED_SIZE
|
||||||
|
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||||
|
$(info -- Custom KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||||
|
endif
|
||||||
|
ifdef KSU_EXPECTED_HASH
|
||||||
|
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||||
|
$(info -- Custom KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef KSU_MANAGER_PACKAGE
|
ifdef KSU_MANAGER_PACKAGE
|
||||||
@@ -45,9 +187,8 @@ 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 -- SukiSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
|
||||||
$(info -- SukiSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
|
||||||
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
||||||
|
# Kernel version check
|
||||||
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||||
KERNEL_TYPE := Non-GKI
|
KERNEL_TYPE := Non-GKI
|
||||||
# Check for GKI 2.0 (5.10+ or 6.x+)
|
# Check for GKI 2.0 (5.10+ or 6.x+)
|
||||||
@@ -63,17 +204,13 @@ $(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
|||||||
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
||||||
|
|
||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
$(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-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-missing-prototypes
|
||||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
|
||||||
|
|
||||||
# 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,22 @@
|
|||||||
#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, 11, 0)
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
#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 "kernel_compat.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,7 +39,8 @@ 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);
|
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)
|
||||||
@@ -40,7 +51,7 @@ static void remove_uid_from_arr(uid_t uid)
|
|||||||
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;
|
||||||
@@ -61,7 +72,7 @@ static void remove_uid_from_arr(uid_t uid)
|
|||||||
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;
|
||||||
|
|
||||||
@@ -70,7 +81,7 @@ static void init_default_profiles()
|
|||||||
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);
|
||||||
|
|
||||||
@@ -90,10 +101,7 @@ 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;
|
void persistent_allow_list(void);
|
||||||
static struct work_struct ksu_load_work;
|
|
||||||
|
|
||||||
bool persistent_allow_list(void);
|
|
||||||
|
|
||||||
void ksu_show_allow_list(void)
|
void ksu_show_allow_list(void)
|
||||||
{
|
{
|
||||||
@@ -108,7 +116,7 @@ void ksu_show_allow_list(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#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,
|
||||||
@@ -116,7 +124,8 @@ static void ksu_grant_root_to_shell()
|
|||||||
.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_DEFAULT_SELINUX_DOMAIN);
|
||||||
ksu_set_app_profile(&profile, false);
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -142,9 +151,10 @@ 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
|
||||||
|
#define SYSTEM_UID 1000
|
||||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +206,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -218,21 +228,24 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
|||||||
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] |=
|
||||||
|
1 << (profile->current_uid % BITS_PER_BYTE);
|
||||||
else
|
else
|
||||||
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] &=
|
||||||
|
~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||||
} else {
|
} else {
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
/*
|
/*
|
||||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||||
* registered to request superuser?
|
* registered to request superuser?
|
||||||
*/
|
*/
|
||||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||||
pr_err("too many apps registered\n");
|
pr_err("too many apps registered\n");
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
allow_list_arr[allow_list_pointer++] =
|
||||||
|
profile->current_uid;
|
||||||
} else {
|
} else {
|
||||||
remove_uid_from_arr(profile->current_uid);
|
remove_uid_from_arr(profile->current_uid);
|
||||||
}
|
}
|
||||||
@@ -252,8 +265,11 @@ out:
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -262,23 +278,20 @@ bool __ksu_is_allow_uid(uid_t uid)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(uid == 0)) {
|
|
||||||
// already root, but only allow our domain.
|
|
||||||
return is_ksu_domain();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forbid_system_uid(uid)) {
|
if (forbid_system_uid(uid)) {
|
||||||
// do not bother going through the list if it's system
|
// do not bother going through the list if it's system
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(ksu_is_manager_uid_valid()) &&
|
||||||
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
// manager is always allowed!
|
// manager is always allowed!
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
|
||||||
|
(1 << (uid % BITS_PER_BYTE)));
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < allow_list_pointer; i++) {
|
for (i = 0; i < allow_list_pointer; i++) {
|
||||||
if (allow_list_arr[i] == uid)
|
if (allow_list_arr[i] == uid)
|
||||||
@@ -289,10 +302,20 @@ bool __ksu_is_allow_uid(uid_t uid)
|
|||||||
return false;
|
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()) &&
|
||||||
|
unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
// we should not umount on manager!
|
// we should not umount on manager!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -349,7 +372,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
|||||||
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;
|
||||||
@@ -357,24 +380,26 @@ void do_save_allow_list(struct work_struct *work)
|
|||||||
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 = ksu_filp_open_compat(
|
||||||
|
KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
pr_err("save_allow_list create file failed: %ld\n",
|
||||||
return;
|
PTR_ERR(fp));
|
||||||
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// store magic and version
|
// store magic and version
|
||||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
if (ksu_kernel_write_compat(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 exit;
|
goto close_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
if (ksu_kernel_write_compat(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 exit;
|
goto close_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
@@ -387,11 +412,37 @@ void do_save_allow_list(struct work_struct *work)
|
|||||||
&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(void)
|
||||||
|
{
|
||||||
|
struct task_struct *tsk;
|
||||||
|
|
||||||
|
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
|
||||||
|
if (!tsk) {
|
||||||
|
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(void)
|
||||||
{
|
{
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
@@ -448,11 +499,17 @@ exit:
|
|||||||
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;
|
||||||
|
|
||||||
|
if (!ksu_boot_completed) {
|
||||||
|
pr_info("boot not completed, skip prune\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
// TODO: use RCU!
|
// TODO: use RCU!
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
@@ -466,7 +523,8 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data
|
|||||||
pr_info("prune uid: %d, package: %s\n", uid, package);
|
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||||
list_del(&np->list);
|
list_del(&np->list);
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
allow_list_bitmap[uid / BITS_PER_BYTE] &=
|
||||||
|
~(1 << (uid % BITS_PER_BYTE));
|
||||||
}
|
}
|
||||||
remove_uid_from_arr(uid);
|
remove_uid_from_arr(uid);
|
||||||
smp_mb();
|
smp_mb();
|
||||||
@@ -480,17 +538,6 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure allow list works cross boot
|
|
||||||
bool persistent_allow_list(void)
|
|
||||||
{
|
|
||||||
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;
|
||||||
@@ -503,9 +550,6 @@ void ksu_allowlist_init(void)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&allow_list);
|
INIT_LIST_HEAD(&allow_list);
|
||||||
|
|
||||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
|
||||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
|
||||||
|
|
||||||
init_default_profiles();
|
init_default_profiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,8 +558,6 @@ 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
|
// free allowlist
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
|||||||
@@ -2,26 +2,44 @@
|
|||||||
#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
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
bool ksu_get_app_profile(struct app_profile *);
|
bool ksu_get_app_profile(struct app_profile *);
|
||||||
bool ksu_set_app_profile(struct app_profile *, bool persist);
|
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;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,22 +15,30 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "apk_sign.h"
|
#include "apk_sign.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
#include "manager_sign.h"
|
||||||
|
|
||||||
struct sdesc {
|
struct sdesc {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
char ctx[];
|
char ctx[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static apk_sign_key_t apk_sign_keys[] = {
|
||||||
|
{ EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO }, // SukiSU
|
||||||
|
#ifdef EXPECTED_SIZE
|
||||||
|
{ EXPECTED_SIZE, EXPECTED_HASH }, // Custom
|
||||||
|
#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;
|
||||||
@@ -71,9 +79,59 @@ static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
|
||||||
unsigned expected_size, const char *expected_sha256)
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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",
|
||||||
|
current_dynamic_key.size, current_dynamic_key.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size4 != current_dynamic_key.size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CERT_MAX_LENGTH 1024
|
||||||
|
char cert[CERT_MAX_LENGTH];
|
||||||
|
if (size4 > CERT_MAX_LENGTH) {
|
||||||
|
pr_info("cert length overlimit\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_kernel_read_compat(fp, cert, size4, pos);
|
||||||
|
|
||||||
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
|
if (ksu_sha256(cert, size4, digest) < 0) {
|
||||||
|
pr_info("sha256 error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
|
||||||
|
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 (matched_index) {
|
||||||
|
*matched_index = DYNAMIC_SIGN_INDEX;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||||
|
int *matched_index)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
apk_sign_key_t sign_key;
|
||||||
|
bool signature_valid = false;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
||||||
@@ -89,7 +147,20 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
|||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||||
*offset += 0x4 * 2;
|
*offset += 0x4 * 2;
|
||||||
|
|
||||||
if (*size4 == expected_size) {
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
|
loff_t temp_pos = *pos;
|
||||||
|
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
||||||
|
*pos = temp_pos;
|
||||||
|
*offset += *size4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||||
|
sign_key = apk_sign_keys[i];
|
||||||
|
|
||||||
|
if (*size4 != sign_key.size)
|
||||||
|
continue;
|
||||||
*offset += *size4;
|
*offset += *size4;
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
@@ -100,7 +171,7 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
|||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
ksu_kernel_read_compat(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;
|
||||||
}
|
}
|
||||||
@@ -109,13 +180,18 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
|||||||
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\n", hash_str,
|
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str,
|
||||||
expected_sha256);
|
sign_key.sha256, i);
|
||||||
if (strcmp(expected_sha256, hash_str) == 0) {
|
|
||||||
return true;
|
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||||
|
signature_valid = true;
|
||||||
|
if (matched_index) {
|
||||||
|
*matched_index = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return signature_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct zip_entry_header {
|
struct zip_entry_header {
|
||||||
@@ -171,9 +247,8 @@ static bool has_v1_signature_file(struct file *fp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline bool check_v2_signature(char *path,
|
static __always_inline bool
|
||||||
unsigned expected_size,
|
check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||||
const char *expected_sha256)
|
|
||||||
{
|
{
|
||||||
unsigned char buffer[0x11] = { 0 };
|
unsigned char buffer[0x11] = { 0 };
|
||||||
u32 size4;
|
u32 size4;
|
||||||
@@ -185,7 +260,7 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
int v2_signing_blocks = 0;
|
int v2_signing_blocks = 0;
|
||||||
bool v3_signing_exist = false;
|
bool v3_signing_exist = false;
|
||||||
bool v3_1_signing_exist = false;
|
bool v3_1_signing_exist = false;
|
||||||
|
int matched_index = -1;
|
||||||
int i;
|
int i;
|
||||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
@@ -193,6 +268,12 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
||||||
|
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
||||||
|
filp_close(fp, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// disable inotify for this file
|
// disable inotify for this file
|
||||||
fp->f_mode |= FMODE_NONOTIFY;
|
fp->f_mode |= FMODE_NONOTIFY;
|
||||||
|
|
||||||
@@ -244,9 +325,11 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
offset = 4;
|
offset = 4;
|
||||||
if (id == 0x7109871au) {
|
if (id == 0x7109871au) {
|
||||||
v2_signing_blocks++;
|
v2_signing_blocks++;
|
||||||
v2_signing_valid =
|
bool result = check_block(fp, &size4, &pos, &offset,
|
||||||
check_block(fp, &size4, &pos, &offset,
|
&matched_index);
|
||||||
expected_size, expected_sha256);
|
if (result) {
|
||||||
|
v2_signing_valid = true;
|
||||||
|
}
|
||||||
} else if (id == 0xf05368c0u) {
|
} else if (id == 0xf05368c0u) {
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||||
v3_signing_exist = true;
|
v3_signing_exist = true;
|
||||||
@@ -287,7 +370,25 @@ clean:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return v2_signing_valid;
|
if (v2_signing_valid) {
|
||||||
|
if (signature_index) {
|
||||||
|
*signature_index = matched_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_multi_manager) {
|
||||||
|
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX: Dynamic Sign
|
||||||
|
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||||
|
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n",
|
||||||
|
matched_index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Common manager check: any valid signature will do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
@@ -316,5 +417,10 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
|||||||
|
|
||||||
bool is_manager_apk(char *path)
|
bool is_manager_apk(char *path)
|
||||||
{
|
{
|
||||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
return check_v2_signature(path, false, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_dynamic_manager_apk(char *path, int *signature_index)
|
||||||
|
{
|
||||||
|
return check_v2_signature(path, true, signature_index);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,10 @@
|
|||||||
#define __KSU_H_APK_V2_SIGN
|
#define __KSU_H_APK_V2_SIGN
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
bool is_manager_apk(char *path);
|
bool is_manager_apk(char *path);
|
||||||
|
|
||||||
|
bool is_dynamic_manager_apk(char *path, int *signature_index);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
313
kernel/app_profile.c
Normal file
313
kernel/app_profile.c
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fdtable.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/proc_ns.h>
|
||||||
|
#include <linux/pid.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
|
#include <linux/sched/signal.h> // signal_struct
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
|
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
|
||||||
|
group_info->gid[i] = kgid;
|
||||||
|
#else
|
||||||
|
GROUP_AT(group_info, i) = kgid;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
groups_sort(group_info);
|
||||||
|
set_groups(cred, group_info);
|
||||||
|
put_group_info(group_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
extern long SYS_SETNS_SYMBOL(const struct pt_regs *regs);
|
||||||
|
static long ksu_sys_setns(int fd, int flags)
|
||||||
|
{
|
||||||
|
struct pt_regs regs;
|
||||||
|
memset(®s, 0, sizeof(regs));
|
||||||
|
|
||||||
|
PT_REGS_PARM1(®s) = fd;
|
||||||
|
PT_REGS_PARM2(®s) = flags;
|
||||||
|
|
||||||
|
// TODO: arm support
|
||||||
|
#if (defined(__aarch64__) || defined(__x86_64__))
|
||||||
|
return SYS_SETNS_SYMBOL(®s);
|
||||||
|
#else
|
||||||
|
return -ENOSYS;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static long ksu_sys_setns(int fd, int flags)
|
||||||
|
{
|
||||||
|
return sys_setns(fd, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak int ksys_unshare(unsigned long unshare_flags)
|
||||||
|
{
|
||||||
|
return sys_unshare(unshare_flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setup_mount_namespace(int32_t ns_mode)
|
||||||
|
{
|
||||||
|
pr_info("setup mount namespace for pid: %d\n", current->pid);
|
||||||
|
|
||||||
|
if (ns_mode == 0) {
|
||||||
|
pr_info("mount namespace mode: inherit\n");
|
||||||
|
// do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode > 2) {
|
||||||
|
pr_warn("unknown mount namespace mode: %d\n", ns_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct cred *old_cred = NULL;
|
||||||
|
struct cred *new_cred = NULL;
|
||||||
|
if (!(capable(CAP_SYS_ADMIN) && capable(CAP_SYS_CHROOT))) {
|
||||||
|
pr_info("process dont have CAP_SYS_ADMIN or CAP_SYS_CHROOT, adding it temporarily.\n");
|
||||||
|
new_cred = prepare_creds();
|
||||||
|
if (!new_cred) {
|
||||||
|
pr_warn("failed to prepare new credentials\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cap_raise(new_cred->cap_effective, CAP_SYS_ADMIN);
|
||||||
|
cap_raise(new_cred->cap_effective, CAP_SYS_CHROOT);
|
||||||
|
old_cred = override_creds(new_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode == 1) {
|
||||||
|
pr_info("mount namespace mode: global\n");
|
||||||
|
struct file *ns_file;
|
||||||
|
struct path ns_path;
|
||||||
|
struct task_struct *pid1_task = NULL;
|
||||||
|
struct pid *pid_struct = NULL;
|
||||||
|
rcu_read_lock();
|
||||||
|
// find init
|
||||||
|
pid_struct = find_pid_ns(1, &init_pid_ns);
|
||||||
|
if (unlikely(!pid_struct)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_warn("failed to find pid_struct for PID 1\n");
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid1_task = get_pid_task(pid_struct, PIDTYPE_PID);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (unlikely(!pid1_task)) {
|
||||||
|
pr_warn("failed to get task_struct for PID 1\n");
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
// mabe you can use &init_task for first stage init?
|
||||||
|
long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations);
|
||||||
|
put_task_struct(pid1_task);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("failed to get path for init's mount namespace: %ld\n",
|
||||||
|
ret);
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
ns_file = dentry_open(&ns_path, O_RDONLY, current_cred());
|
||||||
|
|
||||||
|
path_put(&ns_path);
|
||||||
|
if (IS_ERR(ns_file)) {
|
||||||
|
pr_warn("failed to open file for init's mount namespace: %ld\n",
|
||||||
|
PTR_ERR(ns_file));
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = get_unused_fd_flags(O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
pr_warn("failed to get an unused fd: %d\n", fd);
|
||||||
|
fput(ns_file);
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_install(fd, ns_file);
|
||||||
|
pr_info("calling sys_setns with fd : %d\n", fd);
|
||||||
|
|
||||||
|
ret = ksu_sys_setns(fd, CLONE_NEWNS);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("sys_setns failed: %ld\n", ret);
|
||||||
|
}
|
||||||
|
do_close_fd(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode == 2) {
|
||||||
|
long ret;
|
||||||
|
pr_info("mount namespace mode: independent\n");
|
||||||
|
|
||||||
|
ret = ksys_unshare(CLONE_NEWNS);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("call ksys_unshare failed: %ld\n", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try_drop_caps:
|
||||||
|
if (old_cred) {
|
||||||
|
pr_info("dropping temporarily capability.\n");
|
||||||
|
revert_creds(old_cred);
|
||||||
|
put_cred(new_cred);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RKSU: Use it wisely, not static.
|
||||||
|
void disable_seccomp(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
if (unlikely(!tsk))
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_spin_locked(&tsk->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
|
||||||
|
tsk->seccomp.mode = 0;
|
||||||
|
if (tsk->seccomp.filter) {
|
||||||
|
// 5.9+ have filter_count, but optional.
|
||||||
|
#ifdef KSU_OPTIONAL_SECCOMP_FILTER_CNT
|
||||||
|
atomic_set(&tsk->seccomp.filter_count, 0);
|
||||||
|
#endif
|
||||||
|
// some old kernel backport seccomp_filter_release..
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && \
|
||||||
|
defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)
|
||||||
|
seccomp_filter_release(tsk);
|
||||||
|
#else
|
||||||
|
// never, ever call seccomp_filter_release on 6.10+ (no effect)
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) && \
|
||||||
|
LINUX_VERSION_CODE < KERNEL_VERSION(6, 10, 0))
|
||||||
|
seccomp_filter_release(tsk);
|
||||||
|
#else
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
||||||
|
put_seccomp_filter(tsk);
|
||||||
|
#endif
|
||||||
|
tsk->seccomp.filter = NULL;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_with_root_profile(void)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
// a bit useless, but we just want less ifdefs
|
||||||
|
struct task_struct *p = current;
|
||||||
|
|
||||||
|
if (current_euid().val == 0) {
|
||||||
|
pr_warn("Already root, don't escape!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cred = prepare_creds();
|
||||||
|
if (!cred) {
|
||||||
|
pr_warn("prepare_creds failed!\n");
|
||||||
|
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(&p->sighand->siglock);
|
||||||
|
disable_seccomp(p);
|
||||||
|
spin_unlock_irq(&p->sighand->siglock);
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
setup_mount_namespace(profile->namespaces);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct task_struct *t;
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
66
kernel/app_profile.h
Normal file
66
kernel/app_profile.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -18,11 +18,17 @@
|
|||||||
#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"
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#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"
|
||||||
|
#define SYS_SETNS_SYMBOL __arm64_sys_setns
|
||||||
|
#else
|
||||||
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
|
#define SYS_SETNS_SYMBOL sys_setns
|
||||||
|
#endif
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
@@ -39,15 +45,24 @@
|
|||||||
#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"
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#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"
|
||||||
|
#define SYS_SETNS_SYMBOL __x64_sys_setns
|
||||||
|
#else
|
||||||
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
|
#define SYS_SETNS_SYMBOL sys_setns
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
#error "Unsupported arch"
|
#error "Unsupported arch"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* allow some architecutres to override `struct pt_regs` */
|
/* allow some architecutres to override `struct pt_regs` */
|
||||||
#ifndef __PT_REGS_CAST
|
#ifndef __PT_REGS_CAST
|
||||||
@@ -67,6 +82,10 @@
|
|||||||
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||||
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
|
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
|
||||||
|
#else
|
||||||
|
#define PT_REAL_REGS(regs) ((regs))
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,907 +0,0 @@
|
|||||||
#include <linux/capability.h>
|
|
||||||
#include <linux/cred.h>
|
|
||||||
#include <linux/dcache.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/init_task.h>
|
|
||||||
#include <linux/kallsyms.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/lsm_hooks.h>
|
|
||||||
#include <linux/mm.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/mount.h>
|
|
||||||
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
|
|
||||||
#ifdef MODULE
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/irqflags.h>
|
|
||||||
#include <linux/mm_types.h>
|
|
||||||
#include <linux/rcupdate.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "allowlist.h"
|
|
||||||
#include "arch.h"
|
|
||||||
#include "core_hook.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "ksud.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "selinux/selinux.h"
|
|
||||||
#include "throne_tracker.h"
|
|
||||||
#include "throne_tracker.h"
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
|
|
||||||
#include "kpm/kpm.h"
|
|
||||||
|
|
||||||
static bool ksu_module_mounted = false;
|
|
||||||
|
|
||||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
|
||||||
|
|
||||||
static bool ksu_su_compat_enabled = true;
|
|
||||||
extern void ksu_sucompat_init();
|
|
||||||
extern void ksu_sucompat_exit();
|
|
||||||
|
|
||||||
static inline bool is_allow_su()
|
|
||||||
{
|
|
||||||
if (is_manager()) {
|
|
||||||
// we are manager, allow!
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return ksu_is_allow_uid(current_uid().val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_unsupported_uid(uid_t uid)
|
|
||||||
{
|
|
||||||
#define LAST_APPLICATION_UID 19999
|
|
||||||
uid_t appid = uid % 100000;
|
|
||||||
return appid > LAST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_seccomp()
|
|
||||||
{
|
|
||||||
assert_spin_locked(¤t->sighand->siglock);
|
|
||||||
// disable seccomp
|
|
||||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
|
||||||
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
|
||||||
current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
|
|
||||||
#else
|
|
||||||
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SECCOMP
|
|
||||||
current->seccomp.mode = 0;
|
|
||||||
current->seccomp.filter = NULL;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void escape_to_root(void)
|
|
||||||
{
|
|
||||||
struct cred *cred;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
|
|
||||||
do {
|
|
||||||
cred = (struct cred *)__task_cred((current));
|
|
||||||
BUG_ON(!cred);
|
|
||||||
} while (!get_cred_rcu(cred));
|
|
||||||
|
|
||||||
if (cred->euid.val == 0) {
|
|
||||||
pr_warn("Already root, don't escape!\n");
|
|
||||||
rcu_read_unlock();
|
|
||||||
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);
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
|
||||||
{
|
|
||||||
if (!current->mm) {
|
|
||||||
// skip kernel threads
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current_uid().val != 1000) {
|
|
||||||
// skip non system uid
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!old_dentry || !new_dentry) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /data/system/packages.list.tmp -> /data/system/packages.list
|
|
||||||
if (strcmp(new_dentry->d_iname, "packages.list")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char path[128];
|
|
||||||
char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
|
|
||||||
if (IS_ERR(buf)) {
|
|
||||||
pr_err("dentry_path_raw failed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(buf, "/system/packages.list")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
|
||||||
new_dentry->d_iname, buf);
|
|
||||||
|
|
||||||
track_throne();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nuke_ext4_sysfs() {
|
|
||||||
struct path path;
|
|
||||||
int err = kern_path("/data/adb/modules", 0, &path);
|
|
||||||
if (err) {
|
|
||||||
pr_err("nuke path err: %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct super_block* sb = path.dentry->d_inode->i_sb;
|
|
||||||
const char* name = sb->s_type->name;
|
|
||||||
if (strcmp(name, "ext4") != 0) {
|
|
||||||
pr_info("nuke but module aren't mounted\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ext4_unregister_sysfs(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|
||||||
unsigned long arg4, unsigned long arg5)
|
|
||||||
{
|
|
||||||
// if success, we modify the arg5 as result!
|
|
||||||
u32 *result = (u32 *)arg5;
|
|
||||||
u32 reply_ok = KERNEL_SU_OPTION;
|
|
||||||
|
|
||||||
if (KERNEL_SU_OPTION != option) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: find it in throne tracker!
|
|
||||||
uid_t current_uid_val = current_uid().val;
|
|
||||||
uid_t manager_uid = ksu_get_manager_uid();
|
|
||||||
if (current_uid_val != manager_uid &&
|
|
||||||
current_uid_val % 100000 == manager_uid) {
|
|
||||||
ksu_set_manager_uid(current_uid_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool from_root = 0 == current_uid().val;
|
|
||||||
bool from_manager = is_manager();
|
|
||||||
|
|
||||||
if (!from_root && !from_manager) {
|
|
||||||
// only root or manager can access this interface
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (arg2 == CMD_BECOME_MANAGER) {
|
|
||||||
if (from_manager) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("become_manager: prctl reply error\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_GRANT_ROOT) {
|
|
||||||
if (is_allow_su()) {
|
|
||||||
pr_info("allow root for: %d\n", current_uid().val);
|
|
||||||
escape_to_root();
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("grant_root: prctl reply error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both root manager and root processes should be allowed to get version
|
|
||||||
if (arg2 == CMD_GET_VERSION) {
|
|
||||||
u32 version = KERNEL_SU_VERSION;
|
|
||||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
u32 version_flags = 0;
|
|
||||||
#ifdef MODULE
|
|
||||||
version_flags |= 0x1;
|
|
||||||
#endif
|
|
||||||
if (arg4 &&
|
|
||||||
copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_REPORT_EVENT) {
|
|
||||||
if (!from_root) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
switch (arg3) {
|
|
||||||
case EVENT_POST_FS_DATA: {
|
|
||||||
static bool post_fs_data_lock = false;
|
|
||||||
if (!post_fs_data_lock) {
|
|
||||||
post_fs_data_lock = true;
|
|
||||||
pr_info("post-fs-data triggered\n");
|
|
||||||
on_post_fs_data();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_BOOT_COMPLETED: {
|
|
||||||
static bool boot_complete_lock = false;
|
|
||||||
if (!boot_complete_lock) {
|
|
||||||
boot_complete_lock = true;
|
|
||||||
pr_info("boot_complete triggered\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_MODULE_MOUNTED: {
|
|
||||||
ksu_module_mounted = true;
|
|
||||||
pr_info("module mounted!\n");
|
|
||||||
nuke_ext4_sysfs();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_SET_SEPOLICY) {
|
|
||||||
if (!from_root) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!handle_sepolicy(arg3, arg4)) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("sepolicy: prctl reply error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_CHECK_SAFEMODE) {
|
|
||||||
if (ksu_is_safe_mode()) {
|
|
||||||
pr_warn("safemode enabled!\n");
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("safemode: prctl reply error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
|
||||||
u32 array[128];
|
|
||||||
u32 array_length;
|
|
||||||
bool success = ksu_get_allow_list(array, &array_length,
|
|
||||||
arg2 == CMD_GET_ALLOW_LIST);
|
|
||||||
if (success) {
|
|
||||||
if (!copy_to_user(arg4, &array_length,
|
|
||||||
sizeof(array_length)) &&
|
|
||||||
!copy_to_user(arg3, array,
|
|
||||||
sizeof(u32) * array_length)) {
|
|
||||||
if (copy_to_user(result, &reply_ok,
|
|
||||||
sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n",
|
|
||||||
arg2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("prctl copy allowlist error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
|
|
||||||
uid_t target_uid = (uid_t)arg3;
|
|
||||||
bool allow = false;
|
|
||||||
if (arg2 == CMD_UID_GRANTED_ROOT) {
|
|
||||||
allow = ksu_is_allow_uid(target_uid);
|
|
||||||
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
|
|
||||||
allow = ksu_uid_should_umount(target_uid);
|
|
||||||
} else {
|
|
||||||
pr_err("unknown cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("prctl copy err, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KPM
|
|
||||||
// ADD: 添加KPM模块控制
|
|
||||||
if(sukisu_is_kpm_control_code(arg2)) {
|
|
||||||
int res;
|
|
||||||
|
|
||||||
pr_info("KPM: calling before arg2=%d\n", (int) arg2);
|
|
||||||
|
|
||||||
res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// all other cmds are for 'root manager'
|
|
||||||
if (!from_manager) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are already manager
|
|
||||||
if (arg2 == CMD_GET_APP_PROFILE) {
|
|
||||||
struct app_profile profile;
|
|
||||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = ksu_get_app_profile(&profile);
|
|
||||||
if (success) {
|
|
||||||
if (copy_to_user(arg3, &profile, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_SET_APP_PROFILE) {
|
|
||||||
struct app_profile profile;
|
|
||||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
|
||||||
pr_err("copy profile failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: validate the params
|
|
||||||
if (ksu_set_app_profile(&profile, true)) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_IS_SU_ENABLED) {
|
|
||||||
if (copy_to_user(arg3, &ksu_su_compat_enabled,
|
|
||||||
sizeof(ksu_su_compat_enabled))) {
|
|
||||||
pr_err("copy su compat failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg2 == CMD_ENABLE_SU) {
|
|
||||||
bool enabled = (arg3 != 0);
|
|
||||||
if (enabled == ksu_su_compat_enabled) {
|
|
||||||
pr_info("cmd enable su but no need to change.\n");
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
ksu_sucompat_init();
|
|
||||||
} else {
|
|
||||||
ksu_sucompat_exit();
|
|
||||||
}
|
|
||||||
ksu_su_compat_enabled = enabled;
|
|
||||||
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_appuid(kuid_t uid)
|
|
||||||
{
|
|
||||||
#define PER_USER_RANGE 100000
|
|
||||||
#define FIRST_APPLICATION_UID 10000
|
|
||||||
#define LAST_APPLICATION_UID 19999
|
|
||||||
|
|
||||||
uid_t appid = uid.val % PER_USER_RANGE;
|
|
||||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool should_umount(struct path *path)
|
|
||||||
{
|
|
||||||
if (!path) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
|
||||||
pr_info("ignore global mnt namespace process: %d\n",
|
|
||||||
current_uid().val);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
|
||||||
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
|
||||||
return strcmp(fstype, "overlay") == 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_umount(const char *mnt, bool check_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.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are only interest in some specific mounts
|
|
||||||
if (check_mnt && !should_umount(&path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_umount_mnt(&path, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|
||||||
{
|
|
||||||
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
|
||||||
if (!ksu_module_mounted) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new || !old) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
kuid_t new_uid = new->uid;
|
|
||||||
kuid_t old_uid = old->uid;
|
|
||||||
|
|
||||||
if (0 != old_uid.val) {
|
|
||||||
// old process is not root, ignore it.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
|
||||||
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_is_allow_uid(new_uid.val)) {
|
|
||||||
// pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("uid: %d should not umount!\n", current_uid().val);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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!
|
|
||||||
bool is_zygote_child = is_zygote(old->security);
|
|
||||||
if (!is_zygote_child) {
|
|
||||||
pr_info("handle umount ignore non zygote child: %d\n",
|
|
||||||
current->pid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
// umount the target mnt
|
|
||||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
|
||||||
current->pid);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
|
||||||
// filter the mountpoint whose target is `/data/adb`
|
|
||||||
try_umount("/system", true, 0);
|
|
||||||
try_umount("/vendor", true, 0);
|
|
||||||
try_umount("/product", true, 0);
|
|
||||||
try_umount("/system_ext", true, 0);
|
|
||||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
|
||||||
|
|
||||||
// try umount ksu temp path
|
|
||||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init functons
|
|
||||||
|
|
||||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int option = (int)PT_REGS_PARM1(real_regs);
|
|
||||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
|
||||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
|
||||||
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
|
|
||||||
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
|
||||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe prctl_kp = {
|
|
||||||
.symbol_name = PRCTL_SYMBOL,
|
|
||||||
.pre_handler = handler_pre,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
||||||
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
|
|
||||||
struct renamedata *rd = PT_REGS_PARM1(regs);
|
|
||||||
struct dentry *old_entry = rd->old_dentry;
|
|
||||||
struct dentry *new_entry = rd->new_dentry;
|
|
||||||
#else
|
|
||||||
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
|
|
||||||
struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ksu_handle_rename(old_entry, new_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe renameat_kp = {
|
|
||||||
.symbol_name = "vfs_rename",
|
|
||||||
.pre_handler = renameat_handler_pre,
|
|
||||||
};
|
|
||||||
|
|
||||||
__maybe_unused int ksu_kprobe_init(void)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
rc = register_kprobe(&prctl_kp);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
pr_info("prctl kprobe failed: %d.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = register_kprobe(&renameat_kp);
|
|
||||||
pr_info("renameat kp: %d\n", rc);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
__maybe_unused int ksu_kprobe_exit(void)
|
|
||||||
{
|
|
||||||
unregister_kprobe(&prctl_kp);
|
|
||||||
unregister_kprobe(&renameat_kp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|
||||||
unsigned long arg4, unsigned long arg5)
|
|
||||||
{
|
|
||||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
|
||||||
return -ENOSYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
|
||||||
struct inode *new_inode, struct dentry *new_dentry)
|
|
||||||
{
|
|
||||||
return ksu_handle_rename(old_dentry, new_dentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
return ksu_handle_setuid(new, old);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef MODULE
|
|
||||||
static struct security_hook_list ksu_hooks[] = {
|
|
||||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
|
||||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
|
||||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
|
||||||
};
|
|
||||||
|
|
||||||
void __init ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
static int override_security_head(void *head, const void *new_head, size_t len)
|
|
||||||
{
|
|
||||||
unsigned long base = (unsigned long)head & PAGE_MASK;
|
|
||||||
unsigned long offset = offset_in_page(head);
|
|
||||||
|
|
||||||
// this is impossible for our case because the page alignment
|
|
||||||
// but be careful for other cases!
|
|
||||||
BUG_ON(offset + len > PAGE_SIZE);
|
|
||||||
struct page *page = phys_to_page(__pa(base));
|
|
||||||
if (!page) {
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
|
|
||||||
if (!addr) {
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
local_irq_disable();
|
|
||||||
memcpy(addr + offset, new_head, len);
|
|
||||||
local_irq_enable();
|
|
||||||
vunmap(addr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_security_hook_list(struct hlist_head *head)
|
|
||||||
{
|
|
||||||
struct hlist_node *temp;
|
|
||||||
struct security_hook_list *entry;
|
|
||||||
|
|
||||||
if (!head)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hlist_for_each_entry_safe (entry, temp, head, list) {
|
|
||||||
hlist_del(&entry->list);
|
|
||||||
kfree(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct hlist_head *copy_security_hlist(struct hlist_head *orig)
|
|
||||||
{
|
|
||||||
struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
|
|
||||||
if (!new_head)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
INIT_HLIST_HEAD(new_head);
|
|
||||||
|
|
||||||
struct security_hook_list *entry;
|
|
||||||
struct security_hook_list *new_entry;
|
|
||||||
|
|
||||||
hlist_for_each_entry (entry, orig, list) {
|
|
||||||
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
|
|
||||||
if (!new_entry) {
|
|
||||||
free_security_hook_list(new_head);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*new_entry = *entry;
|
|
||||||
|
|
||||||
hlist_add_tail_rcu(&new_entry->list, new_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LSM_SEARCH_MAX 180 // This should be enough to iterate
|
|
||||||
static void *find_head_addr(void *security_ptr, int *index)
|
|
||||||
{
|
|
||||||
if (!security_ptr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct hlist_head *head_start =
|
|
||||||
(struct hlist_head *)&security_hook_heads;
|
|
||||||
|
|
||||||
for (int i = 0; i < LSM_SEARCH_MAX; i++) {
|
|
||||||
struct hlist_head *head = head_start + i;
|
|
||||||
struct security_hook_list *pos;
|
|
||||||
hlist_for_each_entry (pos, head, list) {
|
|
||||||
if (pos->hook.capget == security_ptr) {
|
|
||||||
if (index) {
|
|
||||||
*index = i;
|
|
||||||
}
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GET_SYMBOL_ADDR(sym) \
|
|
||||||
({ \
|
|
||||||
void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
|
|
||||||
if (!addr) { \
|
|
||||||
addr = kallsyms_lookup_name(#sym); \
|
|
||||||
} \
|
|
||||||
addr; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
|
|
||||||
do { \
|
|
||||||
static struct security_hook_list hook = { \
|
|
||||||
.hook = { .name = func } \
|
|
||||||
}; \
|
|
||||||
hook.head = head_ptr; \
|
|
||||||
hook.lsm = "ksu"; \
|
|
||||||
struct hlist_head *new_head = copy_security_hlist(hook.head); \
|
|
||||||
if (!new_head) { \
|
|
||||||
pr_err("Failed to copy security list: %s\n", #name); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
hlist_add_tail_rcu(&hook.list, new_head); \
|
|
||||||
if (override_security_head(hook.head, new_head, \
|
|
||||||
sizeof(*new_head))) { \
|
|
||||||
free_security_hook_list(new_head); \
|
|
||||||
pr_err("Failed to hack lsm for: %s\n", #name); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void __init ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
|
|
||||||
void *prctl_head = find_head_addr(cap_prctl, NULL);
|
|
||||||
if (prctl_head) {
|
|
||||||
if (prctl_head != &security_hook_heads.task_prctl) {
|
|
||||||
pr_warn("prctl's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
|
|
||||||
} else {
|
|
||||||
pr_warn("Failed to find task_prctl!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int inode_killpriv_index = -1;
|
|
||||||
void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
|
|
||||||
find_head_addr(cap_killpriv, &inode_killpriv_index);
|
|
||||||
if (inode_killpriv_index < 0) {
|
|
||||||
pr_warn("Failed to find inode_rename, use kprobe instead!\n");
|
|
||||||
register_kprobe(&renameat_kp);
|
|
||||||
} else {
|
|
||||||
int inode_rename_index = inode_killpriv_index +
|
|
||||||
&security_hook_heads.inode_rename -
|
|
||||||
&security_hook_heads.inode_killpriv;
|
|
||||||
struct hlist_head *head_start =
|
|
||||||
(struct hlist_head *)&security_hook_heads;
|
|
||||||
void *inode_rename_head = head_start + inode_rename_index;
|
|
||||||
if (inode_rename_head != &security_hook_heads.inode_rename) {
|
|
||||||
pr_warn("inode_rename's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
|
|
||||||
ksu_inode_rename);
|
|
||||||
}
|
|
||||||
void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
|
|
||||||
void *setuid_head = find_head_addr(cap_setuid, NULL);
|
|
||||||
if (setuid_head) {
|
|
||||||
if (setuid_head != &security_hook_heads.task_fix_setuid) {
|
|
||||||
pr_warn("setuid's address has shifted!\n");
|
|
||||||
}
|
|
||||||
KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
|
|
||||||
ksu_task_fix_setuid);
|
|
||||||
} else {
|
|
||||||
pr_warn("Failed to find task_fix_setuid!\n");
|
|
||||||
}
|
|
||||||
smp_mb();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void __init ksu_core_init(void)
|
|
||||||
{
|
|
||||||
ksu_lsm_hook_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_KPROBE
|
|
||||||
pr_info("ksu_core_kprobe_exit\n");
|
|
||||||
// we dont use this now
|
|
||||||
// ksu_kprobe_exit();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#ifndef __KSU_H_KSU_CORE
|
|
||||||
#define __KSU_H_KSU_CORE
|
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
|
|
||||||
void __init ksu_core_init(void);
|
|
||||||
void ksu_core_exit(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
505
kernel/dynamic_manager.c
Normal file
505
kernel/dynamic_manager.c
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
#endif
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
#include <crypto/sha2.h>
|
||||||
|
#else
|
||||||
|
#include <crypto/sha.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#define MAX_MANAGERS 2
|
||||||
|
|
||||||
|
// Dynamic sign configuration
|
||||||
|
static struct dynamic_manager_config dynamic_manager = {
|
||||||
|
.size = 0x300,
|
||||||
|
.hash = "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
.is_set = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Multi-manager state
|
||||||
|
static struct manager_info active_managers[MAX_MANAGERS];
|
||||||
|
static DEFINE_SPINLOCK(managers_lock);
|
||||||
|
static DEFINE_SPINLOCK(dynamic_manager_lock);
|
||||||
|
|
||||||
|
// Work queues for persistent storage
|
||||||
|
static struct work_struct save_dynamic_manager_work;
|
||||||
|
static struct work_struct load_dynamic_manager_work;
|
||||||
|
static struct work_struct clear_dynamic_manager_work;
|
||||||
|
|
||||||
|
bool ksu_is_dynamic_manager_enabled(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
enabled = dynamic_manager.is_set;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_add_manager(uid_t uid, int signature_index)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
// Check if manager already exists and update
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
active_managers[i].signature_index = signature_index;
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find free slot for new manager
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (!active_managers[i].is_active) {
|
||||||
|
active_managers[i].uid = uid;
|
||||||
|
active_managers[i].signature_index = signature_index;
|
||||||
|
active_managers[i].is_active = true;
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_info("Added manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
pr_warn("Failed to add manager, no free slots\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_remove_manager(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
pr_info("Removed manager uid=%d\n", uid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_is_any_manager(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool is_manager = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
is_manager = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
return is_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_manager_signature_index(uid_t uid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int signature_index = -1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Check traditional manager first
|
||||||
|
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
|
||||||
|
return DYNAMIC_SIGN_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_is_dynamic_manager_enabled()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||||
|
signature_index = active_managers[i].signature_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
return signature_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
if (active_managers[i].is_active) {
|
||||||
|
pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n",
|
||||||
|
active_managers[i].uid, active_managers[i].signature_index);
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_get_active_managers(struct manager_list_info *info)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int i, count = 0;
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add traditional manager first
|
||||||
|
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
|
||||||
|
info->managers[count].uid = ksu_manager_uid;
|
||||||
|
info->managers[count].signature_index = 0;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dynamic managers
|
||||||
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
|
spin_lock_irqsave(&managers_lock, flags);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
|
||||||
|
if (active_managers[i].is_active) {
|
||||||
|
info->managers[count].uid = active_managers[i].uid;
|
||||||
|
info->managers[count].signature_index = active_managers[i].signature_index;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&managers_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->count = count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_save_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
u32 magic = DYNAMIC_MANAGER_FILE_MAGIC;
|
||||||
|
u32 version = DYNAMIC_MANAGER_FILE_VERSION;
|
||||||
|
struct dynamic_manager_config config_to_save;
|
||||||
|
loff_t off = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
struct file *fp;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
config_to_save = dynamic_manager;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
if (!config_to_save.is_set) {
|
||||||
|
pr_info("Dynamic sign config not set, skipping save\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||||
|
pr_err("save_dynamic_manager write magic failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
|
pr_err("save_dynamic_manager write version failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
|
||||||
|
pr_err("save_dynamic_manager write config failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config saved successfully\n");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_load_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
struct file *fp = NULL;
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
struct dynamic_manager_config loaded_config;
|
||||||
|
unsigned long flags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
if (PTR_ERR(fp) == -ENOENT) {
|
||||||
|
pr_info("No saved dynamic manager config found\n");
|
||||||
|
} else {
|
||||||
|
pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||||
|
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
|
||||||
|
pr_err("dynamic manager file invalid magic: %x!\n", magic);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||||
|
pr_err("dynamic manager read version failed\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("dynamic manager file version: %d\n", version);
|
||||||
|
|
||||||
|
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
|
||||||
|
if (ret <= 0) {
|
||||||
|
pr_info("load_dynamic_manager read err: %zd\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != sizeof(loaded_config)) {
|
||||||
|
pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) {
|
||||||
|
pr_err("Invalid saved config size: 0x%x\n", loaded_config.size);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(loaded_config.hash) != 64) {
|
||||||
|
pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash format
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
char c = loaded_config.hash[i];
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||||
|
pr_err("Invalid saved config hash character at position %d: %c\n", i, c);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager = loaded_config;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n",
|
||||||
|
loaded_config.size, loaded_config.hash);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool persistent_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&save_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_clear_dynamic_manager(struct work_struct *work)
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
struct file *fp;
|
||||||
|
char zero_buffer[512];
|
||||||
|
|
||||||
|
memset(zero_buffer, 0, sizeof(zero_buffer));
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write null bytes to overwrite the file content
|
||||||
|
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
|
||||||
|
pr_err("clear_dynamic_manager write null bytes failed.\n");
|
||||||
|
} else {
|
||||||
|
pr_info("Dynamic sign config file cleared successfully\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool clear_dynamic_manager_file(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&clear_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config->operation) {
|
||||||
|
case DYNAMIC_MANAGER_OP_SET:
|
||||||
|
if (config->size < 0x100 || config->size > 0x1000) {
|
||||||
|
pr_err("invalid size: 0x%x\n", config->size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(config->hash) != 64) {
|
||||||
|
pr_err("invalid hash length: %zu\n", strlen(config->hash));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash format
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
char c = config->hash[i];
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||||
|
pr_err("invalid hash character at position %d: %c\n", i, c);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager.size = config->size;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||||
|
#else
|
||||||
|
strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||||
|
#endif
|
||||||
|
dynamic_manager.is_set = 1;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
persistent_dynamic_manager();
|
||||||
|
pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
|
||||||
|
config->size, config->hash);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNAMIC_MANAGER_OP_GET:
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
if (dynamic_manager.is_set) {
|
||||||
|
config->size = dynamic_manager.size;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||||
|
#else
|
||||||
|
strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||||
|
#endif
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ENODATA;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNAMIC_MANAGER_OP_CLEAR:
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
dynamic_manager.size = 0x300;
|
||||||
|
strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
dynamic_manager.is_set = 0;
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
// Clear only dynamic managers, preserve default manager
|
||||||
|
clear_dynamic_manager();
|
||||||
|
|
||||||
|
// Clear file using the same method as save
|
||||||
|
clear_dynamic_manager_file();
|
||||||
|
|
||||||
|
pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("Invalid dynamic manager operation: %d\n", config->operation);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_load_dynamic_manager(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&load_dynamic_manager_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_dynamic_manager_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
INIT_WORK(&save_dynamic_manager_work, do_save_dynamic_manager);
|
||||||
|
INIT_WORK(&load_dynamic_manager_work, do_load_dynamic_manager);
|
||||||
|
INIT_WORK(&clear_dynamic_manager_work, do_clear_dynamic_manager);
|
||||||
|
|
||||||
|
// Initialize manager slots
|
||||||
|
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||||
|
active_managers[i].is_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_load_dynamic_manager();
|
||||||
|
|
||||||
|
pr_info("Dynamic sign initialized with conditional multi-manager support\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_dynamic_manager_exit(void)
|
||||||
|
{
|
||||||
|
clear_dynamic_manager();
|
||||||
|
|
||||||
|
// Save current config before exit
|
||||||
|
do_save_dynamic_manager(NULL);
|
||||||
|
pr_info("Dynamic sign exited with persistent storage\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get dynamic manager configuration for signature verification
|
||||||
|
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||||
|
if (dynamic_manager.is_set) {
|
||||||
|
if (size) *size = dynamic_manager.size;
|
||||||
|
if (hash) *hash = dynamic_manager.hash;
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
51
kernel/dynamic_manager.h
Normal file
51
kernel/dynamic_manager.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef __KSU_H_DYNAMIC_MANAGER
|
||||||
|
#define __KSU_H_DYNAMIC_MANAGER
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
#define DYNAMIC_MANAGER_FILE_MAGIC 0x7f445347 // 'DSG', u32
|
||||||
|
#define DYNAMIC_MANAGER_FILE_VERSION 1 // u32
|
||||||
|
#define KERNEL_SU_DYNAMIC_MANAGER "/data/adb/ksu/.dynamic_manager"
|
||||||
|
#define DYNAMIC_SIGN_INDEX 100
|
||||||
|
|
||||||
|
struct dynamic_sign_key {
|
||||||
|
unsigned int size;
|
||||||
|
const char *hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DYNAMIC_SIGN_DEFAULT_CONFIG { \
|
||||||
|
.size = 0x300, \
|
||||||
|
.hash = "0000000000000000000000000000000000000000000000000000000000000000" \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dynamic_manager_config {
|
||||||
|
unsigned int size;
|
||||||
|
char hash[65];
|
||||||
|
int is_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct manager_info {
|
||||||
|
uid_t uid;
|
||||||
|
int signature_index;
|
||||||
|
bool is_active;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamic sign operations
|
||||||
|
void ksu_dynamic_manager_init(void);
|
||||||
|
void ksu_dynamic_manager_exit(void);
|
||||||
|
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config);
|
||||||
|
bool ksu_load_dynamic_manager(void);
|
||||||
|
bool ksu_is_dynamic_manager_enabled(void);
|
||||||
|
|
||||||
|
// Multi-manager operations
|
||||||
|
void ksu_add_manager(uid_t uid, int signature_index);
|
||||||
|
void ksu_remove_manager(uid_t uid);
|
||||||
|
bool ksu_is_any_manager(uid_t uid);
|
||||||
|
int ksu_get_manager_signature_index(uid_t uid);
|
||||||
|
int ksu_get_active_managers(struct manager_list_info *info);
|
||||||
|
|
||||||
|
// Configuration access for signature verification
|
||||||
|
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash);
|
||||||
|
|
||||||
|
#endif
|
||||||
176
kernel/feature.c
Normal file
176
kernel/feature.c
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#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");
|
||||||
|
}
|
||||||
36
kernel/feature.h
Normal file
36
kernel/feature.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#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_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
|
||||||
402
kernel/file_wrapper.c
Normal file
402
kernel/file_wrapper.c
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/anon_inodes.h>
|
||||||
|
#include <linux/aio.h> // kernel 3.18
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
|
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) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
|
||||||
|
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
|
||||||
|
|
||||||
|
// int (*readdir) (struct file *, void *, filldir_t);
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
|
||||||
|
static int ksu_wrapper_readdir(struct file *fp, void *ptr, filldir_t filler) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->readdir(orig, ptr, filler);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// typedef unsigned __bitwise __poll_t;
|
||||||
|
static unsigned __bitwise 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;
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) // int (*setlease)(struct file *, long, struct file_lock **, void **);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#else // int (*setlease)(struct file *, long, struct file_lock **);
|
||||||
|
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||||
|
static int 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);
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1593-1606;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
static ssize_t ksu_wrapper_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out,
|
||||||
|
loff_t pos_out, size_t len, unsigned int flags) {
|
||||||
|
struct ksu_file_wrapper* data = file_out->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->copy_file_range(file_in, pos_in, orig, pos_out, len, flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||||
|
// no REMAP_FILE_DEDUP: use file_in
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1598-1599;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=403-404;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
// REMAP_FILE_DEDUP: use file_out
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=483-484;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
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) {
|
||||||
|
if (remap_flags & REMAP_FILE_DEDUP) {
|
||||||
|
struct ksu_file_wrapper* data = file_out->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->remap_file_range(file_in, pos_in, orig, pos_out, len, remap_flags);
|
||||||
|
} else {
|
||||||
|
struct ksu_file_wrapper* data = file_in->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
|
||||||
|
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
|
||||||
|
p->ops.readdir = fp->f_op->readdir ? ksu_wrapper_readdir : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
|
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
|
||||||
|
#endif
|
||||||
|
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;
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||||
|
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;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||||
|
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
|
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||||
|
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
|
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
|
||||||
|
#endif
|
||||||
|
#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);
|
||||||
|
}
|
||||||
15
kernel/file_wrapper.h
Normal file
15
kernel/file_wrapper.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#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,88 +1,149 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/nsproxy.h>
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
extern struct task_struct init_task;
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
#include <linux/key.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/lsm_hooks.h>
|
||||||
|
|
||||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
extern int install_session_keyring_to_cred(struct cred *, struct key *);
|
||||||
struct ksu_ns_fs_saved {
|
struct key *init_session_keyring = NULL;
|
||||||
struct nsproxy *ns;
|
|
||||||
struct fs_struct *fs;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
static int install_session_keyring(struct key *keyring)
|
||||||
{
|
{
|
||||||
ns_fs_saved->ns = current->nsproxy;
|
struct cred *new;
|
||||||
ns_fs_saved->fs = current->fs;
|
int ret;
|
||||||
}
|
|
||||||
|
|
||||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
new = prepare_creds();
|
||||||
{
|
if (!new)
|
||||||
current->nsproxy = ns_fs_saved->ns;
|
return -ENOMEM;
|
||||||
current->fs = ns_fs_saved->fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool android_context_saved_checked = false;
|
ret = install_session_keyring_to_cred(new, keyring);
|
||||||
static bool android_context_saved_enabled = false;
|
if (ret < 0) {
|
||||||
static struct ksu_ns_fs_saved android_context_saved;
|
abort_creds(new);
|
||||||
|
return ret;
|
||||||
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;
|
|
||||||
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);
|
|
||||||
ksu_save_ns_fs(&android_context_saved);
|
|
||||||
} else {
|
|
||||||
pr_info("android context saved disabled\n");
|
|
||||||
}
|
}
|
||||||
task_unlock(current);
|
|
||||||
|
return commit_creds(new);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
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
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
struct ksu_ns_fs_saved saved;
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
if (android_context_saved_enabled) {
|
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||||
pr_info("start switch current nsproxy and fs to android context\n");
|
(current->flags & PF_WQ_WORKER)) {
|
||||||
task_lock(current);
|
pr_info("installing init session keyring for older kernel\n");
|
||||||
ksu_save_ns_fs(&saved);
|
install_session_keyring(init_session_keyring);
|
||||||
ksu_load_ns_fs(&android_context_saved);
|
|
||||||
task_unlock(current);
|
|
||||||
}
|
}
|
||||||
struct file *fp = filp_open(filename, flags, mode);
|
#endif
|
||||||
if (android_context_saved_enabled) {
|
return filp_open(filename, flags, mode);
|
||||||
task_lock(current);
|
|
||||||
ksu_load_ns_fs(&saved);
|
|
||||||
task_unlock(current);
|
|
||||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
loff_t *pos)
|
loff_t *pos)
|
||||||
{
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
|
||||||
|
defined(KSU_OPTIONAL_KERNEL_READ)
|
||||||
return kernel_read(p, buf, count, pos);
|
return kernel_read(p, buf, count, pos);
|
||||||
|
#else
|
||||||
|
loff_t offset = pos ? *pos : 0;
|
||||||
|
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||||
|
if (pos && result > 0) {
|
||||||
|
*pos = offset + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||||
loff_t *pos)
|
loff_t *pos)
|
||||||
{
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
|
||||||
|
defined(KSU_OPTIONAL_KERNEL_WRITE)
|
||||||
return kernel_write(p, buf, count, pos);
|
return kernel_write(p, buf, count, pos);
|
||||||
|
#else
|
||||||
|
loff_t offset = pos ? *pos : 0;
|
||||||
|
ssize_t result = kernel_write(p, buf, count, offset);
|
||||||
|
if (pos && result > 0) {
|
||||||
|
*pos = offset + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \
|
||||||
|
defined(KSU_OPTIONAL_STRNCPY)
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
long count)
|
long count)
|
||||||
{
|
{
|
||||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||||
}
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||||
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
|
long count)
|
||||||
|
{
|
||||||
|
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
||||||
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
|
long count)
|
||||||
|
{
|
||||||
|
mm_segment_t old_fs = get_fs();
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (unlikely(count <= 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
set_fs(USER_DS);
|
||||||
|
pagefault_disable();
|
||||||
|
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||||
|
pagefault_enable();
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
if (ret >= count) {
|
||||||
|
ret = count;
|
||||||
|
dst[ret - 1] = '\0';
|
||||||
|
} else if (ret > 0) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||||
|
return copy_from_user_nofault(dst, src, size);
|
||||||
|
#else
|
||||||
|
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
|
||||||
|
long ret = -EFAULT;
|
||||||
|
mm_segment_t old_fs = get_fs();
|
||||||
|
|
||||||
|
set_fs(USER_DS);
|
||||||
|
// tweaked to use ksu_access_ok
|
||||||
|
if (ksu_access_ok(src, size)) {
|
||||||
|
pagefault_disable();
|
||||||
|
ret = __copy_from_user_inatomic(dst, src, size);
|
||||||
|
pagefault_enable();
|
||||||
|
}
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/fdtable.h>
|
||||||
#include "ss/policydb.h"
|
#include "ss/policydb.h"
|
||||||
#include "linux/key.h"
|
#include "linux/key.h"
|
||||||
|
|
||||||
@@ -11,25 +13,78 @@
|
|||||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||||
* From ss/ebitmap.h
|
* From ss/ebitmap.h
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
||||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||||
#define CONFIG_IS_HW_HISI
|
#define CONFIG_IS_HW_HISI
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Checks for UH, KDP and RKP
|
||||||
|
#ifdef SAMSUNG_UH_DRIVER_EXIST
|
||||||
|
#if defined(CONFIG_UH) || defined(CONFIG_KDP) || defined(CONFIG_RKP)
|
||||||
|
#error "CONFIG_UH, CONFIG_KDP and CONFIG_RKP is enabled! Please disable or remove it before compile a kernel with KernelSU!"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||||
const void __user *unsafe_addr,
|
const void __user *unsafe_addr,
|
||||||
long count);
|
long count);
|
||||||
|
|
||||||
extern void ksu_android_ns_fs_check();
|
|
||||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||||
umode_t mode);
|
umode_t mode);
|
||||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
loff_t *pos);
|
loff_t *pos);
|
||||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||||
size_t count, loff_t *pos);
|
size_t count, loff_t *pos);
|
||||||
|
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
|
||||||
|
/*
|
||||||
|
* ksu_copy_from_user_retry
|
||||||
|
* try nofault copy first, if it fails, try with plain
|
||||||
|
* paramters are the same as copy_from_user
|
||||||
|
* 0 = success
|
||||||
|
*/
|
||||||
|
static long ksu_copy_from_user_retry(void *to,
|
||||||
|
const void __user *from, unsigned long count)
|
||||||
|
{
|
||||||
|
long ret = ksu_copy_from_user_nofault(to, from, count);
|
||||||
|
if (likely(!ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// we faulted! fallback to slow path
|
||||||
|
return copy_from_user(to, from, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
extern struct key *init_session_keyring;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||||
|
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
||||||
|
#else
|
||||||
|
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Linux >= 5.7
|
||||||
|
// task_work_add (struct, struct, enum)
|
||||||
|
// Linux pre-5.7
|
||||||
|
// task_work_add (struct, struct, bool)
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
|
||||||
|
#ifndef TWA_RESUME
|
||||||
|
#define TWA_RESUME true
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline int do_close_fd(unsigned int fd)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
return close_fd(fd);
|
||||||
|
#else
|
||||||
|
return __close_fd(current->files, fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
206
kernel/kernel_umount.c
Normal file
206
kernel/kernel_umount.c
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#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>
|
||||||
|
#ifndef KSU_HAS_PATH_UMOUNT
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "ksud.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,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \
|
||||||
|
defined(KSU_HAS_PATH_UMOUNT)
|
||||||
|
extern int path_umount(struct path *path, int flags);
|
||||||
|
static void ksu_umount_mnt(const char *__never_use_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void ksu_sys_umount(const char *mnt, int flags)
|
||||||
|
{
|
||||||
|
char __user *usermnt = (char __user *)mnt;
|
||||||
|
mm_segment_t old_fs;
|
||||||
|
|
||||||
|
old_fs = get_fs();
|
||||||
|
set_fs(KERNEL_DS);
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
ksys_umount(usermnt, flags);
|
||||||
|
#else
|
||||||
|
sys_umount(usermnt, flags); // cuz asmlinkage long sys##name
|
||||||
|
#endif
|
||||||
|
set_fs(old_fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ksu_umount_mnt(mnt, __unused, flags) \
|
||||||
|
({ \
|
||||||
|
path_put(__unused); \
|
||||||
|
ksu_sys_umount(mnt, flags); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static 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(mnt, &path, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void do_umount_work(void)
|
||||||
|
{
|
||||||
|
struct mount_entry *entry;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
do_umount_work();
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
if (saved)
|
||||||
|
revert_creds(saved);
|
||||||
|
|
||||||
|
if (tw->old_cred)
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
||||||
|
{
|
||||||
|
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
||||||
|
if (!ksu_module_mounted) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_kernel_umount_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: isolated process which directly forks from zygote is not handled
|
||||||
|
if (!is_appuid(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_uid_should_umount(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!
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// umount the target mnt
|
||||||
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct umount_tw *tw;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Using task work for non-kp context is expansive?
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
do_umount_work();
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
23
kernel/kernel_umount.h
Normal file
23
kernel/kernel_umount.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
// 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
|
||||||
@@ -4,3 +4,6 @@ obj-y += super_access.o
|
|||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
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$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -29,74 +29,77 @@
|
|||||||
#include "../allowlist.h"
|
#include "../allowlist.h"
|
||||||
#include "../manager.h"
|
#include "../manager.h"
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
unsigned long sukisu_compact_find_symbol(const char *name);
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
// 兼容函数 for KPM
|
// 兼容函数 for KPM
|
||||||
|
|
||||||
static
|
static int sukisu_is_su_allow_uid(uid_t uid)
|
||||||
int sukisu_is_su_allow_uid(uid_t uid) {
|
{
|
||||||
return ksu_is_allow_uid(uid) ? 1 : 0;
|
return ksu_is_allow_uid(uid) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_get_ap_mod_exclude(uid_t uid)
|
||||||
int sukisu_get_ap_mod_exclude(uid_t uid) {
|
{
|
||||||
// Not supported
|
// Not supported
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_is_uid_should_umount(uid_t uid)
|
||||||
int sukisu_is_uid_should_umount(uid_t uid) {
|
{
|
||||||
return ksu_uid_should_umount(uid) ? 1 : 0;
|
return ksu_uid_should_umount(uid) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static int sukisu_is_current_uid_manager()
|
||||||
int sukisu_is_current_uid_manager() {
|
{
|
||||||
return is_manager();
|
return is_manager();
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static uid_t sukisu_get_manager_uid()
|
||||||
uid_t sukisu_get_manager_uid() {
|
{
|
||||||
return ksu_manager_uid;
|
return ksu_manager_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
struct CompactAddressSymbol {
|
struct CompactAddressSymbol {
|
||||||
const char* symbol_name;
|
const char *symbol_name;
|
||||||
void* addr;
|
void *addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct CompactAddressSymbol address_symbol [] = {
|
static struct CompactAddressSymbol address_symbol[] = {
|
||||||
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
||||||
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
||||||
{ "is_run_in_sukisu_ultra", (void*)1 },
|
{ "is_run_in_sukisu_ultra", (void *)1 },
|
||||||
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
||||||
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
||||||
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
||||||
{ "is_current_uid_manager", &sukisu_is_current_uid_manager },
|
{ "is_current_uid_manager", &sukisu_is_current_uid_manager },
|
||||||
{ "get_manager_uid", &sukisu_get_manager_uid }
|
{ "get_manager_uid", &sukisu_get_manager_uid }
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name) {
|
unsigned long sukisu_compact_find_symbol(const char *name)
|
||||||
int i;
|
{
|
||||||
unsigned long addr;
|
int i;
|
||||||
|
unsigned long addr;
|
||||||
|
|
||||||
// 先自己在地址表部分查出来
|
// 先自己在地址表部分查出来
|
||||||
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
for (i = 0;
|
||||||
struct CompactAddressSymbol* symbol = &address_symbol[i];
|
i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol));
|
||||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
i++) {
|
||||||
return (unsigned long) symbol->addr;
|
struct CompactAddressSymbol *symbol = &address_symbol[i];
|
||||||
}
|
if (strcmp(name, symbol->symbol_name) == 0) {
|
||||||
}
|
return (unsigned long)symbol->addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 通过内核来查
|
// 通过内核来查
|
||||||
addr = kallsyms_lookup_name(name);
|
addr = kallsyms_lookup_name(name);
|
||||||
if(addr) {
|
if (addr) {
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#ifndef ___SUKISU_KPM_COMPACT_H
|
#ifndef ___SUKISU_KPM_COMPACT_H
|
||||||
#define ___SUKISU_KPM_COMPACT_H
|
#define ___SUKISU_KPM_COMPACT_H
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
unsigned long sukisu_compact_find_symbol(const char *name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
328
kernel/kpm/kpm.c
328
kernel/kpm/kpm.c
@@ -8,13 +8,11 @@
|
|||||||
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
|
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
|
||||||
* 并参照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>
|
||||||
@@ -23,26 +21,26 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#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> // 需要启用 CONFIG_MODULES
|
#include <linux/moduleloader.h>
|
||||||
#endif
|
#endif
|
||||||
#include "kpm.h"
|
#include "kpm.h"
|
||||||
#include "compact.h"
|
#include "compact.h"
|
||||||
|
#include "../kernel_compat.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__)
|
||||||
@@ -54,131 +52,231 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ============================================================================================
|
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
|
||||||
|
const char *args, void *ptr, int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
|
||||||
|
"path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||||
|
|
||||||
noinline
|
__asm__ volatile("nop");
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_load_module_path(const char* path, const char* args, void* ptr, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). path=%s args=%s ptr=%p\n", path, args, ptr);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_unload_module(const char* name, void* ptr, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). name=%s ptr=%p\n", name, ptr);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_num(void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = 0;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_info(const char* name, void __user* out, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_info). name=%s buffer=%p\n", name, out);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_list(void __user* out, unsigned int bufferSize, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_list). buffer=%p size=%d\n", out, bufferSize);
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_control(void __user* name, void __user* args, void __user* result) {
|
|
||||||
// This is a KPM module stub.
|
|
||||||
int res = -1;
|
|
||||||
printk("KPM: Stub function called (sukisu_kpm_control). name=%p args=%p\n", name, args);
|
|
||||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
|
||||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
noinline
|
|
||||||
NO_OPTIMIZE
|
|
||||||
void sukisu_kpm_version(void __user* out, unsigned int bufferSize, void __user* result) {
|
|
||||||
int res = -1;
|
|
||||||
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 faild.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
||||||
|
void *ptr, int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
|
||||||
|
"name=%s ptr=%p\n", name, ptr);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_num);
|
EXPORT_SYMBOL(sukisu_kpm_num);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
|
||||||
|
int *size)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_info). "
|
||||||
|
"name=%s buffer=%p\n", name, buf);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_info);
|
EXPORT_SYMBOL(sukisu_kpm_info);
|
||||||
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
|
||||||
|
int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_list). "
|
||||||
|
"buffer=%p size=%d\n", out, bufferSize);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_list);
|
EXPORT_SYMBOL(sukisu_kpm_list);
|
||||||
EXPORT_SYMBOL(sukisu_kpm_version);
|
|
||||||
|
noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
|
||||||
|
int *result)
|
||||||
|
{
|
||||||
|
pr_info("kpm: Stub function called (sukisu_kpm_control). "
|
||||||
|
"name=%p args=%p arg_len=%ld\n", name, args, arg_len);
|
||||||
|
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(sukisu_kpm_control);
|
EXPORT_SYMBOL(sukisu_kpm_control);
|
||||||
|
|
||||||
noinline
|
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
|
||||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
|
|
||||||
{
|
{
|
||||||
if(arg2 == SUKISU_KPM_LOAD) {
|
pr_info("kpm: Stub function called (sukisu_kpm_version). "
|
||||||
char kernel_load_path[256] = { 0 };
|
"buffer=%p\n", buf);
|
||||||
char kernel_args_buffer[256] = { 0 };
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_kpm_version);
|
||||||
|
|
||||||
if(arg3 == 0) {
|
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
|
||||||
return -1;
|
unsigned long result_code)
|
||||||
|
{
|
||||||
|
int res = -1;
|
||||||
|
if (control_code == SUKISU_KPM_LOAD) {
|
||||||
|
char kernel_load_path[256];
|
||||||
|
char kernel_args_buffer[256];
|
||||||
|
|
||||||
|
if (arg1 == 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_load_path, (const char __user *)arg3, 255);
|
if (!ksu_access_ok(arg1, sizeof(kernel_load_path))) {
|
||||||
if(arg4 != 0) {
|
goto invalid_arg;
|
||||||
strncpy_from_user((char*)&kernel_args_buffer, (const char __user *)arg4, 255);
|
|
||||||
}
|
|
||||||
sukisu_kpm_load_module_path((const char*)&kernel_load_path, (const char*) &kernel_args_buffer, NULL, (void __user*) arg5);
|
|
||||||
} else if(arg2 == SUKISU_KPM_UNLOAD) {
|
|
||||||
char kernel_name_buffer[256] = { 0 };
|
|
||||||
|
|
||||||
if(arg3 == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, sizeof(kernel_load_path));
|
||||||
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 (arg2 != 0) {
|
||||||
return -1;
|
if (!ksu_access_ok(arg2, sizeof(kernel_args_buffer))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, sizeof(kernel_args_buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
|
||||||
sukisu_kpm_info((const char*) &kernel_name_buffer, (char __user*) arg4, (void __user*) arg5);
|
(const char *)&kernel_args_buffer, NULL, &res);
|
||||||
} else if(arg2 == SUKISU_KPM_LIST) {
|
} else if (control_code == SUKISU_KPM_UNLOAD) {
|
||||||
sukisu_kpm_list((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
char kernel_name_buffer[256];
|
||||||
} else if(arg2 == SUKISU_KPM_VERSION) {
|
|
||||||
sukisu_kpm_version((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
if (arg1 == 0) {
|
||||||
} else if(arg2 == SUKISU_KPM_CONTROL) {
|
res = -EINVAL;
|
||||||
sukisu_kpm_control((char __user*) arg3, (char __user*) arg4, (void __user*) arg5);
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
|
||||||
|
|
||||||
|
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
|
||||||
|
} else if (control_code == SUKISU_KPM_NUM) {
|
||||||
|
sukisu_kpm_num(&res);
|
||||||
|
} else if (control_code == SUKISU_KPM_INFO) {
|
||||||
|
char kernel_name_buffer[256];
|
||||||
|
char buf[256];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (arg1 == 0 || arg2 == 0) {
|
||||||
|
res = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_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 (!ksu_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 (!ksu_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 (!ksu_access_ok(arg1, sizeof(kpm_name))) {
|
||||||
|
goto invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2) {
|
|
||||||
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(sukisu_handle_kpm);
|
EXPORT_SYMBOL(sukisu_handle_kpm);
|
||||||
|
|
||||||
|
int sukisu_is_kpm_control_code(unsigned long control_code) {
|
||||||
|
return (control_code >= CMD_KPM_CONTROL &&
|
||||||
|
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 (!ksu_access_ok(cmd.control_code, sizeof(int))) {
|
||||||
|
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_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,44 +1,70 @@
|
|||||||
#ifndef ___SUKISU_KPM_H
|
#ifndef __SUKISU_KPM_H
|
||||||
#define ___SUKISU_KPM_H
|
#define __SUKISU_KPM_H
|
||||||
|
|
||||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
#include <linux/types.h>
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2);
|
#include <linux/ioctl.h>
|
||||||
|
|
||||||
// KPM控制代码
|
struct ksu_kpm_cmd {
|
||||||
#define CMD_KPM_CONTROL 28
|
__aligned_u64 __user control_code;
|
||||||
#define CMD_KPM_CONTROL_MAX 35
|
__aligned_u64 __user arg1;
|
||||||
|
__aligned_u64 __user arg2;
|
||||||
|
__aligned_u64 __user result_code;
|
||||||
|
};
|
||||||
|
|
||||||
// 控制代码
|
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2, unsigned long result_code);
|
||||||
|
int sukisu_is_kpm_control_code(unsigned long control_code);
|
||||||
|
int do_kpm(void __user *arg);
|
||||||
|
|
||||||
// prctl(xxx, 28, "PATH", "ARGS")
|
#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
|
||||||
// success return 0, error return -N
|
|
||||||
#define SUKISU_KPM_LOAD 28
|
|
||||||
|
|
||||||
// prctl(xxx, 29, "NAME")
|
/* KPM Control Code */
|
||||||
// success return 0, error return -N
|
#define CMD_KPM_CONTROL 1
|
||||||
#define SUKISU_KPM_UNLOAD 29
|
#define CMD_KPM_CONTROL_MAX 10
|
||||||
|
|
||||||
// num = prctl(xxx, 30)
|
/* Control Code */
|
||||||
// error return -N
|
/*
|
||||||
// success return +num or 0
|
* prctl(xxx, 1, "PATH", "ARGS")
|
||||||
#define SUKISU_KPM_NUM 30
|
* success return 0, error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_LOAD 1
|
||||||
|
|
||||||
// prctl(xxx, 31, Buffer, BufferSize)
|
/*
|
||||||
// success return +out, error return -N
|
* prctl(xxx, 2, "NAME")
|
||||||
#define SUKISU_KPM_LIST 31
|
* success return 0, error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_UNLOAD 2
|
||||||
|
|
||||||
// prctl(xxx, 32, "NAME", Buffer[256])
|
/*
|
||||||
// success return +out, error return -N
|
* num = prctl(xxx, 3)
|
||||||
#define SUKISU_KPM_INFO 32
|
* error return -N
|
||||||
|
* success return +num or 0
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_NUM 3
|
||||||
|
|
||||||
// prctl(xxx, 33, "NAME", "ARGS")
|
/*
|
||||||
// success return KPM's result value
|
* prctl(xxx, 4, Buffer, BufferSize)
|
||||||
// error return -N
|
* success return +out, error return -N
|
||||||
#define SUKISU_KPM_CONTROL 33
|
*/
|
||||||
|
#define SUKISU_KPM_LIST 4
|
||||||
|
|
||||||
// prctl(xxx, 34, buffer, bufferSize)
|
/*
|
||||||
// success return KPM's result value
|
* prctl(xxx, 5, "NAME", Buffer[256])
|
||||||
// error return -N
|
* success return +out, error return -N
|
||||||
#define SUKISU_KPM_VERSION 34
|
*/
|
||||||
|
#define SUKISU_KPM_INFO 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prctl(xxx, 6, "NAME", "ARGS")
|
||||||
|
* success return KPM's result value
|
||||||
|
* error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_CONTROL 6
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prctl(xxx, 7, buffer, bufferSize)
|
||||||
|
* success return KPM's result value
|
||||||
|
* error return -N
|
||||||
|
*/
|
||||||
|
#define SUKISU_KPM_VERSION 7
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -31,38 +31,37 @@
|
|||||||
|
|
||||||
// 结构体成员元数据
|
// 结构体成员元数据
|
||||||
struct DynamicStructMember {
|
struct DynamicStructMember {
|
||||||
const char* name;
|
const char *name;
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 结构体元数据(包含总大小)
|
// 结构体元数据(包含总大小)
|
||||||
struct DynamicStructInfo {
|
struct DynamicStructInfo {
|
||||||
const char* name;
|
const char *name;
|
||||||
size_t count;
|
size_t count;
|
||||||
size_t total_size;
|
size_t total_size;
|
||||||
struct DynamicStructMember* members;
|
struct DynamicStructMember *members;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义结构体元数据的宏(直接使用 struct 名称)
|
// 定义结构体元数据的宏(直接使用 struct 名称)
|
||||||
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
||||||
static struct DynamicStructMember struct_name##_members[] = {
|
static struct DynamicStructMember struct_name##_members[] = {
|
||||||
|
#define DEFINE_MEMBER(struct_name, member) \
|
||||||
|
{ .name = #member, \
|
||||||
|
.size = sizeof(((struct struct_name *)0)->member), \
|
||||||
|
.offset = offsetof(struct struct_name, member) },
|
||||||
|
|
||||||
#define DEFINE_MEMBER(struct_name, member) \
|
#define DYNAMIC_STRUCT_END(struct_name) \
|
||||||
{ \
|
} \
|
||||||
.name = #member, \
|
; \
|
||||||
.size = sizeof(((struct struct_name*)0)->member), \
|
static struct DynamicStructInfo struct_name##_info = { \
|
||||||
.offset = offsetof(struct struct_name, member) \
|
.name = #struct_name, \
|
||||||
},
|
.count = sizeof(struct_name##_members) / \
|
||||||
|
sizeof(struct DynamicStructMember), \
|
||||||
#define DYNAMIC_STRUCT_END(struct_name) \
|
.total_size = sizeof(struct struct_name), \
|
||||||
}; \
|
.members = struct_name##_members \
|
||||||
static struct DynamicStructInfo struct_name##_info = { \
|
};
|
||||||
.name = #struct_name, \
|
|
||||||
.count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \
|
|
||||||
.total_size = sizeof(struct struct_name), \
|
|
||||||
.members = struct_name##_members \
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================================================================================
|
// ==================================================================================
|
||||||
|
|
||||||
@@ -76,28 +75,28 @@ struct DynamicStructInfo {
|
|||||||
|
|
||||||
// 定义元数据
|
// 定义元数据
|
||||||
DYNAMIC_STRUCT_BEGIN(mount)
|
DYNAMIC_STRUCT_BEGIN(mount)
|
||||||
DEFINE_MEMBER(mount, mnt_parent)
|
DEFINE_MEMBER(mount, mnt_parent)
|
||||||
DEFINE_MEMBER(mount, mnt)
|
DEFINE_MEMBER(mount, mnt)
|
||||||
DEFINE_MEMBER(mount, mnt_id)
|
DEFINE_MEMBER(mount, mnt_id)
|
||||||
DEFINE_MEMBER(mount, mnt_group_id)
|
DEFINE_MEMBER(mount, mnt_group_id)
|
||||||
DEFINE_MEMBER(mount, mnt_expiry_mark)
|
DEFINE_MEMBER(mount, mnt_expiry_mark)
|
||||||
DEFINE_MEMBER(mount, mnt_master)
|
DEFINE_MEMBER(mount, mnt_master)
|
||||||
DEFINE_MEMBER(mount, mnt_devname)
|
DEFINE_MEMBER(mount, mnt_devname)
|
||||||
DYNAMIC_STRUCT_END(mount)
|
DYNAMIC_STRUCT_END(mount)
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(vfsmount)
|
DYNAMIC_STRUCT_BEGIN(vfsmount)
|
||||||
DEFINE_MEMBER(vfsmount, mnt_root)
|
DEFINE_MEMBER(vfsmount, mnt_root)
|
||||||
DEFINE_MEMBER(vfsmount, mnt_sb)
|
DEFINE_MEMBER(vfsmount, mnt_sb)
|
||||||
DEFINE_MEMBER(vfsmount, mnt_flags)
|
DEFINE_MEMBER(vfsmount, mnt_flags)
|
||||||
DYNAMIC_STRUCT_END(vfsmount)
|
DYNAMIC_STRUCT_END(vfsmount)
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(mnt_namespace)
|
DYNAMIC_STRUCT_BEGIN(mnt_namespace)
|
||||||
DEFINE_MEMBER(mnt_namespace, ns)
|
DEFINE_MEMBER(mnt_namespace, ns)
|
||||||
DEFINE_MEMBER(mnt_namespace, root)
|
DEFINE_MEMBER(mnt_namespace, root)
|
||||||
DEFINE_MEMBER(mnt_namespace, seq)
|
DEFINE_MEMBER(mnt_namespace, seq)
|
||||||
DEFINE_MEMBER(mnt_namespace, mounts)
|
DEFINE_MEMBER(mnt_namespace, mounts)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||||
DEFINE_MEMBER(mnt_namespace, count)
|
DEFINE_MEMBER(mnt_namespace, count)
|
||||||
#endif
|
#endif
|
||||||
DYNAMIC_STRUCT_END(mnt_namespace)
|
DYNAMIC_STRUCT_END(mnt_namespace)
|
||||||
|
|
||||||
@@ -105,15 +104,15 @@ DYNAMIC_STRUCT_END(mnt_namespace)
|
|||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
DYNAMIC_STRUCT_BEGIN(kprobe)
|
DYNAMIC_STRUCT_BEGIN(kprobe)
|
||||||
DEFINE_MEMBER(kprobe, addr)
|
DEFINE_MEMBER(kprobe, addr)
|
||||||
DEFINE_MEMBER(kprobe, symbol_name)
|
DEFINE_MEMBER(kprobe, symbol_name)
|
||||||
DEFINE_MEMBER(kprobe, offset)
|
DEFINE_MEMBER(kprobe, offset)
|
||||||
DEFINE_MEMBER(kprobe, pre_handler)
|
DEFINE_MEMBER(kprobe, pre_handler)
|
||||||
DEFINE_MEMBER(kprobe, post_handler)
|
DEFINE_MEMBER(kprobe, post_handler)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||||
DEFINE_MEMBER(kprobe, fault_handler)
|
DEFINE_MEMBER(kprobe, fault_handler)
|
||||||
#endif
|
#endif
|
||||||
DEFINE_MEMBER(kprobe, flags)
|
DEFINE_MEMBER(kprobe, flags)
|
||||||
DYNAMIC_STRUCT_END(kprobe)
|
DYNAMIC_STRUCT_END(kprobe)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -121,103 +120,113 @@ DYNAMIC_STRUCT_END(kprobe)
|
|||||||
#include <linux/mm_types.h>
|
#include <linux/mm_types.h>
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_start)
|
DEFINE_MEMBER(vm_area_struct, vm_start)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_end)
|
DEFINE_MEMBER(vm_area_struct, vm_end)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_flags)
|
DEFINE_MEMBER(vm_area_struct, vm_flags)
|
||||||
DEFINE_MEMBER(vm_area_struct,anon_vma)
|
DEFINE_MEMBER(vm_area_struct, anon_vma)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
|
DEFINE_MEMBER(vm_area_struct, vm_pgoff)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_file)
|
DEFINE_MEMBER(vm_area_struct, vm_file)
|
||||||
DEFINE_MEMBER(vm_area_struct,vm_private_data)
|
DEFINE_MEMBER(vm_area_struct, vm_private_data)
|
||||||
#ifdef CONFIG_ANON_VMA_NAME
|
#ifdef CONFIG_ANON_VMA_NAME
|
||||||
DEFINE_MEMBER(vm_area_struct, anon_name)
|
DEFINE_MEMBER(vm_area_struct, anon_name)
|
||||||
#endif
|
#endif
|
||||||
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
||||||
DYNAMIC_STRUCT_END(vm_area_struct)
|
DYNAMIC_STRUCT_END(vm_area_struct)
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
|
DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
|
||||||
DEFINE_MEMBER(vm_operations_struct, open)
|
DEFINE_MEMBER(vm_operations_struct, open)
|
||||||
DEFINE_MEMBER(vm_operations_struct, close)
|
DEFINE_MEMBER(vm_operations_struct, close)
|
||||||
DEFINE_MEMBER(vm_operations_struct, name)
|
DEFINE_MEMBER(vm_operations_struct, name)
|
||||||
DEFINE_MEMBER(vm_operations_struct, access)
|
DEFINE_MEMBER(vm_operations_struct, access)
|
||||||
DYNAMIC_STRUCT_END(vm_operations_struct)
|
DYNAMIC_STRUCT_END(vm_operations_struct)
|
||||||
|
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, input)
|
DEFINE_MEMBER(netlink_kernel_cfg, input)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
|
||||||
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
||||||
#endif
|
#endif
|
||||||
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
||||||
|
|
||||||
|
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
DYNAMIC_STRUCT_BEGIN(task_struct)
|
DYNAMIC_STRUCT_BEGIN(task_struct)
|
||||||
DEFINE_MEMBER(task_struct, pid)
|
DEFINE_MEMBER(task_struct, pid)
|
||||||
DEFINE_MEMBER(task_struct, tgid)
|
DEFINE_MEMBER(task_struct, tgid)
|
||||||
DEFINE_MEMBER(task_struct, cred)
|
DEFINE_MEMBER(task_struct, cred)
|
||||||
DEFINE_MEMBER(task_struct, real_cred)
|
DEFINE_MEMBER(task_struct, real_cred)
|
||||||
DEFINE_MEMBER(task_struct, comm)
|
DEFINE_MEMBER(task_struct, comm)
|
||||||
DEFINE_MEMBER(task_struct, parent)
|
DEFINE_MEMBER(task_struct, parent)
|
||||||
DEFINE_MEMBER(task_struct, group_leader)
|
DEFINE_MEMBER(task_struct, group_leader)
|
||||||
DEFINE_MEMBER(task_struct, mm)
|
DEFINE_MEMBER(task_struct, mm)
|
||||||
DEFINE_MEMBER(task_struct, active_mm)
|
DEFINE_MEMBER(task_struct, active_mm)
|
||||||
DEFINE_MEMBER(task_struct, thread_pid)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
||||||
DEFINE_MEMBER(task_struct, files)
|
DEFINE_MEMBER(task_struct, pids[PIDTYPE_PID].pid)
|
||||||
DEFINE_MEMBER(task_struct, seccomp)
|
#else
|
||||||
|
DEFINE_MEMBER(task_struct, thread_pid)
|
||||||
|
#endif
|
||||||
|
DEFINE_MEMBER(task_struct, files)
|
||||||
|
DEFINE_MEMBER(task_struct, seccomp)
|
||||||
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
||||||
DEFINE_MEMBER(task_struct, thread_info)
|
DEFINE_MEMBER(task_struct, thread_info)
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_CGROUPS
|
#ifdef CONFIG_CGROUPS
|
||||||
DEFINE_MEMBER(task_struct, cgroups)
|
DEFINE_MEMBER(task_struct, cgroups)
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_SECURITY
|
#ifdef CONFIG_SECURITY
|
||||||
DEFINE_MEMBER(task_struct, security)
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
|
||||||
|
DEFINE_MEMBER(task_struct, security)
|
||||||
|
#else
|
||||||
|
DEFINE_MEMBER(task_struct, cred)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
|
||||||
|
DEFINE_MEMBER(task_struct, thread)
|
||||||
|
#else
|
||||||
|
DEFINE_MEMBER(task_struct, thread_info)
|
||||||
#endif
|
#endif
|
||||||
DEFINE_MEMBER(task_struct, thread)
|
|
||||||
DYNAMIC_STRUCT_END(task_struct)
|
DYNAMIC_STRUCT_END(task_struct)
|
||||||
|
|
||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
#define STRUCT_INFO(name) &(name##_info)
|
#define STRUCT_INFO(name) &(name##_info)
|
||||||
|
|
||||||
static
|
static struct DynamicStructInfo *dynamic_struct_infos[] = {
|
||||||
struct DynamicStructInfo* dynamic_struct_infos[] = {
|
STRUCT_INFO(mount),
|
||||||
STRUCT_INFO(mount),
|
STRUCT_INFO(vfsmount),
|
||||||
STRUCT_INFO(vfsmount),
|
STRUCT_INFO(mnt_namespace),
|
||||||
STRUCT_INFO(mnt_namespace),
|
#ifdef CONFIG_KPROBES
|
||||||
#ifdef CONFIG_KPROBES
|
STRUCT_INFO(kprobe),
|
||||||
STRUCT_INFO(kprobe),
|
#endif
|
||||||
#endif
|
STRUCT_INFO(vm_area_struct),
|
||||||
STRUCT_INFO(vm_area_struct),
|
STRUCT_INFO(vm_operations_struct),
|
||||||
STRUCT_INFO(vm_operations_struct),
|
STRUCT_INFO(netlink_kernel_cfg),
|
||||||
STRUCT_INFO(netlink_kernel_cfg),
|
STRUCT_INFO(task_struct)
|
||||||
STRUCT_INFO(task_struct)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// return 0 if successful
|
// return 0 if successful
|
||||||
// return -1 if struct not defined
|
// return -1 if struct not defined
|
||||||
int sukisu_super_find_struct(
|
int sukisu_super_find_struct(const char *struct_name, size_t *out_size,
|
||||||
const char* struct_name,
|
int *out_members)
|
||||||
size_t* out_size,
|
{
|
||||||
int* out_members
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
|
||||||
) {
|
sizeof(dynamic_struct_infos[0]));
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
i++) {
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
if(out_size)
|
if (out_size)
|
||||||
*out_size = info->total_size;
|
*out_size = info->total_size;
|
||||||
if(out_members)
|
if (out_members)
|
||||||
*out_members = info->count;
|
*out_members = info->count;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_find_struct);
|
EXPORT_SYMBOL(sukisu_super_find_struct);
|
||||||
|
|
||||||
@@ -225,61 +234,67 @@ EXPORT_SYMBOL(sukisu_super_find_struct);
|
|||||||
// return 0 if successful
|
// return 0 if successful
|
||||||
// return -1 if struct not defined
|
// return -1 if struct not defined
|
||||||
// return -2 if member not defined
|
// return -2 if member not defined
|
||||||
int sukisu_super_access (
|
int sukisu_super_access(const char *struct_name, const char *member_name,
|
||||||
const char* struct_name,
|
size_t *out_offset, size_t *out_size)
|
||||||
const char* member_name,
|
{
|
||||||
size_t* out_offset,
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
|
||||||
size_t* out_size
|
sizeof(dynamic_struct_infos[0]));
|
||||||
) {
|
i++) {
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
if (strcmp(info->members[i1].name,
|
||||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
member_name) == 0) {
|
||||||
if(out_offset)
|
if (out_offset)
|
||||||
*out_offset = info->members[i].offset;
|
*out_offset =
|
||||||
if(out_size)
|
info->members[i].offset;
|
||||||
*out_size = info->members[i].size;
|
if (out_size)
|
||||||
return 0;
|
*out_size =
|
||||||
}
|
info->members[i].size;
|
||||||
}
|
return 0;
|
||||||
return -2;
|
}
|
||||||
}
|
}
|
||||||
}
|
return -2;
|
||||||
return -1;
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_access);
|
EXPORT_SYMBOL(sukisu_super_access);
|
||||||
|
|
||||||
// 动态 container_of 宏
|
// 动态 container_of 宏
|
||||||
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
|
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) \
|
||||||
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
|
({ \
|
||||||
})
|
(offset != (size_t)-1) ? \
|
||||||
|
(void *)((char *)(member_ptr) - offset) : \
|
||||||
|
NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
// Dynamic container_of
|
// Dynamic container_of
|
||||||
// return 0 if success
|
// return 0 if success
|
||||||
// return -1 if current struct not defined
|
// return -1 if current struct not defined
|
||||||
// return -2 if target member not defined
|
// return -2 if target member not defined
|
||||||
int sukisu_super_container_of(
|
int sukisu_super_container_of(const char *struct_name, const char *member_name,
|
||||||
const char* struct_name,
|
void *ptr, void **out_ptr)
|
||||||
const char* member_name,
|
{
|
||||||
void* ptr,
|
if (ptr == NULL) {
|
||||||
void** out_ptr
|
return -3;
|
||||||
) {
|
}
|
||||||
if(ptr == NULL) {
|
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) /
|
||||||
return -3;
|
sizeof(dynamic_struct_infos[0]));
|
||||||
}
|
i++) {
|
||||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
if (strcmp(struct_name, info->name) == 0) {
|
||||||
if(strcmp(struct_name, info->name) == 0) {
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
if (strcmp(info->members[i1].name,
|
||||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
member_name) == 0) {
|
||||||
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
*out_ptr = (void *)DYNAMIC_CONTAINER_OF(
|
||||||
return 0;
|
info->members[i1].offset, ptr);
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
return -2;
|
}
|
||||||
}
|
return -2;
|
||||||
}
|
}
|
||||||
return -1;
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sukisu_super_container_of);
|
EXPORT_SYMBOL(sukisu_super_container_of);
|
||||||
@@ -8,32 +8,21 @@
|
|||||||
|
|
||||||
// return 0 if successful
|
// return 0 if successful
|
||||||
// return -1 if struct not defined
|
// return -1 if struct not defined
|
||||||
int sukisu_super_find_struct(
|
int sukisu_super_find_struct(const char *struct_name, size_t *out_size,
|
||||||
const char* struct_name,
|
int *out_members);
|
||||||
size_t* out_size,
|
|
||||||
int* out_members
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dynamic access struct
|
// Dynamic access struct
|
||||||
// return 0 if successful
|
// return 0 if successful
|
||||||
// return -1 if struct not defined
|
// return -1 if struct not defined
|
||||||
// return -2 if member not defined
|
// return -2 if member not defined
|
||||||
int sukisu_super_access (
|
int sukisu_super_access(const char *struct_name, const char *member_name,
|
||||||
const char* struct_name,
|
size_t *out_offset, size_t *out_size);
|
||||||
const char* member_name,
|
|
||||||
size_t* out_offset,
|
|
||||||
size_t* out_size
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dynamic container_of
|
// Dynamic container_of
|
||||||
// return 0 if success
|
// return 0 if success
|
||||||
// return -1 if current struct not defined
|
// return -1 if current struct not defined
|
||||||
// return -2 if target member not defined
|
// return -2 if target member not defined
|
||||||
int sukisu_super_container_of(
|
int sukisu_super_container_of(const char *struct_name, const char *member_name,
|
||||||
const char* struct_name,
|
void *ptr, void **out_ptr);
|
||||||
const char* member_name,
|
|
||||||
void* ptr,
|
|
||||||
void** out_ptr
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
72
kernel/ksu.c
72
kernel/ksu.c
@@ -1,29 +1,48 @@
|
|||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
#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 <generated/utsrelease.h>
|
||||||
|
#include <generated/compile.h>
|
||||||
|
#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION macros */
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "core_hook.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "supercalls.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sukisu_custom_config_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void sukisu_custom_config_exit(void)
|
||||||
|
{
|
||||||
|
ksu_uid_exit();
|
||||||
|
ksu_throne_comm_exit();
|
||||||
|
ksu_dynamic_manager_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags);
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags);
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||||
void *envp, int *flags)
|
void *envp, int *flags)
|
||||||
{
|
{
|
||||||
@@ -32,13 +51,13 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|||||||
flags);
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void ksu_sucompat_init();
|
|
||||||
extern void ksu_sucompat_exit();
|
|
||||||
extern void ksu_ksud_init();
|
|
||||||
extern void ksu_ksud_exit();
|
|
||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
|
#ifndef DDK_ENV
|
||||||
|
pr_info("Initialized on: %s (%s) with driver version: %u\n",
|
||||||
|
UTS_RELEASE, UTS_MACHINE, KSU_VERSION);
|
||||||
|
#endif
|
||||||
|
|
||||||
#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 **");
|
||||||
@@ -49,19 +68,21 @@ int __init kernelsu_init(void)
|
|||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_init();
|
ksu_feature_init();
|
||||||
|
|
||||||
|
ksu_supercalls_init();
|
||||||
|
|
||||||
|
sukisu_custom_config_init();
|
||||||
|
|
||||||
|
ksu_syscall_hook_manager_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
|
|
||||||
ksu_allowlist_init();
|
ksu_allowlist_init();
|
||||||
|
|
||||||
ksu_throne_tracker_init();
|
ksu_throne_tracker_init();
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
ksu_sucompat_init();
|
|
||||||
ksu_ksud_init();
|
ksu_ksud_init();
|
||||||
#else
|
|
||||||
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
#ifndef CONFIG_KSU_DEBUG
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
@@ -71,20 +92,26 @@ int __init kernelsu_init(void)
|
|||||||
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_throne_tracker_exit();
|
||||||
|
|
||||||
|
ksu_observer_exit();
|
||||||
|
|
||||||
destroy_workqueue(ksu_workqueue);
|
destroy_workqueue(ksu_workqueue);
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
ksu_ksud_exit();
|
ksu_ksud_exit();
|
||||||
ksu_sucompat_exit();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ksu_core_exit();
|
ksu_syscall_hook_manager_exit();
|
||||||
|
|
||||||
|
sukisu_custom_config_exit();
|
||||||
|
|
||||||
|
ksu_supercalls_exit();
|
||||||
|
|
||||||
|
ksu_feature_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(kernelsu_init);
|
module_init(kernelsu_init);
|
||||||
@@ -93,4 +120,11 @@ 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(5, 0, 0)
|
||||||
|
#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
|
||||||
|
#endif
|
||||||
|
|||||||
99
kernel/ksu.h
99
kernel/ksu.h
@@ -7,95 +7,56 @@
|
|||||||
#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 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
|
// SukiSU Ultra kernel su version full strings
|
||||||
#define KSU_MAX_PACKAGE_NAME 256
|
#ifndef KSU_VERSION_FULL
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
||||||
#define KSU_MAX_GROUPS 32
|
#endif
|
||||||
#define KSU_SELINUX_DOMAIN 64
|
#define KSU_FULL_VERSION_STRING 255
|
||||||
|
|
||||||
struct root_profile {
|
#define DYNAMIC_MANAGER_OP_SET 0
|
||||||
int32_t uid;
|
#define DYNAMIC_MANAGER_OP_GET 1
|
||||||
int32_t gid;
|
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||||
|
|
||||||
int32_t groups_count;
|
#define UID_SCANNER_OP_GET_STATUS 0
|
||||||
int32_t groups[KSU_MAX_GROUPS];
|
#define UID_SCANNER_OP_TOGGLE 1
|
||||||
|
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||||
|
|
||||||
// kernel_cap_t is u32[2] for capabilities v3
|
struct dynamic_manager_user_config {
|
||||||
struct {
|
unsigned int operation;
|
||||||
u64 effective;
|
unsigned int size;
|
||||||
u64 permitted;
|
char hash[65];
|
||||||
u64 inheritable;
|
|
||||||
} capabilities;
|
|
||||||
|
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
|
||||||
|
|
||||||
int32_t namespaces;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct non_root_profile {
|
struct manager_list_info {
|
||||||
bool umount_modules;
|
int count;
|
||||||
};
|
struct {
|
||||||
|
uid_t uid;
|
||||||
struct app_profile {
|
int signature_index;
|
||||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
} managers[2];
|
||||||
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
|
||||||
604
kernel/ksud.c
604
kernel/ksud.c
@@ -1,3 +1,6 @@
|
|||||||
|
#include <linux/rcupdate.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
@@ -6,20 +9,37 @@
|
|||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
|
#else
|
||||||
|
#include <uapi/linux/input.h>
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
||||||
|
#include <linux/aio.h>
|
||||||
|
#endif
|
||||||
#include <linux/kprobes.h>
|
#include <linux/kprobes.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
|
||||||
|
bool ksu_module_mounted __read_mostly = false;
|
||||||
|
bool ksu_boot_completed __read_mostly = false;
|
||||||
|
|
||||||
static const char KERNEL_SU_RC[] =
|
static const char KERNEL_SU_RC[] =
|
||||||
"\n"
|
"\n"
|
||||||
@@ -44,21 +64,21 @@ static const char KERNEL_SU_RC[] =
|
|||||||
|
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
static void stop_vfs_read_hook();
|
static void stop_vfs_read_hook(void);
|
||||||
static void stop_execve_hook();
|
static void stop_execve_hook(void);
|
||||||
static void stop_input_hook();
|
static void stop_input_hook(void);
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
static struct work_struct stop_vfs_read_work;
|
static struct work_struct stop_vfs_read_work;
|
||||||
static struct work_struct stop_execve_hook_work;
|
static struct work_struct stop_execve_hook_work;
|
||||||
static struct work_struct stop_input_hook_work;
|
static struct work_struct stop_input_hook_work;
|
||||||
#else
|
#else
|
||||||
bool ksu_vfs_read_hook __read_mostly = true;
|
bool ksu_vfs_read_hook __read_mostly = true;
|
||||||
bool ksu_execveat_hook __read_mostly = true;
|
|
||||||
bool ksu_input_hook __read_mostly = true;
|
bool ksu_input_hook __read_mostly = true;
|
||||||
#endif
|
#endif
|
||||||
|
bool ksu_execveat_hook __read_mostly = true;
|
||||||
|
|
||||||
u32 ksu_devpts_sid;
|
u32 ksu_file_sid;
|
||||||
|
|
||||||
// Detect whether it is on or not
|
// Detect whether it is on or not
|
||||||
static bool is_boot_phase = true;
|
static bool is_boot_phase = true;
|
||||||
@@ -73,14 +93,51 @@ void on_post_fs_data(void)
|
|||||||
done = true;
|
done = true;
|
||||||
pr_info("on_post_fs_data!\n");
|
pr_info("on_post_fs_data!\n");
|
||||||
ksu_load_allow_list();
|
ksu_load_allow_list();
|
||||||
|
ksu_observer_init();
|
||||||
// sanity check, this may influence the performance
|
// sanity check, this may influence the performance
|
||||||
stop_input_hook();
|
stop_input_hook();
|
||||||
|
|
||||||
ksu_devpts_sid = ksu_get_devpts_sid();
|
// End of boot state
|
||||||
pr_info("devpts sid: %d\n", ksu_devpts_sid);
|
is_boot_phase = false;
|
||||||
|
|
||||||
// End of boot state
|
ksu_file_sid = ksu_get_ksu_file_sid();
|
||||||
is_boot_phase = false;
|
pr_info("devpts sid: %d\n", ksu_file_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void ext4_unregister_sysfs(struct super_block *sb);
|
||||||
|
int nuke_ext4_sysfs(const char* mnt)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path(mnt, 0, &path);
|
||||||
|
if (err) {
|
||||||
|
pr_err("nuke path err: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct super_block *sb = path.dentry->d_inode->i_sb;
|
||||||
|
const char *name = sb->s_type->name;
|
||||||
|
if (strcmp(name, "ext4") != 0) {
|
||||||
|
pr_info("nuke but module aren't mounted\n");
|
||||||
|
path_put(&path);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext4_unregister_sysfs(sb);
|
||||||
|
path_put(&path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_module_mounted(void)
|
||||||
|
{
|
||||||
|
pr_info("on_module_mounted!\n");
|
||||||
|
ksu_module_mounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_boot_completed(void)
|
||||||
|
{
|
||||||
|
ksu_boot_completed = true;
|
||||||
|
pr_info("on_boot_completed!\n");
|
||||||
|
track_throne(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||||
@@ -96,74 +153,20 @@ struct user_arg_ptr {
|
|||||||
} ptr;
|
} ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
static void on_post_fs_data_cbfun(struct callback_head *cb)
|
||||||
{
|
{
|
||||||
const char __user *native;
|
on_post_fs_data();
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
if (unlikely(argv.is_compat)) {
|
|
||||||
compat_uptr_t compat;
|
|
||||||
|
|
||||||
if (get_user(compat, argv.ptr.compat + nr))
|
|
||||||
return ERR_PTR(-EFAULT);
|
|
||||||
|
|
||||||
return compat_ptr(compat);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (get_user(native, argv.ptr.native + nr))
|
|
||||||
return ERR_PTR(-EFAULT);
|
|
||||||
|
|
||||||
return native;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static struct callback_head on_post_fs_data_cb = {
|
||||||
* count() counts the number of strings in array ARGV.
|
.func = on_post_fs_data_cbfun
|
||||||
*/
|
};
|
||||||
|
|
||||||
/*
|
// since _ksud handler only uses argv and envp for comparisons
|
||||||
* Make sure old GCC compiler can use __maybe_unused,
|
// this can probably work
|
||||||
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
// adapted from ksu_handle_execveat_ksud
|
||||||
*/
|
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
|
||||||
|
|
||||||
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
|
||||||
{
|
{
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (argv.ptr.native != NULL) {
|
|
||||||
for (;;) {
|
|
||||||
const char __user *p = get_user_arg_ptr(argv, i);
|
|
||||||
|
|
||||||
if (!p)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (IS_ERR(p))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (i >= max)
|
|
||||||
return -E2BIG;
|
|
||||||
++i;
|
|
||||||
|
|
||||||
if (fatal_signal_pending(current))
|
|
||||||
return -ERESTARTNOHAND;
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
|
||||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|
||||||
struct user_arg_ptr *argv,
|
|
||||||
struct user_arg_ptr *envp, int *flags)
|
|
||||||
{
|
|
||||||
#ifndef CONFIG_KPROBES
|
|
||||||
if (!ksu_execveat_hook) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
struct filename *filename;
|
|
||||||
|
|
||||||
static const char app_process[] = "/system/bin/app_process";
|
static const char app_process[] = "/system/bin/app_process";
|
||||||
static bool first_app_process = true;
|
static bool first_app_process = true;
|
||||||
|
|
||||||
@@ -173,113 +176,157 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
static const char old_system_init[] = "/init";
|
static const char old_system_init[] = "/init";
|
||||||
static bool init_second_stage_executed = false;
|
static bool init_second_stage_executed = false;
|
||||||
|
|
||||||
if (!filename_ptr)
|
// return early when disabled
|
||||||
|
if (!ksu_execveat_hook)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
filename = *filename_ptr;
|
if (!filename)
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
// debug! remove me!
|
||||||
sizeof(system_bin_init) - 1) &&
|
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
|
||||||
argv)) {
|
|
||||||
// /system/bin/init executed
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
const char *envp_n = envp;
|
||||||
pr_info("/system/bin/init argc: %d\n", argc);
|
unsigned int envc = 1;
|
||||||
if (argc > 1 && !init_second_stage_executed) {
|
do {
|
||||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
||||||
if (p && !IS_ERR(p)) {
|
envp_n += strlen(envp_n) + 1;
|
||||||
char first_arg[16];
|
envc++;
|
||||||
ksu_strncpy_from_user_nofault(
|
} while (envp_n < envp + 256);
|
||||||
first_arg, p, sizeof(first_arg));
|
#endif
|
||||||
pr_info("/system/bin/init first arg: %s\n",
|
|
||||||
first_arg);
|
if (init_second_stage_executed)
|
||||||
if (!strcmp(first_arg, "second_stage")) {
|
goto first_app_process;
|
||||||
pr_info("/system/bin/init second_stage executed\n");
|
|
||||||
apply_kernelsu_rules();
|
// /system/bin/init with argv1
|
||||||
init_second_stage_executed = true;
|
if (!init_second_stage_executed
|
||||||
ksu_android_ns_fs_check();
|
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
|
||||||
}
|
if (argv1 && !strcmp(argv1, "second_stage")) {
|
||||||
} else {
|
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
|
||||||
pr_err("/system/bin/init parse args err!\n");
|
apply_kernelsu_rules();
|
||||||
}
|
init_second_stage_executed = true;
|
||||||
}
|
|
||||||
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
|
||||||
sizeof(old_system_init) - 1) &&
|
|
||||||
argv)) {
|
|
||||||
// /init executed
|
|
||||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
|
||||||
pr_info("/init argc: %d\n", argc);
|
|
||||||
if (argc > 1 && !init_second_stage_executed) {
|
|
||||||
/* This applies to versions between Android 6 ~ 7 */
|
|
||||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
|
||||||
if (p && !IS_ERR(p)) {
|
|
||||||
char first_arg[16];
|
|
||||||
ksu_strncpy_from_user_nofault(
|
|
||||||
first_arg, p, sizeof(first_arg));
|
|
||||||
pr_info("/init first arg: %s\n", first_arg);
|
|
||||||
if (!strcmp(first_arg, "--second-stage")) {
|
|
||||||
pr_info("/init second_stage executed\n");
|
|
||||||
apply_kernelsu_rules();
|
|
||||||
init_second_stage_executed = true;
|
|
||||||
ksu_android_ns_fs_check();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("/init parse args err!\n");
|
|
||||||
}
|
|
||||||
} else if (argc == 1 && !init_second_stage_executed && envp) {
|
|
||||||
/* This applies to versions between Android 8 ~ 9 */
|
|
||||||
int envc = count(*envp, MAX_ARG_STRINGS);
|
|
||||||
if (envc > 0) {
|
|
||||||
int n;
|
|
||||||
for (n = 1; n <= envc; n++) {
|
|
||||||
const char __user *p =
|
|
||||||
get_user_arg_ptr(*envp, n);
|
|
||||||
if (!p || IS_ERR(p)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
char env[256];
|
|
||||||
// Reading environment variable strings from user space
|
|
||||||
if (ksu_strncpy_from_user_nofault(
|
|
||||||
env, p, sizeof(env)) < 0)
|
|
||||||
continue;
|
|
||||||
// Parsing environment variable names and values
|
|
||||||
char *env_name = env;
|
|
||||||
char *env_value = strchr(env, '=');
|
|
||||||
if (env_value == NULL)
|
|
||||||
continue;
|
|
||||||
// Replace equal sign with string terminator
|
|
||||||
*env_value = '\0';
|
|
||||||
env_value++;
|
|
||||||
// Check if the environment variable name and value are matching
|
|
||||||
if (!strcmp(env_name,
|
|
||||||
"INIT_SECOND_STAGE") &&
|
|
||||||
(!strcmp(env_value, "1") ||
|
|
||||||
!strcmp(env_value, "true"))) {
|
|
||||||
pr_info("/init second_stage executed\n");
|
|
||||||
apply_kernelsu_rules();
|
|
||||||
init_second_stage_executed =
|
|
||||||
true;
|
|
||||||
ksu_android_ns_fs_check();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
// /init with argv1
|
||||||
sizeof(app_process) - 1))) {
|
if (!init_second_stage_executed
|
||||||
|
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
||||||
|
if (argv1 && !strcmp(argv1, "--second-stage")) {
|
||||||
|
pr_info("%s: /init --second-stage executed\n", __func__);
|
||||||
|
apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!envp || !envp_len)
|
||||||
|
goto first_app_process;
|
||||||
|
|
||||||
|
// /init without argv1/useless-argv1 but usable envp
|
||||||
|
// untested! TODO: test and debug me!
|
||||||
|
if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
||||||
|
|
||||||
|
// we hunt for "INIT_SECOND_STAGE"
|
||||||
|
const char *envp_n = envp;
|
||||||
|
unsigned int envc = 1;
|
||||||
|
do {
|
||||||
|
if (strstarts(envp_n, "INIT_SECOND_STAGE"))
|
||||||
|
break;
|
||||||
|
envp_n += strlen(envp_n) + 1;
|
||||||
|
envc++;
|
||||||
|
} while (envp_n < envp + envp_len);
|
||||||
|
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
||||||
|
|
||||||
|
if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
|
||||||
|
|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
|
||||||
|
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
|
||||||
|
apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
first_app_process:
|
||||||
|
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
|
||||||
first_app_process = false;
|
first_app_process = false;
|
||||||
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||||
init_second_stage_executed);
|
init_second_stage_executed);
|
||||||
on_post_fs_data(); // we keep this for old ksud
|
struct task_struct *init_task;
|
||||||
|
rcu_read_lock();
|
||||||
|
init_task = rcu_dereference(current->real_parent);
|
||||||
|
if (init_task) {
|
||||||
|
task_work_add(init_task, &on_post_fs_data_cb,
|
||||||
|
TWA_RESUME);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
stop_execve_hook();
|
stop_execve_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_handle_pre_ksud(const char *filename)
|
||||||
|
{
|
||||||
|
if (likely(!ksu_execveat_hook))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
||||||
|
// return 0;
|
||||||
|
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
|
||||||
|
&& !strstarts(filename, "/system/bin/app_process") ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!current || !current->mm)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
|
||||||
|
// unsigned long arg_start, arg_end, env_start, env_end;
|
||||||
|
unsigned long arg_start = current->mm->arg_start;
|
||||||
|
unsigned long arg_end = current->mm->arg_end;
|
||||||
|
unsigned long env_start = current->mm->env_start;
|
||||||
|
unsigned long env_end = current->mm->env_end;
|
||||||
|
|
||||||
|
size_t arg_len = arg_end - arg_start;
|
||||||
|
size_t envp_len = env_end - env_start;
|
||||||
|
|
||||||
|
if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#define ARGV_MAX 32 // this is enough for argv1
|
||||||
|
#define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
|
||||||
|
char args[ARGV_MAX];
|
||||||
|
size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
|
||||||
|
char envp[ENVP_MAX];
|
||||||
|
size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
|
||||||
|
|
||||||
|
// we cant use strncpy on here, else it will truncate once it sees \0
|
||||||
|
if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
args[ARGV_MAX - 1] = '\0';
|
||||||
|
envp[ENVP_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
// we only need argv1 !
|
||||||
|
// abuse strlen here since it only gets length up to \0
|
||||||
|
char *argv1 = args + strlen(args) + 1;
|
||||||
|
if (argv1 >= args + argv_copy_len) // out of bounds!
|
||||||
|
argv1 = "";
|
||||||
|
|
||||||
|
return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
|
struct user_arg_ptr *argv, struct user_arg_ptr *envp,
|
||||||
|
int *flags)
|
||||||
|
{
|
||||||
|
// this is now handled via security_bprm_check
|
||||||
|
// we only keep this for the sake of old hooks.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||||
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||||
static struct file_operations fops_proxy;
|
static struct file_operations fops_proxy;
|
||||||
@@ -311,12 +358,12 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||||
size_t *count_ptr, loff_t **pos)
|
size_t *count_ptr, loff_t **pos)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_KPROBES
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_vfs_read_hook) {
|
if (!ksu_vfs_read_hook) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
struct file *file;
|
struct file *file;
|
||||||
char __user *buf;
|
char __user *buf;
|
||||||
@@ -405,7 +452,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
||||||
size_t *count_ptr)
|
size_t *count_ptr)
|
||||||
{
|
{
|
||||||
struct file *file = fget(fd);
|
struct file *file = fget(fd);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -426,10 +473,10 @@ static bool is_volumedown_enough(unsigned int count)
|
|||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
int *value)
|
int *value)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_KPROBES
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_input_hook) {
|
if (!ksu_input_hook) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
|
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
|
||||||
int val = *value;
|
int val = *value;
|
||||||
@@ -446,7 +493,7 @@ int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_is_safe_mode()
|
bool ksu_is_safe_mode(void)
|
||||||
{
|
{
|
||||||
static bool safe_mode = false;
|
static bool safe_mode = false;
|
||||||
if (safe_mode) {
|
if (safe_mode) {
|
||||||
@@ -468,28 +515,125 @@ bool ksu_is_safe_mode()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
|
||||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
asmlinkage int sys_execve(const char __user *filenamei,
|
||||||
|
const char __user *const __user *argv,
|
||||||
|
const char __user *const __user *envp, struct pt_regs *regs)
|
||||||
|
*/
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
const char __user **filename_user =
|
const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs);
|
||||||
(const char **)&PT_REGS_PARM1(real_regs);
|
const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||||
const char __user *const __user *__argv =
|
const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs);
|
||||||
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
|
||||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
|
||||||
struct filename filename_in, *filename_p;
|
|
||||||
char path[32];
|
char path[32];
|
||||||
|
|
||||||
if (!filename_user)
|
if (!filename_user)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memset(path, 0, sizeof(path));
|
// filename stage
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
if (ksu_copy_from_user_retry(path, filename_user, sizeof(path)))
|
||||||
filename_in.name = path;
|
return 0;
|
||||||
|
|
||||||
filename_p = &filename_in;
|
path[sizeof(path) - 1] = '\0';
|
||||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
|
|
||||||
NULL);
|
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
||||||
|
// we dont care !!
|
||||||
|
if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init")
|
||||||
|
&& !strstarts(path, "/system/bin/app_process") ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// argv stage
|
||||||
|
char argv1[32] = {0};
|
||||||
|
// memzero_explicit(argv1, 32);
|
||||||
|
if (__argv) {
|
||||||
|
const char __user *arg1_user = NULL;
|
||||||
|
// grab argv[1] pointer
|
||||||
|
// this looks like
|
||||||
|
/*
|
||||||
|
* 0x1000 ./program << this is __argv
|
||||||
|
* 0x1001 -o
|
||||||
|
* 0x1002 arg
|
||||||
|
*/
|
||||||
|
if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user)))
|
||||||
|
goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !!
|
||||||
|
|
||||||
|
if (arg1_user)
|
||||||
|
ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1));
|
||||||
|
}
|
||||||
|
|
||||||
|
no_argv1:
|
||||||
|
argv1[sizeof(argv1) - 1] = '\0';
|
||||||
|
|
||||||
|
// envp stage
|
||||||
|
#define ENVP_MAX 256
|
||||||
|
char envp[ENVP_MAX] = {0};
|
||||||
|
char *dst = envp;
|
||||||
|
size_t envp_len = 0;
|
||||||
|
int i = 0; // to track user pointer offset from __envp
|
||||||
|
|
||||||
|
// memzero_explicit(envp, ENVP_MAX);
|
||||||
|
|
||||||
|
if (__envp) {
|
||||||
|
do {
|
||||||
|
const char __user *env_entry_user = NULL;
|
||||||
|
// this is also like argv above
|
||||||
|
/*
|
||||||
|
* 0x1001 PATH=/bin
|
||||||
|
* 0x1002 VARIABLE=value
|
||||||
|
* 0x1002 some_more_env_var=1
|
||||||
|
*/
|
||||||
|
|
||||||
|
// check if pointer exists
|
||||||
|
if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// check if no more env entry
|
||||||
|
if (!env_entry_user)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// probably redundant to while condition but ok
|
||||||
|
if (envp_len >= ENVP_MAX - 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// copy strings from env_entry_user pointer that we collected
|
||||||
|
// also break if failed
|
||||||
|
if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get the length of that new copy above
|
||||||
|
// get lngth of dst as far as ENVP_MAX - current collected envp_len
|
||||||
|
size_t len = strnlen(dst, ENVP_MAX - envp_len);
|
||||||
|
if (envp_len + len + 1 > ENVP_MAX)
|
||||||
|
break; // if more than 255 bytes, bail
|
||||||
|
|
||||||
|
dst[len] = '\0';
|
||||||
|
// collect total number of copied strings
|
||||||
|
envp_len = envp_len + len + 1;
|
||||||
|
// increment dst address since we need to put something on next iter
|
||||||
|
dst = dst + len + 1;
|
||||||
|
// pointer walk, __envp + i
|
||||||
|
i++;
|
||||||
|
} while (envp_len < ENVP_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
at this point, we shoul've collected envp from
|
||||||
|
* 0x1001 PATH=/bin
|
||||||
|
* 0x1002 VARIABLE=value
|
||||||
|
* 0x1002 some_more_env_var=1
|
||||||
|
to
|
||||||
|
* 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0
|
||||||
|
*/
|
||||||
|
|
||||||
|
envp[ENVP_MAX - 1] = '\0';
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len);
|
||||||
|
#endif
|
||||||
|
return ksu_handle_bprm_ksud(path, argv1, envp, envp_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
@@ -540,50 +684,100 @@ static void do_stop_input_hook(struct work_struct *work)
|
|||||||
{
|
{
|
||||||
unregister_kprobe(&input_event_kp);
|
unregister_kprobe(&input_event_kp);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static int ksu_execve_ksud_common(const char __user *filename_user,
|
||||||
|
struct user_arg_ptr *argv)
|
||||||
|
{
|
||||||
|
struct filename filename_in, *filename_p;
|
||||||
|
char path[32];
|
||||||
|
long len;
|
||||||
|
|
||||||
|
// return early if disabled.
|
||||||
|
if (!ksu_execveat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename_user)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = ksu_strncpy_from_user_nofault(path, filename_user, 32);
|
||||||
|
if (len <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
path[sizeof(path) - 1] = '\0';
|
||||||
|
|
||||||
|
// this is because ksu_handle_execveat_ksud calls it filename->name
|
||||||
|
filename_in.name = path;
|
||||||
|
filename_p = &filename_in;
|
||||||
|
|
||||||
|
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __maybe_unused
|
||||||
|
ksu_handle_execve_ksud(const char __user *filename_user,
|
||||||
|
const char __user *const __user *__argv)
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
return ksu_execve_ksud_common(filename_user, &argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT)
|
||||||
|
int __maybe_unused ksu_handle_compat_execve_ksud(
|
||||||
|
const char __user *filename_user, const compat_uptr_t __user *__argv)
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.compat = __argv };
|
||||||
|
return ksu_execve_ksud_common(filename_user, &argv);
|
||||||
|
}
|
||||||
|
#endif /* COMPAT & 64BIT */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void stop_vfs_read_hook()
|
static void stop_vfs_read_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
bool ret = schedule_work(&stop_vfs_read_work);
|
bool ret = schedule_work(&stop_vfs_read_work);
|
||||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
ksu_vfs_read_hook = false;
|
ksu_vfs_read_hook = false;
|
||||||
pr_info("stop vfs_read_hook\n");
|
pr_info("stop vfs_read_hook\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop_execve_hook()
|
static void stop_execve_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
bool ret = schedule_work(&stop_execve_hook_work);
|
bool ret = schedule_work(&stop_execve_hook_work);
|
||||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
ksu_execveat_hook = false;
|
pr_info("stop execve_hook\n");
|
||||||
pr_info("stop execve_hook\n");
|
ksu_execveat_hook = false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop_input_hook()
|
static void stop_input_hook(void)
|
||||||
{
|
{
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
static bool input_hook_stopped = false;
|
static bool input_hook_stopped = false;
|
||||||
if (input_hook_stopped) {
|
if (input_hook_stopped) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
input_hook_stopped = true;
|
input_hook_stopped = true;
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
bool ret = schedule_work(&stop_input_hook_work);
|
bool ret = schedule_work(&stop_input_hook_work);
|
||||||
pr_info("unregister input kprobe: %d!\n", ret);
|
pr_info("unregister input kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
ksu_input_hook = false;
|
if (!ksu_input_hook) {
|
||||||
pr_info("stop input_hook\n");
|
return;
|
||||||
|
}
|
||||||
|
ksu_input_hook = false;
|
||||||
|
pr_info("stop input_hook\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ksud: module support
|
// ksud: module support
|
||||||
void ksu_ksud_init()
|
void ksu_ksud_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = register_kprobe(&execve_kp);
|
ret = register_kprobe(&execve_kp);
|
||||||
@@ -601,9 +795,9 @@ void ksu_ksud_init()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_ksud_exit()
|
void ksu_ksud_exit(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
unregister_kprobe(&execve_kp);
|
unregister_kprobe(&execve_kp);
|
||||||
// this should be done before unregister vfs_read_kp
|
// this should be done before unregister vfs_read_kp
|
||||||
// unregister_kprobe(&vfs_read_kp);
|
// unregister_kprobe(&vfs_read_kp);
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
#ifndef __KSU_H_KSUD
|
#ifndef __KSU_H_KSUD
|
||||||
#define __KSU_H_KSUD
|
#define __KSU_H_KSUD
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define KSUD_PATH "/data/adb/ksud"
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
|
void ksu_ksud_init(void);
|
||||||
|
void ksu_ksud_exit(void);
|
||||||
|
|
||||||
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 u32 ksu_file_sid;
|
||||||
|
extern bool ksu_module_mounted;
|
||||||
|
extern bool ksu_boot_completed;
|
||||||
|
|
||||||
|
extern bool ksu_execveat_hook __read_mostly;
|
||||||
|
extern int ksu_handle_pre_ksud(const char *filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
123
kernel/lsm_hooks.c
Normal file
123
kernel/lsm_hooks.c
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include <linux/lsm_hooks.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
|
||||||
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||||
|
unsigned perm)
|
||||||
|
{
|
||||||
|
if (init_session_keyring != NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (strcmp(current->comm, "init")) {
|
||||||
|
// we are only interested in `init` process
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
init_session_keyring = cred->session_keyring;
|
||||||
|
pr_info("kernel_compat: got init_session_keyring\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
kuid_t new_uid = new->uid;
|
||||||
|
kuid_t new_euid = new->euid;
|
||||||
|
|
||||||
|
return ksu_handle_setresuid((uid_t)new_uid.val, (uid_t)new_euid.val,
|
||||||
|
(uid_t)new_uid.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DEVPTS_SUPER_MAGIC
|
||||||
|
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
bool ksu_is_compat __read_mostly = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ksu_inode_permission(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
if (inode && inode->i_sb
|
||||||
|
&& unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||||
|
//pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
|
||||||
|
__ksu_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_bprm_check(struct linux_binprm *bprm)
|
||||||
|
{
|
||||||
|
char *filename = (char *)bprm->filename;
|
||||||
|
|
||||||
|
if (likely(!ksu_execveat_hook))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static bool compat_check_done __read_mostly = false;
|
||||||
|
if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
|
||||||
|
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
|
||||||
|
if (bprm->buf[4] == 0x01 )
|
||||||
|
ksu_is_compat = true;
|
||||||
|
|
||||||
|
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
|
||||||
|
compat_check_done = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_handle_pre_ksud(filename);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct security_hook_list ksu_hooks[] = {
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
LSM_HOOK_INIT(key_permission, ksu_key_permission),
|
||||||
|
#endif
|
||||||
|
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
||||||
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
|
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
||||||
|
#endif
|
||||||
|
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||||
|
static const struct lsm_id ksu_lsmid = {
|
||||||
|
.name = "ksu",
|
||||||
|
.id = 912,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void __init ksu_lsm_hook_init(void)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
||||||
|
#else
|
||||||
|
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
|
||||||
|
#endif
|
||||||
|
pr_info("LSM hooks initialized.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void ksu_lsm_hook_init(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -8,17 +8,23 @@
|
|||||||
|
|
||||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||||
|
|
||||||
static inline bool ksu_is_manager_uid_valid()
|
extern bool ksu_is_any_manager(uid_t uid);
|
||||||
|
extern void ksu_add_manager(uid_t uid, int signature_index);
|
||||||
|
extern void ksu_remove_manager(uid_t uid);
|
||||||
|
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||||
|
|
||||||
|
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_manager_uid == current_uid().val);
|
return unlikely(ksu_is_any_manager(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;
|
||||||
}
|
}
|
||||||
@@ -28,9 +34,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_observer_init(void);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
19
kernel/manager_sign.h
Normal file
19
kernel/manager_sign.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef MANAGER_SIGN_H
|
||||||
|
#define MANAGER_SIGN_H
|
||||||
|
|
||||||
|
// ShirkNeko/KernelSU
|
||||||
|
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
|
||||||
|
#define EXPECTED_HASH_SHIRKNEKO \
|
||||||
|
"947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
|
||||||
|
|
||||||
|
// Dynamic Sign
|
||||||
|
#define EXPECTED_SIZE_OTHER 0x300
|
||||||
|
#define EXPECTED_HASH_OTHER \
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned size;
|
||||||
|
const char *sha256;
|
||||||
|
} apk_sign_key_t;
|
||||||
|
|
||||||
|
#endif /* MANAGER_SIGN_H */
|
||||||
151
kernel/pkg_observer.c
Normal file
151
kernel/pkg_observer.c
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/fsnotify.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/rculist.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "throne_tracker.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;
|
||||||
|
|
||||||
|
#include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS
|
||||||
|
static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event)
|
||||||
|
{
|
||||||
|
if (!file_name || (mask & FS_ISDIR))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ksu_fname_len(file_name) == 13 &&
|
||||||
|
!memcmp(ksu_fname_arg(file_name), "packages.list", 13)) {
|
||||||
|
pr_info("packages.list detected (mask=%d)\n", mask);
|
||||||
|
track_throne(false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fsnotify_ops ksu_ops = {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
|
.handle_inode_event = ksu_handle_generic_event,
|
||||||
|
#else
|
||||||
|
.handle_event = ksu_handle_generic_event,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __maybe_unused m_free(struct fsnotify_mark *m)
|
||||||
|
{
|
||||||
|
if (m) {
|
||||||
|
kfree(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_mark_on_inode(struct inode *inode, u32 mask,
|
||||||
|
struct fsnotify_mark **out)
|
||||||
|
{
|
||||||
|
struct fsnotify_mark *m;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
m = kzalloc(sizeof(*m), GFP_KERNEL);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
||||||
|
fsnotify_init_mark(m, m_free);
|
||||||
|
m->mask = mask;
|
||||||
|
ret = fsnotify_add_mark(m, g, inode, NULL, 0);
|
||||||
|
#else
|
||||||
|
fsnotify_init_mark(m, g);
|
||||||
|
m->mask = mask;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
ret = fsnotify_add_inode_mark(m, inode, 0);
|
||||||
|
#else
|
||||||
|
ret = fsnotify_add_mark(m, inode, NULL, 0);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
fsnotify_put_mark(m);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*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("%s: done.\n", __func__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_observer_exit(void)
|
||||||
|
{
|
||||||
|
unwatch_one_dir(&g_watch);
|
||||||
|
fsnotify_put_group(g);
|
||||||
|
pr_info("%s: done.\n", __func__);
|
||||||
|
}
|
||||||
42
kernel/pkg_observer_defs.h
Normal file
42
kernel/pkg_observer_defs.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// This header should not be used outside of pkg_observer.c!
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
|
||||||
|
typedef const struct qstr *ksu_fname_t;
|
||||||
|
#define ksu_fname_len(f) ((f)->len)
|
||||||
|
#define ksu_fname_arg(f) ((f)->name)
|
||||||
|
#else
|
||||||
|
typedef const unsigned char *ksu_fname_t;
|
||||||
|
#define ksu_fname_len(f) (strlen(f))
|
||||||
|
#define ksu_fname_arg(f) (f)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
|
#define KSU_DECL_FSNOTIFY_OPS(name) \
|
||||||
|
int name(struct fsnotify_mark *mark, u32 mask, struct inode *inode, \
|
||||||
|
struct inode *dir, const struct qstr *file_name, u32 cookie)
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
|
||||||
|
#define KSU_DECL_FSNOTIFY_OPS(name) \
|
||||||
|
int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \
|
||||||
|
const void *data, int data_type, ksu_fname_t file_name, \
|
||||||
|
u32 cookie, struct fsnotify_iter_info *iter_info)
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
#define KSU_DECL_FSNOTIFY_OPS(name) \
|
||||||
|
int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \
|
||||||
|
const void *data, int data_type, ksu_fname_t file_name, \
|
||||||
|
u32 cookie, struct fsnotify_iter_info *iter_info)
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
||||||
|
#define KSU_DECL_FSNOTIFY_OPS(name) \
|
||||||
|
int name(struct fsnotify_group *group, struct inode *inode, \
|
||||||
|
struct fsnotify_mark *inode_mark, \
|
||||||
|
struct fsnotify_mark *vfsmount_mark, u32 mask, \
|
||||||
|
const void *data, int data_type, ksu_fname_t file_name, \
|
||||||
|
u32 cookie, struct fsnotify_iter_info *iter_info)
|
||||||
|
#else
|
||||||
|
#define KSU_DECL_FSNOTIFY_OPS(name) \
|
||||||
|
int name(struct fsnotify_group *group, struct inode *inode, \
|
||||||
|
struct fsnotify_mark *inode_mark, \
|
||||||
|
struct fsnotify_mark *vfsmount_mark, u32 mask, void *data, \
|
||||||
|
int data_type, ksu_fname_t file_name, u32 cookie)
|
||||||
|
#endif
|
||||||
67
kernel/seccomp_cache.c
Normal file
67
kernel/seccomp_cache.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
#include <linux/fs.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"
|
||||||
|
|
||||||
|
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, 0)
|
||||||
|
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
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
obj-y += selinux.o
|
|
||||||
obj-y += sepolicy.o
|
|
||||||
obj-y += rules.o
|
|
||||||
|
|
||||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
|
||||||
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 += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
|
||||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
#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"
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
|
#endif
|
||||||
|
|
||||||
#define KERNEL_SU_DOMAIN "su"
|
#define KERNEL_SU_DOMAIN "su"
|
||||||
#define KERNEL_SU_FILE "ksu_file"
|
#define KERNEL_SU_FILE "ksu_file"
|
||||||
@@ -19,19 +21,34 @@
|
|||||||
static struct policydb *get_policydb(void)
|
static struct policydb *get_policydb(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
// selinux_state does not exists before 4.19
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
|
struct selinux_policy *policy = selinux_state.policy;
|
||||||
db = &policy->policydb;
|
db = &policy->policydb;
|
||||||
|
#else
|
||||||
|
struct selinux_ss *ss = selinux_state.ss;
|
||||||
|
db = &ss->policydb;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
db = &policydb;
|
||||||
|
#endif
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_kernelsu_rules()
|
static DEFINE_MUTEX(ksu_rules);
|
||||||
|
|
||||||
|
void apply_kernelsu_rules(void)
|
||||||
{
|
{
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
mutex_lock(&ksu_rules);
|
||||||
struct policydb *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");
|
||||||
@@ -118,11 +135,11 @@ void apply_kernelsu_rules()
|
|||||||
// 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");
|
||||||
|
|
||||||
rcu_read_unlock();
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_SEPOL_LEN 128
|
#define MAX_SEPOL_LEN 128
|
||||||
@@ -140,13 +157,13 @@ void apply_kernelsu_rules()
|
|||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
u32 subcmd;
|
u32 subcmd;
|
||||||
char __user *sepol1;
|
u64 sepol1;
|
||||||
char __user *sepol2;
|
u64 sepol2;
|
||||||
char __user *sepol3;
|
u64 sepol3;
|
||||||
char __user *sepol4;
|
u64 sepol4;
|
||||||
char __user *sepol5;
|
u64 sepol5;
|
||||||
char __user *sepol6;
|
u64 sepol6;
|
||||||
char __user *sepol7;
|
u64 sepol7;
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
||||||
@@ -158,18 +175,24 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) || \
|
||||||
|
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
|
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(void)
|
||||||
{
|
{
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||||
|
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
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);
|
||||||
@@ -184,8 +207,10 @@ static void reset_avc_cache()
|
|||||||
|
|
||||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||||
{
|
{
|
||||||
|
struct policydb *db;
|
||||||
|
|
||||||
if (!arg4) {
|
if (!arg4) {
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!getenforce()) {
|
||||||
@@ -195,46 +220,51 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
struct sepol_data data;
|
struct sepol_data data;
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 cmd = data.cmd;
|
u32 cmd = data.cmd;
|
||||||
u32 subcmd = data.subcmd;
|
u32 subcmd = data.subcmd;
|
||||||
|
|
||||||
rcu_read_lock();
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
struct policydb *db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
int ret = -1;
|
int ret = -EINVAL;
|
||||||
if (cmd == CMD_NORMAL_PERM) {
|
switch (cmd) {
|
||||||
|
case CMD_NORMAL_PERM: {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
char perm_buf[MAX_SEPOL_LEN];
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c, *p;
|
char *s, *t, *c, *p;
|
||||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, (void __user *)data.sepol1,
|
||||||
|
sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
if (get_object(perm_buf, (void __user *)data.sepol4,
|
||||||
0) {
|
sizeof(perm_buf), &p) < 0) {
|
||||||
pr_err("sepol: copy perm failed.\n");
|
pr_err("sepol: copy perm failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_allow(db, s, t, c, p);
|
success = ksu_allow(db, s, t, c, p);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
@@ -246,9 +276,10 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -1;
|
ret = success ? 0 : -EINVAL;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_XPERM) {
|
}
|
||||||
|
case CMD_XPERM: {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
@@ -258,25 +289,28 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
char perm_set[MAX_SEPOL_LEN];
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c;
|
char *s, *t, *c;
|
||||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, (void __user *)data.sepol1,
|
||||||
|
sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(operation, data.sepol4,
|
if (strncpy_from_user(operation, (void __user *)data.sepol4,
|
||||||
sizeof(operation)) < 0) {
|
sizeof(operation)) < 0) {
|
||||||
pr_err("sepol: copy operation failed.\n");
|
pr_err("sepol: copy operation failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
|
if (strncpy_from_user(perm_set, (void __user *)data.sepol5,
|
||||||
0) {
|
sizeof(perm_set)) < 0) {
|
||||||
pr_err("sepol: copy perm_set failed.\n");
|
pr_err("sepol: copy perm_set failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -291,11 +325,14 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -1;
|
ret = success ? 0 : -EINVAL;
|
||||||
} else if (cmd == CMD_TYPE_STATE) {
|
break;
|
||||||
|
}
|
||||||
|
case CMD_TYPE_STATE: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -310,16 +347,20 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
}
|
||||||
|
case CMD_TYPE:
|
||||||
|
case CMD_TYPE_ATTR: {
|
||||||
char type[MAX_SEPOL_LEN];
|
char type[MAX_SEPOL_LEN];
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
if (strncpy_from_user(type, (void __user *)data.sepol1,
|
||||||
|
sizeof(type)) < 0) {
|
||||||
pr_err("sepol: copy type failed.\n");
|
pr_err("sepol: copy type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, (void __user *)data.sepol2,
|
||||||
|
sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -335,11 +376,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_ATTR) {
|
}
|
||||||
|
case CMD_ATTR: {
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, (void __user *)data.sepol1,
|
||||||
|
sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -348,36 +391,41 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
}
|
||||||
|
case CMD_TYPE_TRANSITION: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
char object[MAX_SEPOL_LEN];
|
char object[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, data.sepol4,
|
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
char *real_object;
|
char *real_object;
|
||||||
if (data.sepol5 == NULL) {
|
if ((void __user *)data.sepol5 == NULL) {
|
||||||
real_object = NULL;
|
real_object = NULL;
|
||||||
} else {
|
} else {
|
||||||
if (strncpy_from_user(object, data.sepol5,
|
if (strncpy_from_user(object,
|
||||||
|
(void __user *)data.sepol5,
|
||||||
sizeof(object)) < 0) {
|
sizeof(object)) < 0) {
|
||||||
pr_err("sepol: copy object failed.\n");
|
pr_err("sepol: copy object failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -389,26 +437,30 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
default_type, real_object);
|
default_type, real_object);
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
}
|
||||||
|
case CMD_TYPE_CHANGE: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, data.sepol4,
|
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -425,20 +477,24 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else if (cmd == CMD_GENFSCON) {
|
break;
|
||||||
|
}
|
||||||
|
case CMD_GENFSCON: {
|
||||||
char name[MAX_SEPOL_LEN];
|
char name[MAX_SEPOL_LEN];
|
||||||
char path[MAX_SEPOL_LEN];
|
char path[MAX_SEPOL_LEN];
|
||||||
char context[MAX_SEPOL_LEN];
|
char context[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
|
if (strncpy_from_user(name, (void __user *)data.sepol1,
|
||||||
|
sizeof(name)) < 0) {
|
||||||
pr_err("sepol: copy name failed.\n");
|
pr_err("sepol: copy name failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
|
if (strncpy_from_user(path, (void __user *)data.sepol2,
|
||||||
|
sizeof(path)) < 0) {
|
||||||
pr_err("sepol: copy path failed.\n");
|
pr_err("sepol: copy path failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
|
if (strncpy_from_user(context, (void __user *)data.sepol3,
|
||||||
0) {
|
sizeof(context)) < 0) {
|
||||||
pr_err("sepol: copy context failed.\n");
|
pr_err("sepol: copy context failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -448,12 +504,16 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
rcu_read_unlock();
|
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.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "selinux.h"
|
#include "linux/cred.h"
|
||||||
#include "objsec.h"
|
#include "linux/sched.h"
|
||||||
|
#include "linux/security.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
|
#include "selinux_defs.h"
|
||||||
#include "../klog.h" // IWYU pragma: keep
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||||
@@ -34,41 +36,50 @@ static int transive_to_domain(const char *domain)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)
|
||||||
|
bool __maybe_unused
|
||||||
|
is_ksu_transition(const struct task_security_struct *old_tsec,
|
||||||
|
const struct task_security_struct *new_tsec)
|
||||||
|
{
|
||||||
|
static u32 ksu_sid;
|
||||||
|
char *secdata;
|
||||||
|
u32 seclen;
|
||||||
|
bool allowed = false;
|
||||||
|
|
||||||
|
if (!ksu_sid)
|
||||||
|
security_secctx_to_secid(KERNEL_SU_DOMAIN,
|
||||||
|
strlen(KERNEL_SU_DOMAIN), &ksu_sid);
|
||||||
|
|
||||||
|
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
||||||
|
security_release_secctx(secdata, seclen);
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
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
|
__setenforce(enforce);
|
||||||
selinux_state.enforcing = enforce;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getenforce()
|
bool getenforce(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
if (is_selinux_disabled()) {
|
||||||
if (selinux_state.disabled) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
return __is_selinux_enforcing();
|
||||||
return selinux_state.enforcing;
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||||
@@ -84,47 +95,90 @@ static inline u32 current_sid(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_ksu_domain()
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
|
||||||
|
struct lsm_context {
|
||||||
|
char *context;
|
||||||
|
u32 len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
|
||||||
{
|
{
|
||||||
char *domain;
|
return security_secid_to_secctx(secid, &cp->context, &cp->len);
|
||||||
u32 seclen;
|
}
|
||||||
|
static void __security_release_secctx(struct lsm_context *cp)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred *cred)
|
||||||
|
{
|
||||||
|
struct lsm_context ctx;
|
||||||
bool result;
|
bool result;
|
||||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
if (!cred) {
|
||||||
if (err) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
const struct task_security_struct *tsec = __selinux_cred(cred);
|
||||||
security_release_secctx(domain, seclen);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_zygote(void *sec)
|
|
||||||
{
|
|
||||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char *domain;
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
u32 seclen;
|
|
||||||
bool result;
|
|
||||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||||
security_release_secctx(domain, seclen);
|
__security_release_secctx(&ctx);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
bool is_ksu_domain(void)
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid()
|
|
||||||
{
|
{
|
||||||
u32 devpts_sid = 0;
|
current_sid();
|
||||||
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
return is_task_ksu_domain(current_cred());
|
||||||
&devpts_sid);
|
}
|
||||||
if (err) {
|
|
||||||
pr_info("get devpts sid err %d\n", err);
|
bool is_context(const struct cred *cred, const char *context)
|
||||||
}
|
{
|
||||||
return devpts_sid;
|
if (!cred) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const struct task_security_struct *tsec = __selinux_cred(cred);
|
||||||
|
if (!tsec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct lsm_context ctx;
|
||||||
|
bool result;
|
||||||
|
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = strncmp(context, ctx.context, ctx.len) == 0;
|
||||||
|
__security_release_secctx(&ctx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zygote(const struct cred *cred)
|
||||||
|
{
|
||||||
|
return is_context(cred, "u:r:zygote:s0");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_init(const struct cred *cred)
|
||||||
|
{
|
||||||
|
return is_context(cred, "u:r:init:s0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
|
||||||
|
|
||||||
|
u32 ksu_get_ksu_file_sid(void)
|
||||||
|
{
|
||||||
|
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,19 +3,31 @@
|
|||||||
|
|
||||||
#include "linux/types.h"
|
#include "linux/types.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
|
||||||
|
defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||||
|
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#endif
|
||||||
|
|
||||||
void setup_selinux(const char *);
|
void setup_selinux(const char *);
|
||||||
|
|
||||||
void setenforce(bool);
|
void setenforce(bool);
|
||||||
|
|
||||||
bool getenforce();
|
bool getenforce(void);
|
||||||
|
|
||||||
bool is_ksu_domain();
|
bool is_task_ksu_domain(const struct cred *cred);
|
||||||
|
|
||||||
bool is_zygote(void *cred);
|
bool is_ksu_domain(void);
|
||||||
|
|
||||||
void apply_kernelsu_rules();
|
bool is_zygote(const struct cred *cred);
|
||||||
|
|
||||||
u32 ksu_get_devpts_sid();
|
bool is_init(const struct cred *cred);
|
||||||
|
|
||||||
|
void apply_kernelsu_rules(void);
|
||||||
|
|
||||||
|
u32 ksu_get_ksu_file_sid(void);
|
||||||
|
|
||||||
|
int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
42
kernel/selinux/selinux_defs.h
Normal file
42
kernel/selinux/selinux_defs.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef __KSU_H_SELINUX_DEFS
|
||||||
|
#define __KSU_H_SELINUX_DEFS
|
||||||
|
|
||||||
|
#include "selinux.h"
|
||||||
|
#include "objsec.h"
|
||||||
|
#ifdef SAMSUNG_SELINUX_PORTING
|
||||||
|
#include "security.h" // Samsung SELinux Porting
|
||||||
|
#endif
|
||||||
|
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#include "avc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#define is_selinux_disabled() (selinux_state.disabled)
|
||||||
|
#else
|
||||||
|
#define is_selinux_disabled() (selinux_disabled)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define is_selinux_disabled() (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#define __is_selinux_enforcing() (selinux_state.enforcing)
|
||||||
|
#define __setenforce(val) selinux_state.enforcing = val
|
||||||
|
#elif defined(SAMSUNG_SELINUX_PORTING) || !defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
|
#define __is_selinux_enforcing() (selinux_enforcing)
|
||||||
|
#define __setenforce(val) selinux_enforcing = val
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define __is_selinux_enforcing() (1)
|
||||||
|
#define __setenforce(val)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef KSU_OPTIONAL_SELINUX_CRED
|
||||||
|
#define __selinux_cred(cred) (selinux_cred(cred))
|
||||||
|
#else
|
||||||
|
#define __selinux_cred(cred) (cred->security)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -355,7 +355,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
|||||||
|
|
||||||
if (datum->u.xperms == NULL) {
|
if (datum->u.xperms == NULL) {
|
||||||
datum->u.xperms =
|
datum->u.xperms =
|
||||||
(struct avtab_extended_perms *)(kmalloc(
|
(struct avtab_extended_perms *)(kzalloc(
|
||||||
sizeof(xperms), GFP_KERNEL));
|
sizeof(xperms), GFP_KERNEL));
|
||||||
if (!datum->u.xperms) {
|
if (!datum->u.xperms) {
|
||||||
pr_err("alloc xperms failed\n");
|
pr_err("alloc xperms failed\n");
|
||||||
@@ -524,6 +524,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
|
||||||
struct filename_trans_key key;
|
struct filename_trans_key key;
|
||||||
key.ttype = tgt->value;
|
key.ttype = tgt->value;
|
||||||
key.tclass = cls->value;
|
key.tclass = cls->value;
|
||||||
@@ -531,8 +532,13 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
|
|
||||||
struct filename_trans_datum *last = NULL;
|
struct filename_trans_datum *last = NULL;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
struct filename_trans_datum *trans =
|
struct filename_trans_datum *trans =
|
||||||
policydb_filenametr_search(db, &key);
|
policydb_filenametr_search(db, &key);
|
||||||
|
#else
|
||||||
|
struct filename_trans_datum *trans =
|
||||||
|
hashtab_search(&db->filename_trans, &key);
|
||||||
|
#endif
|
||||||
while (trans) {
|
while (trans) {
|
||||||
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
|
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
|
||||||
// Duplicate, overwrite existing data and return
|
// Duplicate, overwrite existing data and return
|
||||||
@@ -549,7 +555,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||||
1, GFP_ATOMIC);
|
1, GFP_ATOMIC);
|
||||||
struct filename_trans_key *new_key =
|
struct filename_trans_key *new_key =
|
||||||
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
(struct filename_trans_key *)kzalloc(sizeof(*new_key),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
*new_key = key;
|
*new_key = key;
|
||||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||||
@@ -561,6 +567,39 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
|
|
||||||
db->compat_filename_trans_count++;
|
db->compat_filename_trans_count++;
|
||||||
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
||||||
|
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
|
||||||
|
|
||||||
|
struct filename_trans key;
|
||||||
|
key.ttype = tgt->value;
|
||||||
|
key.tclass = cls->value;
|
||||||
|
key.name = (char *)o;
|
||||||
|
|
||||||
|
struct filename_trans_datum *trans =
|
||||||
|
hashtab_search(db->filename_trans, &key);
|
||||||
|
|
||||||
|
if (trans == NULL) {
|
||||||
|
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||||
|
1, GFP_ATOMIC);
|
||||||
|
if (!trans) {
|
||||||
|
pr_err("add_filename_trans: Failed to alloc datum\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct filename_trans *new_key =
|
||||||
|
(struct filename_trans *)kzalloc(sizeof(*new_key),
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!new_key) {
|
||||||
|
pr_err("add_filename_trans: Failed to alloc new_key\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*new_key = key;
|
||||||
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||||
|
trans->otype = def->value;
|
||||||
|
hashtab_insert(db->filename_trans, new_key, trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
|
||||||
|
0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
||||||
@@ -587,6 +626,7 @@ static void *ksu_realloc(void *old, size_t new_size, size_t old_size)
|
|||||||
|
|
||||||
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||||
{
|
{
|
||||||
|
#ifdef KSU_SUPPORT_ADD_TYPE
|
||||||
struct type_datum *type = symtab_search(&db->p_types, type_name);
|
struct type_datum *type = symtab_search(&db->p_types, type_name);
|
||||||
if (type) {
|
if (type) {
|
||||||
pr_warn("Type %s already exists\n", type_name);
|
pr_warn("Type %s already exists\n", type_name);
|
||||||
@@ -616,6 +656,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
struct ebitmap *new_type_attr_map_array =
|
struct ebitmap *new_type_attr_map_array =
|
||||||
ksu_realloc(db->type_attr_map_array,
|
ksu_realloc(db->type_attr_map_array,
|
||||||
value * sizeof(struct ebitmap),
|
value * sizeof(struct ebitmap),
|
||||||
@@ -662,6 +703,171 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#elif defined(CONFIG_IS_HW_HISI)
|
||||||
|
/*
|
||||||
|
* Huawei use type_attr_map and type_val_to_struct.
|
||||||
|
* And use ebitmap not flex_array.
|
||||||
|
*/
|
||||||
|
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
||||||
|
struct ebitmap *new_type_attr_map =
|
||||||
|
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
|
||||||
|
|
||||||
|
struct type_datum **new_type_val_to_struct =
|
||||||
|
krealloc(db->type_val_to_struct,
|
||||||
|
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!new_type_attr_map) {
|
||||||
|
pr_err("add_type: alloc type_attr_map failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_type_val_to_struct) {
|
||||||
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **new_val_to_name_types =
|
||||||
|
krealloc(db->sym_val_to_name[SYM_TYPES],
|
||||||
|
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!new_val_to_name_types) {
|
||||||
|
pr_err("add_type: alloc val_to_name failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
db->type_attr_map = new_type_attr_map;
|
||||||
|
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
|
||||||
|
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
|
||||||
|
|
||||||
|
db->type_val_to_struct = new_type_val_to_struct;
|
||||||
|
db->type_val_to_struct[value - 1] = type;
|
||||||
|
|
||||||
|
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||||
|
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||||
|
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// flex_array is not extensible, we need to create a new bigger one instead
|
||||||
|
struct flex_array *new_type_attr_map_array =
|
||||||
|
flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
|
||||||
|
struct flex_array *new_type_val_to_struct =
|
||||||
|
flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
|
||||||
|
struct flex_array *new_val_to_name_types =
|
||||||
|
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
|
||||||
|
if (!new_type_attr_map_array) {
|
||||||
|
pr_err("add_type: alloc type_attr_map_array failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_type_val_to_struct) {
|
||||||
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new_val_to_name_types) {
|
||||||
|
pr_err("add_type: alloc val_to_name failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// preallocate so we don't have to worry about the put ever failing
|
||||||
|
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO)) {
|
||||||
|
pr_err("add_type: prealloc type_attr_map_array failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO)) {
|
||||||
|
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flex_array_prealloc(new_val_to_name_types, 0,
|
||||||
|
db->symtab[SYM_TYPES].nprim,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO)) {
|
||||||
|
pr_err("add_type: prealloc val_to_name_types failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int j;
|
||||||
|
void *old_elem;
|
||||||
|
// copy the old data or pointers to new flex arrays
|
||||||
|
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
|
||||||
|
old_elem = flex_array_get(db->type_attr_map_array, j);
|
||||||
|
if (old_elem)
|
||||||
|
flex_array_put(new_type_attr_map_array, j, old_elem,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
|
||||||
|
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
|
||||||
|
if (old_elem)
|
||||||
|
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
|
||||||
|
old_elem =
|
||||||
|
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
||||||
|
if (old_elem)
|
||||||
|
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the pointer of old flex arrays first, when assigning new ones we
|
||||||
|
// should free it
|
||||||
|
struct flex_array *old_fa;
|
||||||
|
|
||||||
|
old_fa = db->type_attr_map_array;
|
||||||
|
db->type_attr_map_array = new_type_attr_map_array;
|
||||||
|
if (old_fa) {
|
||||||
|
flex_array_free(old_fa);
|
||||||
|
}
|
||||||
|
|
||||||
|
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
|
||||||
|
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
|
||||||
|
value - 1, 1);
|
||||||
|
|
||||||
|
old_fa = db->type_val_to_struct_array;
|
||||||
|
db->type_val_to_struct_array = new_type_val_to_struct;
|
||||||
|
if (old_fa) {
|
||||||
|
flex_array_free(old_fa);
|
||||||
|
}
|
||||||
|
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
|
||||||
|
old_fa = db->sym_val_to_name[SYM_TYPES];
|
||||||
|
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||||
|
if (old_fa) {
|
||||||
|
flex_array_free(old_fa);
|
||||||
|
}
|
||||||
|
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
|
||||||
|
GFP_ATOMIC | __GFP_ZERO);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||||
|
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool set_type_state(struct policydb *db, const char *type_name,
|
static bool set_type_state(struct policydb *db, const char *type_name,
|
||||||
@@ -696,7 +902,18 @@ static bool set_type_state(struct policydb *db, const char *type_name,
|
|||||||
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||||
struct type_datum *attr)
|
struct type_datum *attr)
|
||||||
{
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
|
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
|
||||||
|
#elif defined(CONFIG_IS_HW_HISI)
|
||||||
|
/*
|
||||||
|
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
|
||||||
|
*/
|
||||||
|
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
|
||||||
|
HISI_SELINUX_EBITMAP_RO;
|
||||||
|
#else
|
||||||
|
struct ebitmap *sattr =
|
||||||
|
flex_array_get(db->type_attr_map_array, type->value - 1);
|
||||||
|
#endif
|
||||||
ebitmap_set_bit(sattr, attr->value - 1, 1);
|
ebitmap_set_bit(sattr, attr->value - 1, 1);
|
||||||
|
|
||||||
struct hashtab_node *node;
|
struct hashtab_node *node;
|
||||||
|
|||||||
186
kernel/setuid_hook.c
Normal file
186
kernel/setuid_hook.c
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/seccomp.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/stddef.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/uidgid.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"
|
||||||
|
|
||||||
|
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(void)
|
||||||
|
{
|
||||||
|
if (is_manager()) {
|
||||||
|
// we are manager, allow!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// force_sig kcompat, TODO: move it out of core_hook.c
|
||||||
|
// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||||
|
#define __force_sig(sig) force_sig(sig)
|
||||||
|
#else
|
||||||
|
#define __force_sig(sig) force_sig(sig, current)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void disable_seccomp(struct task_struct *tsk);
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||||
|
{
|
||||||
|
// we rely on the fact that zygote always call setresuid(3) with same uids
|
||||||
|
uid_t new_uid = ruid;
|
||||||
|
uid_t old_uid = current_uid().val;
|
||||||
|
|
||||||
|
if (old_uid != new_uid)
|
||||||
|
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 ksu manager(uid=%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(current);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void ksu_lsm_hook_init(void);
|
||||||
|
void ksu_setuid_hook_init(void)
|
||||||
|
{
|
||||||
|
ksu_kernel_umount_init();
|
||||||
|
ksu_lsm_hook_init(); // <4.11
|
||||||
|
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);
|
||||||
|
}
|
||||||
13
kernel/setuid_hook.h
Normal file
13
kernel/setuid_hook.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef __KSU_H_KSU_SETUID_HOOK
|
||||||
|
#define __KSU_H_KSU_SETUID_HOOK
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void);
|
||||||
|
void ksu_setuid_hook_exit(void);
|
||||||
|
|
||||||
|
// Handler functions for hook_manager
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -41,8 +41,7 @@ setup_kernelsu() {
|
|||||||
echo "[+] Setting up KernelSU..."
|
echo "[+] Setting up KernelSU..."
|
||||||
# Clone the repository and rename it to KernelSU
|
# Clone the repository and rename it to KernelSU
|
||||||
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
|
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
|
||||||
git clone https://github.com/ShirkNeko/SukiSU-Ultra SukiSU-Ultra
|
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra KernelSU
|
||||||
mv SukiSU-Ultra KernelSU
|
|
||||||
echo "[+] Repository cloned and renamed to KernelSU."
|
echo "[+] Repository cloned and renamed to KernelSU."
|
||||||
fi
|
fi
|
||||||
cd "$GKI_ROOT/KernelSU"
|
cd "$GKI_ROOT/KernelSU"
|
||||||
|
|||||||
@@ -1,31 +1,63 @@
|
|||||||
#include <linux/dcache.h>
|
|
||||||
#include <linux/security.h>
|
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.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/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "objsec.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 "syscall_hook_manager.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;
|
||||||
|
|
||||||
|
static const char su[] = SU_PATH;
|
||||||
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
|
|
||||||
|
static int su_compat_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*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;
|
||||||
@@ -40,211 +72,140 @@ static char __user *sh_user_path(void)
|
|||||||
|
|
||||||
static char __user *ksud_user_path(void)
|
static char __user *ksud_user_path(void)
|
||||||
{
|
{
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool __is_su_allowed(const void *ptr_to_check)
|
||||||
|
{
|
||||||
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
|
if (!ksu_su_compat_enabled)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
if (!ksu_is_allow_uid_for_current(current_uid().val))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (unlikely(!ptr_to_check))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#define is_su_allowed(ptr) __is_su_allowed((const void *)ptr)
|
||||||
|
|
||||||
|
static int ksu_sucompat_user_common(const char __user **filename_user,
|
||||||
|
const char *syscall_name,
|
||||||
|
const bool escalate)
|
||||||
|
{
|
||||||
|
char path[sizeof(su)]; // sizeof includes nullterm already!
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (memcmp(path, su, sizeof(su)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (escalate) {
|
||||||
|
pr_info("%s su found\n", syscall_name);
|
||||||
|
*filename_user = ksud_user_path();
|
||||||
|
escape_with_root_profile(); // escalate !!
|
||||||
|
} else {
|
||||||
|
pr_info("%s su->sh!\n", syscall_name);
|
||||||
|
*filename_user = sh_user_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
if (!is_su_allowed(filename_user))
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
return ksu_sucompat_user_common(filename_user, "faccessat", false);
|
||||||
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;
|
if (!is_su_allowed(filename_user))
|
||||||
const char su[] = SU_PATH;
|
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!filename_user)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char path[sizeof(su) + 1];
|
|
||||||
memset(path, 0, sizeof(path));
|
|
||||||
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
|
||||||
// it becomes a `struct filename *` after 5.18
|
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
|
||||||
const char sh[] = SH_PATH;
|
|
||||||
struct filename *filename = *((struct filename **)filename_user);
|
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
pr_info("vfs_statx su->sh!\n");
|
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
#else
|
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
|
||||||
pr_info("newfstatat su->sh!\n");
|
|
||||||
*filename_user = sh_user_path();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (unlikely(!filename_ptr))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
filename = *filename_ptr;
|
return ksu_sucompat_user_common(filename_user, "newfstatat", false);
|
||||||
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,
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
int *__never_use_flags)
|
int *__never_use_flags)
|
||||||
{
|
{
|
||||||
const char su[] = SU_PATH;
|
if (!is_su_allowed(filename_user))
|
||||||
char path[sizeof(su) + 1];
|
|
||||||
|
|
||||||
if (unlikely(!filename_user))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memset(path, 0, sizeof(path));
|
return ksu_sucompat_user_common(filename_user, "sys_execve", true);
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
}
|
||||||
|
|
||||||
if (likely(memcmp(path, su, sizeof(su))))
|
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;
|
||||||
|
|
||||||
|
if (!is_su_allowed(filename_ptr))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
filename = *filename_ptr;
|
||||||
|
if (IS_ERR(filename))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pr_info("sys_execve su found\n");
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
*filename_user = ksud_user_path();
|
return 0;
|
||||||
|
|
||||||
escape_to_root();
|
pr_info("do_execveat_common su found\n");
|
||||||
|
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
||||||
|
|
||||||
|
escape_with_root_profile();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
int __ksu_handle_devpts(struct inode *inode)
|
||||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
{
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
if (!ksu_su_compat_enabled)
|
||||||
const char __user **filename_user =
|
return 0;
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
#endif
|
||||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
if (!current->mm) {
|
||||||
}
|
return 0;
|
||||||
|
|
||||||
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 *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;
|
uid_t uid = current_uid().val;
|
||||||
}
|
if (uid % 100000 < 10000) {
|
||||||
|
// not untrusted_app, ignore it
|
||||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
return 0;
|
||||||
{
|
|
||||||
struct kprobe *kp = *kp_ptr;
|
|
||||||
if (!kp)
|
|
||||||
return;
|
|
||||||
unregister_kprobe(kp);
|
|
||||||
synchronize_rcu();
|
|
||||||
kfree(kp);
|
|
||||||
*kp_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe *su_kps[3];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
|
||||||
void ksu_sucompat_init()
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
|
||||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
|
||||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_sucompat_exit()
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_KPROBES
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
|
||||||
destroy_kprobe(&su_kps[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (likely(!ksu_is_allow_uid(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
|
#endif
|
||||||
|
|
||||||
|
if (ksu_file_sid && sec)
|
||||||
|
sec->sid = ksu_file_sid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sucompat: permitted process can execute 'su' to gain root access.
|
||||||
|
void ksu_sucompat_init(void)
|
||||||
|
{
|
||||||
|
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||||
|
pr_err("Failed to register su_compat feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sucompat_exit(void)
|
||||||
|
{
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
||||||
}
|
}
|
||||||
|
|||||||
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(int *fd, const char __user **filename_user,
|
||||||
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags);
|
||||||
|
|
||||||
|
#endif
|
||||||
993
kernel/supercalls.c
Normal file
993
kernel/supercalls.c
Normal file
@@ -0,0 +1,993 @@
|
|||||||
|
#include <linux/anon_inodes.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fdtable.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "supercalls.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "objsec.h"
|
||||||
|
#include "file_wrapper.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool ksu_uid_scanner_enabled = false;
|
||||||
|
|
||||||
|
// Permission check functions
|
||||||
|
bool only_manager(void)
|
||||||
|
{
|
||||||
|
return is_manager();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool only_root(void)
|
||||||
|
{
|
||||||
|
return current_uid().val == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool manager_or_root(void)
|
||||||
|
{
|
||||||
|
return current_uid().val == 0 || is_manager();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool always_allow(void)
|
||||||
|
{
|
||||||
|
return true; // No permission check
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allowed_for_su(void)
|
||||||
|
{
|
||||||
|
bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
return is_allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_uid_scanner(void)
|
||||||
|
{
|
||||||
|
ksu_uid_init();
|
||||||
|
do_load_throne_state(NULL);
|
||||||
|
|
||||||
|
if (ksu_uid_scanner_enabled) {
|
||||||
|
int ret = ksu_throne_comm_init();
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("Failed to initialize throne communication: %d\n", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_grant_root(void __user *arg)
|
||||||
|
{
|
||||||
|
// we already check uid above on allowed_for_su()
|
||||||
|
|
||||||
|
pr_info("allow root for: %d\n", current_uid().val);
|
||||||
|
escape_with_root_profile();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_info(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0};
|
||||||
|
|
||||||
|
#ifdef MODULE
|
||||||
|
cmd.flags |= 0x1;
|
||||||
|
#endif
|
||||||
|
if (is_manager()) {
|
||||||
|
cmd.flags |= 0x2;
|
||||||
|
}
|
||||||
|
cmd.features = KSU_FEATURE_MAX;
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_version: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_report_event(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_report_event_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd.event) {
|
||||||
|
case EVENT_POST_FS_DATA: {
|
||||||
|
static bool post_fs_data_lock = false;
|
||||||
|
if (!post_fs_data_lock) {
|
||||||
|
post_fs_data_lock = true;
|
||||||
|
pr_info("post-fs-data triggered\n");
|
||||||
|
on_post_fs_data();
|
||||||
|
init_uid_scanner();
|
||||||
|
ksu_dynamic_manager_init();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EVENT_BOOT_COMPLETED: {
|
||||||
|
static bool boot_complete_lock = false;
|
||||||
|
if (!boot_complete_lock) {
|
||||||
|
boot_complete_lock = true;
|
||||||
|
pr_info("boot_complete triggered\n");
|
||||||
|
on_boot_completed();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EVENT_MODULE_MOUNTED: {
|
||||||
|
pr_info("module mounted!\n");
|
||||||
|
on_module_mounted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_set_sepolicy(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_set_sepolicy_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_check_safemode(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_check_safemode_cmd cmd;
|
||||||
|
|
||||||
|
cmd.in_safe_mode = ksu_is_safe_mode();
|
||||||
|
|
||||||
|
if (cmd.in_safe_mode) {
|
||||||
|
pr_warn("safemode enabled!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("check_safemode: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_allow_list(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_allow_list_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_allow_list: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_deny_list(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_allow_list_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_deny_list: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_uid_granted_root(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_uid_granted_root_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.granted = ksu_is_allow_uid_for_current(cmd.uid);
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("uid_granted_root: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_uid_should_umount(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_uid_should_umount_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.should_umount = ksu_uid_should_umount(cmd.uid);
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("uid_should_umount: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_manager_uid(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_manager_uid_cmd cmd;
|
||||||
|
|
||||||
|
cmd.uid = ksu_get_manager_uid();
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_manager_uid: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_app_profile(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_app_profile_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("get_app_profile: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_get_app_profile(&cmd.profile)) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_app_profile: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_set_app_profile(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_set_app_profile_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("set_app_profile: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_set_app_profile(&cmd.profile, true)) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_feature(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_feature_cmd cmd;
|
||||||
|
bool supported;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("get_feature: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported);
|
||||||
|
cmd.supported = supported ? 1 : 0;
|
||||||
|
|
||||||
|
if (ret && supported) {
|
||||||
|
pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_feature: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_set_feature(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_set_feature_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("set_feature: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ksu_set_feature(cmd.feature_id, cmd.value);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_wrapper_fd(void __user *arg) {
|
||||||
|
if (!ksu_file_sid) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ksu_get_wrapper_fd_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("get_wrapper_fd: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file* f = fget(cmd.fd);
|
||||||
|
if (!f) {
|
||||||
|
return -EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ksu_file_wrapper *data = ksu_create_file_wrapper(f);
|
||||||
|
if (data == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto put_orig_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// kcompat for older kernel
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
|
#define getfd_secure anon_inode_create_getfd
|
||||||
|
#elif defined(KSU_HAS_GETFD_SECURE)
|
||||||
|
#define getfd_secure anon_inode_getfd_secure
|
||||||
|
#else
|
||||||
|
// technically not a secure inode, but, this is the only way so.
|
||||||
|
#define getfd_secure(name, ops, data, flags, __unused) \
|
||||||
|
anon_inode_getfd(name, ops, data, flags)
|
||||||
|
#endif
|
||||||
|
ret = getfd_secure("[ksu_fdwrapper]", &data->ops, data, f->f_flags, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("ksu_fdwrapper: getfd failed: %d\n", ret);
|
||||||
|
goto put_wrapper_data;
|
||||||
|
}
|
||||||
|
struct file* pf = fget(ret);
|
||||||
|
|
||||||
|
struct inode* wrapper_inode = file_inode(pf);
|
||||||
|
// copy original inode mode
|
||||||
|
wrapper_inode->i_mode = file_inode(f)->i_mode;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
|
||||||
|
struct inode_security_struct *sec = selinux_inode(wrapper_inode);
|
||||||
|
#else
|
||||||
|
struct inode_security_struct *sec = (struct inode_security_struct *)wrapper_inode->i_security;
|
||||||
|
#endif
|
||||||
|
if (sec) {
|
||||||
|
sec->sid = ksu_file_sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fput(pf);
|
||||||
|
goto put_orig_file;
|
||||||
|
put_wrapper_data:
|
||||||
|
ksu_delete_file_wrapper(data);
|
||||||
|
put_orig_file:
|
||||||
|
fput(f);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_manage_mark(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_manage_mark_cmd cmd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("manage_mark: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd.operation) {
|
||||||
|
case KSU_MARK_GET: {
|
||||||
|
// Get task mark status
|
||||||
|
ret = ksu_get_task_mark(cmd.pid);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
cmd.result = (u32)ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_MARK: {
|
||||||
|
if (cmd.pid == 0) {
|
||||||
|
ksu_mark_all_process();
|
||||||
|
} else {
|
||||||
|
ret = ksu_set_task_mark(cmd.pid, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid,
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_UNMARK: {
|
||||||
|
if (cmd.pid == 0) {
|
||||||
|
ksu_unmark_all_process();
|
||||||
|
} else {
|
||||||
|
ret = ksu_set_task_mark(cmd.pid, false);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: set_unmark failed for pid %d: %d\n",
|
||||||
|
cmd.pid, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_REFRESH: {
|
||||||
|
ksu_mark_running_process();
|
||||||
|
pr_info("manage_mark: refreshed running processes\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
pr_err("manage_mark: invalid operation %u\n", cmd.operation);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("manage_mark: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct list_head mount_list = LIST_HEAD_INIT(mount_list);
|
||||||
|
DECLARE_RWSEM(mount_list_lock);
|
||||||
|
|
||||||
|
static int add_try_umount(void __user *arg)
|
||||||
|
{
|
||||||
|
struct mount_entry *new_entry, *entry, *tmp;
|
||||||
|
struct ksu_add_try_umount_cmd cmd;
|
||||||
|
char buf[256] = { 0 };
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof cmd))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
switch (cmd.mode) {
|
||||||
|
case KSU_UMOUNT_WIPE: {
|
||||||
|
struct mount_entry *entry, *tmp;
|
||||||
|
down_write(&mount_list_lock);
|
||||||
|
list_for_each_entry_safe (entry, tmp, &mount_list, list) {
|
||||||
|
pr_info("wipe_umount_list: removing entry: %s\n",
|
||||||
|
entry->umountable);
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry->umountable);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
up_write(&mount_list_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KSU_UMOUNT_ADD: {
|
||||||
|
long len = strncpy_from_user(buf, (const char __user *)cmd.arg,
|
||||||
|
256);
|
||||||
|
if (len <= 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
|
||||||
|
new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
|
||||||
|
if (!new_entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
new_entry->umountable = kstrdup(buf, GFP_KERNEL);
|
||||||
|
if (!new_entry->umountable) {
|
||||||
|
kfree(new_entry);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
down_write(&mount_list_lock);
|
||||||
|
|
||||||
|
// disallow dupes
|
||||||
|
// if this gets too many, we can consider moving this whole task to a kthread
|
||||||
|
list_for_each_entry (entry, &mount_list, list) {
|
||||||
|
if (!strcmp(entry->umountable, buf)) {
|
||||||
|
pr_info("cmd_add_try_umount: %s is already here!\n",
|
||||||
|
buf);
|
||||||
|
up_write(&mount_list_lock);
|
||||||
|
kfree(new_entry->umountable);
|
||||||
|
kfree(new_entry);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check flags and add
|
||||||
|
// this also serves as a null check
|
||||||
|
if (cmd.flags)
|
||||||
|
new_entry->flags = cmd.flags;
|
||||||
|
else
|
||||||
|
new_entry->flags = 0;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
list_add(&new_entry->list, &mount_list);
|
||||||
|
up_write(&mount_list_lock);
|
||||||
|
pr_info("cmd_add_try_umount: %s added!\n", buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is just strcmp'd wipe anyway
|
||||||
|
case KSU_UMOUNT_DEL: {
|
||||||
|
long len = strncpy_from_user(buf, (const char __user *)cmd.arg,
|
||||||
|
sizeof(buf) - 1);
|
||||||
|
if (len <= 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
|
||||||
|
down_write(&mount_list_lock);
|
||||||
|
list_for_each_entry_safe (entry, tmp, &mount_list, list) {
|
||||||
|
if (!strcmp(entry->umountable, buf)) {
|
||||||
|
pr_info("cmd_add_try_umount: entry removed: %s\n",
|
||||||
|
entry->umountable);
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry->umountable);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
up_write(&mount_list_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // switch(cmd.mode)
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_nuke_ext4_sysfs(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_nuke_ext4_sysfs_cmd cmd;
|
||||||
|
char mnt[256];
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (!cmd.arg)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
memset(mnt, 0, sizeof(mnt));
|
||||||
|
|
||||||
|
ret = strncpy_from_user(mnt, cmd.arg, sizeof(mnt));
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("nuke ext4 copy mnt failed: %ld\\n", ret);
|
||||||
|
return -EFAULT; // 或者 return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == sizeof(mnt)) {
|
||||||
|
pr_err("nuke ext4 mnt path too long\\n");
|
||||||
|
return -ENAMETOOLONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("do_nuke_ext4_sysfs: %s\n", mnt);
|
||||||
|
|
||||||
|
return nuke_ext4_sysfs(mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 100. GET_FULL_VERSION - Get full version string
|
||||||
|
static int do_get_full_version(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_full_version_cmd cmd = {0};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
|
||||||
|
#else
|
||||||
|
strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_full_version: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 101. HOOK_TYPE - Get hook type
|
||||||
|
static int do_get_hook_type(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_hook_type_cmd cmd = {0};
|
||||||
|
const char *type = "Tracepoint";
|
||||||
|
|
||||||
|
#if defined(CONFIG_KSU_MANUAL_HOOK)
|
||||||
|
type = "Manual";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
|
strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
||||||
|
#else
|
||||||
|
strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_hook_type: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 102. ENABLE_KPM - Check if KPM is enabled
|
||||||
|
static int do_enable_kpm(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_enable_kpm_cmd cmd;
|
||||||
|
|
||||||
|
cmd.enabled = IS_ENABLED(CONFIG_KPM);
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("enable_kpm: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_dynamic_manager(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_dynamic_manager_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("dynamic_manager: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = ksu_handle_dynamic_manager(&cmd.config);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET &&
|
||||||
|
copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("dynamic_manager: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_get_managers(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_get_managers_cmd cmd;
|
||||||
|
|
||||||
|
int ret = ksu_get_active_managers(&cmd.manager_info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("get_managers: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_enable_uid_scanner(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_enable_uid_scanner_cmd cmd;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("enable_uid_scanner: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd.operation) {
|
||||||
|
case UID_SCANNER_OP_GET_STATUS: {
|
||||||
|
bool status = ksu_uid_scanner_enabled;
|
||||||
|
if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) {
|
||||||
|
pr_err("enable_uid_scanner: copy status failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UID_SCANNER_OP_TOGGLE: {
|
||||||
|
bool enabled = cmd.enabled;
|
||||||
|
|
||||||
|
if (enabled == ksu_uid_scanner_enabled) {
|
||||||
|
pr_info("enable_uid_scanner: no need to change, already %s\n",
|
||||||
|
enabled ? "enabled" : "disabled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
// Enable UID scanner
|
||||||
|
int ret = ksu_throne_comm_init();
|
||||||
|
if (ret != 0) {
|
||||||
|
pr_err("enable_uid_scanner: failed to initialize: %d\n", ret);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
pr_info("enable_uid_scanner: enabled\n");
|
||||||
|
} else {
|
||||||
|
// Disable UID scanner
|
||||||
|
ksu_throne_comm_exit();
|
||||||
|
pr_info("enable_uid_scanner: disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_uid_scanner_enabled = enabled;
|
||||||
|
ksu_throne_comm_save_state();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UID_SCANNER_OP_CLEAR_ENV: {
|
||||||
|
// Clear environment (force exit)
|
||||||
|
ksu_throne_comm_exit();
|
||||||
|
ksu_uid_scanner_enabled = false;
|
||||||
|
ksu_throne_comm_save_state();
|
||||||
|
pr_info("enable_uid_scanner: environment cleared\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
pr_err("enable_uid_scanner: invalid operation\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IOCTL handlers mapping table
|
||||||
|
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
||||||
|
{ .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow },
|
||||||
|
{ .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root },
|
||||||
|
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root },
|
||||||
|
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager },
|
||||||
|
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_NUKE_EXT4_SYSFS, .name = "NUKE_EXT4_SYSFS", .handler = do_nuke_ext4_sysfs, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_ADD_TRY_UMOUNT, .name = "ADD_TRY_UMOUNT", .handler = add_try_umount, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
||||||
|
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
||||||
|
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
||||||
|
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root},
|
||||||
|
{ .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root},
|
||||||
|
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root},
|
||||||
|
#ifdef CONFIG_KPM
|
||||||
|
{ .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root},
|
||||||
|
#endif
|
||||||
|
{ .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct ksu_install_fd_tw {
|
||||||
|
struct callback_head cb;
|
||||||
|
int __user *outp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ksu_install_fd_tw_func(struct callback_head *cb)
|
||||||
|
{
|
||||||
|
struct ksu_install_fd_tw *tw =
|
||||||
|
container_of(cb, struct ksu_install_fd_tw, cb);
|
||||||
|
int fd = ksu_install_fd();
|
||||||
|
pr_info("[%d] install ksu fd: %d\n", current->pid, fd);
|
||||||
|
|
||||||
|
if (copy_to_user(tw->outp, &fd, sizeof(fd))) {
|
||||||
|
pr_err("install ksu fd reply err\n");
|
||||||
|
do_close_fd(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int magic1 = (int)PT_REGS_PARM1(real_regs);
|
||||||
|
int magic2 = (int)PT_REGS_PARM2(real_regs);
|
||||||
|
unsigned long arg4;
|
||||||
|
|
||||||
|
// Check if this is a request to install KSU fd
|
||||||
|
if (magic1 == KSU_INSTALL_MAGIC1 && magic2 == KSU_INSTALL_MAGIC2) {
|
||||||
|
struct ksu_install_fd_tw *tw;
|
||||||
|
|
||||||
|
arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
||||||
|
|
||||||
|
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
|
if (!tw)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tw->outp = (int __user *)arg4;
|
||||||
|
tw->cb.func = ksu_install_fd_tw_func;
|
||||||
|
|
||||||
|
if (task_work_add(current, &tw->cb, TWA_RESUME)) {
|
||||||
|
kfree(tw);
|
||||||
|
pr_warn("install fd add task_work failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kprobe reboot_kp = {
|
||||||
|
.symbol_name = REBOOT_SYMBOL,
|
||||||
|
.pre_handler = reboot_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd,
|
||||||
|
void __user **arg)
|
||||||
|
{
|
||||||
|
if (magic1 != KSU_INSTALL_MAGIC1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1,
|
||||||
|
magic2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if this is a request to install KSU fd
|
||||||
|
if (magic2 == KSU_INSTALL_MAGIC2) {
|
||||||
|
int fd = ksu_install_fd();
|
||||||
|
// downstream: dereference all arg usage!
|
||||||
|
if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) {
|
||||||
|
pr_err("install ksu fd reply err\n");
|
||||||
|
do_close_fd(fd);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ksu_supercalls_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pr_info("KernelSU IOCTL Commands:\n");
|
||||||
|
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||||
|
pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name,
|
||||||
|
ksu_ioctl_handlers[i].cmd);
|
||||||
|
}
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
int rc = register_kprobe(&reboot_kp);
|
||||||
|
if (rc) {
|
||||||
|
pr_err("reboot kprobe failed: %d\n", rc);
|
||||||
|
} else {
|
||||||
|
pr_info("reboot kprobe registered successfully\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_supercalls_exit(void)
|
||||||
|
{
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
unregister_kprobe(&reboot_kp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// IOCTL dispatcher
|
||||||
|
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *argp = (void __user *)arg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||||
|
if (cmd == ksu_ioctl_handlers[i].cmd) {
|
||||||
|
// Check permission first
|
||||||
|
if (ksu_ioctl_handlers[i].perm_check &&
|
||||||
|
!ksu_ioctl_handlers[i].perm_check()) {
|
||||||
|
pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n",
|
||||||
|
cmd, current_uid().val);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
// Execute handler
|
||||||
|
return ksu_ioctl_handlers[i].handler(argp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
|
||||||
|
return -ENOTTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File release handler
|
||||||
|
static int anon_ksu_release(struct inode *inode, struct file *filp)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("ksu fd released\n");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File operations structure
|
||||||
|
static const struct file_operations anon_ksu_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.unlocked_ioctl = anon_ksu_ioctl,
|
||||||
|
.compat_ioctl = anon_ksu_ioctl,
|
||||||
|
.release = anon_ksu_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Install KSU fd to current process
|
||||||
|
int ksu_install_fd(void)
|
||||||
|
{
|
||||||
|
struct file *filp;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
// Get unused fd
|
||||||
|
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
pr_err("ksu_install_fd: failed to get unused fd\n");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create anonymous inode file
|
||||||
|
filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL,
|
||||||
|
O_RDWR | O_CLOEXEC);
|
||||||
|
if (IS_ERR(filp)) {
|
||||||
|
pr_err("ksu_install_fd: failed to create anon inode file\n");
|
||||||
|
put_unused_fd(fd);
|
||||||
|
return PTR_ERR(filp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install fd
|
||||||
|
fd_install(fd, filp);
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm,
|
||||||
|
current->pid);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
182
kernel/supercalls.h
Normal file
182
kernel/supercalls.h
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#ifndef __KSU_H_SUPERCALLS
|
||||||
|
#define __KSU_H_SUPERCALLS
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ioctl.h>
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPM
|
||||||
|
#include "kpm/kpm.h"
|
||||||
|
#endif
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
// 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_nuke_ext4_sysfs_cmd {
|
||||||
|
__aligned_u64 arg; // Input: mnt pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
};
|
||||||
|
|
||||||
|
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_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
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// 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
|
||||||
381
kernel/syscall_hook_manager.c
Normal file
381
kernel/syscall_hook_manager.c
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
#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/ptrace.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <trace/events/syscalls.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"
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
static inline bool check_syscall_fastpath(int nr)
|
||||||
|
{
|
||||||
|
switch (nr) {
|
||||||
|
case __NR_newfstatat:
|
||||||
|
case __NR_faccessat:
|
||||||
|
case __NR_execve:
|
||||||
|
case __NR_setresuid:
|
||||||
|
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];
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))) {
|
||||||
|
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(
|
||||||
|
NULL, filename_user, NULL,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_init called\n");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_setuid_hook_init();
|
||||||
|
ksu_sucompat_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_exit called\n");
|
||||||
|
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
tracepoint_synchronize_unregister();
|
||||||
|
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
|
||||||
|
|
||||||
|
destroy_kretprobe(&syscall_regfunc_rp);
|
||||||
|
destroy_kretprobe(&syscall_unregfunc_rp);
|
||||||
|
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
ksu_setuid_hook_exit();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_init(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: initializing..\n");
|
||||||
|
ksu_setuid_hook_init();
|
||||||
|
ksu_sucompat_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: exiting..\n");
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
ksu_setuid_hook_exit();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
85
kernel/syscall_hook_manager.h
Normal file
85
kernel/syscall_hook_manager.h
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#ifndef __KSU_H_HOOK_MANAGER
|
||||||
|
#define __KSU_H_HOOK_MANAGER
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
// Hook manager initialization and cleanup
|
||||||
|
void ksu_syscall_hook_manager_init(void);
|
||||||
|
void ksu_syscall_hook_manager_exit(void);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
// 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);
|
||||||
|
#else
|
||||||
|
// Process marking for tracepoint
|
||||||
|
static inline void ksu_mark_all_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_unmark_all_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_mark_running_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-task mark operations
|
||||||
|
static inline int ksu_get_task_mark(pid_t pid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ksu_set_task_mark(pid_t pid, bool mark)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
215
kernel/throne_comm.c
Normal file
215
kernel/throne_comm.c
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
#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 workqueue_struct *scanner_wq = NULL;
|
||||||
|
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
|
||||||
|
static bool need_rescan = false;
|
||||||
|
|
||||||
|
static void rescan_work_fn(struct work_struct *work)
|
||||||
|
{
|
||||||
|
// Signal userspace through proc interface
|
||||||
|
need_rescan = true;
|
||||||
|
pr_info("requested userspace uid rescan\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_request_userspace_scan(void)
|
||||||
|
{
|
||||||
|
if (scanner_wq) {
|
||||||
|
queue_work(scanner_wq, &scan_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_handle_userspace_update(void)
|
||||||
|
{
|
||||||
|
// Called when userspace notifies update complete
|
||||||
|
need_rescan = false;
|
||||||
|
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 = ksu_filp_open_compat(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 (ksu_kernel_write_compat(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 = ksu_filp_open_compat(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 = ksu_kernel_read_compat(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)
|
||||||
|
{
|
||||||
|
if (need_rescan) {
|
||||||
|
seq_puts(m, "RESCAN\n");
|
||||||
|
} else {
|
||||||
|
seq_puts(m, "OK\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, uid_scanner_show, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||||
|
size_t count, loff_t *pos)
|
||||||
|
{
|
||||||
|
char cmd[16];
|
||||||
|
|
||||||
|
if (count >= sizeof(cmd))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(cmd, buffer, count))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
cmd[count] = '\0';
|
||||||
|
|
||||||
|
// Remove newline if present
|
||||||
|
if (count > 0 && cmd[count-1] == '\n')
|
||||||
|
cmd[count-1] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(cmd, "UPDATED") == 0) {
|
||||||
|
ksu_handle_userspace_update();
|
||||||
|
pr_info("received userspace update notification\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
||||||
|
static const struct proc_ops uid_scanner_proc_ops = {
|
||||||
|
.proc_open = uid_scanner_open,
|
||||||
|
.proc_read = seq_read,
|
||||||
|
.proc_write = uid_scanner_write,
|
||||||
|
.proc_lseek = seq_lseek,
|
||||||
|
.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)
|
||||||
|
{
|
||||||
|
// Create workqueue
|
||||||
|
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||||
|
if (!scanner_wq) {
|
||||||
|
pr_err("failed to create scanner workqueue\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&scan_work, rescan_work_fn);
|
||||||
|
|
||||||
|
// Create proc entry
|
||||||
|
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||||
|
if (!proc_entry) {
|
||||||
|
pr_err("failed to create proc entry\n");
|
||||||
|
destroy_workqueue(scanner_wq);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("throne communication initialized\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_comm_exit(void)
|
||||||
|
{
|
||||||
|
if (proc_entry) {
|
||||||
|
proc_remove(proc_entry);
|
||||||
|
proc_entry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scanner_wq) {
|
||||||
|
destroy_workqueue(scanner_wq);
|
||||||
|
scanner_wq = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
22
kernel/throne_comm.h
Normal file
22
kernel/throne_comm.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef __KSU_H_THRONE_COMM
|
||||||
|
#define __KSU_H_THRONE_COMM
|
||||||
|
|
||||||
|
void ksu_request_userspace_scan(void);
|
||||||
|
|
||||||
|
void ksu_handle_userspace_update(void);
|
||||||
|
|
||||||
|
int ksu_throne_comm_init(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
|
||||||
@@ -5,17 +5,23 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
#include "kernel_compat.h"
|
#include "apk_sign.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
#include "throne_comm.h"
|
||||||
|
|
||||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||||
|
static uid_t locked_manager_uid = KSU_INVALID_UID;
|
||||||
|
static uid_t locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||||
|
|
||||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list"
|
||||||
|
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
|
||||||
|
|
||||||
struct uid_data {
|
struct uid_data {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
@@ -23,6 +29,77 @@ struct uid_data {
|
|||||||
char package[KSU_MAX_PACKAGE_NAME];
|
char package[KSU_MAX_PACKAGE_NAME];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Try read /data/misc/user_uid/uid_list
|
||||||
|
static int uid_from_um_list(struct list_head *uid_list)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
char *buf = NULL;
|
||||||
|
loff_t size, pos = 0;
|
||||||
|
ssize_t nr;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
fp = filp_open(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp))
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
size = fp->f_inode->i_size;
|
||||||
|
if (size <= 0) {
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = kzalloc(size + 1, GFP_ATOMIC);
|
||||||
|
if (!buf) {
|
||||||
|
pr_err("uid_list: OOM %lld B\n", size);
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr = kernel_read(fp, buf, size, &pos);
|
||||||
|
filp_close(fp, NULL);
|
||||||
|
if (nr != size) {
|
||||||
|
pr_err("uid_list: short read %zd/%lld\n", nr, size);
|
||||||
|
kfree(buf);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
for (char *line = buf, *next; line; line = next) {
|
||||||
|
next = strchr(line, '\n');
|
||||||
|
if (next) *next++ = '\0';
|
||||||
|
|
||||||
|
while (*line == ' ' || *line == '\t' || *line == '\r') ++line;
|
||||||
|
if (!*line) continue;
|
||||||
|
|
||||||
|
char *uid_str = strsep(&line, " \t");
|
||||||
|
char *pkg = line;
|
||||||
|
if (!pkg) continue;
|
||||||
|
while (*pkg == ' ' || *pkg == '\t') ++pkg;
|
||||||
|
if (!*pkg) continue;
|
||||||
|
|
||||||
|
u32 uid;
|
||||||
|
if (kstrtou32(uid_str, 10, &uid)) {
|
||||||
|
pr_warn_once("uid_list: bad uid <%s>\n", uid_str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC);
|
||||||
|
if (unlikely(!d)) {
|
||||||
|
pr_err("uid_list: OOM uid=%u\n", uid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->uid = uid;
|
||||||
|
strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME);
|
||||||
|
list_add_tail(&d->list, uid_list);
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
pr_info("uid_list: loaded %d entries\n", cnt);
|
||||||
|
return cnt > 0 ? 0 : -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||||
{
|
{
|
||||||
int len = strlen(path);
|
int len = strlen(path);
|
||||||
@@ -62,7 +139,7 @@ static int get_pkg_from_apk_path(char *pkg, const char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void crown_manager(const char *apk, struct list_head *uid_data)
|
static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index)
|
||||||
{
|
{
|
||||||
char pkg[KSU_MAX_PACKAGE_NAME];
|
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||||
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||||
@@ -70,23 +147,52 @@ static void crown_manager(const char *apk, struct list_head *uid_data)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("manager pkg: %s\n", pkg);
|
pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index);
|
||||||
|
|
||||||
#ifdef KSU_MANAGER_PACKAGE
|
#ifdef KSU_MANAGER_PACKAGE
|
||||||
// pkg is `/<real package>`
|
// pkg is `/<real package>`
|
||||||
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
|
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
|
||||||
pr_info("manager package is inconsistent with kernel build: %s\n",
|
pr_info("manager package is inconsistent with kernel build: %s\n",
|
||||||
KSU_MANAGER_PACKAGE);
|
KSU_MANAGER_PACKAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
struct list_head *list = (struct list_head *)uid_data;
|
|
||||||
struct uid_data *np;
|
struct uid_data *np;
|
||||||
|
|
||||||
list_for_each_entry (np, list, list) {
|
list_for_each_entry(np, uid_data, list) {
|
||||||
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2);
|
||||||
ksu_set_manager_uid(np->uid);
|
|
||||||
|
if (is_dynamic) {
|
||||||
|
if (locked_dynamic_manager_uid != KSU_INVALID_UID && locked_dynamic_manager_uid != np->uid) {
|
||||||
|
pr_info("Unlocking previous dynamic manager UID: %d\n", locked_dynamic_manager_uid);
|
||||||
|
ksu_remove_manager(locked_dynamic_manager_uid);
|
||||||
|
locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) {
|
||||||
|
pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid);
|
||||||
|
ksu_invalidate_manager_uid(); // unlock old one
|
||||||
|
locked_manager_uid = KSU_INVALID_UID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n",
|
||||||
|
is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index);
|
||||||
|
|
||||||
|
if (is_dynamic) {
|
||||||
|
ksu_add_manager(np->uid, signature_index);
|
||||||
|
locked_dynamic_manager_uid = np->uid;
|
||||||
|
|
||||||
|
// If there is no traditional manager, set it to the current UID
|
||||||
|
if (!ksu_is_manager_uid_valid()) {
|
||||||
|
ksu_set_manager_uid(np->uid);
|
||||||
|
locked_manager_uid = np->uid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ksu_set_manager_uid(np->uid); // throne new UID
|
||||||
|
locked_manager_uid = np->uid; // store locked UID
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +212,7 @@ struct apk_path_hash {
|
|||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list);
|
static struct list_head apk_path_hash_list;
|
||||||
|
|
||||||
struct my_dir_context {
|
struct my_dir_context {
|
||||||
struct dir_context ctx;
|
struct dir_context ctx;
|
||||||
@@ -127,10 +233,9 @@ struct my_dir_context {
|
|||||||
#define FILLDIR_ACTOR_CONTINUE 0
|
#define FILLDIR_ACTOR_CONTINUE 0
|
||||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
int namelen, loff_t off, u64 ino,
|
int namelen, loff_t off, u64 ino,
|
||||||
unsigned int d_type)
|
unsigned int d_type)
|
||||||
{
|
{
|
||||||
struct my_dir_context *my_ctx =
|
struct my_dir_context *my_ctx =
|
||||||
container_of(ctx, struct my_dir_context, ctx);
|
container_of(ctx, struct my_dir_context, ctx);
|
||||||
@@ -149,22 +254,21 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||||
|
|
||||||
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
||||||
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
||||||
pr_info("Skipping directory: %.*s\n", namelen, name);
|
pr_info("Skipping directory: %.*s\n", namelen, name);
|
||||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||||
namelen, name) >= DATA_PATH_LEN) {
|
namelen, name) >= DATA_PATH_LEN) {
|
||||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||||
name);
|
name);
|
||||||
return FILLDIR_ACTOR_CONTINUE;
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||||
(my_ctx->stop && !*my_ctx->stop)) {
|
(my_ctx->stop && !*my_ctx->stop)) {
|
||||||
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
pr_err("Failed to allocate memory for %s\n", dirpath);
|
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||||
@@ -175,9 +279,14 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
data->depth = my_ctx->depth - 1;
|
data->depth = my_ctx->depth - 1;
|
||||||
list_add_tail(&data->list, my_ctx->data_path_list);
|
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||||
} else {
|
} else {
|
||||||
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
if ((namelen == 8) &&
|
||||||
struct apk_path_hash *pos, *n;
|
(strncmp(name, "base.apk", namelen) == 0)) {
|
||||||
|
struct apk_path_hash *pos;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||||
|
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||||
|
#else
|
||||||
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||||
|
#endif
|
||||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||||
if (hash == pos->hash) {
|
if (hash == pos->hash) {
|
||||||
pos->exists = true;
|
pos->exists = true;
|
||||||
@@ -185,23 +294,19 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_manager = is_manager_apk(dirpath);
|
int signature_index = -1;
|
||||||
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
bool is_multi_manager = is_dynamic_manager_apk(
|
||||||
dirpath, is_manager);
|
dirpath, &signature_index);
|
||||||
if (is_manager) {
|
|
||||||
crown_manager(dirpath, my_ctx->private_data);
|
|
||||||
*my_ctx->stop = 1;
|
|
||||||
|
|
||||||
// Manager found, clear APK cache list
|
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
|
||||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
dirpath, is_multi_manager, signature_index);
|
||||||
list_del(&pos->list);
|
|
||||||
kfree(pos);
|
// Check for dynamic sign or multi-manager signatures
|
||||||
}
|
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
||||||
} else {
|
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
} else if (is_manager_apk(dirpath)) {
|
||||||
apk_data->hash = hash;
|
crown_manager(dirpath, my_ctx->private_data, 0);
|
||||||
apk_data->exists = true;
|
*my_ctx->stop = 1;
|
||||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,15 +314,17 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
return FILLDIR_ACTOR_CONTINUE;
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
static void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||||
{
|
{
|
||||||
int i, stop = 0;
|
int i, stop = 0;
|
||||||
struct list_head data_path_list;
|
struct list_head data_path_list;
|
||||||
INIT_LIST_HEAD(&data_path_list);
|
INIT_LIST_HEAD(&data_path_list);
|
||||||
|
INIT_LIST_HEAD(&apk_path_hash_list);
|
||||||
|
unsigned long data_app_magic = 0;
|
||||||
|
|
||||||
// Initialize APK cache list
|
// Initialize APK cache list
|
||||||
struct apk_path_hash *pos, *n;
|
struct apk_path_hash *pos, *n;
|
||||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
list_for_each_entry (pos, &apk_path_hash_list, list) {
|
||||||
pos->exists = false;
|
pos->exists = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,38 +337,58 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
for (i = depth; i >= 0; i--) {
|
for (i = depth; i >= 0; i--) {
|
||||||
struct data_path *pos, *n;
|
struct data_path *pos, *n;
|
||||||
|
|
||||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
list_for_each_entry_safe (pos, n, &data_path_list, list) {
|
||||||
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||||
.data_path_list = &data_path_list,
|
.data_path_list = &data_path_list,
|
||||||
.parent_dir = pos->dirpath,
|
.parent_dir = pos->dirpath,
|
||||||
.private_data = uid_data,
|
.private_data = uid_data,
|
||||||
.depth = pos->depth,
|
.depth = pos->depth,
|
||||||
.stop = &stop };
|
.stop = &stop };
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
|
||||||
if (!stop) {
|
if (!stop) {
|
||||||
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
file = filp_open(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||||
if (IS_ERR(file)) {
|
if (IS_ERR(file)) {
|
||||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
pr_err("Failed to open directory: %s, err: %ld\n",
|
||||||
|
pos->dirpath, PTR_ERR(file));
|
||||||
|
goto skip_iterate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab magic on first folder, which is /data/app
|
||||||
|
if (!data_app_magic) {
|
||||||
|
if (file->f_inode->i_sb->s_magic) {
|
||||||
|
data_app_magic = file->f_inode->i_sb->s_magic;
|
||||||
|
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__,
|
||||||
|
pos->dirpath, data_app_magic);
|
||||||
|
} else {
|
||||||
|
filp_close(file, NULL);
|
||||||
|
goto skip_iterate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
||||||
|
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n",
|
||||||
|
__func__, pos->dirpath,
|
||||||
|
file->f_inode->i_sb->s_magic, data_app_magic);
|
||||||
|
filp_close(file, NULL);
|
||||||
goto skip_iterate;
|
goto skip_iterate;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterate_dir(file, &ctx.ctx);
|
iterate_dir(file, &ctx.ctx);
|
||||||
filp_close(file, NULL);
|
filp_close(file, NULL);
|
||||||
}
|
}
|
||||||
skip_iterate:
|
skip_iterate:
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
if (pos != &data)
|
if (pos != &data)
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove stale cached APK entries
|
// clear apk_path_hash_list unconditionally
|
||||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
pr_info("search manager: cleanup!\n");
|
||||||
if (!pos->exists) {
|
list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) {
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +400,7 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
|||||||
bool exist = false;
|
bool exist = false;
|
||||||
list_for_each_entry (np, list, list) {
|
list_for_each_entry (np, list, list) {
|
||||||
if (np->uid == uid % 100000 &&
|
if (np->uid == uid % 100000 &&
|
||||||
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
exist = true;
|
exist = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -281,105 +408,146 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
|||||||
return exist;
|
return exist;
|
||||||
}
|
}
|
||||||
|
|
||||||
void track_throne()
|
void track_throne(bool prune_only)
|
||||||
{
|
{
|
||||||
struct file *fp =
|
|
||||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
|
||||||
if (IS_ERR(fp)) {
|
|
||||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
|
||||||
__func__, PTR_ERR(fp));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct list_head uid_list;
|
struct list_head uid_list;
|
||||||
INIT_LIST_HEAD(&uid_list);
|
struct uid_data *np, *n;
|
||||||
|
struct file *fp;
|
||||||
char chr = 0;
|
char chr = 0;
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
loff_t line_start = 0;
|
loff_t line_start = 0;
|
||||||
char buf[KSU_MAX_PACKAGE_NAME];
|
char buf[KSU_MAX_PACKAGE_NAME];
|
||||||
for (;;) {
|
static bool manager_exist = false;
|
||||||
ssize_t count =
|
static bool dynamic_manager_exist = false;
|
||||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
int current_manager_uid = ksu_get_manager_uid() % 100000;
|
||||||
if (count != sizeof(chr))
|
|
||||||
break;
|
|
||||||
if (chr != '\n')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
// init uid list head
|
||||||
&line_start);
|
INIT_LIST_HEAD(&uid_list);
|
||||||
|
|
||||||
struct uid_data *data =
|
if (ksu_uid_scanner_enabled) {
|
||||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH);
|
||||||
if (!data) {
|
|
||||||
filp_close(fp, 0);
|
if (uid_from_um_list(&uid_list) == 0) {
|
||||||
goto out;
|
pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH);
|
||||||
|
goto uid_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *tmp = buf;
|
pr_warn("%s read failed, fallback to %s\n",
|
||||||
const char *delim = " ";
|
KSU_UID_LIST_PATH, SYSTEM_PACKAGES_LIST_PATH);
|
||||||
char *package = strsep(&tmp, delim);
|
|
||||||
char *uid = strsep(&tmp, delim);
|
|
||||||
if (!uid || !package) {
|
|
||||||
pr_err("update_uid: package or uid is NULL!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 res;
|
|
||||||
if (kstrtou32(uid, 10, &res)) {
|
|
||||||
pr_err("update_uid: uid parse err\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data->uid = res;
|
|
||||||
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
|
||||||
list_add_tail(&data->list, &uid_list);
|
|
||||||
// reset line start
|
|
||||||
line_start = pos;
|
|
||||||
}
|
}
|
||||||
filp_close(fp, 0);
|
|
||||||
|
|
||||||
// now update uid list
|
{
|
||||||
struct uid_data *np;
|
fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||||
struct uid_data *n;
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ssize_t count =
|
||||||
|
kernel_read(fp, &chr, sizeof(chr), &pos);
|
||||||
|
if (count != sizeof(chr))
|
||||||
|
break;
|
||||||
|
if (chr != '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count = kernel_read(fp, buf, sizeof(buf),
|
||||||
|
&line_start);
|
||||||
|
struct uid_data *data =
|
||||||
|
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||||
|
if (!data) {
|
||||||
|
filp_close(fp, 0);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmp = buf;
|
||||||
|
const char *delim = " ";
|
||||||
|
char *package = strsep(&tmp, delim);
|
||||||
|
char *uid = strsep(&tmp, delim);
|
||||||
|
if (!uid || !package) {
|
||||||
|
pr_err("update_uid: package or uid is NULL!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 res;
|
||||||
|
if (kstrtou32(uid, 10, &res)) {
|
||||||
|
pr_err("update_uid: uid parse err\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data->uid = res;
|
||||||
|
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
||||||
|
list_add_tail(&data->list, &uid_list);
|
||||||
|
// reset line start
|
||||||
|
line_start = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_ready:
|
||||||
|
if (prune_only)
|
||||||
|
goto prune;
|
||||||
|
|
||||||
// first, check if manager_uid exist!
|
// first, check if manager_uid exist!
|
||||||
bool manager_exist = false;
|
list_for_each_entry(np, &uid_list, list) {
|
||||||
list_for_each_entry (np, &uid_list, list) {
|
if (np->uid == current_manager_uid) {
|
||||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
|
||||||
// don't delete it in this case!
|
|
||||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
|
||||||
if (np->uid == manager_uid) {
|
|
||||||
manager_exist = true;
|
manager_exist = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manager_exist) {
|
if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) {
|
||||||
if (ksu_is_manager_uid_valid()) {
|
pr_info("Manager APK removed, unlock previous UID: %d\n",
|
||||||
pr_info("manager is uninstalled, invalidate it!\n");
|
locked_manager_uid);
|
||||||
ksu_invalidate_manager_uid();
|
ksu_invalidate_manager_uid();
|
||||||
}
|
locked_manager_uid = KSU_INVALID_UID;
|
||||||
pr_info("Searching manager...\n");
|
|
||||||
search_manager("/data/app", 2, &uid_list);
|
|
||||||
pr_info("Search manager finished\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the Dynamic Manager exists (only check locked UIDs)
|
||||||
|
if (ksu_is_dynamic_manager_enabled() &&
|
||||||
|
locked_dynamic_manager_uid != KSU_INVALID_UID) {
|
||||||
|
list_for_each_entry(np, &uid_list, list) {
|
||||||
|
if (np->uid == locked_dynamic_manager_uid) {
|
||||||
|
dynamic_manager_exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dynamic_manager_exist) {
|
||||||
|
pr_info("Dynamic manager APK removed, unlock previous UID: %d\n",
|
||||||
|
locked_dynamic_manager_uid);
|
||||||
|
ksu_remove_manager(locked_dynamic_manager_uid);
|
||||||
|
locked_dynamic_manager_uid = KSU_INVALID_UID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_search = !manager_exist;
|
||||||
|
if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist)
|
||||||
|
need_search = true;
|
||||||
|
|
||||||
|
if (need_search) {
|
||||||
|
pr_info("Searching for manager(s)...\n");
|
||||||
|
search_manager("/data/app", 2, &uid_list);
|
||||||
|
pr_info("Manager search finished\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
prune:
|
||||||
// then prune the allowlist
|
// then prune the allowlist
|
||||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||||
out:
|
out:
|
||||||
// free uid_list
|
// free uid_list
|
||||||
list_for_each_entry_safe (np, n, &uid_list, list) {
|
list_for_each_entry_safe(np, n, &uid_list, list) {
|
||||||
list_del(&np->list);
|
list_del(&np->list);
|
||||||
kfree(np);
|
kfree(np);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_throne_tracker_init()
|
void ksu_throne_tracker_init(void)
|
||||||
{
|
{
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_throne_tracker_exit()
|
void ksu_throne_tracker_exit(void)
|
||||||
{
|
{
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#ifndef __KSU_H_UID_OBSERVER
|
#ifndef __KSU_H_THRONE_TRACKER
|
||||||
#define __KSU_H_UID_OBSERVER
|
#define __KSU_H_THRONE_TRACKER
|
||||||
|
|
||||||
void ksu_throne_tracker_init();
|
void ksu_throne_tracker_init(void);
|
||||||
|
|
||||||
void ksu_throne_tracker_exit();
|
void ksu_throne_tracker_exit(void);
|
||||||
|
|
||||||
void track_throne();
|
void track_throne(bool prune_only);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
10
manager/.gitignore
vendored
10
manager/.gitignore
vendored
@@ -1,10 +0,0 @@
|
|||||||
*.iml
|
|
||||||
.gradle
|
|
||||||
.idea
|
|
||||||
.kotlin
|
|
||||||
.DS_Store
|
|
||||||
build
|
|
||||||
captures
|
|
||||||
.cxx
|
|
||||||
local.properties
|
|
||||||
key.jks
|
|
||||||
2
manager/app/.gitignore
vendored
2
manager/app/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
/build
|
|
||||||
/release/
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
@file:Suppress("UnstableApiUsage")
|
|
||||||
|
|
||||||
import com.android.build.api.dsl.ApkSigningConfig
|
|
||||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
|
||||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.agp.app)
|
|
||||||
alias(libs.plugins.kotlin)
|
|
||||||
alias(libs.plugins.compose.compiler)
|
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.lsplugin.apksign)
|
|
||||||
id("kotlin-parcelize")
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
val managerVersionCode: Int by rootProject.extra
|
|
||||||
val managerVersionName: String by rootProject.extra
|
|
||||||
|
|
||||||
apksign {
|
|
||||||
storeFileProperty = "KEYSTORE_FILE"
|
|
||||||
storePasswordProperty = "KEYSTORE_PASSWORD"
|
|
||||||
keyAliasProperty = "KEY_ALIAS"
|
|
||||||
keyPasswordProperty = "KEY_PASSWORD"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
android {
|
|
||||||
|
|
||||||
/**signingConfigs {
|
|
||||||
create("Debug") {
|
|
||||||
storeFile = file("D:\\other\\AndroidTool\\android_key\\keystore\\release-key.keystore")
|
|
||||||
storePassword = ""
|
|
||||||
keyAlias = ""
|
|
||||||
keyPassword = ""
|
|
||||||
}
|
|
||||||
}**/
|
|
||||||
namespace = "com.sukisu.ultra"
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = true
|
|
||||||
isShrinkResources = true
|
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
||||||
}
|
|
||||||
/**debug {
|
|
||||||
signingConfig = signingConfigs.named("Debug").get() as ApkSigningConfig
|
|
||||||
}**/
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
aidl = true
|
|
||||||
buildConfig = true
|
|
||||||
compose = true
|
|
||||||
prefab = true
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "21"
|
|
||||||
}
|
|
||||||
|
|
||||||
packaging {
|
|
||||||
jniLibs {
|
|
||||||
useLegacyPackaging = true
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
// https://stackoverflow.com/a/58956288
|
|
||||||
// It will break Layout Inspector, but it's unused for release build.
|
|
||||||
excludes += "META-INF/*.version"
|
|
||||||
// https://github.com/Kotlin/kotlinx.coroutines?tab=readme-ov-file#avoiding-including-the-debug-infrastructure-in-the-resulting-apk
|
|
||||||
excludes += "DebugProbesKt.bin"
|
|
||||||
// https://issueantenna.com/repo/kotlin/kotlinx.coroutines/issues/3158
|
|
||||||
excludes += "kotlin-tooling-metadata.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
path("src/main/cpp/CMakeLists.txt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationVariants.all {
|
|
||||||
outputs.forEach {
|
|
||||||
val output = it as BaseVariantOutputImpl
|
|
||||||
output.outputFileName = "SukiSU_${managerVersionName}_${managerVersionCode}-$name.apk"
|
|
||||||
}
|
|
||||||
kotlin.sourceSets {
|
|
||||||
getByName(name) {
|
|
||||||
kotlin.srcDir("build/generated/ksp/$name/kotlin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/77745844
|
|
||||||
tasks.withType<PackageAndroidArtifact> {
|
|
||||||
doFirst { appMetadata.asFile.orNull?.writeText("") }
|
|
||||||
}
|
|
||||||
|
|
||||||
dependenciesInfo {
|
|
||||||
includeInApk = false
|
|
||||||
includeInBundle = false
|
|
||||||
}
|
|
||||||
|
|
||||||
androidResources {
|
|
||||||
generateLocaleConfig = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.androidx.activity.compose)
|
|
||||||
implementation(libs.androidx.navigation.compose)
|
|
||||||
|
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
|
||||||
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.tooling.preview)
|
|
||||||
implementation(libs.androidx.foundation)
|
|
||||||
implementation(libs.androidx.documentfile)
|
|
||||||
|
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
|
||||||
|
|
||||||
implementation(libs.compose.destinations.core)
|
|
||||||
ksp(libs.compose.destinations.ksp)
|
|
||||||
|
|
||||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
|
||||||
implementation(libs.com.github.topjohnwu.libsu.service)
|
|
||||||
implementation(libs.com.github.topjohnwu.libsu.io)
|
|
||||||
|
|
||||||
implementation(libs.dev.rikka.rikkax.parcelablelist)
|
|
||||||
|
|
||||||
implementation(libs.io.coil.kt.coil.compose)
|
|
||||||
|
|
||||||
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.androidx.webkit)
|
|
||||||
|
|
||||||
implementation(libs.lsposed.cxx)
|
|
||||||
|
|
||||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
|
||||||
|
|
||||||
implementation(libs.mmrl.platform)
|
|
||||||
compileOnly(libs.mmrl.hidden.api)
|
|
||||||
implementation(libs.mmrl.webui)
|
|
||||||
implementation(libs.mmrl.ui)
|
|
||||||
|
|
||||||
}
|
|
||||||
47
manager/app/proguard-rules.pro
vendored
47
manager/app/proguard-rules.pro
vendored
@@ -1,47 +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.model.ModId { *; }
|
|
||||||
-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 { *; }
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<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
|
|
||||||
android:name=".KernelSUApplication"
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/Theme.KernelSU"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
tools:targetApi="34">
|
|
||||||
<activity
|
|
||||||
android:name=".ui.MainActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:theme="@style/Theme.KernelSU">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.webui.WebUIActivity"
|
|
||||||
android:autoRemoveFromRecents="true"
|
|
||||||
android:documentLaunchMode="intoExisting"
|
|
||||||
android:exported="false"
|
|
||||||
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
|
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.fileprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/filepaths" />
|
|
||||||
</provider>
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user