aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src/ssh_connection_handler.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src/ssh_connection_handler.erl')
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl121
1 files changed, 72 insertions, 49 deletions
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 4158a52a27..0ca960ef96 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -46,6 +46,7 @@
%%% Internal application API
-export([start_connection/4,
+ available_hkey_algorithms/2,
open_channel/6,
request/6, request/7,
reply_request/3,
@@ -324,23 +325,32 @@ renegotiate_data(ConnectionHandler) ->
%% Internal process state
%%====================================================================
-record(data, {
- starter :: pid(),
+ starter :: pid()
+ | undefined,
auth_user :: string()
| undefined,
connection_state :: #connection{},
- latest_channel_id = 0 :: non_neg_integer(),
+ latest_channel_id = 0 :: non_neg_integer()
+ | undefined,
idle_timer_ref :: undefined
| infinity
| reference(),
idle_timer_value = infinity :: infinity
| pos_integer(),
- transport_protocol :: atom(), % ex: tcp
- transport_cb :: atom(), % ex: gen_tcp
- transport_close_tag :: atom(), % ex: tcp_closed
- ssh_params :: #ssh{},
- socket :: inet:socket(),
- decrypted_data_buffer = <<>> :: binary(),
- encrypted_data_buffer = <<>> :: binary(),
+ transport_protocol :: atom()
+ | undefined, % ex: tcp
+ transport_cb :: atom()
+ | undefined, % ex: gen_tcp
+ transport_close_tag :: atom()
+ | undefined, % ex: tcp_closed
+ ssh_params :: #ssh{}
+ | undefined,
+ socket :: inet:socket()
+ | undefined,
+ decrypted_data_buffer = <<>> :: binary()
+ | undefined,
+ encrypted_data_buffer = <<>> :: binary()
+ | undefined,
undecrypted_packet_length :: undefined | non_neg_integer(),
key_exchange_init_msg :: #ssh_msg_kexinit{}
| undefined,
@@ -369,16 +379,17 @@ init_connection_handler(Role, Socket, Opts) ->
StartState,
D);
- {stop, enotconn} ->
- %% Handles the abnormal sequence:
- %% SYN->
- %% <-SYNACK
- %% ACK->
- %% RST->
- exit({shutdown, "TCP connection to server was prematurely closed by the client"});
-
- {stop, OtherError} ->
- exit({shutdown, {init,OtherError}})
+ {stop, Error} ->
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ C = #connection{system_supervisor = proplists:get_value(system_sup, Sups),
+ sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
+ connection_supervisor = proplists:get_value(connection_sup, Sups)
+ },
+ gen_statem:enter_loop(?MODULE,
+ [],
+ {init_error,Error},
+ #data{connection_state=C,
+ socket=Socket})
end.
@@ -432,13 +443,12 @@ init_ssh_record(Role, Socket, Opts) ->
init_ssh_record(Role, Socket, PeerAddr, Opts).
init_ssh_record(Role, _Socket, PeerAddr, Opts) ->
- KeyCb = ?GET_OPT(key_cb, Opts),
AuthMethods = ?GET_OPT(auth_methods, Opts),
S0 = #ssh{role = Role,
- key_cb = KeyCb,
+ key_cb = ?GET_OPT(key_cb, Opts),
opts = Opts,
userauth_supported_methods = AuthMethods,
- available_host_keys = supported_host_keys(Role, KeyCb, Opts),
+ available_host_keys = available_hkey_algorithms(Role, Opts),
random_length_padding = ?GET_OPT(max_random_length_padding, Opts)
},
@@ -531,6 +541,21 @@ renegotiation(_) -> false.
callback_mode() ->
handle_event_function.
+
+handle_event(_, _Event, {init_error,Error}, _) ->
+ case Error of
+ enotconn ->
+ %% Handles the abnormal sequence:
+ %% SYN->
+ %% <-SYNACK
+ %% ACK->
+ %% RST->
+ {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}};
+
+ OtherError ->
+ {stop, {shutdown,{init,OtherError}}}
+ end;
+
%%% ######## {hello, client|server} ####
%% The very first event that is sent when the we are set as controlling process of Socket
handle_event(_, socket_control, {hello,_}, D) ->
@@ -1544,44 +1569,42 @@ peer_role(client) -> server;
peer_role(server) -> client.
%%--------------------------------------------------------------------
-supported_host_keys(client, _, Options) ->
- try
- find_sup_hkeys(Options)
- of
- [] ->
+available_hkey_algorithms(Role, Options) ->
+ KeyCb = ?GET_OPT(key_cb, Options),
+ case [A || A <- available_hkey_algos(Options),
+ (Role==client) orelse available_host_key(KeyCb, A, Options)
+ ] of
+
+ [] when Role==client ->
error({shutdown, "No public key algs"});
- Algs ->
- [atom_to_list(A) || A<-Algs]
- catch
- exit:Reason ->
- error({shutdown, Reason})
- end;
-supported_host_keys(server, KeyCb, Options) ->
- [atom_to_list(A) || A <- find_sup_hkeys(Options),
- available_host_key(KeyCb, A, Options)
- ].
+ [] when Role==server ->
+ error({shutdown, "No host key available"});
-find_sup_hkeys(Options) ->
- case proplists:get_value(public_key,
- ?GET_OPT(preferred_algorithms,Options)
- )
- of
- undefined ->
- ssh_transport:default_algorithms(public_key);
- L ->
- NonSupported = L--ssh_transport:supported_algorithms(public_key),
- L -- NonSupported
+ Algs ->
+ [atom_to_list(A) || A<-Algs]
end.
+available_hkey_algos(Options) ->
+ SupAlgos = ssh_transport:supported_algorithms(public_key),
+ HKeys = proplists:get_value(public_key,
+ ?GET_OPT(preferred_algorithms,Options)
+ ),
+ NonSupported = HKeys -- SupAlgos,
+ AvailableAndSupported = HKeys -- NonSupported,
+ AvailableAndSupported.
+
%% Alg :: atom()
available_host_key({KeyCb,KeyCbOpts}, Alg, Opts) ->
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok,_} -> true;
- _ -> false
+ {ok,Key} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ ssh_transport:valid_key_sha_alg(Key, Alg);
+ _ ->
+ false
end.