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

SSH password change + When opening secondary shell session fails, the main session is not Closed + Session.ExecuteCommand unit tests

ChangePassword + PasswordChangesFailed counters
task_change_password help keyword

Source commit: 335f4de0796d0367a8ed427790cf1407607cc0d8
This commit is contained in:
Martin Prikryl
2016-10-25 06:34:05 +00:00
parent 8763908626
commit 0ccd2defd3
39 changed files with 290 additions and 88 deletions

View File

@@ -148,7 +148,7 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "WinSCP.Session.#TestHandlesClosedInternal")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.ExeSessionProcess.#TryCreateEvent(System.String,System.Threading.EventWaitHandle&)")]
[assembly: SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Scope = "member", Target = "WinSCP.ExeSessionProcess.#TryCreateEvent(System.String,System.Threading.EventWaitHandle&)")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.SessionOptions.#GetPassword()")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.SessionOptions.#GetPassword(System.Security.SecureString)")]
[assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Scope = "member", Target = "WinSCP.SessionOptions.#ParseUrl(System.String)")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.ExeSessionProcess+NoopSafeHandle.#.ctor(System.IntPtr)")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.ExeSessionProcess.#CreateFileMapping(System.String)")]
@@ -158,6 +158,6 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "WinSCP.ExeSessionProcess.#CreateFileMapping(System.String)")]
[assembly: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "WinSCP.Logger.#CreateCounters()")]
[assembly: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "WinSCP.SessionLogReader.#LogContents()")]
[assembly: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "WinSCP.Session.#SessionOptionsToSwitches(WinSCP.SessionOptions,System.Boolean)")]
[assembly: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "WinSCP.Session.#SessionOptionsToSwitches(WinSCP.SessionOptions,System.Boolean,System.String&,System.String&)")]
[assembly: SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Scope = "member", Target = "WinSCP.UnsafeNativeMethods.#RegGetValue(System.UIntPtr,System.String,System.String,WinSCP.RegistryFlags,WinSCP.RegistryType&,System.IntPtr,System.UInt32&)")]
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "WinSCP.ExeSessionProcess.#GetInstallationPath(Microsoft.Win32.RegistryHive)")]

View File

@@ -1463,22 +1463,27 @@ namespace WinSCP
url += tail;
logUrl += tail;
string arguments = SessionOptionsToSwitches(sessionOptions, scanFingerprint);
string arguments;
string logArguments;
SessionOptionsToSwitches(sessionOptions, scanFingerprint, out arguments, out logArguments);
const string switchName = "-rawsettings";
Tools.AddRawParameters(ref arguments, sessionOptions.RawSettings, "-rawsettings");
Tools.AddRawParameters(ref logArguments, sessionOptions.RawSettings, switchName);
if (!string.IsNullOrEmpty(arguments))
{
arguments = " " + arguments;
logArguments = " " + logArguments;
}
// Switches should (and particularly the -rawsettings MUST) come after the URL
command = "\"" + Tools.ArgumentEscape(url) + "\"" + arguments;
log = "\"" + Tools.ArgumentEscape(logUrl) + "\"" + arguments;
log = "\"" + Tools.ArgumentEscape(logUrl) + "\"" + logArguments;
}
}
private string SessionOptionsToSwitches(SessionOptions sessionOptions, bool scanFingerprint)
private void SessionOptionsToSwitches(SessionOptions sessionOptions, bool scanFingerprint, out string arguments, out string logArguments)
{
using (Logger.CreateCallstack())
{
@@ -1580,7 +1585,20 @@ namespace WinSCP
switches.Add(FormatSwitch("timeout", (int)sessionOptions.Timeout.TotalSeconds));
return string.Join(" ", switches.ToArray());
List<string> logSwitches = new List<string>(switches);
if ((sessionOptions.SecureNewPassword != null) && !scanFingerprint)
{
if (sessionOptions.SecurePassword == null)
{
throw new ArgumentException("SessionOptions.SecureNewPassword is set, but SessionOptions.SecurePassword is not.");
}
switches.Add(FormatSwitch("newpassword", sessionOptions.NewPassword));
logSwitches.Add(FormatSwitch("newpassword", "***"));
}
arguments = string.Join(" ", switches.ToArray());
logArguments = string.Join(" ", logSwitches.ToArray());
}
}

View File

@@ -49,8 +49,10 @@ namespace WinSCP
public string HostName { get; set; }
public int PortNumber { get { return _portNumber; } set { SetPortNumber(value); } }
public string UserName { get; set; }
public string Password { get { return GetPassword(); } set { SetPassword(value); } }
public SecureString SecurePassword { get; set; }
public string Password { get { return GetPassword(_securePassword); } set { SetPassword(ref _securePassword, value); } }
public SecureString SecurePassword { get { return _securePassword; } set { _securePassword = value; } }
public string NewPassword { get { return GetPassword(_secureNewPassword); } set { SetPassword(ref _secureNewPassword, value); } }
public SecureString SecureNewPassword { get { return _secureNewPassword; } set { _secureNewPassword = value; } }
public TimeSpan Timeout { get { return _timeout; } set { SetTimeout(value); } }
public int TimeoutInMilliseconds { get { return Tools.TimeSpanToMilliseconds(Timeout); } set { Timeout = Tools.MillisecondsToTimeSpan(value); } }
public string PrivateKeyPassphrase { get; set; }
@@ -364,25 +366,25 @@ namespace WinSCP
_webdavRoot = value;
}
private void SetPassword(string value)
private static void SetPassword(ref SecureString securePassword, string value)
{
if (value == null)
{
SecurePassword = null;
securePassword = null;
}
else
{
SecurePassword = new SecureString();
securePassword = new SecureString();
foreach (char c in value)
{
SecurePassword.AppendChar(c);
securePassword.AppendChar(c);
}
}
}
private string GetPassword()
private static string GetPassword(SecureString securePassword)
{
if (SecurePassword == null)
if (securePassword == null)
{
return null;
}
@@ -391,7 +393,7 @@ namespace WinSCP
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(SecurePassword);
ptr = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
return Marshal.PtrToStringUni(ptr);
}
finally
@@ -401,6 +403,8 @@ namespace WinSCP
}
}
private SecureString _securePassword;
private SecureString _secureNewPassword;
private string _sshHostKeyFingerprint;
private string _tlsHostCertificateFingerprint;
private TimeSpan _timeout;

View File

@@ -2351,6 +2351,7 @@ bool __fastcall TFTPFileSystem::IsCapable(int Capability) const
case fcLocking:
case fcPreservingTimestampDirs:
case fcResumeSupport:
case fcChangePassword:
return false;
default:

View File

@@ -9,6 +9,7 @@
#define SCRIPT_SWITCH "script"
#define COMMAND_SWITCH L"Command"
#define SESSIONNAME_SWICH L"SessionName"
#define NEWPASSWORD_SWITCH L"newpassword"
#define INI_NUL L"nul"
#define PRESERVETIME_SWITCH L"preservetime"
#define PRESERVETIMEDIRS_SWITCH_VALUE L"all"

View File

@@ -464,6 +464,9 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
case fcResumeSupport:
return false;
case fcChangePassword:
return FSecureShell->CanChangePassword();
default:
DebugFail();
return false;

View File

@@ -2059,7 +2059,7 @@ __fastcall TManagementScript::TManagementScript(TStoredSessionList * StoredSessi
FCommands->Register(L"exit", SCRIPT_EXIT_DESC, SCRIPT_EXIT_HELP, &ExitProc, 0, 0, false);
FCommands->Register(L"bye", 0, SCRIPT_EXIT_HELP, &ExitProc, 0, 0, false);
FCommands->Register(L"open", SCRIPT_OPEN_DESC, SCRIPT_OPEN_HELP8, &OpenProc, 0, -1, true);
FCommands->Register(L"open", SCRIPT_OPEN_DESC, SCRIPT_OPEN_HELP9, &OpenProc, 0, -1, true);
FCommands->Register(L"close", SCRIPT_CLOSE_DESC, SCRIPT_CLOSE_HELP, &CloseProc, 0, 1, false);
FCommands->Register(L"session", SCRIPT_SESSION_DESC, SCRIPT_SESSION_HELP, &SessionProc, 0, 1, false);
FCommands->Register(L"lpwd", SCRIPT_LPWD_DESC, SCRIPT_LPWD_HELP, &LPwdProc, 0, 0, false);

View File

@@ -166,6 +166,7 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
conf_set_str(conf, CONF_username, UTF8String(Data->UserNameExpanded).c_str());
conf_set_int(conf, CONF_port, Data->PortNumber);
conf_set_int(conf, CONF_protocol, PROT_SSH);
conf_set_int(conf, CONF_change_password, Data->ChangePassword);
// always set 0, as we will handle keepalives ourselves to avoid
// multi-threaded issues in putty timer list
conf_set_int(conf, CONF_ping_interval, 0);
@@ -866,6 +867,24 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
FStoredPassphraseTried = true;
}
}
else if (PromptKind == pkNewPassword)
{
if (FSessionData->ChangePassword)
{
FUI->Information(LoadStr(AUTH_CHANGING_PASSWORD), false);
if (!FSessionData->Password.IsEmpty() && !FSessionData->NewPassword.IsEmpty() && !FStoredPasswordTried)
{
LogEvent(L"Using stored password and new password.");
Result = true;
DebugAssert(Results->Count == 3);
Results->Strings[0] = FSessionData->Password;
Results->Strings[1] = FSessionData->NewPassword;
Results->Strings[2] = FSessionData->NewPassword;
FStoredPasswordTried = true;
}
}
}
if (!Result)
{
@@ -896,7 +915,10 @@ void __fastcall TSecureShell::GotHostKey()
if (!FAuthenticating && !FAuthenticated)
{
FAuthenticating = true;
FUI->Information(LoadStr(STATUS_AUTHENTICATE), true);
if (!FSessionData->ChangePassword)
{
FUI->Information(LoadStr(STATUS_AUTHENTICATE), true);
}
}
}
//---------------------------------------------------------------------------
@@ -2520,3 +2542,11 @@ void __fastcall TSecureShell::CollectUsage()
Configuration->Usage->Inc(L"OpenedSessionsSSHOther");
}
}
//---------------------------------------------------------------------------
bool __fastcall TSecureShell::CanChangePassword()
{
return
// These major SSH servers explicitly do not support password change.
(SshImplementation != sshiOpenSSH) && // See userauth_passwd
(SshImplementation != sshiProFTPD); // See sftp_auth_password
}

View File

@@ -139,6 +139,7 @@ public:
void __fastcall ClearStdError();
bool __fastcall GetStoredCredentialsTried();
void __fastcall CollectUsage();
bool __fastcall CanChangePassword();
void __fastcall RegisterReceiveHandler(TNotifyEvent Handler);
void __fastcall UnregisterReceiveHandler(TNotifyEvent Handler);

View File

@@ -104,6 +104,8 @@ void __fastcall TSessionData::Default()
PortNumber = SshPortNumber;
UserName = L"";
Password = L"";
NewPassword = L"";
ChangePassword = false;
PingInterval = 30;
PingType = ptOff;
Timeout = 15;
@@ -277,6 +279,8 @@ void __fastcall TSessionData::NonPersistant()
PROPERTY(Note);
//---------------------------------------------------------------------
#define ADVANCED_PROPERTIES \
PROPERTY(NewPassword); \
PROPERTY(ChangePassword); \
PROPERTY(PingInterval); \
PROPERTY(PingType); \
PROPERTY(Timeout); \
@@ -1420,6 +1424,7 @@ void __fastcall TSessionData::SavePasswords(THierarchicalStorage * Storage, bool
void __fastcall TSessionData::RecryptPasswords()
{
Password = Password;
NewPassword = NewPassword;
ProxyPassword = ProxyPassword;
TunnelPassword = TunnelPassword;
Passphrase = Passphrase;
@@ -1437,12 +1442,17 @@ bool __fastcall TSessionData::HasAnySessionPassword()
//---------------------------------------------------------------------
bool __fastcall TSessionData::HasAnyPassword()
{
return HasAnySessionPassword() || !FProxyPassword.IsEmpty();
return
HasAnySessionPassword() ||
!FProxyPassword.IsEmpty() ||
// will probably be never used
FNewPassword.IsEmpty();
}
//---------------------------------------------------------------------
void __fastcall TSessionData::ClearSessionPasswords()
{
FPassword = L"";
FNewPassword = L"";
FTunnelPassword = L"";
}
//---------------------------------------------------------------------
@@ -1560,7 +1570,9 @@ bool __fastcall TSessionData::IsProtocolUrl(
//---------------------------------------------------------------------
bool __fastcall TSessionData::IsSensitiveOption(const UnicodeString & Option)
{
return SameText(Option, PassphraseOption);
return
SameText(Option, PassphraseOption) ||
SameText(Option, NEWPASSWORD_SWITCH);
}
//---------------------------------------------------------------------
bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
@@ -1869,6 +1881,11 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
{
Name = Value;
}
if (Options->FindSwitch(NEWPASSWORD_SWITCH, Value))
{
ChangePassword = true;
NewPassword = Value;
}
if (Options->FindSwitch(L"privatekey", Value))
{
PublicKeyFile = Value;
@@ -2077,6 +2094,7 @@ void __fastcall TSessionData::SetHostName(UnicodeString value)
{
// HostName is key for password encryption
UnicodeString XPassword = Password;
UnicodeString XNewPassword = Password;
// This is now hardly used as hostname is parsed directly on login dialog.
// But can be used when importing sites from PuTTY, as it allows same format too.
@@ -2090,7 +2108,9 @@ void __fastcall TSessionData::SetHostName(UnicodeString value)
Modify();
Password = XPassword;
NewPassword = XNewPassword;
Shred(XPassword);
Shred(XNewPassword);
}
}
//---------------------------------------------------------------------
@@ -2141,9 +2161,12 @@ void __fastcall TSessionData::SetUserName(UnicodeString value)
{
// UserName is key for password encryption
UnicodeString XPassword = Password;
UnicodeString XNewPassword = NewPassword;
SET_SESSION_PROPERTY(UserName);
Password = XPassword;
NewPassword = XNewPassword;
Shred(XPassword);
Shred(XNewPassword);
}
}
//---------------------------------------------------------------------
@@ -2163,6 +2186,22 @@ UnicodeString __fastcall TSessionData::GetPassword() const
return DecryptPassword(FPassword, UserName+HostName);
}
//---------------------------------------------------------------------
void __fastcall TSessionData::SetNewPassword(UnicodeString avalue)
{
RawByteString value = EncryptPassword(avalue, UserName+HostName);
SET_SESSION_PROPERTY(NewPassword);
}
//---------------------------------------------------------------------
UnicodeString __fastcall TSessionData::GetNewPassword() const
{
return DecryptPassword(FNewPassword, UserName+HostName);
}
//---------------------------------------------------------------------
void __fastcall TSessionData::SetChangePassword(bool value)
{
SET_SESSION_PROPERTY(ChangePassword);
}
//---------------------------------------------------------------------
void __fastcall TSessionData::SetPingInterval(int value)
{
SET_SESSION_PROPERTY(PingInterval);
@@ -3646,6 +3685,19 @@ UnicodeString __fastcall TSessionData::ComposePath(
{
return UnixIncludeTrailingBackslash(Path) + Name;
}
//---------------------------------------------------------------------
void __fastcall TSessionData::DisableAuthentationsExceptPassword()
{
SshNoUserAuth = false;
AuthTIS = false;
AuthKI = false;
AuthKIPassword = false;
AuthGSSAPI = false;
PublicKeyFile = L"";
TlsCertificateFile = L"";
Passphrase = L"";
TryAgent = false;
}
//=== TStoredSessionList ----------------------------------------------
__fastcall TStoredSessionList::TStoredSessionList(bool aReadOnly):
TNamedObjectList(), FReadOnly(aReadOnly)

View File

@@ -93,6 +93,8 @@ private:
int FPortNumber;
UnicodeString FUserName;
RawByteString FPassword;
RawByteString FNewPassword;
bool FChangePassword;
int FPingInterval;
TPingType FPingType;
bool FTryAgent;
@@ -219,6 +221,9 @@ private:
UnicodeString __fastcall GetUserNameExpanded();
void __fastcall SetPassword(UnicodeString value);
UnicodeString __fastcall GetPassword() const;
void __fastcall SetNewPassword(UnicodeString value);
UnicodeString __fastcall GetNewPassword() const;
void __fastcall SetChangePassword(bool value);
void __fastcall SetPingInterval(int value);
void __fastcall SetTryAgent(bool value);
void __fastcall SetAgentFwd(bool value);
@@ -442,6 +447,7 @@ public:
void __fastcall ConfigureTunnel(int PortNumber);
void __fastcall RollbackTunnel();
void __fastcall ExpandEnvironmentVariables();
void __fastcall DisableAuthentationsExceptPassword();
bool __fastcall IsSame(const TSessionData * Default, bool AdvancedOnly);
bool __fastcall IsSameSite(const TSessionData * Default);
bool __fastcall IsInFolderOrWorkspace(UnicodeString Name);
@@ -465,6 +471,8 @@ public:
__property UnicodeString UserName = { read=FUserName, write=SetUserName };
__property UnicodeString UserNameExpanded = { read=GetUserNameExpanded };
__property UnicodeString Password = { read=GetPassword, write=SetPassword };
__property UnicodeString NewPassword = { read=GetNewPassword, write=SetNewPassword };
__property bool ChangePassword = { read=FChangePassword, write=SetChangePassword };
__property int PingInterval = { read=FPingInterval, write=SetPingInterval };
__property bool TryAgent = { read=FTryAgent, write=SetTryAgent };
__property bool AgentFwd = { read=FAgentFwd, write=SetAgentFwd };

View File

@@ -42,6 +42,7 @@ enum TFSCapability { fcUserGroupListing, fcModeChanging, fcGroupChanging,
fcModeChangingUpload, fcPreservingTimestampUpload, fcShellAnyCommand,
fcSecondaryShell, fcRemoveCtrlZUpload, fcRemoveBOMUpload, fcMoveToQueue,
fcLocking, fcPreservingTimestampDirs, fcResumeSupport,
fcChangePassword,
fcCount };
//---------------------------------------------------------------------------
struct TFileSystemInfo

View File

@@ -2161,6 +2161,9 @@ bool __fastcall TSFTPFileSystem::IsCapable(int Capability) const
case fcLocking:
return false;
case fcChangePassword:
return FSecureShell->CanChangePassword();
default:
DebugFail();
return false;

View File

@@ -822,8 +822,7 @@ void __fastcall TTerminal::Close()
{
FFileSystem->Close();
// Cannot rely on CommandSessionOpened here as Status is set to ssClosed
// only after the OnClose is called
// Cannot rely on CommandSessionOpened here as Status is set to ssClosed too late
if ((FCommandSession != NULL) && FCommandSession->Active)
{
// prevent recursion
@@ -1264,14 +1263,6 @@ void __fastcall TTerminal::Reopen(int Params)
SessionData->FSProtocol = (FFSProtocol == cfsSCP ? fsSCPonly : fsSFTPonly);
}
// Could be active before, if fatal error occured in the secondary terminal.
// But now, since we handle the secondary terminal's OnClose,
// by closing outselves, it should not happen anymore.
if (DebugAlwaysFalse(Active))
{
Close();
}
Open();
}
__finally
@@ -4088,6 +4079,25 @@ bool __fastcall TTerminal::GetCommandSessionOpened()
(FCommandSession->Status == ssOpened);
}
//---------------------------------------------------------------------------
TTerminal * __fastcall TTerminal::CreateSecondarySession(const UnicodeString & Name, TSessionData * SessionData)
{
std::unique_ptr<TTerminal> Result(new TSecondaryTerminal(this, SessionData, Configuration, Name));
Result->AutoReadDirectory = false;
Result->FExceptionOnFail = FExceptionOnFail;
Result->OnQueryUser = OnQueryUser;
Result->OnPromptUser = OnPromptUser;
Result->OnShowExtendedException = OnShowExtendedException;
Result->OnProgress = OnProgress;
Result->OnFinished = OnFinished;
Result->OnInformation = OnInformation;
Result->OnCustomCommand = OnCustomCommand;
// do not copy OnDisplayBanner to avoid it being displayed
return Result.release();
}
//---------------------------------------------------------------------------
TTerminal * __fastcall TTerminal::GetCommandSession()
{
if ((FCommandSession != NULL) && !FCommandSession->Active)
@@ -4101,50 +4111,32 @@ TTerminal * __fastcall TTerminal::GetCommandSession()
// levels between main and command session
DebugAssert(FInTransaction == 0);
try
{
FCommandSession = new TSecondaryTerminal(this, SessionData,
Configuration, L"Shell");
std::unique_ptr<TSessionData> CommandSessionData(SessionData->Clone());
CommandSessionData->RemoteDirectory = CurrentDirectory;
CommandSessionData->FSProtocol = fsSCPonly;
CommandSessionData->ClearAliases = false;
CommandSessionData->UnsetNationalVars = false;
CommandSessionData->LookupUserGroups = asOff;
FCommandSession->AutoReadDirectory = false;
FCommandSession = CreateSecondarySession(L"Shell", CommandSessionData.get());
TSessionData * CommandSessionData = FCommandSession->FSessionData;
CommandSessionData->RemoteDirectory = CurrentDirectory;
CommandSessionData->FSProtocol = fsSCPonly;
CommandSessionData->ClearAliases = false;
CommandSessionData->UnsetNationalVars = false;
CommandSessionData->LookupUserGroups = asOff;
FCommandSession->AutoReadDirectory = false;
FCommandSession->FExceptionOnFail = FExceptionOnFail;
FCommandSession->FExceptionOnFail = FExceptionOnFail;
FCommandSession->OnQueryUser = OnQueryUser;
FCommandSession->OnPromptUser = OnPromptUser;
FCommandSession->OnShowExtendedException = OnShowExtendedException;
FCommandSession->OnProgress = OnProgress;
FCommandSession->OnFinished = OnFinished;
FCommandSession->OnInformation = OnInformation;
FCommandSession->OnCustomCommand = OnCustomCommand;
FCommandSession->OnClose = CommandSessionClose;
// do not copy OnDisplayBanner to avoid it being displayed
}
catch(...)
{
SAFE_DESTROY(FCommandSession);
throw;
}
FCommandSession->OnQueryUser = OnQueryUser;
FCommandSession->OnPromptUser = OnPromptUser;
FCommandSession->OnShowExtendedException = OnShowExtendedException;
FCommandSession->OnProgress = OnProgress;
FCommandSession->OnFinished = OnFinished;
FCommandSession->OnInformation = OnInformation;
FCommandSession->OnCustomCommand = OnCustomCommand;
// do not copy OnDisplayBanner to avoid it being displayed
}
return FCommandSession;
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::CommandSessionClose(TObject * /*Sender*/)
{
// Keep the states in sync.
// This is particularly to invoke ours OnClose,
// So that it is triggered before Reopen is called
Close();
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::AnyCommand(const UnicodeString Command,
TCaptureOutputEvent OutputEvent)
{

View File

@@ -394,7 +394,6 @@ protected:
const UnicodeString & FileName, TFileOperation Operation1, TFileOperation Operation2 = foNone);
void __fastcall StartOperationWithFile(
const UnicodeString & FileName, TFileOperation Operation1, TFileOperation Operation2 = foNone);
void __fastcall CommandSessionClose(TObject * Sender);
bool __fastcall CanRecurseToDirectory(const TRemoteFile * File);
bool __fastcall DoOnCustomCommand(const UnicodeString & Command);
@@ -497,6 +496,7 @@ public:
void __fastcall ReflectSettings();
void __fastcall CollectUsage();
bool __fastcall IsThisOrChild(TTerminal * Terminal);
TTerminal * __fastcall CreateSecondarySession(const UnicodeString & Name, TSessionData * SessionData);
const TSessionInfo & __fastcall GetSessionInfo();
const TFileSystemInfo & __fastcall GetFileSystemInfo(bool Retrieve = false);

View File

@@ -702,6 +702,7 @@ bool __fastcall TWebDAVFileSystem::IsCapable(int Capability) const
case fcRemoteCopy:
case fcPreservingTimestampDirs:
case fcResumeSupport:
case fcChangePassword:
return false;
case fcLocking:

View File

@@ -242,6 +242,7 @@ void __fastcall TConsoleDialog::ExecuteCommand()
catch(Exception & E)
{
DebugAssert(FLastTerminal != NULL);
// Should use the command session, if there's one, not to close the main session
FLastTerminal->ShowExtendedException(&E);
}
}

View File

@@ -5599,20 +5599,9 @@ void __fastcall TCustomScpExplorerForm::DoOpenDirectoryDialog(
//---------------------------------------------------------------------------
bool __fastcall TCustomScpExplorerForm::CommandSessionFallback()
{
bool Result = true;
DebugAssert(!FTerminal->CommandSessionOpened);
try
{
TTerminalManager::ConnectTerminal(FTerminal->CommandSession, false);
}
catch(Exception & E)
{
ShowExtendedExceptionEx(FTerminal->CommandSession, &E);
Result = false;
}
return Result;
return TTerminalManager::Instance()->ConnectTerminal(FTerminal->CommandSession);
}
//---------------------------------------------------------------------------
bool __fastcall TCustomScpExplorerForm::EnsureCommandSessionFallback(TFSCapability Capability)
@@ -9132,3 +9121,28 @@ void __fastcall TCustomScpExplorerForm::SessionsPageControlContextPopup(TObject
Handled = true;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomScpExplorerForm::CanChangePassword()
{
return (Terminal != NULL) && Terminal->Active && Terminal->IsCapable[fcChangePassword];
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::ChangePassword()
{
Configuration->Usage->Inc(L"PasswordChanges");
std::unique_ptr<TSessionData> ChangePasswordData(Terminal->SessionData->Clone());
ChangePasswordData->DisableAuthentationsExceptPassword();
ChangePasswordData->ChangePassword = true;
std::unique_ptr<TTerminal> ChangePasswordSession(
Terminal->CreateSecondarySession(L"ChangePassword", ChangePasswordData.get()));
ChangePasswordSession->AutoReadDirectory = false;
if (TTerminalManager::Instance()->ConnectTerminal(ChangePasswordSession.get()))
{
MessageDialog(MainInstructions(LoadStr(PASSWORD_CHANGED)), qtInformation, qaOK, HELP_CHANGE_PASSWORD);
}
else
{
Configuration->Usage->Inc(L"PasswordChangesFailed");
}
}

View File

@@ -684,6 +684,8 @@ public:
virtual void __fastcall DisplaySystemContextMenu();
virtual void __fastcall GoToAddress() = 0;
bool __fastcall CanConsole();
bool __fastcall CanChangePassword();
void __fastcall ChangePassword();
__property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };
__property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };

View File

@@ -444,6 +444,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
UPD(ClearCachesAction, HasTerminal && !ScpExplorer->Terminal->AreCachesEmpty)
UPD(NewFileAction, DirViewEnabled(osCurrent) && !WinConfiguration->DisableOpenEdit)
UPD(EditorListCustomizeAction, true)
UPD(ChangePasswordAction, ScpExplorer->CanChangePassword())
// CUSTOM COMMANDS
UPD(CustomCommandsFileAction, true)
@@ -778,6 +779,7 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
EXE(ClearCachesAction, ScpExplorer->Terminal->ClearCaches())
EXE(NewFileAction, ScpExplorer->EditNew(osCurrent))
EXE(EditorListCustomizeAction, PreferencesDialog(pmEditor))
EXE(ChangePasswordAction, ScpExplorer->ChangePassword())
// CUSTOM COMMANDS
EXE(CustomCommandsFileAction, CreateCustomCommandsMenu(CustomCommandsFileAction, ccltFile))

View File

@@ -2259,6 +2259,13 @@ object NonVisualDataModule: TNonVisualDataModule
Hint = 'Displays tips on using WinSCP'
ImageIndex = 110
end
object ChangePasswordAction: TAction
Tag = 15
Category = 'Session'
Caption = '&Change Password...'
HelpKeyword = 'task_change_password'
Hint = 'Change account password'
end
end
object ExplorerBarPopup: TTBXPopupMenu
Images = GlyphsModule.ExplorerImages

View File

@@ -617,6 +617,7 @@ __published: // IDE-managed Components
TTBXSubmenuItem *RemoteDirViewPopupCustomCommandsMenu;
TTBXItem *TBXItem75;
TTBXItem *TBXItem76;
TAction *ChangePasswordAction;
void __fastcall LogActionsUpdate(TBasicAction *Action, bool &Handled);
void __fastcall LogActionsExecute(TBasicAction *Action, bool &Handled);
void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);

View File

@@ -425,6 +425,9 @@ inherited ScpCommanderForm: TScpCommanderForm
object TBXItem135: TTBXItem
Action = NonVisualDataModule.SessionGenerateUrlAction2
end
object TBXItem227: TTBXItem
Action = NonVisualDataModule.ChangePasswordAction
end
object TBXSeparatorItem29: TTBXSeparatorItem
end
object TBXSubmenuItem21: TTBXSubmenuItem

View File

@@ -420,6 +420,7 @@ __published:
TTBXItem *TBXItem216;
TTBXItem *TBXItem217;
TTBXSubmenuItem *TBXSubmenuItem28;
TTBXItem *TBXItem227;
void __fastcall SplitterMoved(TObject *Sender);
void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
bool &Accept);

View File

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

View File

@@ -312,6 +312,7 @@ __published:
TTBXItem *TBXItem216;
TTBXItem *TBXItem159;
TTBXSubmenuItem *TBXSubmenuItem28;
TTBXItem *TBXItem160;
void __fastcall RemoteDirViewUpdateStatusBar(TObject *Sender,
const TStatusFileInfo &FileInfo);
void __fastcall UnixPathComboBoxBeginEdit(TTBEditItem *Sender,

View File

@@ -913,6 +913,7 @@ void cleanup_exit(int);
X(INT, NONE, connect_timeout) \
X(INT, NONE, sndbuf) \
X(INT, NONE, force_remote_cmd2) \
X(INT, NONE, change_password) \
/* MPEXT END */ \
/* Now define the actual enum of option keywords using that macro. */

View File

@@ -9145,6 +9145,9 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
int kbd_inter_refused;
int we_are_in, userauth_success;
prompts_t *cur_prompt;
#ifdef MPEXT
int change_password;
#endif
int num_prompts;
char *username;
char *password;
@@ -10372,6 +10375,17 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;
#ifdef MPEXT
s->change_password = conf_get_int(ssh->conf, CONF_change_password);
if (s->change_password != 0)
{
s->password = dupstr("");
s->type = AUTH_TYPE_PASSWORD;
}
else
{
/* no indentation to ease merges */
#endif
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("SSH password");
@@ -10436,10 +10450,16 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
* request.
*/
crWaitUntilV(pktin);
#ifdef MPEXT
}
#endif
changereq_first_time = TRUE;
while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
while ((pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ)
#ifdef MPEXT
|| (s->change_password != 0)
#endif
) {
/*
* We're being asked for a new password
* (perhaps not for the first time).
@@ -10450,6 +10470,9 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
char *prompt; /* not live over crReturn */
int prompt_len; /* not live over crReturn */
#ifdef MPEXT
if (s->change_password == 0)
#endif
{
const char *msg;
if (changereq_first_time)
@@ -10461,6 +10484,10 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
c_write_str(ssh, "\r\n");
}
#ifdef MPEXT
s->change_password = 0;
#endif
ssh_pkt_getstring(pktin, &prompt, &prompt_len);
s->cur_prompt = new_prompts(ssh->frontend);

View File

@@ -58,5 +58,6 @@
#define HELP_TIPS "ui_tips"
#define HELP_DEBUGGING "debugging"
#define HELP_EXTENSION_OPTIONS "ui_pref_commands"
#define HELP_CHANGE_PASSWORD "task_change_password"
#endif // TextsWin

View File

@@ -7,7 +7,7 @@
#define SCRIPT_HELP_HELP 4
#define SCRIPT_EXIT_HELP 5
#define SCRIPT_OPEN_HELP8 6
#define SCRIPT_OPEN_HELP9 6
#define SCRIPT_CLOSE_HELP 7
#define SCRIPT_SESSION_HELP 8
#define SCRIPT_PWD_HELP 9
@@ -456,6 +456,7 @@
#define COPY_INFO_PRESERVE_TIME_DIRS 554
#define TEXT_FILE_ENCODING 555
#define AND_STR 556
#define AUTH_CHANGING_PASSWORD 557
#define CORE_VARIABLE_STRINGS 600
#define PUTTY_BASED_ON 601

View File

@@ -426,6 +426,7 @@ BEGIN
COPY_INFO_PRESERVE_TIME_DIRS, "%s (including directories)"
TEXT_FILE_ENCODING, "The file must be in UTF-8 or UTF-16 encoding."
AND_STR, "%s and %s"
AUTH_CHANGING_PASSWORD, "Changing password."
CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"

View File

@@ -59,7 +59,7 @@ BEGIN
" Closes all sessions and terminates the program.\n"
"alias:\n"
" bye\n"
SCRIPT_OPEN_HELP8,
SCRIPT_OPEN_HELP9,
"open <site>\n"
"open sftp|scp|ftp[es]|http[s] :// [ <user> [ :password ] @ ] <host> [ :<port> ]\n"
" Establishes connection to given host. Use either name of the site or\n"
@@ -79,6 +79,7 @@ BEGIN
" Configures any site settings using raw format\n"
" as in an INI file\n"
" -filezilla Load <site> from FileZilla site manager\n"
" -newpassword=<password> Changes password to <password>\n"
"examples:\n"
" open\n"
" open sftp://martin@example.com:2222 -privatekey=mykey.ppk\n"

View File

@@ -283,6 +283,7 @@
#define USAGE_SESSIONNAME 1573
#define USAGE_REFRESH 1574
#define USAGE_LOGSIZE 1575
#define PASSWORD_CHANGED 1576
#define WIN_FORMS_STRINGS 1600
#define LOG_NOLOG 1601

View File

@@ -286,6 +286,7 @@ BEGIN
USAGE_SESSIONNAME, "Gives a name to the session"
USAGE_REFRESH, "Reloads remote panel of all running instances of WinSCP. If session (and optionally a path) is specified, refreshes only the instances with that session (and path)."
USAGE_LOGSIZE, "Enables log rotation and optionally deleting of old logs."
PASSWORD_CHANGED, "Password has been changed."
WIN_FORMS_STRINGS, "WIN_FORMS_STRINGS"
LOG_NOLOG, "No session log."

View File

@@ -210,7 +210,7 @@ void __fastcall TTerminalManager::FreeActiveTerminal()
}
}
//---------------------------------------------------------------------------
void TTerminalManager::ConnectTerminal(TTerminal * Terminal, bool Reopen)
void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool Reopen)
{
TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
// it must be managed terminal, unless it is secondary terminal (of managed terminal)
@@ -271,6 +271,21 @@ void TTerminalManager::ConnectTerminal(TTerminal * Terminal, bool Reopen)
}
}
//---------------------------------------------------------------------------
bool __fastcall TTerminalManager::ConnectTerminal(TTerminal * Terminal)
{
bool Result = true;
try
{
DoConnectTerminal(Terminal, false);
}
catch (Exception & E)
{
ShowExtendedExceptionEx(Terminal, &E);
Result = false;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TTerminalManager::TerminalThreadIdle(void * /*Data*/, TObject * /*Sender*/)
{
Application->ProcessMessages();
@@ -288,7 +303,7 @@ bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen)
{
DebugAssert(ActiveTerminal);
ConnectTerminal(ActiveTerminal, Reopen);
DoConnectTerminal(ActiveTerminal, Reopen);
if (ScpExplorer)
{

View File

@@ -48,7 +48,7 @@ public:
void __fastcall ReconnectActiveTerminal();
void __fastcall FreeActiveTerminal();
void __fastcall CycleTerminals(bool Forward);
static void ConnectTerminal(TTerminal * Terminal, bool Reopen);
bool __fastcall ConnectTerminal(TTerminal * Terminal);
void __fastcall SetActiveTerminalWithAutoReconnect(TTerminal * value);
void __fastcall UpdateAppTitle();
bool __fastcall CanOpenInPutty();
@@ -62,6 +62,7 @@ public:
TTerminal * __fastcall FindActiveTerminalForSite(TSessionData * Data);
TTerminalQueue * __fastcall FindQueueForTerminal(TTerminal * Terminal);
void __fastcall UpdateSessionCredentials(TSessionData * Data);
void __fastcall DoConnectTerminal(TTerminal * Terminal, bool Reopen);
__property TCustomScpExplorerForm * ScpExplorer = { read = FScpExplorer, write = SetScpExplorer };
__property TTerminal * ActiveTerminal = { read = FActiveTerminal, write = SetActiveTerminal };

View File

@@ -175,7 +175,7 @@ void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
WinConfiguration->ConfirmExitOnCompletion;
if (E->InheritsFrom(__classid(EFatal)) && (Terminal != NULL) &&
(Manager != NULL) && (Manager->ActiveTerminal != NULL) && Manager->ActiveTerminal->IsThisOrChild(Terminal))
(Manager != NULL) && (Manager->ActiveTerminal == Terminal))
{
int SessionReopenTimeout = 0;
TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Manager->ActiveTerminal);

View File

@@ -892,12 +892,14 @@ void __fastcall HideAsModal(TForm * Form, void *& Storage)
delete AStorage;
}
//---------------------------------------------------------------------------
void __fastcall ReleaseAsModal(TForm * Form, void *& Storage)
bool __fastcall ReleaseAsModal(TForm * Form, void *& Storage)
{
if (Storage != NULL)
bool Result = (Storage != NULL);
if (Result)
{
HideAsModal(Form, Storage);
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall SelectDirectory(UnicodeString & Path, const UnicodeString Prompt,

View File

@@ -31,7 +31,7 @@ bool __fastcall HasLabelHintPopup(TLabel * Label, const UnicodeString & HintStr)
void __fastcall FixComboBoxResizeBug(TCustomComboBox * ComboBox);
void __fastcall ShowAsModal(TForm * Form, void *& Storage, bool BringToFront = true);
void __fastcall HideAsModal(TForm * Form, void *& Storage);
void __fastcall ReleaseAsModal(TForm * Form, void *& Storage);
bool __fastcall ReleaseAsModal(TForm * Form, void *& Storage);
TImageList * __fastcall SharedSystemImageList(bool Large);
bool __fastcall SelectDirectory(UnicodeString & Path, const UnicodeString Prompt,
bool PreserveFileName);