diff options
author | Tom Cherry | 2018-05-22 12:13:41 -0500 |
---|---|---|
committer | Android (Google) Code Review | 2018-05-22 12:13:41 -0500 |
commit | 9ad11c0eee3ef71efe3bf029338d36ae03b197f7 (patch) | |
tree | 06f0846fe9f4b09ec2018e8d21f7a1ee04b4b8b9 | |
parent | 1ddef8852ac7687ade8455e04b2e4082d1e0e7e5 (diff) | |
parent | 2fa7451e9b530f814f93ca2248fa425b1f5362cd (diff) | |
download | platform-system-core-9ad11c0eee3ef71efe3bf029338d36ae03b197f7.tar.gz platform-system-core-9ad11c0eee3ef71efe3bf029338d36ae03b197f7.tar.xz platform-system-core-9ad11c0eee3ef71efe3bf029338d36ae03b197f7.zip |
Merge "init: allow entering of network namespaces" into pi-dev
-rw-r--r-- | init/README.md | 4 | ||||
-rw-r--r-- | init/service.cpp | 92 | ||||
-rw-r--r-- | init/service.h | 6 |
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; | |||
59 | using android::base::ParseInt; | 60 | using android::base::ParseInt; |
60 | using android::base::StartsWith; | 61 | using android::base::StartsWith; |
61 | using android::base::StringPrintf; | 62 | using android::base::StringPrintf; |
63 | using android::base::unique_fd; | ||
62 | using android::base::WriteStringToFile; | 64 | using android::base::WriteStringToFile; |
63 | 65 | ||
64 | namespace android { | 66 | namespace android { |
65 | namespace init { | 67 | namespace init { |
66 | 68 | ||
67 | static Result<std::string> ComputeContextFromExecutable(std::string& service_name, | 69 | static 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 | ||
104 | static void SetUpPidNamespace(const std::string& service_name) { | 105 | Result<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) { | 140 | Result<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 | |||
173 | Result<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 | ||
158 | static bool ExpandArgsAndExecv(const std::vector<std::string>& args) { | 186 | static 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 | ||
449 | Result<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 | |||
421 | Result<Success> Service::ParseGroup(const std::vector<std::string>& args) { | 463 | Result<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 | ||