blob: b8c2fd2f31cd479d035efd8e5228aa039a087cbf [file] [log] [blame]
David Anderson491e4da2020-12-08 00:21:20 -08001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "snapuserd_transition.h"
18
19#include <sys/mman.h>
20#include <sys/socket.h>
21#include <sys/syscall.h>
22#include <sys/xattr.h>
23#include <unistd.h>
24
25#include <filesystem>
26#include <string>
David Anderson9fd88622021-03-05 14:10:55 -080027#include <string_view>
David Anderson491e4da2020-12-08 00:21:20 -080028
29#include <android-base/file.h>
30#include <android-base/logging.h>
31#include <android-base/parseint.h>
32#include <android-base/strings.h>
33#include <android-base/unique_fd.h>
34#include <cutils/sockets.h>
35#include <libsnapshot/snapshot.h>
David Anderson491e4da2020-12-08 00:21:20 -080036#include <private/android_filesystem_config.h>
David Anderson9fd88622021-03-05 14:10:55 -080037#include <procinfo/process_map.h>
David Anderson491e4da2020-12-08 00:21:20 -080038#include <selinux/android.h>
Akilesh Kailash36aeeb32021-07-26 06:59:18 +000039#include <snapuserd/snapuserd_client.h>
David Anderson491e4da2020-12-08 00:21:20 -080040
41#include "block_dev_initializer.h"
42#include "service_utils.h"
43#include "util.h"
44
45namespace android {
46namespace init {
47
48using namespace std::string_literals;
49
50using android::base::unique_fd;
51using android::snapshot::SnapshotManager;
52using android::snapshot::SnapuserdClient;
53
54static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
55static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
56static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
David Anderson0e5ad5a2021-07-21 21:53:28 -070057static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO";
David Anderson491e4da2020-12-08 00:21:20 -080058static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
59static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
60
61void LaunchFirstStageSnapuserd() {
62 SocketDescriptor socket_desc;
63 socket_desc.name = android::snapshot::kSnapuserdSocket;
64 socket_desc.type = SOCK_STREAM;
65 socket_desc.perm = 0660;
66 socket_desc.uid = AID_SYSTEM;
67 socket_desc.gid = AID_SYSTEM;
68
69 // We specify a label here even though it technically is not needed. During
70 // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,
71 // we bypass the socket entirely.
72 auto socket = socket_desc.Create(kSnapuserdSocketLabel);
73 if (!socket.ok()) {
74 LOG(FATAL) << "Could not create snapuserd socket: " << socket.error();
75 }
76
77 pid_t pid = fork();
78 if (pid < 0) {
79 PLOG(FATAL) << "Cannot launch snapuserd; fork failed";
80 }
81 if (pid == 0) {
82 socket->Publish();
83 char arg0[] = "/system/bin/snapuserd";
84 char* const argv[] = {arg0, nullptr};
85 if (execv(arg0, argv) < 0) {
86 PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
87 }
88 _exit(127);
89 }
90
David Anderson0e5ad5a2021-07-21 21:53:28 -070091 auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
92 if (!client) {
93 LOG(FATAL) << "Could not connect to first-stage snapuserd";
94 }
95 if (client->SupportsSecondStageSocketHandoff()) {
96 setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
97 }
98
David Anderson491e4da2020-12-08 00:21:20 -080099 setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
100
101 LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
102}
103
104std::optional<pid_t> GetSnapuserdFirstStagePid() {
105 const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
106 if (!pid_str) {
107 return {};
108 }
109
110 int pid = 0;
111 if (!android::base::ParseInt(pid_str, &pid)) {
112 LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "="
113 << pid_str;
114 }
115 return {pid};
116}
117
118static void RelabelLink(const std::string& link) {
119 selinux_android_restorecon(link.c_str(), 0);
120
121 std::string path;
122 if (android::base::Readlink(link, &path)) {
123 selinux_android_restorecon(path.c_str(), 0);
124 }
125}
126
127static void RelabelDeviceMapper() {
128 selinux_android_restorecon("/dev/device-mapper", 0);
129
130 std::error_code ec;
131 for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) {
132 const auto& path = iter.path();
133 if (android::base::StartsWith(path.string(), "/dev/block/dm-")) {
134 selinux_android_restorecon(path.string().c_str(), 0);
135 }
136 }
137}
138
139static std::optional<int> GetRamdiskSnapuserdFd() {
140 const char* fd_str = getenv(kSnapuserdFirstStageFdVar);
141 if (!fd_str) {
142 return {};
143 }
144
145 int fd;
146 if (!android::base::ParseInt(fd_str, &fd)) {
147 LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "="
148 << fd_str;
149 }
150 return {fd};
151}
152
153void RestoreconRamdiskSnapuserd(int fd) {
154 if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {
155 PLOG(FATAL) << "fsetxattr snapuserd failed";
156 }
157}
158
159SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)
160 : sm_(std::move(sm)), old_pid_(old_pid) {
161 // Only dm-user device names change during transitions, so the other
162 // devices are expected to be present.
163 sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {
164 if (android::base::StartsWith(device, "/dev/dm-user/")) {
165 return block_dev_init_.InitDmUser(android::base::Basename(device));
166 }
167 return true;
168 });
169}
170
David Anderson9fd88622021-03-05 14:10:55 -0800171static void LockAllSystemPages() {
172 bool ok = true;
173 auto callback = [&](const android::procinfo::MapInfo& map) -> void {
174 if (!ok || android::base::StartsWith(map.name, "/dev/") ||
175 !android::base::StartsWith(map.name, "/")) {
176 return;
177 }
178 auto start = reinterpret_cast<const void*>(map.start);
179 auto len = map.end - map.start;
180 if (!len) {
181 return;
182 }
183 if (mlock(start, len) < 0) {
184 LOG(ERROR) << "mlock failed, " << start << " for " << len << " bytes.";
185 ok = false;
186 }
187 };
188
189 if (!android::procinfo::ReadProcessMaps(getpid(), callback) || !ok) {
190 LOG(FATAL) << "Could not process /proc/" << getpid() << "/maps file for init, "
191 << "falling back to mlockall().";
192 if (mlockall(MCL_CURRENT) < 0) {
193 LOG(FATAL) << "mlockall failed";
194 }
195 }
196}
197
David Anderson491e4da2020-12-08 00:21:20 -0800198void SnapuserdSelinuxHelper::StartTransition() {
199 LOG(INFO) << "Starting SELinux transition of snapuserd";
200
201 // The restorecon path reads from /system etc, so make sure any reads have
202 // been cached before proceeding.
203 auto handle = selinux_android_file_context_handle();
204 if (!handle) {
205 LOG(FATAL) << "Could not create SELinux file context handle";
206 }
207 selinux_android_set_sehandle(handle);
208
209 // We cannot access /system after the transition, so make sure init is
210 // pinned in memory.
David Anderson9fd88622021-03-05 14:10:55 -0800211 LockAllSystemPages();
David Anderson491e4da2020-12-08 00:21:20 -0800212
213 argv_.emplace_back("snapuserd");
214 argv_.emplace_back("-no_socket");
215 if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
216 LOG(FATAL) << "Could not perform selinux transition";
217 }
218
219 // Make sure the process is gone so we don't have any selinux audits.
220 KillFirstStageSnapuserd(old_pid_);
221}
222
223void SnapuserdSelinuxHelper::FinishTransition() {
224 RelabelLink("/dev/block/by-name/super");
225 RelabelDeviceMapper();
226
227 selinux_android_restorecon("/dev/null", 0);
228 selinux_android_restorecon("/dev/urandom", 0);
229 selinux_android_restorecon("/dev/kmsg", 0);
230 selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
231
232 RelaunchFirstStageSnapuserd();
233
234 if (munlockall() < 0) {
235 PLOG(ERROR) << "munlockall failed";
236 }
237}
238
239void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
240 auto fd = GetRamdiskSnapuserdFd();
241 if (!fd) {
242 LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
243 }
244 unsetenv(kSnapuserdFirstStageFdVar);
245
246 RestoreconRamdiskSnapuserd(fd.value());
247
248 pid_t pid = fork();
249 if (pid < 0) {
250 PLOG(FATAL) << "Fork to relaunch snapuserd failed";
251 }
252 if (pid > 0) {
253 // We don't need the descriptor anymore, and it should be closed to
254 // avoid leaking into subprocesses.
255 close(fd.value());
256
257 setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
258
259 LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
260 return;
261 }
262
263 // Make sure the descriptor is gone after we exec.
264 if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {
265 PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd";
266 }
267
268 std::vector<char*> argv;
269 for (auto& arg : argv_) {
270 argv.emplace_back(arg.data());
271 }
272 argv.emplace_back(nullptr);
273
274 int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()),
275 nullptr, AT_EMPTY_PATH);
276 if (rv < 0) {
277 PLOG(FATAL) << "Failed to execveat() snapuserd";
278 }
279}
280
281std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {
282 if (IsRecoveryMode()) {
283 return nullptr;
284 }
285
286 auto old_pid = GetSnapuserdFirstStagePid();
287 if (!old_pid) {
288 return nullptr;
289 }
290
291 auto sm = SnapshotManager::NewForFirstStageMount();
292 if (!sm) {
293 LOG(FATAL) << "Unable to create SnapshotManager";
294 }
295 return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());
296}
297
298void KillFirstStageSnapuserd(pid_t pid) {
299 if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
300 LOG(ERROR) << "Kill snapuserd pid failed: " << pid;
301 } else {
302 LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid;
303 }
304}
305
306void CleanupSnapuserdSocket() {
307 auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket;
308 if (access(socket_path.c_str(), F_OK) != 0) {
309 return;
310 }
311
312 // Tell the daemon to stop accepting connections and to gracefully exit
313 // once all outstanding handlers have terminated.
314 if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {
315 client->DetachSnapuserd();
316 }
317
318 // Unlink the socket so we can create it again in second-stage.
319 if (unlink(socket_path.c_str()) < 0) {
320 PLOG(FATAL) << "unlink " << socket_path << " failed";
321 }
322}
323
324void SaveRamdiskPathToSnapuserd() {
325 int fd = open(kSnapuserdPath, O_PATH);
326 if (fd < 0) {
327 PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath;
328 }
329
330 auto value = std::to_string(fd);
331 if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {
332 PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value;
333 }
334}
335
336bool IsFirstStageSnapuserdRunning() {
337 return GetSnapuserdFirstStagePid().has_value();
338}
339
David Anderson0e5ad5a2021-07-21 21:53:28 -0700340std::vector<std::string> GetSnapuserdFirstStageInfo() {
341 const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);
342 if (!pid_str) {
343 return {};
344 }
345 return android::base::Split(pid_str, ",");
346}
347
David Anderson491e4da2020-12-08 00:21:20 -0800348} // namespace init
349} // namespace android