| // Copyright 2016 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <signal.h> |
| |
| #include <iostream> |
| #include <memory> |
| #include <sys/mount.h> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <brillo/flag_helper.h> |
| #include <brillo/syslog_logging.h> |
| #include <brillo/userdb_utils.h> |
| |
| #include "imageloader/component.h" |
| #include "imageloader/global_context.h" |
| #include "imageloader/helper_process_proxy.h" |
| #include "imageloader/helper_process_receiver.h" |
| #include "imageloader/imageloader.h" |
| #include "imageloader/imageloader_impl.h" |
| |
| namespace { |
| |
| constexpr uint8_t kProdPublicKey[] = { |
| 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, |
| 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, |
| 0x42, 0x00, 0x04, 0x53, 0xd9, 0x6f, 0xb1, 0x92, 0x97, 0x39, 0xa9, 0x97, |
| 0x18, 0xbe, 0xa7, 0x97, 0x15, 0x06, 0x27, 0x9c, 0x55, 0xa5, 0x40, 0xc1, |
| 0x0f, 0x98, 0xfa, 0xd8, 0x61, 0x18, 0xee, 0xcf, 0xf3, 0xbb, 0xf9, 0x6e, |
| 0x6d, 0xa0, 0x66, 0xd2, 0x29, 0xf0, 0x78, 0x5b, 0x7a, 0xab, 0x54, 0xca, |
| 0x53, 0x16, 0xb0, 0xf9, 0xc4, 0xd8, 0x1d, 0x93, 0x5b, 0x83, 0x6e, 0xa5, |
| 0x65, 0xe5, 0x71, 0xbc, 0x8d, 0x72, 0x02}; |
| |
| // The path where the components are stored on the device. |
| constexpr char kComponentsPath[] = "/var/lib/imageloader"; |
| // The location of the container public key. |
| constexpr char kContainerPublicKeyPath[] = |
| "/usr/share/misc/oci-container-key-pub.der"; |
| |
| bool LoadKeyFromFile(const std::string& file, std::vector<uint8_t>* key_out) { |
| CHECK(key_out); |
| |
| base::FilePath key_file(file); |
| std::string key_data; |
| |
| // The key should be pretty small. |
| if (!ReadFileToString(key_file, &key_data)) { |
| LOG(WARNING) << "Could not read key file " << key_file.value(); |
| return false; |
| } |
| |
| key_out->clear(); |
| key_out->insert(key_out->begin(), key_data.begin(), key_data.end()); |
| |
| return true; |
| } |
| |
| bool Init(const std::string& loadedMountsBase) { |
| const char* path = loadedMountsBase.c_str(); |
| if (!base::PathExists(base::FilePath(path))) { |
| // Create a folder for loadedMountsBase. |
| if (mkdir(path, imageloader::kComponentDirPerms) != 0) { |
| PLOG(ERROR) << "Mkdir failed: " << path; |
| return false; |
| } |
| // Mount a tmpfs at loadedMountsBase. |
| if (mount("imageloader", path, "tmpfs", MS_NODEV | MS_NOSUID | MS_NOEXEC, |
| "mode=0755") < 0) { |
| PLOG(ERROR) << "Mount tmpfs failed: " << path; |
| return false; |
| } |
| // Mark the mount point as shared. |
| if (mount(nullptr, path, nullptr, MS_SHARED, "") < 0) { |
| PLOG(ERROR) << "Mount shared failed: " << path; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool CreateComponentsPath() { |
| // Check if kComponentsPath does not exist or it is not a folder. |
| if (!base::DirectoryExists(base::FilePath(kComponentsPath))) { |
| // Remove the file at kComponentsPath. |
| if (!base::DeletePathRecursively(base::FilePath(kComponentsPath))) { |
| PLOG(ERROR) << "base::DeletePathRecursively failed: " << kComponentsPath; |
| return false; |
| } |
| // Create a folder for kComponentsPath. |
| if (mkdir(kComponentsPath, imageloader::kComponentDirPerms) != 0) { |
| PLOG(ERROR) << "Mkdir failed: " << kComponentsPath; |
| return false; |
| } |
| // Set ownership user/groups as imageloaderd:imageloaderd |
| uid_t uid; |
| gid_t gid; |
| if (!brillo::userdb::GetUserInfo("imageloaderd", &uid, &gid)) { |
| PLOG(ERROR) << "Can not get uid/gid."; |
| return false; |
| } |
| if (chown(kComponentsPath, uid, gid) != 0) { |
| PLOG(ERROR) << "Can not set ownership for path: " << kComponentsPath; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| DEFINE_bool(init_only, false, |
| "Only executes one-time setup process for imageloader."); |
| DEFINE_bool(dry_run, false, |
| "Changes unmount_all to print the paths which would be " |
| "affected."); |
| DEFINE_bool(mount, false, |
| "Rather than starting a dbus daemon, verify and mount a single " |
| "component and exit immediately."); |
| DEFINE_string(mount_component, "", |
| "Specifies the name of the component when using --mount."); |
| DEFINE_string(mount_point, "", |
| "Specifies the mountpoint when using either --mount or " |
| "--unmount."); |
| DEFINE_string(loaded_mounts_base, imageloader::ImageLoader::kLoadedMountsBase, |
| "Base path where components are mounted (unless --mount_point " |
| "is used)."); |
| DEFINE_int32(mount_helper_fd, -1, |
| "Control socket for starting an ImageLoader subprocess. Used " |
| "internally."); |
| DEFINE_bool(unmount, false, |
| "Unmounts the path specified by mount_point and exits " |
| "immediately."); |
| DEFINE_bool(unmount_all, false, |
| "Unmounts all the mountpoints under loaded_mounts_base and exits " |
| "immediately."); |
| brillo::FlagHelper::Init(argc, argv, "imageloader"); |
| |
| brillo::OpenLog("imageloader", true); |
| brillo::InitLog(brillo::kLogToSyslog); |
| |
| if (FLAGS_mount + FLAGS_unmount + FLAGS_unmount_all > 1) { |
| LOG(ERROR) << "Only one of --mount, --unmount, and --unmount_all can be " |
| "set at a time."; |
| return 1; |
| } |
| |
| imageloader::GlobalContext g_ctx; |
| g_ctx.SetAsCurrent(); |
| |
| // Create folder for component copies. This ensures that |
| // imageloader's storage exists and is owned by `imageloaderd` user. |
| if (!CreateComponentsPath()) { |
| return 1; |
| } |
| |
| // Executes the setup process. |
| if (FLAGS_init_only) { |
| return Init(FLAGS_loaded_mounts_base) ? 0 : 1; |
| } |
| |
| // Executing this as the helper process if specified. |
| if (FLAGS_mount_helper_fd >= 0) { |
| CHECK_GT(FLAGS_mount_helper_fd, -1); |
| base::ScopedFD fd(FLAGS_mount_helper_fd); |
| imageloader::HelperProcessReceiver root_process(std::move(fd)); |
| return root_process.Run(); |
| } |
| |
| imageloader::Keys keys; |
| // The order of key addition below is important. |
| // 1. Prod key, used to sign components in Omaha. |
| keys.push_back(std::vector<uint8_t>(std::begin(kProdPublicKey), |
| std::end(kProdPublicKey))); |
| // 2. Container key. |
| std::vector<uint8_t> container_key; |
| if (LoadKeyFromFile(kContainerPublicKeyPath, &container_key)) |
| keys.push_back(container_key); |
| |
| imageloader::ImageLoaderConfig config(keys, kComponentsPath, |
| FLAGS_loaded_mounts_base.c_str()); |
| auto helper_process_proxy = |
| std::make_unique<imageloader::HelperProcessProxy>(); |
| helper_process_proxy->Start(argc, argv, "--mount_helper_fd"); |
| |
| // Load and mount the specified component and exit. |
| if (FLAGS_mount) { |
| // Run with minimal privilege. |
| imageloader::ImageLoader::EnterSandbox(); |
| |
| if (FLAGS_mount_point.empty() || FLAGS_mount_component.empty()) { |
| LOG(ERROR) << "--mount_component=name and --mount_point=path must be set " |
| "with --mount"; |
| return 1; |
| } |
| // Access the ImageLoaderImpl directly to avoid needless dbus dependencies, |
| // which may not be available at early boot. |
| imageloader::ImageLoaderImpl loader(std::move(config)); |
| |
| std::string component_version = |
| loader.GetComponentVersion(FLAGS_mount_component); |
| // imageloader returns "" if the component doesn't exist. In this case |
| // return 0 so our crash reporting doesn't think something actually went |
| // wrong. |
| if (component_version.empty()) |
| return 0; |
| |
| if (!loader.LoadComponent(FLAGS_mount_component, FLAGS_mount_point, |
| helper_process_proxy.get())) { |
| LOG(ERROR) << "Failed to verify and mount component: " |
| << FLAGS_mount_component << " at " << FLAGS_mount_point; |
| return 1; |
| } |
| return 0; |
| } |
| |
| // Unmount all component mount points and exit. |
| if (FLAGS_unmount_all) { |
| // Run with minimal privilege. |
| imageloader::ImageLoader::EnterSandbox(); |
| |
| imageloader::ImageLoaderImpl loader(std::move(config)); |
| std::vector<std::string> paths; |
| const base::FilePath parent_dir(FLAGS_loaded_mounts_base); |
| bool success = loader.CleanupAll(FLAGS_dry_run, parent_dir, &paths, |
| helper_process_proxy.get()); |
| if (FLAGS_dry_run) { |
| for (const auto& path : paths) { |
| std::cout << path << "\n"; |
| } |
| } |
| if (!success) { |
| LOG(ERROR) << "--unmount_all failed!"; |
| return 1; |
| } |
| return 0; |
| } |
| |
| // Unmount a component mount point and exit. |
| if (FLAGS_unmount) { |
| // Run with minimal privilege. |
| imageloader::ImageLoader::EnterSandbox(); |
| |
| if (FLAGS_mount_point.empty()) { |
| LOG(ERROR) << "--mount_point=path must be set with --unmount"; |
| return 1; |
| } |
| |
| imageloader::ImageLoaderImpl loader(std::move(config)); |
| const base::FilePath path(FLAGS_mount_point); |
| bool success = loader.Cleanup(path, helper_process_proxy.get()); |
| if (!success) { |
| LOG(ERROR) << "--unmount failed!"; |
| return 1; |
| } |
| return 0; |
| } |
| |
| // Run as a daemon and wait for dbus requests. |
| imageloader::ImageLoader daemon(std::move(config), |
| std::move(helper_process_proxy)); |
| daemon.Run(); |
| |
| return 0; |
| } |