ADD: SSH - verify server fingerprint

UPD: FTP - use standard dialog for quick connection
This commit is contained in:
Alexander Koblov 2018-05-18 20:41:52 +00:00
commit fd85eb22bc
3 changed files with 111 additions and 109 deletions

View file

@ -71,10 +71,8 @@ procedure EnableControls(pDlg: PtrUInt);
begin
with gStartupInfo do
begin
SendDlgMsg(pDlg, 'chkOnlySCP', DM_ENABLE, PtrInt(gConnection.OpenSSH), 0);
SendDlgMsg(pDlg, 'chkShowHidden', DM_ENABLE, PtrInt(not gConnection.OpenSSH), 0);
SendDlgMsg(pDlg, 'chkPassiveMode', DM_ENABLE, PtrInt(not gConnection.OpenSSH), 0);
SendDlgMsg(pDlg, 'chkKeepAliveTransfer', DM_ENABLE, PtrInt(not gConnection.OpenSSH), 0);
SendDlgMsg(pDlg, 'gbSSH', DM_ENABLE, PtrInt(gConnection.OpenSSH), 0);
SendDlgMsg(pDlg, 'gbFTP', DM_ENABLE, PtrInt(not gConnection.OpenSSH), 0);
if not gConnection.OpenSSH then
SendDlgMsg(pDlg, 'chkOnlySCP', DM_SETCHECK, 0, 0)
else begin
@ -293,6 +291,12 @@ begin
Data:= PtrInt(PAnsiChar(gConnection.PrivateKey));
SendDlgMsg(pDlg, 'fnePrivateKey', DM_SETTEXT, Data, 0);
if SameText(gConnection.ConnectionName, cQuickConnection) then
begin
SendDlgMsg(pDlg, 'edtName', DM_ENABLE, 0, 0);
SendDlgMsg(pDlg, 'chkMasterPassword', DM_SHOWITEM, 0, 0);
end;
EnableControls(pDlg);
LoadProxyList(pDlg);

View file

@ -31,6 +31,10 @@ uses
SysUtils, Classes,
WfxPlugin, Extension;
const
cAddConnection = '<Add connection>';
cQuickConnection = '<Quick connection>';
type
{ TConnection }
@ -50,6 +54,7 @@ type
OpenSSH: Boolean;
UseAllocate: Boolean;
Encoding: AnsiString;
Fingerprint: AnsiString;
InitCommands: AnsiString;
ShowHiddenItems: Boolean;
PasswordChanged: Boolean;
@ -123,15 +128,12 @@ var
TcpKeepAlive: Boolean = True;
ActiveConnectionList, ConnectionList: TStringList;
IniFile: TIniFile;
HasDialogAPI: Boolean = False;
ListLock: TCriticalSection;
threadvar
ThreadCon: TFtpSendEx;
const
cAddConnection = '<Add connection>';
cQuickConnection = '<Quick connection>';
FS_COPYFLAGS_FORCE = FS_COPYFLAGS_OVERWRITE or FS_COPYFLAGS_RESUME;
RootList: array [0 .. 1] of AnsiString = (cAddConnection, cQuickConnection);
@ -175,6 +177,7 @@ begin
Connection.UseAllocate:= IniFile.ReadBool('FTP', 'Connection' + sIndex + 'UseAllocate', False);
Connection.PublicKey := IniFile.ReadString('FTP', 'Connection' + sIndex + 'PublicKey', EmptyStr);
Connection.PrivateKey := IniFile.ReadString('FTP', 'Connection' + sIndex + 'PrivateKey', EmptyStr);
Connection.Fingerprint:= IniFile.ReadString('FTP', 'Connection' + sIndex + 'Fingerprint', EmptyStr);
Connection.InitCommands := IniFile.ReadString('FTP', 'Connection' + sIndex + 'InitCommands', EmptyStr);
Connection.ShowHiddenItems := IniFile.ReadBool('FTP', 'Connection' + sIndex + 'ShowHiddenItems', True);
Connection.KeepAliveTransfer := IniFile.ReadBool('FTP', 'Connection' + sIndex + 'KeepAliveTransfer', False);
@ -217,6 +220,7 @@ begin
IniFile.WriteBool('FTP', 'Connection' + sIndex + 'UseAllocate', Connection.UseAllocate);
IniFile.WriteString('FTP', 'Connection' + sIndex + 'PublicKey', Connection.PublicKey);
IniFile.WriteString('FTP', 'Connection' + sIndex + 'PrivateKey', Connection.PrivateKey);
IniFile.WriteString('FTP', 'Connection' + sIndex + 'Fingerprint', Connection.Fingerprint);
IniFile.WriteString('FTP', 'Connection' + sIndex + 'InitCommands', Connection.InitCommands);
IniFile.WriteBool('FTP', 'Connection' + sIndex + 'ShowHiddenItems', Connection.ShowHiddenItems);
IniFile.WriteBool('FTP', 'Connection' + sIndex + 'KeepAliveTransfer', Connection.KeepAliveTransfer);
@ -333,6 +337,7 @@ begin
end;
FtpSend.PublicKey:= Connection.PublicKey;
FtpSend.PrivateKey:= Connection.PrivateKey;
TScpSend(FtpSend).Fingerprint:= Connection.Fingerprint;
end
else begin
FtpSend := TFTPSendEx.Create(Connection.Encoding);
@ -371,6 +376,15 @@ begin
begin
LogProc(PluginNumber, MSGTYPE_CONNECT, PWideChar('CONNECT ' + PathDelim + UTF8Decode(ConnectionName)));
ActiveConnectionList.AddObject(ConnectionName, FtpSend);
if Connection.OpenSSH and (ConnectionName <> cQuickConnection) then
begin
// Save connection server fingerprint
if Connection.Fingerprint <> TScpSend(FtpSend).Fingerprint then
begin
Connection.Fingerprint:= TScpSend(FtpSend).Fingerprint;
WriteConnectionList;
end;
end;
Result:= True;
end
else
@ -383,29 +397,6 @@ begin
end;
end;
function AddQuickConnection(const Connection: TConnection): Boolean;
var
Text: PWideChar;
Temp: UnicodeString;
begin
Result:= False;
SetLength(Temp, MAX_PATH + 1);
Text:= PWideChar(Temp); Text[0]:= #0;
if RequestProc(PluginNumber, RT_URL, nil, nil, Text, MAX_PATH) then
begin
Connection.Host := Text; Text[0]:= #0;
if RequestProc(PluginNumber, RT_TargetDir, nil, nil, Text, MAX_PATH) then
begin
Connection.Path := Text; Text[0]:= #0;
if RequestProc(PluginNumber, RT_UserName, nil, nil, Text, MAX_PATH) then
begin
Connection.UserName := Text;
Result:= True;
end;
end;
end;
end;
function QuickConnection: Boolean;
var
Index: Integer;
@ -416,16 +407,13 @@ begin
if not Result then
begin
Connection := TConnection.Create;
if AddQuickConnection(Connection) then
Connection.ConnectionName:= cQuickConnection;
if ShowFtpConfDlg(Connection) then
begin
if ShowPasswordDialog(Connection.Password) then
begin
Connection.PassiveMode:= True;
Connection.ConnectionName:= cQuickConnection;
Index:= ConnectionList.AddObject(Connection.ConnectionName, Connection);
Result:= FtpConnect(Connection.ConnectionName, FtpSend);
ConnectionList.Delete(Index);
end;
Connection.ConnectionName:= cQuickConnection;
Index:= ConnectionList.AddObject(Connection.ConnectionName, Connection);
Result:= FtpConnect(Connection.ConnectionName, FtpSend);
ConnectionList.Delete(Index);
end;
Connection.Free;
end;
@ -440,37 +428,23 @@ begin
Connection := TConnection.Create;
Connection.PassiveMode := True;
if HasDialogAPI then
if ShowFtpConfDlg(Connection) then
begin
if ShowFtpConfDlg(Connection) then
with Connection do
begin
with Connection do
begin
if ConnectionList.IndexOf(ConnectionName) >= 0 then begin
ConnectionName += '+' + IntToStr(Random(MaxInt));
end;
if MasterPassword then
begin
if Length(Password) = 0 then
MasterPassword:= False
else if CryptFunc(FS_CRYPT_SAVE_PASSWORD, ConnectionName, Password) = FS_FILE_OK then
Password:= EmptyStr
else
MasterPassword:= False;
end;
Result:= ConnectionList.AddObject(ConnectionName, Connection);
if ConnectionList.IndexOf(ConnectionName) >= 0 then begin
ConnectionName += '+' + IntToStr(Random(MaxInt));
end;
end;
end
else begin
SetLength(Temp, MAX_PATH + 1); Temp[1]:= #0;
if RequestProc(PluginNumber, RT_Other, nil, nil, PWideChar(Temp), MAX_PATH) then
begin
Connection.ConnectionName := PAnsiChar(Temp);
if AddQuickConnection(Connection) then
if MasterPassword then
begin
Result:= ConnectionList.AddObject(Connection.ConnectionName, Connection);
if Length(Password) = 0 then
MasterPassword:= False
else if CryptFunc(FS_CRYPT_SAVE_PASSWORD, ConnectionName, Password) = FS_FILE_OK then
Password:= EmptyStr
else
MasterPassword:= False;
end;
Result:= ConnectionList.AddObject(ConnectionName, Connection);
end;
end;
@ -487,55 +461,52 @@ var
Connection: TConnection;
begin
Result:= False;
if HasDialogAPI then
I := ConnectionList.IndexOf(ConnectionName);
if I >= 0 then
begin
I := ConnectionList.IndexOf(ConnectionName);
if I >= 0 then
ATemp:= TConnection.Create;
Connection:= TConnection(ConnectionList.Objects[I]);
ATemp.Assign(Connection);
if ShowFtpConfDlg(ATemp) then
begin
ATemp:= TConnection.Create;
Connection:= TConnection(ConnectionList.Objects[I]);
ATemp.Assign(Connection);
if ShowFtpConfDlg(ATemp) then
with ATemp do
begin
with ATemp do
if ConnectionName <> Connection.ConnectionName then
begin
if ConnectionName <> Connection.ConnectionName then
if Connection.MasterPassword then
begin
if Connection.MasterPassword then
if CryptFunc(FS_CRYPT_MOVE_PASSWORD, Connection.ConnectionName, ConnectionName) <> FS_FILE_OK then
begin
if CryptFunc(FS_CRYPT_MOVE_PASSWORD, Connection.ConnectionName, ConnectionName) <> FS_FILE_OK then
begin
gStartupInfo.MessageBox('Cannot save connection!', 'FTP', MB_OK or MB_ICONERROR);
Exit(False);
end;
gStartupInfo.MessageBox('Cannot save connection!', 'FTP', MB_OK or MB_ICONERROR);
Exit(False);
end;
ConnectionList[I]:= ConnectionName
end;
if PasswordChanged then
begin
// Master password enabled
if MasterPassword then
begin
if Length(Password) = 0 then
MasterPassword:= False
else if CryptFunc(FS_CRYPT_SAVE_PASSWORD, ConnectionName, Password) = FS_FILE_OK then
Password:= EmptyStr
else
MasterPassword:= False;
end;
// Master password disabled
if (MasterPassword = False) and (Connection.MasterPassword <> MasterPassword) then
begin
DeletePassword(ConnectionName);
end;
end
ConnectionList[I]:= ConnectionName
end;
Connection.Assign(ATemp);
WriteConnectionList;
Result:= True;
if PasswordChanged then
begin
// Master password enabled
if MasterPassword then
begin
if Length(Password) = 0 then
MasterPassword:= False
else if CryptFunc(FS_CRYPT_SAVE_PASSWORD, ConnectionName, Password) = FS_FILE_OK then
Password:= EmptyStr
else
MasterPassword:= False;
end;
// Master password disabled
if (MasterPassword = False) and (Connection.MasterPassword <> MasterPassword) then
begin
DeletePassword(ConnectionName);
end;
end
end;
FreeAndNil(ATemp);
Connection.Assign(ATemp);
WriteConnectionList;
Result:= True;
end;
FreeAndNil(ATemp);
end;
end;
@ -1101,8 +1072,6 @@ begin
TcpKeepAlive := IniFile.ReadBool('General', 'TcpKeepAlive', TcpKeepAlive);
ReadConnectionList;
HasDialogAPI:= True;
end;
function ReadPassword(ConnectionName: AnsiString): String;
@ -1137,6 +1106,7 @@ begin
Encoding:= Connection.Encoding;
PublicKey:= Connection.PublicKey;
PrivateKey:= Connection.PrivateKey;
Fingerprint:= Connection.Fingerprint;
PassiveMode:= Connection.PassiveMode;
UseAllocate:= Connection.UseAllocate;
InitCommands:= Connection.InitCommands;

View file

@ -50,6 +50,7 @@ type
FCurrentDir: String;
FLastError: Integer;
FSavedPassword: Boolean;
FFingerprint: AnsiString;
FSession: PLIBSSH2_SESSION;
SourceName, TargetName: PWideChar;
procedure DoProgress(Percent: Int64);
@ -62,6 +63,7 @@ type
function Login: Boolean; override;
function Logout: Boolean; override;
function GetCurrentDir: String; override;
procedure CloneTo(AValue: TFTPSendEx); override;
function FileSize(const FileName: String): Int64; override;
function FileExists(const FileName: String): Boolean; override;
function CreateDir(const Directory: string): Boolean; override;
@ -78,6 +80,8 @@ type
public
function List(Directory: String; NameList: Boolean): Boolean; override;
function FsSetTime(const FileName: String; LastAccessTime, LastWriteTime: PWfxFileTime): BOOL; override;
public
property Fingerprint: AnsiString read FFingerprint write FFingerprint;
end;
implementation
@ -290,8 +294,9 @@ function TScpSend.Connect: Boolean;
const
HOSTKEY_SIZE = 20;
var
S: String;
I: Integer;
S: String = '';
Message: UnicodeString;
userauthlist: PAnsiChar;
FingerPrint: array [0..Pred(HOSTKEY_SIZE)] of AnsiChar;
begin
@ -318,12 +323,29 @@ begin
DoStatus(False, 'Connection established');
FingerPrint := libssh2_hostkey_hash(FSession, LIBSSH2_HOSTKEY_HASH_SHA1);
S:= 'Server fingerprint:';
for I:= Low(FingerPrint) to High(FingerPrint) do
begin
S:= S + #32 + IntToHex(Ord(FingerPrint[i]), 2);
S+= IntToHex(Ord(FingerPrint[I]), 2) + #32;
end;
SetLength(S, Length(S) - 1); // Remove space
DoStatus(False, 'Server fingerprint: ' + S);
// Verify server fingerprint
if FFingerPrint <> S then
begin
if FFingerprint = EmptyStr then
Message:= 'You are using this connection for the first time.' + LineEnding + 'Please verify that the following host fingerprint matches the fingerprint of your server:'
else begin
Message:= 'WARNING!' + LineEnding + 'The fingerprint of the host has changed!' + LineEnding + 'Please make sure that the new fingerprint matches your server:';
end;
Message += UnicodeString(LineEnding + LineEnding + S);
if not RequestProc(PluginNumber, RT_MsgYesNo, nil, PWideChar(Message), nil, 0) then
begin
LogProc(PluginNumber, msgtype_importanterror, 'Wrong server fingerprint!');
Exit(False);
end;
FFingerprint:= S;
end;
DoStatus(False, S);
//* check what authentication methods are available */
userauthlist := libssh2_userauth_list(FSession, PAnsiChar(FUserName), Length(FUserName));
@ -416,6 +438,12 @@ begin
Result:= FCurrentDir;
end;
procedure TScpSend.CloneTo(AValue: TFTPSendEx);
begin
inherited CloneTo(AValue);
TScpSend(AValue).FFingerprint:= FFingerprint;
end;
function TScpSend.FileSize(const FileName: String): Int64;
begin
Result:= -1;