1 // Copyright (c) 2012 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.
5 #include "remoting/host/sas_injector.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/scoped_native_library.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/win/registry.h"
16 #include "base/win/windows_version.h"
17 #include "third_party/webrtc/modules/desktop_capture/win/desktop.h"
18 #include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
24 // Names of the API and library implementing software SAS generation.
25 const base::FilePath::CharType kSasDllFileName
[] = FILE_PATH_LITERAL("sas.dll");
26 const char kSendSasName
[] = "SendSAS";
28 // The prototype of SendSAS().
29 typedef VOID (WINAPI
*SendSasFunc
)(BOOL
);
31 // The registry key and value holding the policy controlling software SAS
33 const wchar_t kSystemPolicyKeyName
[] =
34 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
35 const wchar_t kSoftwareSasValueName
[] = L
"SoftwareSASGeneration";
37 const DWORD kEnableSoftwareSasByServices
= 1;
39 // Toggles the default software SAS generation policy to enable SAS generation
40 // by services. Non-default policy is not changed.
41 class ScopedSoftwareSasPolicy
{
43 ScopedSoftwareSasPolicy();
44 ~ScopedSoftwareSasPolicy();
49 // The handle of the registry key were SoftwareSASGeneration policy is stored.
50 base::win::RegKey system_policy_
;
52 // True if the policy needs to be restored.
55 DISALLOW_COPY_AND_ASSIGN(ScopedSoftwareSasPolicy
);
58 ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy()
59 : restore_policy_(false) {
62 ScopedSoftwareSasPolicy::~ScopedSoftwareSasPolicy() {
63 // Restore the default policy by deleting the value that we have set.
64 if (restore_policy_
) {
65 LONG result
= system_policy_
.DeleteValue(kSoftwareSasValueName
);
66 if (result
!= ERROR_SUCCESS
) {
68 PLOG(ERROR
) << "Failed to restore the software SAS generation policy";
73 bool ScopedSoftwareSasPolicy::Apply() {
74 // Query the currently set SoftwareSASGeneration policy.
75 LONG result
= system_policy_
.Open(HKEY_LOCAL_MACHINE
,
77 KEY_QUERY_VALUE
| KEY_SET_VALUE
|
79 if (result
!= ERROR_SUCCESS
) {
81 PLOG(ERROR
) << "Failed to open 'HKLM\\" << kSystemPolicyKeyName
<< "'";
85 bool custom_policy
= system_policy_
.HasValue(kSoftwareSasValueName
);
87 // Override the default policy (i.e. there is no value in the registry) only.
89 result
= system_policy_
.WriteValue(kSoftwareSasValueName
,
90 kEnableSoftwareSasByServices
);
91 if (result
!= ERROR_SUCCESS
) {
93 PLOG(ERROR
) << "Failed to enable software SAS generation by services";
96 restore_policy_
= true;
105 // Sends Secure Attention Sequence using the SendSAS() function from sas.dll.
106 // This library is shipped starting from Win7/W2K8 R2 only. However Win7 SDK
107 // includes a redistributable verion of the same library that works on
108 // Vista/W2K8. We install the latter along with our binaries.
109 class SasInjectorWin
: public SasInjector
{
112 ~SasInjectorWin() override
;
114 // SasInjector implementation.
115 bool InjectSas() override
;
118 base::ScopedNativeLibrary sas_dll_
;
119 SendSasFunc send_sas_
;
122 // Emulates Secure Attention Sequence (Ctrl+Alt+Del) by switching to
123 // the Winlogon desktop and injecting Ctrl+Alt+Del as a hot key.
124 // N.B. Windows XP/W2K3 only.
125 class SasInjectorXp
: public SasInjector
{
128 ~SasInjectorXp() override
;
130 // SasInjector implementation.
131 bool InjectSas() override
;
134 SasInjectorWin::SasInjectorWin() : send_sas_(nullptr) {
137 SasInjectorWin::~SasInjectorWin() {
140 bool SasInjectorWin::InjectSas() {
141 // Load sas.dll. The library is expected to be in the same folder as this
143 if (!sas_dll_
.is_valid()) {
144 base::FilePath dir_path
;
145 if (!PathService::Get(base::DIR_EXE
, &dir_path
)) {
146 LOG(ERROR
) << "Failed to get the executable file name.";
150 sas_dll_
.Reset(base::LoadNativeLibrary(dir_path
.Append(kSasDllFileName
),
153 if (!sas_dll_
.is_valid()) {
154 LOG(ERROR
) << "Failed to load '" << kSasDllFileName
<< "'";
158 // Get the pointer to sas!SendSAS().
159 if (send_sas_
== nullptr) {
160 send_sas_
= reinterpret_cast<SendSasFunc
>(
161 sas_dll_
.GetFunctionPointer(kSendSasName
));
163 if (send_sas_
== nullptr) {
164 LOG(ERROR
) << "Failed to retrieve the address of '" << kSendSasName
169 // Enable software SAS generation by services and send SAS. SAS can still fail
170 // if the policy does not allow services to generate software SAS.
171 ScopedSoftwareSasPolicy enable_sas
;
172 if (!enable_sas
.Apply())
179 SasInjectorXp::SasInjectorXp() {
182 SasInjectorXp::~SasInjectorXp() {
185 bool SasInjectorXp::InjectSas() {
186 const wchar_t kWinlogonDesktopName
[] = L
"Winlogon";
187 const wchar_t kSasWindowClassName
[] = L
"SAS window class";
188 const wchar_t kSasWindowTitle
[] = L
"SAS window";
190 scoped_ptr
<webrtc::Desktop
> winlogon_desktop(
191 webrtc::Desktop::GetDesktop(kWinlogonDesktopName
));
192 if (!winlogon_desktop
.get()) {
193 PLOG(ERROR
) << "Failed to open '" << kWinlogonDesktopName
<< "' desktop";
197 webrtc::ScopedThreadDesktop desktop
;
198 if (!desktop
.SetThreadDesktop(winlogon_desktop
.release())) {
199 PLOG(ERROR
) << "Failed to switch to '" << kWinlogonDesktopName
204 HWND window
= FindWindow(kSasWindowClassName
, kSasWindowTitle
);
206 PLOG(ERROR
) << "Failed to find '" << kSasWindowTitle
<< "' window";
210 if (PostMessage(window
,
213 MAKELONG(MOD_ALT
| MOD_CONTROL
, VK_DELETE
)) == 0) {
214 PLOG(ERROR
) << "Failed to post WM_HOTKEY message";
221 scoped_ptr
<SasInjector
> SasInjector::Create() {
222 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
223 return make_scoped_ptr(new SasInjectorXp());
225 return make_scoped_ptr(new SasInjectorWin());
229 } // namespace remoting