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 PLOG(ERROR) << "Failed to restore the software SAS generation policy"; 69 } 70 } 71} 72 73bool 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. 109class 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. 125class SasInjectorXp : public SasInjector { 126 public: 127 SasInjectorXp(); 128 virtual ~SasInjectorXp(); 129 130 // SasInjector implementation. 131 virtual bool InjectSas() OVERRIDE; 132}; 133 134SasInjectorWin::SasInjectorWin() : send_sas_(NULL) { 135} 136 137SasInjectorWin::~SasInjectorWin() { 138} 139 140bool 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 179SasInjectorXp::SasInjectorXp() { 180} 181 182SasInjectorXp::~SasInjectorXp() { 183} 184 185bool 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 221scoped_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