using System; using System.Linq; using System.Threading; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Common; namespace Renci.SshNet { /// /// Provides functionality to perform keyboard interactive authentication. /// public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable { private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; private Session _session; private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); private Exception _exception; private readonly RequestMessage _requestMessage; /// /// Gets authentication method name /// public override string Name { get { return this._requestMessage.MethodName; } } /// /// Occurs when server prompts for more authentication information. /// public event EventHandler AuthenticationPrompt; /// /// Initializes a new instance of the class. /// /// The username. /// is whitespace or null. public KeyboardInteractiveAuthenticationMethod(string username) : base(username) { this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username); } /// /// Authenticates the specified session. /// /// The session to authenticate. /// Result of authentication process. public override AuthenticationResult Authenticate(Session session) { this._session = session; session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; session.MessageReceived += Session_MessageReceived; session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); session.SendMessage(this._requestMessage); session.WaitOnHandle(this._authenticationCompleted); session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; session.MessageReceived -= Session_MessageReceived; if (this._exception != null) { throw this._exception; } return this._authenticationResult; } private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) { this._authenticationResult = AuthenticationResult.Success; this._authenticationCompleted.Set(); } private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) { if (e.Message.PartialSuccess) this._authenticationResult = AuthenticationResult.PartialSuccess; else this._authenticationResult = AuthenticationResult.Failure; // Copy allowed authentication methods this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); this._authenticationCompleted.Set(); } private void Session_MessageReceived(object sender, MessageEventArgs e) { var informationRequestMessage = e.Message as InformationRequestMessage; if (informationRequestMessage != null) { var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); this.ExecuteThread(() => { try { if (this.AuthenticationPrompt != null) { this.AuthenticationPrompt(this, eventArgs); } var informationResponse = new InformationResponseMessage(); foreach (var response in from r in eventArgs.Prompts orderby r.Id ascending select r.Response) { informationResponse.Responses.Add(response); } // Send information response message this._session.SendMessage(informationResponse); } catch (Exception exp) { this._exception = exp; this._authenticationCompleted.Set(); } }); } } partial void ExecuteThread(Action action); #region IDisposable Members private bool _isDisposed; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this._isDisposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. if (this._authenticationCompleted != null) { this._authenticationCompleted.Dispose(); this._authenticationCompleted = null; } } // Note disposing has been done. _isDisposed = true; } } /// /// Releases unmanaged resources and performs other cleanup operations before the /// is reclaimed by garbage collection. /// ~KeyboardInteractiveAuthenticationMethod() { // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } #endregion } }