|  | @@ -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)
 |