| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 1 | /* | 
|  | 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 Anderson | 9fd8862 | 2021-03-05 14:10:55 -0800 | [diff] [blame] | 27 | #include <string_view> | 
| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 28 |  | 
|  | 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 Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 36 | #include <private/android_filesystem_config.h> | 
| David Anderson | 9fd8862 | 2021-03-05 14:10:55 -0800 | [diff] [blame] | 37 | #include <procinfo/process_map.h> | 
| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 38 | #include <selinux/android.h> | 
| Akilesh Kailash | 36aeeb3 | 2021-07-26 06:59:18 +0000 | [diff] [blame] | 39 | #include <snapuserd/snapuserd_client.h> | 
| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 40 |  | 
|  | 41 | #include "block_dev_initializer.h" | 
|  | 42 | #include "service_utils.h" | 
|  | 43 | #include "util.h" | 
|  | 44 |  | 
|  | 45 | namespace android { | 
|  | 46 | namespace init { | 
|  | 47 |  | 
|  | 48 | using namespace std::string_literals; | 
|  | 49 |  | 
|  | 50 | using android::base::unique_fd; | 
|  | 51 | using android::snapshot::SnapshotManager; | 
|  | 52 | using android::snapshot::SnapuserdClient; | 
|  | 53 |  | 
|  | 54 | static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd"; | 
|  | 55 | static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID"; | 
|  | 56 | static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD"; | 
| David Anderson | 0e5ad5a | 2021-07-21 21:53:28 -0700 | [diff] [blame] | 57 | static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO"; | 
| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 58 | static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0"; | 
|  | 59 | static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0"; | 
|  | 60 |  | 
|  | 61 | void 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 Anderson | 0e5ad5a | 2021-07-21 21:53:28 -0700 | [diff] [blame] | 91 | 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 Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 99 | setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); | 
|  | 100 |  | 
|  | 101 | LOG(INFO) << "Relaunched snapuserd with pid: " << pid; | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | std::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 |  | 
|  | 118 | static 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 |  | 
|  | 127 | static 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 |  | 
|  | 139 | static 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 |  | 
|  | 153 | void 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 |  | 
|  | 159 | SnapuserdSelinuxHelper::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 Anderson | 9fd8862 | 2021-03-05 14:10:55 -0800 | [diff] [blame] | 171 | static 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 Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 198 | void 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 Anderson | 9fd8862 | 2021-03-05 14:10:55 -0800 | [diff] [blame] | 211 | LockAllSystemPages(); | 
| David Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 212 |  | 
|  | 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 |  | 
|  | 223 | void 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 |  | 
|  | 239 | void 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 |  | 
|  | 281 | std::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 |  | 
|  | 298 | void 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 |  | 
|  | 306 | void 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 |  | 
|  | 324 | void 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 |  | 
|  | 336 | bool IsFirstStageSnapuserdRunning() { | 
|  | 337 | return GetSnapuserdFirstStagePid().has_value(); | 
|  | 338 | } | 
|  | 339 |  | 
| David Anderson | 0e5ad5a | 2021-07-21 21:53:28 -0700 | [diff] [blame] | 340 | std::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 Anderson | 491e4da | 2020-12-08 00:21:20 -0800 | [diff] [blame] | 348 | }  // namespace init | 
|  | 349 | }  // namespace android |