Add support for multiple authentication methods

Minor comment fixes
This commit is contained in:
olegkap_cp
2012-02-28 19:00:57 +00:00
parent 62ae94d39a
commit bc2fb5bedc
23 changed files with 1191 additions and 579 deletions
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Renci.SshNet
{
public partial class KeyboardInteractiveAuthenticationMethod
{
partial void ExecuteThread(Action action)
{
ThreadPool.QueueUserWorkItem((o) => { action(); });
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Renci.SshNet
{
public partial class PasswordAuthenticationMethod
{
partial void ExecuteThread(Action action)
{
ThreadPool.QueueUserWorkItem((o) => { action(); });
}
}
}
@@ -39,6 +39,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Renci.SshNet\AuthenticationMethod.cs">
<Link>AuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\AuthenticationResult.cs">
<Link>AuthenticationResult.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\BaseClient.cs">
<Link>BaseClient.cs</Link>
</Compile>
@@ -237,6 +243,9 @@
<Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
<Link>ForwardedPortRemote.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveAuthenticationMethod.cs">
<Link>KeyboardInteractiveAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveConnectionInfo.cs">
<Link>KeyboardInteractiveConnectionInfo.cs</Link>
</Compile>
@@ -456,12 +465,18 @@
<Compile Include="..\Renci.SshNet\Netconf\NetConfSession.cs">
<Link>Netconf\NetConfSession.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\NoneConnectionInfo.cs">
<Link>NoneConnectionInfo.cs</Link>
<Compile Include="..\Renci.SshNet\NoneAuthenticationMethod.cs">
<Link>NoneAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordAuthenticationMethod.cs">
<Link>PasswordAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordConnectionInfo.cs">
<Link>PasswordConnectionInfo.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PrivateKeyAuthenticationMethod.cs">
<Link>PrivateKeyAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PrivateKeyConnectionInfo.cs">
<Link>PrivateKeyConnectionInfo.cs</Link>
</Compile>
@@ -752,8 +767,8 @@
<Compile Include="ForwardedPortDynamic.NET35.cs" />
<Compile Include="ForwardedPortLocal.NET35.cs" />
<Compile Include="ForwardedPortRemote.NET35.cs" />
<Compile Include="KeyboardInteractiveConnectionInfo.NET35.cs" />
<Compile Include="PasswordConnectionInfo.NET35.cs" />
<Compile Include="KeyboardInteractiveAuthenticationMethod.NET35.cs" />
<Compile Include="PasswordAuthenticationMethod.NET35.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Session.NET35.cs" />
<Compile Include="SftpClient.NET35.cs" />
@@ -763,7 +778,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -0,0 +1,22 @@
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
namespace Renci.SshNet
{
public partial class KeyboardInteractiveAuthenticationMethod
{
partial void ExecuteThread(Action action)
{
ThreadPool.QueueUserWorkItem((o) => { action(); });
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
namespace Renci.SshNet
{
public partial class PasswordAuthenticationMethod
{
partial void ExecuteThread(Action action)
{
ThreadPool.QueueUserWorkItem((o) => { action(); });
}
}
}
@@ -56,6 +56,12 @@
<Reference Include="System.Windows.Browser" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Renci.SshNet\AuthenticationMethod.cs">
<Link>AuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\AuthenticationResult.cs">
<Link>AuthenticationResult.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\BaseClient.cs">
<Link>BaseClient.cs</Link>
</Compile>
@@ -215,6 +221,9 @@
<Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
<Link>ForwardedPortRemote.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveAuthenticationMethod.cs">
<Link>KeyboardInteractiveAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveConnectionInfo.cs">
<Link>KeyboardInteractiveConnectionInfo.cs</Link>
</Compile>
@@ -428,12 +437,18 @@
<Compile Include="..\Renci.SshNet\Messages\Transport\UnimplementedMessage.cs">
<Link>Messages\Transport\UnimplementedMessage.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\NoneConnectionInfo.cs">
<Link>NoneConnectionInfo.cs</Link>
<Compile Include="..\Renci.SshNet\NoneAuthenticationMethod.cs">
<Link>NoneAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordAuthenticationMethod.cs">
<Link>PasswordAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordConnectionInfo.cs">
<Link>PasswordConnectionInfo.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PrivateKeyAuthenticationMethod.cs">
<Link>PrivateKeyAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PrivateKeyConnectionInfo.cs">
<Link>PrivateKeyConnectionInfo.cs</Link>
</Compile>
@@ -716,7 +731,8 @@
<Compile Include="Common\Extensions.SilverlightShared.cs" />
<Compile Include="ForwardedPortLocal.SilverlightShared.cs" />
<Compile Include="ForwardedPortRemote.SilverlightShared.cs" />
<Compile Include="PasswordConnectionInfo.SilverlightShared.cs" />
<Compile Include="KeyboardInteractiveAuthenticationMethod.SilverlightShared.cs" />
<Compile Include="PasswordAuthenticationMethod.SilverlightShared.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScpClient.SilverlightShared.cs" />
<Compile Include="Session.SilverlightBrowser.cs" />
@@ -730,7 +746,7 @@
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
<SilverlightProjectProperties />
</FlavorProperties>
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -49,6 +49,12 @@
<Reference Include="mscorlib.extensions" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Renci.SshNet\AuthenticationMethod.cs">
<Link>AuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\AuthenticationResult.cs">
<Link>AuthenticationResult.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\BaseClient.cs">
<Link>BaseClient.cs</Link>
</Compile>
@@ -211,6 +217,12 @@
<Compile Include="..\Renci.SshNet\ForwardedPortRemote.cs">
<Link>ForwardedPortRemote.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveAuthenticationMethod.cs">
<Link>KeyboardInteractiveAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Silverlight\KeyboardInteractiveAuthenticationMethod.SilverlightShared.cs">
<Link>PasswordAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\KeyboardInteractiveConnectionInfo.cs">
<Link>KeyboardInteractiveConnectionInfo.cs</Link>
</Compile>
@@ -424,14 +436,20 @@
<Compile Include="..\Renci.SshNet\Messages\Transport\UnimplementedMessage.cs">
<Link>Messages\Transport\UnimplementedMessage.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\NoneConnectionInfo.cs">
<Link>NoneConnectionInfo.cs</Link>
<Compile Include="..\Renci.SshNet\NoneAuthenticationMethod.cs">
<Link>NoneAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordAuthenticationMethod.cs">
<Link>PasswordAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Silverlight\PasswordAuthenticationMethod.SilverlightShared.cs">
<Link>PasswordAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PasswordConnectionInfo.cs">
<Link>PasswordConnectionInfo.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Silverlight\PasswordConnectionInfo.SilverlightShared.cs">
<Link>PasswordConnectionInfo.cs</Link>
<Compile Include="..\Renci.SshNet\PrivateKeyAuthenticationMethod.cs">
<Link>PrivateKeyAuthenticationMethod.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\PrivateKeyConnectionInfo.cs">
<Link>PrivateKeyConnectionInfo.cs</Link>
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
namespace Renci.SshNet
{
/// <summary>
/// Base class for all supported authentication methods
/// </summary>
public abstract class AuthenticationMethod
{
/// <summary>
/// Gets authentication method name
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets connection host.
/// </summary>
public string Host { get; private set; }
/// <summary>
/// Gets connection port.
/// </summary>
public int Port { get; private set; }
/// <summary>
/// Gets connection username.
/// </summary>
public string Username { get; private set; }
/// <summary>
/// Gets the authentication error message.
/// </summary>
public string ErrorMessage { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationMethod"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="port">The port.</param>
/// <param name="username">The username.</param>
protected AuthenticationMethod(string host, int port, string username)
{
if (!host.IsValidHost())
throw new ArgumentException("host");
if (!port.IsValidPort())
throw new ArgumentOutOfRangeException("port");
if (username.IsNullOrWhiteSpace())
throw new ArgumentException("username");
this.Host = host;
this.Port = port;
this.Username = username;
}
/// <summary>
/// Authenticates the specified session.
/// </summary>
/// <param name="session">The session to authenticate.</param>
/// <returns></returns>
public abstract AuthenticationResult Authenticate(Session session);
}
}
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Renci.SshNet
{
/// <summary>
/// Represents possible authentication methods results
/// </summary>
public enum AuthenticationResult
{
/// <summary>
/// Authentication was successful.
/// </summary>
Success,
/// <summary>
/// Authentication completed with partial success.
/// </summary>
PartialSuccess,
/// <summary>
/// Authentication failed.
/// </summary>
Failure
}
}
@@ -110,8 +110,10 @@ namespace Renci.SshNet.Channels
/// <param name="rows">The rows.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="terminalMode">The terminal mode.</param>
/// <returns>true if request was successful; otherwise false.</returns>
/// <param name="terminalModeValues">The terminal mode values.</param>
/// <returns>
/// true if request was successful; otherwise false.
/// </returns>
public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, params KeyValuePair<TerminalModes, uint>[] terminalModeValues)
{
this._channelRequestResponse.Reset();
+80 -161
View File
@@ -1,51 +1,25 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Renci.SshNet.Security;
using Renci.SshNet.Compression;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Common;
using System.Threading;
using System.Net;
using Renci.SshNet.Messages.Connection;
using Renci.SshNet.Security.Cryptography.Ciphers;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
using System.Net.Sockets;
using System.Text;
using Renci.SshNet.Security.Cryptography.Ciphers;
using System.Collections.ObjectModel;
using System.Net;
namespace Renci.SshNet
{
/// <summary>
/// Represents remote connection information base class.
/// Represents remote connection information class.
/// </summary>
public abstract partial class ConnectionInfo
public class ConnectionInfo
{
/// <summary>
/// Gets connection name
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets a value indicating whether connection is authenticated.
/// </summary>
/// <value>
/// <c>true</c> if connection is authenticated; otherwise, <c>false</c>.
/// </value>
public bool IsAuthenticated { get; private set; }
/// <summary>
/// Gets the authentication error message.
/// </summary>
public string ErrorMessage { get; private set; }
/// <summary>
/// Gets reference to the session object.
/// </summary>
protected Session Session { get; private set; }
/// <summary>
/// Gets supported key exchange algorithms for this connection.
/// </summary>
@@ -69,7 +43,7 @@ namespace Renci.SshNet
/// <summary>
/// Gets supported authentication methods for this connection.
/// </summary>
public IDictionary<string, Type> AuthenticationMethods { get; private set; }
public IEnumerable<AuthenticationMethod> AuthenticationMethods { get; private set; }
/// <summary>
/// Gets supported compression algorithms for this connection.
@@ -81,6 +55,14 @@ namespace Renci.SshNet
/// </summary>
public IDictionary<string, RequestInfo> ChannelRequests { get; private set; }
/// <summary>
/// Gets a value indicating whether connection is authenticated.
/// </summary>
/// <value>
/// <c>true</c> if connection is authenticated; otherwise, <c>false</c>.
/// </value>
public bool IsAuthenticated { get; private set; }
/// <summary>
/// Gets connection host.
/// </summary>
@@ -194,10 +176,39 @@ namespace Renci.SshNet
public string CurrentClientCompressionAlgorithm { get; internal set; }
/// <summary>
/// Prevents a default instance of the <see cref="ConnectionInfo"/> class from being created.
/// Initializes a new instance of the <see cref="ConnectionInfo"/> class.
/// </summary>
private ConnectionInfo()
/// <param name="host">Connection host.</param>
/// <param name="port">Connection port.</param>
/// <param name="username">Connection username.</param>
/// <param name="proxyType">Type of the proxy.</param>
/// <param name="proxyHost">The proxy host.</param>
/// <param name="proxyPort">The proxy port.</param>
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
/// <param name="authenticationMethods">The authentication methods.</param>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
///
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
///
/// <exception cref="ArgumentException"><paramref name="username"/> is null or empty.</exception>
public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods)
{
if (!host.IsValidHost())
throw new ArgumentException("host");
if (!string.IsNullOrEmpty(proxyHost) && !proxyHost.IsValidHost())
throw new ArgumentException("proxyHost");
if (!port.IsValidPort())
throw new ArgumentOutOfRangeException("port");
if (!proxyPort.IsValidPort())
throw new ArgumentOutOfRangeException("proxyPort");
if (username.IsNullOrWhiteSpace())
throw new ArgumentException("username");
// Set default connection values
this.Timeout = TimeSpan.FromSeconds(30);
this.RetryAttempts = 10;
@@ -260,17 +271,6 @@ namespace Renci.SshNet
//{"pgp-sign-dss", () => { ... },
};
this.AuthenticationMethods = new Dictionary<string, Type>()
{
{"none", typeof(ConnectionInfo)},
{"publickey", typeof(PrivateKeyConnectionInfo)},
{"password", typeof(PasswordConnectionInfo)},
{"keyboard-interactive", typeof(KeyboardInteractiveConnectionInfo)},
//{"hostbased", typeof(...)},
//{"gssapi-keyex", typeof(...)},
//{"gssapi-with-mic", typeof(...)},
};
this.CompressionAlgorithms = new Dictionary<string, Type>()
{
{"none", null},
@@ -294,41 +294,6 @@ namespace Renci.SshNet
{EndOfWriteRequestInfo.NAME, new EndOfWriteRequestInfo()},
{KeepAliveRequestInfo.NAME, new KeepAliveRequestInfo()},
};
}
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionInfo"/> class.
/// </summary>
/// <param name="host">Connection host.</param>
/// <param name="port">Connection port.</param>
/// <param name="username">Connection username.</param>
/// <param name="proxyType">Type of the proxy.</param>
/// <param name="proxyHost">The proxy host.</param>
/// <param name="proxyPort">The proxy port.</param>
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
///
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
///
/// <exception cref="ArgumentException"><paramref name="username"/> is null or empty.</exception>
protected ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword)
: this()
{
if (!host.IsValidHost())
throw new ArgumentException("host");
if (!string.IsNullOrEmpty(proxyHost) && !proxyHost.IsValidHost())
throw new ArgumentException("proxyHost");
if (!port.IsValidPort())
throw new ArgumentOutOfRangeException("port");
if (!proxyPort.IsValidPort())
throw new ArgumentOutOfRangeException("proxyPort");
if (username.IsNullOrWhiteSpace())
throw new ArgumentException("username");
this.Host = host;
this.Port = port;
@@ -339,6 +304,8 @@ namespace Renci.SshNet
this.ProxyPort = proxyPort;
this.ProxyUsername = proxyUsername;
this.ProxyPassword = proxyPassword;
this.AuthenticationMethods = authenticationMethods;
}
/// <summary>
@@ -349,98 +316,50 @@ namespace Renci.SshNet
/// <exception cref="ArgumentNullException"><paramref name="session"/> is null.</exception>
public bool Authenticate(Session session)
{
var authenticated = AuthenticationResult.Failure;
if (session == null)
throw new ArgumentNullException("session");
this.Session = session;
session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
session.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
this.Session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
this.Session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
this.Session.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerReceived;
this.Session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
this.Session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessMessageReceived;
this.Session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerMessageReceived;
this.Session.MessageReceived += Session_MessageReceived;
// Try to authenticate against none
var noneAuthenticationMethod = new NoneAuthenticationMethod(this.Host, this.Port, this.Username);
this.OnAuthenticate();
authenticated = noneAuthenticationMethod.Authenticate(session);
this.Session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessMessageReceived;
this.Session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerMessageReceived;
this.Session.MessageReceived -= Session_MessageReceived;
if (authenticated != AuthenticationResult.Success)
{
foreach (var authenticationMethod in this.AuthenticationMethods.Where((a) => noneAuthenticationMethod.AllowedAuthentications.Contains(a.Name)))
{
authenticated = authenticationMethod.Authenticate(session);
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE");
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER");
if (authenticated == AuthenticationResult.Success)
break;
}
}
return this.IsAuthenticated;
session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerReceived;
session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE");
session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER");
this.IsAuthenticated = authenticated == AuthenticationResult.Success;
return authenticated == AuthenticationResult.Success;
}
/// <summary>
/// Called when connection needs to be authenticated.
/// </summary>
protected abstract void OnAuthenticate();
/// <summary>
/// Sends SSH message to the server.
/// </summary>
/// <param name="message">The message.</param>
protected void SendMessage(Message message)
{
this.Session.SendMessage(message);
}
/// <summary>
/// Waits the handle to signal.
/// </summary>
/// <param name="eventWaitHandle">The event wait handle.</param>
protected void WaitHandle(WaitHandle eventWaitHandle)
{
this.Session.WaitHandle(eventWaitHandle);
}
/// <summary>
/// Handles the UserAuthenticationFailureReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected virtual void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
this.ErrorMessage = e.Message.Message;
this.IsAuthenticated = false;
}
/// <summary>
/// Handles the UserAuthenticationSuccessMessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected virtual void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
this.IsAuthenticated = true;
}
/// <summary>
/// Handles the UserAuthenticationBannerMessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected virtual void Session_UserAuthenticationBannerMessageReceived(object sender, MessageEventArgs<BannerMessage> e)
private void Session_UserAuthenticationBannerReceived(object sender, MessageEventArgs<BannerMessage> e)
{
if (this.AuthenticationBanner != null)
{
this.AuthenticationBanner(this, new AuthenticationBannerEventArgs(this.Username, e.Message.Message, e.Message.Language));
}
}
/// <summary>
/// Handles the MessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected virtual void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
{
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Common;
using System.Threading.Tasks;
namespace Renci.SshNet
{
public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod
{
partial void ExecuteThread(Action action)
{
Task.Factory.StartNew(action);
}
}
}
@@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Common;
namespace Renci.SshNet
{
/// <summary>
/// Provides functionality to perform keyboard interactive authentication.
/// </summary>
public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private Session _session;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
private Exception _exception;
private RequestMessage _requestMessage;
/// <summary>
/// Gets authentication method name
/// </summary>
public override string Name
{
get { return this._requestMessage.MethodName; }
}
/// <summary>
/// Occurs when server prompts for more authentication information.
/// </summary>
public event EventHandler<AuthenticationPromptEventArgs> AuthenticationPrompt;
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardInteractiveAuthenticationMethod"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="port">The port.</param>
/// <param name="username">The username.</param>
public KeyboardInteractiveAuthenticationMethod(string host, int port, string username)
: base(host, port, username)
{
this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username);
}
/// <summary>
/// Authenticates the specified session.
/// </summary>
/// <param name="session">The session to authenticate.</param>
/// <returns></returns>
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.WaitHandle(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<SuccessMessage> e)
{
this._authenticationResult = AuthenticationResult.Success;
this._authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
if (e.Message.PartialSuccess)
this._authenticationResult = AuthenticationResult.PartialSuccess;
else
this._authenticationResult = AuthenticationResult.Failure;
this._authenticationCompleted.Set();
}
private void Session_MessageReceived(object sender, MessageEventArgs<Message> 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 = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~KeyboardInteractiveAuthenticationMethod()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
#endregion
}
}
@@ -2,9 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
using Renci.SshNet.Common;
namespace Renci.SshNet
@@ -12,25 +9,8 @@ namespace Renci.SshNet
/// <summary>
/// Provides connection information when keyboard interactive authentication method is used
/// </summary>
public partial class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
{
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
private Exception _exception;
private RequestMessage _requestMessage;
/// <summary>
/// Gets connection name
/// </summary>
public override string Name
{
get
{
return this._requestMessage.MethodName;
}
}
/// <summary>
/// Occurs when server prompts for more authentication information.
/// </summary>
@@ -142,93 +122,23 @@ namespace Renci.SshNet
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new KeyboardInteractiveAuthenticationMethod(host, port, username))
{
this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username);
foreach (var authenticationMethod in this.AuthenticationMethods.OfType<KeyboardInteractiveAuthenticationMethod>())
{
authenticationMethod.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt;
}
}
/// <summary>
/// Called when connection needs to be authenticated.
/// </summary>
protected override void OnAuthenticate()
private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
this.Session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST");
this.SendMessage(this._requestMessage);
this.WaitHandle(this._authenticationCompleted);
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST");
if (this._exception != null)
if (this.AuthenticationPrompt != null)
{
throw this._exception;
this.AuthenticationPrompt(sender, e);
}
}
/// <summary>
/// Handles the UserAuthenticationSuccessMessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
base.Session_UserAuthenticationSuccessMessageReceived(sender, e);
this._authenticationCompleted.Set();
}
/// <summary>
/// Handles the UserAuthenticationFailureReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
base.Session_UserAuthenticationFailureReceived(sender, e);
this._authenticationCompleted.Set();
}
/// <summary>
/// Handles the MessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_MessageReceived(object sender, MessageEventArgs<Message> 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.SendMessage(informationResponse);
}
catch (Exception exp)
{
this._exception = exp;
this._authenticationCompleted.Set();
}
});
}
}
partial void ExecuteThread(Action action);
#region IDisposable Members
@@ -258,10 +168,12 @@ namespace Renci.SshNet
if (disposing)
{
// Dispose managed resources.
if (this._authenticationCompleted != null)
if (this.AuthenticationMethods != null)
{
this._authenticationCompleted.Dispose();
this._authenticationCompleted = null;
foreach (var authenticationMethods in this.AuthenticationMethods.OfType<IDisposable>())
{
authenticationMethods.Dispose();
}
}
}
@@ -272,7 +184,7 @@ namespace Renci.SshNet
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="KeyboardInteractiveConnectionInfo"/> is reclaimed by garbage collection.
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~KeyboardInteractiveConnectionInfo()
{
@@ -88,7 +88,7 @@ namespace Renci.SshNet.Messages.Connection
/// <param name="rows">The rows.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="terminalMode">The terminal mode.</param>
/// <param name="terminalModeValues">The terminal mode values.</param>
public PseudoTerminalRequestInfo(string environmentVariable, uint columns, uint rows, uint width, uint height, params KeyValuePair<TerminalModes, uint>[] terminalModeValues)
: this()
{
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
namespace Renci.SshNet
{
/// <summary>
/// Provides functionality for "none" authentication method
/// </summary>
public class NoneAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
/// <summary>
/// Gets connection name
/// </summary>
public override string Name
{
get { return "none"; }
}
/// <summary>
/// Gets list of allowed authentications.
/// </summary>
public IEnumerable<string> AllowedAuthentications { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardInteractiveConnectionInfo"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="username">The username.</param>
public NoneAuthenticationMethod(string host, string username)
: this(host, 22, username)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardInteractiveConnectionInfo"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="port">The port.</param>
/// <param name="username">The username.</param>
public NoneAuthenticationMethod(string host, int port, string username)
: base(host, port, username)
{
}
/// <summary>
/// Authenticates the specified session.
/// </summary>
/// <param name="session">The session.</param>
/// <returns></returns>
public override AuthenticationResult Authenticate(Session session)
{
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
session.SendMessage(new RequestMessageNone(ServiceName.Connection, this.Username));
session.WaitHandle(this._authenticationCompleted);
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
return this._authenticationResult;
}
private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
this._authenticationResult = AuthenticationResult.Success;
this._authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> 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();
}
#region IDisposable Members
private bool isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~NoneAuthenticationMethod()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
#endregion
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
using System.Threading.Tasks;
namespace Renci.SshNet
{
public partial class PasswordAuthenticationMethod : AuthenticationMethod
{
partial void ExecuteThread(Action action)
{
Task.Factory.StartNew(action);
}
}
}
@@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
namespace Renci.SshNet
{
/// <summary>
/// Provides functionality to perform password authentication.
/// </summary>
public partial class PasswordAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private Session _session;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
private Exception _exception;
private RequestMessage _requestMessage;
private string _password;
/// <summary>
/// Gets authentication method name
/// </summary>
public override string Name
{
get { return this._requestMessage.MethodName; }
}
/// <summary>
/// Occurs when user's password has expired and needs to be changed.
/// </summary>
public event EventHandler<AuthenticationPasswordChangeEventArgs> PasswordExpired;
/// <summary>
/// Initializes a new instance of the <see cref="PasswordAuthenticationMethod"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="port">The port.</param>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
public PasswordAuthenticationMethod(string host, int port, string username, string password)
: base(host, port, username)
{
if (password == null)
throw new ArgumentNullException("password");
this._password = password;
this._requestMessage = new RequestMessagePassword(ServiceName.Connection, this.Username, password);
}
/// <summary>
/// Authenticates the specified session.
/// </summary>
/// <param name="session">The session to authenticate.</param>
/// <returns></returns>
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_PASSWD_CHANGEREQ");
session.SendMessage(this._requestMessage);
session.WaitHandle(this._authenticationCompleted);
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<SuccessMessage> e)
{
this._authenticationResult = AuthenticationResult.Success;
this._authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
if (e.Message.PartialSuccess)
this._authenticationResult = AuthenticationResult.PartialSuccess;
else
this._authenticationResult = AuthenticationResult.Failure;
this._authenticationCompleted.Set();
}
private void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
{
if (e.Message is PasswordChangeRequiredMessage)
{
this._session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
this.ExecuteThread(() =>
{
try
{
var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username);
// Raise an event to allow user to supply a new password
if (this.PasswordExpired != null)
{
this.PasswordExpired(this, eventArgs);
}
// Send new authentication request with new password
this._session.SendMessage(new RequestMessagePassword(ServiceName.Connection, this.Username, this._password, eventArgs.NewPassword));
}
catch (Exception exp)
{
this._exception = exp;
this._authenticationCompleted.Set();
}
});
}
}
partial void ExecuteThread(Action action);
#region IDisposable Members
private bool isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~PasswordAuthenticationMethod()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
#endregion
}
}
@@ -2,37 +2,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Common;
using Renci.SshNet.Messages;
namespace Renci.SshNet
{
/// <summary>
/// Provides connection information when password authentication method is used
/// </summary>
public partial class PasswordConnectionInfo : ConnectionInfo, IDisposable
public class PasswordConnectionInfo : ConnectionInfo, IDisposable
{
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
private Exception _exception;
private RequestMessage _requestMessage;
private string _password;
/// <summary>
/// Gets connection name
/// </summary>
public override string Name
{
get
{
return this._requestMessage.MethodName;
}
}
/// <summary>
/// Occurs when user's password has expired and needs to be changed.
/// </summary>
@@ -154,93 +132,22 @@ namespace Renci.SshNet
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
public PasswordConnectionInfo(string host, int port, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PasswordAuthenticationMethod(host, port, username, password))
{
if (password == null)
throw new ArgumentNullException("password");
this._password = password;
this._requestMessage = new RequestMessagePassword(ServiceName.Connection, this.Username, password);
}
/// <summary>
/// Called when connection needs to be authenticated.
/// </summary>
protected override void OnAuthenticate()
{
this.Session.RegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
this.SendMessage(this._requestMessage);
this.WaitHandle(this._authenticationCompleted);
if (this._exception != null)
foreach (var authenticationMethod in this.AuthenticationMethods.OfType<PasswordAuthenticationMethod>())
{
throw this._exception;
authenticationMethod.PasswordExpired += AuthenticationMethod_PasswordExpired;
}
}
/// <summary>
/// Handles the UserAuthenticationSuccessMessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
private void AuthenticationMethod_PasswordExpired(object sender, AuthenticationPasswordChangeEventArgs e)
{
base.Session_UserAuthenticationSuccessMessageReceived(sender, e);
this._authenticationCompleted.Set();
}
/// <summary>
/// Handles the UserAuthenticationFailureReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
base.Session_UserAuthenticationFailureReceived(sender, e);
this._authenticationCompleted.Set();
}
/// <summary>
/// Handles the MessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
{
base.Session_MessageReceived(sender, e);
if (e.Message is PasswordChangeRequiredMessage)
if (this.PasswordExpired != null)
{
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
this.ExecuteThread(() =>
{
try
{
var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username);
// Raise an event to allow user to supply a new password
if (this.PasswordExpired != null)
{
this.PasswordExpired(this, eventArgs);
}
// Send new authentication request with new password
this.SendMessage(new RequestMessagePassword(ServiceName.Connection, this.Username, this._password, eventArgs.NewPassword));
}
catch (Exception exp)
{
this._exception = exp;
this._authenticationCompleted.Set();
}
});
this.PasswordExpired(sender, e);
}
}
partial void ExecuteThread(Action action);
#region IDisposable Members
private bool isDisposed = false;
@@ -269,10 +176,12 @@ namespace Renci.SshNet
if (disposing)
{
// Dispose managed resources.
if (this._authenticationCompleted != null)
if (this.AuthenticationMethods != null)
{
this._authenticationCompleted.Dispose();
this._authenticationCompleted = null;
foreach (var authenticationMethods in this.AuthenticationMethods.OfType<IDisposable>())
{
authenticationMethods.Dispose();
}
}
}
@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
using Renci.SshNet.Common;
using System.Threading;
namespace Renci.SshNet
{
/// <summary>
/// Provides functionality to perform private key authentication.
/// </summary>
public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private EventWaitHandle _authenticationCompleted = new ManualResetEvent(false);
private bool _isSignatureRequired;
/// <summary>
/// Gets authentication method name
/// </summary>
public override string Name
{
get { return "publickey"; }
}
/// <summary>
/// Gets the key files used for authentication.
/// </summary>
public ICollection<PrivateKeyFile> KeyFiles { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyAuthenticationMethod"/> class.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="port">The port.</param>
/// <param name="username">The username.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyAuthenticationMethod(string host, int port, string username, params PrivateKeyFile[] keyFiles)
: base(host, port, username)
{
this.KeyFiles = new Collection<PrivateKeyFile>(keyFiles);
}
/// <summary>
/// Authenticates the specified session.
/// </summary>
/// <param name="session">The session to authenticate.</param>
/// <returns></returns>
public override AuthenticationResult Authenticate(Session session)
{
if (this.KeyFiles == null)
return AuthenticationResult.Failure;
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
session.MessageReceived += Session_MessageReceived;
session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
foreach (var keyFile in this.KeyFiles)
{
this._authenticationCompleted.Reset();
this._isSignatureRequired = false;
var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
if (this.KeyFiles.Count < 2)
{
// If only one key file provided then send signature for very first request
var signatureData = new SignatureData(message, session.SessionId).GetBytes();
message.Signature = keyFile.HostKey.Sign(signatureData);
}
// Send public key authentication request
session.SendMessage(message);
session.WaitHandle(this._authenticationCompleted);
if (this._isSignatureRequired)
{
this._authenticationCompleted.Reset();
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
var signatureData = new SignatureData(message, session.SessionId).GetBytes();
signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
// Send public key authentication request with signature
session.SendMessage(signatureMessage);
}
session.WaitHandle(this._authenticationCompleted);
if (this._authenticationResult == AuthenticationResult.Success)
{
break;
}
}
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
session.MessageReceived -= Session_MessageReceived;
session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK");
return this._authenticationResult;
}
private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
this._authenticationResult = AuthenticationResult.Success;
this._authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
if (e.Message.PartialSuccess)
this._authenticationResult = AuthenticationResult.PartialSuccess;
else
this._authenticationResult = AuthenticationResult.Failure;
this._authenticationCompleted.Set();
}
private void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
{
var publicKeyMessage = e.Message as PublicKeyMessage;
if (publicKeyMessage != null)
{
this._isSignatureRequired = true;
this._authenticationCompleted.Set();
}
}
#region IDisposable Members
private bool isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~PrivateKeyAuthenticationMethod()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
#endregion
private class SignatureData : SshData
{
private RequestMessagePublicKey _message;
private byte[] _sessionId;
public SignatureData(RequestMessagePublicKey message, byte[] sessionId)
{
this._message = message;
this._sessionId = sessionId;
}
protected override void LoadData()
{
throw new System.NotImplementedException();
}
protected override void SaveData()
{
this.WriteBinaryString(this._sessionId);
this.Write((byte)50);
this.Write(this._message.Username);
this.WriteAscii("ssh-connection");
this.WriteAscii("publickey");
this.Write((byte)1);
this.WriteAscii(this._message.PublicKeyAlgorithmName);
this.WriteBinaryString(this._message.PublicKeyData);
}
}
}
}
@@ -3,10 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Threading;
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
using Renci.SshNet.Common;
namespace Renci.SshNet
{
@@ -15,21 +11,6 @@ namespace Renci.SshNet
/// </summary>
public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable
{
private EventWaitHandle _publicKeyRequestMessageResponseWaitHandle = new ManualResetEvent(false);
private bool _isSignatureRequired;
/// <summary>
/// Gets connection name
/// </summary>
public override string Name
{
get
{
return "publickey";
}
}
/// <summary>
/// Gets the key files used for authentication.
/// </summary>
@@ -148,139 +129,14 @@ namespace Renci.SshNet
/// <param name="proxyPassword">The proxy password.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(host, port, username, keyFiles))
{
this.KeyFiles = new Collection<PrivateKeyFile>(keyFiles);
}
/// <summary>
/// Called when connection needs to be authenticated.
/// </summary>
protected override void OnAuthenticate()
{
if (this.KeyFiles == null)
return;
this.Session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
foreach (var keyFile in this.KeyFiles)
{
this._publicKeyRequestMessageResponseWaitHandle.Reset();
this._isSignatureRequired = false;
var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
if (this.KeyFiles.Count < 2)
{
// If only one key file provided then send signature for very first request
var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes();
message.Signature = keyFile.HostKey.Sign(signatureData);
}
// Send public key authentication request
this.SendMessage(message);
this.WaitHandle(this._publicKeyRequestMessageResponseWaitHandle);
if (this._isSignatureRequired)
{
this._publicKeyRequestMessageResponseWaitHandle.Reset();
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes();
signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
// Send public key authentication request with signature
this.SendMessage(signatureMessage);
}
this.WaitHandle(this._publicKeyRequestMessageResponseWaitHandle);
if (this.IsAuthenticated)
{
break;
}
}
this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK");
}
/// <summary>
/// Handles the UserAuthenticationSuccessMessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
base.Session_UserAuthenticationSuccessMessageReceived(sender, e);
this._publicKeyRequestMessageResponseWaitHandle.Set();
}
/// <summary>
/// Handles the UserAuthenticationFailureReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
base.Session_UserAuthenticationFailureReceived(sender, e);
this._publicKeyRequestMessageResponseWaitHandle.Set();
}
/// <summary>
/// Handles the MessageReceived event of the session.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data.</param>
protected override void Session_MessageReceived(object sender, MessageEventArgs<Message> e)
{
base.Session_MessageReceived(sender, e);
var publicKeyMessage = e.Message as PublicKeyMessage;
if (publicKeyMessage != null)
{
this._isSignatureRequired = true;
this._publicKeyRequestMessageResponseWaitHandle.Set();
}
}
private class SignatureData : SshData
{
private RequestMessagePublicKey _message;
private byte[] _sessionId;
public SignatureData(RequestMessagePublicKey message, byte[] sessionId)
{
this._message = message;
this._sessionId = sessionId;
}
protected override void LoadData()
{
throw new System.NotImplementedException();
}
protected override void SaveData()
{
this.WriteBinaryString(this._sessionId);
this.Write((byte)50);
this.Write(this._message.Username);
this.WriteAscii("ssh-connection");
this.WriteAscii("publickey");
this.Write((byte)1);
this.WriteAscii(this._message.PublicKeyAlgorithmName);
this.WriteBinaryString(this._message.PublicKeyData);
}
}
#region IDisposable Members
private bool _isDisposed = false;
private bool isDisposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
@@ -299,28 +155,30 @@ namespace Renci.SshNet
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._isDisposed)
if (!this.isDisposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
if (this._publicKeyRequestMessageResponseWaitHandle != null)
if (this.AuthenticationMethods != null)
{
this._publicKeyRequestMessageResponseWaitHandle.Dispose();
this._publicKeyRequestMessageResponseWaitHandle = null;
foreach (var authenticationMethods in this.AuthenticationMethods.OfType<IDisposable>())
{
authenticationMethods.Dispose();
}
}
}
// Note disposing has been done.
_isDisposed = true;
isDisposed = true;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="PrivateKeyConnectionInfo"/> is reclaimed by garbage collection.
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection.
/// </summary>
~PrivateKeyConnectionInfo()
{
@@ -331,6 +189,5 @@ namespace Renci.SshNet
}
#endregion
}
}
@@ -41,6 +41,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AuthenticationMethod.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="AuthenticationResult.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="BaseClient.cs" />
<Compile Include="ChannelAsyncResult.cs" />
<Compile Include="Channels\Channel.cs" />
@@ -126,9 +132,19 @@
<Compile Include="Compression\ZlibStream.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ConnectionInfo.cs">
<Compile Include="ConnectionInfo.cs" />
<Compile Include="KeyboardInteractiveAuthenticationMethod.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="KeyboardInteractiveAuthenticationMethod.NET40.cs" />
<Compile Include="KeyboardInteractiveConnectionInfo.cs" />
<Compile Include="NoneAuthenticationMethod.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PasswordAuthenticationMethod.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PasswordAuthenticationMethod.NET40.cs" />
<Compile Include="ExpectAction.cs">
<SubType>Code</SubType>
</Compile>
@@ -141,6 +157,11 @@
</Compile>
<Compile Include="ForwardedPortLocal.NET.cs" />
<Compile Include="Messages\Transport\IKeyExchangedAllowed.cs" />
<Compile Include="PasswordConnectionInfo.cs" />
<Compile Include="PrivateKeyAuthenticationMethod.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PrivateKeyConnectionInfo.cs" />
<Compile Include="ProxyTypes.cs">
<SubType>Code</SubType>
</Compile>
@@ -157,8 +178,6 @@
</Compile>
<Compile Include="ForwardedPortRemote.cs" />
<Compile Include="ForwardedPortRemote.NET40.cs" />
<Compile Include="KeyboardInteractiveConnectionInfo.cs" />
<Compile Include="KeyboardInteractiveConnectionInfo.NET40.cs" />
<Compile Include="MessageEventArgs.cs" />
<Compile Include="Messages\Authentication\BannerMessage.cs" />
<Compile Include="Messages\Authentication\FailureMessage.cs" />
@@ -244,10 +263,6 @@
<Compile Include="Messages\Transport\UnimplementedMessage.cs" />
<Compile Include="NetConfClient.cs" />
<Compile Include="Netconf\NetConfSession.cs" />
<Compile Include="NoneConnectionInfo.cs" />
<Compile Include="PasswordConnectionInfo.cs" />
<Compile Include="PasswordConnectionInfo.NET40.cs" />
<Compile Include="PrivateKeyConnectionInfo.cs" />
<Compile Include="PrivateKeyFile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Security\Algorithm.cs">
+1 -35
View File
@@ -544,41 +544,7 @@ namespace Renci.SshNet
throw new SshException("Username is not specified.");
}
// Try authenticate using none method
using (var noneConnectionInfo = new NoneConnectionInfo(this.ConnectionInfo.Host, this.ConnectionInfo.Port, this.ConnectionInfo.Username))
{
noneConnectionInfo.Authenticate(this);
this._isAuthenticated = noneConnectionInfo.IsAuthenticated;
if (!this._isAuthenticated)
{
// TODO: Replace this logic here with multiple authentication suppurt
var supportedConnectionInfos = (from c in new[] { this.ConnectionInfo }
where noneConnectionInfo.AllowedAuthentications.Contains(this.ConnectionInfo.Name)
select c).ToList();
if (supportedConnectionInfos.Count > 0)
{
foreach (var connectionInfo in supportedConnectionInfos)
{
// Authenticate using provided connection info object
connectionInfo.Authenticate(this);
if (connectionInfo.IsAuthenticated)
{
this._isAuthenticated = this.ConnectionInfo.IsAuthenticated;
this.ConnectionInfo = connectionInfo;
break;
}
}
}
else
{
throw new SshAuthenticationException("User authentication method is not supported.");
}
}
}
this._isAuthenticated = this.ConnectionInfo.Authenticate(this);
if (!this._isAuthenticated)
{