Skip to content

SSH server exits with PROTOCOL_ERROR when receiving service_request in userauth state #6463

Closed
@yarisx

Description

@yarisx

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 (0113d9c) 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}} ->

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions