// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/sas_injector.h"
#include <windows.h>
#include <string>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/scoped_native_library.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "third_party/webrtc/modules/desktop_capture/win/desktop.h"
#include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
namespace remoting {
namespace {
// Names of the API and library implementing software SAS generation.
const base::FilePath::CharType kSasDllFileName[] = FILE_PATH_LITERAL("sas.dll");
const char kSendSasName[] = "SendSAS";
// The prototype of SendSAS().
typedef VOID (WINAPI *SendSasFunc)(BOOL);
// The registry key and value holding the policy controlling software SAS
// generation.
const wchar_t kSystemPolicyKeyName[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
const wchar_t kSoftwareSasValueName[] = L"SoftwareSASGeneration";
const DWORD kEnableSoftwareSasByServices = 1;
// Toggles the default software SAS generation policy to enable SAS generation
// by services. Non-default policy is not changed.
class ScopedSoftwareSasPolicy {
public:
ScopedSoftwareSasPolicy();
~ScopedSoftwareSasPolicy();
bool Apply();
private:
// The handle of the registry key were SoftwareSASGeneration policy is stored.
base::win::RegKey system_policy_;
// True if the policy needs to be restored.
bool restore_policy_;
DISALLOW_COPY_AND_ASSIGN(ScopedSoftwareSasPolicy);
};
ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy()
: restore_policy_(false) {
}
ScopedSoftwareSasPolicy::~ScopedSoftwareSasPolicy() {
// Restore the default policy by deleting the value that we have set.
if (restore_policy_) {
LONG result = system_policy_.DeleteValue(kSoftwareSasValueName);
if (result != ERROR_SUCCESS) {
SetLastError(result);
PLOG(ERROR) << "Failed to restore the software SAS generation policy";
}
}
}
bool ScopedSoftwareSasPolicy::Apply() {
// Query the currently set SoftwareSASGeneration policy.
LONG result = system_policy_.Open(HKEY_LOCAL_MACHINE,
kSystemPolicyKeyName,
KEY_QUERY_VALUE | KEY_SET_VALUE |
KEY_WOW64_64KEY);
if (result != ERROR_SUCCESS) {
SetLastError(result);
PLOG(ERROR) << "Failed to open 'HKLM\\" << kSystemPolicyKeyName << "'";
return false;
}
bool custom_policy = system_policy_.HasValue(kSoftwareSasValueName);
// Override the default policy (i.e. there is no value in the registry) only.
if (!custom_policy) {
result = system_policy_.WriteValue(kSoftwareSasValueName,
kEnableSoftwareSasByServices);
if (result != ERROR_SUCCESS) {
SetLastError(result);
PLOG(ERROR) << "Failed to enable software SAS generation by services";
return false;
} else {
restore_policy_ = true;
}
}
return true;
}
} // namespace
// Sends Secure Attention Sequence using the SendSAS() function from sas.dll.
// This library is shipped starting from Win7/W2K8 R2 only. However Win7 SDK
// includes a redistributable verion of the same library that works on
// Vista/W2K8. We install the latter along with our binaries.
class SasInjectorWin : public SasInjector {
public:
SasInjectorWin();
virtual ~SasInjectorWin();
// SasInjector implementation.
virtual bool InjectSas() OVERRIDE;
private:
base::ScopedNativeLibrary sas_dll_;
SendSasFunc send_sas_;
};
// Emulates Secure Attention Sequence (Ctrl+Alt+Del) by switching to
// the Winlogon desktop and injecting Ctrl+Alt+Del as a hot key.
// N.B. Windows XP/W2K3 only.
class SasInjectorXp : public SasInjector {
public:
SasInjectorXp();
virtual ~SasInjectorXp();
// SasInjector implementation.
virtual bool InjectSas() OVERRIDE;
};
SasInjectorWin::SasInjectorWin() : send_sas_(NULL) {
}
SasInjectorWin::~SasInjectorWin() {
}
bool SasInjectorWin::InjectSas() {
// Load sas.dll. The library is expected to be in the same folder as this
// binary.
if (!sas_dll_.is_valid()) {
base::FilePath dir_path;
if (!PathService::Get(base::DIR_EXE, &dir_path)) {
LOG(ERROR) << "Failed to get the executable file name.";
return false;
}
sas_dll_.Reset(base::LoadNativeLibrary(dir_path.Append(kSasDllFileName),
NULL));
}
if (!sas_dll_.is_valid()) {
LOG(ERROR) << "Failed to load '" << kSasDllFileName << "'";
return false;
}
// Get the pointer to sas!SendSAS().
if (send_sas_ == NULL) {
send_sas_ = static_cast<SendSasFunc>(
sas_dll_.GetFunctionPointer(kSendSasName));
}
if (send_sas_ == NULL) {
LOG(ERROR) << "Failed to retrieve the address of '" << kSendSasName
<< "()'";
return false;
}
// Enable software SAS generation by services and send SAS. SAS can still fail
// if the policy does not allow services to generate software SAS.
ScopedSoftwareSasPolicy enable_sas;
if (!enable_sas.Apply())
return false;
(*send_sas_)(FALSE);
return true;
}
SasInjectorXp::SasInjectorXp() {
}
SasInjectorXp::~SasInjectorXp() {
}
bool SasInjectorXp::InjectSas() {
const wchar_t kWinlogonDesktopName[] = L"Winlogon";
const wchar_t kSasWindowClassName[] = L"SAS window class";
const wchar_t kSasWindowTitle[] = L"SAS window";
scoped_ptr<webrtc::Desktop> winlogon_desktop(
webrtc::Desktop::GetDesktop(kWinlogonDesktopName));
if (!winlogon_desktop.get()) {
PLOG(ERROR) << "Failed to open '" << kWinlogonDesktopName << "' desktop";
return false;
}
webrtc::ScopedThreadDesktop desktop;
if (!desktop.SetThreadDesktop(winlogon_desktop.release())) {
PLOG(ERROR) << "Failed to switch to '" << kWinlogonDesktopName
<< "' desktop";
return false;
}
HWND window = FindWindow(kSasWindowClassName, kSasWindowTitle);
if (!window) {
PLOG(ERROR) << "Failed to find '" << kSasWindowTitle << "' window";
return false;
}
if (PostMessage(window,
WM_HOTKEY,
0,
MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)) == 0) {
PLOG(ERROR) << "Failed to post WM_HOTKEY message";
return false;
}
return true;
}
scoped_ptr<SasInjector> SasInjector::Create() {
if (base::win::GetVersion() < base::win::VERSION_VISTA) {
return scoped_ptr<SasInjector>(new SasInjectorXp());
} else {
return scoped_ptr<SasInjector>(new SasInjectorWin());
}
}
} // namespace remoting