< Summary

Information
Class: Renci.SshNet.ClientAuthentication
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\ClientAuthentication.cs
Line coverage
97%
Covered lines: 135
Uncovered lines: 4
Coverable lines: 139
Total lines: 319
Line coverage: 97.1%
Branch coverage
97%
Covered branches: 43
Total branches: 44
Branch coverage: 97.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%2100%
get_PartialSuccessLimit()100%1100%
Authenticate(...)100%8100%
TryAuthenticate(...)93.75%1690.9%
.ctor(...)100%1100%
RecordFailure(...)100%1100%
RecordPartialSuccess(...)100%2100%
GetPartialSuccessCount(...)100%2100%
GetSupportedAuthenticationMethods(...)100%6100%
GetActiveAuthenticationMethods()100%8100%

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\ClientAuthentication.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4
 5using Renci.SshNet.Common;
 6
 7namespace Renci.SshNet
 8{
 9    /// <summary>
 10    /// Represents a mechanism to authenticate a given client.
 11    /// </summary>
 12    internal sealed class ClientAuthentication : IClientAuthentication
 13    {
 14        private readonly int _partialSuccessLimit;
 15
 16        /// <summary>
 17        /// Initializes a new instance of the <see cref="ClientAuthentication"/> class.
 18        /// </summary>
 19        /// <param name="partialSuccessLimit">The number of times an authentication attempt with any given <see cref="IA
 20        /// <exception cref="ArgumentOutOfRangeException"><paramref name="partialSuccessLimit"/> is less than one.</exce
 153421        public ClientAuthentication(int partialSuccessLimit)
 153422        {
 153423            if (partialSuccessLimit < 1)
 624            {
 625                throw new ArgumentOutOfRangeException(nameof(partialSuccessLimit), "Cannot be less than one.");
 26            }
 27
 152828            _partialSuccessLimit = partialSuccessLimit;
 152829        }
 30
 31        /// <summary>
 32        /// Gets the number of times an authentication attempt with any given <see cref="IAuthenticationMethod"/> can
 33        /// result in <see cref="AuthenticationResult.PartialSuccess"/> before it is disregarded.
 34        /// </summary>
 35        /// <value>
 36        /// The number of times an authentication attempt with any given <see cref="IAuthenticationMethod"/> can result
 37        /// in <see cref="AuthenticationResult.PartialSuccess"/> before it is disregarded.
 38        /// </value>
 39        internal int PartialSuccessLimit
 40        {
 2741            get { return _partialSuccessLimit; }
 42        }
 43
 44        /// <summary>
 45        /// Attempts to perform authentication for a given <see cref="ISession"/> using the
 46        /// <see cref="IConnectionInfoInternal.AuthenticationMethods"/> of the specified
 47        /// <see cref="IConnectionInfoInternal"/>.
 48        /// </summary>
 49        /// <param name="connectionInfo">A <see cref="IConnectionInfoInternal"/> to use for authenticating.</param>
 50        /// <param name="session">The <see cref="ISession"/> for which to perform authentication.</param>
 51        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> or <paramref name="session"/> is <
 52        /// <exception cref="SshAuthenticationException">Failed to authenticate the client.</exception>
 53        public void Authenticate(IConnectionInfoInternal connectionInfo, ISession session)
 150454        {
 150455            if (connectionInfo is null)
 356            {
 357                throw new ArgumentNullException(nameof(connectionInfo));
 58            }
 59
 150160            if (session is null)
 361            {
 362                throw new ArgumentNullException(nameof(session));
 63            }
 64
 149865            session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
 149866            session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
 149867            session.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
 149868            session.UserAuthenticationBannerReceived += connectionInfo.UserAuthenticationBannerReceived;
 69
 70            try
 149871            {
 72                // the exception to report an authentication failure with
 149873                SshAuthenticationException authenticationException = null;
 74
 75                // try to authenticate against none
 149876                var noneAuthenticationMethod = connectionInfo.CreateNoneAuthenticationMethod();
 77
 149878                var authenticated = noneAuthenticationMethod.Authenticate(session);
 149779                if (authenticated != AuthenticationResult.Success)
 149780                {
 149781                    if (!TryAuthenticate(session, new AuthenticationState(connectionInfo.AuthenticationMethods), noneAut
 16882                    {
 16883                        throw authenticationException;
 84                    }
 132985                }
 132986            }
 87            finally
 149888            {
 149889                session.UserAuthenticationBannerReceived -= connectionInfo.UserAuthenticationBannerReceived;
 149890                session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE");
 149891                session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
 149892                session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER");
 149893            }
 132994        }
 95
 96        private bool TryAuthenticate(ISession session,
 97                                     AuthenticationState authenticationState,
 98                                     string[] allowedAuthenticationMethods,
 99                                     ref SshAuthenticationException authenticationException)
 2139100        {
 2139101            if (allowedAuthenticationMethods.Length == 0)
 0102            {
 0103                authenticationException = new SshAuthenticationException("No authentication methods defined on SSH serve
 0104                return false;
 105            }
 106
 107            // we want to try authentication methods in the order in which they were
 108            // passed in the ctor, not the order in which the SSH server returns
 109            // the allowed authentication methods
 2139110            var matchingAuthenticationMethods = authenticationState.GetSupportedAuthenticationMethods(allowedAuthenticat
 2139111            if (matchingAuthenticationMethods.Count == 0)
 21112            {
 21113                authenticationException = new SshAuthenticationException(string.Format(CultureInfo.InvariantCulture,
 21114                                                                                       "No suitable authentication metho
 21115#if NET || NETSTANDARD2_1_OR_GREATER
 21116                                                                                       string.Join(',', allowedAuthentic
 21117#else
 21118                                                                                       string.Join(",", allowedAuthentic
 21119#endif // NET || NETSTANDARD2_1_OR_GREATER
 21120                ;
 21121                return false;
 122            }
 123
 9568124            foreach (var authenticationMethod in authenticationState.GetActiveAuthenticationMethods(matchingAuthenticati
 2354125            {
 126                // guard against a stack overlow for servers that do not update the list of allowed authentication
 127                // methods after a partial success
 2354128                if (authenticationState.GetPartialSuccessCount(authenticationMethod) >= _partialSuccessLimit)
 202129                {
 130                    /* TODO Get list of all authentication methods that have reached the partial success limit? */
 131
 202132                    authenticationException = new SshAuthenticationException(string.Format("Reached authentication attem
 202133                                                                                           authenticationMethod.Name));
 202134                    continue;
 135                }
 136
 2152137                var authenticationResult = authenticationMethod.Authenticate(session);
 2152138                switch (authenticationResult)
 139                {
 140                    case AuthenticationResult.PartialSuccess:
 642141                        authenticationState.RecordPartialSuccess(authenticationMethod);
 642142                        if (TryAuthenticate(session, authenticationState, authenticationMethod.AllowedAuthentications, r
 165143                        {
 165144                            authenticationResult = AuthenticationResult.Success;
 165145                        }
 146
 642147                        break;
 148                    case AuthenticationResult.Failure:
 181149                        authenticationState.RecordFailure(authenticationMethod);
 181150                        authenticationException = new SshAuthenticationException(string.Format("Permission denied ({0}).
 181151                        break;
 152                    case AuthenticationResult.Success:
 1329153                        authenticationException = null;
 1329154                        break;
 155                    default:
 0156                        break;
 157                }
 158
 2152159                if (authenticationResult == AuthenticationResult.Success)
 1494160                {
 1494161                    return true;
 162                }
 658163            }
 164
 624165            return false;
 2139166        }
 167
 168        private sealed class AuthenticationState
 169        {
 170            private readonly IList<IAuthenticationMethod> _supportedAuthenticationMethods;
 171
 172            /// <summary>
 173            /// Records if a given <see cref="IAuthenticationMethod"/> has been tried, and how many times this resulted
 174            /// in <see cref="AuthenticationResult.PartialSuccess"/>.
 175            /// </summary>
 176            /// <remarks>
 177            /// When there's no entry for a given <see cref="IAuthenticationMethod"/>, then it was never tried.
 178            /// </remarks>
 179            private readonly Dictionary<IAuthenticationMethod, int> _authenticationMethodPartialSuccessRegister;
 180
 181            /// <summary>
 182            /// Holds the list of authentications methods that failed.
 183            /// </summary>
 184            private readonly List<IAuthenticationMethod> _failedAuthenticationMethods;
 185
 1497186            public AuthenticationState(IList<IAuthenticationMethod> supportedAuthenticationMethods)
 1497187            {
 1497188                _supportedAuthenticationMethods = supportedAuthenticationMethods;
 1497189                _failedAuthenticationMethods = new List<IAuthenticationMethod>();
 1497190                _authenticationMethodPartialSuccessRegister = new Dictionary<IAuthenticationMethod, int>();
 1497191            }
 192
 193            /// <summary>
 194            /// Records a <see cref="AuthenticationResult.Failure"/> authentication attempt for the specified
 195            /// <see cref="IAuthenticationMethod"/> .
 196            /// </summary>
 197            /// <param name="authenticationMethod">An <see cref="IAuthenticationMethod"/> for which to record the result
 198            public void RecordFailure(IAuthenticationMethod authenticationMethod)
 181199            {
 181200                _failedAuthenticationMethods.Add(authenticationMethod);
 181201            }
 202
 203            /// <summary>
 204            /// Records a <see cref="AuthenticationResult.PartialSuccess"/> authentication attempt for the specified
 205            /// <see cref="IAuthenticationMethod"/> .
 206            /// </summary>
 207            /// <param name="authenticationMethod">An <see cref="IAuthenticationMethod"/> for which to record the result
 208            public void RecordPartialSuccess(IAuthenticationMethod authenticationMethod)
 642209            {
 642210                if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var partialSuccess
 223211                {
 223212                    _authenticationMethodPartialSuccessRegister[authenticationMethod] = partialSuccessCount + 1;
 223213                }
 214                else
 419215                {
 419216                    _authenticationMethodPartialSuccessRegister.Add(authenticationMethod, 1);
 419217                }
 642218            }
 219
 220            /// <summary>
 221            /// Returns the number of times an authentication attempt with the specified <see cref="IAuthenticationMetho
 222            /// has resulted in <see cref="AuthenticationResult.PartialSuccess"/>.
 223            /// </summary>
 224            /// <param name="authenticationMethod">An <see cref="IAuthenticationMethod"/>.</param>
 225            /// <returns>
 226            /// The number of times an authentication attempt with the specified <see cref="IAuthenticationMethod"/>
 227            /// has resulted in <see cref="AuthenticationResult.PartialSuccess"/>.
 228            /// </returns>
 229            public int GetPartialSuccessCount(IAuthenticationMethod authenticationMethod)
 2354230            {
 2354231                if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var partialSuccess
 516232                {
 516233                    return partialSuccessCount;
 234                }
 235
 1838236                return 0;
 2354237            }
 238
 239            /// <summary>
 240            /// Returns a list of supported authentication methods that match one of the specified allowed authenticatio
 241            /// methods.
 242            /// </summary>
 243            /// <param name="allowedAuthenticationMethods">A list of allowed authentication methods.</param>
 244            /// <returns>
 245            /// A list of supported authentication methods that match one of the specified allowed authentication method
 246            /// </returns>
 247            /// <remarks>
 248            /// The authentication methods are returned in the order in which they were specified in the list that was
 249            /// used to initialize the current <see cref="AuthenticationState"/> instance.
 250            /// </remarks>
 251            public List<IAuthenticationMethod> GetSupportedAuthenticationMethods(string[] allowedAuthenticationMethods)
 2139252            {
 2139253                var result = new List<IAuthenticationMethod>();
 254
 13761255                foreach (var supportedAuthenticationMethod in _supportedAuthenticationMethods)
 3672256                {
 3672257                    var nameOfSupportedAuthenticationMethod = supportedAuthenticationMethod.Name;
 258
 12028259                    for (var i = 0; i < allowedAuthenticationMethods.Length; i++)
 4808260                    {
 4808261                        if (allowedAuthenticationMethods[i] == nameOfSupportedAuthenticationMethod)
 2466262                        {
 2466263                            result.Add(supportedAuthenticationMethod);
 2466264                            break;
 265                        }
 2342266                    }
 3672267                }
 268
 2139269                return result;
 2139270            }
 271
 272            /// <summary>
 273            /// Returns the authentication methods from the specified list that have not yet failed.
 274            /// </summary>
 275            /// <param name="matchingAuthenticationMethods">A list of authentication methods.</param>
 276            /// <returns>
 277            /// The authentication methods from <paramref name="matchingAuthenticationMethods"/> that have not yet faile
 278            /// </returns>
 279            /// <remarks>
 280            /// <para>
 281            /// This method first returns the authentication methods that have not yet been executed, and only then
 282            /// returns those for which an authentication attempt resulted in a <see cref="AuthenticationResult.PartialS
 283            /// </para>
 284            /// <para>
 285            /// Any <see cref="IAuthenticationMethod"/> that has failed is skipped.
 286            /// </para>
 287            /// </remarks>
 288            public IEnumerable<IAuthenticationMethod> GetActiveAuthenticationMethods(List<IAuthenticationMethod> matchin
 2118289            {
 2118290                var skippedAuthenticationMethods = new List<IAuthenticationMethod>();
 291
 6242292                for (var i = 0; i < matchingAuthenticationMethods.Count; i++)
 2409293                {
 2409294                    var authenticationMethod = matchingAuthenticationMethods[i];
 295
 296                    // skip authentication methods that have already failed
 2409297                    if (_failedAuthenticationMethods.Contains(authenticationMethod))
 1298                    {
 1299                        continue;
 300                    }
 301
 302                    // delay use of authentication methods that had a PartialSuccess result
 2408303                    if (_authenticationMethodPartialSuccessRegister.ContainsKey(authenticationMethod))
 570304                    {
 570305                        skippedAuthenticationMethods.Add(authenticationMethod);
 570306                        continue;
 307                    }
 308
 1838309                    yield return authenticationMethod;
 432310                }
 311
 3080312                foreach (var authenticationMethod in skippedAuthenticationMethods)
 516313                {
 516314                    yield return authenticationMethod;
 428315                }
 624316            }
 317        }
 318    }
 319}