Browse Source

Passing password to PuTTY using named pipe instead of commandline

Source commit: 5fe46d2ae161d1c5e2132a3b7ef216def16cb405
Martin Prikryl 2 years ago
parent
commit
4bd0859efa

+ 1 - 1
source/core/FileInfo.h

@@ -28,7 +28,7 @@ UnicodeString __fastcall GetLanguage(Word Language);
 UnicodeString __fastcall GetFileInfoString(void * FileInfo,
   TTranslation Translation, UnicodeString StringName, bool AllowEmpty = false);
 
-int __fastcall CalculateCompoundVersion(int MajorVer, int MinorVer, int Release);
+int __fastcall CalculateCompoundVersion(int MajorVer, int MinorVer, int Release = 0);
 int ZeroBuildNumber(int CompoundVersion);
 
 int __fastcall StrToCompoundVersion(UnicodeString S);

+ 2 - 0
source/windows/GUIConfiguration.cpp

@@ -566,6 +566,7 @@ void __fastcall TGUIConfiguration::Default()
   FDefaultPuttyPathOnly = IncludeTrailingBackslash(ProgramsFolder) + L"PuTTY\\" + OriginalPuttyExecutable;
   FDefaultPuttyPath = L"%ProgramFiles%\\PuTTY\\" + OriginalPuttyExecutable;
   FPuttyPath = FormatCommand(FDefaultPuttyPath, L"");
+  FUsePuttyPwFile = asAuto;
   FPuttyPassword = false;
   FTelnetForFtpInPutty = true;
   FPuttySession = L"WinSCP temporary session";
@@ -642,6 +643,7 @@ void __fastcall TGUIConfiguration::UpdateStaticUsage()
     KEYEX(Bool,   SessionRememberPassword, L"QueueRememberPassword"); \
     KEY(String,   PuttySession); \
     KEY(String,   PuttyPath); \
+    KEY(Integer,  UsePuttyPwFile); \
     KEY(Bool,     PuttyPassword); \
     KEY(Bool,     TelnetForFtpInPutty); \
     KEY(DateTime, IgnoreCancelBeforeFinish); \

+ 2 - 0
source/windows/GUIConfiguration.h

@@ -159,6 +159,7 @@ private:
   bool FContinueOnError;
   bool FConfirmCommandSession;
   UnicodeString FPuttyPath;
+  TAutoSwitch FUsePuttyPwFile;
   bool FPuttyPassword;
   bool FTelnetForFtpInPutty;
   UnicodeString FPuttySession;
@@ -268,6 +269,7 @@ public:
   __property UnicodeString AppliedLocaleHex = { read = GetAppliedLocaleHex };
   __property TObjectList * Locales = { read = GetLocales };
   __property UnicodeString PuttyPath = { read = FPuttyPath, write = FPuttyPath };
+  __property TAutoSwitch UsePuttyPwFile = { read = FUsePuttyPwFile, write = FUsePuttyPwFile };
   __property UnicodeString DefaultPuttyPath = { read = FDefaultPuttyPath };
   __property bool PuttyPassword = { read = FPuttyPassword, write = FPuttyPassword };
   __property bool TelnetForFtpInPutty = { read = FTelnetForFtpInPutty, write = FTelnetForFtpInPutty };

+ 157 - 2
source/windows/GUITools.cpp

@@ -15,7 +15,6 @@
 #include <WinInterface.h>
 #include <TbxUtils.hpp>
 #include <Math.hpp>
-#include <WebBrowserEx.hpp>
 #include <Tools.h>
 #include "PngImageList.hpp"
 #include <StrUtils.hpp>
@@ -287,6 +286,124 @@ void TPuttyCleanupThread::DoSchedule()
   FTimer = IncSecond(Now(), 10);
 }
 //---------------------------------------------------------------------------
+class TPuttyPasswordThread : public TSimpleThread
+{
+public:
+  TPuttyPasswordThread(const UnicodeString & Password, const UnicodeString & PipeName);
+  virtual __fastcall ~TPuttyPasswordThread();
+
+protected:
+  virtual void __fastcall Execute();
+  virtual void __fastcall Terminate();
+  virtual bool __fastcall Finished();
+
+private:
+  HANDLE FPipe;
+  AnsiString FPassword;
+
+  void DoSleep(int & Timeout);
+};
+//---------------------------------------------------------------------------
+TPuttyPasswordThread::TPuttyPasswordThread(const UnicodeString & Password, const UnicodeString & PipeName)
+{
+  DWORD OpenMode = PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
+  DWORD PipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS;
+  DWORD BufferSize = 16 * 1024;
+  FPipe = CreateNamedPipe(PipeName.c_str(), OpenMode, PipeMode, 1, BufferSize, BufferSize, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+  if (FPipe == INVALID_HANDLE_VALUE)
+  {
+    throw EOSExtException(L"Cannot create password pipe");
+  }
+  FPassword = AnsiString(Password);
+  Start();
+}
+//---------------------------------------------------------------------------
+__fastcall TPuttyPasswordThread::~TPuttyPasswordThread()
+{
+  DebugAlwaysTrue(FFinished);
+  AppLog(L"Disconnecting and closing password pipe");
+  DisconnectNamedPipe(FPipe);
+  CloseHandle(FPipe);
+}
+//---------------------------------------------------------------------------
+void __fastcall TPuttyPasswordThread::Terminate()
+{
+  // noop - the thread always self-terminates
+}
+//---------------------------------------------------------------------------
+bool __fastcall TPuttyPasswordThread::Finished()
+{
+  return true;
+}
+//---------------------------------------------------------------------------
+void TPuttyPasswordThread::DoSleep(int & Timeout)
+{
+  unsigned int Step = 50;
+  Sleep(Step);
+  Timeout -= Step;
+  if (Timeout <= 0)
+  {
+    AppLog(L"Timeout waiting for PuTTY");
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TPuttyPasswordThread::Execute()
+{
+  AppLog(L"Waiting for PuTTY to connect to password pipe");
+  int Timeout = MSecsPerSec * SecsPerMin;
+  while (Timeout > 0)
+  {
+    if (ConnectNamedPipe(FPipe, NULL))
+    {
+      AppLog(L"Password pipe is ready");
+    }
+    else
+    {
+      int Error = GetLastError();
+      if (Error == ERROR_PIPE_CONNECTED)
+      {
+        AppLog(L"PuTTY has connected to password pipe");
+        int Pos = 0;
+        while ((Timeout > 0) && (Pos < FPassword.Length()))
+        {
+          DWORD Written = 0;
+          if (!WriteFile(FPipe, FPassword.c_str() + Pos, FPassword.Length() - Pos, &Written, NULL))
+          {
+            AppLog(L"Error writting password pipe");
+            Timeout = 0;
+          }
+          else
+          {
+            Pos += Written;
+            if (Pos >= FPassword.Length())
+            {
+              FlushFileBuffers(FPipe);
+              AppLog(L"Complete password was written to pipe");
+              Timeout = 0;
+            }
+            else
+            {
+              AppLog(L"Part of password was written to pipe");
+              DoSleep(Timeout);
+            }
+          }
+        }
+      }
+      else if (Error == ERROR_PIPE_LISTENING)
+      {
+        DoSleep(Timeout);
+      }
+      else
+      {
+        AppLogFmt(L"Password pipe error %d", (Error));
+        Timeout = 0;
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
+unsigned int PipeCounter = 0;
+//---------------------------------------------------------------------------
 void OpenSessionInPutty(TSessionData * SessionData)
 {
   // putty does not support resolving environment variables in session settings
@@ -432,7 +549,42 @@ void OpenSessionInPutty(TSessionData * SessionData)
     if (!Password.IsEmpty() && !LocalCustomCommand.IsPasswordCommand(AParams))
     {
       Password = NormalizeString(Password); // if password is empty, we should quote it always
-      AddToList(PuttyParams, FORMAT(L"-pw %s", (EscapePuttyCommandParam(Password))), L" ");
+      bool UsePuttyPwFile;
+      if (GUIConfiguration->UsePuttyPwFile == asAuto)
+      {
+        UsePuttyPwFile = false;
+        if (SameText(ExtractFileName(Program), OriginalPuttyExecutable))
+        {
+          unsigned int Version = GetFileVersion(Program);
+          if (Version != static_cast<unsigned int>(-1))
+          {
+            int MajorVersion = HIWORD(Version);
+            int MinorVersion = LOWORD(Version);
+            if (CalculateCompoundVersion(MajorVersion, MinorVersion) >= CalculateCompoundVersion(0, 77))
+            {
+              UsePuttyPwFile = true;
+            }
+          }
+        }
+      }
+      else
+      {
+        UsePuttyPwFile = (GUIConfiguration->UsePuttyPwFile == asOn);
+      }
+
+      UnicodeString PasswordParam;
+      if (UsePuttyPwFile)
+      {
+        PipeCounter++;
+        UnicodeString PipeName = FORMAT(L"\\\\.\\PIPE\\WinSCPPuTTYPassword.%.8x.%.8x.%.4x", (GetCurrentProcessId(), PipeCounter, rand()));
+        new TPuttyPasswordThread(Password, PipeName);
+        PasswordParam = FORMAT(L"-pwfile \"%s\"", (PipeName));
+      }
+      else
+      {
+        PasswordParam = FORMAT(L"-pw %s", (EscapePuttyCommandParam(Password)));
+      }
+      AddToList(PuttyParams, PasswordParam, L" ");
     }
 
     AddToList(PuttyParams, Params, L" ");
@@ -1258,6 +1410,9 @@ protected:
   }
 };
 //---------------------------------------------------------------------------
+// Included only here as it defines ambiguous LONG_PTR, causing INVALID_HANDLE_VALUE to be unusable
+#include <WebBrowserEx.hpp>
+//---------------------------------------------------------------------------
 class TBrowserViewer : public TWebBrowserEx
 {
 public: