blob: 19b5c57a06aedd1b7f0a78250869e939035e61a2 [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>
27
28#include <android-base/file.h>
29#include <android-base/logging.h>
30#include <android-base/parseint.h>
31#include <android-base/strings.h>
32#include <android-base/unique_fd.h>
33#include <cutils/sockets.h>
34#include <libsnapshot/snapshot.h>
35#include <libsnapshot/snapuserd_client.h>
36#include <private/android_filesystem_config.h>
37#include <selinux/android.h>
38
39#include "block_dev_initializer.h"
40#include "service_utils.h"
41#include "util.h"
42
43namespace android {
44namespace init {
45
46using namespace std::string_literals;
47
48using android::base::unique_fd;
49using android::snapshot::SnapshotManager;
50using android::snapshot::SnapuserdClient;
51
52static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
53static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
54static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
55static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
56static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
57
58void LaunchFirstStageSnapuserd() {
59 SocketDescriptor socket_desc;
60 socket_desc.name = android::snapshot::kSnapuserdSocket;
61 socket_desc.type = SOCK_STREAM;
62 socket_desc.perm = 0660;
63 socket_desc.uid = AID_SYSTEM;
64 socket_desc.gid = AID_SYSTEM;
65
66 // We specify a label here even though it technically is not needed. During
67 // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,
68 // we bypass the socket entirely.
69 auto socket = socket_desc.Create(kSnapuserdSocketLabel);
70 if (!socket.ok()) {
71 LOG(FATAL) << "Could not create snapuserd socket: " << socket.error();
72 }
73
74 pid_t pid = fork();
75 if (pid < 0) {
76 PLOG(FATAL) << "Cannot launch snapuserd; fork failed";
77 }
78 if (pid == 0) {
79 socket->Publish();
80 char arg0[] = "/system/bin/snapuserd";
81 char* const argv[] = {arg0, nullptr};
82 if (execv(arg0, argv) < 0) {
83 PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
84 }
85 _exit(127);
86 }
87
88 setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
89
90 LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
91}
92
93std::optional<pid_t> GetSnapuserdFirstStagePid() {
94 const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
95 if (!pid_str) {
96 return {};
97 }
98
99 int pid = 0;
100 if (!android::base::ParseInt(pid_str, &pid)) {
101 LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "="
102 << pid_str;
103 }
104 return {pid};
105}
106
107static void RelabelLink(const std::string& link) {
108 selinux_android_restorecon(link.c_str(), 0);
109
110 std::string path;
111 if (android::base::Readlink(link, &path)) {
112 selinux_android_restorecon(path.c_str(), 0);
113 }
114}
115
116static void RelabelDeviceMapper() {
117 selinux_android_restorecon("/dev/device-mapper", 0);
118
119 std::error_code ec;
120 for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) {
121 const auto& path = iter.path();
122 if (android::base::StartsWith(path.string(), "/dev/block/dm-")) {
123 selinux_android_restorecon(path.string().c_str(), 0);
124 }
125 }
126}
127
128static std::optional<int> GetRamdiskSnapuserdFd() {
129 const char* fd_str = getenv(kSnapuserdFirstStageFdVar);
130 if (!fd_str) {
131 return {};
132 }
133
134 int fd;
135 if (!android::base::ParseInt(fd_str, &fd)) {
136 LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "="
137 << fd_str;
138 }
139 return {fd};
140}
141
142void RestoreconRamdiskSnapuserd(int fd) {
143 if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {
144 PLOG(FATAL) << "fsetxattr snapuserd failed";
145 }
146}
147
148SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)
149 : sm_(std::move(sm)), old_pid_(old_pid) {
150 // Only dm-user device names change during transitions, so the other
151 // devices are expected to be present.
152 sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {
153 if (android::base::StartsWith(device, "/dev/dm-user/")) {
154 return block_dev_init_.InitDmUser(android::base::Basename(device));
155 }
156 return true;
157 });
158}
159
160void SnapuserdSelinuxHelper::StartTransition() {
161 LOG(INFO) << "Starting SELinux transition of snapuserd";
162
163 // The restorecon path reads from /system etc, so make sure any reads have
164 // been cached before proceeding.
165 auto handle = selinux_android_file_context_handle();
166 if (!handle) {
167 LOG(FATAL) << "Could not create SELinux file context handle";
168 }
169 selinux_android_set_sehandle(handle);
170
171 // We cannot access /system after the transition, so make sure init is
172 // pinned in memory.
173 if (mlockall(MCL_CURRENT) < 0) {
174 LOG(FATAL) << "mlockall failed";
175 }
176
177 argv_.emplace_back("snapuserd");
178 argv_.emplace_back("-no_socket");
179 if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
180 LOG(FATAL) << "Could not perform selinux transition";
181 }
182
183 // Make sure the process is gone so we don't have any selinux audits.
184 KillFirstStageSnapuserd(old_pid_);
185}
186
187void SnapuserdSelinuxHelper::FinishTransition() {
188 RelabelLink("/dev/block/by-name/super");
189 RelabelDeviceMapper();
190
191 selinux_android_restorecon("/dev/null", 0);
192 selinux_android_restorecon("/dev/urandom", 0);
193 selinux_android_restorecon("/dev/kmsg", 0);
194 selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
195
196 RelaunchFirstStageSnapuserd();
197
198 if (munlockall() < 0) {
199 PLOG(ERROR) << "munlockall failed";
200 }
201}
202
203void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
204 auto fd = GetRamdiskSnapuserdFd();
205 if (!fd) {
206 LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
207 }
208 unsetenv(kSnapuserdFirstStageFdVar);
209
210 RestoreconRamdiskSnapuserd(fd.value());
211
212 pid_t pid = fork();
213 if (pid < 0) {
214 PLOG(FATAL) << "Fork to relaunch snapuserd failed";
215 }
216 if (pid > 0) {
217 // We don't need the descriptor anymore, and it should be closed to
218 // avoid leaking into subprocesses.
219 close(fd.value());
220
221 setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
222
223 LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
224 return;
225 }
226
227 // Make sure the descriptor is gone after we exec.
228 if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {
229 PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd";
230 }
231
232 std::vector<char*> argv;
233 for (auto& arg : argv_) {
234 argv.emplace_back(arg.data());
235 }
236 argv.emplace_back(nullptr);
237
238 int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()),
239 nullptr, AT_EMPTY_PATH);
240 if (rv < 0) {
241 PLOG(FATAL) << "Failed to execveat() snapuserd";
242 }
243}
244
245std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {
246 if (IsRecoveryMode()) {
247 return nullptr;
248 }
249
250 auto old_pid = GetSnapuserdFirstStagePid();
251 if (!old_pid) {
252 return nullptr;
253 }
254
255 auto sm = SnapshotManager::NewForFirstStageMount();
256 if (!sm) {
257 LOG(FATAL) << "Unable to create SnapshotManager";
258 }
259 return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());
260}
261
262void KillFirstStageSnapuserd(pid_t pid) {
263 if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
264 LOG(ERROR) << "Kill snapuserd pid failed: " << pid;
265 } else {
266 LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid;
267 }
268}
269
270void CleanupSnapuserdSocket() {
271 auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket;
272 if (access(socket_path.c_str(), F_OK) != 0) {
273 return;
274 }
275
276 // Tell the daemon to stop accepting connections and to gracefully exit
277 // once all outstanding handlers have terminated.
278 if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {
279 client->DetachSnapuserd();
280 }
281
282 // Unlink the socket so we can create it again in second-stage.
283 if (unlink(socket_path.c_str()) < 0) {
284 PLOG(FATAL) << "unlink " << socket_path << " failed";
285 }
286}
287
288void SaveRamdiskPathToSnapuserd() {
289 int fd = open(kSnapuserdPath, O_PATH);
290 if (fd < 0) {
291 PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath;
292 }
293
294 auto value = std::to_string(fd);
295 if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {
296 PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value;
297 }
298}
299
300bool IsFirstStageSnapuserdRunning() {
301 return GetSnapuserdFirstStagePid().has_value();
302}
303
304} // namespace init
305} // namespace android