diff --git a/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs b/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs index 9c22c340c..3a2562ae1 100644 --- a/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs +++ b/src/Renci.SshNet/Common/AuthenticationPromptEventArgs.cs @@ -14,7 +14,7 @@ public class AuthenticationPromptEventArgs : AuthenticationEventArgs /// The instruction. /// The language. /// The information request prompts. - public AuthenticationPromptEventArgs(string username, string instruction, string language, IEnumerable prompts) + public AuthenticationPromptEventArgs(string username, string instruction, string language, IReadOnlyList prompts) : base(username) { Instruction = instruction; @@ -35,6 +35,6 @@ public AuthenticationPromptEventArgs(string username, string instruction, string /// /// Gets server information request prompts. /// - public IEnumerable Prompts { get; } + public IReadOnlyList Prompts { get; } } } diff --git a/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs b/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs index 825809950..7169fabc0 100644 --- a/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs +++ b/src/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs @@ -125,9 +125,19 @@ private void Session_UserAuthenticationInformationRequestReceived(object sender, var informationResponse = new InformationResponseMessage(); - foreach (var response in from r in eventArgs.Prompts orderby r.Id ascending select r.Response) + foreach (var prompt in eventArgs.Prompts.OrderBy(r => r.Id)) { - informationResponse.Responses.Add(response); + if (prompt.Response is null) + { + throw new SshAuthenticationException( + $"{nameof(AuthenticationPrompt)}.{nameof(prompt.Response)} is null for " + + $"prompt \"{prompt.Request}\". You can set this by subscribing to " + + $"{nameof(KeyboardInteractiveAuthenticationMethod)}.{nameof(AuthenticationPrompt)} " + + $"and inspecting the {nameof(AuthenticationPromptEventArgs.Prompts)} property " + + $"of the event args."); + } + + informationResponse.Responses.Add(prompt.Response); } // Send information response message diff --git a/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs b/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs index c887cc883..7110222ac 100644 --- a/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs +++ b/src/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs @@ -29,7 +29,7 @@ internal sealed class InformationRequestMessage : Message /// /// Gets information request prompts. /// - public IEnumerable Prompts { get; private set; } + public IReadOnlyList Prompts { get; private set; } /// /// Called when type specific data need to be loaded. diff --git a/test/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs b/test/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs index 17e4c75b6..edb3e74d1 100644 --- a/test/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs +++ b/test/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs @@ -69,7 +69,7 @@ public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyAuthenticationM return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, new PrivateKeyFile(memoryStream)); } - public PasswordAuthenticationMethod CreateRegulatUserPasswordAuthenticationMethod() + public PasswordAuthenticationMethod CreateRegularUserPasswordAuthenticationMethod() { return new PasswordAuthenticationMethod(Users.Regular.UserName, Users.Regular.Password); } diff --git a/test/Renci.SshNet.IntegrationTests/AuthenticationTests.cs b/test/Renci.SshNet.IntegrationTests/AuthenticationTests.cs index c3968e949..dd0a4f3d9 100644 --- a/test/Renci.SshNet.IntegrationTests/AuthenticationTests.cs +++ b/test/Renci.SshNet.IntegrationTests/AuthenticationTests.cs @@ -161,7 +161,7 @@ public void Multifactor_Password_ExceedsPartialSuccessLimit() .Update() .Restart(); - var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod()); using (var client = new SftpClient(connectionInfo)) { try @@ -187,7 +187,7 @@ public void Multifactor_Password_MatchPartialSuccessLimit() .Update() .Restart(); - var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod()); using (var client = new SftpClient(connectionInfo)) { client.Connect(); @@ -205,7 +205,7 @@ public void Multifactor_Password_Or_PublicKeyAndKeyboardInteractive() .Restart(); var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(), - _authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + _authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod()); using (var client = new SftpClient(connectionInfo)) { client.Connect(); @@ -243,7 +243,7 @@ public void Multifactor_PasswordAndPublicKey_Or_PasswordAndPassword() .Update() .Restart(); - var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(), _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); using (var client = new SftpClient(connectionInfo)) { @@ -275,14 +275,14 @@ public void Multifactor_PasswordAndPassword_Or_PublicKey() .Update() .Restart(); - var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(), _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); using (var client = new SftpClient(connectionInfo)) { client.Connect(); } - connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod()); using (var client = new SftpClient(connectionInfo)) { client.Connect(); @@ -297,13 +297,13 @@ public void Multifactor_Password_Or_Password() .Update() .Restart(); - var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod()); + var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod()); using (var client = new SftpClient(connectionInfo)) { client.Connect(); } - connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(), + connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethod(), _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()); using (var client = new SftpClient(connectionInfo)) { @@ -423,5 +423,35 @@ public void KeyboardInteractiveConnectionInfo() Assert.AreEqual(connectionInfo.Host, SshServerHostName); Assert.AreEqual(connectionInfo.Username, User.UserName); } + + [TestMethod] + public void KeyboardInteractive_NoResponseSet_ThrowsSshAuthenticationException() + { + // ...instead of a cryptic ArgumentNullException + // https://github.com/sshnet/SSH.NET/issues/382 + + _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive") + .WithChallengeResponseAuthentication(true) + .WithKeyboardInteractiveAuthentication(true) + .WithUsePAM(true) + .Update() + .Restart(); + + var connectionInfo = _connectionInfoFactory.Create(new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName)); + + using (var client = new SftpClient(connectionInfo)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (SshAuthenticationException ex) + { + Assert.IsNull(ex.InnerException); + Assert.IsTrue(ex.Message.StartsWith("AuthenticationPrompt.Response is null for prompt \"Password: \""), $"Message was \"{ex.Message}\""); + } + } + } } }