init: run vendor commands in a separate SELinux context

One of the major aspects of treble is the compartmentalization of system
and vendor components, however init leaves a huge gap here, as vendor
init scripts run in the same context as system init scripts and thus can
access and modify the same properties, files, etc as the system can.

This change is meant to close that gap.  It forks a separate 'subcontext'
init that runs in a different SELinux context with permissions that match
what vendors should have access to.  Commands get sent over a socket to
this 'subcontext' init that then runs them in this SELinux context and
returns the result.

Note that not all commands run in the subcontext; some commands such as
those dealing with services only make sense in the context of the main
init process.

Bug: 62875318
Test: init unit tests, boot bullhead, boot sailfish

Change-Id: Idf4a4ebf98842d27b8627f901f961ab9eb412aee
diff --git a/init/builtins.cpp b/init/builtins.cpp
index be24573..60c0007 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -66,6 +66,7 @@
 #include "reboot.h"
 #include "rlimit_parser.h"
 #include "service.h"
+#include "subcontext.h"
 #include "util.h"
 
 using namespace std::literals::string_literals;
@@ -95,36 +96,36 @@
     }
 }
 
-static Result<Success> do_class_start(const std::vector<std::string>& args) {
+static Result<Success> do_class_start(const BuiltinArguments& args) {
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
     return Success();
 }
 
-static Result<Success> do_class_stop(const std::vector<std::string>& args) {
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
     return Success();
 }
 
-static Result<Success> do_class_reset(const std::vector<std::string>& args) {
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Reset);
     return Success();
 }
 
-static Result<Success> do_class_restart(const std::vector<std::string>& args) {
+static Result<Success> do_class_restart(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Restart);
     return Success();
 }
 
-static Result<Success> do_domainname(const std::vector<std::string>& args) {
+static Result<Success> do_domainname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
     return Success();
 }
 
-static Result<Success> do_enable(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
@@ -135,8 +136,8 @@
     return Success();
 }
 
-static Result<Success> do_exec(const std::vector<std::string>& args) {
-    auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec service";
     }
@@ -148,8 +149,8 @@
     return Success();
 }
 
-static Result<Success> do_exec_background(const std::vector<std::string>& args) {
-    auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec_background(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
         return Error() << "Could not create exec background service";
     }
@@ -161,7 +162,7 @@
     return Success();
 }
 
-static Result<Success> do_exec_start(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
         return Error() << "Service not found";
@@ -174,21 +175,21 @@
     return Success();
 }
 
-static Result<Success> do_export(const std::vector<std::string>& args) {
+static Result<Success> do_export(const BuiltinArguments& args) {
     if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
         return ErrnoError() << "setenv() failed";
     }
     return Success();
 }
 
-static Result<Success> do_hostname(const std::vector<std::string>& args) {
+static Result<Success> do_hostname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
     }
     return Success();
 }
 
-static Result<Success> do_ifup(const std::vector<std::string>& args) {
+static Result<Success> do_ifup(const BuiltinArguments& args) {
     struct ifreq ifr;
 
     strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
@@ -209,7 +210,7 @@
     return Success();
 }
 
-static Result<Success> do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -231,7 +232,7 @@
 }
 
 // mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const std::vector<std::string>& args) {
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -287,7 +288,7 @@
 }
 
 /* umount <path> */
-static Result<Success> do_umount(const std::vector<std::string>& args) {
+static Result<Success> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
         return ErrnoError() << "umount() failed";
     }
@@ -319,7 +320,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const std::vector<std::string>& args) {
+static Result<Success> do_mount(const BuiltinArguments& args) {
     const char* options = nullptr;
     unsigned flags = 0;
     bool wait = false;
@@ -511,7 +512,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static Result<Success> do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const BuiltinArguments& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
@@ -544,7 +545,7 @@
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
-        import_late(args, 2, path_arg_end);
+        import_late(args.args, 2, path_arg_end);
     }
 
     if (queue_event) {
@@ -559,7 +560,7 @@
     return Success();
 }
 
-static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
     struct fstab *fstab;
     int ret;
 
@@ -571,13 +572,13 @@
     return Success();
 }
 
-static Result<Success> do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const BuiltinArguments& args) {
     property_set(args[1], args[2]);
     return Success();
 }
 
-static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
-    auto rlimit = ParseRlimit(args);
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+    auto rlimit = ParseRlimit(args.args);
     if (!rlimit) return rlimit.error();
 
     if (setrlimit(rlimit->first, &rlimit->second) == -1) {
@@ -586,7 +587,7 @@
     return Success();
 }
 
-static Result<Success> do_start(const std::vector<std::string>& args) {
+static Result<Success> do_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     if (auto result = svc->Start(); !result) {
@@ -595,26 +596,26 @@
     return Success();
 }
 
-static Result<Success> do_stop(const std::vector<std::string>& args) {
+static Result<Success> do_stop(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
     return Success();
 }
 
-static Result<Success> do_restart(const std::vector<std::string>& args) {
+static Result<Success> do_restart(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
     return Success();
 }
 
-static Result<Success> do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return Success();
 }
 
-static Result<Success> do_symlink(const std::vector<std::string>& args) {
+static Result<Success> do_symlink(const BuiltinArguments& args) {
     if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
         // The symlink builtin is often used to create symlinks for older devices to be backwards
         // compatible with new paths, therefore we skip reporting this error.
@@ -626,21 +627,21 @@
     return Success();
 }
 
-static Result<Success> do_rm(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const BuiltinArguments& args) {
     if (unlink(args[1].c_str()) < 0) {
         return ErrnoError() << "unlink() failed";
     }
     return Success();
 }
 
-static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
     if (rmdir(args[1].c_str()) < 0) {
         return ErrnoError() << "rmdir() failed";
     }
     return Success();
 }
 
-static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
     struct timezone tz = {};
     if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
         return Error() << "Unable to parse mins_west_of_gmt";
@@ -652,7 +653,7 @@
     return Success();
 }
 
-static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
     int mode = -1;
     bool loaded = fs_mgr_load_verity_state(&mode);
     if (loaded && mode != VERITY_MODE_DEFAULT) {
@@ -668,14 +669,14 @@
     property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
-static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
     if (!fs_mgr_update_verity_state(verity_update_property)) {
         return Error() << "fs_mgr_update_verity_state() failed";
     }
     return Success();
 }
 
-static Result<Success> do_write(const std::vector<std::string>& args) {
+static Result<Success> do_write(const BuiltinArguments& args) {
     if (auto result = WriteFile(args[1], args[2]); !result) {
         return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
@@ -706,7 +707,7 @@
     return Success();
 }
 
-static Result<Success> do_readahead(const std::vector<std::string>& args) {
+static Result<Success> do_readahead(const BuiltinArguments& args) {
     struct stat sb;
 
     if (stat(args[1].c_str(), &sb)) {
@@ -765,7 +766,7 @@
     return Success();
 }
 
-static Result<Success> do_copy(const std::vector<std::string>& args) {
+static Result<Success> do_copy(const BuiltinArguments& args) {
     auto file_contents = ReadFile(args[1]);
     if (!file_contents) {
         return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -777,7 +778,7 @@
     return Success();
 }
 
-static Result<Success> do_chown(const std::vector<std::string>& args) {
+static Result<Success> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -814,7 +815,7 @@
     return mode;
 }
 
-static Result<Success> do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return ErrnoError() << "fchmodat() failed";
@@ -822,7 +823,7 @@
     return Success();
 }
 
-static Result<Success> do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -864,13 +865,13 @@
     return Success();
 }
 
-static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
-    std::vector<std::string> non_const_args(args);
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+    std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon(non_const_args);
+    return do_restorecon({std::move(non_const_args), args.context});
 }
 
-static Result<Success> do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -891,17 +892,17 @@
     return Success();
 }
 
-static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
     load_persist_props();
     return Success();
 }
 
-static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
     load_system_props();
     return Success();
 }
 
-static Result<Success> do_wait(const std::vector<std::string>& args) {
+static Result<Success> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
         int timeout_int;
@@ -918,7 +919,7 @@
     return Success();
 }
 
-static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
@@ -939,7 +940,7 @@
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<Success> do_installkey(const std::vector<std::string>& args) {
+static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
     auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
@@ -948,64 +949,66 @@
     }
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "enablefilecrypto"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
-static Result<Success> do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const BuiltinArguments& args) {
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "init_user0"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
-        {"bootchart",               {1,     1,    do_bootchart}},
-        {"chmod",                   {2,     2,    do_chmod}},
-        {"chown",                   {2,     3,    do_chown}},
-        {"class_reset",             {1,     1,    do_class_reset}},
-        {"class_restart",           {1,     1,    do_class_restart}},
-        {"class_start",             {1,     1,    do_class_start}},
-        {"class_stop",              {1,     1,    do_class_stop}},
-        {"copy",                    {2,     2,    do_copy}},
-        {"domainname",              {1,     1,    do_domainname}},
-        {"enable",                  {1,     1,    do_enable}},
-        {"exec",                    {1,     kMax, do_exec}},
-        {"exec_background",         {1,     kMax, do_exec_background}},
-        {"exec_start",              {1,     1,    do_exec_start}},
-        {"export",                  {2,     2,    do_export}},
-        {"hostname",                {1,     1,    do_hostname}},
-        {"ifup",                    {1,     1,    do_ifup}},
-        {"init_user0",              {0,     0,    do_init_user0}},
-        {"insmod",                  {1,     kMax, do_insmod}},
-        {"installkey",              {1,     1,    do_installkey}},
-        {"load_persist_props",      {0,     0,    do_load_persist_props}},
-        {"load_system_props",       {0,     0,    do_load_system_props}},
-        {"loglevel",                {1,     1,    do_loglevel}},
-        {"mkdir",                   {1,     4,    do_mkdir}},
-        {"mount_all",               {1,     kMax, do_mount_all}},
-        {"mount",                   {3,     kMax, do_mount}},
-        {"umount",                  {1,     1,    do_umount}},
-        {"readahead",               {1,     2,    do_readahead}},
-        {"restart",                 {1,     1,    do_restart}},
-        {"restorecon",              {1,     kMax, do_restorecon}},
-        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
-        {"rm",                      {1,     1,    do_rm}},
-        {"rmdir",                   {1,     1,    do_rmdir}},
-        {"setprop",                 {2,     2,    do_setprop}},
-        {"setrlimit",               {3,     3,    do_setrlimit}},
-        {"start",                   {1,     1,    do_start}},
-        {"stop",                    {1,     1,    do_stop}},
-        {"swapon_all",              {1,     1,    do_swapon_all}},
-        {"symlink",                 {2,     2,    do_symlink}},
-        {"sysclktz",                {1,     1,    do_sysclktz}},
-        {"trigger",                 {1,     1,    do_trigger}},
-        {"verity_load_state",       {0,     0,    do_verity_load_state}},
-        {"verity_update_state",     {0,     0,    do_verity_update_state}},
-        {"wait",                    {1,     2,    do_wait}},
-        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
-        {"write",                   {2,     2,    do_write}},
+        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
+        {"chmod",                   {2,     2,    {true,   do_chmod}}},
+        {"chown",                   {2,     3,    {true,   do_chown}}},
+        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
+        {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
+        {"copy",                    {2,     2,    {true,   do_copy}}},
+        {"domainname",              {1,     1,    {true,   do_domainname}}},
+        {"enable",                  {1,     1,    {false,  do_enable}}},
+        {"exec",                    {1,     kMax, {false,  do_exec}}},
+        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
+        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
+        {"export",                  {2,     2,    {false,  do_export}}},
+        {"hostname",                {1,     1,    {true,   do_hostname}}},
+        {"ifup",                    {1,     1,    {true,   do_ifup}}},
+        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
+        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
+        {"installkey",              {1,     1,    {false,  do_installkey}}},
+        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
+        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
+        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
+        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
+        {"mount",                   {3,     kMax, {false,  do_mount}}},
+        {"umount",                  {1,     1,    {false,  do_umount}}},
+        {"readahead",               {1,     2,    {true,   do_readahead}}},
+        {"restart",                 {1,     1,    {false,  do_restart}}},
+        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
+        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
+        {"rm",                      {1,     1,    {true,   do_rm}}},
+        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
+        //  TODO: setprop should be run in the subcontext, but property service needs to be split
+        //        out from init before that is possible.
+        {"setprop",                 {2,     2,    {false,  do_setprop}}},
+        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
+        {"start",                   {1,     1,    {false,  do_start}}},
+        {"stop",                    {1,     1,    {false,  do_stop}}},
+        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"symlink",                 {2,     2,    {true,   do_symlink}}},
+        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
+        {"trigger",                 {1,     1,    {false,  do_trigger}}},
+        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
+        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
+        {"wait",                    {1,     2,    {true,   do_wait}}},
+        {"wait_for_prop",           {2,     2,    {true,   do_wait_for_prop}}},
+        {"write",                   {2,     2,    {true,   do_write}}},
     };
     // clang-format on
     return builtin_functions;