How to handle unexpected authentication requests properly.

Mar 29, 2014 at 5:40 PM
I have a situation where executing some commands via ShellStream.WriteLine can result in unexpected authentication requests. I'm trying to figure out how to handle this properly in order to show a password request dialog to the user.

AFAICT, authentication requests are only handled during the time when the connection is established (via one or more AuthenticationMethod passed in ConnectionInfo's authenticationMethods parameter).

So the question is, how do I properly handle these unexpected authentication requests after the connection has already been successfully established?
Mar 29, 2014 at 6:49 PM
Edited Mar 29, 2014 at 7:14 PM
Deleted due to being unrelated to this issue.
Mar 29, 2014 at 7:01 PM
Those "unexpected authentication request" aren't from SSH, right?
If it's sudo, or any other command, you can parse the Output yourself by using async read/write or use the expect().
Mar 29, 2014 at 7:14 PM
da_rinkes wrote:
Those "unexpected authentication request" aren't from SSH, right?
If it's sudo, or any other command, you can parse the Output yourself by using async read/write or use the expect().
No those are not from SSH. As an example, if you use Perforce for as your SCM, then when executing Perforce commands, it can potentially result in a password prompt at any time because the Perforce session has a limited lifetime. So you might happily be executing Perforce commands, and then unexpectedly you get a password prompt in order to renew your login. This is just one example, but I have other cases like this too.

Note again I'm re-using the same channel to run multiple commands by calling ShellStream.WriteLine and responding to the output. I don't know if that is significant in this case.

BTW please ignore the second post, as it is mostly unrelated to the authentication issue.
Mar 29, 2014 at 7:24 PM
K, then you have to do it on your own by parsing the Output and ask the user if a password is necessary.
SSH.Net can't help you here, since it is not part of SSH protocol.

Sounds like you can't use expect() since those password-prompts appear unexpected.
So you have to do it async and parse the shell output if it contains a password request.
But it isn't that hard.

The tests are a great source for examples, e.g:

Test_Execute_Command_Asynchronously() and further.
Mar 29, 2014 at 7:27 PM
Excellent, thanks for your help! I'll take a look at those examples.
Mar 29, 2014 at 9:13 PM
Just one thing I don't understand...

Whenever these unexpected authentication requests occur, the client (like Putty) knows to hide the characters that the user types. So the client knows that this is a password request. I'm thinking that SSH.NET is maybe hiding this fact. Maybe it is because SSH.NET unregisters the authentication related messages after it connects. Seems it should keep these messages registered throughout the session to handle these authentication requests properly.

I'm not an SSH guru so my assumptions might be wrong. I plan to debug this further to see if the above is a correct assumption.
Mar 29, 2014 at 9:44 PM
Aye, you don't understand SSH.
SSH has nothing to do with what happens in the shell, it just gives you access to it.
Not putty is hiding the password, the program in the shell hides the password.
The password request is not a SSH password request, it's a password request of the program.
Perforce is asking for a password, not SSH! It happens in the shell, not in the SSH connection.
I think you should read more about SSH, shell and so on.
Without basic knowledge you get lost IMHO.
Mar 29, 2014 at 9:46 PM
OK thanks. As I said I'm not familiar with SSH, but what you say makes sense and I'll manually parse for password requests.
Mar 29, 2014 at 10:52 PM
I got it to work by parsing the text and writing the password (and appending "\r\n") when requested. Only thing is that it seems a bit unreliable since one doesn't always know exactly what the request text will look like (let alone localized password requests).

Seems there should be a better way.
Mar 30, 2014 at 9:42 AM
Cool, you seem to start understand the problem :)
What about setting the LOCALE environment, if your tools respect this, it should work more reliable.
Mar 30, 2014 at 3:02 PM
Fortunately in my case this is part of an internal tool that is used by me and a few other developers, so the environment should be reasonably predictable and stable. I'm just thinking that in general this would not be a very good solution due to variations in password request text as well as the issue with localization.
Mar 30, 2014 at 3:55 PM
It is actually the only good and properly working solution.
And the problem with localization is one reason, beside its not a SSH problem, why it isn't part of any SSH library.
The environment differs on every machine and the developer on the client side has to ensure it works.

Nobody knows how all programs ask for credentials.
One is asking "Username", another "Login" or "EMail", "Password", "Auth" and so on.

You have to accept this is not "Hello World hacking". Automation or interaction with 3rd party software is never an
easy task.

So pining the environment your app is working in, is the only solution.

At least SSH.Net gives you the freedom to accomplish this task and thats good.
Mar 30, 2014 at 7:02 PM
Edited Mar 30, 2014 at 7:08 PM
I'm actually running into a weird problem. I got this to work reliably with other use cases except for Perforce. For some reason I can't get it to recognize the password I'm sending. I tried sending it as a complete string using ShellStream.WriteLine, as well as one character at a time using ShellStream.Write. I even added some delay between each password character that is sent but still no luck.

I also captured a Putty session where it does accept the password, and tried to simulate the exact character sequence but still no luck.

Here is a Putty session of a successful login. I changed the password to "foo":
Incoming packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 01 00 00 00 00 10 45 6e 74 65 72 20 70 61  ........Enter pa
  00000010  73 73 77 6f 72 64 3a 20                          ssword: 
Outgoing packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 00 00 00 00 00 01 66                       ........f
Incoming packet type 2 / 0x02 (SSH2_MSG_IGNORE)
  00000000  00 00 00 05 db 7c eb 90 d4                       .....|...
Outgoing packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 00 00 00 00 00 01 6f                       ........o
Incoming packet type 2 / 0x02 (SSH2_MSG_IGNORE)
  00000000  00 00 00 05 45 19 e9 63 bf                       ....E..c.
Outgoing packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 00 00 00 00 00 01 6f                       ........o
Incoming packet type 2 / 0x02 (SSH2_MSG_IGNORE)
  00000000  00 00 00 05 51 89 09 fa 51                       ....Q...Q
Outgoing packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 00 00 00 00 00 01 0d                       .........
Incoming packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
  00000000  00 00 01 00 00 00 00 02 0d 0a                    ..........
Incoming packet type 94 / 0x5e (SSH2_MSG_CHANNEL_DATA)
... Password accepted....
I did notice the single \r and then a \r\n sequence, but even simulating that sequence doesn't work as it keeps refusing the password.

EDIT: Ignore the final \r\n as it is actually an incoming packet so is not relevant to the issue. I did try sending just a \r instead of \r\n but that didn't make a difference either.

Any ideas?
Apr 6, 2014 at 5:22 PM
Please try using the latest (beta) release of SSH.NET, and post the - smallest possible, but complete - code fragment that you use to reproduce this issue.

Apr 6, 2014 at 9:33 PM
Thanks for the response, I should probably have updated this thread that I've found a workaround for this issue in the meantime.

Basically, I believe this could be a Perforce specific issue, so unless you can log into Perforce on your end, sample code alone will not be able to reproduce this. I have since found an alternative way to log into Perforce so I'm no longer looking for a solution.

I'm still unsure and curious though why Perforce rejected the login attempts in the first place. It seems like it should work since I'm sending the exact sequence of packets that Putty does. To verify this I added some debugging code into SSH.Net that outputs similar packet info as we see from Putty above, and it looks practically identical.
Apr 7, 2014 at 6:24 AM
Can you try again using the beta we released yesterday ?
If not, would it possible to temporarily share your perforce server with me (privately) ?
I assume the problem is related to the "practically" identical packets :-)

Apr 7, 2014 at 12:16 PM
I will try again with the latest beta and let you know. However I would need to first port some of my local changes over to the new code so it might take some time.

Unfortunately I can't share my Perforce account since it is on an internal company network.
Apr 7, 2014 at 3:54 PM
OK I tried the latest beta and can no longer reproduce the issue! Whatever you changed must have fixed it. Perforce is now accepting a password when initiating a request with "p4 login". Previously it would just not accept it.

I prefer using "p4 login" because the work-around I used was to run another script that for some reason caused the login to be quite slow, although it did ultimately succeed.

Just curious, do you know of any changes you might have made that could have fixed this?
Marked as answer by drieseng on 4/8/2014 at 5:19 AM
Apr 8, 2014 at 12:19 PM
Yes, I modified the ShellStream.WriteLine method to use CR instead of CRLF.

Thanks for the feedback!
Apr 8, 2014 at 12:34 PM
Ah, makes sense now :)

Thanks for all the hard work, much appreciated!