diff options
Diffstat (limited to 'lib/ssh')
-rw-r--r-- | lib/ssh/doc/src/notes.xml | 105 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_sftp.xml | 8 | ||||
-rw-r--r-- | lib/ssh/src/ssh.erl | 60 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 23 | ||||
-rw-r--r-- | lib/ssh/src/ssh_options.erl | 12 | ||||
-rw-r--r-- | lib/ssh/src/ssh_sftp.erl | 13 | ||||
-rw-r--r-- | lib/ssh/src/ssh_sftpd.erl | 12 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 8 | ||||
-rw-r--r-- | lib/ssh/src/ssh_xfer.erl | 2 | ||||
-rw-r--r-- | lib/ssh/test/ssh_basic_SUITE.erl | 20 | ||||
-rw-r--r-- | lib/ssh/vsn.mk | 2 |
11 files changed, 222 insertions, 43 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index db60b4ab6f..7d47674d39 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,111 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.6.9.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.6.9.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Incompatibility with newer OpenSSH fixed. Previously + versions 7.8 and later could cause Erlang SSH to exit.</p> + <p> + Own Id: OTP-15413</p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.6.9.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SFTP clients reported the error reason <c>""</c> if a + non-OTP sftp server was killed during a long file + transmission.</p> + <p> + Now the signal name (for example <c>"KILL"</c>) will be + the error reason if the server's reason is empty.</p> + <p> + The documentation also lacked type information about this + class of errors.</p> + <p> + Own Id: OTP-15148 Aux Id: ERIERL-194 </p> + </item> + <item> + <p> + Fix ssh_sftp decode error for sftp protocol version 4</p> + <p> + Own Id: OTP-15149 Aux Id: ERIERL-199 </p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.6.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Host key hash erroneously calculated for clients + following draft-00 of RFC 4419, for example PuTTY</p> + <p> + Own Id: OTP-15064</p> + </item> + <item> + <p> + Renegotiation could fail in some states</p> + <p> + Own Id: OTP-15066</p> + </item> + </list> + </section> + +</section> + +<section><title>Ssh 4.6.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + An ssh_sftp server (running version 6) could fail if it + is told to remove a file which in fact is a directory.</p> + <p> + Own Id: OTP-15004</p> + </item> + <item> + <p> + Fix rare spurios shutdowns of ssh servers when receiveing + <c>{'EXIT',_,normal}</c> messages.</p> + <p> + Own Id: OTP-15018</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 129426a6d5..f367560b5f 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -46,9 +46,9 @@ <taglist> <tag><c>reason()</c></tag> <item> - <p>= <c>atom()</c> A description of the reason why an operation failed.</p> + <p>= <c>atom() | string() | tuple() </c>A description of the reason why an operation failed.</p> <p> - The value is formed from the sftp error codes in the protocol-level responses as defined in + The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in <url href="https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt">draft-ietf-secsh-filexfer-13.txt</url> section 9.1. </p> @@ -57,6 +57,10 @@ E.g. the error code <c>SSH_FX_NO_SUCH_FILE</c> will cause the <c>reason()</c> to be <c>no_such_file</c>. </p> + <p>The <c>string()</c> reason is the error information from the server in case of an exit-signal. If that information is empty, the reason is the exit signal name. + </p> + <p>The <c>tuple()</c> reason are other errors like the <c>{exit_status,integer()}</c> if the exit status is not 0. + </p> </item> <tag><c>ssh_connection_ref() =</c></tag> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 25d537c624..5f5ccee4f8 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -231,25 +231,38 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535, try {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0), #{} = Options0 = ssh_options:handle_options(server, UserOptions), - - {{Host,Port}, ListenSocket} = - open_listen_socket(Host1, Port0, Options0), - - %% Now Host,Port is what to use for the supervisor to register its name, - %% and ListenSocket is for listening on connections. But it is still owned - %% by self()... - - finalize_start(Host, Port, ?GET_OPT(profile, Options0), - ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0), - fun(Opts, Result) -> - {_, Callback, _} = ?GET_OPT(transport, Opts), - receive - {request_control, ListenSocket, ReqPid} -> - ok = Callback:controlling_process(ListenSocket, ReqPid), - ReqPid ! {its_yours,ListenSocket}, - Result - end - end) + {open_listen_socket(Host1, Port0, Options0), Options0} + of + {{{Host,Port}, ListenSocket}, Options1} -> + try + %% Now Host,Port is what to use for the supervisor to register its name, + %% and ListenSocket is for listening on connections. But it is still owned + %% by self()... + finalize_start(Host, Port, ?GET_OPT(profile, Options1), + ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1), + fun(Opts, Result) -> + {_, Callback, _} = ?GET_OPT(transport, Opts), + receive + {request_control, ListenSocket, ReqPid} -> + ok = Callback:controlling_process(ListenSocket, ReqPid), + ReqPid ! {its_yours,ListenSocket}, + Result + end + end) + of + {error,Err} -> + close_listen_socket(ListenSocket, Options1), + {error,Err}; + OK -> + OK + catch + error:Error -> + close_listen_socket(ListenSocket, Options1), + error(Error); + exit:Exit -> + close_listen_socket(ListenSocket, Options1), + exit(Exit) + end catch throw:bad_fd -> {error,bad_fd}; @@ -457,6 +470,15 @@ open_listen_socket(_Host0, Port0, Options0) -> {{LHost,LPort}, LSock}. %%%---------------------------------------------------------------- +close_listen_socket(ListenSocket, Options) -> + try + {_, Callback, _} = ?GET_OPT(transport, Options), + Callback:close(ListenSocket) + catch + _C:_E -> ok + end. + +%%%---------------------------------------------------------------- finalize_start(Host, Port, Profile, Options0, F) -> try %% throws error:Error if no usable hostkey is found diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index e11d3adee4..84719ebc97 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -522,7 +522,7 @@ role({_,Role}) -> Role; role({_,Role,_}) -> Role. -spec renegotiation(state_name()) -> boolean(). -renegotiation({_,_,ReNeg}) -> ReNeg == renegotiation; +renegotiation({_,_,ReNeg}) -> ReNeg == renegotiate; renegotiation(_) -> false. @@ -952,6 +952,10 @@ handle_event(_, #ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive %%% ######## {connected, client|server} #### +%% Skip ext_info messages in connected state (for example from OpenSSH >= 7.7) +handle_event(_, #ssh_msg_ext_info{}, {connected,_Role}, D) -> + {keep_state, D}; + handle_event(_, {#ssh_msg_kexinit{},_}, {connected,Role}, D0) -> {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D0#data.ssh_params), D = D0#data{ssh_params = Ssh, @@ -1379,8 +1383,21 @@ handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D0) -> {keep_state, D, Repls}; %%% So that terminate will be run when supervisor is shutdown -handle_event(info, {'EXIT', _Sup, Reason}, _, _) -> - {stop, {shutdown, Reason}}; +handle_event(info, {'EXIT', _Sup, Reason}, StateName, _) -> + Role = role(StateName), + if + Role == client -> + %% OTP-8111 tells this function clause fixes a problem in + %% clients, but there were no check for that role. + {stop, {shutdown, Reason}}; + + Reason == normal -> + %% An exit normal should not cause a server to crash. This has happend... + keep_state_and_data; + + true -> + {stop, {shutdown, Reason}} + end; handle_event(info, check_cache, _, D) -> {keep_state, cache_check_set_idle_timer(D)}; diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl index 1e10f72956..3417466c4d 100644 --- a/lib/ssh/src/ssh_options.erl +++ b/lib/ssh/src/ssh_options.erl @@ -445,12 +445,6 @@ default(client) -> class => user_options }, - {pref_public_key_algs, def} => - #{default => ssh_transport:default_algorithms(public_key), - chk => fun check_pref_public_key_algs/1, - class => user_options - }, - {dh_gex_limits, def} => #{default => {1024, 6144, 8192}, % FIXME: Is this true nowadays? chk => fun({Min,I,Max}) -> @@ -516,6 +510,12 @@ default(common) -> class => user_options }, + {pref_public_key_algs, def} => + #{default => ssh_transport:default_algorithms(public_key), + chk => fun check_pref_public_key_algs/1, + class => user_options + }, + {preferred_algorithms, def} => #{default => ssh:default_algorithms(), chk => fun check_preferred_algorithms/1, diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index 9e1229dc85..79586141b2 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -801,13 +801,22 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error0, _}}, State0) -> + Error = + case Error0 of + "" -> Signal; + _ -> Error0 + end, State = reply_all(State0, {error, Error}), {stop, ChannelId, State}; handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State0) -> - State = reply_all(State0, {error, {exit_status, Status}}), + State = + case State0 of + 0 -> State0; + _ -> reply_all(State0, {error, {exit_status, Status}}) + end, {stop, ChannelId, State}. %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 427edf01ab..9e172fdb9c 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -137,9 +137,9 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, State) -> - Report = io_lib:format("Connection closed by peer ~n Error ~p~n", - [Error]), +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error, _}}, State) -> + Report = io_lib:format("Connection closed by peer signal ~p~n Error ~p~n", + [Signal,Error]), error_logger:error_report(Report), {stop, ChannelId, State}; @@ -360,10 +360,12 @@ handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, case IsDir of %% This version 6 we still have ver 5 true when Vsn > 5 -> ssh_xfer:xf_send_status(State0#state.xf, ReqId, - ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"), + State0; true -> ssh_xfer:xf_send_status(State0#state.xf, ReqId, - ?SSH_FX_FAILURE, "File is a directory"); + ?SSH_FX_FAILURE, "File is a directory"), + State0; false -> {Status, FS1} = FileMod:delete(Path, FS0), State1 = State0#state{file_state = FS1}, diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 975053d301..6820f534cb 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -772,8 +772,7 @@ ext_info_message(#ssh{role=server, send_ext_info=true, opts = Opts} = Ssh0) -> AlgsList = lists:map(fun erlang:atom_to_list/1, - proplists:get_value(public_key, - ?GET_OPT(preferred_algorithms, Opts))), + ?GET_OPT(pref_public_key_algs, Opts)), Msg = #ssh_msg_ext_info{nr_extensions = 1, data = [{"server-sig-algs", string:join(AlgsList,",")}] }, @@ -1839,9 +1838,10 @@ kex_alg_dependent({E, F, K}) -> %% diffie-hellman and ec diffie-hellman (with E = Q_c, F = Q_s) <<?Empint(E), ?Empint(F), ?Empint(K)>>; -kex_alg_dependent({-1, _, -1, _, _, E, F, K}) -> +kex_alg_dependent({-1, NBits, -1, Prime, Gen, E, F, K}) -> %% ssh_msg_kex_dh_gex_request_old - <<?Empint(E), ?Empint(F), ?Empint(K)>>; + <<?Euint32(NBits), + ?Empint(Prime), ?Empint(Gen), ?Empint(E), ?Empint(F), ?Empint(K)>>; kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) -> %% diffie-hellman group exchange diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index e1680c120e..7bb9c2d101 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -734,7 +734,7 @@ decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> {Type,Tail2} = if Vsn =< 3 -> {?SSH_FILEXFER_TYPE_UNKNOWN, Tail}; - Vsn >= 5 -> + true -> <<?BYTE(T), TL/binary>> = Tail, {T, TL} end, diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 365f25fabb..0131654dd0 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -37,6 +37,7 @@ cli/1, close/1, daemon_already_started/1, + daemon_error_closes_port/1, daemon_opt_fd/1, multi_daemon_opt_fd/1, double_close/1, @@ -108,6 +109,7 @@ all() -> {group, internal_error}, {group, rsa_host_key_is_actualy_ecdsa}, daemon_already_started, + daemon_error_closes_port, double_close, daemon_opt_fd, multi_daemon_opt_fd, @@ -797,6 +799,24 @@ daemon_already_started(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +%%% Test that a failed daemon start does not leave the port open +daemon_error_closes_port(Config) -> + GoodSystemDir = proplists:get_value(data_dir, Config), + Port = ssh_test_lib:inet_port(), + {error,_} = ssh_test_lib:daemon(Port, []), % No system dir + case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of + {error,eaddrinuse} -> + {fail, "Port leakage"}; + {error,Error} -> + ct:log("Strange error: ~p",[Error]), + {fail, "Strange error"}; + {Pid, _Host, Port} -> + %% Ok + ssh:stop_daemon(Pid) + end. + + +%%-------------------------------------------------------------------- %%% check that known_hosts is updated correctly known_hosts(Config) when is_list(Config) -> SystemDir = proplists:get_value(data_dir, Config), diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index d5eed0b087..2674126672 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.6.7 +SSH_VSN = 4.6.9.3 APP_VSN = "ssh-$(SSH_VSN)" |