1 // Copyright 2024 The Chromium Authors
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 "base/win/elevation_util.h"
6
7 #include <shlobj.h>
8
9 #include "base/command_line.h"
10 #include "base/functional/callback.h"
11 #include "base/process/process.h"
12 #include "base/process/process_handle.h"
13 #include "base/process/process_iterator.h"
14 #include "base/test/test_timeouts.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "base/win/scoped_com_initializer.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
20
21 namespace base::win {
22
23 namespace {
24
25 constexpr wchar_t kMoreExecutable[] = L"more.com";
26
IsExplorerRunningAtMediumOrLower()27 bool IsExplorerRunningAtMediumOrLower() {
28 ProcessId explorer_pid = GetExplorerPid();
29 return explorer_pid ? IsProcessRunningAtMediumOrLower(explorer_pid) : false;
30 }
31
32 } // namespace
33
TEST(ElevationUtil,RunDeElevated)34 TEST(ElevationUtil, RunDeElevated) {
35 if (!::IsUserAnAdmin() || !IsExplorerRunningAtMediumOrLower()) {
36 GTEST_SKIP();
37 }
38
39 Process process = RunDeElevated(CommandLine::FromString(L"more.com"));
40 ASSERT_TRUE(process.IsValid());
41
42 absl::Cleanup terminate_process = [&] {
43 EXPECT_TRUE(process.Terminate(0, false));
44 };
45
46 ASSERT_TRUE(IsProcessRunningAtMediumOrLower(process.Pid()));
47 }
48
49 class ElevationUtilRunDeElevatedNoWaitTest
50 : public ::testing::TestWithParam<RepeatingCallback<HRESULT()>> {};
51
52 INSTANTIATE_TEST_SUITE_P(ElevationUtilRunDeElevatedNoWaitTestCases,
53 ElevationUtilRunDeElevatedNoWaitTest,
__anond1858cc50302null54 ::testing::Values(BindRepeating([] {
55 return RunDeElevatedNoWait(
56 CommandLine::FromString(
57 kMoreExecutable));
58 }),
__anond1858cc50402null59 BindRepeating([] {
60 return RunDeElevatedNoWait(
61 kMoreExecutable, {});
62 })));
63
TEST_P(ElevationUtilRunDeElevatedNoWaitTest,TestCases)64 TEST_P(ElevationUtilRunDeElevatedNoWaitTest, TestCases) {
65 if (!::IsUserAnAdmin() || !IsExplorerRunningAtMediumOrLower()) {
66 GTEST_SKIP();
67 }
68
69 ASSERT_EQ(GetProcessCount(kMoreExecutable, /*filter=*/nullptr), 0)
70 << "This test requires that no instances of the `more` command are "
71 "running.";
72
73 ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
74 ASSERT_TRUE(com_initializer.Succeeded());
75
76 ASSERT_HRESULT_SUCCEEDED(GetParam().Run());
77
78 // Wait for the process to start running.
79 int i = 0;
80 for (; i < 5; ++i) {
81 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
82 if (GetProcessCount(kMoreExecutable, /*filter=*/nullptr) == 1) {
83 break;
84 }
85 }
86 ASSERT_LT(i, 5);
87
88 NamedProcessIterator iter(kMoreExecutable, /*filter=*/nullptr);
89 const ProcessEntry* process_entry = iter.NextProcessEntry();
90 ASSERT_TRUE(process_entry);
91 ASSERT_TRUE(IsProcessRunningAtMediumOrLower(process_entry->pid()));
92
93 EXPECT_TRUE(Process::Open(process_entry->pid()).Terminate(0, false));
94 ASSERT_FALSE(iter.NextProcessEntry());
95 }
96
97 } // namespace base::win
98