1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% 21%%---------------------------------------------------------------------- 22%% Purpose: Handles an ssh connection, e.i. both the 23%% setup SSH Transport Layer Protocol (RFC 4253), Authentication 24%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255) 25%% Details of the different protocols are 26%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl 27%% ---------------------------------------------------------------------- 28 29-module(ssh_fsm_userauth_client). 30 31-include("ssh.hrl"). 32-include("ssh_transport.hrl"). 33-include("ssh_auth.hrl"). 34-include("ssh_connect.hrl"). 35 36-include("ssh_fsm.hrl"). 37 38%%==================================================================== 39%%% Exports 40%%==================================================================== 41 42%%% Behaviour callbacks 43-export([callback_mode/0, handle_event/4, terminate/3, 44 format_status/2, code_change/4]). 45 46%%==================================================================== 47%% gen_statem callbacks 48%%==================================================================== 49 50callback_mode() -> 51 [handle_event_function, 52 state_enter]. 53 54%%-------------------------------------------------------------------- 55 56%%% ######## {userauth, client} #### 57 58%%---- #ssh_msg_ext_info could follow after the key exchange, both the intial and the re-negotiation 59handle_event(internal, #ssh_msg_ext_info{}=Msg, {userauth,client}, D0) -> 60 %% FIXME: need new state to receive this msg! 61 D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0), 62 {keep_state, D}; 63 64%%---- recevied userauth success from the server 65handle_event(internal, #ssh_msg_userauth_success{}, {userauth,client}, D0=#data{ssh_params = Ssh}) -> 66 ssh_auth:ssh_msg_userauth_result(success), 67 ssh_connection_handler:handshake(ssh_connected, D0), 68 D = D0#data{ssh_params=Ssh#ssh{authenticated = true}}, 69 {next_state, {connected,client}, D, {change_callback_module,ssh_connection_handler}}; 70 71 72%%---- userauth failure response to clientfrom the server 73handle_event(internal, #ssh_msg_userauth_failure{}, {userauth,client}=StateName, 74 #data{ssh_params = #ssh{userauth_methods = []}} = D0) -> 75 {Shutdown, D} = 76 ?send_disconnect(?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 77 io_lib:format("User auth failed for: ~p",[D0#data.auth_user]), 78 StateName, D0), 79 {stop, Shutdown, D}; 80 81handle_event(internal, #ssh_msg_userauth_failure{authentications = Methods}, StateName={userauth,client}, 82 D0 = #data{ssh_params = Ssh0}) -> 83 %% The prefered authentication method failed, try next method 84 Ssh1 = case Ssh0#ssh.userauth_methods of 85 none -> 86 %% Server tells us which authentication methods that are allowed 87 Ssh0#ssh{userauth_methods = string:tokens(Methods, ",")}; 88 _ -> 89 %% We already know... 90 Ssh0 91 end, 92 case ssh_auth:userauth_request_msg(Ssh1) of 93 {send_disconnect, Code, Ssh} -> 94 {Shutdown, D} = 95 ?send_disconnect(Code, 96 io_lib:format("User auth failed for: ~p",[D0#data.auth_user]), 97 StateName, D0#data{ssh_params = Ssh}), 98 {stop, Shutdown, D}; 99 {"keyboard-interactive", {Msg, Ssh}} -> 100 D = ssh_connection_handler:send_msg(Msg, D0#data{ssh_params = Ssh}), 101 {next_state, {userauth_keyboard_interactive,client}, D}; 102 {_Method, {Msg, Ssh}} -> 103 D = ssh_connection_handler:send_msg(Msg, D0#data{ssh_params = Ssh}), 104 {keep_state, D} 105 end; 106 107%%---- banner to client 108handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) -> 109 case D#data.ssh_params#ssh.userauth_quiet_mode of 110 false -> io:format("~s", [Msg]); 111 true -> ok 112 end, 113 keep_state_and_data; 114 115 116%%% ######## {userauth_keyboard_interactive, client} 117 118handle_event(internal, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client}, 119 #data{ssh_params = Ssh0} = D0) -> 120 case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of 121 {ok, {Reply, Ssh}} -> 122 D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}), 123 {next_state, {userauth_keyboard_interactive_info_response,client}, D}; 124 not_ok -> 125 {next_state, {userauth,client}, D0, [postpone]} 126 end; 127 128handle_event(internal, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client}, 129 #data{ssh_params = Ssh0} = D0) -> 130 Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference, 131 Method =/= "keyboard-interactive"], 132 D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}}, 133 {next_state, {userauth,client}, D, [postpone]}; 134 135handle_event(internal, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client}, 136 #data{ssh_params = Ssh0} = D0) -> 137 Opts = Ssh0#ssh.opts, 138 D = case ?GET_OPT(password, Opts) of 139 undefined -> 140 D0; 141 _ -> 142 D0#data{ssh_params = 143 Ssh0#ssh{opts = ?PUT_OPT({password,not_ok}, Opts)}} % FIXME:intermodule dependency 144 end, 145 {next_state, {userauth,client}, D, [postpone]}; 146 147handle_event(internal, #ssh_msg_ext_info{}=Msg, {userauth_keyboard_interactive_info_response, client}, D0) -> 148 %% FIXME: need new state to receive this msg! 149 D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0), 150 {keep_state, D}; 151 152handle_event(internal, #ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) -> 153 {next_state, {userauth,client}, D, [postpone]}; 154 155handle_event(internal, #ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive_info_response, client}, D) -> 156 {next_state, {userauth_keyboard_interactive,client}, D, [postpone]}; 157 158 159%%% ######## UNHANDLED EVENT! 160handle_event(Type, Event, StateName, D) -> 161 ssh_connection_handler:handle_event(Type, Event, StateName, D). 162 163%%-------------------------------------------------------------------- 164format_status(A, B) -> 165 ssh_connection_handler:format_status(A, B). 166 167%%-------------------------------------------------------------------- 168terminate(Reason, StateName, D) -> 169 ssh_connection_handler:terminate(Reason, StateName, D). 170 171%%-------------------------------------------------------------------- 172code_change(_OldVsn, StateName, State, _Extra) -> 173 {ok, StateName, State}. 174 175%%==================================================================== 176%% Internal functions 177%%==================================================================== 178 179 180