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

Change: Session.ScanFingerprint has mandatory algorithm parameter. + Displaying both SHA-256 and MD5 fingerprint of SSH host key on Server and protocol information dialog

Source commit: 11b4510273b16007335d41eefede396e3ebf0d0f
This commit is contained in:
Martin Prikryl
2018-01-03 13:40:06 +00:00
parent cbe7d544f3
commit 9d0b0e3fc6
12 changed files with 241 additions and 43 deletions

View File

@@ -336,10 +336,16 @@ namespace WinSCP
return s;
}
public string ScanFingerprint(SessionOptions sessionOptions)
public string ScanFingerprint(SessionOptions sessionOptions, string algorithm)
{
using (Logger.CreateCallstackAndLock())
{
string normalizeAlgorithm = NormalizeIdent(algorithm);
if (string.IsNullOrEmpty(normalizeAlgorithm))
{
throw Logger.WriteException(new ArgumentException("Algorithm cannot be empty", "algorithm"));
}
string result;
CheckNotDisposed();
@@ -372,14 +378,32 @@ namespace WinSCP
CheckForTimeout();
}
string output = string.Join(Environment.NewLine, new List<string>(Output).ToArray());
if (_process.ExitCode == 0)
{
result = output;
result = null;
foreach (string s in Output)
{
int p = s.IndexOf(":", StringComparison.Ordinal);
if (p < 0)
{
throw Logger.WriteException(new SessionLocalException(this, string.Format(CultureInfo.CurrentCulture, "Unexpected fingerprint scan result line '{0}'", s)));
}
string a = NormalizeIdent(s.Substring(0, p).Trim());
if (normalizeAlgorithm.Equals(a, StringComparison.OrdinalIgnoreCase))
{
result = s.Substring(p + 1).Trim();
break;
}
}
if (result == null)
{
throw Logger.WriteException(new SessionLocalException(this, string.Format(CultureInfo.CurrentCulture, "Fingerprint for algorithm {0} not supported", algorithm)));
}
}
else
{
throw Logger.WriteException(new SessionRemoteException(this, output));
throw Logger.WriteException(new SessionRemoteException(this, ListToString(Output)));
}
}
catch (Exception e)
@@ -396,6 +420,11 @@ namespace WinSCP
}
}
private static string NormalizeIdent(string algorithm)
{
return algorithm.Replace("-", string.Empty);
}
public void Close()
{
using (Logger.CreateCallstackAndLock())

View File

@@ -1123,7 +1123,7 @@ __fastcall TCustomCommandData::TCustomCommandData()
__fastcall TCustomCommandData::TCustomCommandData(TTerminal * Terminal)
{
Init(Terminal->SessionData, Terminal->UserName, Terminal->Password,
Terminal->GetSessionInfo().HostKeyFingerprint);
Terminal->GetSessionInfo().HostKeyFingerprintSHA256);
}
//---------------------------------------------------------------------------
__fastcall TCustomCommandData::TCustomCommandData(

View File

@@ -125,9 +125,10 @@ const TSessionInfo & __fastcall TSecureShell::GetSessionInfo()
return FSessionInfo;
}
//---------------------------------------------------------------------
UnicodeString __fastcall TSecureShell::GetHostKeyFingerprint()
void __fastcall TSecureShell::GetHostKeyFingerprint(UnicodeString & SHA256, UnicodeString & MD5)
{
return FSessionInfo.HostKeyFingerprint;
SHA256 = FSessionInfo.HostKeyFingerprintSHA256;
MD5 = FSessionInfo.HostKeyFingerprintMD5;
}
//---------------------------------------------------------------------
Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
@@ -2241,7 +2242,8 @@ void __fastcall TSecureShell::VerifyHostKey(
UnicodeString NormalizedFingerprintMD5 = NormalizeFingerprint(FingerprintMD5);
UnicodeString NormalizedFingerprintSHA256 = NormalizeFingerprint(FingerprintSHA256);
FSessionInfo.HostKeyFingerprint = FingerprintSHA256;
FSessionInfo.HostKeyFingerprintSHA256 = FingerprintSHA256;
FSessionInfo.HostKeyFingerprintMD5 = FingerprintMD5;
if (FSessionData->FingerprintScan)
{

View File

@@ -133,7 +133,7 @@ public:
void __fastcall SendNull();
const TSessionInfo & __fastcall GetSessionInfo();
UnicodeString __fastcall GetHostKeyFingerprint();
void __fastcall GetHostKeyFingerprint(UnicodeString & SHA256, UnicodeString & MD5);
bool __fastcall SshFallbackCmd() const;
unsigned long __fastcall MaxPacketSize();
void __fastcall ClearStdError();

View File

@@ -23,7 +23,8 @@ struct TSessionInfo
UnicodeString SshVersionString;
UnicodeString SshImplementation;
UnicodeString HostKeyFingerprint;
UnicodeString HostKeyFingerprintSHA256;
UnicodeString HostKeyFingerprintMD5;
UnicodeString CertificateFingerprint;
UnicodeString Certificate;

View File

@@ -1228,25 +1228,28 @@ void __fastcall TTerminal::ResetConnection()
// as they can still be referenced in the GUI atm
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TTerminal::FingerprintScan()
void __fastcall TTerminal::FingerprintScan(UnicodeString & SHA256, UnicodeString & MD5)
{
SessionData->FingerprintScan = true;
try
{
Open();
// we should never get here
DebugFail();
Abort();
}
catch (...)
{
if (!FFingerprintScanned.IsEmpty())
if (!FFingerprintScannedSHA256.IsEmpty() || !FFingerprintScannedMD5.IsEmpty())
{
return FFingerprintScanned;
SHA256 = FFingerprintScannedSHA256;
MD5 = FFingerprintScannedMD5;
}
else
{
throw;
}
throw;
}
DebugFail();
return UnicodeString();
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::Open()
@@ -1334,7 +1337,7 @@ void __fastcall TTerminal::Open()
DebugAssert(!FSecureShell->Active);
if (SessionData->FingerprintScan)
{
FFingerprintScanned = FSecureShell->GetHostKeyFingerprint();
FSecureShell->GetHostKeyFingerprint(FFingerprintScannedSHA256, FFingerprintScannedMD5);
}
if (!FSecureShell->Active && !FTunnelError.IsEmpty())
{
@@ -1420,7 +1423,8 @@ void __fastcall TTerminal::Open()
if (SessionData->FingerprintScan && (FFileSystem != NULL) &&
DebugAlwaysTrue(SessionData->Ftps != ftpsNone))
{
FFingerprintScanned = FFileSystem->GetSessionInfo().CertificateFingerprint;
FFingerprintScannedSHA256 = UnicodeString();
FFingerprintScannedMD5 = FFileSystem->GetSessionInfo().CertificateFingerprint;
}
// Particularly to prevent reusing a wrong client certificate passphrase
// in the next login attempt
@@ -4755,7 +4759,7 @@ TTerminal * __fastcall TTerminal::CreateSecondarySession(const UnicodeString & N
void __fastcall TTerminal::FillSessionDataForCode(TSessionData * Data)
{
const TSessionInfo & SessionInfo = GetSessionInfo();
Data->HostKey = SessionInfo.HostKeyFingerprint;
Data->HostKey = SessionInfo.HostKeyFingerprintSHA256;
}
//---------------------------------------------------------------------------
TTerminal * __fastcall TTerminal::GetCommandSession()

View File

@@ -210,7 +210,8 @@ private:
bool FRememberedPasswordTried;
bool FRememberedTunnelPasswordTried;
int FNesting;
UnicodeString FFingerprintScanned;
UnicodeString FFingerprintScannedSHA256;
UnicodeString FFingerprintScannedMD5;
DWORD FLastProgressLogged;
UnicodeString FDestFileName;
bool FMultipleDestinationFiles;
@@ -456,7 +457,7 @@ public:
__fastcall ~TTerminal();
void __fastcall Open();
void __fastcall Close();
UnicodeString __fastcall FingerprintScan();
void __fastcall FingerprintScan(UnicodeString & SHA256, UnicodeString & MD5);
void __fastcall Reopen(int Params);
virtual void __fastcall DirectoryModified(const UnicodeString Path, bool SubDirs);
virtual void __fastcall DirectoryLoaded(TRemoteFileList * FileList);

View File

@@ -11,6 +11,7 @@
#include "TextsWin.h"
#include "GUITools.h"
#include <BaseUtils.hpp>
#include <StrUtils.hpp>
//---------------------------------------------------------------------
#pragma link "HistoryComboBox"
#ifndef NO_RESOURCES
@@ -44,7 +45,9 @@ __fastcall TFileSystemInfoDialog::TFileSystemInfoDialog(TComponent * AOwner,
CertificateGroup->Top = HostKeyGroup->Top;
ReadOnlyControl(HostKeyFingerprintEdit);
ReadOnlyControl(HostKeyAlgorithmEdit);
ReadOnlyControl(HostKeyFingerprintSHA256Edit);
ReadOnlyControl(HostKeyFingerprintMD5Edit);
ReadOnlyControl(CertificateFingerprintEdit);
ReadOnlyControl(InfoMemo);
}
@@ -110,7 +113,8 @@ void __fastcall TFileSystemInfoDialog::Feed(TFeedFileSystemData AddItem)
}
AddItem(ServerView, FSINFO_COMPRESSION, Str);
AddItem(HostKeyFingerprintEdit, 0, FSessionInfo.HostKeyFingerprint);
AddItem(HostKeyFingerprintSHA256Edit, 0, FSessionInfo.HostKeyFingerprintSHA256);
AddItem(HostKeyFingerprintMD5Edit, 0, FSessionInfo.HostKeyFingerprintMD5);
AddItem(CertificateFingerprintEdit, 0, FSessionInfo.CertificateFingerprint);
AddItem(ProtocolView, FSINFO_MODE_CHANGING, CapabilityStr(fcModeChanging));
@@ -153,11 +157,14 @@ void __fastcall TFileSystemInfoDialog::ControlsAddItem(TControl * Control,
FLastListItem = 0;
}
if (Control == HostKeyFingerprintEdit)
if ((Control == HostKeyFingerprintSHA256Edit) || (Control == HostKeyFingerprintMD5Edit))
{
EnableControl(HostKeyGroup, !Value.IsEmpty());
HostKeyGroup->Visible = !Value.IsEmpty();
HostKeyFingerprintEdit->Text = Value;
UnicodeString Alg1 = CutToChar(Value, L' ', true);
UnicodeString Alg2 = CutToChar(Value, L' ', true);
HostKeyAlgorithmEdit->Text = FORMAT(L"%s %s", (Alg1, Alg2));
DebugNotNull(dynamic_cast<TEdit *>(Control))->Text = Value;
}
else if (Control == CertificateFingerprintEdit)
{
@@ -228,24 +235,43 @@ void __fastcall TFileSystemInfoDialog::ClipboardAddItem(TControl * Control,
{
if (Control->Enabled && !Value.IsEmpty())
{
if (FLastFeededControl != Control)
TGroupBox * Group = dynamic_cast<TGroupBox *>(Control->Parent);
TControl * ControlGroup = (Group != NULL) ? Group : Control;
if (FLastFeededControl != ControlGroup)
{
if (FLastFeededControl != NULL)
{
FClipboard += UnicodeString::StringOfChar(L'-', 60) + L"\r\n";
}
FLastFeededControl = Control;
if (Group != NULL)
{
FClipboard += FORMAT(L"%s\r\n", (Group->Caption));
}
FLastFeededControl = ControlGroup;
}
if (dynamic_cast<TListView *>(Control) == NULL)
{
TGroupBox * Group = dynamic_cast<TGroupBox *>(Control->Parent);
DebugAssert(Group != NULL);
if ((Value.Length() >= 2) && (Value.SubString(Value.Length() - 1, 2) == L"\r\n"))
for (int Index = 0; Index < Control->Parent->ControlCount; Index++)
{
TLabel * Label = dynamic_cast<TLabel *>(Control->Parent->Controls[Index]);
if ((Label != NULL) && (Label->FocusControl == Control))
{
UnicodeString S = StripHotkey(Label->Caption);
if (EndsStr(L":", S))
{
S.SetLength(S.Length() - 1);
}
FClipboard += FORMAT(L"%s = ", (S));
}
}
if (EndsStr(L"\r\n", Value))
{
Value.SetLength(Value.Length() - 2);
}
FClipboard += FORMAT(L"%s\r\n%s\r\n", (Group->Caption, Value));
FClipboard += FORMAT(L"%s\r\n", (Value));
}
else
{
@@ -383,3 +409,28 @@ void __fastcall TFileSystemInfoDialog::SpaceAvailableViewCustomDrawItem(
}
}
//---------------------------------------------------------------------------
void __fastcall TFileSystemInfoDialog::EditCopyActionExecute(TObject * /*Sender*/)
{
TEdit * Edit = HostKeyFingerprintSHA256Edit->Focused() ? HostKeyFingerprintSHA256Edit : HostKeyFingerprintMD5Edit;
if ((Edit->SelLength == 0) || (Edit->SelLength == Edit->Text.Length()))
{
CopyToClipboard(FORMAT(L"%s %s", (HostKeyAlgorithmEdit->Text, Edit->Text)));
}
else
{
Edit->CopyToClipboard();
}
}
//---------------------------------------------------------------------------
void __fastcall TFileSystemInfoDialog::HostKeyFingerprintSHA256EditContextPopup(
TObject * Sender, TPoint & MousePos, bool & Handled)
{
MenuPopup(Sender, MousePos, Handled);
}
//---------------------------------------------------------------------------
void __fastcall TFileSystemInfoDialog::EditCopyActionUpdate(TObject * /*Sender*/)
{
// When noting is selected, we copy whole key, including algorithm, see EditCopyActionExecute
EditCopyAction->Enabled = true;
}
//---------------------------------------------------------------------------

View File

@@ -57,19 +57,58 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
324)
object HostKeyGroup: TGroupBox
Left = 6
Top = 202
Top = 201
Width = 351
Height = 41
Height = 87
Anchors = [akLeft, akRight, akBottom]
Caption = 'Server host key fingerprint'
Caption = 'Server host key fingerprints'
TabOrder = 1
DesignSize = (
351
41)
object HostKeyFingerprintEdit: TEdit
Left = 10
87)
object Label2: TLabel
Left = 7
Top = 18
Width = 334
Width = 49
Height = 13
Caption = 'Algorithm:'
FocusControl = HostKeyAlgorithmEdit
end
object Label3: TLabel
Left = 7
Top = 41
Width = 46
Height = 13
Caption = 'SHA-256:'
FocusControl = HostKeyFingerprintSHA256Edit
end
object Label4: TLabel
Left = 7
Top = 64
Width = 25
Height = 13
Caption = 'MD5:'
FocusControl = HostKeyFingerprintMD5Edit
end
object HostKeyFingerprintSHA256Edit: TEdit
Left = 62
Top = 41
Width = 282
Height = 17
TabStop = False
Anchors = [akLeft, akTop, akRight]
BorderStyle = bsNone
Color = clBtnFace
PopupMenu = FingerprintPopupMenu
ReadOnly = True
TabOrder = 1
Text = 'HostKeyFingerprintSHA256Edit'
OnContextPopup = HostKeyFingerprintSHA256EditContextPopup
end
object HostKeyAlgorithmEdit: TEdit
Left = 62
Top = 18
Width = 282
Height = 17
TabStop = False
Anchors = [akLeft, akTop, akRight]
@@ -77,7 +116,22 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
Color = clBtnFace
ReadOnly = True
TabOrder = 0
Text = 'HostKeyFingerprintEdit'
Text = 'HostKeyAlgorithmEdit'
end
object HostKeyFingerprintMD5Edit: TEdit
Left = 62
Top = 64
Width = 282
Height = 17
TabStop = False
Anchors = [akLeft, akTop, akRight]
BorderStyle = bsNone
Color = clBtnFace
PopupMenu = FingerprintPopupMenu
ReadOnly = True
TabOrder = 2
Text = 'HostKeyFingerprintMD5Edit'
OnContextPopup = HostKeyFingerprintSHA256EditContextPopup
end
end
object ServerView: TListView
@@ -108,7 +162,7 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
end
object CertificateGroup: TGroupBox
Left = 6
Top = 246
Top = 294
Width = 351
Height = 72
Anchors = [akLeft, akRight, akBottom]
@@ -286,4 +340,30 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
OnClick = CopyClick
end
end
object FingerprintPopupMenu: TPopupMenu
Left = 240
Top = 354
object Copy1: TMenuItem
Action = EditCopyAction
end
object TMenuItem
Action = EditSelectAllAction
end
end
object FingerprintActionList: TActionList
Left = 328
Top = 354
object EditCopyAction: TEditCopy
Category = 'Edit'
Caption = '&Copy'
ShortCut = 16451
OnExecute = EditCopyActionExecute
OnUpdate = EditCopyActionUpdate
end
object EditSelectAllAction: TEditSelectAll
Category = 'Edit'
Caption = 'Select &All'
ShortCut = 16449
end
end
end

View File

@@ -7,6 +7,9 @@
#include <Vcl.Controls.hpp>
#include <Vcl.Menus.hpp>
#include <Vcl.StdCtrls.hpp>
#include <System.Actions.hpp>
#include <Vcl.ActnList.hpp>
#include <Vcl.StdActns.hpp>
//----------------------------------------------------------------------------
typedef void __fastcall (__closure *TFeedFileSystemData)
(TControl * Control, int Label, UnicodeString Value);
@@ -19,7 +22,7 @@ __published:
TPageControl *PageControl;
TTabSheet *ProtocolSheet;
TGroupBox *HostKeyGroup;
TEdit *HostKeyFingerprintEdit;
TEdit *HostKeyFingerprintSHA256Edit;
TTabSheet *CapabilitiesSheet;
TGroupBox *InfoGroup;
TMemo *InfoMemo;
@@ -36,6 +39,16 @@ __published:
TGroupBox *CertificateGroup;
TEdit *CertificateFingerprintEdit;
TButton *CertificateViewButton;
TLabel *Label2;
TEdit *HostKeyAlgorithmEdit;
TLabel *Label3;
TLabel *Label4;
TEdit *HostKeyFingerprintMD5Edit;
TPopupMenu *FingerprintPopupMenu;
TActionList *FingerprintActionList;
TEditCopy *EditCopyAction;
TEditSelectAll *EditSelectAllAction;
TMenuItem *Copy1;
void __fastcall HelpButtonClick(TObject *Sender);
void __fastcall ClipboardButtonClick(TObject *Sender);
void __fastcall CopyClick(TObject *Sender);
@@ -50,6 +63,9 @@ __published:
void __fastcall CertificateViewButtonClick(TObject *Sender);
void __fastcall SpaceAvailableViewCustomDrawItem(TCustomListView *Sender, TListItem *Item,
TCustomDrawState State, bool &DefaultDraw);
void __fastcall EditCopyActionExecute(TObject *Sender);
void __fastcall HostKeyFingerprintSHA256EditContextPopup(TObject *Sender, TPoint &MousePos, bool &Handled);
void __fastcall EditCopyActionUpdate(TObject *Sender);
public:
virtual __fastcall TFileSystemInfoDialog(TComponent * AOwner,
TGetSpaceAvailable OnGetSpaceAvailable);

View File

@@ -2506,7 +2506,17 @@ int __fastcall FingerprintScan(TConsole * Console, TProgramParams * Params)
}
std::unique_ptr<TTerminal> Terminal(new TTerminal(SessionData.get(), Configuration));
ConsolePrintLine(Console, Terminal->FingerprintScan());
UnicodeString SHA256;
UnicodeString MD5;
Terminal->FingerprintScan(SHA256, MD5);
if (!SHA256.IsEmpty())
{
ConsolePrintLine(Console, FORMAT(L"SHA-256: %s", (SHA256)));
}
if (!MD5.IsEmpty())
{
ConsolePrintLine(Console, FORMAT(L"MD5: %s", (MD5)));
}
}
catch (Exception & E)
{

View File

@@ -356,7 +356,11 @@ void __fastcall ReadOnlyControl(TControl * Control, bool ReadOnly)
Edit->Color = clWindow;
// not supported atm, we need to persist previous value of WantReturns
DebugAssert(Memo == NULL);
delete Edit->PopupMenu;
if ((Edit->PopupMenu != NULL) && (Edit->PopupMenu->Owner == Edit))
{
delete Edit->PopupMenu;
}
}
}
else if ((dynamic_cast<TCustomComboBox *>(Control) != NULL) ||