blob: cd1248d86adfc1c8ba0fb500d6d9701880af0c66 [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.
// This file gets compiled into the 'cryptohome-namespace-mounter' executable.
// This executable performs an ephemeral mount (for Guest sessions) on behalf of
// cryptohome.
// Eventually, this executable will perform all cryptohome mounts.
// The lifetime of this executable's process matches the lifetime of the mount:
// it's launched by cryptohome when a Guest session is requested, and it's
// killed by cryptohome when the Guest session exits.
#include <sys/types.h>
#include <sys/wait.h>
#include <sysexits.h>
#include <unistd.h>
#include <memory>
#include <base/at_exit.h>
#include <base/callback.h>
#include <base/callback_helpers.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/run_loop.h>
#include <brillo/asynchronous_signal_handler.h>
#include <brillo/cryptohome.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/scoped_mount_namespace.h>
#include <brillo/secure_blob.h>
#include <brillo/syslog_logging.h>
#include "cryptohome/cryptohome_common.h"
#include "cryptohome/mount_constants.h"
#include "cryptohome/mount_helper.h"
#include "cryptohome/mount_utils.h"
#include "cryptohome/namespace_mounter_ipc.pb.h"
using base::FilePath;
namespace {
// Forks a child process that immediately prints |message| and crashes.
// This is useful to report an error through crash reporting without taking down
// the entire cryptohome-namespace-mounter process, therefore allowing it to
// clean up and exit normally. This ensures the process doesn't leave mounts
// laying around.
void ForkAndCrash(const std::string& message) {
pid_t child_pid = fork();
if (child_pid < 0) {
PLOG(ERROR) << "fork() failed";
} else if (child_pid == 0) {
// Child process: crash with |message|.
LOG(FATAL) << message;
} else {
// |child_pid| > 0
// Parent process: reap the child process in a best-effort way and return
// normally.
// Reaping is not absolutely necessary because the parent process is also
// short-lived. As soon as this process exits the child process would get
// reparented to init and init would reap it. Still, reap for completeness.
waitpid(child_pid, nullptr, 0);
}
}
void TearDown(cryptohome::MountHelper* mounter) {
mounter->TearDownEphemeralMount();
}
bool TearDownFromSignal(cryptohome::MountHelper* mounter,
base::Closure quit_closure,
const struct signalfd_siginfo&) {
VLOG(1) << "Got signal";
TearDown(mounter);
quit_closure.Run();
return true; // unregister the handler
}
} // namespace
int main(int argc, char** argv) {
brillo::BaseMessageLoop message_loop;
message_loop.SetAsCurrent();
brillo::AsynchronousSignalHandler sig_handler;
sig_handler.Init();
brillo::InitLog(brillo::kLogToSyslog);
constexpr uid_t uid = 1000; // UID for 'chronos'.
constexpr gid_t gid = 1000; // GID for 'chronos'.
constexpr gid_t access_gid = 1001; // GID for 'chronos-access'.
cryptohome::OutOfProcessMountRequest request;
if (!cryptohome::ReadProtobuf(STDIN_FILENO, &request)) {
LOG(ERROR) << "Failed to read request protobuf";
return EX_NOINPUT;
}
brillo::SecureBlob system_salt;
brillo::SecureBlob::HexStringToSecureBlob(request.system_salt(),
&system_salt);
std::unique_ptr<brillo::ScopedMountNamespace> ns_mnt;
if (!request.mount_namespace_path().empty()) {
// Enter the required mount namespace.
ns_mnt = brillo::ScopedMountNamespace::CreateFromPath(
base::FilePath(request.mount_namespace_path()));
}
cryptohome::Platform platform;
cryptohome::MountHelper mounter(
uid, gid, access_gid, FilePath(cryptohome::kDefaultShadowRoot),
FilePath(cryptohome::kDefaultSkeletonSource), system_salt,
request.legacy_home(), &platform);
// If PerformEphemeralMount fails, or reporting back to cryptohome fails,
// attempt to clean up.
base::ScopedClosureRunner tear_down_runner(
base::Bind(&TearDown, base::Unretained(&mounter)));
if (!mounter.PerformEphemeralMount(request.username())) {
ForkAndCrash("PerformEphemeralMount failed");
return EX_SOFTWARE;
}
VLOG(1) << "PerformEphemeralMount succeeded";
cryptohome::OutOfProcessMountResponse response;
for (const auto& path : mounter.MountedPaths()) {
response.add_paths(path.value());
}
if (!cryptohome::WriteProtobuf(STDOUT_FILENO, response)) {
ForkAndCrash("Failed to write response protobuf");
return EX_OSERR;
}
VLOG(1) << "Sent protobuf";
// Mount and ack succeeded, release the closure without running it.
ignore_result(tear_down_runner.Release());
base::RunLoop run_loop;
// Clean up mounts when we get signalled.
sig_handler.RegisterHandler(
SIGTERM, base::Bind(&TearDownFromSignal, base::Unretained(&mounter),
run_loop.QuitClosure()));
run_loop.Run();
return EX_OK;
}