chromium/chrome/browser/ash/smb_client/smb_service.h

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_SMB_CLIENT_SMB_SERVICE_H_
#define CHROME_BROWSER_ASH_SMB_CLIENT_SMB_SERVICE_H_

#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/files/file.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/ash/file_system_provider/provider_interface.h"
#include "chrome/browser/ash/file_system_provider/service.h"
#include "chrome/browser/ash/smb_client/smb_errors.h"
#include "chrome/browser/ash/smb_client/smb_persisted_share_registry.h"
#include "chrome/browser/ash/smb_client/smb_share_finder.h"
#include "chrome/browser/ash/smb_client/smbfs_share.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/dbus/smbprovider/smb_provider_client.h"
#include "components/keyed_service/core/keyed_service.h"
#include "net/base/network_change_notifier.h"

namespace base {
class FilePath;
}  // namespace base

namespace user_prefs {
class PrefRegistrySyncable;
}  // namespace user_prefs

namespace ash::smb_client {

class SmbKerberosCredentialsUpdater;
class SmbShareInfo;

// Creates and manages an smb file system.
class SmbService : public KeyedService,
                   public net::NetworkChangeNotifier::NetworkChangeObserver {
 public:
  using MountResponse = base::OnceCallback<void(SmbMountResult result)>;
  using StartReadDirIfSuccessfulCallback =
      base::OnceCallback<void(bool should_retry_start_read_dir)>;
  using GatherSharesResponse =
      base::RepeatingCallback<void(const std::vector<SmbUrl>& shares_gathered,
                                   bool done)>;

  SmbService(Profile* profile, std::unique_ptr<base::TickClock> tick_clock);
  SmbService(const SmbService&) = delete;
  SmbService& operator=(const SmbService&) = delete;
  ~SmbService() override;

  // KeyedService override.
  void Shutdown() override;

  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);

  // Starts the process of mounting an SMB file system. |use_kerberos| indicates
  // whether the share should be mounted with a Kerberos ticket - acquired
  // though Chromad login or KerberosCredentialsManager.
  void Mount(const std::string& display_name,
             const base::FilePath& share_path,
             const std::string& username,
             const std::string& password,
             bool use_kerberos,
             bool should_open_file_manager_after_mount,
             bool save_credentials,
             MountResponse callback);

  // Unmounts the SmbFs share mounted at |mount_path|.
  void UnmountSmbFs(const base::FilePath& mount_path);

  // Returns the SmbFsShare instance for the file at |path|. If |path| is not
  // part of an smbfs share, returns nullptr.
  SmbFsShare* GetSmbFsShareForPath(const base::FilePath& path);

  // Gathers the hosts in the network using |share_finder_| and gets the shares
  // for each of the hosts found. |discovery_callback| is called as soon as host
  // discovery is complete. |shares_callback| may be called multiple times with
  // new shares. |shares_callback| will be called with |done| == false when more
  // shares are expected to be discovered. When share discovery is finished,
  // |shares_callback| is called with |done| == true and will not be called
  // again.
  void GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,
                             GatherSharesResponse shares_callback);

  // Disable share discovery in test.
  static void DisableShareDiscoveryForTesting() {
    disable_share_discovery_for_testing_ = true;
  }

  // Run |callback| when setup had completed. If setup has already completed,
  // |callback| will be run inline.
  void OnSetupCompleteForTesting(base::OnceClosure callback);

  // Sets up Kerberos / AD services.
  void SetupKerberos(const std::string& account_identifier);

  // Updates credentials for Kerberos service.
  void UpdateKerberosCredentials(const std::string& account_identifier);

  // Returns true if the Kerberos feature is enabled.
  bool IsKerberosEnabledViaPolicy() const;

  // Sets the mounter creation callback, which is passed to
  // SmbFsShare::SetMounterCreationCallbackForTest() when a new SmbFs share is
  // created.
  void SetSmbFsMounterCreationCallbackForTesting(
      SmbFsShare::MounterCreationCallback callback);

  // Returns true if any SMB shares have been configured or saved before.
  bool IsAnySmbShareConfigured();

 private:
  FRIEND_TEST_ALL_PREFIXES(SmbServiceWithSmbfsTest, MountInvalidSaved);
  FRIEND_TEST_ALL_PREFIXES(SmbServiceWithSmbfsTest, MountInvalidPreconfigured);

  using MountInternalCallback =
      base::OnceCallback<void(SmbMountResult result,
                              const base::FilePath& mount_path)>;

  // Callback passed to MountInternal() when mounts are initiated
  // (generally by user interaction) via Mount().
  void OnUserInitiatedMountDone(MountResponse callback,
                                const SmbShareInfo& info,
                                bool should_open_file_manager_after_mount,
                                SmbMountResult result,
                                const base::FilePath& mount_path);

  // Mounts an SMB share with url |share_url| using either smbprovider or smbfs
  // based on feature flags.
  // Calls SmbProviderClient::Mount() or start the smbfs mount process.
  void MountInternal(const SmbShareInfo& info,
                     const std::string& password,
                     bool save_credentials,
                     bool skip_connect,
                     MountInternalCallback callback);

  // Handles the response from mounting an smbfs share. Passes |result| onto
  // |callback|.
  void OnSmbfsMountDone(const std::string& smbfs_mount_id,
                        MountInternalCallback callback,
                        SmbMountResult result);

  file_system_provider::Service* GetProviderService() const;

  SmbProviderClient* GetSmbProviderClient() const;

  // Attempts to restore any previously mounted shares remembered by the File
  // System Provider.
  void RestoreMounts();

  void OnHostsDiscovered(
      const std::vector<SmbShareInfo>& saved_smbfs_shares,
      const std::vector<SmbUrl>& preconfigured_shares);

  // Sets the callback passed to MountInternal() when a saved or
  // preconfigured share mount request is made during setup.
  void SetRestoredShareMountDoneCallbackForTesting(
      MountInternalCallback callback);

  // Mounts a saved (smbfs) SMB share with details |info|.
  void MountSavedSmbfsShare(const SmbShareInfo& info);

  // Handles the response from attempting to mount a previously saved share.
  void OnMountSavedSmbfsShareDone(SmbMountResult result,
                                  const base::FilePath& mount_path);

  // Mounts a preconfigured (by policy) SMB share with path |share_url|. The
  // share is mounted with empty credentials.
  void MountPreconfiguredShare(const SmbUrl& share_url);

  // Handles the response from attempting to mount a share configured via
  // policy.
  void OnMountPreconfiguredShareDone(SmbMountResult result,
                                     const base::FilePath& mount_path);

  // Completes SmbService setup including ShareFinder initialization and
  // remounting shares.
  void CompleteSetup();

  // Handles the response from attempting to setup Kerberos.
  void OnSetupKerberosResponse(bool success);

  // Handles the response from attempting to update Kerberos credentials.
  void OnUpdateKerberosCredentialsResponse(bool success);

  // Registers host locators for |share_finder_|.
  void RegisterHostLocators();

  // Set up Multicast DNS host locator.
  void SetUpMdnsHostLocator();

  // Set up NetBios host locator.
  void SetUpNetBiosHostLocator();

  // Whether NetBios discovery should be used. Controlled via policy.
  bool IsNetBiosDiscoveryEnabled() const;

  // Whether NTLM should be used. Controlled via policy.
  bool IsNTLMAuthenticationEnabled() const;

  // Whether |share| is already mounted.
  bool IsShareMounted(const SmbUrl& share) const;

  // Gets the list of all shares preconfigured via policy with mode
  // |policy_mode|. If |policy_mode| is "unknown", returns a list of all shares
  // preconfigured with a mode that does not match any currently known mode.
  // This can occur if a new policy is added not yet supported by CrOS.
  std::vector<SmbUrl> GetPreconfiguredSharePaths(
      const std::string& policy_mode) const;

  // Gets the shares preconfigured via policy that should be displayed in the
  // discovery dropdown. This includes shares that are explicitly set to be
  // shown in the dropdown as well as shares configured with an unrecognized
  // mode.
  std::vector<SmbUrl> GetPreconfiguredSharePathsForDropdown() const;

  // Gets the shares preconfigured via policy that should be premounted.
  std::vector<SmbUrl> GetPreconfiguredSharePathsForPremount() const;

  // Handles the callback for SmbFsShare::RemoveSavedCredentials().
  void OnSmbfsRemoveSavedCredentialsDone(const std::string& mount_id,
                                         bool success);

  // NetworkChangeNotifier::NetworkChangeObserver override. Runs HostDiscovery
  // when network detects a change.
  void OnNetworkChanged(
      net::NetworkChangeNotifier::ConnectionType type) override;

  // Records metrics on the number of SMB mounts a user has.
  void RecordMountCount() const;

  static bool disable_share_discovery_for_testing_;

  const file_system_provider::ProviderId provider_id_;
  raw_ptr<Profile> profile_;
  std::unique_ptr<SmbShareFinder> share_finder_;
  // |smbfs_mount_id| -> SmbFsShare
  // Note, mount ID for smbfs is a randomly generated string. For smbprovider
  // shares, it is an integer.
  std::unordered_map<std::string, std::unique_ptr<SmbFsShare>> smbfs_shares_;
  SmbPersistedShareRegistry registry_;

  std::unique_ptr<SmbKerberosCredentialsUpdater> kerberos_credentials_updater_;

  base::OnceClosure setup_complete_callback_;
  SmbFsShare::MounterCreationCallback smbfs_mounter_creation_callback_;
  MountInternalCallback restored_share_mount_done_callback_;
  base::WeakPtrFactory<SmbService> weak_ptr_factory_{this};
};

}  // namespace ash::smb_client

#endif  // CHROME_BROWSER_ASH_SMB_CLIENT_SMB_SERVICE_H_