Android L, SELinux and Root Apps

Last modified on 6. Sep 2017 at 19:34

With the official announcement of Android L during the keynote of the Google I/O conference on June 25 and the release of a developer preview the day after, long-foreseen restrictions for root apps became more real.

Note: This article is intended for developers only. The Android L developer preview is also only intended for developers. The information in this article is provided „as-is“ without any guarantees. Use it at your own risk and only use it if you know what you are doing.

Please check Additions for recent additions to this article.

About Android L and SELinux

The developer preview of Android L hints at which restrictions for root apps, mainly with regard to SELinux, can be expected in future. Chainfire provides a very helpful, developer-aimed explanation of the impact of SELinux and thus I won’t dive into more details here. Fact is: most root apps don’t work on the Android L developer preview. At least, not yet. Development of root apps becomes more difficult. As a developer of a root app myself, I’m highly interested in understanding the reasons and fixing them as soon as possible. The restrictions present in the developer preview will most-likely be included in the official release of Android L and later versions. Further restrictions must be expected. Thus, even although the results I describe in this article show that superuser access can still be achieved (at least for some apps) it’s very well possible that these methods won’t work in a few months.

Setup

The following experiments were done on a Nexus 5 running the Android L developer preview. The device was rooted using the steps shown by XDA user savoca here. Thanks, savoca! Additionally, SuperSU 2.00 by Chainfire was used. Thanks, Chainfire!

After setting up the device and rooting it, I tried to run my Android app RepetiTouch Pro. As expected, it did not work. Like many root apps, RepetiTouch uses an executable stored in its private app directory (under /data, e.g., /data/data/com.example.myapp/files/myexecutable) to handle the tasks which require superuser privileges. Trying to run this executable, called eventserver in this app, in a su shell results in

 
tmp-mksh: ./eventserver: can't execute: Permission denied

SELinux context experiments

Following this test, I experimented with various SELinux file contexts and additionally changed the file owner of this executable to root (the default file owner is similar to u0_a123). The file contexts can be listed via ls -Z and changed via the chcon command which is part of Android’s toolbox. Example:


 


 
ls -Z eventserver
-rwx------ u0_a88 u0_a88 u:object_r:app_data_file:s0 eventserver
chcon u:object_r:system_file:s0 eventserver
ls -Z eventserver
-rwx------ u0_a88 u0_a88 u:object_r:system_file:s0 eventserver

SuperSU supports the -cn/–context argument to set the SELinux context, e.g.,

su --context u:r:untrusted_app:s0

The default context is u:r:init:s0.

There were different results/errors:

  • „./eventserver: can’t execute: Permission denied“ with error code 126 and different shells prepended, e.g., „tmp-mksh: ./eventserver: can’t execute: Permission denied“. For this error, exec error is used in the following.
  • „error: only position independent executables (PIE) are supported.“ with error code 1. For this error, PIE error is used in the following.
  • success (which only means that the file could be executed, it still might not get access to other files or resources)

Default file context (u:object_r:app_data_file:s0)

su contextdefault ownerroot owner
u:r:init:s0tmp-mksh: exec errortmp-mksh: exec error
u:r:untrusted_app:s0sh: <stdin>[1]: exec errorPIE error
u:r:system_app:s0sh: exec errorsh: exec error
u:r:system_server:s0sh: exec errorsh: exec error

System file context (u:object_r:system_file:s0)

su contextdefault ownerroot owner
u:r:init:s0PIE errorPIE error
u:r:untrusted_app:s0sh: <stdin>[1]: exec errorPIE error
u:r:system_app:s0PIE errorPIE error
u:r:system_server:s0PIE errorPIE error

Position Independent Executables (PIE)

Information about this kind of executables can be found, e.g., on Wikipedia. Important at this point is that support for PIE was introduced in Android 4.1 (API level 16). However, non-PIE are supported in apparently all Android versions prior to the Android L developer release. When building native binaries for my apps I use the NDK. Because I’m trying to support many versions of Android, I define an API/platform level of 9 in the Application.mk file using

APP_PLATFORM := android-9

However, the resulting executables are then not PIEs. To build PIEs, I changed this to

APP_PLATFORM := android-16

Note: Just changing the platform level this way can result in binaries which don’t work on Android versions previous to Android 4.1. Therefore, in my app, I built the binaries twice, once for API level 9 (to 15) and once for API level 16 and up.
Another note: I assume that this will be useful to most if not all developers of root apps which will run into this PIE error with a very high probability. Thus, check your binaries and build configurations.

After building the eventserver using the updated platform level I repeated the same tests.

Default file context (u:object_r:app_data_file:s0)

su contextdefault ownerroot owner
u:r:init:s0tmp-mksh: exec errortmp-mksh: exec error
u:r:untrusted_app:s0sh: <stdin>[1]: exec errorsuccess (*)
u:r:system_app:s0sh: exec errorsh: exec error
u:r:system_server:s0sh: exec errorsh: exec error

System file context (u:object_r:system_file:s0)

Important: Please check the addition from July 9 at the end of this post. Changing the file context to u:object_r:system_file:s0 most probably won’t be possible outside of the recovery context in future Android versions.

su contextdefault ownerroot owner
u:r:init:s0successsuccess
u:r:untrusted_app:s0sh: <stdin>[1]: exec errorsuccess (*)
u:r:system_app:s0successsuccess
u:r:system_server:s0successsuccess

Although these results show that it is possible to use the u:r:untrusted_app:s0 context an executable might hit other restrictions as indicated by the two tests with the (*). The eventserver of my app accesses device files in /dev/input/ which can’t be accessed using the u:r:untrusted_app:s0 context. Thus, I’m using u:r:init:s0. At least as long as it works.

Bottom line

Be prepared for the new restrictions and any new restrictions which will be added in future. Although there are probably still ways to operate for many root apps it’s becoming more and more difficult.

Make sure to update your build configurations to create position independent executables. However, also make sure to not break compatibility on older Android versions.

Chainfire suggests to use the u:r:untrusted_app:s0 context wherever possible. I had no luck with this context with my app but generally agree to his advice. You might have to test and use different context for different tasks.

There are many more cases where changes might be required, e.g., sockets. Again, I recommend the explanations by Chainfire which are linked at the beginning of this article.

Additions

June 29: Changing the file context might result in the file being not removed if the app it belongs to is uninstalled. A possible approach to avoid this in most but not all cases is to change the file context when the app is launched and to reset it when the app is closed. However, an app might still be killed by the system leaving the file context changed.

July 4: Please star this issue which Joël Bourquard opened on the issue tracker for the Android L developer preview. Thanks, Joël!

July 9: A Google employee left a discouraging comment and closed the issue Joël Bourquard opened on the Android L developer preview’s issue tracker about the problem mentioned in the addition from June 29. Basically, the issue that files whose contexts were changed could prevent re-installations of the app they belong to is considered to be an application bug. However, much more important is this excerpt from the comment:

3) chcon u:object_r:system_file:s0 outside of recovery is no longer supported as of https://android-review.googlesource.com/95216 . The example provided in the initial bug report will now return EPERM.

Although other root apps might be able to work without using the u:object_r:system_file:s0 context this commit will certainly affect a lot of root apps, including RepetiTouch.

July 10: As far as I can see a possible solution for this issue could be to change the file context via a custom recovery using an appropriate update zip file. As Chainfire points out in this post this would result in further issues with different/outdated custom recoveries etc. This approach would also require a similar update file to reset the file context before uninstalling the app (which makes the issue even a bit more worse). Please post any ideas on this in the comments or contact me.

August 1: Chainfire told me that „the system_file context will be restricted to /system.“ Thus, binaries which require this file context will have to be placed under /system (unless another solution or workaround will be implemented).

October 23: Commenting on the issue on the bug tracker has been disabled. Additionally, a possible solution to the issue is described in comments 12 and 13.

November 4: Chainfire posted a new beta of SuperSU which solves the issue of left behind files by clearing the data directories when an app is fully uninstalled. Thanks a lot!

November 13: I’ve quickly tested RepetiTouch on the final release of Android 5.0 (LRX21O) on my Nexus 5. Recording and replaying touch input works. Thus, I don’t expect any issues but will check and update this post in case any issue occurs. Without having investigated this further, the system_file context apparently is not restricted to /system (yet).

This entry was posted in Android, Programming and tagged , , , , . Bookmark the permalink.

17 thoughts on “Android L, SELinux and Root Apps

  1. Don't despair, I think there's still hope until the final release of Android L.
    I think the Google developer didn't fully measure the consequences of this change, so I added a strong (but still polite) comment to the bug report. Hopefully they'll take it into account…

    • I've no news, yet. Using the latest preview of Lollipop I've encountered no issues with the described method but I plan to post an update once the final release is live anyway.

  2. so… interesting article. I'm kind of trying to track the follow-up. Have seen a couple/few pie patches for lollipop mentioned over on xda-developers. Saw the first for L preview and a follow-up for release on Nexus 5 and one by defcon for Nexus 4. These seem to mount the system partition and swap out the /system/bin/linker file as best I can tell. Not a lot of information floating about on said; so, I enjoy reading informational articles [such as yours] that explain how android evolved to this point.

  3. I am just willing to change my app's context. But it doesn't seem to change using su –context. Still remains untrusted_app. Tried with libsuperuser and RootTools. On my Nexus 7 2013.

    Could you please help me?

      • Yes I am. When trying to use su –context the control the doesn't progress to post execute.

        • What is your full su invocation command? I'm not using libsuperuser or RootTools myself so I can't help you with issues specific to them in case such an issue causes your problem.

          • su -cn u:r:system_app:s0

            This works in terminal emulator but not programmatically. The code execution just stops when this code is executed.

            I had to add supolicy as changing context wasn't being possible. The issue is solved though.

            Thanks for your time.

  4. Hi, how did you mange to make the communication between server and client in your RepetiTouch app ?
    I've tried to use the pipe in my own app but many oem roms don't allow the function 'fork' to be called in "/data".
    Any help please ?

  5. Thank you for your response, I did the same thing using fork, but how can I communicate through stdin/stdout of the binary without forking a child and dup the pipes ? what trick did you use to keep sending and receiving ?

    • I'm not sure if I understand you correctly but there's no trick involved here. Both client (Android app) and server (binary) just open and read/write from/to those standard streams, I don't use fork etc. but just keep the su session open, i.e., my server binary runs in foreground there.