|
|
@@ -1,5 +1,6 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Text;
|
|
|
using Renci.SshNet.Security;
|
|
|
@@ -15,6 +16,10 @@ namespace Renci.SshNet
|
|
|
/// <summary>
|
|
|
/// Represents remote connection information class.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// This class is NOT thread-safe. Do not use the same <see cref="ConnectionInfo"/> with multiple
|
|
|
+ /// client instances.
|
|
|
+ /// </remarks>
|
|
|
public class ConnectionInfo
|
|
|
{
|
|
|
internal static int DEFAULT_PORT = 22;
|
|
|
@@ -385,64 +390,93 @@ namespace Renci.SshNet
|
|
|
/// Authenticates the specified session.
|
|
|
/// </summary>
|
|
|
/// <param name="session">The session to be authenticated.</param>
|
|
|
- /// <returns>true if authenticated; otherwise false.</returns>
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="session"/> is null.</exception>
|
|
|
- /// <exception cref="SshAuthenticationException">No suitable authentication method found to complete authentication.</exception>
|
|
|
- public bool Authenticate(Session session)
|
|
|
+ /// <exception cref="SshAuthenticationException">No suitable authentication method found to complete authentication, or permission denied.</exception>
|
|
|
+ public void Authenticate(Session session)
|
|
|
{
|
|
|
- var authenticated = AuthenticationResult.Failure;
|
|
|
-
|
|
|
if (session == null)
|
|
|
throw new ArgumentNullException("session");
|
|
|
|
|
|
session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
|
|
|
session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
|
|
|
session.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
|
|
|
-
|
|
|
session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerReceived;
|
|
|
|
|
|
- // Try to authenticate against none
|
|
|
- var noneAuthenticationMethod = new NoneAuthenticationMethod(this.Username);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // the exception to report an authentication failure with
|
|
|
+ SshAuthenticationException authenticationException = null;
|
|
|
|
|
|
- authenticated = noneAuthenticationMethod.Authenticate(session);
|
|
|
+ // try to authenticate against none
|
|
|
+ var noneAuthenticationMethod = new NoneAuthenticationMethod(this.Username);
|
|
|
|
|
|
- var allowedAuthentications = noneAuthenticationMethod.AllowedAuthentications;
|
|
|
+ var authenticated = noneAuthenticationMethod.Authenticate(session);
|
|
|
+ if (authenticated != AuthenticationResult.Success)
|
|
|
+ {
|
|
|
+ var failedAuthenticationMethods = new List<AuthenticationMethod>();
|
|
|
+ if (TryAuthenticate(session, noneAuthenticationMethod.AllowedAuthentications.ToList(), failedAuthenticationMethods, ref authenticationException))
|
|
|
+ {
|
|
|
+ authenticated = AuthenticationResult.Success;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var triedAuthentications = new List<string>();
|
|
|
- while (authenticated != AuthenticationResult.Success)
|
|
|
+ this.IsAuthenticated = authenticated == AuthenticationResult.Success;
|
|
|
+ if (!IsAuthenticated)
|
|
|
+ throw authenticationException;
|
|
|
+ }
|
|
|
+ finally
|
|
|
{
|
|
|
- // Find first authentication method
|
|
|
- var method = this.AuthenticationMethods.Where((a) => allowedAuthentications.Contains(a.Name) && !triedAuthentications.Contains(a.Name)).FirstOrDefault();
|
|
|
- if (method == null)
|
|
|
- throw new SshAuthenticationException("No suitable authentication method found to complete authentication.");
|
|
|
+ session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerReceived;
|
|
|
+ session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE");
|
|
|
+ session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
|
|
|
+ session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER");
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- triedAuthentications.Add(method.Name);
|
|
|
+ private bool TryAuthenticate(Session session, ICollection<string> allowedAuthenticationMethods, IList<AuthenticationMethod> failedAuthenticationMethods, ref SshAuthenticationException authenticationException)
|
|
|
+ {
|
|
|
+ if (!allowedAuthenticationMethods.Any())
|
|
|
+ {
|
|
|
+ authenticationException = new SshAuthenticationException("No authentication methods defined on SSH server.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- authenticated = method.Authenticate(session);
|
|
|
+ // we want to try authentication methods in the order in which they were
|
|
|
+ // passed in the ctor, not the order in which the SSH server returns
|
|
|
+ // the allowed authentication methods
|
|
|
+ var matchingAuthenticationMethods = AuthenticationMethods.Where(a => allowedAuthenticationMethods.Contains(a.Name)).ToList();
|
|
|
+ if (!matchingAuthenticationMethods.Any())
|
|
|
+ {
|
|
|
+ authenticationException = new SshAuthenticationException(string.Format("No suitable authentication method found to complete authentication ({0}).", string.Join(",", allowedAuthenticationMethods)));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- if (authenticated == AuthenticationResult.PartialSuccess || (method.AllowedAuthentications != null && method.AllowedAuthentications.Count() < allowedAuthentications.Count()))
|
|
|
- {
|
|
|
- // If further authentication is required then continue to try another method
|
|
|
- allowedAuthentications = method.AllowedAuthentications;
|
|
|
+ foreach (var authenticationMethod in matchingAuthenticationMethods)
|
|
|
+ {
|
|
|
+ if (failedAuthenticationMethods.Contains(authenticationMethod))
|
|
|
continue;
|
|
|
- }
|
|
|
|
|
|
- // If authentication Fail, and all the authentication have been tried.
|
|
|
- if (authenticated == AuthenticationResult.Failure && (triedAuthentications.Count() == allowedAuthentications.Count()))
|
|
|
+ var authenticationResult = authenticationMethod.Authenticate(session);
|
|
|
+ switch (authenticationResult)
|
|
|
{
|
|
|
- break;
|
|
|
+ case AuthenticationResult.PartialSuccess:
|
|
|
+ if (TryAuthenticate(session, authenticationMethod.AllowedAuthentications.ToList(), failedAuthenticationMethods, ref authenticationException))
|
|
|
+ authenticationResult = AuthenticationResult.Success;
|
|
|
+ break;
|
|
|
+ case AuthenticationResult.Failure:
|
|
|
+ failedAuthenticationMethods.Add(authenticationMethod);
|
|
|
+ authenticationException = new SshAuthenticationException(string.Format("Permission denied ({0}).", authenticationMethod.Name));
|
|
|
+ break;
|
|
|
+ case AuthenticationResult.Success:
|
|
|
+ authenticationException = null;
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
+ if (authenticationResult == AuthenticationResult.Success)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- return authenticated == AuthenticationResult.Success;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
private void Session_UserAuthenticationBannerReceived(object sender, MessageEventArgs<BannerMessage> e)
|