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 #include "chrome/browser/chromeos/smb_client/smb_service.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/task/post_task.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/time/default_tick_clock.h"
18 #include "base/unguessable_token.h"
19 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
20 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
21 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h"
22 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager_factory.h"
23 #include "chrome/browser/chromeos/profiles/profile_helper.h"
24 #include "chrome/browser/chromeos/smb_client/discovery/mdns_host_locator.h"
25 #include "chrome/browser/chromeos/smb_client/discovery/netbios_client.h"
26 #include "chrome/browser/chromeos/smb_client/discovery/netbios_host_locator.h"
27 #include "chrome/browser/chromeos/smb_client/smb_file_system.h"
28 #include "chrome/browser/chromeos/smb_client/smb_file_system_id.h"
29 #include "chrome/browser/chromeos/smb_client/smb_kerberos_credentials_updater.h"
30 #include "chrome/browser/chromeos/smb_client/smb_provider.h"
31 #include "chrome/browser/chromeos/smb_client/smb_service_helper.h"
32 #include "chrome/browser/chromeos/smb_client/smb_share_info.h"
33 #include "chrome/browser/chromeos/smb_client/smb_url.h"
34 #include "chrome/browser/platform_util.h"
35 #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_credentials_dialog.h"
36 #include "chrome/common/chrome_features.h"
37 #include "chrome/common/pref_names.h"
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "chromeos/dbus/smb_provider_client.h"
40 #include "components/pref_registry/pref_registry_syncable.h"
41 #include "components/prefs/pref_service.h"
42 #include "content/public/browser/browser_context.h"
43 #include "content/public/browser/storage_partition.h"
44 #include "crypto/random.h"
45 #include "net/base/network_interfaces.h"
46 #include "url/url_util.h"
47 
48 namespace chromeos {
49 namespace smb_client {
50 
51 namespace {
52 
53 const char kShareUrlKey[] = "share_url";
54 const char kModeKey[] = "mode";
55 const char kModeDropDownValue[] = "drop_down";
56 const char kModePreMountValue[] = "pre_mount";
57 const char kModeUnknownValue[] = "unknown";
58 const base::TimeDelta kHostDiscoveryInterval = base::TimeDelta::FromSeconds(60);
59 // -3 is chosen because -1 and -2 have special meaning in smbprovider.
60 const int32_t kInvalidMountId = -3;
61 // Maximum number of smbfs shares to be mounted at the same time, only enforced
62 // on user-initiated mount requests.
63 const size_t kMaxSmbFsShares = 16;
64 // Length of salt used to obfuscate stored password in smbfs.
65 const size_t kSaltLength = 16;
66 static_assert(kSaltLength >=
67                   smbfs::mojom::CredentialStorageOptions::kMinSaltLength,
68               "Minimum salt length is "
69               "smbfs::mojom::CredentialStorageOptions::kMinSaltLength");
70 
GetInterfaces()71 net::NetworkInterfaceList GetInterfaces() {
72   net::NetworkInterfaceList list;
73   if (!net::GetNetworkList(&list, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
74     LOG(ERROR) << "GetInterfaces failed";
75   }
76   return list;
77 }
78 
GetNetBiosClient(Profile * profile)79 std::unique_ptr<NetBiosClientInterface> GetNetBiosClient(Profile* profile) {
80   auto* network_context =
81       content::BrowserContext::GetDefaultStoragePartition(profile)
82           ->GetNetworkContext();
83   return std::make_unique<NetBiosClient>(network_context);
84 }
85 
IsSmbFsEnabled()86 bool IsSmbFsEnabled() {
87   return base::FeatureList::IsEnabled(features::kSmbFs);
88 }
89 
90 // Metric recording functions.
91 
92 // This enum is used to define the buckets for an enumerated UMA histogram.
93 // Hence,
94 //   (a) existing enumerated constants should never be deleted or reordered, and
95 //   (b) new constants should only be appended at the end of the enumeration.
96 enum class AuthMethod {
97   kNoCredentials = 0,
98   kUsernameOnly = 1,
99   kUsernameAndPassword = 2,
100   kSSOKerberosAD = 3,
101   kSSOKerberosGaia = 4,
102   kMaxValue = kSSOKerberosGaia,
103 };
104 
RecordMountResult(SmbMountResult result)105 void RecordMountResult(SmbMountResult result) {
106   DCHECK_LE(result, SmbMountResult::kMaxValue);
107   UMA_HISTOGRAM_ENUMERATION("NativeSmbFileShare.MountResult", result);
108 }
109 
RecordRemountResult(SmbMountResult result)110 void RecordRemountResult(SmbMountResult result) {
111   DCHECK_LE(result, SmbMountResult::kMaxValue);
112   UMA_HISTOGRAM_ENUMERATION("NativeSmbFileShare.RemountResult", result);
113 }
114 
RecordAuthenticationMethod(AuthMethod method)115 void RecordAuthenticationMethod(AuthMethod method) {
116   DCHECK_LE(method, AuthMethod::kMaxValue);
117   UMA_HISTOGRAM_ENUMERATION("NativeSmbFileShare.AuthenticationMethod", method);
118 }
119 
MakeFdWithContents(const std::string & contents)120 base::ScopedFD MakeFdWithContents(const std::string& contents) {
121   const size_t content_size = contents.size();
122 
123   base::ScopedFD read_fd, write_fd;
124   if (!base::CreatePipe(&read_fd, &write_fd, true /* non_blocking */)) {
125     LOG(ERROR) << "Unable to create pipe";
126     return {};
127   }
128   bool success =
129       base::WriteFileDescriptor(write_fd.get(),
130                                 reinterpret_cast<const char*>(&content_size),
131                                 sizeof(content_size)) &&
132       base::WriteFileDescriptor(write_fd.get(), contents.data(), content_size);
133   if (!success) {
134     PLOG(ERROR) << "Unable to write contents to pipe";
135     return {};
136   }
137   return read_fd;
138 }
139 
140 }  // namespace
141 
142 bool SmbService::disable_share_discovery_for_testing_ = false;
143 
SmbService(Profile * profile,std::unique_ptr<base::TickClock> tick_clock)144 SmbService::SmbService(Profile* profile,
145                        std::unique_ptr<base::TickClock> tick_clock)
146     : provider_id_(ProviderId::CreateFromNativeId("smb")),
147       profile_(profile),
148       tick_clock_(std::move(tick_clock)),
149       registry_(profile) {
150   user_manager::User* user =
151       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
152   DCHECK(user);
153 
154   SmbProviderClient* client = GetSmbProviderClient();
155   if (!client) {
156     return;
157   }
158 
159   if (user->IsActiveDirectoryUser()) {
160     const std::string& account_id_guid = user->GetAccountId().GetObjGuid();
161     SetupKerberos(account_id_guid);
162     return;
163   }
164 
165   KerberosCredentialsManager* credentials_manager =
166       KerberosCredentialsManagerFactory::GetExisting(profile);
167   if (credentials_manager && credentials_manager->IsKerberosEnabled()) {
168     smb_credentials_updater_ = std::make_unique<SmbKerberosCredentialsUpdater>(
169         credentials_manager,
170         base::BindRepeating(&SmbService::UpdateKerberosCredentials,
171                             AsWeakPtr()));
172     SetupKerberos(smb_credentials_updater_->active_account_name());
173     return;
174   }
175 
176   // Post a task to complete setup. This is to allow unit tests to perform
177   // expectations setup after constructing an instance. It also mirrors the
178   // behaviour when Kerberos is being used.
179   base::ThreadTaskRunnerHandle::Get()->PostTask(
180       FROM_HERE, base::BindOnce(&SmbService::CompleteSetup, AsWeakPtr()));
181 }
182 
~SmbService()183 SmbService::~SmbService() {
184   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
185   if (chromeos::PowerManagerClient::Get()) {
186     chromeos::PowerManagerClient::Get()->RemoveObserver(this);
187   }
188 }
189 
Shutdown()190 void SmbService::Shutdown() {
191   // Unmount and destroy all smbfs instances explicitly before destruction,
192   // since SmbFsShare accesses KeyedServices on destruction.
193   smbfs_shares_.clear();
194 }
195 
196 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)197 void SmbService::RegisterProfilePrefs(
198     user_prefs::PrefRegistrySyncable* registry) {
199   registry->RegisterBooleanPref(prefs::kNetworkFileSharesAllowed, true);
200   registry->RegisterBooleanPref(prefs::kNetBiosShareDiscoveryEnabled, true);
201   registry->RegisterBooleanPref(prefs::kNTLMShareAuthenticationEnabled, true);
202   registry->RegisterListPref(prefs::kNetworkFileSharesPreconfiguredShares);
203   registry->RegisterStringPref(prefs::kMostRecentlyUsedNetworkFileShareURL, "");
204   SmbPersistedShareRegistry::RegisterProfilePrefs(registry);
205 }
206 
UnmountSmbFs(const base::FilePath & mount_path)207 void SmbService::UnmountSmbFs(const base::FilePath& mount_path) {
208   DCHECK(!mount_path.empty());
209 
210   for (auto it = smbfs_shares_.begin(); it != smbfs_shares_.end(); ++it) {
211     SmbFsShare* share = it->second.get();
212     if (share->mount_path() == mount_path) {
213       if (share->options().save_restore_password) {
214         share->RemoveSavedCredentials(
215             base::BindOnce(&SmbService::OnSmbfsRemoveSavedCredentialsDone,
216                            base::Unretained(this), it->first));
217       } else {
218         // If the password wasn't saved, there's nothing for smbfs to do.
219         OnSmbfsRemoveSavedCredentialsDone(it->first, true /* success */);
220       }
221       return;
222     }
223   }
224 
225   LOG(WARNING) << "Smbfs mount path not found: " << mount_path;
226 }
227 
OnSmbfsRemoveSavedCredentialsDone(const std::string & mount_id,bool success)228 void SmbService::OnSmbfsRemoveSavedCredentialsDone(const std::string& mount_id,
229                                                    bool success) {
230   DCHECK(!mount_id.empty());
231 
232   auto it = smbfs_shares_.find(mount_id);
233   if (it == smbfs_shares_.end()) {
234     LOG(WARNING) << "Smbfs mount id " << mount_id << " already deleted";
235     return;
236   }
237 
238   // UnmountSmbFs() is called by an explicit unmount by the user. In this
239   // case, forget the share.
240   registry_.Delete(it->second->share_url());
241   smbfs_shares_.erase(it);
242 }
243 
GetSmbFsShareForPath(const base::FilePath & path)244 SmbFsShare* SmbService::GetSmbFsShareForPath(const base::FilePath& path) {
245   DCHECK(!path.empty());
246   DCHECK(path.IsAbsolute());
247 
248   for (const auto& entry : smbfs_shares_) {
249     const base::FilePath mount_path = entry.second->mount_path();
250     if (mount_path == path || mount_path.IsParent(path)) {
251       return entry.second.get();
252     }
253   }
254   return nullptr;
255 }
256 
GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,GatherSharesResponse shares_callback)257 void SmbService::GatherSharesInNetwork(HostDiscoveryResponse discovery_callback,
258                                        GatherSharesResponse shares_callback) {
259   auto preconfigured_shares = GetPreconfiguredSharePathsForDropdown();
260   if (!preconfigured_shares.empty()) {
261     shares_callback.Run(std::move(preconfigured_shares), false);
262   }
263   share_finder_->GatherSharesInNetwork(
264       std::move(discovery_callback),
265       base::BindOnce(
266           [](GatherSharesResponse shares_callback,
267              const std::vector<SmbUrl>& shares_gathered) {
268             std::move(shares_callback).Run(shares_gathered, true);
269           },
270           std::move(shares_callback)));
271 }
272 
UpdateSharePath(int32_t mount_id,const std::string & share_path,StartReadDirIfSuccessfulCallback reply)273 void SmbService::UpdateSharePath(int32_t mount_id,
274                                  const std::string& share_path,
275                                  StartReadDirIfSuccessfulCallback reply) {
276   GetSmbProviderClient()->UpdateSharePath(
277       mount_id, share_path,
278       base::BindOnce(&SmbService::OnUpdateSharePathResponse, AsWeakPtr(),
279                      mount_id, std::move(reply)));
280 }
281 
OnUpdateSharePathResponse(int32_t mount_id,StartReadDirIfSuccessfulCallback reply,smbprovider::ErrorType error)282 void SmbService::OnUpdateSharePathResponse(
283     int32_t mount_id,
284     StartReadDirIfSuccessfulCallback reply,
285     smbprovider::ErrorType error) {
286   if (error != smbprovider::ERROR_OK) {
287     LOG(ERROR) << "Failed to update the share path for mount id " << mount_id;
288     std::move(reply).Run(false /* should_retry_start_read_dir */);
289     return;
290   }
291   std::move(reply).Run(true /* should_retry_start_read_dir */);
292 }
293 
Mount(const file_system_provider::MountOptions & options,const base::FilePath & share_path,const std::string & username_input,const std::string & password_input,bool use_kerberos,bool should_open_file_manager_after_mount,bool save_credentials,MountResponse callback)294 void SmbService::Mount(const file_system_provider::MountOptions& options,
295                        const base::FilePath& share_path,
296                        const std::string& username_input,
297                        const std::string& password_input,
298                        bool use_kerberos,
299                        bool should_open_file_manager_after_mount,
300                        bool save_credentials,
301                        MountResponse callback) {
302   SmbUrl parsed_url(share_path.value());
303   if (!parsed_url.IsValid() || parsed_url.GetShare().empty()) {
304     // Handle invalid URLs early to avoid having unaccounted for UMA counts for
305     // authentication method.
306     std::move(callback).Run(SmbMountResult::kInvalidUrl);
307     return;
308   }
309 
310   // When using kerberos, the URL must contain the hostname because that is used
311   // to obtain the ticket. If the user enters an IP address, Samba will give us
312   // a permission error, which isn't correct or useful to the end user.
313   if (use_kerberos && url::HostIsIPAddress(parsed_url.GetHost())) {
314     std::move(callback).Run(SmbMountResult::kInvalidSsoUrl);
315     return;
316   }
317 
318   if (IsShareMounted(parsed_url)) {
319     // Prevent a share from being mounted twice. Although technically possible,
320     // the UX when doing so is incomplete.
321     std::move(callback).Run(SmbMountResult::kMountExists);
322     return;
323   }
324 
325   if (IsSmbFsEnabled() && smbfs_shares_.size() >= kMaxSmbFsShares) {
326     // Prevent users from mounting an excessive number of shares.
327     std::move(callback).Run(SmbMountResult::kTooManyOpened);
328     return;
329   }
330 
331   std::string username;
332   std::string password;
333   std::string workgroup;
334 
335   user_manager::User* user =
336       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
337   DCHECK(user);
338 
339   if (use_kerberos) {
340     // Differentiate between AD and KerberosEnabled via policy in metrics.
341     if (IsKerberosEnabledViaPolicy()) {
342       RecordAuthenticationMethod(AuthMethod::kSSOKerberosGaia);
343     } else {
344       RecordAuthenticationMethod(AuthMethod::kSSOKerberosAD);
345     }
346 
347     // Get the user's username and workgroup from their email address to be used
348     // for Kerberos authentication.
349     ParseUserPrincipalName(user->GetDisplayEmail(), &username, &workgroup);
350   } else {
351     // Record authentication method metrics.
352     if (!username_input.empty() && !password_input.empty()) {
353       RecordAuthenticationMethod(AuthMethod::kUsernameAndPassword);
354     } else if (!username_input.empty()) {
355       RecordAuthenticationMethod(AuthMethod::kUsernameOnly);
356     } else {
357       RecordAuthenticationMethod(AuthMethod::kNoCredentials);
358     }
359 
360     // Use provided credentials and parse the username into username and
361     // workgroup if necessary.
362     username = username_input;
363     password = password_input;
364     ParseUserName(username_input, &username, &workgroup);
365   }
366 
367   // Construct the file system ID before calling mount so that numerous
368   // arguments don't have to be plumbed through.
369   file_system_provider::MountOptions provider_options(options);
370   if (use_kerberos) {
371     provider_options.file_system_id =
372         CreateFileSystemId(share_path, use_kerberos);
373   } else {
374     std::string full_username;
375     if (save_credentials) {
376       // Only save the username if the user request credentials be saved.
377       full_username = username;
378       if (!workgroup.empty()) {
379         DCHECK(!username.empty());
380         full_username.append("@");
381         full_username.append(workgroup);
382       }
383     }
384     provider_options.file_system_id =
385         CreateFileSystemIdForUser(share_path, full_username);
386   }
387   std::vector<uint8_t> salt;
388   if (save_credentials && !password.empty()) {
389     // Only generate a salt if threre's a password and we've been asked to save
390     // credentials. If there is no password, there's nothing for smbfs to store
391     // and the salt is unused.
392     salt.resize(kSaltLength);
393     crypto::RandBytes(salt);
394   }
395   SmbShareInfo info(parsed_url, options.display_name, username, workgroup,
396                     use_kerberos, salt);
397   MountInternal(provider_options, info, password, save_credentials,
398                 false /* skip_connect */,
399                 base::BindOnce(&SmbService::MountInternalDone,
400                                base::Unretained(this), std::move(callback),
401                                info, should_open_file_manager_after_mount));
402 
403   profile_->GetPrefs()->SetString(prefs::kMostRecentlyUsedNetworkFileShareURL,
404                                   share_path.value());
405 }
406 
MountInternalDone(MountResponse callback,const SmbShareInfo & info,bool should_open_file_manager_after_mount,SmbMountResult result,const base::FilePath & mount_path)407 void SmbService::MountInternalDone(MountResponse callback,
408                                    const SmbShareInfo& info,
409                                    bool should_open_file_manager_after_mount,
410                                    SmbMountResult result,
411                                    const base::FilePath& mount_path) {
412   if (result != SmbMountResult::kSuccess) {
413     std::move(callback).Run(result);
414     return;
415   }
416 
417   DCHECK(!mount_path.empty());
418   if (should_open_file_manager_after_mount) {
419     platform_util::ShowItemInFolder(profile_, mount_path);
420   }
421 
422   if (IsSmbFsEnabled()) {
423     registry_.Save(info);
424   }
425 
426   RecordMountCount();
427   std::move(callback).Run(SmbMountResult::kSuccess);
428 }
429 
MountInternal(const file_system_provider::MountOptions & options,const SmbShareInfo & info,const std::string & password,bool save_credentials,bool skip_connect,MountInternalCallback callback)430 void SmbService::MountInternal(
431     const file_system_provider::MountOptions& options,
432     const SmbShareInfo& info,
433     const std::string& password,
434     bool save_credentials,
435     bool skip_connect,
436     MountInternalCallback callback) {
437   user_manager::User* user =
438       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
439   DCHECK(user);
440 
441   if (IsSmbFsEnabled()) {
442     SmbFsShare::MountOptions smbfs_options;
443     smbfs_options.resolved_host =
444         share_finder_->GetResolvedHost(info.share_url().GetHost());
445     smbfs_options.username = info.username();
446     smbfs_options.workgroup = info.workgroup();
447     smbfs_options.password = password;
448     smbfs_options.allow_ntlm = IsNTLMAuthenticationEnabled();
449     smbfs_options.skip_connect = skip_connect;
450     if (save_credentials && !info.password_salt().empty()) {
451       smbfs_options.save_restore_password = true;
452       smbfs_options.account_hash = user->username_hash();
453       smbfs_options.password_salt = info.password_salt();
454     }
455     if (info.use_kerberos()) {
456       if (user->IsActiveDirectoryUser()) {
457         smbfs_options.kerberos_options =
458             base::make_optional<SmbFsShare::KerberosOptions>(
459                 SmbFsShare::KerberosOptions::Source::kActiveDirectory,
460                 user->GetAccountId().GetObjGuid());
461       } else if (smb_credentials_updater_) {
462         smbfs_options.kerberos_options =
463             base::make_optional<SmbFsShare::KerberosOptions>(
464                 SmbFsShare::KerberosOptions::Source::kKerberos,
465                 smb_credentials_updater_->active_account_name());
466       } else {
467         LOG(WARNING) << "No Kerberos credential source available";
468         std::move(callback).Run(SmbMountResult::kAuthenticationFailed, {});
469         return;
470       }
471     }
472 
473     std::unique_ptr<SmbFsShare> mount = std::make_unique<SmbFsShare>(
474         profile_, info.share_url(), info.display_name(), smbfs_options);
475     if (smbfs_mounter_creation_callback_) {
476       mount->SetMounterCreationCallbackForTest(
477           smbfs_mounter_creation_callback_);
478     }
479 
480     SmbFsShare* raw_mount = mount.get();
481     const std::string mount_id = mount->mount_id();
482     smbfs_shares_[mount_id] = std::move(mount);
483     raw_mount->Mount(base::BindOnce(&SmbService::OnSmbfsMountDone, AsWeakPtr(),
484                                     mount_id, std::move(callback)));
485   } else {
486     // If using kerberos, the hostname should not be resolved since kerberos
487     // service tickets are keyed on hosname.
488     const SmbUrl url = info.use_kerberos()
489                            ? info.share_url()
490                            : share_finder_->GetResolvedUrl(info.share_url());
491 
492     SmbProviderClient::MountOptions smb_mount_options;
493     smb_mount_options.original_path = info.share_url().ToString();
494     smb_mount_options.username = info.username();
495     smb_mount_options.workgroup = info.workgroup();
496     smb_mount_options.ntlm_enabled = IsNTLMAuthenticationEnabled();
497     smb_mount_options.save_password = save_credentials && !info.use_kerberos();
498     smb_mount_options.account_hash = user->username_hash();
499     smb_mount_options.skip_connect = skip_connect;
500     GetSmbProviderClient()->Mount(
501         base::FilePath(url.ToString()), smb_mount_options,
502         MakeFdWithContents(password),
503         base::BindOnce(&SmbService::OnProviderMountDone, AsWeakPtr(),
504                        std::move(callback), options, save_credentials));
505   }
506 }
507 
OnSmbfsMountDone(const std::string & smbfs_mount_id,MountInternalCallback callback,SmbMountResult result)508 void SmbService::OnSmbfsMountDone(const std::string& smbfs_mount_id,
509                                   MountInternalCallback callback,
510                                   SmbMountResult result) {
511   RecordMountResult(result);
512 
513   if (result != SmbMountResult::kSuccess) {
514     smbfs_shares_.erase(smbfs_mount_id);
515     std::move(callback).Run(result, {});
516     return;
517   }
518 
519   SmbFsShare* mount = smbfs_shares_[smbfs_mount_id].get();
520   if (!mount) {
521     LOG(ERROR) << "smbfs mount " << smbfs_mount_id << " does not exist";
522     std::move(callback).Run(SmbMountResult::kUnknownFailure, {});
523     return;
524   }
525 
526   std::move(callback).Run(SmbMountResult::kSuccess, mount->mount_path());
527 }
528 
OnProviderMountDone(MountInternalCallback callback,const file_system_provider::MountOptions & options,bool save_credentials,smbprovider::ErrorType error,int32_t mount_id)529 void SmbService::OnProviderMountDone(
530     MountInternalCallback callback,
531     const file_system_provider::MountOptions& options,
532     bool save_credentials,
533     smbprovider::ErrorType error,
534     int32_t mount_id) {
535   SmbMountResult mount_result = TranslateErrorToMountResult(error);
536   RecordMountResult(mount_result);
537 
538   if (mount_result != SmbMountResult::kSuccess) {
539     std::move(callback).Run(mount_result, {});
540     return;
541   }
542 
543   DCHECK_GE(mount_id, 0);
544   mount_id_map_[options.file_system_id] = mount_id;
545 
546   base::File::Error result =
547       GetProviderService()->MountFileSystem(provider_id_, options);
548   if (result != base::File::FILE_OK) {
549     mount_id_map_.erase(options.file_system_id);
550     // If the password was asked to be saved, remove it.
551     GetSmbProviderClient()->Unmount(
552         mount_id, save_credentials /* remove_password */, base::DoNothing());
553 
554     std::move(callback).Run(TranslateErrorToMountResult(result), {});
555     return;
556   }
557 
558   base::FilePath mount_path = file_system_provider::util::GetMountPath(
559       profile_, provider_id_, options.file_system_id);
560   std::move(callback).Run(SmbMountResult::kSuccess, mount_path);
561 }
562 
GetMountId(const ProvidedFileSystemInfo & info) const563 int32_t SmbService::GetMountId(const ProvidedFileSystemInfo& info) const {
564   const auto iter = mount_id_map_.find(info.file_system_id());
565   if (iter == mount_id_map_.end()) {
566     // Either the mount process has not yet completed, or it failed to provide
567     // us with a mount id.
568     return kInvalidMountId;
569   }
570   return iter->second;
571 }
572 
Unmount(const std::string & file_system_id,file_system_provider::Service::UnmountReason reason)573 base::File::Error SmbService::Unmount(
574     const std::string& file_system_id,
575     file_system_provider::Service::UnmountReason reason) {
576   base::File::Error result = GetProviderService()->UnmountFileSystem(
577       provider_id_, file_system_id, reason);
578   // Always erase the mount_id, because at this point, the share has already
579   // been unmounted in smbprovider.
580   mount_id_map_.erase(file_system_id);
581   return result;
582 }
583 
GetProviderService() const584 file_system_provider::Service* SmbService::GetProviderService() const {
585   return file_system_provider::Service::Get(profile_);
586 }
587 
GetSmbProviderClient() const588 SmbProviderClient* SmbService::GetSmbProviderClient() const {
589   // If the DBusThreadManager or the SmbProviderClient aren't available,
590   // there isn't much we can do. This should only happen when running tests.
591   if (!chromeos::DBusThreadManager::IsInitialized() ||
592       !chromeos::DBusThreadManager::Get()) {
593     return nullptr;
594   }
595   return chromeos::DBusThreadManager::Get()->GetSmbProviderClient();
596 }
597 
RestoreMounts()598 void SmbService::RestoreMounts() {
599   std::vector<ProvidedFileSystemInfo> provided_file_systems =
600       GetProviderService()->GetProvidedFileSystemInfoList(provider_id_);
601 
602   std::vector<SmbUrl> preconfigured_shares =
603       GetPreconfiguredSharePathsForPremount();
604 
605   std::vector<SmbShareInfo> saved_smbfs_shares;
606   if (IsSmbFsEnabled()) {
607     // Restore smbfs shares.
608     // TODO(crbug.com/1055571): Migrate saved smbprovider shares to smbfs.
609     saved_smbfs_shares = registry_.GetAll();
610   }
611 
612   if (!provided_file_systems.empty() || !saved_smbfs_shares.empty() ||
613       !preconfigured_shares.empty()) {
614     share_finder_->DiscoverHostsInNetwork(base::BindOnce(
615         &SmbService::OnHostsDiscovered, AsWeakPtr(),
616         std::move(provided_file_systems), std::move(saved_smbfs_shares),
617         std::move(preconfigured_shares)));
618   }
619 }
620 
OnHostsDiscovered(const std::vector<ProvidedFileSystemInfo> & file_systems,const std::vector<SmbShareInfo> & saved_smbfs_shares,const std::vector<SmbUrl> & preconfigured_shares)621 void SmbService::OnHostsDiscovered(
622     const std::vector<ProvidedFileSystemInfo>& file_systems,
623     const std::vector<SmbShareInfo>& saved_smbfs_shares,
624     const std::vector<SmbUrl>& preconfigured_shares) {
625   for (const auto& file_system : file_systems) {
626     Remount(file_system);
627   }
628   for (const auto& smbfs_share : saved_smbfs_shares) {
629     MountSavedSmbfsShare(smbfs_share);
630   }
631   for (const auto& url : preconfigured_shares) {
632     MountPreconfiguredShare(url);
633   }
634 }
635 
OnHostsDiscoveredForUpdateSharePath(int32_t mount_id,const std::string & share_path,StartReadDirIfSuccessfulCallback reply)636 void SmbService::OnHostsDiscoveredForUpdateSharePath(
637     int32_t mount_id,
638     const std::string& share_path,
639     StartReadDirIfSuccessfulCallback reply) {
640   SmbUrl resolved_url(share_path);
641   if (share_finder_->TryResolveUrl(SmbUrl(share_path), &resolved_url)) {
642     UpdateSharePath(mount_id, resolved_url.ToString(), std::move(reply));
643   } else {
644     std::move(reply).Run(false /* should_retry_start_read_dir */);
645   }
646 }
647 
Remount(const ProvidedFileSystemInfo & file_system_info)648 void SmbService::Remount(const ProvidedFileSystemInfo& file_system_info) {
649   const base::FilePath share_path =
650       GetSharePathFromFileSystemId(file_system_info.file_system_id());
651   const bool is_kerberos_chromad =
652       IsKerberosChromadFileSystemId(file_system_info.file_system_id());
653 
654   std::string workgroup;
655   std::string username;
656 
657   user_manager::User* user =
658       chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
659   DCHECK(user);
660   if (is_kerberos_chromad) {
661     DCHECK(user->IsActiveDirectoryUser());
662 
663     ParseUserPrincipalName(user->GetDisplayEmail(), &username, &workgroup);
664   } else {
665     base::Optional<std::string> user_workgroup =
666         GetUserFromFileSystemId(file_system_info.file_system_id());
667     if (user_workgroup &&
668         !ParseUserName(*user_workgroup, &username, &workgroup)) {
669       LOG(ERROR) << "Failed to parse username/workgroup from file system ID";
670     }
671   }
672 
673   SmbUrl parsed_url(share_path.value());
674   if (!parsed_url.IsValid()) {
675     OnRemountResponse(file_system_info.file_system_id(),
676                       smbprovider::ERROR_INVALID_URL, kInvalidMountId);
677     return;
678   }
679 
680   // If using kerberos, the hostname should not be resolved since kerberos
681   // service tickets are keyed on hosname.
682   const SmbUrl resolved_url = is_kerberos_chromad
683                                   ? parsed_url
684                                   : share_finder_->GetResolvedUrl(parsed_url);
685 
686   // An empty password is passed to Mount to conform with the credentials API
687   // which expects username & workgroup strings along with a password file
688   // descriptor.
689   SmbProviderClient::MountOptions smb_mount_options;
690   smb_mount_options.original_path = parsed_url.ToString();
691   smb_mount_options.username = username;
692   smb_mount_options.workgroup = workgroup;
693   smb_mount_options.ntlm_enabled = IsNTLMAuthenticationEnabled();
694   smb_mount_options.skip_connect = true;
695   smb_mount_options.restore_password =
696       !username.empty() && !is_kerberos_chromad;
697   smb_mount_options.account_hash = user->username_hash();
698   GetSmbProviderClient()->Mount(
699       base::FilePath(resolved_url.ToString()), smb_mount_options,
700       MakeFdWithContents(""),
701       base::BindOnce(&SmbService::OnRemountResponse, AsWeakPtr(),
702                      file_system_info.file_system_id()));
703 }
704 
OnRemountResponse(const std::string & file_system_id,smbprovider::ErrorType error,int32_t mount_id)705 void SmbService::OnRemountResponse(const std::string& file_system_id,
706                                    smbprovider::ErrorType error,
707                                    int32_t mount_id) {
708   RecordRemountResult(TranslateErrorToMountResult(error));
709 
710   if (error != smbprovider::ERROR_OK) {
711     LOG(ERROR) << "SmbService: failed to restore filesystem with error: "
712                << error;
713     // Note: The filesystem isn't removed on failure because doing so will
714     // stop persisting the mount. The mount should only be removed as a result
715     // of user action, and not due to failures, which might be transient (i.e.
716     // smbprovider crashed).
717     return;
718   }
719 
720   DCHECK_GE(mount_id, 0);
721   mount_id_map_[file_system_id] = mount_id;
722 }
723 
MountSavedSmbfsShare(const SmbShareInfo & info)724 void SmbService::MountSavedSmbfsShare(const SmbShareInfo& info) {
725   MountInternal(
726       {} /* fsp::MountOptions, ignored by smbfs */, info, "" /* password */,
727       true /* save_credentials */, true /* skip_connect */,
728       base::BindOnce(
729           [](SmbMountResult result, const base::FilePath& mount_path) {
730             LOG_IF(ERROR, result != SmbMountResult::kSuccess)
731                 << "Error restoring saved share: " << static_cast<int>(result);
732           }));
733 }
734 
MountPreconfiguredShare(const SmbUrl & share_url)735 void SmbService::MountPreconfiguredShare(const SmbUrl& share_url) {
736   file_system_provider::MountOptions mount_options;
737   mount_options.display_name =
738       base::FilePath(share_url.ToString()).BaseName().value();
739   mount_options.writable = true;
740   // |is_chromad_kerberos| is false because we do not pass user and workgroup
741   // at mount time. Premounts also do not get remounted and currently
742   // |is_chromad_kerberos| is only used at remounts to determine if the share
743   // was mounted with chromad kerberos.
744   // TODO(crbug.com/922269): Support kerberos for preconfigured shares.
745   mount_options.file_system_id = CreateFileSystemId(
746       base::FilePath(share_url.ToString()), false /* is_chromad_kerberos */);
747   // Disable remounting of preconfigured shares.
748   mount_options.persistent = false;
749 
750   // Note: Preconfigured shares are mounted without credentials.
751   SmbShareInfo info(share_url, mount_options.display_name, "" /* username */,
752                     "" /* workgroup */, false /* use_kerberos */);
753   MountInternal(
754       mount_options, info, "" /* password */, false /* save_credentials */,
755       true /* skip_connect */,
756       base::BindOnce(&SmbService::OnMountPreconfiguredShareDone, AsWeakPtr()));
757 }
758 
OnMountPreconfiguredShareDone(SmbMountResult result,const base::FilePath & mount_path)759 void SmbService::OnMountPreconfiguredShareDone(
760     SmbMountResult result,
761     const base::FilePath& mount_path) {
762   LOG_IF(ERROR, result != SmbMountResult::kSuccess)
763       << "Error mounting preconfigured share: " << static_cast<int>(result);
764 }
765 
IsKerberosEnabledViaPolicy() const766 bool SmbService::IsKerberosEnabledViaPolicy() const {
767   return smb_credentials_updater_ &&
768          smb_credentials_updater_->IsKerberosEnabled();
769 }
770 
SetupKerberos(const std::string & account_identifier)771 void SmbService::SetupKerberos(const std::string& account_identifier) {
772   SmbProviderClient* client = GetSmbProviderClient();
773   if (!client) {
774     return;
775   }
776 
777   client->SetupKerberos(
778       account_identifier,
779       base::BindOnce(&SmbService::OnSetupKerberosResponse, AsWeakPtr()));
780 }
781 
UpdateKerberosCredentials(const std::string & account_identifier)782 void SmbService::UpdateKerberosCredentials(
783     const std::string& account_identifier) {
784   SmbProviderClient* client = GetSmbProviderClient();
785   if (!client) {
786     return;
787   }
788 
789   client->SetupKerberos(
790       account_identifier,
791       base::BindOnce(&SmbService::OnUpdateKerberosCredentialsResponse,
792                      AsWeakPtr()));
793 }
794 
OnUpdateKerberosCredentialsResponse(bool success)795 void SmbService::OnUpdateKerberosCredentialsResponse(bool success) {
796   LOG_IF(ERROR, !success) << "Update Kerberos credentials failed.";
797 }
798 
OnSetupKerberosResponse(bool success)799 void SmbService::OnSetupKerberosResponse(bool success) {
800   if (!success) {
801     LOG(ERROR) << "SmbService: Kerberos setup failed.";
802   }
803 
804   CompleteSetup();
805 }
806 
CompleteSetup()807 void SmbService::CompleteSetup() {
808   share_finder_ = std::make_unique<SmbShareFinder>(GetSmbProviderClient());
809   RegisterHostLocators();
810 
811   GetProviderService()->RegisterProvider(std::make_unique<SmbProvider>(
812       base::BindRepeating(&SmbService::GetMountId, base::Unretained(this)),
813       base::BindRepeating(&SmbService::Unmount, base::Unretained(this)),
814       base::BindRepeating(&SmbService::RequestCredentials,
815                           base::Unretained(this)),
816       base::BindRepeating(&SmbService::RequestUpdatedSharePath,
817                           base::Unretained(this))));
818   RestoreMounts();
819   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
820   if (chromeos::PowerManagerClient::Get()) {
821     chromeos::PowerManagerClient::Get()->AddObserver(this);
822   }
823 
824   if (setup_complete_callback_) {
825     std::move(setup_complete_callback_).Run();
826   }
827 }
828 
OnSetupCompleteForTesting(base::OnceClosure callback)829 void SmbService::OnSetupCompleteForTesting(base::OnceClosure callback) {
830   DCHECK(!setup_complete_callback_);
831   if (share_finder_) {
832     std::move(callback).Run();
833     return;
834   }
835   setup_complete_callback_ = std::move(callback);
836 }
837 
SetSmbFsMounterCreationCallbackForTesting(SmbFsShare::MounterCreationCallback callback)838 void SmbService::SetSmbFsMounterCreationCallbackForTesting(
839     SmbFsShare::MounterCreationCallback callback) {
840   smbfs_mounter_creation_callback_ = std::move(callback);
841 }
842 
RegisterHostLocators()843 void SmbService::RegisterHostLocators() {
844   if (disable_share_discovery_for_testing_) {
845     return;
846   }
847 
848   SetUpMdnsHostLocator();
849   if (IsNetBiosDiscoveryEnabled()) {
850     SetUpNetBiosHostLocator();
851   } else {
852     LOG(WARNING) << "SmbService: NetBios discovery disabled.";
853   }
854 }
855 
SetUpMdnsHostLocator()856 void SmbService::SetUpMdnsHostLocator() {
857   share_finder_->RegisterHostLocator(std::make_unique<MDnsHostLocator>());
858 }
859 
SetUpNetBiosHostLocator()860 void SmbService::SetUpNetBiosHostLocator() {
861   auto get_interfaces = base::BindRepeating(&GetInterfaces);
862   auto client_factory = base::BindRepeating(&GetNetBiosClient, profile_);
863 
864   auto netbios_host_locator = std::make_unique<NetBiosHostLocator>(
865       std::move(get_interfaces), std::move(client_factory),
866       GetSmbProviderClient());
867 
868   share_finder_->RegisterHostLocator(std::move(netbios_host_locator));
869 }
870 
IsNetBiosDiscoveryEnabled() const871 bool SmbService::IsNetBiosDiscoveryEnabled() const {
872   return profile_->GetPrefs()->GetBoolean(prefs::kNetBiosShareDiscoveryEnabled);
873 }
874 
IsNTLMAuthenticationEnabled() const875 bool SmbService::IsNTLMAuthenticationEnabled() const {
876   return profile_->GetPrefs()->GetBoolean(
877       prefs::kNTLMShareAuthenticationEnabled);
878 }
879 
IsShareMounted(const SmbUrl & share) const880 bool SmbService::IsShareMounted(const SmbUrl& share) const {
881   std::vector<ProvidedFileSystemInfo> file_systems =
882       GetProviderService()->GetProvidedFileSystemInfoList(provider_id_);
883 
884   for (const auto& info : file_systems) {
885     base::FilePath share_path =
886         GetSharePathFromFileSystemId(info.file_system_id());
887     SmbUrl parsed_url(share_path.value());
888     DCHECK(parsed_url.IsValid());
889     if (parsed_url.ToString() == share.ToString()) {
890       return true;
891     }
892   }
893 
894   for (const auto& entry : smbfs_shares_) {
895     if (entry.second->share_url().ToString() == share.ToString()) {
896       return true;
897     }
898   }
899   return false;
900 }
901 
GetPreconfiguredSharePaths(const std::string & policy_mode) const902 std::vector<SmbUrl> SmbService::GetPreconfiguredSharePaths(
903     const std::string& policy_mode) const {
904   std::vector<SmbUrl> preconfigured_urls;
905 
906   const base::Value* preconfigured_shares = profile_->GetPrefs()->GetList(
907       prefs::kNetworkFileSharesPreconfiguredShares);
908 
909   for (const base::Value& info : preconfigured_shares->GetList()) {
910     // |info| is a dictionary with entries for |share_url| and |mode|.
911     const base::Value* share_url = info.FindKey(kShareUrlKey);
912     const base::Value* mode = info.FindKey(kModeKey);
913 
914     if (policy_mode == kModeUnknownValue) {
915       // kModeUnknownValue is used to filter for any shares that do not match
916       // a presently known mode for preconfiguration. As new preconfigure
917       // modes are added, this should be kept in sync.
918       if (mode->GetString() != kModeDropDownValue &&
919           mode->GetString() != kModePreMountValue) {
920         preconfigured_urls.emplace_back(share_url->GetString());
921       }
922 
923     } else {
924       // Filter normally
925       if (mode->GetString() == policy_mode) {
926         preconfigured_urls.emplace_back(share_url->GetString());
927       }
928     }
929   }
930   return preconfigured_urls;
931 }
932 
RequestCredentials(const std::string & share_path,int32_t mount_id,base::OnceClosure reply)933 void SmbService::RequestCredentials(const std::string& share_path,
934                                     int32_t mount_id,
935                                     base::OnceClosure reply) {
936   smb_dialog::SmbCredentialsDialog::Show(
937       base::NumberToString(mount_id), share_path,
938       base::BindOnce(&SmbService::OnSmbCredentialsDialogShown, AsWeakPtr(),
939                      mount_id, std::move(reply)));
940 }
941 
OnSmbCredentialsDialogShown(int32_t mount_id,base::OnceClosure reply,bool canceled,const std::string & username,const std::string & password)942 void SmbService::OnSmbCredentialsDialogShown(int32_t mount_id,
943                                              base::OnceClosure reply,
944                                              bool canceled,
945                                              const std::string& username,
946                                              const std::string& password) {
947   if (canceled) {
948     return;
949   }
950 
951   std::string parsed_username = username;
952   std::string workgroup;
953   ParseUserName(username, &parsed_username, &workgroup);
954 
955   GetSmbProviderClient()->UpdateMountCredentials(
956       mount_id, workgroup, parsed_username, MakeFdWithContents(password),
957       base::BindOnce(
958           [](int32_t mount_id, base::OnceClosure reply,
959              smbprovider::ErrorType error) {
960             if (error == smbprovider::ERROR_OK) {
961               std::move(reply).Run();
962             } else {
963               LOG(ERROR) << "Failed to update the credentials for mount id "
964                          << mount_id;
965             }
966           },
967           mount_id, std::move(reply)));
968 }
969 
GetPreconfiguredSharePathsForDropdown() const970 std::vector<SmbUrl> SmbService::GetPreconfiguredSharePathsForDropdown() const {
971   auto drop_down_paths = GetPreconfiguredSharePaths(kModeDropDownValue);
972   auto fallback_paths = GetPreconfiguredSharePaths(kModeUnknownValue);
973 
974   for (auto&& fallback_path : fallback_paths) {
975     drop_down_paths.push_back(std::move(fallback_path));
976   }
977 
978   return drop_down_paths;
979 }
980 
GetPreconfiguredSharePathsForPremount() const981 std::vector<SmbUrl> SmbService::GetPreconfiguredSharePathsForPremount() const {
982   return GetPreconfiguredSharePaths(kModePreMountValue);
983 }
984 
RequestUpdatedSharePath(const std::string & share_path,int32_t mount_id,StartReadDirIfSuccessfulCallback reply)985 void SmbService::RequestUpdatedSharePath(
986     const std::string& share_path,
987     int32_t mount_id,
988     StartReadDirIfSuccessfulCallback reply) {
989   if (ShouldRunHostDiscoveryAgain()) {
990     previous_host_discovery_time_ = tick_clock_->NowTicks();
991     share_finder_->DiscoverHostsInNetwork(
992         base::BindOnce(&SmbService::OnHostsDiscoveredForUpdateSharePath,
993                        AsWeakPtr(), mount_id, share_path, std::move(reply)));
994     return;
995   }
996   // Host discovery did not run, but try to resolve the hostname in case a
997   // previous host discovery found the host.
998   SmbUrl resolved_url(share_path);
999   if (share_finder_->TryResolveUrl(SmbUrl(share_path), &resolved_url)) {
1000     UpdateSharePath(mount_id, share_path, std::move(reply));
1001   } else {
1002     std::move(reply).Run(false /* should_retry_start_read_dir */);
1003   }
1004 }
1005 
ShouldRunHostDiscoveryAgain() const1006 bool SmbService::ShouldRunHostDiscoveryAgain() const {
1007   return tick_clock_->NowTicks() >
1008          previous_host_discovery_time_ + kHostDiscoveryInterval;
1009 }
1010 
OnNetworkChanged(net::NetworkChangeNotifier::ConnectionType type)1011 void SmbService::OnNetworkChanged(
1012     net::NetworkChangeNotifier::ConnectionType type) {
1013   // Run host discovery to refresh list of cached hosts for subsequent name
1014   // resolution attempts.
1015   share_finder_->DiscoverHostsInNetwork(base::DoNothing()
1016                                         /* HostDiscoveryResponse */);
1017 }
1018 
RecordMountCount() const1019 void SmbService::RecordMountCount() const {
1020   const std::vector<ProvidedFileSystemInfo> file_systems =
1021       GetProviderService()->GetProvidedFileSystemInfoList(provider_id_);
1022   UMA_HISTOGRAM_COUNTS_100("NativeSmbFileShare.MountCount",
1023                            file_systems.size() + smbfs_shares_.size());
1024 }
1025 
SuspendImminent(power_manager::SuspendImminent::Reason reason)1026 void SmbService::SuspendImminent(
1027     power_manager::SuspendImminent::Reason reason) {
1028   for (auto it = smbfs_shares_.begin(); it != smbfs_shares_.end(); ++it) {
1029     SmbFsShare* share = it->second.get();
1030 
1031     // For each share, block suspend until the unmount has completed, to ensure
1032     // that no smbfs instances are active when the system goes to sleep.
1033     auto token = base::UnguessableToken::Create();
1034     chromeos::PowerManagerClient::Get()->BlockSuspend(token, "SmbService");
1035     share->Unmount(
1036         base::BindOnce(&SmbService::OnSuspendUnmountDone, AsWeakPtr(), token));
1037   }
1038 }
1039 
OnSuspendUnmountDone(base::UnguessableToken power_manager_suspend_token,chromeos::MountError result)1040 void SmbService::OnSuspendUnmountDone(
1041     base::UnguessableToken power_manager_suspend_token,
1042     chromeos::MountError result) {
1043   LOG_IF(ERROR, result != chromeos::MountError::MOUNT_ERROR_NONE)
1044       << "Could not unmount smbfs share during suspension: "
1045       << static_cast<int>(result);
1046   // Regardless of the outcome, unblock suspension for this share.
1047   chromeos::PowerManagerClient::Get()->UnblockSuspend(
1048       power_manager_suspend_token);
1049 }
1050 
SuspendDone(const base::TimeDelta & sleep_duration)1051 void SmbService::SuspendDone(const base::TimeDelta& sleep_duration) {
1052   // Don't iterate directly over the share map during the remount
1053   // process as shares can be removed on failure in OnSmbfsMountDone.
1054   std::vector<std::string> mount_ids;
1055   for (const auto& s : smbfs_shares_)
1056     mount_ids.push_back(s.first);
1057 
1058   for (const auto& mount_id : mount_ids) {
1059     auto share_it = smbfs_shares_.find(mount_id);
1060     if (share_it == smbfs_shares_.end()) {
1061       LOG(WARNING) << "Smbfs mount id " << mount_id
1062                    << " no longer present during remount after suspend";
1063       continue;
1064     }
1065     SmbFsShare* share = share_it->second.get();
1066 
1067     // Don't try to reconnect as we race the network stack in getting an IP
1068     // address.
1069     SmbFsShare::MountOptions options = share->options();
1070     options.skip_connect = true;
1071     // Observing power management changes from SmbService allows us to remove
1072     // the share in OnSmbfsMountDone if remount fails.
1073     share->Remount(
1074         options, base::BindOnce(
1075                      &SmbService::OnSmbfsMountDone, AsWeakPtr(), mount_id,
1076                      base::BindOnce([](SmbMountResult result,
1077                                        const base::FilePath& mount_path) {
1078                        LOG_IF(ERROR, result != SmbMountResult::kSuccess)
1079                            << "Error remounting smbfs share after suspension: "
1080                            << static_cast<int>(result);
1081                      })));
1082   }
1083 }
1084 
1085 }  // namespace smb_client
1086 }  // namespace chromeos
1087