mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 17:25:51 +00:00
Add support for multiple authentication methods
Minor comment fixes
This commit is contained in:
@@ -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.
|
||||
|
||||
+22
@@ -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(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
+1
-1
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user