smbfs_mounter_unittest.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. // Copyright 2019 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. #include "ash/components/smbfs/smbfs_mounter.h"
  5. #include <string.h>
  6. #include <memory>
  7. #include <tuple>
  8. #include <utility>
  9. #include "ash/components/disks/disk_mount_manager.h"
  10. #include "ash/components/disks/mock_disk_mount_manager.h"
  11. #include "base/bind.h"
  12. #include "base/files/file_util.h"
  13. #include "base/run_loop.h"
  14. #include "base/test/bind.h"
  15. #include "base/test/gmock_callback_support.h"
  16. #include "base/test/multiprocess_test.h"
  17. #include "base/test/task_environment.h"
  18. #include "base/threading/sequenced_task_runner_handle.h"
  19. #include "base/threading/thread.h"
  20. #include "chromeos/components/mojo_bootstrap/pending_connection_manager.h"
  21. #include "mojo/core/embedder/scoped_ipc_support.h"
  22. #include "mojo/public/cpp/bindings/receiver.h"
  23. #include "mojo/public/cpp/platform/platform_channel.h"
  24. #include "mojo/public/cpp/system/invitation.h"
  25. #include "mojo/public/cpp/system/platform_handle.h"
  26. #include "testing/gmock/include/gmock/gmock.h"
  27. #include "testing/gtest/include/gtest/gtest.h"
  28. #include "testing/multiprocess_func_list.h"
  29. using testing::_;
  30. using testing::StartsWith;
  31. using testing::WithArgs;
  32. namespace smbfs {
  33. namespace {
  34. constexpr char kMountUrlPrefix[] = "smbfs://";
  35. constexpr char kSharePath[] = "smb://server/share";
  36. constexpr char kMountDir[] = "bar";
  37. constexpr base::FilePath::CharType kMountPath[] = FILE_PATH_LITERAL("/foo/bar");
  38. constexpr int kChildInvitationFd = 42;
  39. constexpr char kUsername[] = "username";
  40. constexpr char kWorkgroup[] = "example.com";
  41. constexpr char kPassword[] = "myverysecurepassword";
  42. constexpr char kKerberosIdentity[] = "my-kerberos-identity";
  43. constexpr char kAccountHash[] = "00112233445566778899aabb";
  44. ash::disks::DiskMountManager::MountPoint MakeMountPointInfo(
  45. const std::string& source_path,
  46. const std::string& mount_path) {
  47. return {source_path, mount_path, ash::MountType::kNetworkStorage};
  48. }
  49. class MockDelegate : public SmbFsHost::Delegate {
  50. public:
  51. MOCK_METHOD(void, OnDisconnected, (), (override));
  52. MOCK_METHOD(void,
  53. RequestCredentials,
  54. (RequestCredentialsCallback),
  55. (override));
  56. };
  57. class TestSmbFsBootstrapImpl : public mojom::SmbFsBootstrap {
  58. public:
  59. MOCK_METHOD(void,
  60. MountShare,
  61. (mojom::MountOptionsPtr,
  62. mojo::PendingRemote<mojom::SmbFsDelegate>,
  63. MountShareCallback),
  64. (override));
  65. };
  66. class TestSmbFsImpl : public mojom::SmbFs {
  67. public:
  68. MOCK_METHOD(void,
  69. RemoveSavedCredentials,
  70. (RemoveSavedCredentialsCallback),
  71. (override));
  72. MOCK_METHOD(void,
  73. DeleteRecursively,
  74. (const base::FilePath&, DeleteRecursivelyCallback),
  75. (override));
  76. };
  77. class TestSmbFsMounter : public SmbFsMounter {
  78. public:
  79. TestSmbFsMounter(const std::string& share_path,
  80. const MountOptions& options,
  81. SmbFsHost::Delegate* delegate,
  82. const base::FilePath& mount_path,
  83. ash::MountError mount_error,
  84. mojo::Remote<mojom::SmbFsBootstrap> bootstrap)
  85. : SmbFsMounter(share_path,
  86. kMountDir,
  87. options,
  88. delegate,
  89. &mock_disk_mount_manager_,
  90. std::move(bootstrap)) {
  91. EXPECT_CALL(mock_disk_mount_manager_, MountPath(StartsWith(kMountUrlPrefix),
  92. _, kMountDir, _, _, _, _))
  93. .WillOnce(WithArgs<0, 6>(
  94. [mount_error, mount_path](
  95. const std::string& source_path,
  96. ash::disks::DiskMountManager::MountPathCallback callback) {
  97. base::SequencedTaskRunnerHandle::Get()->PostTask(
  98. FROM_HERE,
  99. base::BindOnce(
  100. std::move(callback), mount_error,
  101. MakeMountPointInfo(source_path, mount_path.value())));
  102. }));
  103. if (mount_error == ash::MountError::kNone) {
  104. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(mount_path.value(), _))
  105. .WillOnce(base::test::RunOnceCallback<1>(ash::MountError::kNone));
  106. } else {
  107. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(mount_path.value(), _))
  108. .Times(0);
  109. }
  110. }
  111. private:
  112. ash::disks::MockDiskMountManager mock_disk_mount_manager_;
  113. };
  114. class SmbFsMounterTest : public testing::Test {
  115. public:
  116. void PostMountEvent(
  117. const std::string& source_path,
  118. const std::string& mount_path,
  119. ash::MountError mount_error,
  120. ash::disks::DiskMountManager::MountPathCallback callback) {
  121. base::SequencedTaskRunnerHandle::Get()->PostTask(
  122. FROM_HERE, base::BindOnce(std::move(callback), mount_error,
  123. MakeMountPointInfo(source_path, mount_path)));
  124. }
  125. protected:
  126. base::test::TaskEnvironment task_environment_{
  127. base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  128. MockDelegate mock_delegate_;
  129. ash::disks::MockDiskMountManager mock_disk_mount_manager_;
  130. };
  131. TEST_F(SmbFsMounterTest, FilesystemMountTimeout) {
  132. base::RunLoop run_loop;
  133. auto callback =
  134. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  135. std::unique_ptr<SmbFsHost> host) {
  136. EXPECT_EQ(mount_error, mojom::MountError::kTimeout);
  137. EXPECT_FALSE(host);
  138. run_loop.Quit();
  139. });
  140. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
  141. kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
  142. &mock_disk_mount_manager_);
  143. EXPECT_CALL(mock_disk_mount_manager_,
  144. MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
  145. .Times(1);
  146. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
  147. base::TimeTicks start_time = task_environment_.NowTicks();
  148. mounter->Mount(callback);
  149. // TaskEnvironment will automatically advance mock time to the next posted
  150. // task, which is the mount timeout in this case.
  151. run_loop.Run();
  152. EXPECT_GE(task_environment_.NowTicks() - start_time, base::Seconds(20));
  153. }
  154. TEST_F(SmbFsMounterTest, FilesystemMountFailure) {
  155. base::RunLoop run_loop;
  156. auto callback =
  157. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  158. std::unique_ptr<SmbFsHost> host) {
  159. EXPECT_EQ(mount_error, mojom::MountError::kUnknown);
  160. EXPECT_FALSE(host);
  161. run_loop.Quit();
  162. });
  163. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
  164. kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
  165. &mock_disk_mount_manager_);
  166. EXPECT_CALL(mock_disk_mount_manager_,
  167. MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
  168. .WillOnce(WithArgs<0, 6>(
  169. [this](const std::string& source_path,
  170. ash::disks::DiskMountManager::MountPathCallback callback) {
  171. PostMountEvent(source_path, kMountPath, ash::MountError::kInternal,
  172. std::move(callback));
  173. }));
  174. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
  175. mounter->Mount(callback);
  176. run_loop.Run();
  177. }
  178. TEST_F(SmbFsMounterTest, TimeoutAfterFilesystemMount) {
  179. base::RunLoop run_loop;
  180. auto callback =
  181. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  182. std::unique_ptr<SmbFsHost> host) {
  183. EXPECT_EQ(mount_error, mojom::MountError::kTimeout);
  184. EXPECT_FALSE(host);
  185. run_loop.Quit();
  186. });
  187. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
  188. kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
  189. &mock_disk_mount_manager_);
  190. EXPECT_CALL(mock_disk_mount_manager_,
  191. MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
  192. .WillOnce(WithArgs<0, 6>(
  193. [this](const std::string& source_path,
  194. ash::disks::DiskMountManager::MountPathCallback callback) {
  195. PostMountEvent(source_path, kMountPath, ash::MountError::kNone,
  196. std::move(callback));
  197. }));
  198. // Destructing SmbFsMounter on failure will cause the mount point to be
  199. // unmounted.
  200. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(kMountPath, _)).Times(1);
  201. base::TimeTicks start_time = task_environment_.NowTicks();
  202. mounter->Mount(callback);
  203. // TaskEnvironment will automatically advance mock time to the next posted
  204. // task, which is the mount timeout in this case.
  205. run_loop.Run();
  206. EXPECT_GE(task_environment_.NowTicks() - start_time, base::Seconds(20));
  207. }
  208. TEST_F(SmbFsMounterTest, FilesystemMountAfterDestruction) {
  209. base::RunLoop run_loop;
  210. auto callback = base::BindLambdaForTesting(
  211. [](mojom::MountError mount_error, std::unique_ptr<SmbFsHost> host) {
  212. FAIL() << "Callback should not be run";
  213. });
  214. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
  215. kSharePath, kMountDir, SmbFsMounter::MountOptions(), &mock_delegate_,
  216. &mock_disk_mount_manager_);
  217. EXPECT_CALL(mock_disk_mount_manager_,
  218. MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
  219. .WillOnce(WithArgs<0, 6>(
  220. [this](const std::string& source_path,
  221. ash::disks::DiskMountManager::MountPathCallback callback) {
  222. // This posts a mount event to the task queue, which will not be run
  223. // until |run_loop| is started.
  224. PostMountEvent(source_path, kMountPath, ash::MountError::kInternal,
  225. std::move(callback));
  226. }));
  227. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(_, _)).Times(0);
  228. mounter->Mount(callback);
  229. // Delete the mounter. Callback should not be run.
  230. mounter.reset();
  231. run_loop.RunUntilIdle();
  232. }
  233. TEST_F(SmbFsMounterTest, MountOptions) {
  234. base::RunLoop run_loop;
  235. auto callback =
  236. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  237. std::unique_ptr<SmbFsHost> host) {
  238. EXPECT_EQ(mount_error, mojom::MountError::kOk);
  239. ASSERT_TRUE(host);
  240. EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
  241. run_loop.Quit();
  242. });
  243. // Dummy Mojo bindings to satifsy lifetimes.
  244. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  245. TestSmbFsImpl mock_smbfs;
  246. mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
  247. TestSmbFsBootstrapImpl mock_bootstrap;
  248. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  249. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  250. .WillOnce([&delegate_remote, &smbfs_receiver](
  251. mojom::MountOptionsPtr options,
  252. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  253. mojom::SmbFsBootstrap::MountShareCallback callback) {
  254. EXPECT_EQ(options->share_path, kSharePath);
  255. EXPECT_EQ(options->username, kUsername);
  256. EXPECT_EQ(options->workgroup, kWorkgroup);
  257. ASSERT_TRUE(options->password);
  258. EXPECT_EQ(options->password->length,
  259. static_cast<int32_t>(strlen(kPassword)));
  260. std::string password_buf(options->password->length, 'a');
  261. base::ScopedFD fd =
  262. mojo::UnwrapPlatformHandle(std::move(options->password->fd))
  263. .TakeFD();
  264. EXPECT_TRUE(base::ReadFromFD(fd.get(), &(password_buf.front()),
  265. options->password->length));
  266. EXPECT_EQ(password_buf, kPassword);
  267. EXPECT_TRUE(options->allow_ntlm);
  268. EXPECT_FALSE(options->skip_connect);
  269. EXPECT_EQ(options->resolved_host, net::IPAddress(1, 2, 3, 4));
  270. EXPECT_FALSE(options->credential_storage_options);
  271. delegate_remote = std::move(delegate);
  272. std::move(callback).Run(mojom::MountError::kOk,
  273. smbfs_receiver.BindNewPipeAndPassRemote());
  274. });
  275. SmbFsMounter::MountOptions mount_options;
  276. mount_options.username = kUsername;
  277. mount_options.workgroup = kWorkgroup;
  278. mount_options.password = kPassword;
  279. mount_options.allow_ntlm = true;
  280. mount_options.resolved_host = net::IPAddress(1, 2, 3, 4);
  281. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  282. kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
  283. ash::MountError::kNone,
  284. mojo::Remote<mojom::SmbFsBootstrap>(
  285. bootstrap_receiver.BindNewPipeAndPassRemote()));
  286. mounter->Mount(callback);
  287. run_loop.Run();
  288. }
  289. TEST_F(SmbFsMounterTest, MountOptions_SkipConnect) {
  290. base::RunLoop run_loop;
  291. auto callback =
  292. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  293. std::unique_ptr<SmbFsHost> host) {
  294. EXPECT_EQ(mount_error, mojom::MountError::kOk);
  295. ASSERT_TRUE(host);
  296. EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
  297. run_loop.Quit();
  298. });
  299. // Dummy Mojo bindings to satisfy lifetimes.
  300. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  301. TestSmbFsImpl mock_smbfs;
  302. mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
  303. TestSmbFsBootstrapImpl mock_bootstrap;
  304. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  305. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  306. .WillOnce([&delegate_remote, &smbfs_receiver](
  307. mojom::MountOptionsPtr options,
  308. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  309. mojom::SmbFsBootstrap::MountShareCallback callback) {
  310. EXPECT_EQ(options->share_path, kSharePath);
  311. EXPECT_TRUE(options->skip_connect);
  312. delegate_remote = std::move(delegate);
  313. std::move(callback).Run(mojom::MountError::kOk,
  314. smbfs_receiver.BindNewPipeAndPassRemote());
  315. });
  316. SmbFsMounter::MountOptions mount_options;
  317. mount_options.skip_connect = true;
  318. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  319. kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
  320. ash::MountError::kNone,
  321. mojo::Remote<mojom::SmbFsBootstrap>(
  322. bootstrap_receiver.BindNewPipeAndPassRemote()));
  323. mounter->Mount(callback);
  324. run_loop.Run();
  325. }
  326. TEST_F(SmbFsMounterTest, MountOptions_SavePassword) {
  327. // Salt must be at least 16 bytes.
  328. const std::vector<uint8_t> kSalt = {0, 1, 2, 3, 4, 5, 6, 7,
  329. 8, 9, 10, 11, 12, 13, 14, 15};
  330. base::RunLoop run_loop;
  331. auto callback =
  332. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  333. std::unique_ptr<SmbFsHost> host) {
  334. EXPECT_EQ(mount_error, mojom::MountError::kOk);
  335. ASSERT_TRUE(host);
  336. EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
  337. run_loop.Quit();
  338. });
  339. // Dummy Mojo bindings to satisfy lifetimes.
  340. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  341. TestSmbFsImpl mock_smbfs;
  342. mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
  343. TestSmbFsBootstrapImpl mock_bootstrap;
  344. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  345. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  346. .WillOnce([&delegate_remote, &smbfs_receiver, kSalt](
  347. mojom::MountOptionsPtr options,
  348. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  349. mojom::SmbFsBootstrap::MountShareCallback callback) {
  350. EXPECT_EQ(options->share_path, kSharePath);
  351. ASSERT_TRUE(options->credential_storage_options);
  352. EXPECT_EQ(options->credential_storage_options->account_hash,
  353. kAccountHash);
  354. EXPECT_EQ(options->credential_storage_options->salt, kSalt);
  355. delegate_remote = std::move(delegate);
  356. std::move(callback).Run(mojom::MountError::kOk,
  357. smbfs_receiver.BindNewPipeAndPassRemote());
  358. });
  359. SmbFsMounter::MountOptions mount_options;
  360. mount_options.save_restore_password = true;
  361. mount_options.account_hash = kAccountHash;
  362. mount_options.password_salt = kSalt;
  363. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  364. kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
  365. ash::MountError::kNone,
  366. mojo::Remote<mojom::SmbFsBootstrap>(
  367. bootstrap_receiver.BindNewPipeAndPassRemote()));
  368. mounter->Mount(callback);
  369. run_loop.Run();
  370. }
  371. TEST_F(SmbFsMounterTest, KerberosAuthentication) {
  372. base::RunLoop run_loop;
  373. auto callback =
  374. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  375. std::unique_ptr<SmbFsHost> host) {
  376. EXPECT_EQ(mount_error, mojom::MountError::kOk);
  377. ASSERT_TRUE(host);
  378. EXPECT_EQ(host->mount_path(), base::FilePath(kMountPath));
  379. run_loop.Quit();
  380. });
  381. // Dummy Mojo bindings to satifsy lifetimes.
  382. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  383. TestSmbFsImpl mock_smbfs;
  384. mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
  385. TestSmbFsBootstrapImpl mock_bootstrap;
  386. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  387. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  388. .WillOnce([&delegate_remote, &smbfs_receiver](
  389. mojom::MountOptionsPtr options,
  390. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  391. mojom::SmbFsBootstrap::MountShareCallback callback) {
  392. EXPECT_EQ(options->share_path, kSharePath);
  393. EXPECT_EQ(options->username, kUsername);
  394. EXPECT_EQ(options->workgroup, kWorkgroup);
  395. EXPECT_FALSE(options->allow_ntlm);
  396. EXPECT_FALSE(options->password);
  397. ASSERT_TRUE(options->kerberos_config);
  398. EXPECT_EQ(options->kerberos_config->source,
  399. mojom::KerberosConfig::Source::kKerberos);
  400. EXPECT_EQ(options->kerberos_config->identity, kKerberosIdentity);
  401. delegate_remote = std::move(delegate);
  402. std::move(callback).Run(mojom::MountError::kOk,
  403. smbfs_receiver.BindNewPipeAndPassRemote());
  404. });
  405. SmbFsMounter::MountOptions mount_options;
  406. mount_options.username = kUsername;
  407. mount_options.workgroup = kWorkgroup;
  408. // Even though the password is set, it should be ignored because kerberos
  409. // authentication is being used.
  410. mount_options.password = kPassword;
  411. mount_options.kerberos_options =
  412. absl::make_optional<SmbFsMounter::KerberosOptions>(
  413. SmbFsMounter::KerberosOptions::Source::kKerberos, kKerberosIdentity);
  414. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  415. kSharePath, mount_options, &mock_delegate_, base::FilePath(kMountPath),
  416. ash::MountError::kNone,
  417. mojo::Remote<mojom::SmbFsBootstrap>(
  418. bootstrap_receiver.BindNewPipeAndPassRemote()));
  419. mounter->Mount(callback);
  420. run_loop.Run();
  421. }
  422. TEST_F(SmbFsMounterTest, BootstrapMountError) {
  423. base::RunLoop run_loop;
  424. auto callback =
  425. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  426. std::unique_ptr<SmbFsHost> host) {
  427. EXPECT_EQ(mount_error, mojom::MountError::kAccessDenied);
  428. EXPECT_FALSE(host);
  429. run_loop.Quit();
  430. });
  431. // Dummy Mojo bindings to satifsy lifetimes.
  432. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  433. TestSmbFsBootstrapImpl mock_bootstrap;
  434. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  435. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  436. .WillOnce([&delegate_remote](
  437. mojom::MountOptionsPtr options,
  438. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  439. mojom::SmbFsBootstrap::MountShareCallback callback) {
  440. delegate_remote = std::move(delegate);
  441. std::move(callback).Run(mojom::MountError::kAccessDenied, {});
  442. });
  443. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  444. kSharePath, SmbFsMounter::MountOptions(), &mock_delegate_,
  445. base::FilePath(kMountPath), ash::MountError::kNone,
  446. mojo::Remote<mojom::SmbFsBootstrap>(
  447. bootstrap_receiver.BindNewPipeAndPassRemote()));
  448. mounter->Mount(callback);
  449. run_loop.Run();
  450. }
  451. TEST_F(SmbFsMounterTest, BootstrapDisconnection) {
  452. base::RunLoop run_loop;
  453. auto callback =
  454. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  455. std::unique_ptr<SmbFsHost> host) {
  456. EXPECT_EQ(mount_error, mojom::MountError::kUnknown);
  457. EXPECT_FALSE(host);
  458. run_loop.Quit();
  459. });
  460. // Dummy Mojo bindings to satifsy lifetimes.
  461. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  462. TestSmbFsBootstrapImpl mock_bootstrap;
  463. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  464. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  465. .WillOnce([&bootstrap_receiver](
  466. mojom::MountOptionsPtr options,
  467. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  468. mojom::SmbFsBootstrap::MountShareCallback callback) {
  469. // Reset the bootstrap binding, which should cause a disconnect event.
  470. bootstrap_receiver.reset();
  471. std::move(callback).Run(mojom::MountError::kAccessDenied, {});
  472. });
  473. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<TestSmbFsMounter>(
  474. kSharePath, SmbFsMounter::MountOptions(), &mock_delegate_,
  475. base::FilePath(kMountPath), ash::MountError::kNone,
  476. mojo::Remote<mojom::SmbFsBootstrap>(
  477. bootstrap_receiver.BindNewPipeAndPassRemote()));
  478. mounter->Mount(callback);
  479. run_loop.Run();
  480. }
  481. class SmbFsMounterE2eTest : public testing::Test {
  482. public:
  483. void PostMountEvent(
  484. const std::string& source_path,
  485. const std::string& mount_path,
  486. ash::disks::DiskMountManager::MountPathCallback callback) {
  487. base::SequencedTaskRunnerHandle::Get()->PostTask(
  488. FROM_HERE, base::BindOnce(std::move(callback), ash::MountError::kNone,
  489. MakeMountPointInfo(source_path, mount_path)));
  490. }
  491. protected:
  492. // This test performs actual IPC using sockets, and therefore cannot use
  493. // MOCK_TIME, which automatically advances time when the main loop is idle.
  494. base::test::TaskEnvironment task_environment_;
  495. MockDelegate mock_delegate_;
  496. ash::disks::MockDiskMountManager mock_disk_mount_manager_;
  497. };
  498. // Child process that emulates the behaviour of smbfs.
  499. MULTIPROCESS_TEST_MAIN(SmbFsMain) {
  500. base::test::TaskEnvironment task_environment(
  501. base::test::TaskEnvironment::MainThreadType::IO,
  502. base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED);
  503. mojo::core::ScopedIPCSupport ipc_support(
  504. task_environment.GetMainThreadTaskRunner(),
  505. mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
  506. mojo::IncomingInvitation invitation =
  507. mojo::IncomingInvitation::Accept(mojo::PlatformChannelEndpoint(
  508. mojo::PlatformHandle(base::ScopedFD(kChildInvitationFd))));
  509. TestSmbFsImpl mock_smbfs;
  510. mojo::Receiver<mojom::SmbFs> smbfs_receiver(&mock_smbfs);
  511. TestSmbFsBootstrapImpl mock_bootstrap;
  512. mojo::Receiver<mojom::SmbFsBootstrap> bootstrap_receiver(&mock_bootstrap);
  513. mojo::PendingRemote<mojom::SmbFsDelegate> delegate_remote;
  514. base::RunLoop run_loop;
  515. EXPECT_CALL(mock_bootstrap, MountShare(_, _, _))
  516. .WillOnce([&smbfs_receiver, &run_loop, &delegate_remote](
  517. mojom::MountOptionsPtr options,
  518. mojo::PendingRemote<mojom::SmbFsDelegate> delegate,
  519. mojom::SmbFsBootstrap::MountShareCallback callback) {
  520. EXPECT_EQ(options->share_path, kSharePath);
  521. EXPECT_EQ(options->username, kUsername);
  522. EXPECT_EQ(options->workgroup, kWorkgroup);
  523. ASSERT_TRUE(options->password);
  524. EXPECT_EQ(options->password->length,
  525. static_cast<int32_t>(strlen(kPassword)));
  526. std::string password_buf(options->password->length, 'a');
  527. base::ScopedFD fd =
  528. mojo::UnwrapPlatformHandle(std::move(options->password->fd))
  529. .TakeFD();
  530. EXPECT_TRUE(base::ReadFromFD(fd.get(), &(password_buf.front()),
  531. options->password->length));
  532. EXPECT_EQ(password_buf, kPassword);
  533. EXPECT_FALSE(options->allow_ntlm);
  534. delegate_remote = std::move(delegate);
  535. mojo::PendingRemote<mojom::SmbFs> smbfs =
  536. smbfs_receiver.BindNewPipeAndPassRemote();
  537. // When the SmbFsHost in the parent is destroyed, this message pipe will
  538. // be closed and treat that as a signal to shut down.
  539. smbfs_receiver.set_disconnect_handler(run_loop.QuitClosure());
  540. std::move(callback).Run(mojom::MountError::kOk, std::move(smbfs));
  541. });
  542. bootstrap_receiver.Bind(mojo::PendingReceiver<mojom::SmbFsBootstrap>(
  543. invitation.ExtractMessagePipe(mojom::kBootstrapPipeName)));
  544. run_loop.Run();
  545. return 0;
  546. }
  547. TEST_F(SmbFsMounterE2eTest, MountSuccess) {
  548. mojo::PlatformChannel channel;
  549. base::LaunchOptions launch_options;
  550. base::ScopedFD child_fd =
  551. channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();
  552. launch_options.fds_to_remap.push_back(
  553. std::make_pair(child_fd.get(), kChildInvitationFd));
  554. base::Process child_process = base::SpawnMultiProcessTestChild(
  555. "SmbFsMain", base::GetMultiProcessTestChildBaseCommandLine(),
  556. launch_options);
  557. ASSERT_TRUE(child_process.IsValid());
  558. // The child FD has been passed to the child process at this point.
  559. std::ignore = child_fd.release();
  560. EXPECT_CALL(mock_disk_mount_manager_,
  561. MountPath(StartsWith(kMountUrlPrefix), _, kMountDir, _, _, _, _))
  562. .WillOnce(WithArgs<0,
  563. 6>([this, &channel](
  564. const std::string& source_path,
  565. ash::disks::DiskMountManager::MountPathCallback
  566. callback) {
  567. // Emulates cros-disks mount success.
  568. PostMountEvent(source_path, kMountPath, std::move(callback));
  569. // Emulates smbfs connecting to the org.chromium.SmbFs D-Bus service and
  570. // providing a Mojo connection endpoint.
  571. const std::string token =
  572. source_path.substr(sizeof(kMountUrlPrefix) - 1);
  573. base::SequencedTaskRunnerHandle::Get()->PostTask(
  574. FROM_HERE, base::BindLambdaForTesting([token, &channel]() {
  575. mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
  576. token,
  577. channel.TakeLocalEndpoint().TakePlatformHandle().TakeFD());
  578. }));
  579. }));
  580. EXPECT_CALL(mock_disk_mount_manager_, UnmountPath(kMountPath, _))
  581. .WillOnce(base::test::RunOnceCallback<1>(ash::MountError::kNone));
  582. EXPECT_CALL(mock_delegate_, OnDisconnected()).Times(0);
  583. base::RunLoop run_loop;
  584. auto callback =
  585. base::BindLambdaForTesting([&run_loop](mojom::MountError mount_error,
  586. std::unique_ptr<SmbFsHost> host) {
  587. EXPECT_EQ(mount_error, mojom::MountError::kOk);
  588. EXPECT_TRUE(host);
  589. // Don't capture |host|. Its destruction will close the Mojo message
  590. // pipe and cause the child process to shut down gracefully.
  591. run_loop.Quit();
  592. });
  593. SmbFsMounter::MountOptions mount_options;
  594. mount_options.username = kUsername;
  595. mount_options.workgroup = kWorkgroup;
  596. mount_options.password = kPassword;
  597. std::unique_ptr<SmbFsMounter> mounter = std::make_unique<SmbFsMounter>(
  598. kSharePath, kMountDir, mount_options, &mock_delegate_,
  599. &mock_disk_mount_manager_);
  600. mounter->Mount(callback);
  601. run_loop.Run();
  602. EXPECT_TRUE(child_process.WaitForExit(nullptr));
  603. }
  604. } // namespace
  605. } // namespace smbfs