Hey developers,
I would like to ask for your advise regarding SELinux. As you have probably read, there sepolicy in AOSP gets stricter and stricter. I think that for most users (especially corporate/non-geeky ones), this is a good step, but I don't want to discuss the sense here. It's a fact that restrictions will be stricter, so let's see what we as developers can do about this.
Note that disabling SELinux or setting it to permissive mode would be the obvious solution, but that's not always possible and has a wider impact on the system. So it's not something I would like to discuss here either.
So, Xposed and modules are accessing a couple of different files:
1) Module list - must be readable by the Zygote process (preferably by others as well), must be read- and writeable by the Xposed Installer app
2) Configuration files for the framework (such as "disable resources" etc.) - same as above
3) Configuration files for modules - must be (at least) readable by Zygote, system_server and every app, must be writable by a normal app (the module's UI)
4) Log - must be writable by Zygote, system_server and every app, must be readable by the Xposed Installer app (at least)
5) Upcoming libxposed_<runtime>.so files - must be loadable by at least the Zygote process (via dlopen), must be writable by the installer (preferably directly, otherwise via su)
Currently, only 5) seems to be an issue. That's mainly because the installer doesn't execute "restorecon" on app_process after replacing it, due to which it stays in context ubject_r:system_file:s0 instead of ubject_r:zygote_exec:s0 due to which the process runs as u:r:init:s0. This was accidental, but effective. I assume that it will no longer work properly in the future, and even now it already caused issues on some devices. So any solutions have to work for the correct zygote context.
Some things I have been wondering about:
- Would it be possible to add new policies to any ROM without recompiling it/flashing a new kernel/things like that? If so, then maybe the missing permissions could simply be added.
- Could the app which creates a configuration file also set its context? If so, it would be required to find a context which is less restrictive.
- In case changing the files' context is part of the solution, what is the risk of this change getting reverted? It seems that there is at least some code that triggers a relabelling: https://github.com/android/platform...a/com/android/server/pm/SELinuxMMAC.java#L388
- Would it be less risky to use something like /data/xposed/ or /data/system/xposed/ instead? I previously used the former, but got rid of it to avoid the need to clean up and because SELinux blocked writing to files there (with the default context).
I also had some ideas like using a shared memory segment or a socket to have some kind of daemon in the Zygote process (or a fork of it) to exchange content for configuration files. It would obviously have to store the data somewhere for the next restart. Configuration read/writes would have to be sent to the daemon. Might need changes in all modules using it, or maybe could be addressed globally with some hooks. That would be a bit like XPrivacy's service, which however is running in the system_server process only and therefore not available in Zygote.
Anyway, if you know a bit about how SELinux works on Android and can share some ideas or advise about the points mentioned above, please do so. This could be very crucial for Xposed support, even in the near future. Thanks!
First off, keep in mind I have not actually looked into Xposed internals and thus I'm probably making some assumptions about how it works that may not be correct. I'm also not actually testing the cases you present, just commenting on them from memory, a full answer would require full testing. Hopefully the comments are still of some help.
rovo89 said:
So, Xposed and modules are accessing a couple of different files:
1) Module list - must be readable by the Zygote process (preferably by others as well), must be read- and writeable by the Xposed Installer app
2) Configuration files for the framework (such as "disable resources" etc.) - same as above
3) Configuration files for modules - must be (at least) readable by Zygote, system_server and every app, must be writable by a normal app (the module's UI)
4) Log - must be writable by Zygote, system_server and every app, must be readable by the Xposed Installer app (at least)
5) Upcoming libxposed_<runtime>.so files - must be loadable by at least the Zygote process (via dlopen), must be writable by the installer (preferably directly, otherwise via su)
Click to expand...
Click to collapse
First, everything that needs to be written semi-regularly, should move outside of /system. On future stock rooted firmwares (without kernel mods) writing to /system will probably only be possible through recovery. So aside from the Xposed core, I would suggest starting the move to /data based operation as soon as possible.
rovo89 said:
Currently, only 5) seems to be an issue. That's mainly because the installer doesn't execute "restorecon" on app_process after replacing it, due to which it stays in context ubject_r:system_file:s0 instead of ubject_r:zygote_exec:s0 due to which the process runs as u:r:init:s0. This was accidental, but effective. I assume that it will no longer work properly in the future, and even now it already caused issues on some devices. So any solutions have to work for the correct zygote context.
Click to expand...
Click to collapse
This might already not work anymore on recent AOSP-based firmwares.
I have not tried this specific case, but why would (5) be an issue using /data under zygote-based contexts?
rovo89 said:
Some things I have been wondering about:
- Would it be possible to add new policies to any ROM without recompiling it/flashing a new kernel/things like that? If so, then maybe the missing permissions could simply be added.
Click to expand...
Click to collapse
On some devices maybe, but generally speaking: no. Changing policies will require flashing a custom kernel at least once. It currently seems that in many cases a modified initramfs will probably suffice, and the kernel will not need to be recompiled from source, but it's still a reflash.
I have already prototyped an automated system for this, in case SELinux policies are making the rooted future even more impossible than it is in AOSP already, but I'm not building it until we are closer to an Android release actually containing these SELinux enhancements, as this target is moving pretty fast.
rovo89 said:
- Could the app which creates a configuration file also set its context? If so, it would be required to find a context which is less restrictive.
Click to expand...
Click to collapse
That depends (on stuff and things!). It seems "ubject_r:app_data_file:s0" context can be made read/writable by most instances.
- In case changing the files' context is part of the solution, what is the risk of this change getting reverted? It seems that there is at least some code that triggers a relabelling: https://github.com/android/platform...a/com/android/server/pm/SELinuxMMAC.java#L388
Click to expand...
Click to collapse
This specific piece of code is triggered by a kernel/OTA update with an updated embedded policy. While this isn't exactly a common occurance, it is common enough that you will have to take it into account and relabel as needed.
- Would it be less risky to use something like /data/xposed/ or /data/system/xposed/ instead? I previously used the former, but got rid of it to avoid the need to clean up and because SELinux blocked writing to files there (with the default context).
Click to expand...
Click to collapse
Be sure to set your context when testing. The context set (or not) when playing around with su is usually none (unlabeled, but depends on firmware) and access to unlabeled files will be removed completely in the future. You need to specifically set some context (I might change SuperSU to use a sane default context in the future, not sure yet).
I'd probably use something like /data/data/com.rovo89.xposed/modules or /config or so ...
I also had some ideas like using a shared memory segment or a socket to have some kind of daemon in the Zygote process (or a fork of it) to exchange content for configuration files. It would obviously have to store the data somewhere for the next restart. Configuration read/writes would have to be sent to the daemon. Might need changes in all modules using it, or maybe could be addressed globally with some hooks. That would be a bit like XPrivacy's service, which however is running in the system_server process only and therefore not available in Zygote.
Click to expand...
Click to collapse
Unfortunately, like SuperSU, Xposed has to deal with some very rare edge-cases, which may not make the socket/daemon combination viable. Not all sub-contexts of zygote can necessarily connect to sockets, even when the socket context is changed. Unless you provide multiple sockets with multiple contexts - this gets messy quickly. I would advise against the daemon/socket combination specifically.
Shared memory segments are bit fishy on Android in general, but could work. I'd be very afraid of them restricting these in the near future, though.
There is no easy fix-all solution to this for something like if you need to constantly pump data from one process to another (SuperSU actually employs various methods to do this, of which so far it seems that even in all the edge-cases I have tested always at least one works), but I think for Xposed configuration and low-volume data exchange, simply using files in a folder you watch may be the most practical answer (if that can be made to work), maybe with a daemon orchestrating it all.
Also please note that in many cases the subcontexts of zygote have more access than the zygote context itself. The system_server context seems to be the most powerful at the moment.
Thanks a lot for your detailed reply!
Chainfire said:
First, everything that needs to be written semi-regularly, should move outside of /system. On future stock rooted firmwares (without kernel mods) writing to /system will probably only be possible through recovery. So aside from the Xposed core, I would suggest starting the move to /data based operation as soon as possible.
Click to expand...
Click to collapse
Yes, it has always been like this. Only /system/bin/app_process needs to be replaced on the system partition, everything else is in /data.
I have not tried this specific case, but why would (5) be an issue using /data under zygote-based contexts?
Click to expand...
Click to collapse
Whenever I set app_process to the original zygote context, this fails:
Code:
access("/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar", R_OK)
And I get such audit messages:
Code:
type=1400 msg=audit(1402425979.373:155): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:156): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:157): avc: denied { read } for pid=22027 comm="zygote" name="input" dev="tmpfs" ino=6392 scontext=u:r:zygote:s0 tcontext=u:object_r:input_device:s0 tclass=dir
type=1400 msg=audit(1402425979.373:158): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:159): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:160): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
Inode 1490945 = /data/data/, so it isn't even about the file itself, but it's cut off at the root (worse than I thought it would be).
Even if I ignore this error and add the JAR file with Xposed's Java classes to the classpath anyway, it can't continue because the VM can't access the JAR itself. That's the actual problem, no matter what I do to detect whether the file exists.
For the library, I had seen some other errors (dlopen couldn't map segment 2, audit log showed that the it would need "execute" permissions), but I can't reproduce them right now. In my current dev version, I load the library later, so maybe that solved it.
Changing policies will require flashing a custom kernel at least once,
Click to expand...
Click to collapse
Ok, so it's not really an option for the masses, at least it's not something that several apps should handle each on their own...
That depends (on stuff and things!). It seems "ubject_r:app_data_file:s0" context can be made read/writable by most instances.
Click to expand...
Click to collapse
You say "can be made read/writable" - what exactly do you mean with that? Are there any options for contexts? Or do you mean a file could be changed from "ubject_r:app_data_file:s0" to some other, more accessible context?
This specific piece of code is triggered by a kernel/OTA update with an updated embedded policy. While this isn't exactly a common occurance, it is common enough that you will have to take it into account and relabel as needed.
Click to expand...
Click to collapse
I expected that.. Pretty sure that this can't be done from the zygote context, but as it is running as root, couldn't I call "su" and change the file contexts as needed? Or do you expect that even with "su" and the context awareness that you introduced, this won't be possible?
Be sure to set your context when testing. The context set (or not) when playing around with su is usually none (unlabeled, but depends on firmware) and access to unlabeled files will be removed completely in the future. You need to specifically set some context (I might change SuperSU to use a sane default context in the future, not sure yet).
I'd probably use something like /data/data/com.rovo89.xposed/modules or /config or so ...
Click to expand...
Click to collapse
Sure, the context would need to be set. Actually I currently have all the files in subdirectories of /data/data/de.robv.android.xposed.installer/, but I was wondering whether a custom subdirectory in /data might be less restricted than /data/data/, maybe had more sensible default contexts or things like that.
Unfortunately, like SuperSU, Xposed has to deal with some very rare edge-cases, which may not make the socket/daemon combination viable. Not all sub-contexts of zygote can necessarily connect to sockets, even when the socket context is changed. Unless you provide multiple sockets with multiple contexts - this gets messy quickly. I would advise against the daemon/socket combination specifically.
Shared memory segments are bit fishy on Android in general, but could work. I'd be very afraid of them restricting these in the near future, though.
Click to expand...
Click to collapse
As system_server and all apps are forked from the Zygote process, would that change something? I thought of opening a pipe or shared mmap at the very beginning and let all the children use it. Or would restrictions also apply when the handle is already open?
There is no easy fix-all solution to this for something like if you need to constantly pump data from one process to another (SuperSU actually employs various methods to do this, of which so far it seems that even in all the edge-cases I have tested always at least one works), but I think for Xposed configuration and low-volume data exchange, simply using files in a folder you watch may be the most practical answer (if that can be made to work), maybe with a daemon orchestrating it all.
Also please note that in many cases the subcontexts of zygote have more access than the zygote context itself. The system_server context seems to be the most powerful at the moment.
Click to expand...
Click to collapse
Exactly those different subcontexts might be the problem, because config files need to be readable by zygote, system_server and all the apps. That's what I'm worried about. Otherwise, it sounds fine.
I can't switch back from e.g. system_server to zygote context, can I? So if I want to use some of the additional permissions that the system_server has, it would have to be in a new or forked process?
Again, please take all this with a grain of salt. Moving target, answered from memory. We are getting more and more into deep specifics that really need to be tested to know the answers for sure.
rovo89 said:
Whenever I set app_process to the original zygote context, this fails:
Code:
access("/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar", R_OK)
And I get such audit messages:
Code:
type=1400 msg=audit(1402425979.373:155): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:156): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:157): avc: denied { read } for pid=22027 comm="zygote" name="input" dev="tmpfs" ino=6392 scontext=u:r:zygote:s0 tcontext=u:object_r:input_device:s0 tclass=dir
type=1400 msg=audit(1402425979.373:158): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:159): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
type=1400 msg=audit(1402425979.373:160): avc: denied { search } for pid=22027 comm="zygote" name="data" dev="mmcblk0p28" ino=1490945 scontext=u:r:zygote:s0 tcontext=u:object_r:app_data_file:s0 tclass=dir
Inode 1490945 = /data/data/, so it isn't even about the file itself, but it's cut off at the root (worse than I thought it would be).
Even if I ignore this error and add the JAR file with Xposed's Java classes to the classpath anyway, it can't continue because the VM can't access the JAR itself. That's the actual problem, no matter what I do to detect whether the file exists.
Click to expand...
Click to collapse
Is it absolutely necessary to do this from the zygote context, as opposed to the system_server/system_app/platform_app/untrusted_app contexts, where this might actually work? (as stated before zygote seems to have less permissions than the ones it actually transitions to)
Perhaps this specific jar should go into /system/framework (or something) ?
rovo89 said:
For the library, I had seen some other errors (dlopen couldn't map segment 2, audit log showed that the it would need "execute" permissions), but I can't reproduce them right now. In my current dev version, I load the library later, so maybe that solved it.
Click to expand...
Click to collapse
This is a different library than the jar? The init context cannot load executable content from /data, but the contexts apps actually run as can...
rovo89 said:
You say "can be made read/writable" - what exactly do you mean with that? Are there any options for contexts? Or do you mean a file could be changed from "ubject_r:app_data_file:s0" to some other, more accessible context?
Click to expand...
Click to collapse
chcon ubject_r:app_data_file:s0 && chmod 777 seems to allow most contexts full read/write access to most files. Not necessarily execute. However, if you're talking about a native library and you've moved the loading from where the context was init to a place where the context is now zygote or friends, that has indeed probably solved that problem.
rovo89 said:
I expected that.. Pretty sure that this can't be done from the zygote context, but as it is running as root, couldn't I call "su" and change the file contexts as needed? Or do you expect that even with "su" and the context awareness that you introduced, this won't be possible?
Click to expand...
Click to collapse
For the time being, it should work as "su", though that may be changed in the future. I'm not so sure this can't be done from zygote, or system_server.
rovo89 said:
Sure, the context would need to be set. Actually I currently have all the files in subdirectories of /data/data/de.robv.android.xposed.installer/, but I was wondering whether a custom subdirectory in /data might be less restricted than /data/data/, maybe had more sensible default contexts or things like that.
Click to expand...
Click to collapse
I haven't really investigated this in-depth, but I wasn't able to get some things working from /data with SuperSU, that I was able to get working from /data/data ... Maybe I didn't try the right chcon, just saying what happened.
rovo89 said:
As system_server and all apps are forked from the Zygote process, would that change something? I thought of opening a pipe or shared mmap at the very beginning and let all the children use it. Or would restrictions also apply when the handle is already open?
Click to expand...
Click to collapse
If you transition from one context to another and a handle is open that the target context should not have access to, that handle will be closed. At the moment, there are only 5 relevant contexts for all this, zygote, system_server, system_app, platform_app, and untrusted_app, but who knows how they will expand that... You could a socket under each of these contexts from zygote and test if you can connect to at least one from each of them, and your problem would be solved for now. I don't like it much, though. Note that you cannot set the context for a pipe for some reason, only for sockets (and files, and ...).
rovo89 said:
Exactly those different subcontexts might be the problem, because config files need to be readable by zygote, system_server and all the apps. That's what I'm worried about. Otherwise, it sounds fine.
Click to expand...
Click to collapse
I still think reading config files should be possible with correctly chcon'd and chmod'd files under /data/data somewhere.
rovo89 said:
I can't switch back from e.g. system_server to zygote context, can I? So if I want to use some of the additional permissions that the system_server has, it would have to be in a new or forked process?
Click to expand...
Click to collapse
You can switch dynamically from zygote to system_server and *_app domains, but you indeed cannot switch back.
Chainfire said:
Again, please take all this with a grain of salt. Moving target, answered from memory. We are getting more and more into deep specifics that really need to be tested to know the answers for sure.
Click to expand...
Click to collapse
Sure. I'm fully aware that this will require lots of testing. Just trying to figure out the rough direction to go.
Is it absolutely necessary to do this from the zygote context, as opposed to the system_server/system_app/platform_app/untrusted_app contexts, where this might actually work? (as stated before zygote seems to have less permissions than the ones it actually transitions to)
Click to expand...
Click to collapse
Yes, it's absolutely necessary. Xposed has to be initialized in the Zygote process, before anything of the Java stack has been loaded (shortly after the VM was started). At this time, it's still running in the zygote process. Switching to a different context at this time seems very risky to me, as probably none of the subcontexts will contain all the permissions granted to the zygote context (which are needed for other things the Zygote process does).
Perhaps this specific jar should go into /system/framework (or something) ?
Click to expand...
Click to collapse
Should be possible. I put it into /data/data/.../bin now because it's much easier to manage it than requesting root every time.
This is a different library than the jar? The init context cannot load executable content from /data, but the contexts apps actually run as can...
Click to expand...
Click to collapse
Yes, it's an .so file which needs to be loaded from the zygote context (not a subcontext) as well. It includes implementations of the native methods and other stuff. But I could probably put it to /system/lib, that simply has to be accessible.
chcon ubject_r:app_data_file:s0 && chmod 777 seems to allow most contexts full read/write access to most files. Not necessarily execute. However, if you're talking about a native library and you've moved the loading from where the context was init to a place where the context is now zygote or friends, that has indeed probably solved that problem.
Click to expand...
Click to collapse
I have tested it again, and unfortunately, the zygote context doesn't seem to be able to access files labeled as "ubject_r:app_data_file:s0" (even when I put them to /system/framework, where it can read files with other contexts). The zygote context is really very limited with regard to file permissions: https://android.googlesource.com/platform/external/sepolicy/+/master/zygote.te
Also the system_server process just seems to have { getattr read write } on these files: https://android.googlesource.com/platform/external/sepolicy/+/master/system_server.te
I'm not used to reading these definitions, but it seems to me that only system_data_file and dalvikcache_data_file could be useful, the latter being r/w from both zygote and system_server. So at least these two could exchange data both ways. And apps could contact the system_server via IPC. It shouldn't be too hard to inject an additional service or receiver via Xposed. It could receive config values which are needed for Zygote and store them in a file which is at least readable by it. Reading data from other apps should be possible with the same requirement as today (world-readable bit).
If you transition from one context to another and a handle is open that the target context should not have access to, that handle will be closed.
Click to expand...
Click to collapse
Too bad. This doesn't solve it, does it? "allow appdomain zygote:fd use;"
I still think reading config files should be possible with correctly chcon'd and chmod'd files under /data/data somewhere.
Click to expand...
Click to collapse
I hoped it would... but zygote seems to be the blocker here. I will have to test further if it works better in other contexts.
rovo89 said:
Yes, it has always been like this. Only /system/bin/app_process needs to be replaced on the system partition, everything else is in /data.
Click to expand...
Click to collapse
Instead of replacing the file, the command "mount -o bind A B" could help.
So /system has not to be altered, but the "mount" has to be executed in any way. Maybe it is enough to mount sometime, and not before the 1st start of the binary. Additionally i'm not sure if a mounted app_process could be executed (SE).
defim said:
Instead of replacing the file, the command "mount -o bind A B" could help.
So /system has not to be altered, but the "mount" has to be executed in any way. Maybe it is enough to mount sometime, and not before the 1st start of the binary. Additionally i'm not sure if a mounted app_process could be executed (SE).
Click to expand...
Click to collapse
There is a tiny catch in that plan: When would you execute that mount command? It would need to be done very early, before app_process is started. I think that would be very hard, if not impossible to achieve for most ROMs.
I didn't know that mount --bind works for single files as well. I'll keep it in mind, maybe it will be handy for something. I already though whether creating a tmpfs mount for file exchange could be useful.
rovo89 said:
There is a tiny catch in that plan: When would you execute that mount command? It would need to be done very early, before app_process is started. I think that would be very hard, if not impossible to achieve for most ROMs.
I didn't know that mount --bind works for single files as well. I'll keep it in mind, maybe it will be handy for something. I already though whether creating a tmpfs mount for file exchange could be useful.
Click to expand...
Click to collapse
Remounting a single file is possible with this command. I'm using this on another device maby times to alter read only files in a rom.
But I dont know where to exectute the mount for android...
Maybe the system could be started unmodifyed, and the app_process is mounted&re-started later. I dont know if this is possible, dont know the Android startup procedure.
@rovo89 and @Chainfire,
Would a decoder for binary sepolicy files be of any use in this context?
As part of the learning curve for this stuff, I not only read a bit of documentation but was also looking at the binary file format. Given that non-AOSP roms don't necessarily have the same SELinux definitions, and if no such tool exists yet, I could put together a few java classes to dump the binary file in a readable form.
Let me know if that would serve any purpose; if so, I just need to work on the "display" part as the parsing of the raw structures is already working, both for an aosp-built file and from my device's ROM. It's not possible to output the info in the original format but it should be readable even if it's a bit more verbose than the *.te sources.
I think it definitely would be useful. I haven't done enough research yet to know whether this data could be used for some intelligent algorithm to find a good storage position. But for manual analysis, it would be very helpful. If something doesn't work on some ROMs, it would be much easier to ask users to send their sepolicy and *_contexts files than sending them test versions for trial and error.
Anything into that direction would be great to have. A few ideas came to my mind right away, maybe you can check whether they could be realized:
1) Would it be possible to emulate a certain access? Give a certain operation, source and target context and it says yes or no? That's a bit more than a search function, and could especially be useful for some automated compatibility checks on the user's device.
2) Maybe using 1) with wildcards, or with a different approach, would it be possible to find out e.g. which file contexts the zygote domain can write to? Or which operations are allowed for the system server on dalvik cache files?
3) It might be useful to diff two policies, which would be easy if the policy could be dumped into a text file in a reproducable, human readable way. Actually, this would allow for other scripts to perform post-processing and analysis as suggested in 1) or 2) as well, so maybe that could be the answer to the display part.
By the way, my current use case for 3) is that I'm playing around with the L preview and had to recompile sepolicy from AOSP with the userdebug variant in order to get adbd running as root (with the correct context). It's working fine now, but I would really like to know whether I ditched some other important policies with this.
I just found the "checkpolicy" command, which could be used with the "-b -d - M" options to show some menu:
Code:
Select an option:
0) Call compute_access_vector
1) Call sid_to_context
2) Call context_to_sid
3) Call transition_sid
4) Call member_sid
5) Call change_sid
6) Call list_sids
7) Call load_policy
8) Call fs_sid
9) Call port_sid
a) Call netif_sid
b) Call node_sid
c) Call fs_use
d) Call genfs_sid
e) Call get_user_sids
f) display conditional bools
g) display conditional expressions
h) change a boolean value
m) Show menu again
q) Exit
But to be honest, I don't really understand what it can do.
Don't have anything useful to add, just posting to let you know I'm still following and reading up on this.
@Tungstwenty I checked a bit further. "sesearch" is available on many Linux distributions and seems to work quite well to search for certain rules. Maybe you want to look at it before investing more time.
I have been able to dump most of the info in a very verbose format.
The binary format doesn't allow the reconstruction of the source file because many of the "wildcard" entries are expanded to individual ones for speed. Anyway, for quick search of rules on the text it's much simpler to grep for the intended token instead of needing to e.g. cope with "*", "~{ ... }", etc. instead of say "read" or "write".
I still need to change the code so it runs elsewhere, but here are the dumps of 3 policies:
- AOSP, 4.4.2 tag, compiled with the ENG configuration
- my stock Xperia Z 4.4.2
- S5 (shared here, don't know the exact ROM)
From AOSP to the Xperia Z the differences aren't that big.
The "su" type is removed, as well as the few entries related with allowing shell to run it, automatically doing the transition to the su type, and the permissions of something running with su context.
Apart from that, only additional entries and permissions specific to the Xperia Z.
In both of them the "init" type is set to permissive, which for Xposed means that having /system/bin/app_process assigned to context "u: object_r:system_file:s0" instead of "u: object_r:zygote_exec:s0" makes everything work fine.
Since there is no type transition from init to system_file:file, zygote will run with "u:r:init:s0" and do everything it wants since init is permissive. Denied avc messages could pop up on dmsg that might not be triggered if the zygote context was being used, but they're merely informative.
Changing app_process to zygote_exec would make the transition happen from "init" to "zygote", and then no access could be done to /data/data/* since 1) zygote isn't defined as permissive and 2) there isn't any allow command to "app_data_file:file { ... }" either to zygote directly, or none of the attribute it has ("mlstrustedsubject" and "domain").
Now, on the S5 there are lots of differences against AOSP.
First of all, none of the types (including init) are permissive. Access will fail unless explicit grants exist.
init can execute system_file:file objects (through one of its attributes such as domain) and there's still no type_transition for that, so app_process will remain as init.
Even though init is no longer permissive, it will be able to read /data/data/* app_data_file objects, since it's (still) declared with the "unconfineddomain" and there's now a rule to allow unconfineddomain access to app_data_file:file.
As for the other permissions of init vs zygote, there are *a lot* of additional entries for zygote.
To query:
Code:
TYPES=`grep "^type zygote," *.dump | cut -c6- | sed "s/, /|/g" | sed "s/;//g"`
grep -E "^allow ($TYPES) " *.dump | cut -d" " -f3 | sort | uniq
grep -E "^type_transition ($TYPES) " *.dump | cut -d" " -f3-
Repeat for "init" instead of "zygote" and compare.
The good news for Xposed is that zygote now has access to app_data_files:
Code:
$ grep -E "^allow ($TYPES) " *.dump | cut -d" " -f3- | sort | uniq | grep "^app_data_file:"
app_data_file:blk_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:chr_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:dir { ioctl read write create getattr setattr unlink link rename add_name remove_name reparent search rmdir open }
app_data_file:fifo_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:file { ioctl read write create getattr setattr lock append unlink link rename execute open }
app_data_file:file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:lnk_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:sock_file { ioctl read write create getattr setattr lock append unlink link rename open }
Configuration files can be read, and the log can be written from zygote.
Regular apps will have type "untrusted_app" (from seapp_contexts) and its permissions for app_data_file (where mod's configurations are):
Code:
$ TYPES=`grep "^type untrusted_app," *.dump | cut -c6- | sed "s/, /|/g" | sed "s/;//g"`
$ grep -E "^allow ($TYPES) " *.dump | cut -d" " -f3- | sort | uniq | grep "^app_data_file:"
app_data_file:blk_file getattr
app_data_file:chr_file getattr
app_data_file:dir { ioctl read getattr search open }
app_data_file:dir { ioctl read write create getattr setattr unlink link rename add_name remove_name reparent search rmdir open }
app_data_file:fifo_file { ioctl read getattr lock open }
app_data_file:fifo_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:fifo_file getattr
app_data_file:file { ioctl read getattr lock execute execute_no_trans open }
app_data_file:file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:file getattr
app_data_file:lnk_file { ioctl read getattr lock open }
app_data_file:lnk_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:lnk_file getattr
app_data_file:sock_file { ioctl read write create getattr setattr lock append unlink link rename open }
app_data_file:sock_file { ioctl read write getattr lock append open }
app_data_file:sock_file getattr
so they should also be able to load their configurations and write to the xposed log (TBC)
I also noticed this and this commit on the master of sepolicy, suggesting that this sort of changes will indeed make it to the next release.
Chainfire had already posted along those lines here, about the removal of the execute permission for files other than rootfs, system_file or exec_type (such as scripts on /data/data/*.
@Chainfire, perhaps running "chcon u: object_r:system_file:s0 ..." on the script file prior to its execution might be a workaround?
EDIT: Just noticed that you already mention this kind of approach in section "Android 4.5" of your How-to-SU guide.
Tungstwenty said:
- S5 (shared here, don't know the exact ROM)
Click to expand...
Click to collapse
It's Samsung Galaxy S5 G900F with stock firmware/kernel G900FXXU1ANC9.
Thanks, @Tungstwenty! One thing is for sure: Vendor-specific policies make it even harder.
In my research, I came to the conclusion that AOSP master doesn't have any file contexts which can be written by zygote, system_server and apps. That affects the logfile, so I will probably just send it to logcat for now and think of other ways later.
The safest way to ensure that the JAR file and native libraries are working is probably to put them next to similar files, i.e. to /system/framework and /system/lib.
For modules' config files (shared preferences), it would be enough to have read-only access to app_data_file. Unfortunately, AOSP master and the L preview don't allow this for zygote, and system_server can only read already opened files, but can't open any such files itself.
Zygote has access to some other contexts, like system_data_file, but normals apps don't have write access to it, so all the modules which want to provide settings would have to use su, couldn't use the standard preference screens and so on.
So here is my plan to work around these issues and hopefully keep modules working without any changes on their part. The Xposed API includes an XSharedPreferences class which modules should use to read their settings. If this class could abstract different ways to read a file, it would be transparent to the modules. I think three different ways would be necessary:
1. Direct access. This is the easiest one. Requires chmod o+r, which is the case without SELinux as well. Then it should work for any normal app:
Code:
sesearch -A sepolicy_l -c file -p read -s appdomain -t app_data_file
Found 2 semantic av rules:
allow untrusted_app app_data_file : file { ioctl read getattr lock execute execute_no_trans execmod open } ;
allow appdomain app_data_file : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
2. Native binder call to a forked process. system_server has only read/write/getattr (but not open) access to app_data_file. So my idea is to fork a process early in zygote/app_process. In this process, I setcon("u:r:untrusted_app:s0"). Then I have a process running as root in the same domain as normal apps run, so it should be able to read any files written by normal apps. This process could host a binder service implemented in C++ which could receive a filename and return the content of the file. In case method 1 doesn't work (especially for the system_server), the app could make a binder call to that service. Both system_server and appdomain are allowed to do that:
Code:
sesearch -A sepolicy_l -c binder -p call -t appdomain
Found 15 semantic av rules:
allow unconfineddomain untrusted_app : binder { call set_context_mgr transfer } ;
allow unconfineddomain system_app : binder { call set_context_mgr transfer } ;
allow mediaserver appdomain : binder { call transfer } ;
allow unconfineddomain shell : binder { call set_context_mgr transfer } ;
allow unconfineddomain isolated_app : binder { call set_context_mgr transfer } ;
allow appdomain appdomain : binder { call transfer } ;
allow unconfineddomain nfc : binder { call set_context_mgr transfer } ;
allow unconfineddomain platform_app : binder { call set_context_mgr transfer } ;
allow drmserver appdomain : binder { call transfer } ;
allow dumpstate appdomain : binder { call transfer } ;
allow unconfineddomain bluetooth : binder { call set_context_mgr transfer } ;
allow system_server appdomain : binder { call transfer } ;
allow surfaceflinger appdomain : binder { call transfer } ;
allow unconfineddomain radio : binder { call set_context_mgr transfer } ;
allow surfaceflinger shell : binder { call transfer } ;
I have also verified in a PoC that the call from context system_server to untrusted_app works. It could theoretically also be used by normal apps of the file is not world readable. It could then make it world readable. But this has to be evaluated later, it depends on how well this service can be protected (system_server can be recognized by its UID, otherwise filename patterns would be an option).
3. Unfortunately, zygote is not allowed to make binder calls, and even if it was, the additional threads created for it can interfere with the startup procedure. So an alternative is needed. Fortunately, there is not much concurrency in zygote and only a few file reading attempts have to be expected. So I would use mmap with MAP_SHARED | MAP_ANONYMOUS to create an anonymous shared memory region that could be used by child processes as well. I believe that this can't be restricted, in contrast to named shared memory (ashmem). Again, I would fork a process and use setcon to let it access the files (actually, it should be possible to use one multi-threaded process for options 2 and 3). The communication between zygote and this child process would happen via the mmap'd area. Zygote would write the filename and some kind of operation code to it, then the child process would write the file content back. I'm using mutex and cond to ensure the proper control flow, which seems to be working fine.
XSharedPreferences would first try option 1, and fall back to either option 2 or 3 (zygote runs as UID 0, so that's probably an easy choice).
@Tungstwenty, @Chainfire: Any comments on these plans?
rovo89 said:
@Tungstwenty, @Chainfire: Any comments on these plans?
Click to expand...
Click to collapse
I don't see any obvious issues with it. Maybe I'd still try to do the communication via sockets (if possible) rather than MMAP, but that seems a rather inconsequential difference.
Chainfire said:
Maybe I'd still try to do the communication via sockets (if possible) rather than MMAP, but that seems a rather inconsequential difference.
Click to expand...
Click to collapse
I'll try that, but I assume it won't work because zygote isn't listed for any socket permissions:
Code:
sesearch -A sepolicy_l -c socket
Found 14 semantic av rules:
allow platform_app platform_app : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow mtp mtp : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow radio radio : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow rild rild : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow untrusted_app untrusted_app : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow system_server system_server : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow unconfineddomain domain : socket { ioctl read write create getattr setattr lock relabelfrom relabelto append bind connect listen accept getopt setopt shutdown recvfrom sendto recv_msg send_msg name_bind } ;
allow ppp mtp : socket { ioctl read write getattr setattr append bind connect getopt setopt shutdown } ;
allow sensors sensors : socket { ioctl read write create getattr setattr lock relabelfrom relabelto append bind connect listen accept getopt setopt shutdown recvfrom sendto recv_msg send_msg name_bind } ;
allow time time : socket { ioctl read write create getattr setattr lock relabelfrom relabelto append bind connect listen accept getopt setopt shutdown recvfrom sendto recv_msg send_msg name_bind } ;
allow unconfineddomain port_type : socket name_bind ;
allow thermald thermald : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow rmt rmt : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
allow mediaserver mediaserver : socket { ioctl read write create getattr setattr append bind connect getopt setopt shutdown } ;
rovo89 said:
@Tungstwenty, @Chainfire: Any comments on these plans?
Click to expand...
Click to collapse
Looks good to me.
My main doubt was whether it would be possible to use *inter-process* mutexes not subject to SELinux restrictions. As you mentioned, it should be that way. pthread relies on kernel futexes for the inter-process communication, and there doesn't seem to be any SEL class for that kind of object.
One thing I read here is the possibility of problems in case the (shared) memory being used for the futex operation is swapped out. It isn't clear to me if this is the responsibility of the developer to lock the userspace page where the futext is located, or if the kernel handles it automatically when it creates the futex object pointing to that address. It might be something worth clarifying, since if it is indeed a possibility, it will be something that might be very hard to reproduce in case of occasional failures.
rovo89 said:
I'll try that, but I assume it won't work because zygote isn't listed for any socket permissions:
Click to expand...
Click to collapse
Ah yes, I misremembered this. I actually have a root process running above zygote that opens sockets, then reconnect to those from zygote subs (system, untrusted, etc) rather than the zygote context itself.