blob: 96f375e1733167ec4873aab02ab60fe39a4a10a1 [file] [log] [blame]
// Copyright 2018 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/drivefs_helper.h"
#include <utility>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include "cros-disks/fuse_mounter.h"
#include "cros-disks/mount_options.h"
#include "cros-disks/platform.h"
#include "cros-disks/quote.h"
#include "cros-disks/system_mounter.h"
#include "cros-disks/uri.h"
namespace cros_disks {
namespace {
const char kDataDirOptionPrefix[] = "datadir=";
const char kIdentityOptionPrefix[] = "identity=";
const char kMyFilesOptionPrefix[] = "myfiles=";
const char kPathPrefixOptionPrefix[] = "prefix=";
const char kHelperTool[] = "/opt/google/drive-file-stream/drivefs";
const char kSeccompPolicyFile[] =
"/opt/google/drive-file-stream/drivefs-seccomp.policy";
const char kType[] = "drivefs";
const char kDbusSocketPath[] = "/run/dbus";
class DrivefsMounter : public FUSEMounter {
public:
DrivefsMounter(std::string filesystem_type,
MountOptions mount_options,
const Platform* platform,
brillo::ProcessReaper* process_reaper,
std::string mount_program,
std::string mount_user,
std::string seccomp_policy,
BindPaths bind_paths)
: FUSEMounter({.bind_paths = std::move(bind_paths),
.filesystem_type = std::move(filesystem_type),
.mount_options = std::move(mount_options),
.mount_program = std::move(mount_program),
.mount_user = std::move(mount_user),
.network_access = true,
.platform = platform,
.process_reaper = process_reaper,
.seccomp_policy = seccomp_policy}) {}
// FUSEMounter overrides:
std::unique_ptr<MountPoint> Mount(const std::string& source,
const base::FilePath& target_path,
std::vector<std::string> options,
MountErrorType* error) const override {
return FUSEMounter::Mount("", target_path, options, error);
}
};
} // namespace
DrivefsHelper::DrivefsHelper(const Platform* platform,
brillo::ProcessReaper* process_reaper)
: FUSEHelper(kType,
platform,
process_reaper,
base::FilePath(kHelperTool),
kFilesUser) {}
DrivefsHelper::~DrivefsHelper() = default;
std::unique_ptr<FUSEMounter> DrivefsHelper::CreateMounter(
const base::FilePath& working_dir,
const Uri& source,
const base::FilePath& target_path,
const std::vector<std::string>& options) const {
const std::string& identity = source.path();
// Enforced by FUSEHelper::CanMount().
DCHECK(!identity.empty());
auto data_dir = GetValidatedDirectory(options, kDataDirOptionPrefix);
if (data_dir.empty()) {
return nullptr;
}
uid_t files_uid;
gid_t files_gid;
if (!platform()->GetUserAndGroupId(kFilesUser, &files_uid, nullptr) ||
!platform()->GetGroupId(kFilesGroup, &files_gid)) {
LOG(ERROR) << "Invalid user configuration.";
return nullptr;
}
auto my_files_path = GetValidatedDirectory(options, kMyFilesOptionPrefix);
if (!my_files_path.empty() && !CheckMyFilesPermissions(my_files_path)) {
return nullptr;
}
if (!CheckDataDirPermissions(data_dir)) {
return nullptr;
}
MountOptions mount_options;
mount_options.EnforceOption(kDataDirOptionPrefix + data_dir.value());
mount_options.EnforceOption(kIdentityOptionPrefix + identity);
mount_options.EnforceOption(kPathPrefixOptionPrefix + target_path.value());
if (!my_files_path.empty()) {
mount_options.EnforceOption(kMyFilesOptionPrefix + my_files_path.value());
}
mount_options.Initialize(options, true, base::NumberToString(files_uid),
base::NumberToString(files_gid));
// TODO(crbug.com/859802): Make seccomp mandatory when testing done.
std::string seccomp =
platform()->PathExists(kSeccompPolicyFile) ? kSeccompPolicyFile : "";
// Bind datadir and DBus communication socket into the sandbox.
FUSEMounter::BindPaths paths = {{.path = data_dir.value(), .writable = true},
{.path = kDbusSocketPath, .writable = true}};
if (!my_files_path.empty()) {
paths.push_back(
{.path = my_files_path.value(), .writable = true, .recursive = true});
}
return std::make_unique<DrivefsMounter>(
type(), mount_options, platform(), process_reaper(),
program_path().value(), user(), seccomp, paths);
}
base::FilePath DrivefsHelper::GetValidatedDirectory(
const std::vector<std::string>& options,
const base::StringPiece prefix) const {
for (const auto& option : options) {
if (base::StartsWith(option, prefix, base::CompareCase::SENSITIVE)) {
std::string path_string = option.substr(prefix.size());
base::FilePath data_dir(path_string);
if (data_dir.empty() || !data_dir.IsAbsolute() ||
data_dir.ReferencesParent()) {
LOG(ERROR) << "Invalid DriveFS option " << prefix << path_string;
return {};
}
base::FilePath suffix_component;
// If the datadir doesn't exist, canonicalize the parent directory
// instead, and append the last path component to that path.
if (!platform()->DirectoryExists(data_dir.value())) {
suffix_component = data_dir.BaseName();
data_dir = data_dir.DirName();
}
if (!platform()->GetRealPath(data_dir.value(), &path_string)) {
return {};
}
return base::FilePath(path_string).Append(suffix_component);
}
}
return {};
}
bool DrivefsHelper::CheckDataDirPermissions(const base::FilePath& dir) const {
CHECK(dir.IsAbsolute() && !dir.ReferencesParent())
<< "Unsafe path " << quote(dir);
uid_t mounter_uid;
gid_t files_gid;
if (!platform()->GetUserAndGroupId(user(), &mounter_uid, nullptr) ||
!platform()->GetGroupId(kFilesGroup, &files_gid)) {
LOG(ERROR) << "Invalid user configuration.";
return false;
}
std::string path = dir.value();
if (!platform()->DirectoryExists(path)) {
LOG(ERROR) << "Datadir does not exist " << quote(path);
return false;
}
uid_t current_uid;
if (!platform()->GetOwnership(path, &current_uid, nullptr)) {
LOG(ERROR) << "Cannot access datadir " << quote(path);
return false;
}
if (current_uid != mounter_uid) {
LOG(ERROR) << "Wrong owner of datadir " << current_uid;
return false;
}
return true;
}
bool DrivefsHelper::CheckMyFilesPermissions(const base::FilePath& dir) const {
CHECK(dir.IsAbsolute() && !dir.ReferencesParent())
<< "Unsafe 'My Files' path " << quote(dir);
uid_t mounter_uid;
if (!platform()->GetUserAndGroupId(user(), &mounter_uid, nullptr)) {
LOG(ERROR) << "Invalid user configuration.";
return false;
}
std::string path = dir.value();
if (!platform()->DirectoryExists(path)) {
LOG(ERROR) << "My files directory " << quote(path) << " does not exist";
return false;
}
uid_t current_uid;
if (!platform()->GetOwnership(path, &current_uid, nullptr)) {
LOG(WARNING) << "Cannot access my files directory " << quote(path);
return false;
}
if (current_uid != mounter_uid) {
LOG(ERROR) << "Incorrect owner for my files directory " << quote(path);
return false;
}
return true;
}
} // namespace cros_disks