1 // Copyright 2018 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/chrome_cleaner/test/reboot_deletion_helper.h"
6 
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/logging.h"
9 #include "base/test/test_reg_util_win.h"
10 #include "base/win/registry.h"
11 #include "chrome/chrome_cleaner/os/disk_util.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace chrome_cleaner {
16 
17 namespace {
18 
19 // The moves-pending-reboot is a MULTISZ registry value in the HKLM part of the
20 // registry.
21 const wchar_t kSessionManagerKey[] =
22     L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
23 const wchar_t kPendingFileRenameOps[] = L"PendingFileRenameOperations";
24 
25 const uint32_t kMaxRegistryValueLength = 1024;
26 
27 const wchar_t kRemoveFile1[] = L"remove_one";
28 const wchar_t kRemoveFile2[] = L"remove_two";
29 const wchar_t kRemoveFile3[] = L"remove_three";
30 const wchar_t kRemoveFile4[] = L"remove_four";
31 const wchar_t kRemoveFile5[] = L"remove_five";
32 const wchar_t kDeleteFile[] = L"";
33 const BYTE kNoNullInvalidString[] = {12, 13};
34 const BYTE kMissingNullEntryString[] = {65, 0, 0, 0};
35 const BYTE kOddSizeEntryString[] = {0, 65, 0, 66, 0xFF, 0, 0, 0, 0};
36 const wchar_t kRemoveRawMultipleFiles[] =
37     L"remove_one\0\0remove_two\0remove_three\0\0\0";
38 const wchar_t kRemoveRawEmpty0[] = L"";
39 const wchar_t kRemoveRawEmpty1[] = L"\0";
40 const wchar_t kRemoveRawEmpty2[] = L"\0\0";
41 const wchar_t kRemoveRawCorrupt[] = L"file_with_no_ending_null";
42 
TestPendingFileRenameOperations(const wchar_t * content,size_t content_size,PendingMoveVector * pending_moves)43 bool TestPendingFileRenameOperations(const wchar_t* content,
44                                      size_t content_size,
45                                      PendingMoveVector* pending_moves) {
46   DCHECK(content);
47   DCHECK(pending_moves);
48   pending_moves->clear();
49 
50   base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
51                                         KEY_ALL_ACCESS);
52   if (!session_manager_key.Handle()) {
53     PLOG(ERROR) << "Can't open session manager.";
54     return false;
55   }
56 
57   // Write raw content of the registry value.
58   if (session_manager_key.WriteValue(kPendingFileRenameOps, content,
59                                      content_size,
60                                      REG_MULTI_SZ) != ERROR_SUCCESS) {
61     PLOG(ERROR) << "Can't write to registry value '" << kPendingFileRenameOps
62                 << "' value: '" << content << "'.";
63     return false;
64   }
65 
66   // Read back the pending moves.
67   if (!GetPendingMoves(pending_moves)) {
68     PLOG(ERROR) << "Can't read back or parse the registry value '"
69                 << kPendingFileRenameOps << "'.";
70     return false;
71   }
72 
73   return true;
74 }
75 
76 }  // namespace
77 
TEST(RebootDeletionHelper,GetPendingMovesRawValue)78 TEST(RebootDeletionHelper, GetPendingMovesRawValue) {
79   registry_util::RegistryOverrideManager registry_override;
80   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
81 
82   PendingMoveVector pending_moves;
83   EXPECT_TRUE(
84       TestPendingFileRenameOperations(kRemoveRawEmpty0, 0, &pending_moves));
85   EXPECT_TRUE(pending_moves.empty());
86 
87   EXPECT_TRUE(TestPendingFileRenameOperations(
88       kRemoveRawEmpty0, sizeof(kRemoveRawEmpty0), &pending_moves));
89   EXPECT_TRUE(pending_moves.empty());
90 
91   EXPECT_TRUE(TestPendingFileRenameOperations(
92       kRemoveRawEmpty1, sizeof(kRemoveRawEmpty1), &pending_moves));
93   EXPECT_TRUE(pending_moves.empty());
94 
95   EXPECT_TRUE(TestPendingFileRenameOperations(
96       kRemoveRawEmpty2, sizeof(kRemoveRawEmpty2), &pending_moves));
97   EXPECT_TRUE(pending_moves.empty());
98 
99   EXPECT_TRUE(TestPendingFileRenameOperations(kRemoveRawMultipleFiles,
100                                               sizeof(kRemoveRawMultipleFiles),
101                                               &pending_moves));
102   EXPECT_THAT(pending_moves,
103               testing::ElementsAre(std::make_pair(kRemoveFile1, kDeleteFile),
104                                    std::make_pair(kRemoveFile2, kRemoveFile3)));
105 
106   const wchar_t kRemoveRawSingleFile[] = L"remove_one\0\0";
107   EXPECT_TRUE(TestPendingFileRenameOperations(
108       kRemoveRawSingleFile, sizeof(kRemoveRawSingleFile), &pending_moves));
109   EXPECT_THAT(pending_moves,
110               testing::ElementsAre(std::make_pair(kRemoveFile1, kDeleteFile)));
111 
112   const wchar_t kRemoveRawRename0[] = L"remove_one\0remove_two";
113   EXPECT_TRUE(TestPendingFileRenameOperations(
114       kRemoveRawRename0, sizeof(kRemoveRawRename0), &pending_moves));
115   EXPECT_THAT(pending_moves,
116               testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
117 
118   const wchar_t kRemoveRawRename1[] = L"remove_one\0remove_two\0";
119   EXPECT_TRUE(TestPendingFileRenameOperations(
120       kRemoveRawRename1, sizeof(kRemoveRawRename1), &pending_moves));
121   EXPECT_THAT(pending_moves,
122               testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
123 
124   const wchar_t kRemoveRawRename2[] = L"remove_one\0remove_two\0\0";
125   EXPECT_TRUE(TestPendingFileRenameOperations(
126       kRemoveRawRename2, sizeof(kRemoveRawRename2), &pending_moves));
127   EXPECT_THAT(pending_moves,
128               testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
129 
130   EXPECT_FALSE(TestPendingFileRenameOperations(
131       kRemoveRawCorrupt, sizeof(kRemoveRawCorrupt), &pending_moves));
132 }
133 
TEST(RebootDeletionHelper,SetPendingMovesRawValue)134 TEST(RebootDeletionHelper, SetPendingMovesRawValue) {
135   registry_util::RegistryOverrideManager registry_override;
136   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
137 
138   base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
139                                         KEY_ALL_ACCESS);
140   EXPECT_TRUE(session_manager_key.Handle());
141 
142   // Write some pending moves.
143   PendingMoveVector pending_moves;
144   pending_moves.push_back(std::make_pair(kRemoveFile1, kDeleteFile));
145   pending_moves.push_back(std::make_pair(kRemoveFile2, kRemoveFile3));
146 
147   EXPECT_TRUE(SetPendingMoves(pending_moves));
148 
149   wchar_t buffer[kMaxRegistryValueLength];
150   DWORD buffer_size = kMaxRegistryValueLength;
151   DWORD buffer_type = REG_NONE;
152 
153   // Validate the raw content of the registry value.
154   EXPECT_EQ(ERROR_SUCCESS,
155             session_manager_key.ReadValue(kPendingFileRenameOps, &buffer[0],
156                                           &buffer_size, &buffer_type));
157 
158   EXPECT_EQ(buffer_size, sizeof(kRemoveRawMultipleFiles));
159   EXPECT_EQ(0, ::memcmp(kRemoveRawMultipleFiles, buffer, buffer_size));
160   EXPECT_EQ(REG_MULTI_SZ, buffer_type);
161 }
162 
TEST(RebootDeletionHelper,SetPendingMovesValueWithEmptyInput)163 TEST(RebootDeletionHelper, SetPendingMovesValueWithEmptyInput) {
164   registry_util::RegistryOverrideManager registry_override;
165   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
166 
167   base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
168                                         KEY_ALL_ACCESS);
169   EXPECT_TRUE(session_manager_key.Handle());
170 
171   // Write raw content of the registry value.
172   EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
173                                kPendingFileRenameOps, kRemoveRawMultipleFiles,
174                                sizeof(kRemoveRawMultipleFiles), REG_MULTI_SZ));
175 
176   // Write an empty pending moves vector.
177   PendingMoveVector pending_moves;
178   SetPendingMoves(pending_moves);
179 
180   // The registry value must be removed.
181   EXPECT_FALSE(session_manager_key.HasValue(kPendingFileRenameOps));
182 }
183 
TEST(RebootDeletionHelper,GetAndSetPendingMoves)184 TEST(RebootDeletionHelper, GetAndSetPendingMoves) {
185   // Declare the scoped temp dir before the registry override manager because
186   // the recursive deletion of folders fail when running elevated while a
187   // registry override is active.
188   base::ScopedTempDir temp;
189   ASSERT_TRUE(temp.CreateUniqueTempDir());
190 
191   registry_util::RegistryOverrideManager registry_override;
192   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
193 
194   // Expect the registry key to be empty.
195   PendingMoveVector pending_moves;
196   EXPECT_TRUE(GetPendingMoves(&pending_moves));
197   ASSERT_TRUE(pending_moves.empty());
198 
199   // Write back some moves into the registry key.
200   base::FilePath file_path1 = temp.GetPath().Append(kRemoveFile1);
201   base::FilePath file_path2 = temp.GetPath().Append(kRemoveFile2);
202   base::FilePath file_path3 = temp.GetPath().Append(kRemoveFile3);
203 
204   pending_moves.push_back(
205       std::make_pair(file_path1.value(), file_path2.value()));
206   pending_moves.push_back(std::make_pair(file_path3.value(), kDeleteFile));
207 
208   EXPECT_TRUE(SetPendingMoves(pending_moves));
209 
210   // Read back the written moves.
211   PendingMoveVector written_moves;
212   EXPECT_TRUE(GetPendingMoves(&written_moves));
213 
214   // Validate that moves before and after serialisation are the same.
215   EXPECT_THAT(written_moves, testing::ElementsAreArray(pending_moves));
216 
217   // Write back an empty set of moves.
218   pending_moves.clear();
219   EXPECT_TRUE(SetPendingMoves(pending_moves));
220   EXPECT_TRUE(GetPendingMoves(&written_moves));
221   EXPECT_TRUE(written_moves.empty());
222 }
223 
TEST(RebootDeletionHelper,GetInvalidPendingMoves)224 TEST(RebootDeletionHelper, GetInvalidPendingMoves) {
225   // Open the pending registry key to write into invalid data.
226   registry_util::RegistryOverrideManager registry_override;
227   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
228 
229   base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
230                                         KEY_CREATE_SUB_KEY | KEY_SET_VALUE);
231   EXPECT_TRUE(session_manager_key.Handle());
232 
233   // Write an invalid pending moves registry key with a wrong type.
234   EXPECT_EQ(ERROR_SUCCESS,
235             session_manager_key.WriteValue(kPendingFileRenameOps, 0xCCCCCCCC));
236   PendingMoveVector pending_moves;
237   EXPECT_FALSE(GetPendingMoves(&pending_moves));
238 
239   // Write an invalid pending moves registry key with an invalid string format.
240   EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
241                                kPendingFileRenameOps, kNoNullInvalidString,
242                                sizeof(kNoNullInvalidString), REG_MULTI_SZ));
243   EXPECT_FALSE(GetPendingMoves(&pending_moves));
244 
245   // Write an invalid string without the ending empty entry.
246   EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
247                                kPendingFileRenameOps, kMissingNullEntryString,
248                                sizeof(kMissingNullEntryString), REG_MULTI_SZ));
249   EXPECT_FALSE(GetPendingMoves(&pending_moves));
250 
251   // Write an invalid string with an odd number of bytes.
252   EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
253                                kPendingFileRenameOps, kOddSizeEntryString,
254                                sizeof(kOddSizeEntryString), REG_MULTI_SZ));
255   EXPECT_TRUE(GetPendingMoves(&pending_moves));
256 }
257 
TEST(RebootDeletionHelper,UnregisterPostRebootRemovals)258 TEST(RebootDeletionHelper, UnregisterPostRebootRemovals) {
259   // Declare the scoped temp dir before the registry override manager because
260   // the recursive deletion of folders fail when running elevated while a
261   // registry override is active.
262   base::ScopedTempDir temp;
263   ASSERT_TRUE(temp.CreateUniqueTempDir());
264 
265   registry_util::RegistryOverrideManager registry_override;
266   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
267 
268   // Write some moves and renamings into the registry key.
269   base::FilePath file_path1 = temp.GetPath().Append(kRemoveFile1);
270   base::FilePath file_path2 = temp.GetPath().Append(kRemoveFile2);
271   base::FilePath file_path3 = temp.GetPath().Append(kRemoveFile3);
272   base::FilePath file_path4 = temp.GetPath().Append(kRemoveFile4);
273   base::FilePath file_path5 = temp.GetPath().Append(kRemoveFile5);
274 
275   PendingMoveVector pending_moves;
276   pending_moves.push_back(std::make_pair(file_path1.value(), kDeleteFile));
277   pending_moves.push_back(std::make_pair(file_path2.value(), kDeleteFile));
278   pending_moves.push_back(std::make_pair(file_path3.value(), kDeleteFile));
279   pending_moves.push_back(
280       std::make_pair(file_path4.value(), file_path5.value()));
281 
282   EXPECT_TRUE(SetPendingMoves(pending_moves));
283 
284   // Unregister paths from the pending moves.
285   FilePathSet paths;
286   paths.Insert(file_path2);
287   paths.Insert(file_path3);
288   // A file renaming action must not be removed, even if a unregister is
289   // requested.
290   paths.Insert(file_path4);
291   EXPECT_TRUE(UnregisterPostRebootRemovals(paths));
292 
293   // Read back the pending moves.
294   PendingMoveVector written_moves;
295   EXPECT_TRUE(GetPendingMoves(&written_moves));
296   EXPECT_THAT(written_moves,
297               testing::ElementsAre(
298                   std::make_pair(file_path1.value(), kDeleteFile),
299                   std::make_pair(file_path4.value(), file_path5.value())));
300 }
301 
TEST(RebootDeletionHelper,UnregisterPostRebootRemovalsOfQualifiedPath)302 TEST(RebootDeletionHelper, UnregisterPostRebootRemovalsOfQualifiedPath) {
303   registry_util::RegistryOverrideManager registry_override;
304   registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
305 
306   // Add a pending deletion with a qualified path.
307   PendingMoveVector pending_moves;
308   pending_moves.push_back(
309       std::make_pair(L"\\??\\C:\\this_file_doesnt_exist", kDeleteFile));
310   EXPECT_TRUE(SetPendingMoves(pending_moves));
311 
312   // Try to remove it with an unqualified path.
313   FilePathSet paths;
314   paths.Insert(base::FilePath(FILE_PATH_LITERAL("C:\\this_file_doesnt_exist")));
315   EXPECT_TRUE(UnregisterPostRebootRemovals(paths));
316 
317   // Read back the pending moves.
318   PendingMoveVector written_moves;
319   EXPECT_TRUE(GetPendingMoves(&written_moves));
320   EXPECT_TRUE(written_moves.empty());
321 }
322 
323 }  // namespace chrome_cleaner
324