Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String reference not set to an instance of a String. Parameter name: s - version 2020.0.2 #1237

Closed
amitbarkai opened this issue Nov 12, 2023 · 13 comments · Fixed by #1245
Closed
Milestone

Comments

@amitbarkai
Copy link

amitbarkai commented Nov 12, 2023

Hi All

i am using version 2020.0.2 and getting the following exception while trying to connect to linux machine

System.ArgumentNullException: String reference not set to an instance of a String.

Parameter name: s

at Renci.SshNet.KeyboardInteractiveAuthenticationMethod.Authenticate(Session session)

at Renci.SshNet.ClientAuthentication.TryAuthenticate(ISession session, AuthenticationState authenticationState, String[] allowedAuthenticationMethods, SshAuthenticationException& authenticationException)

at Renci.SshNet.ClientAuthentication.Authenticate(IConnectionInfoInternal connectionInfo, ISession session)

at Renci.SshNet.ConnectionInfo.Authenticate(ISession session, IServiceFactory serviceFactory)

at Renci.SshNet.Session.Connect()

at Renci.SshNet.BaseClient.CreateAndConnectSession()

at Renci.SshNet.BaseClient.Connect()

when trying to connect vis Putty everything works just fine.

` var keyboardAuth = new KeyboardInteractiveAuthenticationMethod(username);
keyboardAuth.AuthenticationPrompt += keyboardAuthentication_AuthenticationPrompt;

            var passwordAuth = new Renci.SshNet.PasswordAuthenticationMethod(username, password.GetStringFromSecureString());

            var connectionInfo = new Renci.SshNet.ConnectionInfo(machine.Address, port, username, keyboardAuth, passwordAuth);
            connectionInfo.Timeout = connectionTimeout;

            _client = new Renci.SshNet.SshClient(connectionInfo);

_client.Connect()`

could you please explain what is happening here?

@amitbarkai
Copy link
Author

amitbarkai commented Nov 16, 2023

i think i've found the issue which might point to a BUG
in my case the authentication method order was :

  • keyboard-interactive
  • username password

code example :
`
// Create KeyboardInteractiveAuthenticationMethod
var keyboardAuth = new KeyboardInteractiveAuthenticationMethod(username);
keyboardAuth.AuthenticationPrompt += keyboardAuthentication_AuthenticationPrompt;

    // Create ConnectionInfo with both authentication methods
    var connectionInfo = new ConnectionInfo(host, port, username, keyboardAuth, passwordAuth);

`

when there is an issue / authentication method not defined
"If there is no prompt defined on the server for keyboard-interactive authentication, it could potentially lead to authentication failures, but it's not the typical cause for a NullReferenceException within the SSH.NET"

when i've change the order / removed the user interactive everything worked fine

@lifeincha0s
Copy link

I came across this same issue when I was implementing some of the more recent updates to the library. It has since been fixed in the latest builds. I started with a 2016.x.x version (in 2017) and I highly modified the library to meet my specific needs. The commit "Allow for easier troubleshooting of protocol version exchange." broke it. The issue stems from Session.Connect().

Prior to 2020.x.x:

// Set connection versions
ConnectionInfo.ServerVersion = ServerVersion; // where ServerVersion is initialized in while loop prior
ConnectionInfo.ClientVersion = ClientVersion;

2020.x.x

// Set connection versions
ConnectionInfo.ServerVersion = serverIdentification.ToString();
ConnectionInfo.ClientVersion = ClientVersion;

Latest Build:

// Set connection versions
ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
ConnectionInfo.ClientVersion = ClientVersion;

Session.ClientVersion is initialized in Session.Session(), which, IMO, is a really bad place to put it. So, I changed it to a static readonly string, dropped it to ConnectionInfo (since it is technically connection information), and updated Session.ClientVersion to pull from there. However for 2020.x.x, the values are simply passed down to ConnectionInfo and Session.Serversion is never initialized, so the exception is thrown in CalculateHash() when passing Session.ServerVersion to ExchangeHashData. The latest version fixed that, but it seems a bit redundant since it can be fixed without modifying Session.Connect() by modifying the local properties:

Session.ServerVersion
{
	get { return ConnectionInfo.ServerVersion; }
}
Session.ClientVersion
{
	get { return ConnectionInfo.ClientVersion; }
}

Cheers,

@amitbarkai
Copy link
Author

@lifeincha0s
thanks for the replay
since this issue happened for one of our customers , we are still trying to understand
what is preventing the keyboard-interactive from working.

having said that this is clearly a bug in the SSH.NET
throwing this ArgumentNullException

@amitbarkai
Copy link
Author

continue to our conversation ..
private void keyboardAuthentication_AuthenticationPrompt(object sender, Renci.SshNet.Common.AuthenticationPromptEventArgs e)
{
if (ConnectionInfo.AuthenticationMethod is PasswordAuthenticationMethod)
{
var passwordConnection = (PasswordAuthenticationMethod)ConnectionInfo.AuthenticationMethod;
var passwordRegex = new Regex(IOC.Get.Service().LoginPasswordPrompt, RegexOptions.Multiline | RegexOptions.IgnoreCase);

        IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR618I, LogTarget.Trace);

        foreach (var prompt in e.Prompts)
        {
           IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR619I, LogTarget.Trace, prompt.Request);
           if (passwordRegex.IsMatch(prompt.Request))
           {
              prompt.Response = passwordConnection.Password.GetStringFromSecureString();
           }
           else
           {
              IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR673E, LogTarget.Trace, prompt.Request);
           }
        }
     }
  }

this is the AuthenticationPrompt implementation function
and i would expect to see any of my logs , in case this issue is on our code.

but for now it seems , at least for me , the issue is on the SSH.NET side no ?

@Rob-Hague
Copy link
Collaborator

if (ConnectionInfo.AuthenticationMethod is PasswordAuthenticationMethod) looks suspicious to me. Can you try without it?

@amitbarkai
Copy link
Author

unfortunately , no , this is the first time this issue happens for us :)
and we have more than 8K customers and much more traget machines.

this issue happens only for this customer .
not sure if it is related he has BeyhondTrust (PBrun) installed on this target machine

NOTE : this issue happens for domain user only , for local user it works fine

@Rob-Hague
Copy link
Collaborator

OK, understood. I'm sorry but I don't think any of that is related to SSH.NET and I can't help you on that.

If you believe it is related, you can try troubleshooting using https://github.com/sshnet/SSH.NET/wiki/Troubleshooting-SSH.NET and report back. I hope you find a solution.

@amitbarkai
Copy link
Author

i have to admit it sounds like an adge case , when something is blocking the Keyboard-interactive
in either domain / gpo level.

we are still waiting to get more information about the customer environment , so we could understand what is the issue

@amitbarkai
Copy link
Author

amitbarkai commented Dec 4, 2023

would like to better understand what can / needs to be done
this is our ssh client object creation - using two authentication methods ( with that order
1.keyboard-interactive
2.password authenticaiton

      public SshClient(IMachine machine, string username, SecureString password, int port, TimeSpan connectionTimeout)
      {
         try
         {
            ConnectionInfo = new PasswordConnectionInfo(machine.Address, username, password, port);
            var authenticationMethods = new List<Renci.SshNet.AuthenticationMethod>();
            var includeKeyboardInteractiveMethod = IOC.Get.Service<IConfig>().GetBool("IncludeKeyboardInteractiveMethod", true, false);
            var keyboardAuth = new KeyboardInteractiveAuthenticationMethod(username);
            keyboardAuth.AuthenticationPrompt += keyboardAuthentication_AuthenticationPrompt;
            IOC.Get.Service<ILogService>().Write($"IncludeKeyboardInteractiveMethod value is : {includeKeyboardInteractiveMethod}", LogTarget.Trace);
            if (includeKeyboardInteractiveMethod)
            {
                authenticationMethods.Add(keyboardAuth);
            }
            authenticationMethods.Add(new Renci.SshNet.PasswordAuthenticationMethod(username, password.GetStringFromSecureString()));

            var connectionInfo = new Renci.SshNet.ConnectionInfo(machine.Address, port, username, authenticationMethods.ToArray());
            connectionInfo.Timeout = connectionTimeout;

            _client = new Renci.SshNet.SshClient(connectionInfo);

            setShellColumns(machine);
         }
         catch (Exception ex)
         {
            throw new Exception(string.Format(LogMessages.DNAPR209E, ex.Message), ex);
         }
      }

the implementation for keyboardAuthentication_AuthenticationPrompt is

      private void keyboardAuthentication_AuthenticationPrompt(object sender, Renci.SshNet.Common.AuthenticationPromptEventArgs e)
      {
         if (ConnectionInfo.AuthenticationMethod is PasswordAuthenticationMethod)
         {
            var passwordConnection = (PasswordAuthenticationMethod)ConnectionInfo.AuthenticationMethod;
            var passwordRegex = new Regex(IOC.Get.Service<IUnixConfigReader>().LoginPasswordPrompt, RegexOptions.Multiline | RegexOptions.IgnoreCase);


            IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR618I, LogTarget.Trace);

            foreach (var prompt in e.Prompts)
            {
               IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR619I, LogTarget.Trace, prompt.Request);
               if (passwordRegex.IsMatch(prompt.Request))
               {
                  prompt.Response = passwordConnection.Password.GetStringFromSecureString();
               }
               else
               {
                  IOC.Get.Service<ILogService>().Write(LogMessages.DNAPR673E, LogTarget.Trace, prompt.Request);
               }
            }
         }
      }

** this is the Login prompt regex - @"^Password: ?$|'+s password: ?$";
the exception is thrown from the connect function showing the following logs

11/9/2023 8:40:27 AM [5960][7] - Connect - DNAPR722E Failed to initialize SSH client to remote machine Machine-IP. Error: String reference not set to an instance of a String.
Parameter name: s.
11/9/2023 8:40:27 AM [5960][7] - ConnectClient - Connect Exception data: System.ArgumentNullException: String reference not set to an instance of a String.
Parameter name: s
at Renci.SshNet.KeyboardInteractiveAuthenticationMethod.Authenticate(Session session)
at Renci.SshNet.ClientAuthentication.TryAuthenticate(ISession session, AuthenticationState authenticationState, String[] allowedAuthenticationMethods, SshAuthenticationException& authenticationException)
at Renci.SshNet.ClientAuthentication.Authenticate(IConnectionInfoInternal connectionInfo, ISession session)
at Renci.SshNet.ConnectionInfo.Authenticate(ISession session, IServiceFactory serviceFactory)
at Renci.SshNet.Session.Connect()
at Renci.SshNet.BaseClient.CreateAndConnectSession()
at Renci.SshNet.BaseClient.Connect()
at Cyberark.DNA.Common.Communication.SSH.SshClient.Connect()

as you could see the DNAPR618I is not show in the logs ( so something is clearly failing in an earlier stage)
our customer was able to pin point the issue when
his prompt was :
Password for username@domain:
which seems to affect to login process

when the bold section was removed from the prompt everything worked fine.
regardless of the above , i would expect when providing 2 authentication methods
would this additional information help ?

would you agree , that in case one , authentication throws an exception
we should try and move to the next one ?

thanks

@Rob-Hague
Copy link
Collaborator

(FYI I edited your comment to format the code blocks: you need to surround with 3 ticks instead of 1 (```)

I don't know what ConnectionInfo is in your code, but I'm guessing it is not Renci.SshNet.ConnectionInfo. Could the if (ConnectionInfo.AuthenticationMethod is PasswordAuthenticationMethod) be false which stops DNAPR618I? If it is true then I can't explain why the log does not show.

Aside from that, your regex will indeed not match "Password for username@domain", so maybe the regex could be changed.

As for whether the authentication process should continue on exception, it will already continue if for example, the wrong key or password is given as part of the normal procedure. But in an "exceptional" case like this one, which in fact indicates a problem in the code, I do not think the process should continue.

What you could do is have an else in keyboardAuthentication_AuthenticationPrompt which just sets all the prompt.Response = "".

Similarly we could null-coalesce the response to an empty string during serialisation, but (again) I think this is working around incorrect usage of the code. I don't have strong opinion here.

@amitbarkai
Copy link
Author

amitbarkai commented Dec 5, 2023 via email

@amitbarkai
Copy link
Author

amitbarkai commented Dec 5, 2023 via email

@WojciechNagorski WojciechNagorski added this to the 2023.0.1 milestone Dec 20, 2023
@WojciechNagorski
Copy link
Collaborator

The 2023.0.1 version has been released to Nuget: https://www.nuget.org/packages/SSH.NET/2023.0.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants