• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <objbase.h>
8 
9 #include <windows.h>
10 
11 #include <shlobj.h>
12 #include <wrl/client.h>
13 
14 #include <string>
15 
16 #include "base/command_line.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/process/launch.h"
20 #include "base/process/process.h"
21 #include "base/process/process_handle.h"
22 #include "base/process/process_info.h"
23 #include "base/win/access_token.h"
24 #include "base/win/scoped_bstr.h"
25 #include "base/win/scoped_process_information.h"
26 #include "base/win/scoped_variant.h"
27 #include "base/win/startup_information.h"
28 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
29 
30 namespace base::win {
31 
GetExplorerPid()32 ProcessId GetExplorerPid() {
33   const HWND hwnd = ::GetShellWindow();
34   ProcessId pid = 0;
35   return hwnd && ::GetWindowThreadProcessId(hwnd, &pid) ? pid : kNullProcessId;
36 }
37 
IsProcessRunningAtMediumOrLower(ProcessId process_id)38 bool IsProcessRunningAtMediumOrLower(ProcessId process_id) {
39   IntegrityLevel level = GetProcessIntegrityLevel(process_id);
40   return level != INTEGRITY_UNKNOWN && level <= MEDIUM_INTEGRITY;
41 }
42 
43 // Based on
44 // https://learn.microsoft.com/en-us/archive/blogs/aaron_margosis/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.
RunDeElevated(const CommandLine & command_line)45 Process RunDeElevated(const CommandLine& command_line) {
46   if (!::IsUserAnAdmin()) {
47     return LaunchProcess(command_line, {});
48   }
49 
50   ProcessId explorer_pid = GetExplorerPid();
51   if (!explorer_pid || !IsProcessRunningAtMediumOrLower(explorer_pid)) {
52     return Process();
53   }
54 
55   auto shell_process =
56       Process::OpenWithAccess(explorer_pid, PROCESS_QUERY_LIMITED_INFORMATION);
57   if (!shell_process.IsValid()) {
58     return Process();
59   }
60 
61   auto token = AccessToken::FromProcess(
62       ::GetCurrentProcess(), /*impersonation=*/false, MAXIMUM_ALLOWED);
63   if (!token) {
64     return Process();
65   }
66   auto previous_impersonate = token->SetPrivilege(SE_IMPERSONATE_NAME, true);
67   if (!previous_impersonate) {
68     return Process();
69   }
70   absl::Cleanup restore_previous_privileges = [&] {
71     token->SetPrivilege(SE_IMPERSONATE_NAME, *previous_impersonate);
72   };
73 
74   auto shell_token = AccessToken::FromProcess(
75       shell_process.Handle(), /*impersonation=*/false, TOKEN_DUPLICATE);
76   if (!shell_token) {
77     return Process();
78   }
79 
80   auto duplicated_shell_token = shell_token->DuplicatePrimary(
81       TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE |
82       TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);
83   if (!duplicated_shell_token) {
84     return Process();
85   }
86 
87   StartupInformation startupinfo;
88   PROCESS_INFORMATION pi = {};
89   if (!::CreateProcessWithTokenW(duplicated_shell_token->get(), 0,
90                                  command_line.GetProgram().value().c_str(),
91                                  command_line.GetCommandLineString().data(), 0,
92                                  nullptr, nullptr, startupinfo.startup_info(),
93                                  &pi)) {
94     return Process();
95   }
96   ScopedProcessInformation process_info(pi);
97   Process process(process_info.TakeProcessHandle());
98   const DWORD pid = process.Pid();
99   VLOG(1) << __func__ << ": Started process, PID: " << pid;
100 
101   // Allow the spawned process to show windows in the foreground.
102   if (!::AllowSetForegroundWindow(pid)) {
103     VPLOG(1) << __func__ << ": ::AllowSetForegroundWindow failed";
104   }
105 
106   return process;
107 }
108 
RunDeElevatedNoWait(const CommandLine & command_line)109 HRESULT RunDeElevatedNoWait(const CommandLine& command_line) {
110   return RunDeElevatedNoWait(command_line.GetProgram().value(),
111                              command_line.GetArgumentsString());
112 }
113 
RunDeElevatedNoWait(const std::wstring & path,const std::wstring & parameters)114 HRESULT RunDeElevatedNoWait(const std::wstring& path,
115                             const std::wstring& parameters) {
116   Microsoft::WRL::ComPtr<IShellWindows> shell;
117   HRESULT hr = ::CoCreateInstance(CLSID_ShellWindows, nullptr,
118                                   CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&shell));
119   if (FAILED(hr)) {
120     return hr;
121   }
122 
123   LONG hwnd = 0;
124   Microsoft::WRL::ComPtr<IDispatch> dispatch;
125   hr = shell->FindWindowSW(ScopedVariant(CSIDL_DESKTOP).AsInput(),
126                            ScopedVariant().AsInput(), SWC_DESKTOP, &hwnd,
127                            SWFO_NEEDDISPATCH, &dispatch);
128   if (hr == S_FALSE || FAILED(hr)) {
129     return hr == S_FALSE ? E_FAIL : hr;
130   }
131 
132   Microsoft::WRL::ComPtr<IServiceProvider> service;
133   hr = dispatch.As(&service);
134   if (FAILED(hr)) {
135     return hr;
136   }
137 
138   Microsoft::WRL::ComPtr<IShellBrowser> browser;
139   hr = service->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&browser));
140   if (FAILED(hr)) {
141     return hr;
142   }
143 
144   Microsoft::WRL::ComPtr<IShellView> view;
145   hr = browser->QueryActiveShellView(&view);
146   if (FAILED(hr)) {
147     return hr;
148   }
149 
150   hr = view->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&dispatch));
151   if (FAILED(hr)) {
152     return hr;
153   }
154 
155   Microsoft::WRL::ComPtr<IShellFolderViewDual> folder;
156   hr = dispatch.As(&folder);
157   if (FAILED(hr)) {
158     return hr;
159   }
160 
161   hr = folder->get_Application(&dispatch);
162   if (FAILED(hr)) {
163     return hr;
164   }
165 
166   Microsoft::WRL::ComPtr<IShellDispatch2> shell_dispatch;
167   hr = dispatch.As(&shell_dispatch);
168   if (FAILED(hr)) {
169     return hr;
170   }
171 
172   return shell_dispatch->ShellExecute(
173       ScopedBstr(path.c_str()).Get(), ScopedVariant(parameters.c_str()),
174       ScopedVariant::kEmptyVariant, ScopedVariant::kEmptyVariant,
175       ScopedVariant::kEmptyVariant);
176 }
177 
178 }  // namespace base::win
179