blob: 82c0bba2df45b2a44f4214208980d42f577c4c22 [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.
#include "smbfs/smbfs_daemon.h"
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <utility>
#include <base/bind.h>
#include <base/files/file_util.h>
#include <base/threading/thread_task_runner_handle.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/platform/platform_channel.h>
#include <mojo/public/cpp/system/invitation.h>
#include "smbfs/authpolicy_client.h"
#include "smbfs/dbus-proxies.h"
#include "smbfs/fuse_session.h"
#include "smbfs/kerberos_artifact_synchronizer.h"
#include "smbfs/kerberos_client.h"
#include "smbfs/smb_credential.h"
#include "smbfs/smb_filesystem.h"
#include "smbfs/smbfs.h"
#include "smbfs/smbfs_impl.h"
#include "smbfs/test_filesystem.h"
namespace smbfs {
namespace {
constexpr char kSmbConfDir[] = ".smb";
constexpr char kSmbConfFile[] = "smb.conf";
constexpr char kKerberosConfDir[] = ".krb";
constexpr char kKrb5ConfFile[] = "krb5.conf";
constexpr char kCCacheFile[] = "ccache";
constexpr char kKrbTraceFile[] = "krb_trace.txt";
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;
}
} // 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()) {
auto fs = std::make_unique<SmbFilesystem>(share_path_, uid_, gid_, nullptr);
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();
}
if (!StartFuseSession(std::move(fs))) {
return EX_SOFTWARE;
}
return EX_OK;
}
bool SmbFsDaemon::StartFuseSession(std::unique_ptr<Filesystem> fs) {
DCHECK(!session_);
DCHECK(chan_);
session_ = std::make_unique<FuseSession>(std::move(fs), chan_);
chan_ = nullptr;
return session_->Start(base::BindOnce(&Daemon::Quit, base::Unretained(this)));
}
base::FilePath SmbFsDaemon::KerberosConfFilePath(const std::string& file_name) {
DCHECK(temp_dir_.IsValid());
return temp_dir_.GetPath().Append(kKerberosConfDir).Append(file_name);
}
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);
PCHECK(setenv("KRB5_CONFIG",
KerberosConfFilePath(kKrb5ConfFile).value().c_str(),
1 /* overwrite */) == 0);
PCHECK(setenv("KRB5CCNAME", KerberosConfFilePath(kCCacheFile).value().c_str(),
1 /* overwrite */) == 0);
PCHECK(setenv("KRB5_TRACE",
KerberosConfFilePath(kKrbTraceFile).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)) &&
CreateDirectoryAndLog(temp_dir_.GetPath().Append(kKerberosConfDir));
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::ThreadTaskRunnerHandle::Get(),
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());
bootstrap_impl_ = std::make_unique<SmbFsBootstrapImpl>(
mojom::SmbFsBootstrapRequest(
invitation.ExtractMessagePipe(mojom::kBootstrapPipeName)),
this);
return true;
}
void SmbFsDaemon::OnBootstrapConnectionError() {
if (session_) {
// Do nothing because the session is running.
return;
}
LOG(ERROR) << "Connection error during Mojo bootstrap. Exiting.";
QuitWithExitCode(EX_SOFTWARE);
}
void SmbFsDaemon::SetupKerberos(
mojom::KerberosConfigPtr kerberos_config,
base::OnceCallback<void(bool success)> callback) {
DCHECK(!kerberos_sync_);
DCHECK(kerberos_config);
std::unique_ptr<KerberosArtifactClientInterface> client;
switch (kerberos_config->source) {
case mojom::KerberosConfig::Source::kActiveDirectory:
client = std::make_unique<AuthPolicyClient>(bus_);
break;
case mojom::KerberosConfig::Source::kKerberos:
client = std::make_unique<KerberosClient>(bus_);
break;
}
DCHECK(client);
kerberos_sync_ = std::make_unique<KerberosArtifactSynchronizer>(
KerberosConfFilePath(kKrb5ConfFile), KerberosConfFilePath(kCCacheFile),
kerberos_config->identity, std::move(client));
kerberos_sync_->SetupKerberos(std::move(callback));
}
std::unique_ptr<SmbFilesystem> SmbFsDaemon::CreateSmbFilesystem(
const std::string& share_path, std::unique_ptr<SmbCredential> credential) {
return std::make_unique<SmbFilesystem>(share_path, uid_, gid_,
std::move(credential));
}
} // namespace smbfs