• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "remoting/host/sas_injector.h"
6 
7 #include <windows.h>
8 #include <string>
9 
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"
19 
20 namespace remoting {
21 
22 namespace {
23 
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";
27 
28 // The prototype of SendSAS().
29 typedef VOID (WINAPI *SendSasFunc)(BOOL);
30 
31 // The registry key and value holding the policy controlling software SAS
32 // generation.
33 const wchar_t kSystemPolicyKeyName[] =
34     L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
35 const wchar_t kSoftwareSasValueName[] = L"SoftwareSASGeneration";
36 
37 const DWORD kEnableSoftwareSasByServices = 1;
38 
39 // Toggles the default software SAS generation policy to enable SAS generation
40 // by services. Non-default policy is not changed.
41 class ScopedSoftwareSasPolicy {
42  public:
43   ScopedSoftwareSasPolicy();
44   ~ScopedSoftwareSasPolicy();
45 
46   bool Apply();
47 
48  private:
49   // The handle of the registry key were SoftwareSASGeneration policy is stored.
50   base::win::RegKey system_policy_;
51 
52   // True if the policy needs to be restored.
53   bool restore_policy_;
54 
55   DISALLOW_COPY_AND_ASSIGN(ScopedSoftwareSasPolicy);
56 };
57 
ScopedSoftwareSasPolicy()58 ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy()
59     : restore_policy_(false) {
60 }
61 
~ScopedSoftwareSasPolicy()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) {
67       SetLastError(result);
68       PLOG(ERROR) << "Failed to restore the software SAS generation policy";
69     }
70   }
71 }
72 
Apply()73 bool ScopedSoftwareSasPolicy::Apply() {
74   // Query the currently set SoftwareSASGeneration policy.
75   LONG result = system_policy_.Open(HKEY_LOCAL_MACHINE,
76                                     kSystemPolicyKeyName,
77                                     KEY_QUERY_VALUE | KEY_SET_VALUE |
78                                         KEY_WOW64_64KEY);
79   if (result != ERROR_SUCCESS) {
80     SetLastError(result);
81     PLOG(ERROR) << "Failed to open 'HKLM\\" << kSystemPolicyKeyName << "'";
82     return false;
83   }
84 
85   bool custom_policy = system_policy_.HasValue(kSoftwareSasValueName);
86 
87   // Override the default policy (i.e. there is no value in the registry) only.
88   if (!custom_policy) {
89     result = system_policy_.WriteValue(kSoftwareSasValueName,
90                                        kEnableSoftwareSasByServices);
91     if (result != ERROR_SUCCESS) {
92       SetLastError(result);
93       PLOG(ERROR) << "Failed to enable software SAS generation by services";
94       return false;
95     } else {
96       restore_policy_ = true;
97     }
98   }
99 
100   return true;
101 }
102 
103 } // namespace
104 
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 {
110  public:
111   SasInjectorWin();
112   virtual ~SasInjectorWin();
113 
114   // SasInjector implementation.
115   virtual bool InjectSas() OVERRIDE;
116 
117  private:
118   base::ScopedNativeLibrary sas_dll_;
119   SendSasFunc send_sas_;
120 };
121 
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 {
126  public:
127   SasInjectorXp();
128   virtual ~SasInjectorXp();
129 
130   // SasInjector implementation.
131   virtual bool InjectSas() OVERRIDE;
132 };
133 
SasInjectorWin()134 SasInjectorWin::SasInjectorWin() : send_sas_(NULL) {
135 }
136 
~SasInjectorWin()137 SasInjectorWin::~SasInjectorWin() {
138 }
139 
InjectSas()140 bool SasInjectorWin::InjectSas() {
141   // Load sas.dll. The library is expected to be in the same folder as this
142   // binary.
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.";
147       return false;
148     }
149 
150     sas_dll_.Reset(base::LoadNativeLibrary(dir_path.Append(kSasDllFileName),
151                                            NULL));
152   }
153   if (!sas_dll_.is_valid()) {
154     LOG(ERROR) << "Failed to load '" << kSasDllFileName << "'";
155     return false;
156   }
157 
158   // Get the pointer to sas!SendSAS().
159   if (send_sas_ == NULL) {
160     send_sas_ = reinterpret_cast<SendSasFunc>(
161         sas_dll_.GetFunctionPointer(kSendSasName));
162   }
163   if (send_sas_ == NULL) {
164     LOG(ERROR) << "Failed to retrieve the address of '" << kSendSasName
165                << "()'";
166     return false;
167   }
168 
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())
173     return false;
174 
175   (*send_sas_)(FALSE);
176   return true;
177 }
178 
SasInjectorXp()179 SasInjectorXp::SasInjectorXp() {
180 }
181 
~SasInjectorXp()182 SasInjectorXp::~SasInjectorXp() {
183 }
184 
InjectSas()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";
189 
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";
194     return false;
195   }
196 
197   webrtc::ScopedThreadDesktop desktop;
198   if (!desktop.SetThreadDesktop(winlogon_desktop.release())) {
199     PLOG(ERROR) << "Failed to switch to '" << kWinlogonDesktopName
200                 << "' desktop";
201     return false;
202   }
203 
204   HWND window = FindWindow(kSasWindowClassName, kSasWindowTitle);
205   if (!window) {
206     PLOG(ERROR) << "Failed to find '" << kSasWindowTitle << "' window";
207     return false;
208   }
209 
210   if (PostMessage(window,
211                   WM_HOTKEY,
212                   0,
213                   MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)) == 0) {
214     PLOG(ERROR) << "Failed to post WM_HOTKEY message";
215     return false;
216   }
217 
218   return true;
219 }
220 
Create()221 scoped_ptr<SasInjector> SasInjector::Create() {
222   if (base::win::GetVersion() < base::win::VERSION_VISTA) {
223     return scoped_ptr<SasInjector>(new SasInjectorXp());
224   } else {
225     return scoped_ptr<SasInjector>(new SasInjectorWin());
226   }
227 }
228 
229 } // namespace remoting
230