Run executable from init service with root permissions

Hi,

I am trying to run an executable from init service. I was able to log a simple ‘hello world’ to the logcat from my executable. I am looking to monitor all the apps from my program, if and when they run on the board. Hence I believe I need root access (for creating files, accessing system related info, etc). How can I start the executable as root ?

I tried to create and save data to a file from my executable, but nothing happens. My plan is to give root access first and ensure my executable works as desired and then later update the sepolicy(once I learn it) accordingly.

init.hikey960.rc contains:

on post-fs-data
chmod 0777 /vendor/etc/bin/myExe #this never works

service my_service /vendor/etc/bin/myExe
class main
user root
oneshot

device-hikey960.mk contains:
PRODUCT_COPY_FILES += device/linaro/hikey/myExe:$(TARGET_COPY_OUT_VENDOR)/etc/bin/myExe

sepolicy/my_service.te contains:
type my_service, domain;
type my_service_exec, exec_type, vendor_file_type, file_type;

init_daemon_domain(my_service)

and sepolicy/file_contexts contains:
/vendor/etc/bin/myExe u:object_r:my_service_exec:s0

First of all, don’t be messing with existing init scripts. That is unnecessary, and increases YOUR maintenance burden dramatically. What you want to do is create a new init script, which will be installed to /vendor/etc/init/your_init_script.rc – this will be automatically executed at boot time as root.

And don’t be using PRODUCT_COPY_FILES. Build your daemon properly.

Here is an init service that you can use as a starting point;

Pay close attention to the Android.bp, and note how it refers to the init script. Also note that you will add the value of the “name” specified in the Android.bp to the PRODUCT_PACKAGES variable.

This part you seem to have figured out, but for the sake of providing a complete picture of this daemon’s setup…

This is the accompanying sepolicy for it:

*** Only the first 3 lines are strictly needed.

And line 3 for the executable’s context;

Now follow that and you will end up about where you already are, but in a much cleaner configuration, and WAY easier to maintain.

But you still have a problem in that you believe that you need to add root access in order to achieve a greater amount of access to the system. This right?

Here is the thing. Contrary to popular belief, root is not God. Just because you are running as root does not mean that you have complete access and control over everything. In fact, given what you’ve added, your process is running as root. The problem you are running into now, is that from the perspective of selinux, root is just another user. This means that in order to gain the access to the system that you are looking for, you need to write selinux policy to allow a process running in the “my_service” context to access those parts of the system that you are looking to access.

So back to the daemon example I gave you above. The selinux policy file where I said that strictly you only need the first 3 lines. The remainder of the lines in that file grant that context access to the parts of the system that are required by that daemon, which you will note from its init script, runs as root.

So what you will need to do, is grep your logcat and dmesg for lines containing “avc”.
logcat | grep avc
dmesg | grep avc

Those will tell you when selinux is interfering with your process, and will give you enough information to write rules to tell selinux to allow it.

HOWEVER, the default AOSP selinux policy is loaded with “neverallow” rules. It can be extremely challenging to work your way around them, and in some cases, impossible.

The good news is that neverallow violations dump out at compile time with details about the file and line where the neverallow rule is located, so you can easily inspect the rule and see if there are ways around it, such as adding special attributes to your context, like you can see in this sepolicy on line 3;

@doitright, Thank you for the detailed reply. As per your suggestion, I have updated my implementation.

Blockquote First of all, don’t be messing with existing init scripts. That is unnecessary, and increases YOUR maintenance burden dramatically. What you want to do is create a new init script, which will be installed to /vendor/etc/init/your_init_script.rc – this will be automatically executed at boot time as root.

I have removed my contents from the init.hikey960.rc file and moved it to “my_service.rc” in a separate folder similar to thermalfand implementation.

Blockquote Pay close attention to the Android.bp, and note how it refers to the init script. Also note that you will add the value of the “name” specified in the Android.bp to the PRODUCT_PACKAGES variable.

I have removed PRODUCT_COPY_FILES and made use of PRODUCT_PACKAGES to add my executable into the /vendor/etc/bin with help of Android.bp file.

Blockquote Now follow that and you will end up about where you already are, but in a much cleaner configuration, and WAY easier to maintain.

Yes, this is a much cleaner way now that I am not messing around with default/other rc files and easy to maintain as well.

Blockquote But you still have a problem in that you believe that you need to add root access in order to achieve a greater amount of access to the system. This right?

Yes, thats what I thought.

Blockquote Those will tell you when selinux is interfering with your process, and will give you enough information to write rules to tell selinux to allow it.

Well, I thought I could try giving my service, some already existing system or root type context which will help me run my executable and see if it works properly and later on add my context and rules to it.

Start by putting selinux in permissive to make it work. You don’t need to be fighting with some other context that isn’t intended to do what you’re trying to do. Watch the AVC messages, write policy until the selinux is nice and happy, then switch back to enforcing.

Add this kernel parameter;

androidboot.selinux=permissive

@doitright I have started by setting selinux to permissive with the help of setenforce command and my code throws avc denied errors (as expected). I am trying to make policies with help of allow2audit tool.

How to handle the obtained neverallow rules ? Is there some rules or workarounds to proceed with this or should we go for alternatives/better implementations ?

Eg: I am trying to access sockets from my executable and defined allow my_service netd:unix_stream_socket connectto; in my policy. But throws a neverallow which points to # Vendor domains (except netdomain) are not permitted to initiate communications to netd sockets

Eg: I am trying to access sockets from my executable and defined allow my_service netd:unix_stream_socket connectto; in my policy. But throws a neverallow which points to # Vendor domains (except netdomain) are not permitted to initiate communications to netd sockets

Try adding below to your service in ‘my_service.rc’. E.g.

service my_service /path/to/my_service
   ...
   capabilities NET_ADMIN
   ...

You can also try adding group inet [<additional group name as needed>] as well, but not too sure about this.

Did you try adding it to net domain?

netdomain is an attribute defined in public/attributes on line 136.
typeattribute your_context netdomain;

Or (equivalently) using the net_domain macro:
net_domain(your_context)

And you really should use the kernel commandline parameter to set it to permissive, because you can’t type in the setenforce command until after the system is already booted and your process has already failed to start.

@vchong: does that actually work? It was my understanding that capabilities NET_ADMIN was just to retain privileges when you are switching to an otherwise less privileged context, so the context you are coming from would have to already have those privileges.

@doitright I thought it does, but I just tried removing it and the executable still works! I’m sure it didn’t work before. Not sure what’s changed since then. :? Will have to investigate more later if possible. In any case, looks like netdomain or net_domain is the right approach to use. Good to know and thanks!

@foo fyi

@doitright Yep, typeattribute my_context netdomin solved the issue.

How do you know what to do in case of neverallows ? Is it by experience or is there a really good documentation that you could point me to ?

You have to read the code. If it gets choked up with a neverallow, it does you the courtesy of telling you the file path and line number where that rule is located, so you read it and see what exceptions, if any, it offers. If it offers you an exception that you can use, then go ahead and use it, like in this case, it offered an exception for ‘netdomain’ (you can see the ‘-’ sign, can be read as “except for”). If not, then you need to rethink your approach to what problem you are trying to solve.

yes, got it. Thanks for the help :slight_smile: