otp icon indicating copy to clipboard operation
otp copied to clipboard

SSH server exits with PROTOCOL_ERROR when receiving service_request in userauth state

Open yarisx opened this issue 3 years ago • 2 comments

Description The issue is strictly speaking not a bug rather than improvement. When the SSH server receives USERAUTH_REQUEST it starts user authentication, changing server's state to userauth. If SERVICE_REQUEST comes in this state the server drops the connection with PROTOCOL_ERROR. While the behaviour for such case is not described in the RFCs 4252, 4253 (if I'm reading it right) it does not harm to accept the SERVICE_REQUEST if it would be accepted in the previous state (i.e. if it would start normal authentication procedure if the server was not in the userauth state).

The behaviour with sending one SERVICE_REQUEST per authentication method is observed with Paramiko, maybe there are more clients that can do that.

Affected versions: OTP-23.2, OTP-26 (0113d9cc5a11a14bafdded89b0ba9d700bacd20d) configured as ./configure --enable-hipe --with-ssl-rpath=no --with-ssl=/usr/local/openssl-1.1.1d --enable-aslr --enable-small-memory Tested on Linux, Mac OSX.

Attached is the archive with test and printouts from Erlang. ssh_proto_error.zip

To reproduce the issue prerequisites are: Installed OTP with erl and erlc in PATH, Python3 installed with Paramiko (I have ver 2.11 locally) Unpack the archive, run "make -f Makefile.protoerr build start test". The printout from Paramiko will show "Disconnect (code 2): Protocol error"

A patch that could solve the issue:

--- a/lib/ssh/src/ssh_fsm_userauth_server.erl
+++ b/lib/ssh/src/ssh_fsm_userauth_server.erl
@@ -119,6 +119,23 @@ handle_event(internal,
             {stop, Shutdown, D}
     end;

+handle_event(internal, Msg = #ssh_msg_service_request{name=ServiceName},
+             StateName = {userauth,server}, D0) ->
+    case ServiceName of
+       "ssh-userauth" ->
+           Ssh0 = #ssh{session_id=SessionId} = D0#data.ssh_params,
+           {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
+            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+            {keep_state, D};
+
+       _ ->
+            {Shutdown, D} =
+                ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+                                 io_lib:format("Unknown service: ~p",[ServiceName]),
+                                 StateName, D0),
+            {stop, Shutdown, D}
+    end;
+
 handle_event(internal, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D0) ->
     case ssh_auth:handle_userauth_info_response(Msg, D0#data.ssh_params) of
        {authorized, User, {Reply, Ssh1}} ->

yarisx avatar Nov 14 '22 13:11 yarisx

thank you for report, we planned looking into it.

u3s avatar Nov 18 '22 11:11 u3s

sorry for delay and thanks for detailed reproduction steps. I've reproduced the issue and see that Paramiko client sends another USERAUTH_REQUEST because it received negative response for first one. Erlang server remains in user_auth state and is not ready to handle another request.

connection with OpenSSH sshd is possible with the same Python script and Paramiko client.

i will try to investigate it further soonish.

u3s avatar Sep 10 '24 07:09 u3s