blob: cc5dda8983409c2a59cee56bf286e530679f79be [file] [log] [blame]
// Copyright 2019 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.
// MountHelper objects carry out mount(2) and unmount(2) operations for a single
// cryptohome mount.
#include <sys/types.h>
#include <memory>
#include <string>
#include <vector>
#include <base/files/file_path.h>
#include <base/macros.h>
#include <brillo/process/process.h>
#include <brillo/secure_blob.h>
#include <chromeos/dbus/service_constants.h>
#include "cryptohome/credentials.h"
#include "cryptohome/platform.h"
#include "cryptohome/storage/mount_constants.h"
#include "cryptohome/storage/mount_stack.h"
using base::FilePath;
namespace cryptohome {
extern const char kDefaultHomeDir[];
extern const char kEphemeralCryptohomeRootContext[];
// Objects that implement MountHelperInterface can perform mount operations.
// This interface will be used as we transition all cryptohome mounts to be
// performed out-of-process.
class MountHelperInterface {
virtual ~MountHelperInterface() {}
struct Options {
MountType type = MountType::NONE;
bool to_migrate_from_ecryptfs = false;
bool shadow_only = false;
// Ephemeral mounts cannot be performed twice, so cryptohome needs to be able
// to check whether an ephemeral mount can be performed.
virtual bool CanPerformEphemeralMount() const = 0;
// Returns whether an ephemeral mount has been performed.
virtual bool MountPerformed() const = 0;
// Returns whether |path| is currently mounted as part of the ephemeral mount.
virtual bool IsPathMounted(const base::FilePath& path) const = 0;
// Carries out an ephemeral mount for user |username|.
virtual bool PerformEphemeralMount(const std::string& username) = 0;
// Tears down the existing ephemeral mount.
virtual bool TearDownEphemeralMount() = 0;
// Tears down non-ephemeral cryptohome mount.
virtual void TearDownNonEphemeralMount() = 0;
// Carries out mount operations for a regular cryptohome.
virtual bool PerformMount(const Options& mount_opts,
const std::string& username,
const std::string& fek_signature,
const std::string& fnek_signature,
bool is_pristine,
MountError* error) = 0;
class MountHelper : public MountHelperInterface {
MountHelper(uid_t uid,
gid_t gid,
gid_t access_gid,
const brillo::SecureBlob& system_salt,
bool legacy_mount,
bool bind_mount_downloads,
Platform* platform)
: default_uid_(uid),
platform_(platform) {}
MountHelper(const MountHelper&) = delete;
MountHelper& operator=(const MountHelper&) = delete;
~MountHelper() = default;
// Returns the temporary user path while we're migrating for
static base::FilePath GetNewUserPath(const std::string& username);
// Returns the path to sparse file used for ephemeral cryptohome for the user.
static FilePath GetEphemeralSparseFile(
const std::string& obfuscated_username);
// Ensures that root and user mountpoints for the specified user are present.
// Returns false if the mountpoints were not present and could not be created.
bool EnsureUserMountPoints(const std::string& username) const;
// Gets the directory to temporarily mount the user's cryptohome at.
// Parameters
// obfuscated_username - Obfuscated username field of the credentials.
FilePath GetUserTemporaryMountDirectory(
const std::string& obfuscated_username) const;
// Creates the tracked subdirectories in a user's cryptohome.
// If the cryptohome did not have tracked directories, but had them untracked,
// migrate their contents.
// Parameters
// obfuscated_username - The obfuscated form of the username
// type - Mount type: eCryptfs or dircrypto
bool CreateTrackedSubdirectories(const std::string& obfuscated_username,
const MountType& type) const;
// Sets up the ecryptfs mount.
bool SetUpEcryptfsMount(const std::string& obfuscated_username,
const std::string& fek_signature,
const std::string& fnek_signature,
bool should_migrate);
// Sets up the dircrypto mount.
void SetUpDircryptoMount(const std::string& obfuscated_username);
// Carries out eCryptfs/dircrypto mount(2) operations for a regular
// cryptohome.
bool PerformMount(const Options& mount_opts,
const std::string& username,
const std::string& fek_signature,
const std::string& fnek_signature,
bool is_pristine,
MountError* error) override;
// Carries out dircrypto mount(2) operations for an ephemeral cryptohome.
// Does not clean up on failure.
bool PerformEphemeralMount(const std::string& username) override;
// Tears down an ephemeral cryptohome mount in-process by calling umount(2).
bool TearDownEphemeralMount() override;
// Tears down non-ephemeral cryptohome mount in-process by calling umount(2).
void TearDownNonEphemeralMount() override;
// Unmounts all mount points.
// Relies on ForceUnmount() internally; see the caveat listed for it.
void UnmountAll();
// Deletes loop device used for ephemeral cryptohome and underlying temporary
// sparse file.
bool CleanUpEphemeral();
// Returns whether an ephemeral mount operation can be performed.
bool CanPerformEphemeralMount() const override;
// Returns whether a mount operation has been performed.
bool MountPerformed() const override;
// Returns whether |path| is the destination of an existing mount.
bool IsPathMounted(const base::FilePath& path) const override;
// Returns a list of paths that have been mounted as part of the mount.
std::vector<base::FilePath> MountedPaths() const;
// Returns the names of all tracked subdirectories.
static std::vector<base::FilePath> GetTrackedSubdirectories();
// Returns the mounted userhome path (e.g. /home/.shadow/.../mount/user)
// Parameters
// obfuscated_username - Obfuscated username field of the credentials.
FilePath GetMountedUserHomePath(const std::string& obfuscated_username) const;
// Returns the mounted roothome path (e.g. /home/.shadow/.../mount/root)
// Parameters
// obfuscated_username - Obfuscated username field of the credentials.
FilePath GetMountedRootHomePath(const std::string& obfuscated_username) const;
// Mounts a mount point and pushes it to the mount stack.
// Returns true if the mount succeeds, false otherwise.
// Parameters
// src - Path to mount from
// dest - Path to mount to
// type - Filesystem type to mount with
// options - Filesystem options to supply
bool MountAndPush(const base::FilePath& src,
const base::FilePath& dest,
const std::string& type,
const std::string& options);
// Binds a mount point, remembering it for later unmounting.
// Returns true if the bind succeeds, false otherwise.
// Parameters
// src - Path to bind from
// dest - Path to bind to
// is_shared - bind mount as MS_SHARED
bool BindAndPush(const FilePath& src,
const FilePath& dest,
bool is_shared = false);
// Bind mounts |user_home|/Downloads to |user_home|/MyFiles/Downloads so Files
// app can manage MyFiles as user volume instead of just Downloads.
bool BindMyFilesDownloads(const base::FilePath& user_home);
// Copies the skeleton directory to the user's cryptohome.
void CopySkeleton(const FilePath& destination) const;
// Ensures that a specified directory exists, with all path components but the
// last one owned by kMountOwnerUid:kMountOwnerGid and the last component
// owned by desired_uid:desired_gid.
// Parameters
// dir - Directory to check
// desired_uid - uid that must own the directory
// desired_gid - gid that muts own the directory
bool EnsureDirHasOwner(const base::FilePath& dir,
uid_t desired_uid,
gid_t desired_gid) const;
// Ensures that the |num|th component of |path| is owned by |uid|:|gid| and is
// a directory.
bool EnsurePathComponent(const FilePath& path,
size_t num,
uid_t uid,
gid_t gid) const;
// Ensures that the permissions on every parent of /home/chronos/u-$hash are
// correct and that they are all directories. Since we're going to bind-mount
// over the directory, we don't care what the permissions on it are, just that
// it exists.
// /home needs to be root:root.
// /home/chronos needs to be default_uid_:default_gid_.
bool EnsureNewUserDirExists(const std::string& username) const;
// Attempts to unmount a mountpoint. If the unmount fails, logs processes with
// open handles to it and performs a lazy unmount.
// Parameters
// src - Path mounted at |dest|
// dest - Mount point to unmount
void ForceUnmount(const base::FilePath& src, const base::FilePath& dest);
// Creates subdirectories for the user's cryptohome.
// Parameters
// vault_path - path to create subdirectories under.
void CreateHomeSubdirectories(const FilePath& vault_path) const;
// Facilitates migration of files from one directory to another, removing the
// duplicates.
void MigrateDirectory(const base::FilePath& dst,
const base::FilePath& src) const;
// Bind-mounts
// /home/.shadow/$hash/mount/root/$daemon (*)
// to
// /run/daemon-store/$daemon/$hash
// for a hardcoded list of $daemon directories.
// This can be used to make the Cryptohome mount propagate into the daemon's
// mount namespace. See
// for details.
// (*) Path for a regular mount. The path is different for an ephemeral mount.
bool MountDaemonStoreDirectories(const FilePath& root_home,
const std::string& obfuscated_username);
// Sets up bind mounts from |user_home| and |root_home| to
// - /home/chronos/user (see MountLegacyHome()),
// - /home/chronos/u-<user_hash>,
// - /home/user/<user_hash>,
// - /home/root/<user_hash> and
// - /run/daemon-store/$daemon/<user_hash>
// (see MountDaemonStoreDirectories()).
// The parameters have the same meaning as in MountCryptohome resp.
// MountEphemeralCryptohomeInner. Returns true if successful, false otherwise.
bool MountHomesAndDaemonStores(const std::string& username,
const std::string& obfuscated_username,
const FilePath& user_home,
const FilePath& root_home);
// Mounts the legacy home directory.
// The legacy home directory is from before multiprofile and is mounted at
// /home/chronos/user.
bool MountLegacyHome(const FilePath& from);
// Creates a loop device formatted as an ext4 partition.
bool PrepareEphemeralDevice(const std::string& obfuscated_username);
// Recursively copies directory contents to the destination if the destination
// file does not exist. Sets ownership to |default_user_|.
// Parameters
// source - Where to copy files from
// destination - Where to copy files to
void RecursiveCopy(const FilePath& source, const FilePath& destination) const;
// Sets up a freshly mounted ephemeral cryptohome by adjusting its permissions
// and populating it with a skeleton directory and file structure.
bool SetUpEphemeralCryptohome(const FilePath& source_path);
// Changes the group ownership and permissions on those directories inside
// the cryptohome that need to be accessible by other system daemons.
bool SetUpGroupAccess(const FilePath& home_dir) const;
uid_t default_uid_;
uid_t default_gid_;
uid_t default_access_gid_;
// Stores the global system salt.
brillo::SecureBlob system_salt_;
bool legacy_mount_ = true;
bool bind_mount_downloads_ = true;
// Stack of mounts (in the mount(2) sense) that have been made.
MountStack stack_;
// Tracks loop device used for ephemeral cryptohome.
// Empty when the device is not present.
base::FilePath ephemeral_loop_device_;
// Tracks path to ephemeral cryptohome sparse file.
// Empty when the file is not created or already deleted.
base::FilePath ephemeral_file_path_;
Platform* platform_; // Un-owned.
FRIEND_TEST(MountTest, BindMyFilesDownloadsSuccess);
FRIEND_TEST(MountTest, BindMyFilesDownloadsMissingUserHome);
FRIEND_TEST(MountTest, BindMyFilesDownloadsMissingDownloads);
FRIEND_TEST(MountTest, BindMyFilesDownloadsMissingMyFilesDownloads);
FRIEND_TEST(MountTest, BindMyFilesDownloadsRemoveExistingFiles);
FRIEND_TEST(MountTest, BindMyFilesDownloadsMoveForgottenFiles);
FRIEND_TEST(MountTest, CreateTrackedSubdirectories);
FRIEND_TEST(MountTest, CreateTrackedSubdirectoriesReplaceExistingDir);
FRIEND_TEST(MountTest, RememberMountOrderingTest);
FRIEND_TEST(EphemeralNoUserSystemTest, CreateMyFilesDownloads);
FRIEND_TEST(EphemeralNoUserSystemTest, CreateMyFilesDownloadsAlreadyExists);
} // namespace cryptohome