Skip to content

Commit

Permalink
ssh: avoid protocol error on userauth banner
Browse files Browse the repository at this point in the history
SSH servers can send userauth banners at any time during authentication
and the erlang SSH client only accepted userauth banners in some limited
circumstances.

Closes #9065
  • Loading branch information
alexandrejbr committed Dec 10, 2024
1 parent 0418c10 commit 42050aa
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
5 changes: 4 additions & 1 deletion lib/ssh/src/ssh_fsm_userauth_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ handle_event(internal, #ssh_msg_userauth_failure{authentications = Methods}, Sta
end;

%%---- banner to client
handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) ->
handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {S,client}, D)
when S == userauth; S == userauth_keyboard_interactive;
S == userauth_keyboard_interactive_extra;
S == userauth_keyboard_interactive_info_response ->
case D#data.ssh_params#ssh.userauth_quiet_mode of
false -> io:format("~s", [Msg]);
true -> ok
Expand Down
79 changes: 78 additions & 1 deletion lib/ssh/test/ssh_protocol_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
bad_service_name_then_correct/1,
bad_very_long_service_name/1,
client_handles_keyboard_interactive_0_pwds/1,
client_handles_banner_keyboard_interactive/1,
client_info_line/1,
do_gex_client_init/3,
do_gex_client_init_old/3,
Expand Down Expand Up @@ -144,7 +145,8 @@ groups() ->
empty_service_name,
bad_service_name_then_correct
]},
{authentication, [], [client_handles_keyboard_interactive_0_pwds
{authentication, [], [client_handles_keyboard_interactive_0_pwds,
client_handles_banner_keyboard_interactive
]},
{ext_info, [], [no_ext_info_s1,
no_ext_info_s2,
Expand Down Expand Up @@ -682,7 +684,82 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
]}]
).

%%%--------------------------------------------------------------------
%%% SSH_MSG_USERAUTH_BANNER can be sent at any time during user auth.
%%% The following test mimics a SSH server implementation that sends the banner
%%% immediately before sending SSH_MSG_USERAUTH_SUCCESS.
client_handles_banner_keyboard_interactive(Config) ->
{User,_Pwd} = server_user_password(Config),

%% Create a listening socket as server socket:
{ok,InitialState} = ssh_trpt_test_lib:exec(listen),
HostPort = ssh_trpt_test_lib:server_host_port(InitialState),

%% Start a process handling one connection on the server side:
spawn_link(
fun() ->
{ok,_} =
ssh_trpt_test_lib:exec(
[{set_options, [print_ops, print_messages]},
{accept, [{system_dir, system_dir(Config)},
{user_dir, user_dir(Config)}]},
receive_hello,
{send, hello},

{send, ssh_msg_kexinit},
{match, #ssh_msg_kexinit{_='_'}, receive_msg},

{match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
{send, ssh_msg_kexdh_reply},

{send, #ssh_msg_newkeys{}},
{match, #ssh_msg_newkeys{_='_'}, receive_msg},

{match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
{send, #ssh_msg_service_accept{name="ssh-userauth"}},

{match, #ssh_msg_userauth_request{service="ssh-connection",
method="none",
user=User,
_='_'}, receive_msg},
{send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive",
partial_success = false}},

{match, #ssh_msg_userauth_request{service="ssh-connection",
method="keyboard-interactive",
user=User,
_='_'}, receive_msg},
{send, #ssh_msg_userauth_info_request{name = "",
instruction = "",
language_tag = "",
num_prompts = 1,
data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>>
}},
{match, #ssh_msg_userauth_info_response{num_responses = 1,
_='_'}, receive_msg},
{send, #ssh_msg_userauth_info_request{name = "",
instruction = "",
language_tag = "",
num_prompts = 0,
data = <<>>
}},
{match, #ssh_msg_userauth_info_response{num_responses = 0,
data = <<>>,
_='_'}, receive_msg},
{send, #ssh_msg_userauth_banner{message = "Banner\n"}},
{send, #ssh_msg_userauth_success{}},
close_socket,
print_state
],
InitialState)
end),

%% and finally connect to it with a regular Erlang SSH client:
{ok,_} = std_connect(HostPort, Config,
[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
{cipher,?DEFAULT_CIPHERS}
]}]
).

%%%--------------------------------------------------------------------
client_info_line(Config) ->
Expand Down

0 comments on commit 42050aa

Please sign in to comment.