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 20namespace remoting { 21 22namespace { 23 24// Names of the API and library implementing software SAS generation. 25const base::FilePath::CharType kSasDllFileName[] = FILE_PATH_LITERAL("sas.dll"); 26const char kSendSasName[] = "SendSAS"; 27 28// The prototype of SendSAS(). 29typedef VOID (WINAPI *SendSasFunc)(BOOL); 30 31// The registry key and value holding the policy controlling software SAS 32// generation. 33const wchar_t kSystemPolicyKeyName[] = 34 L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; 35const wchar_t kSoftwareSasValueName[] = L"SoftwareSASGeneration"; 36 37const 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. 41class 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 58ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy() 59 : restore_policy_(false) { 60} 61 62ScopedSoftwareSasPolicy::~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 LOG_GETLASTERROR(ERROR) 69 << "Failed to restore the software SAS generation policy"; 70 } 71 } 72} 73 74bool ScopedSoftwareSasPolicy::Apply() { 75 // Query the currently set SoftwareSASGeneration policy. 76 LONG result = system_policy_.Open(HKEY_LOCAL_MACHINE, 77 kSystemPolicyKeyName, 78 KEY_QUERY_VALUE | KEY_SET_VALUE | 79 KEY_WOW64_64KEY); 80 if (result != ERROR_SUCCESS) { 81 SetLastError(result); 82 LOG_GETLASTERROR(ERROR) << "Failed to open 'HKLM\\" 83 << kSystemPolicyKeyName << "'"; 84 return false; 85 } 86 87 bool custom_policy = system_policy_.HasValue(kSoftwareSasValueName); 88 89 // Override the default policy (i.e. there is no value in the registry) only. 90 if (!custom_policy) { 91 result = system_policy_.WriteValue(kSoftwareSasValueName, 92 kEnableSoftwareSasByServices); 93 if (result != ERROR_SUCCESS) { 94 SetLastError(result); 95 LOG_GETLASTERROR(ERROR) 96 << "Failed to enable software SAS generation by services"; 97 return false; 98 } else { 99 restore_policy_ = true; 100 } 101 } 102 103 return true; 104} 105 106} // namespace 107 108// Sends Secure Attention Sequence using the SendSAS() function from sas.dll. 109// This library is shipped starting from Win7/W2K8 R2 only. However Win7 SDK 110// includes a redistributable verion of the same library that works on 111// Vista/W2K8. We install the latter along with our binaries. 112class SasInjectorWin : public SasInjector { 113 public: 114 SasInjectorWin(); 115 virtual ~SasInjectorWin(); 116 117 // SasInjector implementation. 118 virtual bool InjectSas() OVERRIDE; 119 120 private: 121 base::ScopedNativeLibrary sas_dll_; 122 SendSasFunc send_sas_; 123}; 124 125// Emulates Secure Attention Sequence (Ctrl+Alt+Del) by switching to 126// the Winlogon desktop and injecting Ctrl+Alt+Del as a hot key. 127// N.B. Windows XP/W2K3 only. 128class SasInjectorXp : public SasInjector { 129 public: 130 SasInjectorXp(); 131 virtual ~SasInjectorXp(); 132 133 // SasInjector implementation. 134 virtual bool InjectSas() OVERRIDE; 135}; 136 137SasInjectorWin::SasInjectorWin() : send_sas_(NULL) { 138} 139 140SasInjectorWin::~SasInjectorWin() { 141} 142 143bool SasInjectorWin::InjectSas() { 144 // Load sas.dll. The library is expected to be in the same folder as this 145 // binary. 146 if (!sas_dll_.is_valid()) { 147 base::FilePath dir_path; 148 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 149 LOG(ERROR) << "Failed to get the executable file name."; 150 return false; 151 } 152 153 sas_dll_.Reset(base::LoadNativeLibrary(dir_path.Append(kSasDllFileName), 154 NULL)); 155 } 156 if (!sas_dll_.is_valid()) { 157 LOG(ERROR) << "Failed to load '" << kSasDllFileName << "'"; 158 return false; 159 } 160 161 // Get the pointer to sas!SendSAS(). 162 if (send_sas_ == NULL) { 163 send_sas_ = static_cast<SendSasFunc>( 164 sas_dll_.GetFunctionPointer(kSendSasName)); 165 } 166 if (send_sas_ == NULL) { 167 LOG(ERROR) << "Failed to retrieve the address of '" << kSendSasName 168 << "()'"; 169 return false; 170 } 171 172 // Enable software SAS generation by services and send SAS. SAS can still fail 173 // if the policy does not allow services to generate software SAS. 174 ScopedSoftwareSasPolicy enable_sas; 175 if (!enable_sas.Apply()) 176 return false; 177 178 (*send_sas_)(FALSE); 179 return true; 180} 181 182SasInjectorXp::SasInjectorXp() { 183} 184 185SasInjectorXp::~SasInjectorXp() { 186} 187 188bool SasInjectorXp::InjectSas() { 189 const wchar_t kWinlogonDesktopName[] = L"Winlogon"; 190 const wchar_t kSasWindowClassName[] = L"SAS window class"; 191 const wchar_t kSasWindowTitle[] = L"SAS window"; 192 193 scoped_ptr<webrtc::Desktop> winlogon_desktop( 194 webrtc::Desktop::GetDesktop(kWinlogonDesktopName)); 195 if (!winlogon_desktop.get()) { 196 LOG_GETLASTERROR(ERROR) 197 << "Failed to open '" << kWinlogonDesktopName << "' desktop"; 198 return false; 199 } 200 201 webrtc::ScopedThreadDesktop desktop; 202 if (!desktop.SetThreadDesktop(winlogon_desktop.release())) { 203 LOG_GETLASTERROR(ERROR) 204 << "Failed to switch to '" << kWinlogonDesktopName << "' desktop"; 205 return false; 206 } 207 208 HWND window = FindWindow(kSasWindowClassName, kSasWindowTitle); 209 if (!window) { 210 LOG_GETLASTERROR(ERROR) 211 << "Failed to find '" << kSasWindowTitle << "' window"; 212 return false; 213 } 214 215 if (PostMessage(window, 216 WM_HOTKEY, 217 0, 218 MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)) == 0) { 219 LOG_GETLASTERROR(ERROR) 220 << "Failed to post WM_HOTKEY message"; 221 return false; 222 } 223 224 return true; 225} 226 227scoped_ptr<SasInjector> SasInjector::Create() { 228 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 229 return scoped_ptr<SasInjector>(new SasInjectorXp()); 230 } else { 231 return scoped_ptr<SasInjector>(new SasInjectorWin()); 232 } 233} 234 235} // namespace remoting 236