summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Cherry2018-04-20 18:18:12 -0500
committerTom Cherry2018-05-21 10:52:13 -0500
commit2fa7451e9b530f814f93ca2248fa425b1f5362cd (patch)
treeed598b5b9cdc2646be490d3058292562c7cc509b
parent7905b1e16286653679b7ff9b595f3ed7de5e182f (diff)
downloadplatform-system-core-2fa7451e9b530f814f93ca2248fa425b1f5362cd.tar.gz
platform-system-core-2fa7451e9b530f814f93ca2248fa425b1f5362cd.tar.xz
platform-system-core-2fa7451e9b530f814f93ca2248fa425b1f5362cd.zip
init: allow entering of network namespaces
Add the ability to enter a network namespace when launching a service. Typical usage of this would be something similar to the below: on fs exec ip netns add namespace_name service vendor_something /vendor/... capabilities <lower than root> user not_root enter_namespace net /mnt/.../namespace_name Note changes to the `ip` tool are needed to create the namespace in the correct directory. Bug: 73334854 Test: auto team verified Merged-In: Ifa91c873d36d69db399bb9c04ff2362518a0b07d Change-Id: Ifa91c873d36d69db399bb9c04ff2362518a0b07d (cherry picked from commit aead51b418e70a49191bc0cc5b67c92c969ae699)
-rw-r--r--init/README.md4
-rw-r--r--init/service.cpp92
-rw-r--r--init/service.h6
3 files changed, 85 insertions, 17 deletions
diff --git a/init/README.md b/init/README.md
index 5c2352b9f..90994279e 100644
--- a/init/README.md
+++ b/init/README.md
@@ -187,6 +187,10 @@ runs the service.
187 seclabel or computed based on the service executable file security context. 187 seclabel or computed based on the service executable file security context.
188 For native executables see libcutils android\_get\_control\_socket(). 188 For native executables see libcutils android\_get\_control\_socket().
189 189
190`enter_namespace <type> <path>`
191> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
192 _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
193
190`file <path> <type>` 194`file <path> <type>`
191> Open a file path and pass its fd to the launched process. _type_ must be 195> Open a file path and pass its fd to the launched process. _type_ must be
192 "r", "w" or "rw". For native executables see libcutils 196 "r", "w" or "rw". For native executables see libcutils
diff --git a/init/service.cpp b/init/service.cpp
index 09d8dae11..37d3a8807 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
34#include <android-base/parseint.h> 34#include <android-base/parseint.h>
35#include <android-base/stringprintf.h> 35#include <android-base/stringprintf.h>
36#include <android-base/strings.h> 36#include <android-base/strings.h>
37#include <android-base/unique_fd.h>
37#include <hidl-util/FQName.h> 38#include <hidl-util/FQName.h>
38#include <processgroup/processgroup.h> 39#include <processgroup/processgroup.h>
39#include <selinux/selinux.h> 40#include <selinux/selinux.h>
@@ -59,13 +60,13 @@ using android::base::Join;
59using android::base::ParseInt; 60using android::base::ParseInt;
60using android::base::StartsWith; 61using android::base::StartsWith;
61using android::base::StringPrintf; 62using android::base::StringPrintf;
63using android::base::unique_fd;
62using android::base::WriteStringToFile; 64using android::base::WriteStringToFile;
63 65
64namespace android { 66namespace android {
65namespace init { 67namespace init {
66 68
67static Result<std::string> ComputeContextFromExecutable(std::string& service_name, 69static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
68 const std::string& service_path) {
69 std::string computed_context; 70 std::string computed_context;
70 71
71 char* raw_con = nullptr; 72 char* raw_con = nullptr;
@@ -101,36 +102,49 @@ static Result<std::string> ComputeContextFromExecutable(std::string& service_nam
101 return computed_context; 102 return computed_context;
102} 103}
103 104
104static void SetUpPidNamespace(const std::string& service_name) { 105Result<Success> Service::SetUpMountNamespace() const {
105 constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID; 106 constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
106 107
107 // It's OK to LOG(FATAL) in this function since it's running in the first
108 // child process.
109
110 // Recursively remount / as slave like zygote does so unmounting and mounting /proc 108 // Recursively remount / as slave like zygote does so unmounting and mounting /proc
111 // doesn't interfere with the parent namespace's /proc mount. This will also 109 // doesn't interfere with the parent namespace's /proc mount. This will also
112 // prevent any other mounts/unmounts initiated by the service from interfering 110 // prevent any other mounts/unmounts initiated by the service from interfering
113 // with the parent namespace but will still allow mount events from the parent 111 // with the parent namespace but will still allow mount events from the parent
114 // namespace to propagate to the child. 112 // namespace to propagate to the child.
115 if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) { 113 if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
116 PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name; 114 return ErrnoError() << "Could not remount(/) recursively as slave";
117 } 115 }
118 // umount() then mount() /proc. 116
117 // umount() then mount() /proc and/or /sys
119 // Note that it is not sufficient to mount with MS_REMOUNT. 118 // Note that it is not sufficient to mount with MS_REMOUNT.
120 if (umount("/proc") == -1) { 119 if (namespace_flags_ & CLONE_NEWPID) {
121 PLOG(FATAL) << "couldn't umount(/proc) for " << service_name; 120 if (umount("/proc") == -1) {
121 return ErrnoError() << "Could not umount(/proc)";
122 }
123 if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
124 return ErrnoError() << "Could not mount(/proc)";
125 }
122 } 126 }
123 if (mount("", "/proc", "proc", kSafeFlags, "") == -1) { 127 bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
124 PLOG(FATAL) << "couldn't mount(/proc) for " << service_name; 128 [](const auto& entry) { return entry.first == CLONE_NEWNET; });
129 if (remount_sys) {
130 if (umount2("/sys", MNT_DETACH) == -1) {
131 return ErrnoError() << "Could not umount(/sys)";
132 }
133 if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
134 return ErrnoError() << "Could not mount(/sys)";
135 }
125 } 136 }
137 return Success();
138}
126 139
127 if (prctl(PR_SET_NAME, service_name.c_str()) == -1) { 140Result<Success> Service::SetUpPidNamespace() const {
128 PLOG(FATAL) << "couldn't set name for " << service_name; 141 if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
142 return ErrnoError() << "Could not set name";
129 } 143 }
130 144
131 pid_t child_pid = fork(); 145 pid_t child_pid = fork();
132 if (child_pid == -1) { 146 if (child_pid == -1) {
133 PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name; 147 return ErrnoError() << "Could not fork init inside the PID namespace";
134 } 148 }
135 149
136 if (child_pid > 0) { 150 if (child_pid > 0) {
@@ -153,6 +167,20 @@ static void SetUpPidNamespace(const std::string& service_name) {
153 } 167 }
154 _exit(WEXITSTATUS(init_exitstatus)); 168 _exit(WEXITSTATUS(init_exitstatus));
155 } 169 }
170 return Success();
171}
172
173Result<Success> Service::EnterNamespaces() const {
174 for (const auto& [nstype, path] : namespaces_to_enter_) {
175 auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
176 if (!fd) {
177 return ErrnoError() << "Could not open namespace at " << path;
178 }
179 if (setns(fd, nstype) == -1) {
180 return ErrnoError() << "Could not setns() namespace at " << path;
181 }
182 }
183 return Success();
156} 184}
157 185
158static bool ExpandArgsAndExecv(const std::vector<std::string>& args) { 186static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
@@ -418,6 +446,20 @@ Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
418 return Success(); 446 return Success();
419} 447}
420 448
449Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
450 if (args[1] != "net") {
451 return Error() << "Init only supports entering network namespaces";
452 }
453 if (!namespaces_to_enter_.empty()) {
454 return Error() << "Only one network namespace may be entered";
455 }
456 // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
457 // present. Therefore, they also require mount namespaces.
458 namespace_flags_ |= CLONE_NEWNS;
459 namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
460 return Success();
461}
462
421Result<Success> Service::ParseGroup(const std::vector<std::string>& args) { 463Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
422 auto gid = DecodeUid(args[1]); 464 auto gid = DecodeUid(args[1]);
423 if (!gid) { 465 if (!gid) {
@@ -682,6 +724,8 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
682 {"console", {0, 1, &Service::ParseConsole}}, 724 {"console", {0, 1, &Service::ParseConsole}},
683 {"critical", {0, 0, &Service::ParseCritical}}, 725 {"critical", {0, 0, &Service::ParseCritical}},
684 {"disabled", {0, 0, &Service::ParseDisabled}}, 726 {"disabled", {0, 0, &Service::ParseDisabled}},
727 {"enter_namespace",
728 {2, 2, &Service::ParseEnterNamespace}},
685 {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, 729 {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
686 {"interface", {2, 2, &Service::ParseInterface}}, 730 {"interface", {2, 2, &Service::ParseInterface}},
687 {"ioprio", {2, 2, &Service::ParseIoprio}}, 731 {"ioprio", {2, 2, &Service::ParseIoprio}},
@@ -783,7 +827,7 @@ Result<Success> Service::Start() {
783 if (!seclabel_.empty()) { 827 if (!seclabel_.empty()) {
784 scon = seclabel_; 828 scon = seclabel_;
785 } else { 829 } else {
786 auto result = ComputeContextFromExecutable(name_, args_[0]); 830 auto result = ComputeContextFromExecutable(args_[0]);
787 if (!result) { 831 if (!result) {
788 return result.error(); 832 return result.error();
789 } 833 }
@@ -802,10 +846,24 @@ Result<Success> Service::Start() {
802 if (pid == 0) { 846 if (pid == 0) {
803 umask(077); 847 umask(077);
804 848
849 if (auto result = EnterNamespaces(); !result) {
850 LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
851 }
852
853 if (namespace_flags_ & CLONE_NEWNS) {
854 if (auto result = SetUpMountNamespace(); !result) {
855 LOG(FATAL) << "Service '" << name_
856 << "' could not set up mount namespace: " << result.error();
857 }
858 }
859
805 if (namespace_flags_ & CLONE_NEWPID) { 860 if (namespace_flags_ & CLONE_NEWPID) {
806 // This will fork again to run an init process inside the PID 861 // This will fork again to run an init process inside the PID
807 // namespace. 862 // namespace.
808 SetUpPidNamespace(name_); 863 if (auto result = SetUpPidNamespace(); !result) {
864 LOG(FATAL) << "Service '" << name_
865 << "' could not set up PID namespace: " << result.error();
866 }
809 } 867 }
810 868
811 for (const auto& [key, value] : environment_vars_) { 869 for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index bcf194386..87c9ac804 100644
--- a/init/service.h
+++ b/init/service.h
@@ -124,6 +124,9 @@ class Service {
124 using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args); 124 using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
125 class OptionParserMap; 125 class OptionParserMap;
126 126
127 Result<Success> SetUpMountNamespace() const;
128 Result<Success> SetUpPidNamespace() const;
129 Result<Success> EnterNamespaces() const;
127 void NotifyStateChange(const std::string& new_state) const; 130 void NotifyStateChange(const std::string& new_state) const;
128 void StopOrReset(int how); 131 void StopOrReset(int how);
129 void ZapStdio() const; 132 void ZapStdio() const;
@@ -136,6 +139,7 @@ class Service {
136 Result<Success> ParseConsole(const std::vector<std::string>& args); 139 Result<Success> ParseConsole(const std::vector<std::string>& args);
137 Result<Success> ParseCritical(const std::vector<std::string>& args); 140 Result<Success> ParseCritical(const std::vector<std::string>& args);
138 Result<Success> ParseDisabled(const std::vector<std::string>& args); 141 Result<Success> ParseDisabled(const std::vector<std::string>& args);
142 Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
139 Result<Success> ParseGroup(const std::vector<std::string>& args); 143 Result<Success> ParseGroup(const std::vector<std::string>& args);
140 Result<Success> ParsePriority(const std::vector<std::string>& args); 144 Result<Success> ParsePriority(const std::vector<std::string>& args);
141 Result<Success> ParseInterface(const std::vector<std::string>& args); 145 Result<Success> ParseInterface(const std::vector<std::string>& args);
@@ -179,6 +183,8 @@ class Service {
179 std::vector<gid_t> supp_gids_; 183 std::vector<gid_t> supp_gids_;
180 CapSet capabilities_; 184 CapSet capabilities_;
181 unsigned namespace_flags_; 185 unsigned namespace_flags_;
186 // Pair of namespace type, path to namespace.
187 std::vector<std::pair<int, std::string>> namespaces_to_enter_;
182 188
183 std::string seclabel_; 189 std::string seclabel_;
184 190