blob: 808592a9e387c312341db7e5c74e6b2c3ff66a12 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "smbfs/smbfs_daemon.h"
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <utility>
#include <base/check.h>
#include <base/files/file_util.h>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/no_destructor.h>
#include <base/notreached.h>
#include <base/task/single_thread_task_runner.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/daemons/dbus_daemon.h>
#include <chromeos/dbus/service_constants.h>
#include <mojo/core/embedder/embedder.h>
#include <mojo/public/cpp/bindings/pending_receiver.h>
#include <mojo/public/cpp/platform/platform_channel.h>
#include <mojo/public/cpp/system/invitation.h>
#include "smbfs/dbus-proxies.h"
#include "smbfs/fuse_session.h"
#include "smbfs/smb_filesystem.h"
#include "smbfs/smbfs.h"
#include "smbfs/test_filesystem.h"
namespace smbfs {
namespace {
constexpr char kSmbConfDir[] = ".smb";
constexpr char kSmbConfFile[] = "smb.conf";
constexpr char kSmbConfData[] = R"(
[global]
client min protocol = SMB2
client max protocol = SMB3
security = user
)";
bool CreateDirectoryAndLog(const base::FilePath& path) {
CHECK(path.IsAbsolute());
base::File::Error error;
bool success = base::CreateDirectoryAndGetError(path, &error);
LOG_IF(ERROR, !success) << "Failed to create directory " << path.value()
<< ": " << base::File::ErrorToString(error);
return success;
}
// SmbFilesystem delegate that does nothing.
class NopSmbFilesystemDelegate : public SmbFilesystem::Delegate {
public:
NopSmbFilesystemDelegate() = default;
~NopSmbFilesystemDelegate() = default;
private:
// SmbFilesystem::Delegate overrides.:
void RequestCredentials(RequestCredentialsCallback callback) override {
// Respond with no null credentials, equivalent to the user canceling the
// request dialog.
std::move(callback).Run(nullptr);
}
};
} // namespace
SmbFsDaemon::SmbFsDaemon(fuse_chan* chan, const Options& options)
: chan_(chan),
use_test_fs_(options.use_test),
share_path_(options.share_path),
uid_(options.uid ? options.uid : getuid()),
gid_(options.gid ? options.gid : getgid()),
mojo_id_(options.mojo_id ? options.mojo_id : "") {
DCHECK(chan_);
}
SmbFsDaemon::~SmbFsDaemon() = default;
int SmbFsDaemon::OnInit() {
int ret = brillo::DBusDaemon::OnInit();
if (ret != EX_OK) {
return ret;
}
if (!SetupSmbConf()) {
return EX_SOFTWARE;
}
if (!share_path_.empty()) {
static NopSmbFilesystemDelegate dummy_delegate;
SmbFilesystem::Options options;
options.share_path = share_path_;
options.uid = uid_;
options.gid = gid_;
options.allow_ntlm = true;
std::unique_ptr<SmbFilesystem> fs =
std::make_unique<SmbFilesystem>(&dummy_delegate, std::move(options));
SmbFilesystem::ConnectError error = fs->EnsureConnected();
if (error != SmbFilesystem::ConnectError::kOk) {
LOG(ERROR) << "Unable to connect to SMB filesystem: " << error;
return EX_SOFTWARE;
}
fs_ = std::move(fs);
}
return EX_OK;
}
int SmbFsDaemon::OnEventLoopStarted() {
int ret = brillo::DBusDaemon::OnEventLoopStarted();
if (ret != EX_OK) {
return ret;
}
std::unique_ptr<Filesystem> fs;
if (use_test_fs_) {
fs = std::make_unique<TestFilesystem>(uid_, gid_);
} else if (fs_) {
fs = std::move(fs_);
} else if (!mojo_id_.empty()) {
if (!InitMojo()) {
return EX_SOFTWARE;
}
return EX_OK;
} else {
NOTREACHED();
}
session_ = std::make_unique<FuseSession>(std::move(fs), chan_);
chan_ = nullptr;
if (!session_->Start(base::BindOnce(&Daemon::Quit, base::Unretained(this)))) {
return EX_SOFTWARE;
}
return EX_OK;
}
bool SmbFsDaemon::SetupSmbConf() {
// Create a temporary "home" directory where configuration files used by
// libsmbclient will be placed.
CHECK(temp_dir_.CreateUniqueTempDir());
PCHECK(setenv("HOME", temp_dir_.GetPath().value().c_str(),
1 /* overwrite */) == 0);
LOG(INFO) << "Storing SMB configuration files in: "
<< temp_dir_.GetPath().value();
bool success = CreateDirectoryAndLog(temp_dir_.GetPath().Append(kSmbConfDir));
if (!success) {
return false;
}
// TODO(amistry): Replace with smbc_setOptionProtocols() when Samba is
// updated.
return base::WriteFile(
temp_dir_.GetPath().Append(kSmbConfDir).Append(kSmbConfFile),
kSmbConfData, sizeof(kSmbConfData)) == sizeof(kSmbConfData);
}
bool SmbFsDaemon::InitMojo() {
LOG(INFO) << "Boostrapping connection using Mojo";
mojo::core::Init();
ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
base::SingleThreadTaskRunner::GetCurrentDefault(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
mojo::PlatformChannel channel;
// The SmbFs service is hosted in the browser, so is expected to
// already be running when this starts. If this is not the case, the D-Bus
// IPC below will fail and this process will shut down.
org::chromium::SmbFsProxy dbus_proxy(bus_, kSmbFsServiceName);
brillo::ErrorPtr error;
if (!dbus_proxy.OpenIpcChannel(
mojo_id_, channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(),
&error)) {
return false;
}
mojo::IncomingInvitation invitation =
mojo::IncomingInvitation::Accept(channel.TakeLocalEndpoint());
mojo_session_ = std::make_unique<MojoSession>(
bus_, temp_dir_.GetPath(), chan_,
mojo::PendingReceiver<mojom::SmbFsBootstrap>(
invitation.ExtractMessagePipe(mojom::kBootstrapPipeName)),
uid_, gid_,
base::BindOnce(&SmbFsDaemon::OnSessionShutdown, base::Unretained(this)));
return true;
}
void SmbFsDaemon::OnSessionShutdown() {
LOG(INFO) << "Mojo session shut down. Exiting.";
QuitWithExitCode(EX_SOFTWARE);
}
} // namespace smbfs