blob: eff226c1f37a4d8e951f13424c3a4c7c8608d068 [file] [log] [blame]
// Copyright (c) 2012 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 "cros-disks/archive_manager.h"
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <brillo/cryptohome.h>
#include "cros-disks/archive_mounter.h"
#include "cros-disks/platform.h"
#include "cros-disks/quote.h"
#include "cros-disks/rar_mounter.h"
#include "cros-disks/user.h"
namespace cros_disks {
ArchiveManager::ArchiveManager(const std::string& mount_root,
Platform* platform,
Metrics* metrics,
brillo::ProcessReaper* process_reaper)
: MountManager(mount_root, platform, metrics, process_reaper) {}
ArchiveManager::~ArchiveManager() = default;
bool ArchiveManager::Initialize() {
if (!MountManager::Initialize())
return false;
{
SandboxedExecutable executable = {
base::FilePath("/usr/bin/fuse-zip"),
base::FilePath("/usr/share/policy/fuse-zip-seccomp.policy")};
auto sandbox_factory =
CreateSandboxFactory(std::move(executable), "fuse-zip");
std::vector<int> password_needed_codes = {
23, // ZIP_ER_BASE + ZIP_ER_ZLIB
36, // ZIP_ER_BASE + ZIP_ER_NOPASSWD
37}; // ZIP_ER_BASE + ZIP_ER_WRONGPASSWD
mounters_.push_back(std::make_unique<ArchiveMounter>(
platform(), process_reaper(), "zip", metrics(), "FuseZip",
std::move(password_needed_codes), std::move(sandbox_factory)));
}
{
SandboxedExecutable executable = {
base::FilePath("/usr/bin/rar2fs"),
base::FilePath("/usr/share/policy/rar2fs-seccomp.policy")};
auto sandbox_factory =
CreateSandboxFactory(std::move(executable), "fuse-rar2fs");
mounters_.push_back(std::make_unique<RarMounter>(
platform(), process_reaper(), metrics(), std::move(sandbox_factory)));
}
// TODO(nigeltao): refactor the ArchiveMounter C++ class (and superclasses)
// to break the "1 instance = 1 file extension" assumption. That would let us
// add just 1 element to mounters_, not 1 per file extension.
const char* const archivemount_extensions[] = {
// The empty // comments make clang-format place one entry per line.
"7z", //
"bz2", //
"crx", //
"gz", //
"iso", //
"tar", //
"tbz", //
"tbz2", //
"tgz", //
};
for (const char* const ext : archivemount_extensions) {
SandboxedExecutable executable = {
base::FilePath("/usr/bin/archivemount"),
base::FilePath("/usr/share/policy/archivemount-seccomp.policy")};
auto sandbox_factory =
CreateSandboxFactory(std::move(executable), "fuse-archivemount");
// The archivemount program (or, at least, the way we use it) doesn't
// support passwords.
std::vector<int> password_needed_codes = {};
mounters_.push_back(std::make_unique<ArchiveMounter>(
platform(), process_reaper(), ext, metrics(), "Archivemount",
std::move(password_needed_codes), std::move(sandbox_factory)));
}
return true;
}
bool ArchiveManager::ResolvePath(const std::string& path,
std::string* real_path) {
std::unique_ptr<brillo::ScopedMountNamespace> mount_ns;
if (!platform()->PathExists(path)) {
// Try to locate the file in Chrome's mount namespace.
mount_ns = brillo::ScopedMountNamespace::CreateFromPath(
base::FilePath(ArchiveMounter::kChromeNamespace));
if (!mount_ns) {
PLOG(ERROR) << "Cannot find archive " << redact(path)
<< " in mount namespace "
<< quote(ArchiveMounter::kChromeNamespace);
return false;
}
}
return platform()->GetRealPath(path, real_path);
}
bool ArchiveManager::IsInAllowedFolder(const std::string& source_path) {
std::vector<std::string> parts;
base::FilePath(source_path).GetComponents(&parts);
if (parts.size() < 2 || parts[0] != "/")
return false;
if (parts[1] == "home")
return parts.size() > 5 && parts[2] == "chronos" &&
base::StartsWith(parts[3], "u-", base::CompareCase::SENSITIVE) &&
brillo::cryptohome::home::IsSanitizedUserName(parts[3].substr(2)) &&
parts[4] == "MyFiles";
if (parts[1] == "media")
return parts.size() > 4 && (parts[2] == "archive" || parts[2] == "fuse" ||
parts[2] == "removable");
if (parts[1] == "run")
return parts.size() > 8 && parts[2] == "arc" && parts[3] == "sdcard" &&
parts[4] == "write" && parts[5] == "emulated" && parts[6] == "0";
return false;
}
std::string ArchiveManager::SuggestMountPath(
const std::string& source_path) const {
// Use the archive name to name the mount directory.
base::FilePath base_name = base::FilePath(source_path).BaseName();
return mount_root().Append(base_name).value();
}
std::vector<gid_t> ArchiveManager::GetSupplementaryGroups() const {
std::vector<gid_t> groups;
// To access Play Files.
gid_t gid;
if (platform()->GetGroupId("android-everybody", &gid))
groups.push_back(gid);
return groups;
}
bool ArchiveManager::CanMount(const std::string& source_path) const {
if (IsInAllowedFolder(source_path)) {
base::FilePath name;
for (const auto& m : mounters_) {
if (m->CanMount(source_path, {}, &name)) {
return true;
}
}
}
return false;
}
std::unique_ptr<MountPoint> ArchiveManager::DoMount(
const std::string& source_path,
const std::string& filesystem_type,
const std::vector<std::string>& options,
const base::FilePath& mount_path,
MountErrorType* error) {
// Here source_path is already resolved and free from symlinks and '..' by
// the base class.
if (!IsInAllowedFolder(source_path)) {
LOG(ERROR) << "Source path " << redact(source_path) << " is not allowed";
*error = MOUNT_ERROR_INVALID_DEVICE_PATH;
return nullptr;
}
base::FilePath name;
for (const auto& m : mounters_) {
if (m->CanMount(source_path, {}, &name)) {
return m->Mount(source_path, mount_path, options, error);
}
}
LOG(ERROR) << "Cannot find mounter for archive " << redact(source_path)
<< " of type " << quote(filesystem_type);
*error = MOUNT_ERROR_UNKNOWN_FILESYSTEM;
return nullptr;
}
std::unique_ptr<FUSESandboxedProcessFactory>
ArchiveManager::CreateSandboxFactory(SandboxedExecutable executable,
const std::string& user_name) const {
// To access Play Files.
std::vector<gid_t> groups;
gid_t gid;
if (platform()->GetGroupId("android-everybody", &gid))
groups.push_back(gid);
OwnerUser run_as;
if (!platform()->GetUserAndGroupId(user_name, &run_as.uid, &run_as.gid)) {
PLOG(ERROR) << "Cannot resolve required user " << quote(user_name);
return nullptr;
}
// Archivers need to run in chronos-access group to be able to access
// user's files.
run_as.gid = kChronosAccessGID;
return std::make_unique<FUSESandboxedProcessFactory>(
platform(), std::move(executable), std::move(run_as),
/* has_network_access= */ false, std::move(groups));
}
} // namespace cros_disks