From 42050aad12179197ede91d554c84bf5a7eb1b0d2 Mon Sep 17 00:00:00 2001 From: Alexandre Rodrigues Date: Tue, 3 Dec 2024 20:14:59 +0100 Subject: [PATCH] ssh: avoid protocol error on userauth banner 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 --- lib/ssh/src/ssh_fsm_userauth_client.erl | 5 +- lib/ssh/test/ssh_protocol_SUITE.erl | 79 ++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/ssh/src/ssh_fsm_userauth_client.erl b/lib/ssh/src/ssh_fsm_userauth_client.erl index 73c8446639d4..124d07e79c2b 100644 --- a/lib/ssh/src/ssh_fsm_userauth_client.erl +++ b/lib/ssh/src/ssh_fsm_userauth_client.erl @@ -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 diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 666ac76f6334..558cfb8d4ab5 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -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, @@ -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, @@ -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) ->