Home | History | Annotate | Download | only in host
      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 
     58 ScopedSoftwareSasPolicy::ScopedSoftwareSasPolicy()
     59     : restore_policy_(false) {
     60 }
     61 
     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       LOG_GETLASTERROR(ERROR)
     69           << "Failed to restore the software SAS generation policy";
     70     }
     71   }
     72 }
     73 
     74 bool 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.
    112 class 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.
    128 class SasInjectorXp : public SasInjector {
    129  public:
    130   SasInjectorXp();
    131   virtual ~SasInjectorXp();
    132 
    133   // SasInjector implementation.
    134   virtual bool InjectSas() OVERRIDE;
    135 };
    136 
    137 SasInjectorWin::SasInjectorWin() : send_sas_(NULL) {
    138 }
    139 
    140 SasInjectorWin::~SasInjectorWin() {
    141 }
    142 
    143 bool 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 
    182 SasInjectorXp::SasInjectorXp() {
    183 }
    184 
    185 SasInjectorXp::~SasInjectorXp() {
    186 }
    187 
    188 bool 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 
    227 scoped_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