diff --git a/Renci.SshClient/Renci.SshClient/Common/AuthenticationBannerEventArgs.cs b/Renci.SshClient/Renci.SshClient/Common/AuthenticationBannerEventArgs.cs new file mode 100644 index 00000000..d5b978ea --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Common/AuthenticationBannerEventArgs.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshClient.Common +{ + public class AuthenticationBannerEventArgs : AuthenticationEventArgs + { + public string BannerMessage { get; private set; } + + public string Language { get; private set; } + + public AuthenticationBannerEventArgs(string username, string message, string language) + : base(username) + { + this.BannerMessage = message; + this.Language = language; + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Common/AuthenticationEventArgs.cs b/Renci.SshClient/Renci.SshClient/Common/AuthenticationEventArgs.cs index c3553e3b..49ea69a4 100644 --- a/Renci.SshClient/Renci.SshClient/Common/AuthenticationEventArgs.cs +++ b/Renci.SshClient/Renci.SshClient/Common/AuthenticationEventArgs.cs @@ -6,27 +6,13 @@ using Renci.SshClient.Messages.Authentication; namespace Renci.SshClient.Common { - public class AuthenticationEventArgs : EventArgs + public abstract class AuthenticationEventArgs : EventArgs { - public string BannerMessage { get; private set; } + public string Username { get; private set; } - public string Language { get; private set; } - - public string Instruction { get; private set; } - - public IEnumerable Prompts { get; private set; } - - public AuthenticationEventArgs(string message, string language) + public AuthenticationEventArgs(string username) { - this.BannerMessage = message; - this.Language = language; - } - - public AuthenticationEventArgs(string instruction, string language, IEnumerable prompts) - { - this.Instruction = instruction; - this.Language = language; - this.Prompts = prompts; + this.Username = username; } } } diff --git a/Renci.SshClient/Renci.SshClient/Common/AuthenticationPasswordChangeEventArgs.cs b/Renci.SshClient/Renci.SshClient/Common/AuthenticationPasswordChangeEventArgs.cs new file mode 100644 index 00000000..494839aa --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Common/AuthenticationPasswordChangeEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshClient.Common +{ + public class AuthenticationPasswordChangeEventArgs : AuthenticationEventArgs + { + public string NewPassword { get; set; } + + public AuthenticationPasswordChangeEventArgs(string username) + : base(username) + { + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Common/AuthenticationPromptEventArgs.cs b/Renci.SshClient/Renci.SshClient/Common/AuthenticationPromptEventArgs.cs new file mode 100644 index 00000000..66dfa1a8 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Common/AuthenticationPromptEventArgs.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshClient.Common +{ + public class AuthenticationPromptEventArgs : AuthenticationEventArgs + { + public string Language { get; private set; } + + public string Instruction { get; private set; } + + public IEnumerable Prompts { get; private set; } + + public AuthenticationPromptEventArgs(string username, string instruction, string language, IEnumerable prompts) + : base(username) + { + this.Instruction = instruction; + this.Language = language; + this.Prompts = prompts; + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj index 037a6f1a..5f4948a4 100644 --- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj +++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj @@ -60,7 +60,10 @@ + + + diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs index 0650ea3f..d9ae72e7 100644 --- a/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs @@ -63,7 +63,7 @@ namespace Renci.SshClient.Security protected virtual void Session_UserAuthenticationBannerMessageReceived(object sender, MessageEventArgs e) { - RaiseAuthenticating(new AuthenticationEventArgs(e.Message.Message, e.Message.Language)); + RaiseAuthenticating(new AuthenticationBannerEventArgs(this.Username, e.Message.Message, e.Message.Language)); } protected void RaiseAuthenticating(AuthenticationEventArgs args) diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationKeyboardInteractive.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationKeyboardInteractive.cs index 0bd36b04..a0f7c66c 100644 --- a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationKeyboardInteractive.cs +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationKeyboardInteractive.cs @@ -59,7 +59,7 @@ namespace Renci.SshClient.Security var informationRequestMessage = e.Message as InformationRequestMessage; if (informationRequestMessage != null) { - var eventArgs = new AuthenticationEventArgs(informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); + var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); var eventTask = Task.Factory.StartNew(() => { diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs index 3cbb3952..e16bf547 100644 --- a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs @@ -2,6 +2,8 @@ using System.Threading; using Renci.SshClient.Messages; using Renci.SshClient.Messages.Authentication; +using System.Threading.Tasks; +using Renci.SshClient.Common; namespace Renci.SshClient.Security { @@ -9,6 +11,10 @@ namespace Renci.SshClient.Security { private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + private Exception _exception; + + private PasswordConnectionInfo _connectionInfo; + public override string Name { get @@ -19,22 +25,26 @@ namespace Renci.SshClient.Security protected override void OnAuthenticate() { - var passwordConnectionInfo = this.Session.ConnectionInfo as PasswordConnectionInfo; + this._connectionInfo = this.Session.ConnectionInfo as PasswordConnectionInfo; - if (passwordConnectionInfo == null) + if (this._connectionInfo == null) return; - - // TODO: Handle PasswordChangeRequiredMessage authentication message - //Message.RegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired); + + this.Session.RegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired); this.SendMessage(new RequestMessagePassword { ServiceName = ServiceNames.Connection, Username = this.Username, - Password = passwordConnectionInfo.Password ?? string.Empty, + Password = this._connectionInfo.Password ?? string.Empty, }); this.WaitHandle(this._authenticationCompleted); + + if (this._exception != null) + { + throw this._exception; + } } protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) @@ -49,6 +59,41 @@ namespace Renci.SshClient.Security this._authenticationCompleted.Set(); } + protected override void Session_MessageReceived(object sender, MessageEventArgs e) + { + base.Session_MessageReceived(sender, e); + + if (e.Message is PasswordChangeRequiredMessage) + { + this.Session.UnRegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired); + + var eventTask = Task.Factory.StartNew(() => + { + try + { + var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username); + + // Raise an event to allow user to supply a new password + this.RaiseAuthenticating(eventArgs); + + // Send new authentication request with new password + this.SendMessage(new RequestMessagePassword + { + ServiceName = ServiceNames.Connection, + Username = this.Username, + Password = this._connectionInfo.Password ?? string.Empty, + NewPassword = eventArgs.NewPassword ?? string.Empty, + }); + } + catch (Exception exp) + { + this._exception = exp; + this._authenticationCompleted.Set(); + } + }); + } + } + #region IDisposable Members private bool isDisposed = false;