Skip to content

Ignore data of SSH_MSG_IGNORE when its specified length is greater than the actual available bytes #41

Closed
@rhegner

Description

@rhegner

I'm getting an ArgumentOutOfRangeException from within SshDataStream.ReadBytes when I execute the following code:

using (var ssh = new SshClient("ip", 22, "usr", "pwd"))
{
    ssh.Connect();
    IDictionary<Renci.SshNet.Common.TerminalModes, uint> termkvp = new Dictionary<Renci.SshNet.Common.TerminalModes, uint>();
    termkvp.Add(Renci.SshNet.Common.TerminalModes.ECHO, 53);

    using (var stream = ssh.CreateShellStream("xterm", 80, 24, 800, 600, 1024, termkvp))
    using (var sr = new StreamReader(stream))
    {

        // wait for command promt
        var ln = ReadLine(sr, ">");
        while ((ln == null) || !ln.EndsWith(">"))
            ln = ReadLine(sr, ">");

        // send enable
        stream.WriteLine("enable");

        // wait for password promt
        ln = ReadLine(sr, "Password: ");
        while ((ln == null) || !ln.Equals("Password: "))
            ln = ReadLine(sr, "Password: ");

        // send password
        stream.WriteLine("enable-pwd");

        // wait for command promt
        ln = ReadLine(sr, "#");
        while ((ln == null) || !ln.EndsWith("#"))
            ln = ReadLine(sr, "#");
        var commandPromt = ln;
    }

    ssh.Disconnect();
}

Background: This code is for connecting to a Cisco router. I need privileged access to the router so I send the "enable" command. The router then promts for a password. The router does not echo the password. And I think that's exactly what confuses SshDataStream.ReadBytes. The problem occurs only after sending the (hidden) password to the router.

Here is the stack trace from the exception:

Renci.SshNet.dll!Renci.SshNet.Common.SshDataStream.ReadBytes(int length) Line 222
Renci.SshNet.dll!Renci.SshNet.Common.SshDataStream.ReadUInt32() Line 173
Renci.SshNet.dll!Renci.SshNet.Common.SshDataStream.ReadString(System.Text.Encoding encoding) Line 198
Renci.SshNet.dll!Renci.SshNet.Common.SshData.ReadString(System.Text.Encoding encoding) Line 267
Renci.SshNet.dll!Renci.SshNet.Common.SshData.ReadNamesList() Line 289
Renci.SshNet.dll!Renci.SshNet.Messages.Transport.KeyExchangeInitMessage.LoadData() Line 146
Renci.SshNet.dll!Renci.SshNet.Common.SshData.Load(byte[] value, int offset) Line 126
Renci.SshNet.dll!Renci.SshNet.Session.LoadMessage(byte[] data, int offset) Line 1642
Renci.SshNet.dll!Renci.SshNet.Session.ReceiveMessage() Line 954
Renci.SshNet.dll!Renci.SshNet.Session.MessageListener() Line 1791
Renci.SshNet.dll!Renci.SshNet.Abstractions.ThreadAbstraction.ExecuteThread.AnonymousMethod__0(object o) Line 32
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object state)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

When I remove the check which causes the exception to be thrown

    if (bytesRead < length)
        throw new ArgumentOutOfRangeException("length");

from SshDataStream.ReadBytes, everything works fine.

So I'm wondering: Am I doing something wrong or is this a bug in SSH.NET? Could you remove that check from SshDataStream.ReadBytes? If not, is there something else you can do to not getting messed up by such a hidden password use-case?

Note that the ReadLine method I'm using above is a custom one which basically reads from the underlying stream byte by byte. But the problem described above also occured when I experimented with ShellStream.Expect or ShellStream.ReadLine. Nevertheless, here is the code of my ReadLine method, just for reference:

private static string ReadLine(StreamReader stream, params string[] acceptedLineEndings)
{
    var ret = "";
    var eol = false;
    while (!eol)
    {
        var b = stream.Read();
        if (b >= 0)
        {
            var c = (char)b;
            if (c == '\r')
            {

            }
            else if (c == '\n')
            {
                eol = true;
            }
            else
            {
                ret += (char)b;
            }
        }
        else
        {
            // if no more data and received data ends with a promt char, we return as well
            foreach (var le in acceptedLineEndings)
            {
                if (ret.EndsWith(le))
                {
                    eol = true;
                    break;
                }
            }
        }
    }
    return ret;
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions