1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_CHROMEOS_SMB_CLIENT_SMB_SERVICE_H_
6 #define CHROME_BROWSER_CHROMEOS_SMB_CLIENT_SMB_SERVICE_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 #include <unordered_map>
12 #include <vector>
13 
14 #include "base/callback.h"
15 #include "base/files/file.h"
16 #include "base/macros.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
20 #include "chrome/browser/chromeos/file_system_provider/provider_interface.h"
21 #include "chrome/browser/chromeos/file_system_provider/service.h"
22 #include "chrome/browser/chromeos/smb_client/smb_errors.h"
23 #include "chrome/browser/chromeos/smb_client/smb_persisted_share_registry.h"
24 #include "chrome/browser/chromeos/smb_client/smb_share_finder.h"
25 #include "chrome/browser/chromeos/smb_client/smb_task_queue.h"
26 #include "chrome/browser/chromeos/smb_client/smbfs_share.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chromeos/dbus/power/power_manager_client.h"
29 #include "chromeos/dbus/smb_provider_client.h"
30 #include "components/keyed_service/core/keyed_service.h"
31 #include "net/base/network_change_notifier.h"
32 
33 namespace base {
34 class FilePath;
35 }  // namespace base
36 
37 namespace user_prefs {
38 class PrefRegistrySyncable;
39 }  // namespace user_prefs
40 
41 namespace chromeos {
42 namespace smb_client {
43 
44 using file_system_provider::ProvidedFileSystemInfo;
45 
46 class SmbKerberosCredentialsUpdater;
47 class SmbShareInfo;
48 
49 // Creates and manages an smb file system.
50 class SmbService : public KeyedService,
51                    public net::NetworkChangeNotifier::NetworkChangeObserver,
52                    public chromeos::PowerManagerClient::Observer,
53                    public base::SupportsWeakPtr<SmbService> {
54  public:
55   using MountResponse = base::OnceCallback<void(SmbMountResult result)>;
56   using StartReadDirIfSuccessfulCallback =
57       base::OnceCallback<void(bool should_retry_start_read_dir)>;
58   using GatherSharesResponse =
59       base::RepeatingCallback<void(const std::vector<SmbUrl>& shares_gathered,
60                                    bool done)>;
61 
62   SmbService(Profile* profile, std::unique_ptr<base::TickClock> tick_clock);
63   ~SmbService() override;
64 
65   // KeyedService override.
66   void Shutdown() override;
67 
68   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
69 
70   // Starts the process of mounting an SMB file system.
71   // |use_kerberos| indicates whether the share should be mounted with a user's
72   // chromad kerberos tickets.
73   void Mount(const file_system_provider::MountOptions& options,
74              const base::FilePath& share_path,
75              const std::string& username,
76              const std::string& password,
77              bool use_kerberos,
78              bool should_open_file_manager_after_mount,
79              bool save_credentials,
80              MountResponse callback);
81 
82   // Unmounts the SmbFs share mounted at |mount_path|.
83   void UnmountSmbFs(const base::FilePath& mount_path);
84 
85   // Returns the SmbFsShare instance for the file at |path|. If |path| is not
86   // part of an smbfs share, returns nullptr.
87   SmbFsShare* GetSmbFsShareForPath(const base::FilePath& path);
88 
89   // Gathers the hosts in the network using |share_finder_| and gets the shares
90   // for each of the hosts found. |discovery_callback| is called as soon as host
91   // discovery is complete. |shares_callback| may be called multiple times with
92   // new shares. |shares_callback| will be called with |done| == false when more
93   // shares are expected to be discovered. When share discovery is finished,
94   // |shares_callback| is called with |done| == true and will not be called
95   // again.
96   void GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,
97                              GatherSharesResponse shares_callback);
98 
99   // Updates the share path for |mount_id|.
100   void UpdateSharePath(int32_t mount_id,
101                        const std::string& share_path,
102                        StartReadDirIfSuccessfulCallback reply);
103 
104   // Disable share discovery in test.
DisableShareDiscoveryForTesting()105   static void DisableShareDiscoveryForTesting() {
106     disable_share_discovery_for_testing_ = true;
107   }
108 
109   // Run |callback| when setup had completed. If setup has already completed,
110   // |callback| will be run inline.
111   void OnSetupCompleteForTesting(base::OnceClosure callback);
112 
113   // Sets up Kerberos / AD services.
114   void SetupKerberos(const std::string& account_identifier);
115 
116   // Updates credentials for Kerberos service.
117   void UpdateKerberosCredentials(const std::string& account_identifier);
118 
119   // Returns true if Kerberos was enabled via policy at service creation time
120   // and is still enabled now.
121   bool IsKerberosEnabledViaPolicy() const;
122 
123   // Sets the mounter creation callback, which is passed to
124   // SmbFsShare::SetMounterCreationCallbackForTest() when a new SmbFs share is
125   // created.
126   void SetSmbFsMounterCreationCallbackForTesting(
127       SmbFsShare::MounterCreationCallback callback);
128 
129   // chromeos::PowerManagerClient::Observer overrides
130   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
131   void SuspendDone(const base::TimeDelta& sleep_duration) override;
132 
133  private:
134   friend class SmbServiceTest;
135 
136   using MountInternalCallback =
137       base::OnceCallback<void(SmbMountResult result,
138                               const base::FilePath& mount_path)>;
139 
140   // Callback passed to MountInternal().
141   void MountInternalDone(MountResponse callback,
142                          const SmbShareInfo& info,
143                          bool should_open_file_manager_after_mount,
144                          SmbMountResult result,
145                          const base::FilePath& mount_path);
146 
147   // Mounts an SMB share with url |share_url| using either smbprovider or smbfs
148   // based on feature flags.
149   // Calls SmbProviderClient::Mount() or start the smbfs mount process.
150   void MountInternal(const file_system_provider::MountOptions& options,
151                      const SmbShareInfo& info,
152                      const std::string& password,
153                      bool save_credentials,
154                      bool skip_connect,
155                      MountInternalCallback callback);
156 
157   // Handles the response from mounting an SMB share using smbprovider.
158   // Completes the mounting of an SMB file system, passing |options| on to
159   // file_system_provider::Service::MountFileSystem(). Passes error status to
160   // callback.
161   void OnProviderMountDone(MountInternalCallback callback,
162                            const file_system_provider::MountOptions& options,
163                            bool save_credentials,
164                            smbprovider::ErrorType error,
165                            int32_t mount_id);
166 
167   // Handles the response from mounting an smbfs share. Passes |result| onto
168   // |callback|.
169   void OnSmbfsMountDone(const std::string& smbfs_mount_id,
170                         MountInternalCallback callback,
171                         SmbMountResult result);
172 
173   // Callback passed to SmbFsShare::Unmount() during a power management
174   // suspension. Ensures that suspension is blocked until the unmount completes.
175   void OnSuspendUnmountDone(base::UnguessableToken power_manager_suspend_token,
176                             chromeos::MountError result);
177 
178   // Retrieves the mount_id for |file_system_info|.
179   int32_t GetMountId(const ProvidedFileSystemInfo& info) const;
180 
181   // Calls file_system_provider::Service::UnmountFileSystem().
182   base::File::Error Unmount(
183       const std::string& file_system_id,
184       file_system_provider::Service::UnmountReason reason);
185 
186   file_system_provider::Service* GetProviderService() const;
187 
188   SmbProviderClient* GetSmbProviderClient() const;
189 
190   // Attempts to restore any previously mounted shares remembered by the File
191   // System Provider.
192   void RestoreMounts();
193 
194   void OnHostsDiscovered(
195       const std::vector<ProvidedFileSystemInfo>& file_systems,
196       const std::vector<SmbShareInfo>& saved_smbfs_shares,
197       const std::vector<SmbUrl>& preconfigured_shares);
198 
199   // Closure for OnHostDiscovered(). |reply| is passed down to
200   // UpdateSharePath().
201   void OnHostsDiscoveredForUpdateSharePath(
202       int32_t mount_id,
203       const std::string& share_path,
204       StartReadDirIfSuccessfulCallback reply);
205 
206   // Attempts to remount a share with the information in |file_system_info|.
207   void Remount(const ProvidedFileSystemInfo& file_system_info);
208 
209   // Handles the response from attempting to remount the file system. If
210   // remounting fails, this logs and removes the file_system from the volume
211   // manager.
212   void OnRemountResponse(const std::string& file_system_id,
213                          smbprovider::ErrorType error,
214                          int32_t mount_id);
215 
216   // Mounts a saved (smbfs) SMB share with details |info|.
217   void MountSavedSmbfsShare(const SmbShareInfo& info);
218 
219   // Mounts a preconfigured (by policy) SMB share with path |share_url|. The
220   // share is mounted with empty credentials.
221   void MountPreconfiguredShare(const SmbUrl& share_url);
222 
223   // Handles the response from attempting to mount a share configured via
224   // policy.
225   void OnMountPreconfiguredShareDone(SmbMountResult result,
226                                      const base::FilePath& mount_path);
227 
228   // Completes SmbService setup including ShareFinder initialization and
229   // remounting shares.
230   void CompleteSetup();
231 
232   // Handles the response from attempting to setup Kerberos.
233   void OnSetupKerberosResponse(bool success);
234 
235   // Handles the response from attempting to update Kerberos credentials.
236   void OnUpdateKerberosCredentialsResponse(bool success);
237 
238   // Registers host locators for |share_finder_|.
239   void RegisterHostLocators();
240 
241   // Set up Multicast DNS host locator.
242   void SetUpMdnsHostLocator();
243 
244   // Set up NetBios host locator.
245   void SetUpNetBiosHostLocator();
246 
247   // Whether NetBios discovery should be used. Controlled via policy.
248   bool IsNetBiosDiscoveryEnabled() const;
249 
250   // Whether NTLM should be used. Controlled via policy.
251   bool IsNTLMAuthenticationEnabled() const;
252 
253   // Whether |share| is already mounted.
254   bool IsShareMounted(const SmbUrl& share) const;
255 
256   // Gets the list of all shares preconfigured via policy with mode
257   // |policy_mode|. If |policy_mode| is "unknown", returns a list of all shares
258   // preconfigured with a mode that does not match any currently known mode.
259   // This can occur if a new policy is added not yet supported by CrOS.
260   std::vector<SmbUrl> GetPreconfiguredSharePaths(
261       const std::string& policy_mode) const;
262 
263   // Gets the shares preconfigured via policy that should be displayed in the
264   // discovery dropdown. This includes shares that are explicitly set to be
265   // shown in the dropdown as well as shares configured with an unrecognized
266   // mode.
267   std::vector<SmbUrl> GetPreconfiguredSharePathsForDropdown() const;
268 
269   // Gets the shares preconfigured via policy that should be premounted.
270   std::vector<SmbUrl> GetPreconfiguredSharePathsForPremount() const;
271 
272   // Requests new credentials for the |share_path|. |reply| is stored. Once the
273   // credentials have been successfully updated, |reply| is run.
274   void RequestCredentials(const std::string& share_path,
275                           int32_t mount_id,
276                           base::OnceClosure reply);
277 
278   // Handles the response from showing the SMB credentials dialog. If |canceled|
279   // is true, the |reply| callback is dropped. Otherwise, |username| and
280   // |password| are passed to the smb service and |reply| is run if the service
281   // returns success.
282   void OnSmbCredentialsDialogShown(int32_t mount_id,
283                                    base::OnceClosure reply,
284                                    bool canceled,
285                                    const std::string& username,
286                                    const std::string& password);
287 
288   // Requests an updated share path via running
289   // ShareFinder::DiscoverHostsInNetwork. |reply| is stored. Once the share path
290   // has been successfully updated, |reply| is run.
291   void RequestUpdatedSharePath(const std::string& share_path,
292                                int32_t mount_id,
293                                StartReadDirIfSuccessfulCallback reply);
294 
295   // Handles the response for attempting to update the share path of a mount.
296   // |reply| will run if |error| is ERROR_OK. Logs the error otherwise.
297   void OnUpdateSharePathResponse(int32_t mount_id,
298                                  StartReadDirIfSuccessfulCallback reply,
299                                  smbprovider::ErrorType error);
300 
301   // Handles the callback for SmbFsShare::RemoveSavedCredentials().
302   void OnSmbfsRemoveSavedCredentialsDone(const std::string& mount_id,
303                                          bool success);
304 
305   // Helper function that determines if HostDiscovery can be run again. Returns
306   // false if HostDiscovery was recently run.
307   bool ShouldRunHostDiscoveryAgain() const;
308 
309   // NetworkChangeNotifier::NetworkChangeObserver override. Runs HostDiscovery
310   // when network detects a change.
311   void OnNetworkChanged(
312       net::NetworkChangeNotifier::ConnectionType type) override;
313 
314   // Records metrics on the number of SMB mounts a user has.
315   void RecordMountCount() const;
316 
317   static bool disable_share_discovery_for_testing_;
318 
319   base::TimeTicks previous_host_discovery_time_;
320   const file_system_provider::ProviderId provider_id_;
321   Profile* profile_;
322   std::unique_ptr<base::TickClock> tick_clock_;
323   std::unique_ptr<SmbShareFinder> share_finder_;
324   // |file_system_id| -> |mount_id|
325   std::unordered_map<std::string, int32_t> mount_id_map_;
326   // |smbfs_mount_id| -> SmbFsShare
327   // Note, mount ID for smbfs is a randomly generated string. For smbprovider
328   // shares, it is an integer.
329   std::unordered_map<std::string, std::unique_ptr<SmbFsShare>> smbfs_shares_;
330   SmbPersistedShareRegistry registry_;
331 
332   std::unique_ptr<SmbKerberosCredentialsUpdater> smb_credentials_updater_;
333 
334   base::OnceClosure setup_complete_callback_;
335   SmbFsShare::MounterCreationCallback smbfs_mounter_creation_callback_;
336 
337   DISALLOW_COPY_AND_ASSIGN(SmbService);
338 };
339 
340 }  // namespace smb_client
341 }  // namespace chromeos
342 
343 #endif  // CHROME_BROWSER_CHROMEOS_SMB_CLIENT_SMB_SERVICE_H_
344