termux / termux-app

Termux - a terminal emulator application for Android OS extendible by variety of packages.
https://f-droid.org/en/packages/com.termux
Other
35.58k stars 3.73k forks source link

Access to Termux private storage directory on External Storage (SD card) stopped working #3041

Open cbonet-sw opened 2 years ago

cbonet-sw commented 2 years ago

Problem description

I used to use the Termux private (app-specific) storage directory on external storage (real SD card) to store files that we larger than I wanted to store in private internal storage ($HOME/) but now I don't seem to have permission to access it any more (and I had files stored in there).

This is under Android 11 on Samsung, and the change occurred within the last month: I wonder if it's related to a recent minor update to the Android OS? It might be intentional rather than a Termux bug.

Specifically, I have access to shares external storage at /storage/3934-6139/ but when I access the app-private folder for user files at /storage/3934-6139/Android/data/com.termux/files I get "ls: cannot open directory '/storage/3934-6139/Android/data/com.termux/files': Permission denied"

I have tried

I think Termux should have access itself to files in subdirs beneath /storage/3934-6139/Android/data/com.termux/ and I wonder if it is just shell commands such as ls (implemented through coreutils) that have a problem and if within code of the app itself access is still possible?

Thanks for any insight!

Steps to reproduce the behavior.

ls -l /storage/3934-6139/Android/data/com.termux/files

I get "ls: cannot open directory '/storage/3934-6139/Android/data/com.termux/files': Permission denied"

(obviously substitute the label of the SD card on your system into the pathname above).

What is the expected behavior?

Should give directory listing.

System information

sylirre commented 2 years ago

Try accessing through Termux storage directory like ls $HOME/storage/external-1/.

cbonet-sw commented 2 years ago

Thanks for the suggestion, but I did try that with the same result. After rebuilding the links by running termux-setup-storage again, I also verified that the symlink it created at $HOME/storage/external-1 pointed to the same directory I had been attempting to access by the direct path under /storage/3934-6139/Android/data/....

agnostic-apollo commented 2 years ago

revoking then granting again the Android file storage permission for the Termux app

Considering you have already done this, could be some other bug or policy change.

Attempt writing and then immediately run adb logcat -d > logcat,txt from pc (or termux with adb wireless) and check for avc or other permission denials near the end for termux.

cbonet-sw commented 1 year ago

I have now had time to investigate this further.

Curiously, in fact I do actually still seem to have the ability to read files located in the Termux Private directory on the External SD card, what is being blocked is actually listing the contents of directories (and sub-directories) using 'ls'. If I know the exact pathname of a file within this private directory, then I can still 'ls' that specific file, and I can still read the contents of that file.

Also, I do now seem to have read access (and can 'ls') files stored within the 'public' directories on the external SD card (eg. 'Documents', 'Download', etc.). This is very unusual, since I recall that when I first experienced the upgrade to Android 11, I lost all access to the public areas of the SD card from within Termux, and the only way I could use the SD card was via Termux's private directory on the SD card. That, of course, complicated accessing files on the SD card from outside the Termux environment, other than through SAF using Termux's DocumentProvider interface.

So, it would appear that a work-around to allow me to continue using the external SD card with Termux at the moment would be to move to using a public subdirectory on the SD card, eg. something like ${EXTERNAL_SD_ROOT_PATH}/Documents/Termux and manually set symlinks in ~/storage/ to point to this. Then I could use adb to move the 'trapped' files currently in Termux's private directory to this new public directory. This would

I will try implementing this as a (temporary) work-around shortly.

I'm going to post more detailed debugging information regarding this issue in separate message(s) in a few moments...

cbonet-sw commented 1 year ago

Doing a "adb logcat -d" after running a "ls -l" within Termux that failed, we find no SELinux warnings; grepping for 'avc' or 'audit' only reveals grants of executing bash:

10-27 09:24:48.334  3622  3622 E audit   : type=1400 audit(1666859088.331:127945): avc:  granted  { execute } for  pid=15666 comm="bash" name="coreutils" dev="mmcblk0p32" ino=280848 scontext=u:r:untrusted_app_27:s0:c173,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c173,c257,c512,c768 tclass=file SEPF_SM-A105FN_11_0010 audit_filtered
10-27 09:24:48.334  3622  3622 E audit   : type=1300 audit(1666859088.331:127945): arch=40000028 syscall=334 per=8 success=yes exit=0 a0=ffffff9c a1=f356b728 a2=1 a3=0 items=0 ppid=15318 pid=15666 auid=4294967295 uid=10429 gid=10429 euid=10429 suid=10429 fsuid=10429 egid=10429 sgid=10429 fsgid=10429 tty=pts1 ses=4294967295 comm="bash" exe="/data/data/com.termux/files/usr/bin/bash" subj=u:r:untrusted_app_27:s0:c173,c257,c512,c768 key=(null)
10-27 09:24:48.334  3622  3622 E audit   : type=1327 audit(1666859088.331:127945): proctitle=2F646174612F646174612F636F6D2E7465726D75782F66696C65732F7573722F62696E2F62617368002D6C
10-27 09:24:48.335  3622  3622 E audit   : type=1400 audit(1666859088.331:127946): avc:  granted  { execute } for  pid=15666 comm="bash" name="coreutils" dev="mmcblk0p32" ino=280848 scontext=u:
cbonet-sw commented 1 year ago

Information on Android permissions:

Permission from Termux App Manifest:

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.DUMP" />
    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" 
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />

Permissions granted from adb dumpsys package com.termux command:

    installPermissionsFixed=true
    pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ]
    declared permissions:
      com.termux.permission.RUN_COMMAND: prot=dangerous, INSTALLED
    requested permissions:
      android.permission.ACCESS_NETWORK_STATE
      android.permission.INTERNET
      android.permission.WRITE_EXTERNAL_STORAGE: restricted=true
      android.permission.WAKE_LOCK
      android.permission.VIBRATE
      android.permission.FOREGROUND_SERVICE
      android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
      android.permission.SYSTEM_ALERT_WINDOW
      android.permission.READ_LOGS
      android.permission.DUMP
      android.permission.WRITE_SECURE_SETTINGS
      android.permission.REQUEST_INSTALL_PACKAGES
      android.permission.PACKAGE_USAGE_STATS
      android.permission.READ_EXTERNAL_STORAGE: restricted=true
      android.permission.ACCESS_MEDIA_LOCATION
    install permissions:
      android.permission.NFC: granted=true
      android.permission.FOREGROUND_SERVICE: granted=true
      android.permission.RECEIVE_BOOT_COMPLETED: granted=true
      android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: granted=true
      android.permission.INTERNET: granted=true
      android.permission.TRANSMIT_IR: granted=true
      android.permission.CHANGE_WIFI_STATE: granted=true
      android.permission.ACCESS_NETWORK_STATE: granted=true
      android.permission.SET_WALLPAPER: granted=true
      android.permission.USE_FINGERPRINT: granted=true
      android.permission.REQUEST_DELETE_PACKAGES: granted=true
      android.permission.VIBRATE: granted=true
      android.permission.ACCESS_WIFI_STATE: granted=true
      android.permission.USE_BIOMETRIC: granted=true
      android.permission.WAKE_LOCK: granted=true
    User 0: ceDataInode=311297 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false
    User 95: ceDataInode=0 installed=false hidden=false suspended=false distractionFlags=0 stopped=true notLaunched=true enabled=0 instant=false virtual=false
    User 150: ceDataInode=0 installed=false hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false

Shared users:
  SharedUser [com.termux] (afb1fa2):
    userId=10429
    Packages
      PackageSetting{45634ff com.termux/10429}
      PackageSetting{8393b4e com.termux.api/10429}
      PackageSetting{feea228 com.termux.widget/10429}
    install permissions:
      android.permission.NFC: granted=true
      android.permission.FOREGROUND_SERVICE: granted=true
      android.permission.RECEIVE_BOOT_COMPLETED: granted=true
      android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: granted=true
      android.permission.INTERNET: granted=true
      android.permission.TRANSMIT_IR: granted=true
      android.permission.CHANGE_WIFI_STATE: granted=true
      android.permission.ACCESS_NETWORK_STATE: granted=true
      android.permission.SET_WALLPAPER: granted=true
      android.permission.USE_FINGERPRINT: granted=true
      android.permission.REQUEST_DELETE_PACKAGES: granted=true
      android.permission.VIBRATE: granted=true
      android.permission.ACCESS_WIFI_STATE: granted=true
      android.permission.USE_BIOMETRIC: granted=true
      android.permission.WAKE_LOCK: granted=true
    User 0: 
      gids=[3003]
      runtime permissions:
        android.permission.READ_SMS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.READ_CALL_LOG: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.BODY_SENSORS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.READ_PHONE_STATE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.SEND_SMS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.CALL_PHONE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.CAMERA: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.WRITE_EXTERNAL_STORAGE: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.RECORD_AUDIO: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.READ_CONTACTS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.ACCESS_MEDIA_LOCATION: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
    User 95: 
      gids=[3003]
      runtime permissions:
        android.permission.READ_SMS: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.READ_CALL_LOG: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ REVOKE_WHEN_REQUESTED|APPLY_RESTRICTION]
        android.permission.SEND_SMS: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|APPLY_RESTRICTION]
        android.permission.ACCESS_MEDIA_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED]
    User 150: 
      gids=[3003]
      runtime permissions:
        android.permission.READ_SMS: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.READ_CALL_LOG: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.SEND_SMS: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ APPLY_RESTRICTION]
        android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ APPLY_RESTRICTION]
cbonet-sw commented 1 year ago

This message contains a mixture of Termux and adb shell sessions looking at file accesses and file modes:

The curious thing is that in Termux we can use 'ls' to query a particular directory, but not read the contents of the directory. However, if we already know the name of an object within that directory, we can explicitly query the object with 'ls' using its full path (and then any objects within sub-directories, providing we know their exact pathnames).

Termux session:

~ $ ls -lZ /storage/3934-6139/Android/data/com.termux/files
ls: cannot open directory '/storage/3934-6139/Android/data/com.termux/files': Permission denied
~ $ ls -lZd /storage/3934-6139/Android/data/com.termux/files
drwxrwx--- 16 u0_a429 everybody ? 131072 Sep 18 08:14 /storage/3934-6139/Android/data/com.termux/files
~ $ id
uid=10429(u0_a429) gid=10429(u0_a429) groups=10429(u0_a429),3003(inet),9997(everybody),20429(u0_a429_cache),50429(all_a429)

Things to note:

Looking through the directory hierarchy in a Termux session:

~ $ pwd
/data/data/com.termux/files/home
~ $ ls -lZd .
drwx------ 29 u0_a429 u0_a429 ? 4096 Oct 27 13:59 .
~ $ ls -lZ /data
ls: cannot open directory '/data': Permission denied
~ $ ls -lZ /storage
ls: cannot open directory '/storage': Permission denied
~ $ ls -lZd /storage
drwx--x--x 5 shell everybody ? 100 Oct 27 10:34 /storage
~ $ ls -lZd /storage/3934-6139
drwxrwx--- 33 root everybody ? 131072 Oct 27 10:34 /storage/3934-6139
~ $ ls -lZd /storage/3934-6139/Android
drwxrwx--- 6 root everybody ? 131072 Oct  6  2021 /storage/3934-6139/Android
~ $ ls -lZd /storage/3934-6139/Android/data
drwxrwx--- 218 root everybody ? 131072 Oct 27 13:11 /storage/3934-6139/Android/data
~ $ ls -lZd /storage/3934-6139/Android/data/com.termux
drwxrwx--- 3 u0_a429 everybody ? 131072 Nov 30  2021 /storage/3934-6139/Android/data/com.termux
~ $ ls -lZd /
drwxr-xr-x 26 root root ? 4096 Dec 31  2008 /

where I can understand the denials on listing the contents of '/data' and '/storage' but obviously I can still descend into them via the 'x' bit.

We now use adb to find the full pathname of a file held within the Termux App's private directory on the external SD card. Take for example the file /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt within Termux's private directory on the external SD card: Now look for this file from within a Termux session:

~ $ ls -lZd /storage
drwx--x--x 5 shell everybody ? 100 Oct 27 10:34 /storage
~ $ ls -lZd /storage/3934-6139/Android/data/com.termux/files/
drwxrwx--- 16 u0_a429 everybody ? 131072 Sep 18 08:14 /storage/3934-6139/Android/data/com.termux/files/
~ $ ls -lZd /storage/emulated/0/Documents
drwxrwx--- 13 root everybody ? 4096 Oct 19 10:41 /storage/emulated/0/Documents
~ $ ls -lZ /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt
-rwxrwx--- 1 u0_a429 everybody ? 684 Mar 26  2022 /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt
~ $ hexdump -C /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt
[[successfully reads and displays contents of file]]

which demonstrates we can access information (using 'ls') about a specific file (providing we know its full pathname) and even open that file for reading; we just cannot list the contents of directories within app private storage on the external SD card.

Conclusion: there is a problem with using 'ls' to list directory contents of Termux 's private directory (and subdirectories thereof) on the external SD card. This is not just limited to 'ls', other methods within the Termux shell seem to fail in the same way. I presume that this is common to any code that uses the C stdio calls to opendir() and readdir() to enumerate directory contents.

On the other hand, we now seem to be fine looking at the 'public' parts of the external SD card that previously we didn't (under Android 11) have access to in Termux: we're ok with the root of external storage and the 'common' directories within this, eg. 'Download' and 'Documents'.

~ $ ls -lZ /storage/3934-6139
~ $ ls -lZ /storage/3934-6139/Documents
~ $ ls -lZ /storage/3934-6139/Download

all succeed and produce a directory listing. Also, /storage/3934-6139/Android and /storage/3934-6139/Android/media can be listed, but not /storage/3934-6139/Android/data.

Looking at some of the directory entries that caused problems under Termux, this time from an adb session reveals:

a10:/ $ ls /storage/3934-6139/Android/data/com.termux/files/
test
test2
...
a10:/ $ ls -ldZ /storage/3934-6139/Android/data/com.termux/files/                                                                                                              
drwxrwx--- 16 u0_a429 everybody u:object_r:fuse:s0  131072 2022-09-18 08:14 /storage/3934-6139/Android/data/com.termux/files/
a10:/ $ ls -ldZ /                                                                                                                                                              
drwxr-xr-x 26 root root u:object_r:rootfs:s0  4096 2008-12-31 15:00 /
a10:/ $ ls -ldZ /storage                                                                                                                                                       
drwx--x--x 5 shell everybody u:object_r:mnt_user_file:s0  100 2022-10-27 10:34 /storage
a10:/ $ ls -ldZ /storage/3934-6139/                                                                                                                                            
drwxrwx--- 33 root everybody u:object_r:fuse:s0  131072 2022-10-27 10:34 /storage/3934-6139/
a10:/ $ ls -ldZ /storage/3934-6139/Download                                                                                                                                    
drwxrwx--- 16 root everybody u:object_r:fuse:s0  131072 2022-10-27 20:23 /storage/3934-6139/Download
a10:/ $ ls -ldZ /storage/3934-6139/Android/                                                                                                                                    
drwxrwx--- 6 root everybody u:object_r:fuse:s0  131072 2021-10-06 17:09 /storage/3934-6139/Android/
a10:/ $ ls -ldZ /storage/3934-6139/Android/data                                                                                                                                
drwxrwx--- 218 root everybody u:object_r:fuse:s0  131072 2022-10-27 13:11 /storage/3934-6139/Android/data
a10:/ $ ls -ldZ /storage/3934-6139/Android/data/com.termux                                                                                                                     
drwxrwx--- 3 u0_a429 everybody u:object_r:fuse:s0  131072 2021-11-30 15:10 /storage/3934-6139/Android/data/com.termux
a10:/ $ ls -ldZ /storage/3934-6139/Android/data/com.termux/files                                                                                                               
drwxrwx--- 16 u0_a429 everybody u:object_r:fuse:s0  131072 2022-09-18 08:14 /storage/3934-6139/Android/data/com.termux/files
a10:/ $ ls -lZ /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt                                                                              
-rwxrwx--- 1 u0_a429 everybody u:object_r:fuse:s0  684 2022-03-26 07:40 /storage/3934-6139/Android/data/com.termux/files/test/repodata-idx.txt

and as we can see, using 'ls' from within an adb session does reveal the SELinux Contexts on the files.