Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 5 | #include "cros-disks/archive_manager.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 6 | |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 7 | #include <utility> |
| 8 | |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 9 | #include <base/files/file_path.h> |
Ben Chan | cd8fda4 | 2014-09-05 08:21:06 -0700 | [diff] [blame] | 10 | #include <base/files/file_util.h> |
François Degros | f76886e | 2020-07-24 18:13:40 +1000 | [diff] [blame] | 11 | #include <base/strings/string_number_conversions.h> |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 12 | #include <base/strings/string_util.h> |
Alex Vakulenko | e769653 | 2015-10-16 16:27:29 -0700 | [diff] [blame] | 13 | #include <brillo/cryptohome.h> |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 14 | |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 15 | #include "cros-disks/archive_mounter.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 16 | #include "cros-disks/platform.h" |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 17 | #include "cros-disks/quote.h" |
| 18 | #include "cros-disks/rar_mounter.h" |
Sergei Datsenko | f3ebcc3 | 2020-12-07 23:47:56 +1100 | [diff] [blame] | 19 | #include "cros-disks/user.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 20 | |
Anand K Mistry | 40cff45 | 2019-07-30 10:24:48 +1000 | [diff] [blame] | 21 | namespace cros_disks { |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 22 | |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 23 | ArchiveManager::ArchiveManager(const std::string& mount_root, |
| 24 | Platform* platform, |
| 25 | Metrics* metrics, |
| 26 | brillo::ProcessReaper* process_reaper) |
| 27 | : MountManager(mount_root, platform, metrics, process_reaper) {} |
| 28 | |
| 29 | ArchiveManager::~ArchiveManager() = default; |
| 30 | |
| 31 | bool ArchiveManager::Initialize() { |
| 32 | if (!MountManager::Initialize()) |
| 33 | return false; |
| 34 | |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 35 | { |
Sergei Datsenko | 87c49bb | 2021-01-13 15:08:07 +1100 | [diff] [blame] | 36 | SandboxedExecutable executable = { |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 37 | base::FilePath("/usr/bin/fuse-zip"), |
| 38 | base::FilePath("/usr/share/policy/fuse-zip-seccomp.policy")}; |
| 39 | |
Sergei Datsenko | 87c49bb | 2021-01-13 15:08:07 +1100 | [diff] [blame] | 40 | auto sandbox_factory = |
| 41 | CreateSandboxFactory(std::move(executable), "fuse-zip"); |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 42 | std::vector<int> password_needed_codes = { |
| 43 | 23, // ZIP_ER_BASE + ZIP_ER_ZLIB |
| 44 | 36, // ZIP_ER_BASE + ZIP_ER_NOPASSWD |
| 45 | 37}; // ZIP_ER_BASE + ZIP_ER_WRONGPASSWD |
| 46 | |
| 47 | mounters_.push_back(std::make_unique<ArchiveMounter>( |
| 48 | platform(), process_reaper(), "zip", metrics(), "FuseZip", |
| 49 | std::move(password_needed_codes), std::move(sandbox_factory))); |
| 50 | } |
| 51 | |
| 52 | { |
Sergei Datsenko | 87c49bb | 2021-01-13 15:08:07 +1100 | [diff] [blame] | 53 | SandboxedExecutable executable = { |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 54 | base::FilePath("/usr/bin/rar2fs"), |
| 55 | base::FilePath("/usr/share/policy/rar2fs-seccomp.policy")}; |
| 56 | |
Sergei Datsenko | 87c49bb | 2021-01-13 15:08:07 +1100 | [diff] [blame] | 57 | auto sandbox_factory = |
| 58 | CreateSandboxFactory(std::move(executable), "fuse-rar2fs"); |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 59 | |
| 60 | mounters_.push_back(std::make_unique<RarMounter>( |
| 61 | platform(), process_reaper(), metrics(), std::move(sandbox_factory))); |
| 62 | } |
| 63 | |
| 64 | return true; |
| 65 | } |
| 66 | |
François Degros | 7f7a416 | 2020-06-13 00:13:00 +1000 | [diff] [blame] | 67 | bool ArchiveManager::ResolvePath(const std::string& path, |
| 68 | std::string* real_path) { |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 69 | std::unique_ptr<brillo::ScopedMountNamespace> mount_ns; |
| 70 | if (!platform()->PathExists(path)) { |
| 71 | // Try to locate the file in Chrome's mount namespace. |
| 72 | mount_ns = brillo::ScopedMountNamespace::CreateFromPath( |
| 73 | base::FilePath(ArchiveMounter::kChromeNamespace)); |
| 74 | if (!mount_ns) { |
François Degros | ca45086 | 2021-01-14 14:40:02 +1100 | [diff] [blame] | 75 | PLOG(ERROR) << "Cannot find archive " << redact(path) |
| 76 | << " in mount namespace " |
| 77 | << quote(ArchiveMounter::kChromeNamespace); |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 78 | return false; |
| 79 | } |
| 80 | } |
François Degros | 7f7a416 | 2020-06-13 00:13:00 +1000 | [diff] [blame] | 81 | return platform()->GetRealPath(path, real_path); |
| 82 | } |
| 83 | |
François Degros | 7f7a416 | 2020-06-13 00:13:00 +1000 | [diff] [blame] | 84 | bool ArchiveManager::IsInAllowedFolder(const std::string& source_path) { |
| 85 | std::vector<std::string> parts; |
| 86 | base::FilePath(source_path).GetComponents(&parts); |
| 87 | |
| 88 | if (parts.size() < 2 || parts[0] != "/") |
François Degros | 853c7d9 | 2020-02-17 10:32:21 +1100 | [diff] [blame] | 89 | return false; |
| 90 | |
François Degros | 7f7a416 | 2020-06-13 00:13:00 +1000 | [diff] [blame] | 91 | if (parts[1] == "home") |
| 92 | return parts.size() > 5 && parts[2] == "chronos" && |
| 93 | base::StartsWith(parts[3], "u-", base::CompareCase::SENSITIVE) && |
| 94 | brillo::cryptohome::home::IsSanitizedUserName(parts[3].substr(2)) && |
| 95 | parts[4] == "MyFiles"; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 96 | |
François Degros | 7f7a416 | 2020-06-13 00:13:00 +1000 | [diff] [blame] | 97 | if (parts[1] == "media") |
| 98 | return parts.size() > 4 && (parts[2] == "archive" || parts[2] == "fuse" || |
| 99 | parts[2] == "removable"); |
| 100 | |
| 101 | if (parts[1] == "run") |
| 102 | return parts.size() > 8 && parts[2] == "arc" && parts[3] == "sdcard" && |
| 103 | parts[4] == "write" && parts[5] == "emulated" && parts[6] == "0"; |
| 104 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 105 | return false; |
| 106 | } |
| 107 | |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 108 | std::string ArchiveManager::SuggestMountPath( |
| 109 | const std::string& source_path) const { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 110 | // Use the archive name to name the mount directory. |
Ben Chan | 213c6d9 | 2019-04-10 16:21:52 -0700 | [diff] [blame] | 111 | base::FilePath base_name = base::FilePath(source_path).BaseName(); |
Anand K Mistry | 30e32fa | 2020-02-04 10:07:57 +1100 | [diff] [blame] | 112 | return mount_root().Append(base_name).value(); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 113 | } |
| 114 | |
François Degros | f76886e | 2020-07-24 18:13:40 +1000 | [diff] [blame] | 115 | std::vector<gid_t> ArchiveManager::GetSupplementaryGroups() const { |
| 116 | std::vector<gid_t> groups; |
| 117 | |
| 118 | // To access Play Files. |
| 119 | gid_t gid; |
| 120 | if (platform()->GetGroupId("android-everybody", &gid)) |
| 121 | groups.push_back(gid); |
| 122 | |
| 123 | return groups; |
| 124 | } |
| 125 | |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 126 | bool ArchiveManager::CanMount(const std::string& source_path) const { |
| 127 | if (IsInAllowedFolder(source_path)) { |
| 128 | base::FilePath name; |
| 129 | for (const auto& m : mounters_) { |
| 130 | if (m->CanMount(source_path, {}, &name)) { |
| 131 | return true; |
| 132 | } |
| 133 | } |
François Degros | f76886e | 2020-07-24 18:13:40 +1000 | [diff] [blame] | 134 | } |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 135 | return false; |
| 136 | } |
| 137 | |
| 138 | std::unique_ptr<MountPoint> ArchiveManager::DoMount( |
| 139 | const std::string& source_path, |
François Degros | ca45086 | 2021-01-14 14:40:02 +1100 | [diff] [blame] | 140 | const std::string& filesystem_type, |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 141 | const std::vector<std::string>& options, |
| 142 | const base::FilePath& mount_path, |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 143 | MountErrorType* error) { |
| 144 | // Here source_path is already resolved and free from symlinks and '..' by |
| 145 | // the base class. |
| 146 | if (!IsInAllowedFolder(source_path)) { |
François Degros | ca45086 | 2021-01-14 14:40:02 +1100 | [diff] [blame] | 147 | LOG(ERROR) << "Source path " << redact(source_path) << " is not allowed"; |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 148 | *error = MOUNT_ERROR_INVALID_DEVICE_PATH; |
| 149 | return nullptr; |
| 150 | } |
| 151 | base::FilePath name; |
| 152 | for (const auto& m : mounters_) { |
| 153 | if (m->CanMount(source_path, {}, &name)) { |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 154 | return m->Mount(source_path, mount_path, options, error); |
| 155 | } |
| 156 | } |
François Degros | ca45086 | 2021-01-14 14:40:02 +1100 | [diff] [blame] | 157 | LOG(ERROR) << "Cannot find mounter for archive " << redact(source_path) |
| 158 | << " of type " << quote(filesystem_type); |
Sergei Datsenko | c9904bb | 2020-12-11 12:46:02 +1100 | [diff] [blame] | 159 | *error = MOUNT_ERROR_UNKNOWN_FILESYSTEM; |
| 160 | return nullptr; |
François Degros | f76886e | 2020-07-24 18:13:40 +1000 | [diff] [blame] | 161 | } |
| 162 | |
Sergei Datsenko | 87c49bb | 2021-01-13 15:08:07 +1100 | [diff] [blame] | 163 | std::unique_ptr<FUSESandboxedProcessFactory> |
| 164 | ArchiveManager::CreateSandboxFactory(SandboxedExecutable executable, |
| 165 | const std::string& user_name) const { |
| 166 | // To access Play Files. |
| 167 | std::vector<gid_t> groups; |
| 168 | gid_t gid; |
| 169 | if (platform()->GetGroupId("android-everybody", &gid)) |
| 170 | groups.push_back(gid); |
| 171 | |
| 172 | OwnerUser run_as; |
| 173 | if (!platform()->GetUserAndGroupId(user_name, &run_as.uid, &run_as.gid)) { |
| 174 | PLOG(ERROR) << "Cannot resolve required user " << quote(user_name); |
| 175 | return nullptr; |
| 176 | } |
| 177 | // Archivers need to run in chronos-access group to be able to access |
| 178 | // user's files. |
| 179 | run_as.gid = kChronosAccessGID; |
| 180 | |
| 181 | return std::make_unique<FUSESandboxedProcessFactory>( |
| 182 | platform(), std::move(executable), std::move(run_as), |
| 183 | /* has_network_access= */ false, std::move(groups)); |
| 184 | } |
| 185 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 186 | } // namespace cros_disks |