Commit 8c6afe3a authored by Josh Simmons's avatar Josh Simmons Committed by Commit Bot

smbfs: Use stable mount path for smbfs shares

Uses a combination of share-specific values (user hash, URL, source,
authentication type, workgroup & username) as input to a SHA256 hash
that is then used to create a mount path that does not change across
mount / unmount cycles.

Test: unit_tests --gtest_filter="*SmbFs*"
Test: Deploy to DUT, ensure SMB share mounts successfully
Test: Mount share, unmount, remount, ensure mount path remains constant
Bug: 1130884
Change-Id: I24603cc9d09609e65c3b869c3c42142c30425788
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2430539
Commit-Queue: Josh Simmons <simmonsjosh@google.com>
Reviewed-by: default avatarAnand K Mistry <amistry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815026}
parent 6c0735e3
...@@ -8,12 +8,15 @@ ...@@ -8,12 +8,15 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/strcat.h" #include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h" #include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/smb_client/smb_service_helper.h" #include "chrome/browser/chromeos/smb_client/smb_service_helper.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chromeos/smb_shares/smb_credentials_dialog.h" #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_credentials_dialog.h"
#include "crypto/sha2.h"
#include "storage/browser/file_system/external_mount_points.h" #include "storage/browser/file_system/external_mount_points.h"
namespace chromeos { namespace chromeos {
...@@ -22,6 +25,7 @@ namespace smb_client { ...@@ -22,6 +25,7 @@ namespace smb_client {
namespace { namespace {
constexpr char kMountDirPrefix[] = "smbfs-"; constexpr char kMountDirPrefix[] = "smbfs-";
constexpr char kMountIdHashSeparator[] = "#";
constexpr base::TimeDelta kAllowCredentialsTimeout = constexpr base::TimeDelta kAllowCredentialsTimeout =
base::TimeDelta::FromSeconds(5); base::TimeDelta::FromSeconds(5);
...@@ -57,8 +61,7 @@ SmbFsShare::SmbFsShare(Profile* profile, ...@@ -57,8 +61,7 @@ SmbFsShare::SmbFsShare(Profile* profile,
share_url_(share_url), share_url_(share_url),
display_name_(display_name), display_name_(display_name),
options_(options), options_(options),
mount_id_( mount_id_(GenerateStableMountId()) {
base::ToLowerASCII(base::UnguessableToken::Create().ToString())) {
DCHECK(share_url_.IsValid()); DCHECK(share_url_.IsValid());
} }
...@@ -305,5 +308,40 @@ void SmbFsShare::SetMounterCreationCallbackForTest( ...@@ -305,5 +308,40 @@ void SmbFsShare::SetMounterCreationCallbackForTest(
mounter_creation_callback_for_test_ = std::move(callback); mounter_creation_callback_for_test_ = std::move(callback);
} }
std::string SmbFsShare::GenerateStableMountId() const {
std::string hash_input = GenerateStableMountIdInput();
return base::ToLowerASCII(base::HexEncode(
crypto::SHA256HashString(hash_input).c_str(), crypto::kSHA256Length));
}
std::string SmbFsShare::GenerateStableMountIdInput() const {
std::vector<std::string> mount_id_hash_components;
// Shares are unique based on the user the profile is owned by.
mount_id_hash_components.push_back(
chromeos::ProfileHelper::Get()->GetUserIdHashFromProfile(profile_));
// The hostname in the URL should be that entered by the user or
// specified in the preconfigured share policy. It should not have
// been resolved to an IP address if a hostname was specified.
//
// This property is true implicitly due to the call path for
// SmbFsShare creation.
mount_id_hash_components.push_back(share_url_.ToString());
// Distinguish between Kerberos and user-supplied credentials.
mount_id_hash_components.push_back(
base::NumberToString(options_.kerberos_options ? 1 : 0));
// Distinguish between domains / workgroups (may be empty for guest or
// Kerberos).
mount_id_hash_components.push_back(options_.workgroup);
// Distinguish between usernames (may be empty for guest or Kerberos).
mount_id_hash_components.push_back(options_.username);
return base::JoinString(mount_id_hash_components, kMountIdHashSeparator);
}
} // namespace smb_client } // namespace smb_client
} // namespace chromeos } // namespace chromeos
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file.h" #include "base/files/file.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/chromeos/smb_client/smb_errors.h" #include "chrome/browser/chromeos/smb_client/smb_errors.h"
...@@ -87,6 +88,9 @@ class SmbFsShare : public smbfs::SmbFsHost::Delegate { ...@@ -87,6 +88,9 @@ class SmbFsShare : public smbfs::SmbFsHost::Delegate {
void SetMounterCreationCallbackForTest(MounterCreationCallback callback); void SetMounterCreationCallbackForTest(MounterCreationCallback callback);
private: private:
FRIEND_TEST_ALL_PREFIXES(SmbFsShareTest, GenerateStableMountId);
FRIEND_TEST_ALL_PREFIXES(SmbFsShareTest, GenerateStableMountIdInput);
// Callback for smbfs::SmbFsMounter::Mount(). // Callback for smbfs::SmbFsMounter::Mount().
void OnMountDone(MountCallback callback, void OnMountDone(MountCallback callback,
smbfs::mojom::MountError mount_error, smbfs::mojom::MountError mount_error,
...@@ -112,6 +116,21 @@ class SmbFsShare : public smbfs::SmbFsHost::Delegate { ...@@ -112,6 +116,21 @@ class SmbFsShare : public smbfs::SmbFsHost::Delegate {
void OnDisconnected() override; void OnDisconnected() override;
void RequestCredentials(RequestCredentialsCallback callback) override; void RequestCredentials(RequestCredentialsCallback callback) override;
// Generate a stable ID to uniquely identify the share across each
// mount / unmount cycle. This allows the share to have the same path
// on the filesystem each time it is mounted.
//
// This function creates uniqueness beyond that currently enforced by
// the system (which presently only allows one share per canonical URL
// to be mounted). IDs generated here will be forward compatible in a
// future where the same share could be mounted once (ie. read-only)
// by preconfigured policy and subsequently by the user but using
// read-write credentials).
std::string GenerateStableMountId() const;
// Generate the input for stable mount ID hash (simplifies testing).
std::string GenerateStableMountIdInput() const;
Profile* const profile_; Profile* const profile_;
const SmbUrl share_url_; const SmbUrl share_url_;
const std::string display_name_; const std::string display_name_;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/gmock_callback_support.h" #include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include "chrome/browser/chromeos/file_manager/volume_manager.h" #include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/chromeos/file_manager/volume_manager_factory.h" #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
#include "chrome/browser/chromeos/file_manager/volume_manager_observer.h" #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/smb_client/smb_url.h" #include "chrome/browser/chromeos/smb_client/smb_url.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "chromeos/components/smbfs/smbfs_host.h" #include "chromeos/components/smbfs/smbfs_host.h"
...@@ -35,9 +37,15 @@ namespace smb_client { ...@@ -35,9 +37,15 @@ namespace smb_client {
namespace { namespace {
constexpr char kSharePath[] = "smb://share/path"; constexpr char kSharePath[] = "smb://share/path";
constexpr char kSharePath2[] = "smb://share/path2";
constexpr char kShareUsername[] = "user";
constexpr char kShareUsername2[] = "user2";
constexpr char kShareWorkgroup[] = "workgroup";
constexpr char kKerberosIdentity[] = "my-kerberos-identity";
constexpr char kDisplayName[] = "Public"; constexpr char kDisplayName[] = "Public";
constexpr char kMountPath[] = "/share/mount/path"; constexpr char kMountPath[] = "/share/mount/path";
constexpr char kFileName[] = "file_name.ext"; constexpr char kFileName[] = "file_name.ext";
constexpr char kMountIdHashSeparator[] = "#";
// Creates a new VolumeManager for tests. // Creates a new VolumeManager for tests.
// By default, VolumeManager KeyedService is null for testing. // By default, VolumeManager KeyedService is null for testing.
...@@ -87,6 +95,8 @@ class TestSmbFsImpl : public smbfs::mojom::SmbFs { ...@@ -87,6 +95,8 @@ class TestSmbFsImpl : public smbfs::mojom::SmbFs {
(override)); (override));
}; };
} // namespace
class SmbFsShareTest : public testing::Test { class SmbFsShareTest : public testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
...@@ -424,6 +434,66 @@ TEST_F(SmbFsShareTest, RemoveSavedCredentials_Disconnect) { ...@@ -424,6 +434,66 @@ TEST_F(SmbFsShareTest, RemoveSavedCredentials_Disconnect) {
} }
} }
} // namespace TEST_F(SmbFsShareTest, GenerateStableMountIdInput) {
TestSmbFsImpl smbfs;
std::string profile_user_hash =
chromeos::ProfileHelper::Get()->GetUserIdHashFromProfile(&profile_);
smbfs::SmbFsMounter::MountOptions options1;
options1.username = kShareUsername;
options1.workgroup = kShareWorkgroup;
SmbFsShare share1(&profile_, SmbUrl(kSharePath), kDisplayName, options1);
std::string hash_input1 = share1.GenerateStableMountIdInput();
std::vector<std::string> tokens1 =
base::SplitString(hash_input1, kMountIdHashSeparator,
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
EXPECT_EQ(tokens1.size(), 5);
EXPECT_EQ(tokens1[0], profile_user_hash);
EXPECT_EQ(tokens1[1], SmbUrl(kSharePath).ToString());
EXPECT_EQ(tokens1[2], "0" /* kerberos */);
EXPECT_EQ(tokens1[3], kShareWorkgroup);
EXPECT_EQ(tokens1[4], kShareUsername);
smbfs::SmbFsMounter::MountOptions options2;
options2.kerberos_options =
base::make_optional<smbfs::SmbFsMounter::KerberosOptions>(
smbfs::SmbFsMounter::KerberosOptions::Source::kKerberos,
kKerberosIdentity);
SmbFsShare share2(&profile_, SmbUrl(kSharePath2), kDisplayName, options2);
std::string hash_input2 = share2.GenerateStableMountIdInput();
std::vector<std::string> tokens2 =
base::SplitString(hash_input2, kMountIdHashSeparator,
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
EXPECT_EQ(tokens2.size(), 5);
EXPECT_EQ(tokens2[0], profile_user_hash);
EXPECT_EQ(tokens2[1], SmbUrl(kSharePath2).ToString());
EXPECT_EQ(tokens2[2], "1" /* kerberos */);
EXPECT_EQ(tokens2[3], "");
EXPECT_EQ(tokens2[4], "");
}
TEST_F(SmbFsShareTest, GenerateStableMountId) {
TestSmbFsImpl smbfs;
smbfs::SmbFsMounter::MountOptions options1;
options1.username = kShareUsername;
SmbFsShare share1(&profile_, SmbUrl(kSharePath), kDisplayName, options1);
std::string mount_id1 = share1.GenerateStableMountId();
// Check: We get a different hash when options are varied.
smbfs::SmbFsMounter::MountOptions options2;
options2.username = kShareUsername2;
SmbFsShare share2(&profile_, SmbUrl(kSharePath), kDisplayName, options2);
std::string mount_id2 = share2.GenerateStableMountId();
EXPECT_TRUE(mount_id1.compare(mount_id2));
// Check: String is 64 characters long (SHA256 encoded as hex).
EXPECT_EQ(mount_id1.size(), 64);
EXPECT_EQ(mount_id2.size(), 64);
}
} // namespace smb_client } // namespace smb_client
} // namespace chromeos } // namespace chromeos
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment