Open Mark-Joy opened 1 year ago
Sorry for the late response. Crazy month. bindfs doesn't currently know anything about selinux. Also, I don't currently know much about selinux, and I'm unfortunately way too busy in the foreseeable future to learn :(
The FUSE changelog says "SELinux support" was added in FUSE 2.9.7, and this commit seems to add some relevant options. Hope this helps.
I was adding code in termux-app to detect if legacy app data directory path /data/data/com.termux
is actually accessible for multi user support and was wondering whether someone would be using bindfs
instead of bind
mounts, and viola, someone already is! ;)
On Android version <= 10
, the /data/user/0
is a symlink to /data/data
directory and on Android version >= 11
, the /data/data
directory is bind mounted at /data/user/0
. But bind mount does not exist on secondary users by default, like user 10
. So if someone tried to bindfs
mount /data/user/10/com.termux
on /data/data/com.termux
in secondary user, it would fail the check for whether both paths are for the same file, i.e device (st_dev
) and inode (st_ino
) are same for both paths. Currently, by default, such a case would assume non-accessible, since it would be complex to find if two directories are for the same directory with a fuse mount, since would requiring mount points parsing or creation of random file and checking existence, which may have synchronization delay issues. If using bind
mount or symlink, the st_dev
and st_ino
are same. Also would be same if mounting an image on both paths, mounts are automatically translated with bind
and bindfs
mount done on single path below. I have only tested on Android 13
avd and not with symlink
era.
To answer the OP's query, you can pass -o context=<context>
option to bindfs
to pass contexts, but its currently broken and for termux usage I would recommend using bind mounts. Persistence of context to actual files would be an issue too with such options and so bind
mount should be used instead.
# Install packages
apt update
apt install root-repo
apt install bindfs mount-utils
# Start root shell in global mount namespace
# https://github.com/agnostic-apollo/sudo
sudo --su-run-options=-mm su
# Create 300mb loop image
dd if=/dev/zero of=/data/termux.img bs=1M count=0 seek=300
# Format to ext4
mkfs.ext4 /data/termux.img
# Create mount directory
# Do not try to mount on subdirectory of /data like /data/termux, since
# will result in avc denials to search `/`
# Need to assign `system_data_file` type on parent directory
mkdir -p /data/mount/termux
# Set security context same as /data/data for parent directory of mount directory
# chcon for some reason is only working from root adb shell and not in
# termux even with `setenforce 0`
chcon u:object_r:system_data_file:s0:c512,c768 /data/mount
# Set ownership
chown system:system /data/mount
# Mount termux.img as a loop device
# If using `/system/bin/mount` or its `mount` wrapper provided by termux
# by default with `termux-tools` package, it commadn may fail with
# `losetup: invalid option -- s` (not in root adb shell).
# Install `mount-utils` package to replace wrapper with `coreutils` `mount` binary.
mount -o loop /data/termux.img /data/mount/termux
# Set security context same as /data/data for parent directory of mountpoint/app data directory
# The -h option must be passed, otherwise will result in symlink files to be set to
# `u:object_r:unlabeled:s0` and result in avc denials for lnk_file
# The `c159` is for the user assigned to termux app and must be replaced
# Check `$SE_` env variables or use `/system/bin/ls -ldZ`
# on existing files to get context. The termux provided `ls` will print `?` for security context.
chcon -Rh u:object_r:app_data_file:s0:c159,c256,c512,c768 /data/mount/termux
# Set ownership
# The `10159` is for the user assigned to termux app and must be replaced
# Use `id -u` in termux shell or `$TERMUX_APP__UID` env variable
chown -R 10159:10159 /data/mount/termux
# Set `rwx------` permissions to remove group and others permission
# If different permissions already exist on subdirectories, use
# `find -exec` with `-type` option to fix them. Not required for
# functioning, but better for security while selinux is disabled
chmod 700 /data/mount/termux
# Bind mount loop device mountpoint to app data directory
mount -o bind /data/mount/termux /data/data/com.termux
# Check context
ls -ldZ /data/data/com.termux/files
#drwx------ 5 u0_a159 u0_a159 u:object_r:app_data_file:s0:c159,c256,c512,c768 1024 /data/data/com.termux/files
ls -ldZ /data/data/com.termux/files/usr/bin/sh
#lrwxrwxrwx 1 u0_a159 u0_a159 u:object_r:app_data_file:s0:c159,c256,c512,c768 4 /data/data/com.termux/files/usr/bin/sh -> dash
ls -ldZ /data/data/com.termux/files/usr/bin/dash
#-rwx------ 1 u0_a159 u0_a159 u:object_r:app_data_file:s0:c159,c256,c512,c768 106112 /data/data/com.termux/files/usr/bin/dash
# Check mounts
cat /proc/self/mountinfo | grep termux
#4919 92 7:256 / /data/mount/termux rw,relatime shared:43 - ext4 /dev/block/loop32 rw,seclabel
#4918 92 7:256 / /data/data/com.termux rw,relatime shared:43 - ext4 /dev/block/loop32 rw,seclabel
#4923 99 7:256 / /data_mirror/data_ce/null/0/com.termux rw,relatime shared:43 - ext4 /dev/block/loop32 rw,seclabel
#4920 94 7:256 / /data/user/0/com.termux rw,relatime shared:43 - ext4 /dev/block/loop32 rw,seclabel
# Patch sepolicy temporarily to prevent policy violation and use `deny` again afterwards
# `avc: denied { associate } for scontext=u:object_r:app_data_file:s0:c159 tcontext=u:object_r:app_data_file:s0:c159 tclass=filesystem permissive=0`
# Or use `setenforce 0` temporarily
supolicy --live "allow app_data_file app_data_file filesystem associate"
# Bindfs mount loop device mountpoint to app data directory
# This fails with `fuse: unknown option `c256'` since `bindfs` `1.17.1` uses comma to separate different fuse `-o` options.
bindfs -o nonempty -o 'context=u:object_r:app_data_file:s0:c159,c256,c512,c768' /data/mount/termux /data/data/com.termux
# Mount with the categories removed from `context` since termux app
# process with security context `u:r:untrusted_app_27:s0:c159,c256,c512,c768`
# should still be able to access files without any categories.
# However, even though mount works, the shell doesn't
# avc: denied { read } for name="sh" dev="fuse" ino=16350 scontext=u:r:untrusted_app_27:s0:c159,c256,c512,c768 tcontext=u:object_r:app_data_file:s0 tclass=lnk_file permissive=0 app=com.termux
bindfs -o nonempty -o 'context=u:object_r:app_data_file:s0' /data/mount/termux /data/data/com.termux
# Check context
ls -ldZ /data/data/com.termux/files
#drwxrwx--x 5 u0_a159 u0_a159 u:object_r:app_data_file:s0 1024 /data/data/com.termux/files
ls -ldZ /data/data/com.termux/files/usr/bin/sh
#lrwxrwxrwx 1 u0_a159 u0_a159 u:object_r:app_data_file:s0 4 /data/data/com.termux/files/usr/bin/sh -> dash
ls -ldZ /data/data/com.termux/files/usr/bin/dash
#-rwx------ 1 u0_a159 u0_a159 u:object_r:app_data_file:s0 106112 /data/data/com.termux/files/usr/bin/dash
# Check mounts
cat /proc/self/mountinfo | grep termux
#4919 92 7:256 / /data/mount/termux rw,relatime shared:43 - ext4 /dev/block/loop32 rw,seclabel
#87 92 0:114 / /data/data/com.termux rw,nosuid,nodev,relatime shared:44 - fuse /data/mount/termux rw,context=u:object_r:app_data_file:s0,user_id=0,group_id=0,default_permissions,allow_other
#4920 99 0:114 / /data_mirror/data_ce/null/0/com.termux rw,nosuid,nodev,relatime shared:44 - fuse /data/mount/termux rw,context=u:object_r:app_data_file:s0,user_id=0,group_id=0,default_permissions,allow_other
#4918 94 0:114 / /data/user/0/com.termux rw,nosuid,nodev,relatime shared:44 - fuse /data/mount/termux rw,context=u:object_r:app_data_file:s0,user_id=0,group_id=0,default_permissions,allow_other
I tried patching the sepolicy with supolicy --live "allow untrusted_app_27 app_data_file lnk_file read"
and also for open
, getattr
, setattr
, link
, unlink
and rename
, but didn't work. Disabling selinux policy with setenforce 0
does work. I am not sure why this is happening. Maybe selinux is getting confused for a lnk_file
without the categories or maybe fuse
filesystem itself is broken. Mounting with fscontext
or rootcontext
option does not work either. Mounting with defcontext
fails with fuse: mount failed: Invalid argument
.
The c159,c256,c512,c768
categories are part of Multi-Category Security (MCS)
and required to Isolate the app data from access by another app
and Isolate the app data from one physical user to another
. The c512
is for user 0
(primary user), it gets changed to c522
for user 10
(secondary user).
https://source.android.com/docs/security/features/selinux/concepts#security_contexts
https://selinuxproject.org/page/NB_SEforAndroid_2#Computing_a_Context
@mpartel The bindfs
-o
option will need to be fixed to allow passing commas in context
/fscontext
/defcontext
/rootcontext
args, but likely not easily possible so dedicated options would need to be provided instead. Don't think you will need to do anything else, like learning selinux ;) The lnk_file
issue is likely not related to bindfs
.
On Android through adb, I have created a file and made an ext4 FS. I created a loop device with losetup and I mounted it on "/mnt/pass_through/0/xMySDCard/com.termux". Then I used bindfs to mirror it to "/data/data/com.termux"
Doing
ls -laZ "/mnt/pass_through/0/xMySDCard/com.termux"
gave:Doing
ls -laZ "/data/data/com.termux"
gave:Obviously, the selinux contexts are different.
When I tried with standard "mount -o bind"
I got the correct selinux context