diff --git a/lib/kernel/test/shell_test_lib.erl b/lib/kernel/test/shell_test_lib.erl index 84fdf1bd1ecb..a0f7453294be 100644 --- a/lib/kernel/test/shell_test_lib.erl +++ b/lib/kernel/test/shell_test_lib.erl @@ -182,9 +182,10 @@ setup_tty(Config) -> PrivDir = filename:join(proplists:get_value(priv_dir, Config), "nopubkey"), file:make_dir(PrivDir), SysDir = proplists:get_value(data_dir, Config), - {ok, _Sshd} = ssh:daemon(8989, [{system_dir, SysDir}, - {user_dir, PrivDir}, - {password, "bar"}]) + {ok, _Sshd} = ssh:daemon(8989, [{shell, {shell, start, []}}, + {system_dir, SysDir}, + {user_dir, PrivDir}, + {password, "bar"}]) end), os:cmd(os:find_executable("tmux") ++ " new-window -n " ++ ClientName ++ " -d -- "++ "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null localhost -p 8989 -l foo"), diff --git a/lib/ssh/doc/guides/hardening.md b/lib/ssh/doc/guides/hardening.md index e1a3da2881cb..06782a9acc41 100644 --- a/lib/ssh/doc/guides/hardening.md +++ b/lib/ssh/doc/guides/hardening.md @@ -122,7 +122,7 @@ excessive memory consumption. ### SFTP Server Resource Limits -When running an SFTP server via `ssh_sftpd:subsystem_spec/1`, additional +When enabling the SFTP subsystem via `ssh_sftpd:subsystem_spec/1`, additional resource limits can be configured to protect against resource exhaustion attacks: #### max_handles @@ -303,10 +303,9 @@ _exec_ server-side service takes a string provided by the client, evaluates it and returns the result. The _shell_ function enables the client to open a shell in the shell host. -Those service could - and should - be disabled when they are not needed. The -options [exec](`t:ssh:exec_daemon_option/0`) and -[shell](`t:ssh:shell_daemon_option/0`) are enabled per default but could be set -to `disabled` if not needed. The same options could also install handlers for +The options [exec](`t:ssh:exec_daemon_option/0`) and +[shell](`t:ssh:shell_daemon_option/0`) are disabled per default. +The same options could also install handlers for the string(s) passed from the client to the server. ### The id string @@ -358,6 +357,10 @@ The negotiation (session setup time) time can be limited with the _parameter_ ## SFTP Security +Note that the SFTP server runs with the file access rights of the OS +process running the Erlang emulator, regardless of the authenticated +SSH user. See the [Terminology](terminology.md) section for details. + ### Root Directory Isolation The `root` option (see `m:ssh_sftpd`) restricts SFTP users to a diff --git a/lib/ssh/doc/guides/introduction.md b/lib/ssh/doc/guides/introduction.md index 44be5dae98ae..8a97ae18d549 100644 --- a/lib/ssh/doc/guides/introduction.md +++ b/lib/ssh/doc/guides/introduction.md @@ -34,8 +34,9 @@ The `ssh` application is an implementation of the SSH Transport, Connection and Authentication Layer Protocols in Erlang. It provides the following: - API functions to write customized SSH clients and servers applications -- The Erlang shell available over SSH -- An SFTP client (`m:ssh_sftp`) and server (`m:ssh_sftpd`) +- The Erlang shell available over SSH, enabled via the `shell` daemon option +- An SFTP client (`m:ssh_sftp`) and server (`m:ssh_sftpd`), enabled via the + `subsystems` daemon option ## Prerequisites @@ -114,15 +115,16 @@ the application logic. Channels come in the following three flavors: - _Subsystem_ \- Named services that can be run as part of an SSH server, such - as SFTP [(ssh_sftpd)](`m:ssh_sftpd`), that is built into the SSH daemon - (server) by default, but it can be disabled. The Erlang `ssh` daemon can be - configured to run any Erlang- implemented SSH subsystem. -- _Shell_ \- Interactive shell. By default the Erlang daemon runs the Erlang - shell. The shell can be customized by providing your own read-eval-print loop. + as SFTP [(ssh_sftpd)](`m:ssh_sftpd`). No subsystems are enabled by default. + The Erlang `ssh` daemon can be configured to run any Erlang-implemented SSH + subsystem. +- _Shell_ \- Interactive shell. By default the Erlang daemon does not expose the Erlang + shell. It can be enabled with option `{shell, {shell, start, []}}` + The shell can be customized by providing your own read-eval-print loop. You can also provide your own Command-Line Interface (CLI) implementation, but that is much more work. -- _Exec_ \- One-time remote execution of commands. See function - `ssh_connection:exec/4` for more information. +- _Exec_ \- By default one-time remote execution of commands is disabled. + See function `ssh_connection:exec/4` for more information. ## Where to Find More Information diff --git a/lib/ssh/doc/guides/terminology.md b/lib/ssh/doc/guides/terminology.md index c7e39f0bba16..3e87f22b1da6 100644 --- a/lib/ssh/doc/guides/terminology.md +++ b/lib/ssh/doc/guides/terminology.md @@ -157,8 +157,9 @@ the server's emulator. The rights in that shell is independent of the just authenticated user. In case of an sftp request, an sftp server is started with the rights of the -user of the Erlang emulator's OS process. So with sftp the authenticated user -does not influence the rights. +user of the Erlang emulator's OS process, provided the SFTP subsystem is +enabled. So with sftp, the file access rights are those of the OS process +running the Erlang emulator, regardless of the authenticated SSH user. So after an authentication, the user name is not used anymore and has no influence. diff --git a/lib/ssh/doc/guides/using_ssh.md b/lib/ssh/doc/guides/using_ssh.md index a1b76ce63b21..b0b5729e7ad5 100644 --- a/lib/ssh/doc/guides/using_ssh.md +++ b/lib/ssh/doc/guides/using_ssh.md @@ -83,7 +83,8 @@ _Step 3._ Start the Erlang `ssh` daemon: 1> ssh:start(). ok 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, - {user_dir, "/tmp/otptest_user/.ssh"}]). + {user_dir, "/tmp/otptest_user/.ssh"}, + {shell, {shell, start, []}}]). {ok,<0.54.0>} 3> ``` @@ -432,7 +433,8 @@ and `ClientAddress`). See the ## SFTP Server -Start the Erlang `ssh` daemon with the SFTP subsystem: +The SFTP subsystem is not enabled by default. To start an SSH daemon with +SFTP, configure the `subsystems` option explicitly: ```erlang 1> ssh:start(). diff --git a/lib/ssh/doc/ssh_app.md b/lib/ssh/doc/ssh_app.md index be8b1ba2d999..700ea8220dbd 100644 --- a/lib/ssh/doc/ssh_app.md +++ b/lib/ssh/doc/ssh_app.md @@ -28,8 +28,9 @@ SSH File Transfer Protocol (SFTP) client and server. The `ssh` application is an implementation of the SSH protocol in Erlang. `ssh` offers API functions to write customized SSH clients and servers as well as -making the Erlang shell available over SSH. An SFTP client, `ssh_sftp`, and -server, `ssh_sftpd`, are also included. +making the Erlang shell available over SSH. An SFTP client (`ssh_sftp`) and +server (`ssh_sftpd`) are also included. The SFTP server can be enabled via +the `subsystems` daemon option. ## Dependencies diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index f9e359bfb0ba..378759203b26 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -36,8 +36,6 @@ -define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ). --define(DEFAULT_SHELL, {shell, start, []} ). - -define(DEFAULT_TIMEOUT, 5000). -define(MAX_RND_PADDING_LEN, 15). @@ -210,10 +208,13 @@ The `channel_callback` is the module that implements the `m:ssh_server_channel` [Creating a Subsystem](using_ssh.md#usersguide_creating_a_subsystem) in the User's Guide for more information and an example. -If the subsystems option is not present, the value of -`ssh_sftpd:subsystem_spec([])` is used. This enables the sftp subsystem by -default. The option can be set to the empty list if you do not want the daemon -to run any subsystems. +If the subsystems option is not present, the default is an empty list +and no subsystems are enabled. + +To enable the SFTP subsystem: +``` +ssh:daemon(Port, [{subsystems, [ssh_sftpd:subsystem_spec([])]} | Options]) +``` """. -doc(#{group => <<"Daemon Options">>}). -type subsystem_spec() :: {Name::string(), mod_args()} . @@ -842,6 +843,14 @@ risk. -type shell_daemon_option() :: {shell, shell_spec()} . -doc(#{group => <<"Daemon Options">>}). -type shell_spec() :: mod_fun_args() | shell_fun() | disabled . +-doc """ +The default is `disabled`. + +To enable the Erlang shell (the behavior from OTP versions prior to OTP @OTP-19969@): +``` +ssh:daemon(Port, [{shell, {shell, start, []}} | Options]) +``` +""". -doc(#{group => <<"Daemon Options">>, equiv => 'shell_fun/2'/0}). -type shell_fun() :: 'shell_fun/1'() | 'shell_fun/2'() . @@ -850,7 +859,7 @@ risk. -type 'shell_fun/1'() :: fun((User::string()) -> pid()) . -doc """ Defines the read-eval-print loop used in a daemon when a shell is requested by -the client. The default is to use the Erlang shell: `{shell, start, []}` +the client. See the option [`exec-option`](`t:exec_daemon_option/0`) for a description of how the daemon executes shell-requests and exec-requests depending on the shell- @@ -862,7 +871,23 @@ and exec-options. -doc(#{group => <<"Daemon Options">>}). -type exec_daemon_option() :: {exec, exec_spec()} . -doc(#{group => <<"Daemon Options">>}). --type exec_spec() :: {direct, exec_fun()} | disabled | deprecated_exec_opt(). +-type exec_spec() :: {direct, exec_fun()} | disabled | deprecated_exec_opt() | erlang_eval. +-doc """ +The default is `disabled`. + +Value `erlang_eval` enables evaluation of Erlang terms via exec requests. +This works when the shell option is either `disabled` (no shell) or +`{shell, start, []}` (Erlang shell). It does not work with custom shells. + +To restore the behavior from OTP versions prior to OTP @OTP-19969@, configure: +``` +ssh:daemon(Port, [{shell, {shell, start, []}}, + {exec, erlang_eval} + | Options]) +``` + +For new code, consider using `{direct, Fun}` for more controlled exec handling. +""". -doc(#{group => <<"Daemon Options">>}). -type exec_fun() :: 'exec_fun/1'() | 'exec_fun/2'() | 'exec_fun/3'(). -doc(#{group => <<"Daemon Options">>}). diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 0c4682bff097..f0438aa7819f 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -41,19 +41,20 @@ %% state -record(state, { - cm, - channel, - pty, - encoding, - deduced_encoding, % OpenSSH sometimes lies about its encodeing. This variable - % is for the process of guessing the peer encoding, taylord - % after the behaviour of openssh. If it says latin1 it is so. - % It there arrives characters encoded in latin1 it is so. Otherwise - % assume utf8 until otherwise is proved. - group, - shell, - exec, - tty + cm, + channel, + pty, + encoding, + %% OpenSSH sometimes lies about its encodeing. This variable + %% is for the process of guessing the peer encoding, taylord + %% after the behaviour of openssh. If it says latin1 it is so. + %% It there arrives characters encoded in latin1 it is so. Otherwise + %% assume utf8 until otherwise is proved. + deduced_encoding, + group, + shell, + exec = erlang_eval, + tty }). @@ -212,16 +213,16 @@ handle_ssh_msg({ssh_cm, ConnectionHandler, {exec, ChannelId, WantReply, Cmd0}}, %% The standard I/O is directed from/to the channel ChannelId. exec_direct(ConnectionHandler, ChannelId, Cmd, F, WantReply, S1); - undefined when S0#state.shell == ?DEFAULT_SHELL ; - S0#state.shell == disabled -> - %% Exec called and the shell is the default shell (= Erlang shell). - %% To be exact, eval the term as an Erlang term (but not using the - %% ?DEFAULT_SHELL directly). This disables banner, prompts and such. + erlang_eval when S0#state.shell == disabled; + S0#state.shell == {shell, start, []} -> + %% Exec called and the shell is the Erlang shell or disabled. + %% To be exact, eval the term as an Erlang term + %% This disables banner, prompts and such. %% The standard I/O is directed from/to the channel ChannelId. exec_in_erlang_default_shell(ConnectionHandler, ChannelId, Cmd, WantReply, S1); - undefined -> - %% Exec called, but the a shell other than the default shell is defined. + erlang_eval -> + %% Exec called, but the shell is custom (not Erlang shell). %% No new exec shell is defined, so don't execute! %% We don't know if it is intended to use the new shell or not. {"Prohibited.", ?EXEC_ERROR_STATUS, 1}; diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl index a4f272d24379..e650a8fa999e 100644 --- a/lib/ssh/src/ssh_client_channel.erl +++ b/lib/ssh/src/ssh_client_channel.erl @@ -420,7 +420,11 @@ init([Options]) -> channel_cb_init_args(Options) -> case proplists:get_value(exec, Options) of + erlang_eval -> + %% erlang_eval is a mode flag for ssh_cli, not a cb init arg + proplists:get_value(init_args, Options); undefined -> + %% exec key absent from Options (client-side ssh:shell/1) proplists:get_value(init_args, Options); Exec -> proplists:get_value(init_args, Options) ++ [Exec] diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index ab7a7972ac6d..8c8c6dd18dab 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -50,7 +50,6 @@ these messages are handled by -include("ssh.hrl"). -include("ssh_connect.hrl"). -include("ssh_transport.hrl"). - %% API -export([session_channel/2, session_channel/4, exec/4, shell/2, subsystem/4, send/3, send/4, send/5, @@ -1497,7 +1496,8 @@ start_cli(#connection{options = Options, no_cli -> {error, cli_disabled}; {CbModule, Args} -> - ssh_connection_sup:start_channel(server, ConnectionSup, self(), CbModule, ChannelId, Args, Exec, Options) + ssh_connection_sup:start_channel(server, ConnectionSup, self(), + CbModule, ChannelId, Args, Exec, Options) end. @@ -1505,35 +1505,20 @@ start_subsystem(BinName, #connection{options = Options, connection_supervisor = ConnectionSup}, #channel{local_id = ChannelId}, _ReplyMsg) -> Name = binary_to_list(BinName), - case check_subsystem(Name, Options) of + Subsystems = ?GET_OPT(subsystems, Options), + case proplists:get_value(Name, Subsystems, {none, []}) of {Callback, Opts} when is_atom(Callback), Callback =/= none -> - ssh_connection_sup:start_channel(server, ConnectionSup, self(), Callback, ChannelId, Opts, undefined, Options); + %% Exec is not used by subsystems; undefined is filtered + %% out by channel_cb_init_args/1 so it won't be appended + %% to the callback's init args. + ssh_connection_sup:start_channel(server, ConnectionSup, self(), + Callback, ChannelId, Opts, undefined, Options); {none, _} -> {error, bad_subsystem}; {_, _} -> {error, legacy_option_not_supported} end. - -%%% Helpers for starting cli/subsystems -check_subsystem("sftp"= SsName, Options) -> - case ?GET_OPT(subsystems, Options) of - no_subsys -> % FIXME: Can 'no_subsys' ever be matched? - {SsName, {Cb, Opts}} = ssh_sftpd:subsystem_spec([]), - {Cb, Opts}; - SubSystems -> - proplists:get_value(SsName, SubSystems, {none, []}) - end; - -check_subsystem(SsName, Options) -> - Subsystems = ?GET_OPT(subsystems, Options), - case proplists:get_value(SsName, Subsystems, {none, []}) of - Fun when is_function(Fun) -> - {Fun, []}; - {_, _} = Value -> - Value - end. - %%%---------------------------------------------------------------- %%% %%% Send-window handling diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl index 5d6df1efc3c0..77e0245be049 100644 --- a/lib/ssh/src/ssh_options.erl +++ b/lib/ssh/src/ssh_options.erl @@ -405,7 +405,7 @@ default(server) -> (default(common)) #{ subsystems => - #{default => [ssh_sftpd:subsystem_spec([])], + #{default => [], chk => fun(L) -> is_list(L) andalso lists:all(fun(SubSystem = {Name,{CB,Args}}) -> @@ -421,7 +421,7 @@ default(server) -> }, shell => - #{default => ?DEFAULT_SHELL, + #{default => disabled, chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A); (disabled) -> true; (V) -> check_function1(V) orelse @@ -431,9 +431,10 @@ default(server) -> }, exec => - #{default => undefined, + #{default => disabled, chk => fun({direct, V}) -> check_function1(V) orelse check_function2(V) orelse check_function3(V); (disabled) -> true; + (erlang_eval) -> true; % Enable Erlang term evaluation %% Compatibility (undocumented): ({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A); (V) -> check_function1(V) orelse check_function2(V) orelse check_function3(V) diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index b54687d1ebdc..634376519498 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -172,6 +172,8 @@ start_channel(Cm, UserOptions0) when is_pid(Cm) -> PacketSize = proplists:get_value(packet_size, ChanOpts, ?XFER_PACKET_SIZE), case ssh_connection:session_channel(Cm, WindowSize, PacketSize, Timeout) of {ok, ChannelId} -> + %% Exec is not used by SFTP channels; undefined is filtered + %% out by channel_cb_init_args/1. case ssh_connection_handler:start_channel(Cm, ?MODULE, ChannelId, [Cm,ChannelId,SftpOpts], undefined) of {ok, Pid} -> diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index dc82ea4370cf..24527a99e420 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -231,7 +231,9 @@ init_per_group(Tag, Algs, Alg, PA, Config) -> end; _ -> - start_std_daemon([PrefAlgs], + start_std_daemon([{exec, erlang_eval}, + {subsystems, [ssh_sftpd:subsystem_spec([])]} + | [PrefAlgs]], [{pref_algs,PrefAlgs}, {tag_alg,{Tag,[Alg]}} | Config]) diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 32ddad6564f9..84319dcbeb3c 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -88,6 +88,7 @@ parallel_login/1, setopts_getopts/1, shell/1, + shell_disabled/1, shell_exit_status/1, shell_no_unicode/1, shell_socket/1, @@ -157,7 +158,7 @@ groups() -> max_initial_idle_time, openssh_zlib_basic_test, misc_ssh_options, inet_option, inet6_option, - shell, shell_socket, shell_ssh_conn, + shell, shell_disabled, shell_socket, shell_ssh_conn, shell_no_unicode, shell_unicode_string, close]}]. @@ -199,10 +200,11 @@ init_per_testcase(TestCase, Config) PrivDir = proplists:get_value(priv_dir, Config), UserDir = proplists:get_value(priv_dir, Config), SysDir = proplists:get_value(data_dir, Config), - Sftpd = {_Pid, _Host, Port} = - ssh_test_lib:daemon([{system_dir, SysDir}, - {user_dir, PrivDir}, - {user_passwords, [{"foo", "bar"}]}]), + Sftpd = {_Pid, _Host, Port} = + ssh_test_lib:daemon([{shell, {shell, start, []}}, + {system_dir, SysDir}, + {user_dir, PrivDir}, + {user_passwords, [{"foo", "bar"}]}]), ct:sleep(500), IO = ssh_test_lib:start_io_server(), Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}, @@ -311,9 +313,10 @@ exec(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, @@ -354,9 +357,10 @@ exec_with_io_out(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, @@ -384,9 +388,10 @@ exec_with_io_in(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, {user_interaction, false}]), @@ -418,7 +423,8 @@ exec_compressed_helper(Config, CompressAlgorithm) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, + {Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir},{user_dir, UserDir}, {preferred_algorithms,[{compression, [CompressAlgorithm]}]}, {failfun, fun ssh_test_lib:failfun/2}]), @@ -452,7 +458,9 @@ idle_time_common(DaemonExtraOpts, ClientExtraOpts, Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}, + {system_dir, SystemDir}, {user_dir, UserDir}, {failfun, fun ssh_test_lib:failfun/2} | DaemonExtraOpts @@ -507,24 +515,51 @@ shell(Config) when is_list(Config) -> process_flag(trap_exit, true), SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - - {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {_Pid, _Host, Port} = ssh_test_lib:daemon([{shell, {shell, start, []}}, + {system_dir, SystemDir},{user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), ct:sleep(500), - IO = ssh_test_lib:start_io_server(), Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), receive {'EXIT', _, _} = Exit -> ct:log("~p:~p ~p", [?MODULE,?LINE,Exit]), - ct:fail(no_ssh_connection); + ct:fail(no_ssh_connection); ErlShellStart -> ct:log("Erlang shell start: ~p~n", [ErlShellStart]), do_shell(IO, Shell) - after + after 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. - + +shell_disabled(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), + UserDir = proplists:get_value(priv_dir, Config), + {_Pid, _Host, Port} = ssh_test_lib:daemon([{shell, disabled}, + {system_dir, SystemDir},{user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + ct:sleep(500), + IO = ssh_test_lib:start_io_server(), + Shell = ssh_test_lib:start_shell(Port, IO, [{user_dir,UserDir}]), + ?CT_LOG("Shell = ~p", [Shell]), + ExpectedMessages = + [<<"Prohibited.">>, + <<"Connection closed by peer">>, + <<"Status: 255">>, + {'EXIT', Shell, normal}], + [receive + Msg -> + ?CT_LOG("Received msg = ~p", [Msg]), + ok + after + 3000 -> + ?CT_LOG("Missing msg = ~p", [Msg]), + ?CT_LOG("Message queue of ~p:~n~p", + [self(), erlang:process_info(self(), messages)]), + ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end || Msg <- ExpectedMessages]. + %%-------------------------------------------------------------------- %%% Test that ssh:shell/2 works when attaching to a open TCP-connection shell_socket(Config) when is_list(Config) -> @@ -532,8 +567,9 @@ shell_socket(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {_Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {_Pid, Host0, Port} = ssh_test_lib:daemon([{shell, {shell, start, []}}, + {system_dir, SystemDir},{user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), Host = ssh_test_lib:mangle_connect_address(Host0), ct:sleep(500), @@ -576,8 +612,9 @@ shell_ssh_conn(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {failfun, fun ssh_test_lib:failfun/2}]), + {_Pid, Host, Port} = ssh_test_lib:daemon([{shell, {shell, start, []}}, + {system_dir, SystemDir},{user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), ct:sleep(500), IO = ssh_test_lib:start_io_server(), @@ -606,11 +643,12 @@ cli(Config) when is_list(Config) -> ok = ssh_test_lib:del_dirs(TmpDir), ok = file:make_dir(TmpDir), - {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {password, "morot"}, - {ssh_cli, {ssh_test_cli, [cli,TmpDir]}}, - {subsystems, []}, - {failfun, fun ssh_test_lib:failfun/2}]), + {_Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir},{user_dir, UserDir}, + {password, "morot"}, + {ssh_cli, {ssh_test_cli, [cli,TmpDir]}}, + {subsystems, []}, + {failfun, fun ssh_test_lib:failfun/2}]), ct:sleep(500), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, @@ -1077,12 +1115,11 @@ peername_sockname(Config) when is_list(Config) -> SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), UserDir = proplists:get_value(priv_dir, Config), - {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {subsystems, [{"peername_sockname", - {ssh_peername_sockname_server, []}} - ]} - ]), + {_Pid, Host, Port} = ssh_test_lib:daemon([{exec, erlang_eval}, + {system_dir, SystemDir}, + {user_dir, UserDir}, + {subsystems, [{"peername_sockname", + {ssh_peername_sockname_server, []}}]}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl index e81f4ec40aac..ee288561d2bd 100644 --- a/lib/ssh/test/ssh_compat_SUITE.erl +++ b/lib/ssh/test/ssh_compat_SUITE.erl @@ -349,6 +349,7 @@ renegotiation_otp_is_server(Config) -> [{system_dir, setup_local_hostdir(Config)}, {user_dir, UserDir}, {user_passwords, [{?USER,?PASSWD}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}, {failfun, fun ssh_test_lib:failfun/2}, {modify_algorithms, [{append, [{public_key,PublicKeyAlgs}]}]}, {connectfun, diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index 8e29485a6475..aff752613a80 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -112,6 +112,7 @@ trap_exit_connect/1, trap_exit_daemon/1, handler_down_before_open/1, + replace_options_enable_services/1, ssh_exec_echo/2 % called as an MFA ]). @@ -187,7 +188,8 @@ all() -> no_sensitive_leak, start_subsystem_on_closed_channel, max_channels_option, - handler_down_before_open + handler_down_before_open, + replace_options_enable_services ]. groups() -> [{openssh, [], payload() ++ ptty() ++ sock()}]. @@ -427,7 +429,8 @@ daemon_start(Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, UserDir}, {password, "morot"}, - {exec, fun ssh_exec_echo/1}]), + {exec, fun ssh_exec_echo/1}, + {shell, {shell, start, []}}]), {Pid, Host, Port, UserDir}. %%-------------------------------------------------------------------- @@ -891,9 +894,10 @@ start_shell_exec(Config) when is_list(Config) -> file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, - {user_dir, UserDir}, - {password, "morot"}, - {exec, {?MODULE,ssh_exec_echo,["foo"]}} ]), + {user_dir, UserDir}, + {password, "morot"}, + {exec, {?MODULE,ssh_exec_echo,["foo"]}}, + {shell, {shell, start, []}}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, @@ -901,7 +905,7 @@ start_shell_exec(Config) when is_list(Config) -> {user_interaction, true}, {user_dir, UserDir}]), test_shell_is_enabled(ConnectionRef), - test_exec_is_enabled(ConnectionRef, "testing", <<"echo testing\r\n">>), + test_exec_is_enabled(ConnectionRef, "testing", <<"echo testing foo\r\n">>), ssh:close(ConnectionRef), ssh:stop_daemon(Pid). @@ -912,9 +916,10 @@ exec_erlang_term(Config) when is_list(Config) -> file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, - {user_dir, UserDir}, - {password, "morot"} - ]), + {user_dir, UserDir}, + {password, "morot"}, + {shell, {shell, start, []}}, + {exec, erlang_eval}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, @@ -955,10 +960,10 @@ exec_disabled(Config) when is_list(Config) -> file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, - {user_dir, UserDir}, - {password, "morot"}, - {exec, disabled} - ]), + {user_dir, UserDir}, + {password, "morot"}, + {exec, disabled}, + {shell, {shell, start, []}}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, {password, "morot"}, @@ -978,7 +983,8 @@ exec_shell_disabled(Config) when is_list(Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, UserDir}, {password, "morot"}, - {shell, disabled} + {shell, disabled}, + {exec, erlang_eval} ]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, @@ -1768,11 +1774,12 @@ max_channels_option(Config) when is_list(Config) -> file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, - {user_dir, UserDir}, - {password, "morot"}, - {max_channels, 3}, - {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]} - ]), + {user_dir, UserDir}, + {password, "morot"}, + {max_channels, 3}, + {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}, + {shell, {shell, start, []}}, + {exec, erlang_eval}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, @@ -2087,6 +2094,52 @@ test_exec_is_disabled(ConnectionRef) -> ct:fail("Exec Timeout") end. +%%-------------------------------------------------------------------- +replace_options_enable_services(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), + file:make_dir(UserDir), + SysDir = proplists:get_value(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}]), + ConnOpts = [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_dir, UserDir}], + + %% Verify all services disabled before replace + C1 = ssh_test_lib:connect(Host, Port, ConnOpts), + ?CT_LOG("Checking shell is disabled before replace", []), + test_shell_is_disabled(C1), + ?CT_LOG("Checking exec is disabled before replace", []), + test_exec_is_disabled(C1), + ?CT_LOG("Checking SFTP is unavailable before replace", []), + {error, _} = ssh_sftp:start_channel(C1), + ?CT_LOG("All services confirmed disabled", []), + ssh:close(C1), + + %% Enable shell, exec and SFTP + ?CT_LOG("Replacing options: enabling shell, exec and SFTP", []), + {ok, Pid} = ssh:daemon_replace_options(Pid, + [{shell, {shell, start, []}}, + {exec, erlang_eval}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}]), + + %% Verify all services work after replace + C2 = ssh_test_lib:connect(Host, Port, ConnOpts), + ?CT_LOG("Checking shell is enabled after replace", []), + test_shell_is_enabled(C2), + ?CT_LOG("Checking exec is enabled after replace", []), + test_exec_is_enabled(C2), + ?CT_LOG("Checking SFTP is available after replace", []), + {ok, SftpPid} = ssh_sftp:start_channel(C2), + ssh_sftp:stop_channel(SftpPid), + ?CT_LOG("All services confirmed enabled after replace", []), + + ssh:close(C2), + ssh:stop_daemon(Pid). + %%-------------------------------------------------------------------- test_shell_is_enabled(ConnectionRef) -> test_shell_is_enabled(ConnectionRef, <<"Eshell V">>). @@ -2131,12 +2184,11 @@ test_exec_is_enabled(ConnectionRef, Exec, Expect) -> <>}} = R -> ct:log("~p:~p Got expected ~p",[?MODULE,?LINE,R]); Other -> - ct:log("~p:~p Got unexpected ~p~nExpect: ~p~n", - [?MODULE,?LINE, Other, {ssh_cm, ConnectionRef, - {data, ChannelId, 0, Expect}}]), - {fail, "Unexpected data"} + ct:fail("Got unexpected ~p~nExpect: ~p~n", + [Other, {ssh_cm, ConnectionRef, + {data, ChannelId, 0, Expect}}]) after 5000 -> - {fail,"Exec Timeout"} + ct:fail("Exec Timeout") end. %%%---------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_dbg_SUITE.erl b/lib/ssh/test/ssh_dbg_SUITE.erl index 6f9b3f98a2ff..00d2cc4571ad 100644 --- a/lib/ssh/test/ssh_dbg_SUITE.erl +++ b/lib/ssh/test/ssh_dbg_SUITE.erl @@ -481,8 +481,9 @@ all_dbg(Config) -> {_, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {user_passwords, [{?USR,?PWD}]} + {user_dir, UserDir}, + {user_passwords, [{?USR,?PWD}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]} ]), {ok, ChPid, _C} = diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index bc08c97498f6..664e36dc593a 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -1397,9 +1397,10 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) -> ct:log("Parallel: ~p",[Parallel]), {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {parallel_login, Parallel}, - {negotiation_timeout, NegTimeOut}, - {failfun, fun ssh_test_lib:failfun/2}]), + {parallel_login, Parallel}, + {negotiation_timeout, NegTimeOut}, + {failfun, fun ssh_test_lib:failfun/2}, + {shell, {shell, start, []}}]), ct:log("~p Listen ~p:~p",[_Pid,_Host,Port]), ct:sleep(500), @@ -1550,7 +1551,8 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) -> {user_dir, UserDir}, {user_passwords, [{"carni", "meat"}]}, {parallel_login, ParallelLogin}, - {max_sessions, MaxSessions} + {max_sessions, MaxSessions}, + {subsystems, [ssh_sftpd:subsystem_spec([])]} ]), ct:log("~p Listen ~p:~p for max ~p sessions",[Pid,Host,Port,MaxSessions]), try [Connect(Host,Port) || _ <- lists:seq(1,MaxSessions)] @@ -1981,7 +1983,8 @@ daemon_replace_options_algs_connect(Config) -> {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, - [{preferred_algorithms,[{kex,[A1]}]} + [{exec, erlang_eval}, + {preferred_algorithms,[{kex,[A1]}]} ]), [A1] = get_preferred_algorithms(Pid, kex), diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 2b7494f1756b..358574dbf05a 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -144,7 +144,8 @@ rekey4(Config) -> rekey_chk(Config, 0, {infinity,infinity}). rekey_chk(Config, RLdaemon, RLclient) -> {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, - [{rekey_limit, RLdaemon}, ?ALIVE]), + [{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {rekey_limit, RLdaemon}, ?ALIVE]), ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, RLclient}, ?ALIVE]), @@ -172,7 +173,8 @@ rekey_limit_client(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, - [{max_random_length_padding,0}, + [{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -226,7 +228,8 @@ rekey_limit_daemon(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = - ssh_test_lib:std_daemon(Config, [{rekey_limit, Limit}, + ssh_test_lib:std_daemon(Config, [{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {rekey_limit, Limit}, {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -279,7 +282,8 @@ norekey_limit_client(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = - ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + ssh_test_lib:std_daemon(Config,[{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -311,7 +315,8 @@ norekey_limit_daemon(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = - ssh_test_lib:std_daemon(Config,[{rekey_limit, Limit}, + ssh_test_lib:std_daemon(Config,[{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {rekey_limit, Limit}, {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -342,7 +347,8 @@ rekey_time_limit_client(Config) -> GB = 1024*1000*1000, Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = - ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + ssh_test_lib:std_daemon(Config,[{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), ConnectionRef = @@ -358,7 +364,8 @@ rekey_time_limit_daemon(Config) -> GB = 1024*1000*1000, Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, Port} = - ssh_test_lib:std_daemon(Config,[{rekey_limit, {Minutes, GB}}, + ssh_test_lib:std_daemon(Config,[{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {rekey_limit, {Minutes, GB}}, {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -397,7 +404,8 @@ renegotiate1(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, DPort} = - ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + ssh_test_lib:std_daemon(Config,[{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), @@ -440,7 +448,8 @@ renegotiate2(Config) -> Algs = proplists:get_value(preferred_algorithms, Config), {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config, - [{max_random_length_padding,0}, + [{subsystems, [ssh_sftpd:subsystem_spec([])]}, + {max_random_length_padding,0}, ?ALIVE, {preferred_algorithms,Algs}]), diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 6ee2bc9bfa1c..f01ef59242f9 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -230,7 +230,8 @@ init_per_group(erlang_server, Config) -> ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, PrivDir}, {user_passwords, - [{User, Passwd}]}]), + [{User, Passwd}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}]), [{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config]; init_per_group(openssh_server, Config) -> diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 604789d30748..60730a0ad28f 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -124,7 +124,7 @@ init_per_testcase(TestCase, Config) -> Spec = ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}]), [{subsystems, [Spec]}]; _ -> - [] + [{subsystems, [ssh_sftpd:subsystem_spec([])]}] end, {Sftpd, Host, Port} = ssh_test_lib:daemon([{preferred_algorithms, diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index 4032e7fcdb09..26dc7b723c69 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -97,7 +97,8 @@ init_per_testcase(sshc_subtree, Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {failfun, fun ssh_test_lib:failfun/2}, {user_passwords, - [{?USER, ?PASSWD}]}]), + [{?USER, ?PASSWD}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}]), [{server, {Pid, Host, Port}} | Config]; init_per_testcase(Case, Config) -> end_per_testcase(Case, Config), @@ -160,7 +161,8 @@ sshd_subtree(Config) when is_list(Config) -> {Daemon, HostIP, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {failfun, fun ssh_test_lib:failfun/2}, {user_passwords, - [{?USER, ?PASSWD}]}]), + [{?USER, ?PASSWD}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}]), ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]), ?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP, port=Port, @@ -182,6 +184,7 @@ sshd_subtree_profile(Config) when is_list(Config) -> {failfun, fun ssh_test_lib:failfun/2}, {user_passwords, [{?USER, ?PASSWD}]}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}, {profile, Profile}]), ct:log("Expect HostIP=~p, Port=~p, Profile=~p, Daemon=~p",[HostIP,Port,Profile,Daemon]), ?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP, @@ -293,6 +296,7 @@ shell_channel_tree(Config) -> {Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, UserDir}, {password, "morot"}, + {subsystems, [ssh_sftpd:subsystem_spec([])]}, {shell, fun(_User) -> spawn(TimeoutShell) end diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 116b5a0140da..fe6efccd4b97 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -463,19 +463,19 @@ rcv_expected(Expect, SshPort, Timeout) -> {SshPort, Recvd} when is_function(Expect) -> case Expect(Recvd) of true -> - ct:log("Got expected ~p from ~p",[Recvd,SshPort]), + ?CT_LOG("Got expected ~p from ~p",[Recvd,SshPort]), catch port_close(SshPort), rcv_lingering(50); false -> - ct:log("Got UNEXPECTED ~p~n",[Recvd]), + ?CT_LOG("Got UNEXPECTED ~p~n",[Recvd]), rcv_expected(Expect, SshPort, Timeout) end; {SshPort, Expect} -> - ct:log("Got expected ~p from ~p",[Expect,SshPort]), + ?CT_LOG("Got expected ~p from ~p",[Expect,SshPort]), catch port_close(SshPort), rcv_lingering(50); Other -> - ct:log("Got UNEXPECTED ~p~nExpect ~p",[Other, {SshPort,Expect}]), + ?CT_LOG("Got UNEXPECTED ~p~nExpect ~p",[Other, {SshPort,Expect}]), rcv_expected(Expect, SshPort, Timeout) after Timeout -> diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index da4bcafdb624..258a341b80d8 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -206,6 +206,7 @@ eclient_oserver_helper2({Shell, Prev, IO}, Config) -> exec_with_io_in_sshc(Config) when is_list(Config) -> SystemDir = proplists:get_value(data_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {exec, erlang_eval}, ?ALIVE, {failfun, fun ssh_test_lib:failfun/2}]), ct:sleep(500), @@ -302,7 +303,8 @@ eserver_oclient_renegotiate_helper1(Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {failfun, fun ssh_test_lib:failfun/2}, ?ALIVE, - {bannerfun, BannerFun}]), + {bannerfun, BannerFun}, + {shell, {shell, start, []}}]), ct:sleep(500), RenegLimitK = 3, @@ -368,9 +370,10 @@ tunnel_out_non_erlclient_erlserver(Config) -> _PrivDir = proplists:get_value(priv_dir, Config), {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_out, true}, - {system_dir, SystemDir}, + {system_dir, SystemDir}, ?ALIVE, - {failfun, fun ssh_test_lib:failfun/2}]), + {failfun, fun ssh_test_lib:failfun/2}, + {shell, {shell, start, []}}]), {ToSock, _ToHost, ToPort} = tunneling_listner(), ListenHost = {127,0,0,1}, @@ -401,7 +404,8 @@ tunnel_in_non_erlclient_erlserver(Config) -> {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_in, true}, {system_dir, SystemDir}, ?ALIVE, - {failfun, fun ssh_test_lib:failfun/2}]), + {failfun, fun ssh_test_lib:failfun/2}, + {shell, {shell, start, []}}]), {ToSock, _ToHost, ToPort} = tunneling_listner(), ListenHost = {127,0,0,1},