1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Bug 386: Automatic upload of keys pairs + Public key can be displayed in Advanced Site Settings dialog

https://winscp.net/tracker/386

Source commit: 78d8baba92c09728ef3bccc9b49f1590a76d4ca5
This commit is contained in:
Martin Prikryl
2018-05-10 09:08:45 +00:00
parent bc9622ab93
commit 64f634db37
28 changed files with 466 additions and 46 deletions

View File

@@ -81,6 +81,7 @@ public:
const TRemoteFile * File, UnicodeString Command, int Params, TCaptureOutputEvent OutputEvent) = 0;
virtual void __fastcall DoStartup() = 0;
virtual void __fastcall HomeDirectory() = 0;
virtual UnicodeString __fastcall GetHomeDirectory() { throw Exception(L"Not implemented"); };
virtual bool __fastcall IsCapable(int Capability) const = 0;
virtual void __fastcall LookupUsersGroups() = 0;
virtual void __fastcall ReadCurrentDirectory() = 0;

View File

@@ -10,6 +10,7 @@
#include "CoreMain.h"
#include "TextsCore.h"
#include <StrUtils.hpp>
#include <Soap.EncdDecd.hpp>
//---------------------------------------------------------------------------
char sshver[50];
extern const char commitid[] = "";
@@ -686,6 +687,49 @@ void FreeKey(TPrivateKey * PrivateKey)
sfree(Ssh2Key);
}
//---------------------------------------------------------------------------
RawByteString LoadPublicKey(const UnicodeString & FileName, UnicodeString & Algorithm, UnicodeString & Comment)
{
RawByteString Result;
UTF8String UtfFileName = UTF8String(FileName);
Filename * KeyFile = filename_from_str(UtfFileName.c_str());
try
{
char * AlgorithmStr = NULL;
int PublicKeyLen = 0;
char * CommentStr = NULL;
const char * ErrorStr = NULL;
unsigned char * PublicKeyPtr =
ssh2_userkey_loadpub(KeyFile, &AlgorithmStr, &PublicKeyLen, &CommentStr, &ErrorStr);
if (PublicKeyPtr == NULL)
{
UnicodeString Error = UnicodeString(AnsiString(ErrorStr));
throw Exception(Error);
}
Algorithm = UnicodeString(AnsiString(AlgorithmStr));
sfree(AlgorithmStr);
Comment = UnicodeString(AnsiString(CommentStr));
sfree(CommentStr);
Result = RawByteString(reinterpret_cast<char *>(PublicKeyPtr), PublicKeyLen);
free(PublicKeyPtr);
}
__finally
{
filename_free(KeyFile);
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString GetPublicKeyLine(const UnicodeString & FileName, UnicodeString & Comment)
{
UnicodeString Algorithm;
RawByteString PublicKey = LoadPublicKey(FileName, Algorithm, Comment);
UnicodeString PublicKeyBase64 = EncodeBase64(PublicKey.c_str(), PublicKey.Length());
PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\r", L"");
PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\n", L"");
UnicodeString Result = FORMAT(L"%s %s %s", (Algorithm, PublicKeyBase64, Comment));
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall HasGSSAPI(UnicodeString CustomPath)
{
static int has = -1;
@@ -879,4 +923,13 @@ UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
return Result;
}
//---------------------------------------------------------------------------
bool IsOpenSSH(const UnicodeString & SshImplementation)
{
return
// e.g. "OpenSSH_5.3"
(SshImplementation.Pos(L"OpenSSH") == 1) ||
// Sun SSH is based on OpenSSH (suffers the same bugs)
(SshImplementation.Pos(L"Sun_SSH") == 1);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

View File

@@ -17,6 +17,7 @@ void ChangeKeyComment(TPrivateKey * PrivateKey, const UnicodeString & Comment);
void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
const UnicodeString & Passphrase, TPrivateKey * PrivateKey);
void FreeKey(TPrivateKey * PrivateKey);
UnicodeString GetPublicKeyLine(const UnicodeString & FileName, UnicodeString & Comment);
extern const UnicodeString PuttyKeyExt;
//---------------------------------------------------------------------------
bool __fastcall HasGSSAPI(UnicodeString CustomPath);
@@ -37,4 +38,6 @@ UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const s
//---------------------------------------------------------------------------
UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType);
//---------------------------------------------------------------------------
bool IsOpenSSH(const UnicodeString & SshImplementation);
//---------------------------------------------------------------------------
#endif

View File

@@ -455,10 +455,7 @@ void __fastcall TSecureShell::Open()
FOpened = true;
UnicodeString SshImplementation = GetSessionInfo().SshImplementation;
if (// e.g. "OpenSSH_5.3"
(SshImplementation.Pos(L"OpenSSH") == 1) ||
// Sun SSH is based on OpenSSH (suffers the same bugs)
(SshImplementation.Pos(L"Sun_SSH") == 1))
if (IsOpenSSH(SshImplementation))
{
FSshImplementation = sshiOpenSSH;
}

View File

@@ -509,7 +509,7 @@ bool __fastcall TSessionData::IsSame(const TSessionData * Default, bool Advanced
return IsSame(Default, AdvancedOnly, NULL);
}
//---------------------------------------------------------------------
static TFSProtocol NormalizeFSProtocol(TFSProtocol FSProtocol)
TFSProtocol NormalizeFSProtocol(TFSProtocol FSProtocol)
{
if ((FSProtocol == fsSCPonly) || (FSProtocol == fsSFTPonly))
{

View File

@@ -711,5 +711,6 @@ bool __fastcall IsSshProtocol(TFSProtocol FSProtocol);
int __fastcall DefaultPort(TFSProtocol FSProtocol, TFtps Ftps);
bool __fastcall IsIPv6Literal(const UnicodeString & HostName);
UnicodeString __fastcall EscapeIPv6Literal(const UnicodeString & IP);
TFSProtocol NormalizeFSProtocol(TFSProtocol FSProtocol);
//---------------------------------------------------------------------------
#endif

View File

@@ -72,6 +72,7 @@ public:
const TRemoteFile * File, UnicodeString Command, int Params, TCaptureOutputEvent OutputEvent);
virtual void __fastcall DoStartup();
virtual void __fastcall HomeDirectory();
virtual UnicodeString __fastcall GetHomeDirectory();
virtual bool __fastcall IsCapable(int Capability) const;
virtual void __fastcall LookupUsersGroups();
virtual void __fastcall ReadCurrentDirectory();
@@ -132,7 +133,6 @@ protected:
TRemoteFile *& File, unsigned char Type, TRemoteFile * ALinkedByFile = NULL,
int AllowStatus = -1);
virtual UnicodeString __fastcall GetCurrentDirectory();
UnicodeString __fastcall GetHomeDirectory();
unsigned long __fastcall GotStatusPacket(TSFTPPacket * Packet, int AllowStatus);
bool __fastcall RemoteFileExists(const UnicodeString FullPath, TRemoteFile ** File = NULL);
TRemoteFile * __fastcall LoadFile(TSFTPPacket * Packet,

View File

@@ -4681,6 +4681,11 @@ void __fastcall TTerminal::HomeDirectory()
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TTerminal::GetHomeDirectory()
{
return FFileSystem->GetHomeDirectory();
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::ChangeDirectory(const UnicodeString Directory)
{
DebugAssert(FFileSystem);

View File

@@ -500,6 +500,7 @@ public:
void __fastcall ChangeDirectory(const UnicodeString Directory);
void __fastcall EndTransaction();
void __fastcall HomeDirectory();
UnicodeString __fastcall GetHomeDirectory();
void __fastcall ChangeFileProperties(UnicodeString FileName,
const TRemoteFile * File, /*const TRemoteProperties */ void * Properties);
void __fastcall ChangeFilesProperties(TStrings * FileList,

View File

@@ -9486,6 +9486,19 @@ void __fastcall TCustomScpExplorerForm::ChangePassword()
}
}
//---------------------------------------------------------------------------
bool __fastcall TCustomScpExplorerForm::CanPrivateKeyUpload()
{
// No nice way to assert SSH2
return (Terminal != NULL) && Terminal->Active && (Terminal->FSProtocol == cfsSFTP);
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::PrivateKeyUpload()
{
TOperationVisualizer OperationVisualizer;
UnicodeString FileName = Terminal->SessionData->PublicKeyFile;
TTerminalManager::Instance()->UploadPublicKey(Terminal, NULL, FileName);
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::ChangeScale(int M, int D)
{
TForm::ChangeScale(M, D);

View File

@@ -717,6 +717,8 @@ public:
bool __fastcall CanConsole();
bool __fastcall CanChangePassword();
void __fastcall ChangePassword();
bool __fastcall CanPrivateKeyUpload();
void __fastcall PrivateKeyUpload();
bool __fastcall IsComponentPossible(Byte Component);
__property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };

View File

@@ -417,6 +417,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
UPD(NewFileAction, DirViewEnabled(osCurrent) && !WinConfiguration->DisableOpenEdit)
UPD(EditorListCustomizeAction, true)
UPD(ChangePasswordAction, ScpExplorer->CanChangePassword())
UPD(PrivateKeyUploadAction, ScpExplorer->CanPrivateKeyUpload())
// CUSTOM COMMANDS
UPD(CustomCommandsFileAction, true)
@@ -730,6 +731,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
EXE(NewFileAction, ScpExplorer->EditNew(osCurrent))
EXE(EditorListCustomizeAction, PreferencesDialog(pmEditor))
EXE(ChangePasswordAction, ScpExplorer->ChangePassword())
EXE(PrivateKeyUploadAction, ScpExplorer->PrivateKeyUpload())
// CUSTOM COMMANDS
EXE(CustomCommandsFileAction, CreateCustomCommandsMenu(CustomCommandsFileAction, ccltFile))

View File

@@ -2208,6 +2208,13 @@ object NonVisualDataModule: TNonVisualDataModule
HelpKeyword = 'task_change_password'
Hint = 'Change account password'
end
object PrivateKeyUploadAction: TAction
Tag = 15
Category = 'Session'
Caption = '&Install Public Key into Server...'
HelpKeyword = 'guide_public_key'
Hint = 'Install public key for authentication into the server'
end
object RemoteNewFileAction: TAction
Tag = 15
Category = 'Remote Selected Operation'

View File

@@ -615,6 +615,7 @@ __published: // IDE-managed Components
TTBXSubmenuItem *TBXSubmenuItem4;
TTBXSubmenuItem *TBXSubmenuItem6;
TTBXSubmenuItem *TBXSubmenuItem9;
TAction *PrivateKeyUploadAction;
void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
void __fastcall ExplorerActionsExecute(TBasicAction *Action, bool &Handled);
void __fastcall SessionIdleTimerTimer(TObject *Sender);

View File

@@ -429,6 +429,9 @@ inherited ScpCommanderForm: TScpCommanderForm
object TBXItem227: TTBXItem
Action = NonVisualDataModule.ChangePasswordAction
end
object TBXItem76: TTBXItem
Action = NonVisualDataModule.PrivateKeyUploadAction
end
object TBXSeparatorItem29: TTBXSeparatorItem
end
object TBXSubmenuItem21: TTBXSubmenuItem

View File

@@ -423,6 +423,7 @@ __published:
TTBXItem *TBXItem248;
TTBXItem *TBXItem249;
TTBXItem *TBXItem250;
TTBXItem *TBXItem76;
void __fastcall SplitterMoved(TObject *Sender);
void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
bool &Accept);

View File

@@ -305,6 +305,9 @@ inherited ScpExplorerForm: TScpExplorerForm
object TBXItem160: TTBXItem
Action = NonVisualDataModule.ChangePasswordAction
end
object TBXItem14: TTBXItem
Action = NonVisualDataModule.ChangePasswordAction
end
object TBXSeparatorItem29: TTBXSeparatorItem
end
object TBXSubmenuItem21: TTBXSubmenuItem

View File

@@ -313,6 +313,7 @@ __published:
TTBXItem *TBXItem247;
TTBXItem *TBXItem244;
TTBXItem *TBXItem246;
TTBXItem *TBXItem14;
void __fastcall RemoteDirViewUpdateStatusBar(TObject *Sender,
const TStatusFileInfo &FileInfo);
void __fastcall UnixPathComboBoxBeginEdit(TTBEditItem *Sender,

View File

@@ -16,6 +16,8 @@
#include "Tools.h"
#include "WinConfiguration.h"
#include "PuttyTools.h"
#include "TerminalManager.h"
#include "Authenticate.h"
//---------------------------------------------------------------------
#pragma link "ComboEdit"
#pragma link "PasswordEdit"
@@ -105,8 +107,7 @@ void __fastcall TSiteAdvancedDialog::InitControls()
SelectScaledImageList(ColorImageList);
SetSessionColor((TColor)0);
UnicodeString Dummy;
PrivateKeyGenerateButton->Enabled = FindTool(PuttygenTool, Dummy);
MenuButton(PrivateKeyToolsButton);
}
//---------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::LoadSession()
@@ -752,8 +753,7 @@ void __fastcall TSiteAdvancedDialog::UpdateControls()
TAutoNestingCounter NoUpdateCounter(NoUpdate);
bool SshProtocol = FSessionData->UsesSsh;
bool SftpProtocol =
(FSessionData->FSProtocol == fsSFTPonly) || (FSessionData->FSProtocol == fsSFTP);
bool SftpProtocol = (NormalizeFSProtocol(FSessionData->FSProtocol) == fsSFTP);
bool ScpProtocol = (FSessionData->FSProtocol == fsSCPonly);
bool FtpProtocol = (FSessionData->FSProtocol == fsFTP);
bool WebDavProtocol = (FSessionData->FSProtocol == fsWebDAV);
@@ -794,6 +794,7 @@ void __fastcall TSiteAdvancedDialog::UpdateControls()
((AuthTISCheck->Enabled && AuthTISCheck->Checked) ||
(AuthKICheck->Enabled && AuthKICheck->Checked)));
EnableControl(AuthenticationParamsGroup, AuthenticationGroup->Enabled);
EnableControl(PrivateKeyViewButton, PrivateKeyEdit3->Enabled && !PrivateKeyEdit3->Text.IsEmpty());
EnableControl(AuthGSSAPICheck3,
AuthenticationGroup->Enabled && (GetSshProt() == ssh2only));
EnableControl(GSSAPIFwdTGTCheck,
@@ -1255,7 +1256,7 @@ void __fastcall TSiteAdvancedDialog::PrivateKeyEdit3AfterDialog(TObject * Sender
TFilenameEdit * Edit = dynamic_cast<TFilenameEdit *>(Sender);
if (Name != Edit->Text)
{
VerifyAndConvertKey(Name, GetSshProt());
VerifyAndConvertKey(Name, GetSshProt(), true);
}
}
//---------------------------------------------------------------------------
@@ -1521,14 +1522,61 @@ void __fastcall TSiteAdvancedDialog::PrivateKeyCreatedOrModified(TObject * /*Sen
{
if (SameText(ExtractFileExt(FileName), FORMAT(L".%s", (PuttyKeyExt))))
{
PrivateKeyEdit3->FileName = FileName;
PrivateKeyEdit3->Text = FileName;
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyGenerateButtonClick(TObject * /*Sender*/)
void __fastcall TSiteAdvancedDialog::PrivateKeyToolsButtonClick(TObject * /*Sender*/)
{
UnicodeString Dummy;
PrivateKeyGenerateItem->Enabled = FindTool(PuttygenTool, Dummy);
PrivateKeyUploadItem->Enabled = (GetSshProt() == ssh2only) && (NormalizeFSProtocol(FSessionData->FSProtocol) == fsSFTP);
MenuPopup(PrivateKeyMenu, PrivateKeyToolsButton);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyGenerateItemClick(TObject * /*Sender*/)
{
unsigned int Filters = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE;
FPrivateKeyMonitors.reset(StartCreationDirectoryMonitorsOnEachDrive(Filters, PrivateKeyCreatedOrModified));
ExecuteTool(PuttygenTool);
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyUploadItemClick(TObject * /*Sender*/)
{
SaveSession();
FSessionData->FSProtocol = fsSFTPonly; // no SCP fallback, as SCP does not implement GetHomeDirectory
FSessionData->RemoteDirectory = UnicodeString();
UnicodeString FileName = PrivateKeyEdit3->Text;
if (TTerminalManager::Instance()->UploadPublicKey(NULL, FSessionData, FileName))
{
PrivateKeyEdit3->Text = FileName;
PrivateKeyEdit3->SetFocus();
}
}
//---------------------------------------------------------------------------
void __fastcall TSiteAdvancedDialog::PrivateKeyViewButtonClick(TObject * /*Sender*/)
{
UnicodeString FileName = PrivateKeyEdit3->Text;
VerifyAndConvertKey(FileName, GetSshProt(), false);
PrivateKeyEdit3->Text = FileName;
UnicodeString CommentDummy;
UnicodeString Line = GetPublicKeyLine(FileName, CommentDummy);
std::unique_ptr<TStrings> Messages(TextToStringList(Line));
TClipboardHandler ClipboardHandler;
ClipboardHandler.Text = Line;
TMessageParams Params;
TQueryButtonAlias Aliases[1];
Aliases[0].Button = qaRetry;
Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
Aliases[0].OnSubmit = &ClipboardHandler.Copy;
Params.Aliases = Aliases;
Params.AliasesCount = LENOF(Aliases);
UnicodeString Message = LoadStr(LOGIN_AUTHORIZED_KEYS);
int Answers = qaOK | qaRetry;
MoreMessageDialog(Message, Messages.get(), qtInformation, Answers, HELP_LOGIN_AUTHORIZED_KEYS, &Params);
}
//---------------------------------------------------------------------------

View File

@@ -2048,15 +2048,23 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
Text = 'PrivateKeyEdit3'
OnChange = DataChange
end
object PrivateKeyGenerateButton: TButton
Left = 12
object PrivateKeyToolsButton: TButton
Left = 151
Top = 86
Width = 101
Height = 25
Anchors = [akTop, akRight]
Caption = 'Ge&nerate...'
Caption = '&Tools'
TabOrder = 2
OnClick = PrivateKeyGenerateButtonClick
OnClick = PrivateKeyToolsButtonClick
end
object PrivateKeyViewButton: TButton
Left = 12
Top = 86
Width = 133
Height = 25
Caption = '&Display Public Key'
TabOrder = 3
OnClick = PrivateKeyViewButtonClick
end
end
object GSSAPIGroup: TGroupBox
@@ -3662,4 +3670,16 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
FFFFFFFF00000000000000000000000000000000000000000000000000000000
000000000000}
end
object PrivateKeyMenu: TPopupMenu
Left = 128
Top = 384
object PrivateKeyGenerateItem: TMenuItem
Caption = '&Generate New Key Pair with PuTTYgen...'
OnClick = PrivateKeyGenerateItemClick
end
object PrivateKeyUploadItem: TMenuItem
Caption = '&Install Public Key into Server...'
OnClick = PrivateKeyUploadItemClick
end
end
end

View File

@@ -257,7 +257,11 @@ __published:
TImageList *ColorImageList120;
TImageList *ColorImageList144;
TImageList *ColorImageList192;
TButton *PrivateKeyGenerateButton;
TButton *PrivateKeyToolsButton;
TPopupMenu *PrivateKeyMenu;
TMenuItem *PrivateKeyGenerateItem;
TMenuItem *PrivateKeyUploadItem;
TButton *PrivateKeyViewButton;
void __fastcall DataChange(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
void __fastcall PageControlChange(TObject *Sender);
@@ -290,7 +294,10 @@ __published:
void __fastcall NoteMemoKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
void __fastcall TlsCertificateFileEditAfterDialog(TObject *Sender, UnicodeString &Name,
bool &Action);
void __fastcall PrivateKeyGenerateButtonClick(TObject *Sender);
void __fastcall PrivateKeyUploadItemClick(TObject *Sender);
void __fastcall PrivateKeyGenerateItemClick(TObject *Sender);
void __fastcall PrivateKeyToolsButtonClick(TObject *Sender);
void __fastcall PrivateKeyViewButtonClick(TObject *Sender);
public:

View File

@@ -60,5 +60,6 @@
#define HELP_EXTENSION_OPTIONS "ui_pref_commands"
#define HELP_CHANGE_PASSWORD "task_change_password"
#define HELP_FILTER "ui_filter"
#define HELP_LOGIN_AUTHORIZED_KEYS "guide_public_key"
#endif // TextsWin

View File

@@ -598,6 +598,13 @@
#define PREFERENCES_DRAGEXT_NOT_INSTALLED 6000
#define PREFERENCES_DRAGEXT_NOT_RUNNING 6001
#define PREFERENCES_DRAGEXT_RUNNING 6002
#define LOGIN_AUTHORIZED_KEYS 6003
#define LOGIN_NOT_OPENSSH 6004
#define LOGIN_PUBLIC_KEY_UPLOAD 6005
#define LOGIN_PUBLIC_KEY_UPLOADED 6006
#define LOGIN_PUBLIC_KEY_PERMISSIONS 6007
#define LOGIN_PUBLIC_KEY_TITLE 6008
#define LOGIN_PUBLIC_KEY_FILTER 6009
// 2xxx is reserved for TextsFileZilla.h

View File

@@ -601,6 +601,13 @@ BEGIN
PREFERENCES_DRAGEXT_NOT_INSTALLED, "Shell extension is not installed."
PREFERENCES_DRAGEXT_NOT_RUNNING, "Shell extension is installed, but is not loaded."
PREFERENCES_DRAGEXT_RUNNING, "Shell extension is installed and loaded."
LOGIN_AUTHORIZED_KEYS, "**Public key for pasting into OpenSSH authorized_keys file:**"
LOGIN_NOT_OPENSSH, "**Install public key to non-OpenSSH server?**\n\nInstalling public key is supported for OpenSSH server only (authorized_keys file).\n\nYour server is %s."
LOGIN_PUBLIC_KEY_UPLOAD, "Installing public key \"%s\"..."
LOGIN_PUBLIC_KEY_UPLOADED, "**Public key \"%s\" was installed.**\n\nYou can now login to the server using the key pair."
LOGIN_PUBLIC_KEY_PERMISSIONS, "Though potentially wrong permissions of \"%s\" file and/or its parent folder were detected. Please check them."
LOGIN_PUBLIC_KEY_TITLE, "Select key to install into server"
LOGIN_PUBLIC_KEY_FILTER, "PuTTY Private Key Files (*.ppk)|*.ppk|All Private Key Files (*.ppk;*.pem;*.key;id_*)|*.ppk;*.pem;*.key;id_*|All Files (*.*)|*.*"
WIN_VARIABLE_STRINGS, "WIN_VARIABLE"
WINSCP_COPYRIGHT, "Copyright © 2000-2018 Martin Prikryl"

View File

@@ -18,6 +18,10 @@
#include <Exceptions.h>
#include <VCLCommon.h>
#include <WinApi.h>
#include <PuttyTools.h>
#include <HelpWin.h>
#include <System.IOUtils.hpp>
#include <StrUtils.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
@@ -72,6 +76,7 @@ __fastcall TTerminalManager::TTerminalManager() :
FMainThread = GetCurrentThreadId();
FChangeSection.reset(new TCriticalSection());
FPendingConfigurationChange = 0;
FKeepAuthenticateForm = false;
FApplicationsEvents.reset(new TApplicationEvents(Application));
FApplicationsEvents->OnException = ApplicationException;
@@ -116,7 +121,7 @@ __fastcall TTerminalManager::~TTerminalManager()
delete FQueues;
delete FTerminationMessages;
delete FTerminalList;
delete FAuthenticateForm;
CloseAutheticateForm();
delete FQueueSection;
ReleaseTaskbarList();
}
@@ -218,7 +223,7 @@ void __fastcall TTerminalManager::FreeActiveTerminal()
}
}
//---------------------------------------------------------------------------
void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool Reopen)
void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool Reopen, bool AdHoc)
{
TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
// it must be managed terminal, unless it is secondary terminal (of managed terminal)
@@ -263,22 +268,21 @@ void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool R
__finally
{
TerminalThread->OnIdle = NULL;
if (!TerminalThread->Release() && (DebugAlwaysTrue(Terminal == FActiveTerminal)))
if (!TerminalThread->Release())
{
// terminal was abandoned, must create a new one to replace it
Terminal = CreateTerminal(new TSessionData(L""));
SetupTerminal(Terminal);
OwnsObjects = false;
Items[ActiveTerminalIndex] = Terminal;
OwnsObjects = true;
FActiveTerminal = Terminal;
if (!AdHoc && (DebugAlwaysTrue(Terminal == FActiveTerminal)))
{
// terminal was abandoned, must create a new one to replace it
Terminal = CreateTerminal(new TSessionData(L""));
SetupTerminal(Terminal);
OwnsObjects = false;
Items[ActiveTerminalIndex] = Terminal;
OwnsObjects = true;
FActiveTerminal = Terminal;
}
// when abandoning cancelled terminal, the form remains open
if (FAuthenticateForm != NULL)
{
delete FAuthenticateForm;
FAuthenticateForm = NULL;
}
CloseAutheticateForm();
}
else
{
@@ -299,6 +303,11 @@ void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool R
}
}
//---------------------------------------------------------------------------
void __fastcall TTerminalManager::CloseAutheticateForm()
{
SAFE_DESTROY(FAuthenticateForm);
}
//---------------------------------------------------------------------------
bool __fastcall TTerminalManager::ConnectTerminal(TTerminal * Terminal)
{
bool Result = true;
@@ -306,7 +315,7 @@ bool __fastcall TTerminalManager::ConnectTerminal(TTerminal * Terminal)
DebugAssert(Terminal != FActiveTerminal);
try
{
DoConnectTerminal(Terminal, false);
DoConnectTerminal(Terminal, false, false);
}
catch (Exception & E)
{
@@ -333,7 +342,7 @@ bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen)
{
DebugAssert(ActiveTerminal);
DoConnectTerminal(ActiveTerminal, Reopen);
DoConnectTerminal(ActiveTerminal, Reopen, false);
if (ScpExplorer)
{
@@ -1124,7 +1133,10 @@ void __fastcall TTerminalManager::TerminalInformation(
BusyEnd(FBusyToken);
FBusyToken = NULL;
}
SAFE_DESTROY(FAuthenticateForm);
if (!FKeepAuthenticateForm)
{
CloseAutheticateForm();
}
}
else
{
@@ -1570,3 +1582,222 @@ TTerminalQueue * __fastcall TTerminalManager::FindQueueForTerminal(TTerminal * T
int Index = IndexOf(Terminal);
return reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
}
//---------------------------------------------------------------------------
TRemoteFile * __fastcall TTerminalManager::CheckRights(
TTerminal * Terminal, const UnicodeString & EntryType, const UnicodeString & FileName, bool & WrongRights)
{
std::unique_ptr<TRemoteFile> FileOwner;
TRemoteFile * File;
try
{
Terminal->LogEvent(FORMAT(L"Checking %s \"%s\"...", (LowerCase(EntryType), FileName)));
Terminal->ReadFile(FileName, File);
FileOwner.reset(File);
int ForbiddenRights = TRights::rfGroupWrite | TRights::rfOtherWrite;
if ((File->Rights->Number & ForbiddenRights) != 0)
{
Terminal->LogEvent(FORMAT(L"%s \"%s\" exists, but has incorrect permissions %s.", (EntryType, FileName, File->Rights->Octal)));
WrongRights = true;
}
else
{
Terminal->LogEvent(FORMAT(L"%s \"%s\" exists and has correct permissions %s.", (EntryType, FileName, File->Rights->Octal)));
}
}
catch (Exception & E)
{
}
return FileOwner.release();
}
//---------------------------------------------------------------------------
bool __fastcall TTerminalManager::UploadPublicKey(
TTerminal * Terminal, TSessionData * Data, UnicodeString & FileName)
{
std::unique_ptr<TOpenDialog> OpenDialog(new TOpenDialog(Application));
OpenDialog->Title = LoadStr(LOGIN_PUBLIC_KEY_TITLE);
OpenDialog->Filter = LoadStr(LOGIN_PUBLIC_KEY_FILTER);
OpenDialog->DefaultExt = PuttyKeyExt;
OpenDialog->FileName = FileName;
bool Result = OpenDialog->Execute();
if (Result)
{
Configuration->Usage->Inc(L"PublicKeyInstallation");
FileName = OpenDialog->FileName;
bool AutoReadDirectory;
bool ExceptionOnFail;
UnicodeString TemporaryDir;
bool WrongRights = false;
const UnicodeString SshFolder = L".ssh";
const UnicodeString AuthorizedKeysFile = L"authorized_keys";
UnicodeString AuthorizedKeysFilePath = FORMAT(L"%s/%s", (SshFolder, AuthorizedKeysFile));
VerifyAndConvertKey(FileName, ssh2only, false);
UnicodeString Comment;
UnicodeString Line = GetPublicKeyLine(FileName, Comment);
bool AdHocTerminal = (Terminal == NULL);
std::unique_ptr<TTerminal> TerminalOwner;
if (AdHocTerminal)
{
DebugAssert(Data != NULL);
TAutoFlag KeepAuthenticateFormFlag(FKeepAuthenticateForm);
try
{
TerminalOwner.reset(CreateTerminal(Data));
Terminal = TerminalOwner.get();
SetupTerminal(Terminal);
Terminal->OnProgress = NULL;
Terminal->OnFinished = NULL;
DoConnectTerminal(Terminal, false, true);
}
catch (Exception & E)
{
CloseAutheticateForm();
throw;
}
}
AutoReadDirectory = Terminal->AutoReadDirectory;
ExceptionOnFail = Terminal->ExceptionOnFail;
try
{
Terminal->AutoReadDirectory = false;
Terminal->ExceptionOnFail = true;
UnicodeString SshImplementation = Terminal->GetSessionInfo().SshImplementation;
UnicodeString NotOpenSSHMessage = FMTLOAD(LOGIN_NOT_OPENSSH, (SshImplementation));
if (IsOpenSSH(SshImplementation) ||
(MessageDialog(NotOpenSSHMessage, qtConfirmation, qaOK | qaCancel, HELP_LOGIN_AUTHORIZED_KEYS) == qaOK))
{
Terminal->Log->AddSeparator();
Terminal->LogEvent(FORMAT(L"Adding public key line to \"%s\" file:\n%s", (AuthorizedKeysFilePath, Line)));
// Ad-hoc terminal
if (FAuthenticateForm != NULL)
{
FAuthenticateForm->Log(FMTLOAD(LOGIN_PUBLIC_KEY_UPLOAD, (Comment)));
}
UnicodeString SshFolderAbsolutePath = UnixIncludeTrailingBackslash(Terminal->GetHomeDirectory()) + SshFolder;
std::unique_ptr<TRemoteFile> SshFolderFile(CheckRights(Terminal, L"Folder", SshFolderAbsolutePath, WrongRights));
if (SshFolderFile.get() == NULL)
{
TRights SshFolderRights;
SshFolderRights.Number = TRights::rfUserRead | TRights::rfUserWrite | TRights::rfUserExec;
TRemoteProperties SshFolderProperties;
SshFolderProperties.Rights = SshFolderRights;
SshFolderProperties.Valid = TValidProperties() << vpRights;
Terminal->LogEvent(FORMAT(L"Trying to create \"%s\" folder with permissions %s...", (SshFolder, SshFolderRights.Octal)));
Terminal->CreateDirectory(SshFolderAbsolutePath, &SshFolderProperties);
}
TemporaryDir = ExcludeTrailingBackslash(WinConfiguration->TemporaryDir());
if (!ForceDirectories(ApiPath(TemporaryDir)))
{
throw EOSExtException(FMTLOAD(CREATE_TEMP_DIR_ERROR, (TemporaryDir)));
}
UnicodeString TemporaryAuthorizedKeysFile = IncludeTrailingBackslash(TemporaryDir) + AuthorizedKeysFile;
UnicodeString AuthorizedKeysFileAbsolutePath = UnixIncludeTrailingBackslash(SshFolderAbsolutePath) + AuthorizedKeysFile;
bool Updated = true;
TCopyParamType CopyParam; // Use factory defaults
CopyParam.ResumeSupport = rsOff; // not to break the permissions
CopyParam.PreserveTime = false; // not needed
UnicodeString AuthorizedKeys;
std::unique_ptr<TRemoteFile> AuthorizedKeysFileFile(CheckRights(Terminal, L"File", AuthorizedKeysFileAbsolutePath, WrongRights));
if (AuthorizedKeysFileFile.get() != NULL)
{
AuthorizedKeysFileFile->FullFileName = AuthorizedKeysFileAbsolutePath;
std::unique_ptr<TStrings> Files(new TStringList());
Files->AddObject(AuthorizedKeysFileAbsolutePath, AuthorizedKeysFileFile.get());
Terminal->LogEvent(FORMAT(L"Downloading current \"%s\" file...", (AuthorizedKeysFile)));
Terminal->CopyToLocal(Files.get(), TemporaryDir, &CopyParam, cpNoConfirmation, NULL);
// Overload with Encoding parameter work incorrectly, when used on a file without BOM
AuthorizedKeys = TFile::ReadAllText(TemporaryAuthorizedKeysFile);
std::unique_ptr<TStrings> AuthorizedKeysLines(TextToStringList(AuthorizedKeys));
int P = Line.Pos(L" ");
if (DebugAlwaysTrue(P > 0))
{
P = PosEx(L" ", Line, P + 1);
}
UnicodeString Prefix = Line.SubString(1, P); // including the space
for (int Index = 0; Index < AuthorizedKeysLines->Count; Index++)
{
if (StartsStr(Prefix, AuthorizedKeysLines->Strings[Index]))
{
Terminal->LogEvent(FORMAT(L"\"%s\" file already contains public key line:\n%s", (AuthorizedKeysFile, AuthorizedKeysLines->Strings[Index])));
Updated = false;
}
}
if (Updated)
{
Terminal->LogEvent(FORMAT(L"\"%s\" file does not contain the public key line yet.", (AuthorizedKeysFile)));
if (!EndsStr(L"\n", AuthorizedKeys))
{
Terminal->LogEvent(FORMAT(L"Adding missing trailing new line to \"%s\" file...", (AuthorizedKeysFile)));
AuthorizedKeys += L"\n";
}
}
}
else
{
Terminal->LogEvent(FORMAT(L"Creating new \"%s\" file...", (AuthorizedKeysFile)));
CopyParam.PreserveRights = true;
CopyParam.Rights.Number = TRights::rfUserRead | TRights::rfUserWrite;
}
if (Updated)
{
AuthorizedKeys += Line + L"\n";
// Overload without Encoding parameter uses TEncoding::UTF8, but does not write BOM, what we want
TFile::WriteAllText(TemporaryAuthorizedKeysFile, AuthorizedKeys);
std::unique_ptr<TStrings> Files(new TStringList());
Files->Add(TemporaryAuthorizedKeysFile);
Terminal->LogEvent(FORMAT(L"Uploading updated \"%s\" file...", (AuthorizedKeysFile)));
Terminal->CopyToRemote(Files.get(), SshFolderAbsolutePath, &CopyParam, cpNoConfirmation, NULL);
}
}
}
__finally
{
Terminal->AutoReadDirectory = AutoReadDirectory;
Terminal->ExceptionOnFail = ExceptionOnFail;
if (!TemporaryDir.IsEmpty())
{
RecursiveDeleteFile(ExcludeTrailingBackslash(TemporaryDir), false);
}
CloseAutheticateForm(); // When uploading from Login dialog
}
Terminal->LogEvent(L"Public key installation done.");
if (AdHocTerminal)
{
TerminalOwner.reset(NULL);
}
else
{
Terminal->Log->AddSeparator();
}
UnicodeString Message = FMTLOAD(LOGIN_PUBLIC_KEY_UPLOADED, (Comment));
if (WrongRights)
{
Message += L"\n\n" + FMTLOAD(LOGIN_PUBLIC_KEY_PERMISSIONS, (AuthorizedKeysFilePath));
}
MessageDialog(Message, qtInformation, qaOK, HELP_LOGIN_AUTHORIZED_KEYS);
}
return Result;
}

View File

@@ -63,7 +63,7 @@ public:
TTerminal * __fastcall FindActiveTerminalForSite(TSessionData * Data);
TTerminalQueue * __fastcall FindQueueForTerminal(TTerminal * Terminal);
void __fastcall UpdateSessionCredentials(TSessionData * Data);
void __fastcall DoConnectTerminal(TTerminal * Terminal, bool Reopen);
bool __fastcall UploadPublicKey(TTerminal * Terminal, TSessionData * Data, UnicodeString & FileName);
__property TCustomScpExplorerForm * ScpExplorer = { read = FScpExplorer, write = SetScpExplorer };
__property TTerminal * ActiveTerminal = { read = FActiveTerminal, write = SetActiveTerminal };
@@ -76,6 +76,7 @@ public:
protected:
virtual TTerminal * __fastcall CreateTerminal(TSessionData * Data);
void __fastcall DoConnectTerminal(TTerminal * Terminal, bool Reopen, bool AdHoc);
private:
static TTerminalManager * FInstance;
@@ -104,6 +105,7 @@ private:
void * FBusyToken;
bool FAuthenticationCancelled;
std::unique_ptr<TApplicationEvents> FApplicationsEvents;
bool FKeepAuthenticateForm;
bool __fastcall ConnectActiveTerminalImpl(bool Reopen);
bool __fastcall ConnectActiveTerminal();
@@ -166,6 +168,9 @@ private:
void __fastcall DoConfigurationChange();
bool __fastcall ShouldDisplayQueueStatusOnAppTitle();
void __fastcall SetupTerminal(TTerminal * Terminal);
void __fastcall CloseAutheticateForm();
TRemoteFile * __fastcall CheckRights(
TTerminal * Terminal, const UnicodeString & EntryType, const UnicodeString & FileName, bool & WrongRights);
};
//---------------------------------------------------------------------------
#endif

View File

@@ -1162,7 +1162,7 @@ static void __fastcall ConvertKey(UnicodeString & FileName, TKeyType Type)
}
//---------------------------------------------------------------------------
static void __fastcall DoVerifyKey(
UnicodeString & FileName, TSshProt SshProt, bool Convert)
UnicodeString & FileName, TSshProt SshProt, bool Convert, bool CanIgnore)
{
if (!FileName.Trim().IsEmpty())
{
@@ -1242,8 +1242,8 @@ static void __fastcall DoVerifyKey(
if (!Message.IsEmpty())
{
Configuration->Usage->Inc(L"PrivateKeySelectErrors");
if (MoreMessageDialog(Message, MoreMessages.get(), qtWarning, qaIgnore | qaAbort,
HelpKeyword) == qaAbort)
unsigned int Answers = (CanIgnore ? (qaIgnore | qaAbort) : qaOK);
if (MoreMessageDialog(Message, MoreMessages.get(), qtWarning, Answers, HelpKeyword) != qaIgnore)
{
Abort();
}
@@ -1251,14 +1251,14 @@ static void __fastcall DoVerifyKey(
}
}
//---------------------------------------------------------------------------
void __fastcall VerifyAndConvertKey(UnicodeString & FileName, TSshProt SshProt)
void __fastcall VerifyAndConvertKey(UnicodeString & FileName, TSshProt SshProt, bool CanIgnore)
{
DoVerifyKey(FileName, SshProt, true);
DoVerifyKey(FileName, SshProt, true, CanIgnore);
}
//---------------------------------------------------------------------------
void __fastcall VerifyKey(UnicodeString FileName, TSshProt SshProt)
{
DoVerifyKey(FileName, SshProt, false);
DoVerifyKey(FileName, SshProt, false, true);
}
//---------------------------------------------------------------------------
void __fastcall VerifyCertificate(const UnicodeString & FileName)

View File

@@ -68,7 +68,7 @@ void __fastcall CopyToClipboard(TStrings * Strings);
void __fastcall ShutDownWindows();
void __fastcall SuspendWindows();
void __fastcall EditSelectBaseName(HWND Edit);
void __fastcall VerifyAndConvertKey(UnicodeString & FileName, TSshProt SshProt);
void __fastcall VerifyAndConvertKey(UnicodeString & FileName, TSshProt SshProt, bool CanIgnore);
void __fastcall VerifyKey(UnicodeString FileName, TSshProt SshProt);
void __fastcall VerifyCertificate(const UnicodeString & FileName);
TStrings * __fastcall GetUnwrappedMemoLines(TMemo * Memo);