mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
Add Authenticating event to allow capture of any banner text if exists that need to be presented to the user and to provide other authentication related information
Refactor userauthentication proccess and add support for "keyboard-interactive" authentication method
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Renci.SshClient.Messages.Authentication;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
public class AuthenticationEventArgs : EventArgs
|
||||
{
|
||||
public string BannerMessage { get; private set; }
|
||||
|
||||
public string Language { get; private set; }
|
||||
|
||||
public string Instruction { get; private set; }
|
||||
|
||||
public IEnumerable<AuthenticationPrompt> Prompts { get; private set; }
|
||||
|
||||
public AuthenticationEventArgs(string message, string language)
|
||||
{
|
||||
this.BannerMessage = message;
|
||||
this.Language = language;
|
||||
}
|
||||
|
||||
public AuthenticationEventArgs(string instruction, string language, IEnumerable<AuthenticationPrompt> prompts)
|
||||
{
|
||||
this.Instruction = instruction;
|
||||
this.Language = language;
|
||||
this.Prompts = prompts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
public class AuthenticationPrompt
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user input should be echoed as characters are typed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the user input should be echoed as characters are typed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsEchoed { get; private set; }
|
||||
|
||||
public string Request { get; private set; }
|
||||
|
||||
public string Response { get; set; }
|
||||
|
||||
public AuthenticationPrompt(int id, bool isEchoed, string request)
|
||||
{
|
||||
this.Id = id;
|
||||
this.IsEchoed = isEchoed;
|
||||
this.Request = request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
[Serializable]
|
||||
public class SshAuthenticationException : SshException
|
||||
{
|
||||
public SshAuthenticationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-16
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Renci.SshClient.Common;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Renci.SshClient.Messages.Authentication
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
public IEnumerable<PromptEcho> Prompts { get; set; }
|
||||
public IEnumerable<AuthenticationPrompt> Prompts { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
@@ -26,16 +27,14 @@ namespace Renci.SshClient.Messages.Authentication
|
||||
this.Language = this.ReadString();
|
||||
|
||||
var numOfPrompts = this.ReadUInt32();
|
||||
var prompts = new List<PromptEcho>();
|
||||
var prompts = new List<AuthenticationPrompt>();
|
||||
|
||||
for (int i = 0; i < numOfPrompts; i++)
|
||||
{
|
||||
prompts.Add(new PromptEcho
|
||||
{
|
||||
Prompt = this.ReadString(),
|
||||
Echo = this.ReadBoolean(),
|
||||
});
|
||||
}
|
||||
{
|
||||
var prompt = this.ReadString();
|
||||
var echo = this.ReadBoolean();
|
||||
prompts.Add(new AuthenticationPrompt(i, echo, prompt));
|
||||
}
|
||||
|
||||
this.Prompts = prompts;
|
||||
}
|
||||
@@ -44,12 +43,5 @@ namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public class PromptEcho
|
||||
{
|
||||
public string Prompt { get; set; }
|
||||
|
||||
public bool Echo { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
@@ -9,6 +10,13 @@ namespace Renci.SshClient.Messages.Authentication
|
||||
get { return MessageTypes.UserAuthenticationInformationResponse; }
|
||||
}
|
||||
|
||||
public IList<string> Responses { get; private set; }
|
||||
|
||||
public InformationResponseMessage()
|
||||
{
|
||||
this.Responses = new List<string>();
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -16,7 +24,11 @@ namespace Renci.SshClient.Messages.Authentication
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
this.Write((UInt32)this.Responses.Count);
|
||||
foreach (var response in this.Responses)
|
||||
{
|
||||
this.Write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class RequestMessageKeyboardInteractive : RequestMessage
|
||||
{
|
||||
public override string MethodName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "keyboard-interactive";
|
||||
}
|
||||
}
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
public string SubMethods { get; set; }
|
||||
|
||||
public RequestMessageKeyboardInteractive()
|
||||
{
|
||||
this.Language = string.Empty;
|
||||
this.SubMethods = string.Empty;
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
this.Write(this.Language);
|
||||
|
||||
this.Write(this.SubMethods, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PasswordRequestMessage : RequestMessage
|
||||
internal class RequestMessagePassword : RequestMessage
|
||||
{
|
||||
public override string MethodName
|
||||
{
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PublicKeyRequestMessage : RequestMessage
|
||||
internal class RequestMessagePublicKey : RequestMessage
|
||||
{
|
||||
public override string MethodName
|
||||
{
|
||||
@@ -60,13 +60,17 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Common\AuthenticationEventArgs.cs" />
|
||||
<Compile Include="Common\AuthenticationPrompt.cs" />
|
||||
<Compile Include="Common\ChannelDataEventArgs.cs" />
|
||||
<Compile Include="Common\ChannelEventArgs.cs" />
|
||||
<Compile Include="Common\ChannelOpenFailedEventArgs.cs" />
|
||||
<Compile Include="Common\ChannelRequestEventArgs.cs" />
|
||||
<Compile Include="Common\ConnectingEventArgs.cs" />
|
||||
<Compile Include="Common\SshAuthenticationException.cs" />
|
||||
<Compile Include="Common\SshConnectionException.cs" />
|
||||
<Compile Include="Common\SshOperationTimeoutException.cs" />
|
||||
<Compile Include="Messages\Authentication\RequestMessageKeyboardInteractive.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpen\ChannelOpenInfo.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpen\DirectTcpipChannelInfo.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs" />
|
||||
@@ -99,6 +103,15 @@
|
||||
</Compile>
|
||||
<Compile Include="Security\KeyExchangeDiffieHellmanGroup14Sha1.cs" />
|
||||
<Compile Include="Security\KeyExchangeDiffieHellmanGroupExchangeSha1.cs" />
|
||||
<Compile Include="Security\UserAuthenticationKeyboardInteractive.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Security\UserAuthenticationPassword.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Security\UserAuthenticationPublicKey.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="SftpClient.cs" />
|
||||
<Compile Include="Sftp\CreateDirectoryCommand.cs" />
|
||||
<Compile Include="Sftp\DownloadFileCommand.cs" />
|
||||
@@ -187,10 +200,7 @@
|
||||
<Compile Include="PrivateKeyFile.cs" />
|
||||
<Compile Include="Messages\Connection\ChannelMessage.cs" />
|
||||
<Compile Include="Security\UserAuthentication.cs" />
|
||||
<Compile Include="Security\UserAuthenticationHost.cs" />
|
||||
<Compile Include="Security\UserAuthenticationNone.cs" />
|
||||
<Compile Include="Security\UserAuthenticationPassword.cs" />
|
||||
<Compile Include="Security\UserAuthenticationPublicKey.cs" />
|
||||
<Compile Include="Channels\Channel.cs" />
|
||||
<Compile Include="Channels\ChannelTypes.cs" />
|
||||
<Compile Include="ConnectionInfo.cs" />
|
||||
@@ -246,9 +256,9 @@
|
||||
<Compile Include="Messages\Authentication\InformationResponseMessage.cs" />
|
||||
<Compile Include="Messages\Authentication\Methods.cs" />
|
||||
<Compile Include="Messages\Authentication\PasswordChangeRequiredMessage.cs" />
|
||||
<Compile Include="Messages\Authentication\PasswordRequestMessage.cs" />
|
||||
<Compile Include="Messages\Authentication\RequestMessagePassword.cs" />
|
||||
<Compile Include="Messages\Authentication\PublicKeyMessage.cs" />
|
||||
<Compile Include="Messages\Authentication\PublicKeyRequestMessage.cs" />
|
||||
<Compile Include="Messages\Authentication\RequestMessagePublicKey.cs" />
|
||||
<Compile Include="Messages\Authentication\RequestMessage.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@@ -259,6 +269,7 @@
|
||||
<Compile Include="Common\SshData.cs" />
|
||||
<Compile Include="Shell.cs" />
|
||||
<Compile Include="SshClient.cs" />
|
||||
<Compile Include="UserAuthentication.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Authentication;
|
||||
using System;
|
||||
using Renci.SshClient.Common;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
@@ -11,32 +13,28 @@ namespace Renci.SshClient.Security
|
||||
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
public event EventHandler<AuthenticationEventArgs> Authenticating;
|
||||
|
||||
protected Session Session { get; private set; }
|
||||
|
||||
public void Init(Session session)
|
||||
{
|
||||
this.Session = session;
|
||||
}
|
||||
protected string Username { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes this instance.
|
||||
/// </summary>
|
||||
/// <returns>true if method was execute; otherwise false.</returns>
|
||||
public bool Execute()
|
||||
public bool Authenticate(string username, Session session)
|
||||
{
|
||||
this.Username = username;
|
||||
this.Session = session;
|
||||
|
||||
this.Session.RegisterMessageType<FailureMessage>(MessageTypes.UserAuthenticationFailure);
|
||||
this.Session.RegisterMessageType<SuccessMessage>(MessageTypes.UserAuthenticationSuccess);
|
||||
this.Session.RegisterMessageType<BannerMessage>(MessageTypes.UserAuthenticationBanner);
|
||||
|
||||
this.Session.UserAuthenticationRequestReceived += Session_UserAuthenticationRequestMessageReceived;
|
||||
this.Session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
|
||||
this.Session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessMessageReceived;
|
||||
this.Session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerMessageReceived;
|
||||
this.Session.MessageReceived += Session_MessageReceived;
|
||||
|
||||
var result = this.Run();
|
||||
this.OnAuthenticate();
|
||||
|
||||
this.Session.UserAuthenticationRequestReceived -= Session_UserAuthenticationRequestMessageReceived;
|
||||
this.Session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
|
||||
this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessMessageReceived;
|
||||
this.Session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerMessageReceived;
|
||||
@@ -46,12 +44,10 @@ namespace Renci.SshClient.Security
|
||||
this.Session.UnRegisterMessageType(MessageTypes.UserAuthenticationSuccess);
|
||||
this.Session.UnRegisterMessageType(MessageTypes.UserAuthenticationBanner);
|
||||
|
||||
return result;
|
||||
return this.IsAuthenticated;
|
||||
}
|
||||
|
||||
protected virtual void Session_UserAuthenticationRequestMessageReceived(object sender, MessageEventArgs<RequestMessage> e)
|
||||
{
|
||||
}
|
||||
protected abstract void OnAuthenticate();
|
||||
|
||||
protected virtual void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
|
||||
{
|
||||
@@ -66,34 +62,20 @@ namespace Renci.SshClient.Security
|
||||
|
||||
protected virtual void Session_UserAuthenticationBannerMessageReceived(object sender, MessageEventArgs<BannerMessage> e)
|
||||
{
|
||||
RaiseAuthenticating(new AuthenticationEventArgs(e.Message.Message, e.Message.Language));
|
||||
}
|
||||
|
||||
protected void RaiseAuthenticating(AuthenticationEventArgs args)
|
||||
{
|
||||
if (this.Authenticating != null)
|
||||
{
|
||||
this.Authenticating(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
|
||||
{
|
||||
//dynamic message = e.Message;
|
||||
//this.HandleMessage(message);
|
||||
}
|
||||
|
||||
|
||||
protected abstract bool Run();
|
||||
|
||||
//protected abstract void HandleMessage<T>(T message) where T : Message;
|
||||
|
||||
//protected virtual void HandleMessage(SuccessMessage message)
|
||||
//{
|
||||
// this.IsAuthenticated = true;
|
||||
//}
|
||||
|
||||
//protected virtual void HandleMessage(FailureMessage message)
|
||||
//{
|
||||
// this.ErrorMessage = message.Message;
|
||||
// this.IsAuthenticated = false;
|
||||
//}
|
||||
|
||||
//private void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
|
||||
//{
|
||||
// dynamic message = e.Message;
|
||||
// this.HandleMessage(message);
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
internal class UserAuthenticationHost : UserAuthentication
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "hostbased";
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool Run()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Authentication;
|
||||
using Renci.SshClient.Common;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
internal class UserAuthenticationKeyboardInteractive : UserAuthentication, IDisposable
|
||||
{
|
||||
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
|
||||
|
||||
private Exception _exception;
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "keyboard-interactive";
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAuthenticate()
|
||||
{
|
||||
this.Session.RegisterMessageType<InformationRequestMessage>(MessageTypes.UserAuthenticationInformationRequest);
|
||||
|
||||
this.Session.SendMessage(new RequestMessageKeyboardInteractive
|
||||
{
|
||||
ServiceName = ServiceNames.Connection,
|
||||
Username = this.Session.ConnectionInfo.Username,
|
||||
});
|
||||
|
||||
this.Session.WaitHandle(this._authenticationCompleted);
|
||||
|
||||
this.Session.UnRegisterMessageType(MessageTypes.UserAuthenticationInformationRequest);
|
||||
|
||||
if (this._exception != null)
|
||||
{
|
||||
throw this._exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
|
||||
{
|
||||
base.Session_UserAuthenticationSuccessMessageReceived(sender, e);
|
||||
this._authenticationCompleted.Set();
|
||||
}
|
||||
|
||||
protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
|
||||
{
|
||||
base.Session_UserAuthenticationFailureReceived(sender, e);
|
||||
this._authenticationCompleted.Set();
|
||||
}
|
||||
|
||||
protected override void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
|
||||
{
|
||||
var informationRequestMessage = e.Message as InformationRequestMessage;
|
||||
if (informationRequestMessage != null)
|
||||
{
|
||||
var eventArgs = new AuthenticationEventArgs(informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts);
|
||||
|
||||
var eventTask = Task.Factory.StartNew(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
this.RaiseAuthenticating(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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private bool isDisposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Note disposing has been done.
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~UserAuthenticationKeyboardInteractive()
|
||||
{
|
||||
// Do not re-create Dispose clean-up code here.
|
||||
// Calling Dispose(false) is optimal in terms of
|
||||
// readability and maintainability.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Authentication;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
@@ -14,17 +15,17 @@ namespace Renci.SshClient.Security
|
||||
get { return "none"; }
|
||||
}
|
||||
|
||||
protected override bool Run()
|
||||
public IEnumerable<string> Methods { get; private set; }
|
||||
|
||||
protected override void OnAuthenticate()
|
||||
{
|
||||
this.Session.SendMessage(new RequestMessage
|
||||
{
|
||||
ServiceName = ServiceNames.Connection,
|
||||
Username = this.Session.ConnectionInfo.Username,
|
||||
Username = this.Username,
|
||||
});
|
||||
|
||||
this.Session.WaitHandle(this._authenticationCompleted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
|
||||
@@ -36,6 +37,7 @@ namespace Renci.SshClient.Security
|
||||
protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
|
||||
{
|
||||
base.Session_UserAuthenticationFailureReceived(sender, e);
|
||||
this.Methods = e.Message.AllowedAuthentications;
|
||||
this._authenticationCompleted.Set();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,24 +17,19 @@ namespace Renci.SshClient.Security
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool Run()
|
||||
protected override void OnAuthenticate()
|
||||
{
|
||||
// TODO: Handle all user authentication messages
|
||||
//Message.RegisterMessageType<PasswordChangeRequiredMessage>(MessageTypes.UserAuthenticationPasswordChangeRequired);
|
||||
|
||||
if (string.IsNullOrEmpty(this.Session.ConnectionInfo.Password))
|
||||
return false;
|
||||
|
||||
this.Session.SendMessage(new PasswordRequestMessage
|
||||
this.Session.SendMessage(new RequestMessagePassword
|
||||
{
|
||||
ServiceName = ServiceNames.Connection,
|
||||
Username = this.Session.ConnectionInfo.Username,
|
||||
Password = this.Session.ConnectionInfo.Password,
|
||||
Username = this.Username,
|
||||
Password = this.Session.ConnectionInfo.Password ?? string.Empty,
|
||||
});
|
||||
|
||||
this.Session.WaitHandle(this._authenticationCompleted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
|
||||
|
||||
@@ -20,10 +20,10 @@ namespace Renci.SshClient.Security
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool Run()
|
||||
protected override void OnAuthenticate()
|
||||
{
|
||||
if (this.Session.ConnectionInfo.KeyFiles == null)
|
||||
return false;
|
||||
return;
|
||||
|
||||
this.Session.RegisterMessageType<PublicKeyMessage>(MessageTypes.UserAuthenticationPublicKey);
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Renci.SshClient.Security
|
||||
this._publicKeyRequestMessageResponseWaitHandle.Reset();
|
||||
this._isSignatureRequired = false;
|
||||
|
||||
var message = new PublicKeyRequestMessage
|
||||
var message = new RequestMessagePublicKey
|
||||
{
|
||||
ServiceName = ServiceNames.Connection,
|
||||
Username = this.Session.ConnectionInfo.Username,
|
||||
@@ -57,7 +57,7 @@ namespace Renci.SshClient.Security
|
||||
{
|
||||
this._publicKeyRequestMessageResponseWaitHandle.Reset();
|
||||
|
||||
var signatureMessage = new PublicKeyRequestMessage
|
||||
var signatureMessage = new RequestMessagePublicKey
|
||||
{
|
||||
ServiceName = ServiceNames.Connection,
|
||||
Username = this.Session.ConnectionInfo.Username,
|
||||
@@ -82,13 +82,12 @@ namespace Renci.SshClient.Security
|
||||
}
|
||||
|
||||
this.Session.UnRegisterMessageType(MessageTypes.UserAuthenticationPublicKey);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
|
||||
{
|
||||
base.Session_UserAuthenticationSuccessMessageReceived(sender, e);
|
||||
|
||||
this._publicKeyRequestMessageResponseWaitHandle.Set();
|
||||
}
|
||||
|
||||
@@ -112,12 +111,11 @@ namespace Renci.SshClient.Security
|
||||
|
||||
private class SignatureData : SshData
|
||||
{
|
||||
|
||||
private PublicKeyRequestMessage _message;
|
||||
private RequestMessagePublicKey _message;
|
||||
|
||||
private string _sessionId;
|
||||
|
||||
public SignatureData(PublicKeyRequestMessage message, string sessionId)
|
||||
public SignatureData(RequestMessagePublicKey message, string sessionId)
|
||||
{
|
||||
this._message = message;
|
||||
this._sessionId = sessionId;
|
||||
|
||||
@@ -228,6 +228,11 @@ namespace Renci.SshClient
|
||||
|
||||
public event EventHandler<EventArgs> Disconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when user is being authenticated and additional information available or required
|
||||
/// </summary>
|
||||
public event EventHandler<AuthenticationEventArgs> Authenticating;
|
||||
|
||||
#region Message events
|
||||
|
||||
/// <summary>
|
||||
@@ -444,7 +449,10 @@ namespace Renci.SshClient
|
||||
{"none", typeof(UserAuthenticationNone).AssemblyQualifiedName},
|
||||
{"publickey", typeof(UserAuthenticationPublicKey).AssemblyQualifiedName},
|
||||
{"password", typeof(UserAuthenticationPassword).AssemblyQualifiedName},
|
||||
{"keyboard-interactive", typeof(UserAuthenticationKeyboardInteractive).AssemblyQualifiedName},
|
||||
//{"hostbased", typeof(...).AssemblyQualifiedName},
|
||||
//{"gssapi-keyex", typeof(...).AssemblyQualifiedName},
|
||||
//{"gssapi-with-mic", typeof(...).AssemblyQualifiedName},
|
||||
};
|
||||
|
||||
this.CompressionAlgorithms = new Dictionary<string, string>()
|
||||
@@ -531,7 +539,7 @@ namespace Renci.SshClient
|
||||
{
|
||||
throw new InvalidOperationException("Server string is null or empty.");
|
||||
}
|
||||
|
||||
|
||||
versionMatch = _serverVersionRe.Match(this.ServerVersion);
|
||||
|
||||
if (versionMatch.Success)
|
||||
@@ -595,33 +603,50 @@ namespace Renci.SshClient
|
||||
{
|
||||
throw new SshException("Username is not specified.");
|
||||
}
|
||||
|
||||
|
||||
// This implementation will ignore supported by server methods and will try to authenticated user using method supported by the client.
|
||||
string errorMessage = null; // Hold last authentication error if any
|
||||
foreach (var methodName in this.SupportedAuthenticationMethods.Keys)
|
||||
// Query server supported authentication methods
|
||||
var username = this.ConnectionInfo.Username;
|
||||
IEnumerable<string> serverMethods = null;
|
||||
using (var noneAuthentication = new UserAuthenticationNone())
|
||||
{
|
||||
var userAuthentication = this.SupportedAuthenticationMethods[methodName].CreateInstance<UserAuthentication>();
|
||||
|
||||
userAuthentication.Init(this);
|
||||
|
||||
if (userAuthentication.Execute())
|
||||
if (noneAuthentication.Authenticate(username, this))
|
||||
{
|
||||
if (userAuthentication.IsAuthenticated)
|
||||
throw new SshAuthenticationException("'none' authentication should not be allowed.");
|
||||
}
|
||||
|
||||
serverMethods = noneAuthentication.Methods;
|
||||
}
|
||||
|
||||
var methodNames = from serverMethod in serverMethods
|
||||
from clientMethod in this.SupportedAuthenticationMethods.Keys
|
||||
where
|
||||
serverMethod == clientMethod
|
||||
select serverMethod;
|
||||
|
||||
foreach (var methodName in methodNames)
|
||||
{
|
||||
var authentication = this.SupportedAuthenticationMethods[methodName].CreateInstance<UserAuthentication>();
|
||||
|
||||
authentication.Authenticating += delegate(object sender, AuthenticationEventArgs e)
|
||||
{
|
||||
if (this.Authenticating != null)
|
||||
{
|
||||
this._isAuthenticated = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = userAuthentication.ErrorMessage;
|
||||
this.Authenticating(sender, e);
|
||||
}
|
||||
};
|
||||
|
||||
authentication.Authenticate(username, this);
|
||||
|
||||
if (authentication.IsAuthenticated)
|
||||
{
|
||||
this._isAuthenticated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._isAuthenticated)
|
||||
{
|
||||
throw new AuthenticationException(errorMessage ?? "User cannot be authenticated.");
|
||||
throw new SshAuthenticationException("User cannot be authenticated.");
|
||||
}
|
||||
|
||||
Monitor.Pulse(this);
|
||||
|
||||
@@ -43,6 +43,8 @@ namespace Renci.SshClient
|
||||
/// </summary>
|
||||
public event EventHandler<ConnectingEventArgs> Connecting;
|
||||
|
||||
public event EventHandler<AuthenticationEventArgs> Authenticating;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SshBaseClient"/> class.
|
||||
/// </summary>
|
||||
@@ -67,6 +69,7 @@ namespace Renci.SshClient
|
||||
|
||||
this.Session = new Session(this.ConnectionInfo);
|
||||
this.Session.Connecting += Session_Connecting;
|
||||
this.Session.Authenticating += Session_Authenticating;
|
||||
this.Session.Connect();
|
||||
|
||||
this.OnConnected();
|
||||
@@ -81,6 +84,7 @@ namespace Renci.SshClient
|
||||
|
||||
this.Session.Disconnect();
|
||||
this.Session.Connecting -= Session_Connecting;
|
||||
this.Session.Authenticating -= Session_Authenticating;
|
||||
|
||||
this.OnDisconnected();
|
||||
}
|
||||
@@ -133,6 +137,14 @@ namespace Renci.SshClient
|
||||
}
|
||||
}
|
||||
|
||||
private void Session_Authenticating(object sender, AuthenticationEventArgs e)
|
||||
{
|
||||
if (this.Authenticating != null)
|
||||
{
|
||||
this.Authenticating(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class UserAuthentication2
|
||||
{
|
||||
private Session _session;
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
internal UserAuthentication2(Session session)
|
||||
{
|
||||
this._session = session;
|
||||
this.Username = session.ConnectionInfo.Username;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user