aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/test')
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl373
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1019
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh28
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run27
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image3
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image14
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all28
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl199
-rw-r--r--lib/ssh/test/ssh_engine_SUITE.erl26
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl122
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl237
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa13
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa15
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key13
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub11
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl136
-rw-r--r--lib/ssh/test/ssh_test_lib.erl12
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl7
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl359
22 files changed, 1525 insertions, 1139 deletions
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 21359a0386..4d84b6c6b6 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -34,7 +34,6 @@ VSN=$(GS_VSN)
MODULES= \
ssh_algorithms_SUITE \
ssh_options_SUITE \
- ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
ssh_compat_SUITE \
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 202b0afe57..d3f93c7382 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -28,60 +28,12 @@
-include("ssh_test_lib.hrl").
%% Note: This directive should only be used in test suites.
-%%-compile(export_all).
-
-%%% Test cases
--export([
- app_test/1,
- appup_test/1,
- cli/1,
- close/1,
- daemon_already_started/1,
- daemon_opt_fd/1,
- multi_daemon_opt_fd/1,
- double_close/1,
- exec/1,
- exec_compressed/1,
- exec_key_differs1/1,
- exec_key_differs2/1,
- exec_key_differs3/1,
- exec_key_differs_fail/1,
- fail_daemon_start/1,
- idle_time_client/1,
- idle_time_server/1,
- inet6_option/1,
- inet_option/1,
- internal_error/1,
- known_hosts/1,
- login_bad_pwd_no_retry1/1,
- login_bad_pwd_no_retry2/1,
- login_bad_pwd_no_retry3/1,
- login_bad_pwd_no_retry4/1,
- login_bad_pwd_no_retry5/1,
- misc_ssh_options/1,
- openssh_zlib_basic_test/1,
- packet_size_zero/1,
- pass_phrase/1,
- peername_sockname/1,
- send/1,
- shell/1,
- shell_no_unicode/1,
- shell_unicode_string/1,
- ssh_info_print/1,
- key_callback/1,
- key_callback_options/1,
- shell_exit_status/1
- ]).
-
-%%% Common test callbacks
--export([suite/0, all/0, groups/0,
- init_per_suite/1, end_per_suite/1,
- init_per_group/2, end_per_group/2,
- init_per_testcase/2, end_per_testcase/2
- ]).
+-compile(export_all).
-define(NEWLINE, <<"\r\n">>).
+-define(REKEY_DATA_TMO, 65000).
+
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
@@ -91,76 +43,97 @@ suite() ->
{timetrap,{seconds,40}}].
all() ->
- [app_test,
- appup_test,
- {group, dsa_key},
- {group, rsa_key},
- {group, ecdsa_sha2_nistp256_key},
- {group, ecdsa_sha2_nistp384_key},
- {group, ecdsa_sha2_nistp521_key},
- {group, dsa_pass_key},
- {group, rsa_pass_key},
- {group, ecdsa_sha2_nistp256_pass_key},
- {group, ecdsa_sha2_nistp384_pass_key},
- {group, ecdsa_sha2_nistp521_pass_key},
- {group, host_user_key_differs},
- {group, key_cb},
- {group, internal_error},
- {group, rsa_host_key_is_actualy_ecdsa},
- daemon_already_started,
- double_close,
- daemon_opt_fd,
- multi_daemon_opt_fd,
- packet_size_zero,
- ssh_info_print,
- {group, login_bad_pwd_no_retry},
- shell_exit_status
- ].
+ [{group, all_tests}].
+
groups() ->
- [{dsa_key, [], basic_tests()},
- {rsa_key, [], basic_tests()},
- {ecdsa_sha2_nistp256_key, [], basic_tests()},
- {ecdsa_sha2_nistp384_key, [], basic_tests()},
- {ecdsa_sha2_nistp521_key, [], basic_tests()},
+ [{all_tests, [parallel], [{group, ssh_renegotiate_SUITE},
+ {group, ssh_basic_SUITE}
+ ]},
+ {ssh_basic_SUITE, [], [app_test,
+ appup_test,
+ {group, dsa_key},
+ {group, rsa_key},
+ {group, ecdsa_sha2_nistp256_key},
+ {group, ecdsa_sha2_nistp384_key},
+ {group, ecdsa_sha2_nistp521_key},
+ {group, dsa_pass_key},
+ {group, rsa_pass_key},
+ {group, ecdsa_sha2_nistp256_pass_key},
+ {group, ecdsa_sha2_nistp384_pass_key},
+ {group, ecdsa_sha2_nistp521_pass_key},
+ {group, host_user_key_differs},
+ {group, key_cb},
+ {group, internal_error},
+ {group, rsa_host_key_is_actualy_ecdsa},
+ daemon_already_started,
+ double_close,
+ daemon_opt_fd,
+ multi_daemon_opt_fd,
+ packet_size,
+ ssh_info_print,
+ {group, login_bad_pwd_no_retry},
+ shell_exit_status
+ ]},
+
+ {ssh_renegotiate_SUITE, [parallel], [rekey,
+ rekey_limit,
+ renegotiate1,
+ renegotiate2]},
+
+ {dsa_key, [], [{group, basic}]},
+ {rsa_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp256_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp384_key, [], [{group, basic}]},
+ {ecdsa_sha2_nistp521_key, [], [{group, basic}]},
{rsa_host_key_is_actualy_ecdsa, [], [fail_daemon_start]},
- {host_user_key_differs, [], [exec_key_differs1,
- exec_key_differs2,
- exec_key_differs3,
- exec_key_differs_fail]},
+ {host_user_key_differs, [parallel], [exec_key_differs1,
+ exec_key_differs2,
+ exec_key_differs3,
+ exec_key_differs_fail]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp256_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp384_pass_key, [], [pass_phrase]},
{ecdsa_sha2_nistp521_pass_key, [], [pass_phrase]},
- {key_cb, [], [key_callback, key_callback_options]},
+ {key_cb, [parallel], [key_callback, key_callback_options]},
{internal_error, [], [internal_error]},
- {login_bad_pwd_no_retry, [], [login_bad_pwd_no_retry1,
- login_bad_pwd_no_retry2,
- login_bad_pwd_no_retry3,
- login_bad_pwd_no_retry4,
- login_bad_pwd_no_retry5
- ]}
+ {login_bad_pwd_no_retry, [parallel], [login_bad_pwd_no_retry1,
+ login_bad_pwd_no_retry2,
+ login_bad_pwd_no_retry3,
+ login_bad_pwd_no_retry4,
+ login_bad_pwd_no_retry5
+ ]},
+
+ {basic, [], [{group,p_basic},
+ close,
+ known_hosts
+ ]},
+ {p_basic, [parallel], [send, peername_sockname,
+ exec, exec_compressed,
+ shell, shell_no_unicode, shell_unicode_string,
+ cli,
+ idle_time_client, idle_time_server, openssh_zlib_basic_test,
+ misc_ssh_options, inet_option, inet6_option]}
].
-basic_tests() ->
- [send, close, peername_sockname,
- exec, exec_compressed,
- shell, shell_no_unicode, shell_unicode_string,
- cli, known_hosts,
- idle_time_client, idle_time_server, openssh_zlib_basic_test,
- misc_ssh_options, inet_option, inet6_option].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- ?CHECK_CRYPTO(Config).
+ ?CHECK_CRYPTO(begin
+ ssh:start(),
+ Config
+ end).
end_per_suite(_Config) ->
ssh:stop().
%%--------------------------------------------------------------------
+init_per_group(ssh_renegotiate_SUITE, Config) ->
+ [{preferred_algorithms, ssh:default_algorithms()} | Config];
init_per_group(dsa_key, Config) ->
case lists:member('ssh-dss',
ssh_transport:default_algorithms(public_key)) of
@@ -414,7 +387,6 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ;
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- ssh:start(),
Sftpd = {_Pid, _Host, Port} =
ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, PrivDir},
@@ -437,7 +409,6 @@ init_per_testcase(inet6_option, Config) ->
{skip,"No ipv6 interface address"}
end;
init_per_testcase(_TestCase, Config) ->
- ssh:start(),
Config.
end_per_testcase(TestCase, Config) when TestCase == server_password_option;
@@ -458,7 +429,6 @@ end_per_testcase(_TestCase, Config) ->
end_per_testcase(Config).
end_per_testcase(_Config) ->
- ssh:stop(),
ok.
%%--------------------------------------------------------------------
@@ -480,8 +450,8 @@ misc_ssh_options(Config) when is_list(Config) ->
SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
UserDir = proplists:get_value(priv_dir, Config),
- CMiscOpt0 = [{connect_timeout, 1000}, {user_dir, UserDir}],
- CMiscOpt1 = [{connect_timeout, infinity}, {user_dir, UserDir}],
+ CMiscOpt0 = [{connect_timeout, 1000}, {user_dir, UserDir}, {silently_accept_hosts, true}],
+ CMiscOpt1 = [{connect_timeout, infinity}, {user_dir, UserDir}, {silently_accept_hosts, true}],
SMiscOpt0 = [{user_dir, UserDir}, {system_dir, SystemDir}],
SMiscOpt1 = [{user_dir, UserDir}, {system_dir, SystemDir}],
@@ -764,11 +734,11 @@ cli(Config) when is_list(Config) ->
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh_connection:shell(ConnectionRef, ChannelId),
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
receive
{ssh_cm, ConnectionRef,
{data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end,
@@ -1104,7 +1074,7 @@ multi_daemon_opt_fd(Config) ->
end || {S,Pid,C} <- Tests].
%%--------------------------------------------------------------------
-packet_size_zero(Config) ->
+packet_size(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1119,21 +1089,36 @@ packet_size_zero(Config) ->
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),
-
- {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
- ok = ssh_connection:shell(Conn, Chan),
+ lists:foreach(
+ fun(MaxPacketSize) ->
+ ct:log("Try max_packet_size=~p",[MaxPacketSize]),
+ {ok,Ch} = ssh_connection:session_channel(Conn, 1000, MaxPacketSize, 60000),
+ ok = ssh_connection:shell(Conn, Ch),
+ rec(Server, Conn, Ch, MaxPacketSize),
+ ssh_connection:close(Conn, Ch)
+ end, [0, 1, 10, 25]),
ssh:close(Conn),
ssh:stop_daemon(Server),
+ ok.
+
+rec(Server, Conn, Ch, MaxSz) ->
receive
- {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:log("Got ~p",[M]),
- ct:fail(doesnt_obey_max_packet_size_0)
- after 5000 ->
- ok
- end.
-
+ {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+ ct:log("~p: ~p",[MaxSz,M]),
+ rec(Server, Conn, Ch, MaxSz);
+ {ssh_cm,Conn,{data,Ch,_,_}} = M ->
+ ct:log("Max pkt size=~p. Got ~p",[MaxSz,M]),
+ ssh:close(Conn),
+ ssh:stop_daemon(Server),
+ ct:fail("Does not obey max_packet_size=~p",[MaxSz])
+ after
+ 2000 ->
+ ct:log("~p: ok!",[MaxSz]),
+ ok
+ end.
+
%%--------------------------------------------------------------------
shell_no_unicode(Config) ->
new_do_shell(proplists:get_value(io,Config),
@@ -1340,6 +1325,156 @@ shell_exit_status(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
+%%% Idle timeout test
+rekey() -> [{timetrap,{seconds,90}}].
+
+rekey(Config) ->
+ {Pid, Host, Port} =
+ ssh_test_lib:std_daemon(Config,
+ [{rekey_limit, 0}]),
+ ConnectionRef =
+ ssh_test_lib:std_connect(Config, Host, Port,
+ [{rekey_limit, 0}]),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ receive
+ after ?REKEY_DATA_TMO ->
+ %%By this time rekeying would have been done
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+ false = (Kex2 == Kex1),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid)
+ end.
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying by data volume
+
+rekey_limit() -> [{timetrap,{seconds,400}}].
+
+rekey_limit(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "rekey.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000},
+ {max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ Data = lists:duplicate(159000,1),
+ ok = ssh_sftp:write_file(SftpPid, DataFile, Data),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ timer:sleep(?REKEY_DATA_TMO),
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying with simulataneous send request
+
+renegotiate1(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "renegotiate1.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ RPort = ssh_test_lib:inet_port(),
+ {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
+
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
+
+ ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
+
+ ssh_relay:hold(RelayPid, rx, 20, 1000),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
+
+ timer:sleep(2000),
+
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ ssh_relay:stop(RelayPid),
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+
+%%% Test rekeying with inflight messages from peer
+
+renegotiate2(Config) ->
+ UserDir = proplists:get_value(priv_dir, Config),
+ DataFile = filename:join(UserDir, "renegotiate2.data"),
+
+ Algs = proplists:get_value(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
+
+ RPort = ssh_test_lib:inet_port(),
+ {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
+
+ ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
+ {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
+
+ ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
+
+ ssh_relay:hold(RelayPid, rx, 20, infinity),
+ spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
+ %% need a small pause here to ensure ssh_sftp:write is executed
+ ct:sleep(10),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ ssh_relay:release(RelayPid, rx),
+
+ timer:sleep(2000),
+
+ Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
+
+ false = (Kex2 == Kex1),
+
+ ssh_relay:stop(RelayPid),
+ ssh_sftp:stop_channel(SftpPid),
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -1491,7 +1626,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
end
after 30000 ->
- ct:log("Meassage queue of ~p:~n~p",
+ ct:log("Message queue of ~p:~n~p",
[self(), erlang:process_info(self(), messages)]),
case Order of
expect -> ct:fail("timeout, expected ~p",[string:strip(Arg)]);
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 74ab5aca3a..f7eda1dc08 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -32,7 +32,8 @@
-compile(export_all).
-define(USER,"sshtester").
--define(PWD, "foobar").
+-define(PASSWD, "foobar").
+-define(BAD_PASSWD, "NOT-"?PASSWD).
-define(DOCKER_PFX, "ssh_compat_suite-ssh").
%%--------------------------------------------------------------------
@@ -44,25 +45,22 @@ suite() ->
{timetrap,{seconds,40}}].
all() ->
- [{group,G} || G <- vers()].
+%% [check_docker_present] ++
+ [{group,G} || G <- ssh_image_versions()].
groups() ->
- [{G, [], tests()} || G <- vers()].
-
-tests() ->
- [login_with_password_otp_is_client,
- login_with_password_otp_is_server,
- login_with_keyboard_interactive_otp_is_client,
- login_with_keyboard_interactive_otp_is_server,
- login_with_all_public_keys_otp_is_client,
- login_with_all_public_keys_otp_is_server,
- all_algorithms_otp_is_client,
- all_algorithms_otp_is_server
+ [{otp_client, [], [login_otp_is_client,
+ all_algorithms_sftp_exec_reneg_otp_is_client,
+ send_recv_big_with_renegotiate_otp_is_client
+ ]},
+ {otp_server, [], [login_otp_is_server,
+ all_algorithms_sftp_exec_reneg_otp_is_server
+ ]} |
+ [{G, [], [{group,otp_client}, {group,otp_server}]} || G <- ssh_image_versions()]
].
-
-vers() ->
+ssh_image_versions() ->
try
%% Find all useful containers in such a way that undefined command, too low
%% priviliges, no containers and containers found give meaningful result:
@@ -94,28 +92,60 @@ end_per_suite(Config) ->
%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
%% Remove dangling images:
%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ catch ssh:stop(),
Config.
+init_per_group(otp_server, Config) ->
+ case proplists:get_value(common_remote_client_algs, Config) of
+ undefined ->
+ SSHver = proplists:get_value(ssh_version, Config, ""),
+ {skip,"No "++SSHver++ " client found in docker"};
+ _ ->
+ Config
+ end;
+
+init_per_group(otp_client, Config) ->
+ Config;
-init_per_group(G, Config) ->
- case lists:member(G, vers()) of
+init_per_group(G, Config0) ->
+ case lists:member(G, ssh_image_versions()) of
true ->
+ %% This group is for one of the images
+ Vssh = atom_to_list(G),
+ Cmnt = io_lib:format("+++ ~s +++",[Vssh]),
+ ct:comment("~s",[Cmnt]),
try start_docker(G) of
{ok,ID} ->
- ct:log("==> ~p",[G]),
- [Vssh|VsslRest] = string:tokens(atom_to_list(G), "-"),
- Vssl = lists:flatten(lists:join($-,VsslRest)),
- ct:comment("+++ ~s + ~s +++",[Vssh,Vssl]),
+ ct:log("==> ~p started",[G]),
%% Find the algorithms that both client and server supports:
{IP,Port} = ip_port([{id,ID}]),
- try common_algs([{id,ID}|Config], IP, Port) of
- {ok, RemoteServerCommon, RemoteClientCommon} ->
- [{ssh_version,Vssh},{ssl_version,Vssl},
- {id,ID},
- {common_server_algs,RemoteServerCommon},
- {common_client_algs,RemoteClientCommon}
- |Config];
+ ct:log("Try contact ~p:~p",[IP,Port]),
+ Config1 = [{id,ID},
+ {ssh_version,Vssh}
+ | Config0],
+ try common_algs(Config1, IP, Port) of
+ {ok, ServerHello, RemoteServerCommon, ClientHello, RemoteClientCommon} ->
+ case chk_hellos([ServerHello,ClientHello], Cmnt) of
+ Cmnt ->
+ ok;
+ NewCmnt ->
+ ct:comment("~s",[NewCmnt])
+ end,
+ AuthMethods =
+ %% This should be obtained by quering the peer, but that
+ %% is a bit hard. It is possible with ssh_protocol_SUITE
+ %% techniques, but it can wait.
+ case Vssh of
+ "dropbear" ++ _ ->
+ [password, publickey];
+ _ ->
+ [password, 'keyboard-interactive', publickey]
+ end,
+ [{common_remote_server_algs,RemoteServerCommon},
+ {common_remote_client_algs,RemoteClientCommon},
+ {common_authmethods,AuthMethods}
+ |Config1];
Other ->
ct:log("Error in init_per_group: ~p",[Other]),
stop_docker(ID),
@@ -138,188 +168,301 @@ init_per_group(G, Config) ->
end;
false ->
- Config
+ Config0
end.
-end_per_group(_, Config) ->
- catch stop_docker(proplists:get_value(id,Config)),
- Config.
+end_per_group(G, Config) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ catch stop_docker(proplists:get_value(id,Config));
+ false ->
+ ok
+ end.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-login_with_password_otp_is_client(Config) ->
- {IP,Port} = ip_port(Config),
- {ok,C} = ssh:connect(IP, Port, [{auth_methods,"password"},
- {user,?USER},
- {password,?PWD},
- {user_dir, new_dir(Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ]),
- ssh:close(C).
-
-%%--------------------------------------------------------------------
-login_with_password_otp_is_server(Config) ->
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods,"password"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, new_dir(Config)},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
- R = exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+2]).\r\n'",
- [<<"Answer=3">>],
- ""),
- ssh:stop_daemon(Server),
- R.
-
-%%--------------------------------------------------------------------
-login_with_keyboard_interactive_otp_is_client(Config) ->
- {DockerIP,DockerPort} = ip_port(Config),
- {ok,C} = ssh:connect(DockerIP, DockerPort,
- [{auth_methods,"keyboard-interactive"},
- {user,?USER},
- {password,?PWD},
- {user_dir, new_dir(Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ]),
- ssh:close(C).
-
-%%--------------------------------------------------------------------
-login_with_keyboard_interactive_otp_is_server(Config) ->
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods,"keyboard-interactive"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, new_dir(Config)},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
- R = exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+3]).\r\n'",
- [<<"Answer=4">>],
- ""),
- ssh:stop_daemon(Server),
- R.
+check_docker_present(_Config) ->
+ ct:log("This testcase is just to show in Monitor that we have a test host with docker installed",[]),
+ {fail, "Test is OK: just showing docker is available"}.
%%--------------------------------------------------------------------
-login_with_all_public_keys_otp_is_client(Config) ->
- CommonAlgs = [{public_key_from_host,A}
- || {public_key,A} <- proplists:get_value(common_server_algs, Config)],
- {DockerIP,DockerPort} = ip_port(Config),
- chk_all_algos(CommonAlgs, Config,
- fun(_Tag,Alg) ->
- ssh:connect(DockerIP, DockerPort,
- [{auth_methods, "publickey"},
- {user, ?USER},
- {user_dir, setup_remote_auth_keys_and_local_priv(Alg, Config)},
- {silently_accept_hosts,true},
- {user_interaction,false}
- ])
+login_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_server_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,Dir} =
+ case AuthMethod of
+ publickey ->
+ {[], setup_remote_auth_keys_and_local_priv(Alg, Config)};
+ _ ->
+ {[{password,?PASSWD}], new_dir(Config)}
+ end,
+ ssh:connect(IP, Port, [{auth_methods, atom_to_list(AuthMethod)},
+ {user,?USER},
+ {user_dir, Dir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ | Opts
+ ])
end).
+
%%--------------------------------------------------------------------
-login_with_all_public_keys_otp_is_server(Config) ->
- CommonAlgs = [{public_key_to_host,A}
- || {public_key,A} <- proplists:get_value(common_client_algs, Config)],
- UserDir = new_dir(Config),
- {Server, Host, HostPort} =
- ssh_test_lib:daemon(0,
- [{auth_methods, "publickey"},
- {system_dir, setup_local_hostdir('ssh-rsa',Config)},
- {user_dir, UserDir},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
- ]),
-
- R = chk_all_algos(CommonAlgs, Config,
- fun(_Tag,Alg) ->
- setup_remote_priv_and_local_auth_keys(Alg, clear_dir(UserDir), Config),
- exec_from_docker(Config, Host, HostPort,
- "'lists:concat([\"Answer=\",1+4]).\r\n'",
- [<<"Answer=5">>],
- "")
- end),
- ssh:stop_daemon(Server),
- R.
+login_otp_is_server(Config) ->
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_client_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+ SysDir = setup_local_hostdir(hd(PublicKeyAlgs), Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,UsrDir} =
+ case AuthMethod of
+ publickey ->
+ {[{user_passwords, [{?USER,?BAD_PASSWD}]}],
+ setup_remote_priv_and_local_auth_keys(Alg, Config)
+ };
+ _ ->
+ {[{user_passwords, [{?USER,?PASSWD}]}],
+ new_dir(Config)
+ }
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, atom_to_list(AuthMethod)},
+ {system_dir, SysDir},
+ {user_dir, UsrDir},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Opts
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
%%--------------------------------------------------------------------
-all_algorithms_otp_is_client(Config) ->
- CommonAlgs = proplists:get_value(common_server_algs, Config),
+all_algorithms_sftp_exec_reneg_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_server_algs, Config),
{IP,Port} = ip_port(Config),
- chk_all_algos(CommonAlgs, Config,
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
fun(Tag, Alg) ->
- ssh:connect(IP, Port, [{user,?USER},
- {password,?PWD},
- {auth_methods, "password"},
- {user_dir, new_dir(Config)},
- {preferred_algorithms, [{Tag,[Alg]}]},
- {silently_accept_hosts,true},
- {user_interaction,false}
+ ConnRes =
+ ssh:connect(IP, Port,
+ [{user,?USER},
+ {password,?PASSWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]) ,
+ test_erl_client_reneg(ConnRes, % Seems that max 10 channels may be open in sshd
+ [{exec,1},
+ {sftp,5},
+ {no_subsyst,1},
+ {setenv, 1},
+ {sftp_async,1}
])
end).
%%--------------------------------------------------------------------
-all_algorithms_otp_is_server(Config) ->
- CommonAlgs = proplists:get_value(common_client_algs, Config),
+all_algorithms_sftp_exec_reneg_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_client_algs, Config),
UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
- chk_all_algos(CommonAlgs, Config,
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
fun(Tag,Alg) ->
HostKeyAlg = case Tag of
public_key -> Alg;
_ -> 'ssh-rsa'
end,
+ SftpRootDir = new_dir(Config),
+ %% ct:log("Rootdir = ~p",[SftpRootDir]),
{Server, Host, HostPort} =
ssh_test_lib:daemon(0,
[{preferred_algorithms, [{Tag,[Alg]}]},
{system_dir, setup_local_hostdir(HostKeyAlg, Config)},
{user_dir, UserDir},
- {user_passwords, [{?USER,?PWD}]},
- {failfun, fun ssh_test_lib:failfun/2}
+ {user_passwords, [{?USER,?PASSWD}]},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {subsystems,
+ [ssh_sftpd:subsystem_spec([{cwd,SftpRootDir},
+ {root,SftpRootDir}]),
+ {"echo_10",{ssh_echo_server,[10,[{dbg,true}]]}}
+ ]}
]),
- R = exec_from_docker(Config, Host, HostPort,
- "hi_there.\r\n",
- [<<"hi_there">>],
- ""),
+ R = do([fun() ->
+ exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ "")
+ end,
+ fun() ->
+ sftp_tests_erl_server(Config, Host, HostPort, SftpRootDir, UserDir)
+ end
+ ]),
ssh:stop_daemon(Server),
R
end).
%%--------------------------------------------------------------------
+send_recv_big_with_renegotiate_otp_is_client(Config) ->
+ %% Connect to the remote openssh server:
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, setup_remote_auth_keys_and_local_priv('ssh-rsa', Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Open a channel and exec the Linux 'cat' command at the openssh side.
+ %% This 'cat' will read stdin and write to stdout until an eof is read from stdin.
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch1, "cat", infinity),
+
+ %% Build big binary
+ HalfSizeBytes = 100*1000*1000,
+ Data = << <<X:32>> || X <- lists:seq(1, HalfSizeBytes div 4)>>,
+
+ %% Send the data. Must spawn a process to avoid deadlock. The client will block
+ %% until all is sent through the send window. But the server will stop receiveing
+ %% when the servers send-window towards the client is full.
+ %% Since the client can't receive before the server has received all but 655k from the client
+ %% ssh_connection:send/4 is blocking...
+ spawn_link(
+ fun() ->
+ ct:comment("Sending ~p Mbytes with renegotiation in the middle",[2*byte_size(Data)/1000000]),
+ %% ct:log("sending first ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Init renegotiation test",[]),
+ Kex1 = renegotiate_test(init, C),
+ %% ct:log("sending next ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Finnish renegotiation test",[]),
+ renegotiate_test(Kex1, C),
+ %% ct:log("sending eof",[]),
+ ok = ssh_connection:send_eof(C, Ch1)
+ %%, ct:log("READY, sent ~p bytes",[2*byte_size(Data)])
+ end),
+
+ {eof,ReceivedData} =
+ loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ %%ct:log("Get more ~p",[ ExpectedSize-byte_size(Acc) ]),
+ receive
+ {ssh_cm, C, {eof,Ch}} when Ch==Ch1 ->
+ %% ct:log("eof received",[]),
+ {eof,Acc};
+
+ {ssh_cm, C, {data,Ch,0,B}} when Ch==Ch1,
+ is_binary(B) ->
+ %% ct:log("(1) Received ~p bytes (total ~p), missing ~p bytes",
+ %% [byte_size(B),
+ %% byte_size(B)+byte_size(Acc),
+ %% 2*byte_size(Data)-(byte_size(B)+byte_size(Acc))]),
+ ssh_connection:adjust_window(C, Ch1, byte_size(B)),
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>),
+
+ ExpectedData = <<Data/binary, Data/binary>>,
+ case ReceivedData of
+ ExpectedData ->
+ %% ct:log("Correct data returned",[]),
+ %% receive close messages
+ loop_until(fun(Left) -> %% ct:log("Expect: ~p",[Left]),
+ Left == []
+ end,
+ fun([Next|Rest]) ->
+ receive
+ {ssh_cm,C,Next} -> Rest
+ end
+ end,
+ [%% Already received: {eof, Ch1},
+ {exit_status,Ch1,0},
+ {closed,Ch1}]
+ ),
+ ok;
+ _ when is_binary(ReceivedData) ->
+ ct:fail("~p bytes echoed but ~p expected", [byte_size(ReceivedData), 2*byte_size(Data)])
+ end.
+
+%%--------------------------------------------------------------------
%% Utilities ---------------------------------------------------------
%%--------------------------------------------------------------------
-exec_from_docker(WhatEver, {0,0,0,0}, HostPort, Command, Expects, ExtraSshArg) ->
- exec_from_docker(WhatEver, host_ip(), HostPort, Command, Expects, ExtraSshArg);
+%%--------------------------------------------------------------------
+%%
+%% A practical meta function
+%%
+loop_until(CondFun, DoFun, Acc) ->
+ case CondFun(Acc) of
+ true ->
+ Acc;
+ false ->
+ loop_until(CondFun, DoFun, DoFun(Acc))
+ end.
+
+%%--------------------------------------------------------------------
+%%
+%% Exec the Command in the docker. Add the arguments ExtraSshArg in the
+%% ssh command.
+%%
+%% If Expects is returned, then return 'ok', else return {fail,Msg}.
+%%
exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
is_list(Config) ->
{DockerIP,DockerPort} = ip_port(Config),
{ok,C} = ssh:connect(DockerIP, DockerPort,
[{user,?USER},
- {password,?PWD},
+ {password,?PASSWD},
{user_dir, new_dir(Config)},
{silently_accept_hosts,true},
{user_interaction,false}
]),
- R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg, Config),
ssh:close(C),
- R;
-
-exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)) ->
- SSH_from_docker =
- lists:concat(["sshpass -p ",?PWD," ",
- "/buildroot/ssh/bin/ssh -p ",HostPort," -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
- ExtraSshArg," ",
- inet_parse:ntoa(HostIP)," "
- ]),
- ExecCommand = SSH_from_docker ++ Command,
- R = exec(C, ExecCommand),
- case R of
- {ok,{ExitStatus,Result}} when ExitStatus == 0 ->
+ R.
+
+exec_from_docker(C, DestIP, DestPort, Command, Expects, ExtraSshArg, Config) when is_binary(hd(Expects)) ->
+ ExecCommand =
+ lists:concat(
+ ["sshpass -p ",?PASSWD," "
+ | case proplists:get_value(ssh_version,Config) of
+ "dropbear" ++ _ ->
+ ["dbclient -y -y -p ",DestPort," ",ExtraSshArg," ",iptoa(DestIP)," "];
+
+ _ -> %% OpenSSH or compatible
+ ["/buildroot/ssh/bin/ssh -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," -p ",DestPort," ",iptoa(DestIP)," "]
+ end]) ++ Command,
+
+ case exec(C, ExecCommand) of
+ {ok,{ExitStatus,Result}} = R when ExitStatus == 0 ->
case binary:match(Result, Expects) of
nomatch ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
@@ -327,28 +470,26 @@ exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_bin
_ ->
ok
end;
- {ok,_} ->
+ {ok,_} = R ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
{fail, "Exit status =/= 0"};
- _ ->
+ R ->
ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
{fail, "Couldn't login to host"}
end.
-
-
exec(C, Cmd) ->
- ct:log("~s",[Cmd]),
+ %% ct:log("~s",[Cmd]),
{ok,Ch} = ssh_connection:session_channel(C, 10000),
success = ssh_connection:exec(C, Ch, Cmd, 10000),
- exec_result(C, Ch).
+ result_of_exec(C, Ch).
-exec_result(C, Ch) ->
- exec_result(C, Ch, undefined, <<>>).
+result_of_exec(C, Ch) ->
+ result_of_exec(C, Ch, undefined, <<>>).
-exec_result(C, Ch, ExitStatus, Acc) ->
+result_of_exec(C, Ch, ExitStatus, Acc) ->
receive
{ssh_cm,C,{closed,Ch}} ->
%%ct:log("CHAN ~p got *closed*",[Ch]),
@@ -356,29 +497,37 @@ exec_result(C, Ch, ExitStatus, Acc) ->
{ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
%%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
- exec_result(C, Ch, ExStat, Acc);
+ result_of_exec(C, Ch, ExStat, Acc);
{ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
%%ct:log("CHAN ~p got ~p",[Ch,_X]),
- exec_result(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+ result_of_exec(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
_Other ->
%%ct:log("OTHER: ~p",[_Other]),
- exec_result(C, Ch, ExitStatus, Acc)
+ result_of_exec(C, Ch, ExitStatus, Acc)
after 5000 ->
- %%ct:log("NO MORE, received so far:~n~s",[Acc]),
+ ct:log("NO MORE, received so far:~n~s",[Acc]),
{error, timeout}
end.
-chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+%%--------------------------------------------------------------------
+%%
+%% Loop through all {Tag,Alg} pairs in CommonAlgs, call DoTestFun(Tag,Alg) which
+%% returns one of {ok,C}, ok, or Other.
+%%
+%% The chk_all_algos returns 'ok' or {fail,FaledAlgosList}
+%%
+
+chk_all_algos(FunctionName, CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
ct:comment("~p algorithms",[length(CommonAlgs)]),
%% Check each algorithm
Failed =
lists:foldl(
fun({Tag,Alg}, FailedAlgos) ->
- ct:log("Try ~p",[Alg]),
+ %% ct:log("Try ~p",[Alg]),
case DoTestFun(Tag,Alg) of
{ok,C} ->
ssh:close(C),
@@ -387,10 +536,10 @@ chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
FailedAlgos;
Other ->
ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
- [Alg|FailedAlgos]
+ [{Alg,Other}|FailedAlgos]
end
end, [], CommonAlgs),
- ct:pal("~s", [format_result_table_use_all_algos(Config, CommonAlgs, Failed)]),
+ ct:pal("~s", [format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed)]),
case Failed of
[] ->
ok;
@@ -398,6 +547,41 @@ chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
{fail, Failed}
end.
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Call all Funs as Fun() which returns 'ok', {ok,C} or Other.
+%%% do/1 returns 'ok' or the first encountered value that is not
+%%% successful.
+%%%
+
+do(Funs) ->
+ do(Funs, 1).
+
+do([Fun|Funs], N) ->
+ case Fun() of
+ ok ->
+ %% ct:log("Fun ~p ok",[N]),
+ do(Funs, N-1);
+ {ok,C} ->
+ %% ct:log("Fun ~p {ok,C}",[N]),
+ ssh:close(C),
+ do(Funs, N-1);
+ Other ->
+ ct:log("Fun ~p FAILED:~n~p",[N, Other]),
+ Other
+ end;
+
+do([], _) ->
+ %% ct:log("All Funs ok",[]),
+ ok.
+
+%%--------------------------------------------------------------------
+%%
+%% Functions to set up local and remote host's and user's keys and directories
+%%
+
setup_local_hostdir(KeyAlg, Config) ->
setup_local_hostdir(KeyAlg, new_dir(Config), Config).
setup_local_hostdir(KeyAlg, HostDir, Config) ->
@@ -428,7 +612,7 @@ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
ok = file:write_file(DstFile++".pub", Publ),
%% Remote auth_methods with public key
{ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
- {password, ?PWD },
+ {password, ?PASSWD },
{auth_methods, "password"},
{silently_accept_hosts,true},
{user_interaction,false}
@@ -460,7 +644,7 @@ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
ok = file:write_file(AuthKeyFile, Publ),
%% Remote private and public key
{ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
- {password, ?PWD },
+ {password, ?PASSWD },
{auth_methods, "password"},
{silently_accept_hosts,true},
{user_interaction,false}
@@ -485,6 +669,7 @@ priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
{ok, {Priv,Publ}}.
+%%%---------------- The default filenames
src_filename(user, 'ssh-rsa' ) -> "id_rsa";
src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
@@ -516,7 +701,11 @@ dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
-format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
+%%--------------------------------------------------------------------
+%%
+%% Format the result table for chk_all_algos/4
+%%
+format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed) ->
%% Write a nice table with the result
AlgHead = 'Algorithm',
AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
@@ -529,23 +718,25 @@ format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
end,
{io_lib:format('~s ~*s ~s~n',
[Tag, -AlgWidth, A,
- case lists:member(A,Failed) of
- true -> "<<<< FAIL <<<<";
- false-> "(ok)"
+ case proplists:get_value(A,Failed) of
+ undefined -> "(ok)";
+ Err -> io_lib:format("<<<< FAIL <<<< ~p",[Err])
end]),
T}
end, undefined, CommonAlgs),
Vssh = proplists:get_value(ssh_version,Config,""),
- Vssl = proplists:get_value(ssl_version,Config,""),
- io_lib:format("~nResults, Peer versions: ~s and ~s~n"
+ io_lib:format("~nResults of ~p, Peer version: ~s~n~n"
"Tag ~*s Result~n"
"=====~*..=s=======~n~s"
- ,[Vssh,Vssl,
- -AlgWidth,AlgHead,
+ ,[FunctionName, Vssh,
+ -AlgWidth, AlgHead,
AlgWidth, "", ResultTable]).
-
+%%--------------------------------------------------------------------
+%%
+%% Docker handling: start_docker/1 and stop_docker/1
+%%
start_docker(Ver) ->
Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
Id0 = os:cmd(Cmnd),
@@ -572,6 +763,10 @@ is_docker_sha(L) ->
(_) -> false
end, L).
+%%--------------------------------------------------------------------
+%%
+%% Misc docker info functions
+
ip_port(Config) ->
{_Ver,{IP,Port},_} = proplists:get_value(id,Config),
{IP,Port}.
@@ -590,6 +785,23 @@ ip(Id) ->
{ok,IP} = inet:parse_address(IPstr),
IP.
+%%--------------------------------------------------------------------
+%%
+%% Normalize the host returned from ssh_test_lib
+
+iptoa({0,0,0,0}) -> inet_parse:ntoa(host_ip());
+iptoa(IP) -> inet_parse:ntoa(IP).
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Create a new fresh directory or clear an existing one
+%%
+
new_dir(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
SubDirName = integer_to_list(erlang:system_time()),
@@ -626,20 +838,34 @@ delete_all_contents(Dir) ->
end
end, Fs).
+%%--------------------------------------------------------------------
+%%
+%% Find the intersection of algoritms for otp ssh and the docker ssh.
+%% Returns {ok, ServerHello, Server, ClientHello, Client} where Server are the algorithms common
+%% with the docker server and analogous for Client.
+%%
+%% Client may be undefined if no usable client is found.
+%%
+%% Both Server and Client are lists of {Tag,AlgName}.
+%%
+
common_algs(Config, IP, Port) ->
case remote_server_algs(IP, Port) of
- {ok, {RemoteHelloBin, RemoteServerKexInit}} ->
+ {ok, {ServerHello, RemoteServerKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(ServerHello)),
+ ct:log("Remote server:~n~p~n~p",[ServerHello, RemoteServerAlgs]),
case remote_client_algs(Config) of
- {ok,{_Hello,RemoteClientKexInit}} ->
- RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
- Server = find_common_algs(RemoteServerAlgs,
- use_algorithms(RemoteHelloBin)),
+ {ok,{ClientHello,RemoteClientKexInit}} ->
RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
Client = find_common_algs(RemoteClientAlgs,
- use_algorithms(RemoteHelloBin)),
- ct:log("Docker server algorithms:~n ~p~n~nDocker client algorithms:~n ~p",
- [RemoteServerAlgs,RemoteClientAlgs]),
- {ok, Server, Client};
+ use_algorithms(ClientHello)),
+ ct:log("Remote client:~n~p~n~p",[ClientHello, RemoteClientAlgs]),
+ {ok, ServerHello, Server, ClientHello, Client};
+ {error,_} =TO ->
+ ct:log("Remote client algs can't be found: ~p",[TO]),
+ {ok, ServerHello, Server, undefined, undefined};
Other ->
Other
end;
@@ -648,6 +874,24 @@ common_algs(Config, IP, Port) ->
end.
+chk_hellos(Hs, Str) ->
+ lists:foldl(
+ fun(H, Acc) ->
+ try binary:split(H, <<"-">>, [global])
+ of
+ %% [<<"SSH">>,<<"2.0">>|_] ->
+ %% Acc;
+ [<<"SSH">>,OldVer = <<"1.",_/binary>>|_] ->
+ io_lib:format("~s, Old SSH ver ~s",[Acc,OldVer]);
+ _ ->
+ Acc
+ catch
+ _:_ ->
+ Acc
+ end
+ end, Str, Hs).
+
+
find_common_algs(Remote, Local) ->
[{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
ssh_test_lib:intersection(Remote,
@@ -685,12 +929,18 @@ kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
{server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
-
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote server
+%%
+%% Connect with tcp to the server, send a hello and read the returned
+%% server hello and kexinit message.
+%%
remote_server_algs(IP, Port) ->
case try_gen_tcp_connect(IP, Port, 5) of
{ok,S} ->
ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
- receive_hello(S, <<>>);
+ receive_hello(S);
{error,Error} ->
{error,Error}
end.
@@ -709,6 +959,13 @@ try_gen_tcp_connect(_, _, _) ->
{error, "No contact"}.
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote client
+%%
+%% Set up a fake ssh server and make the remote client connect to it. Use
+%% hello message and the kexinit message.
+%%
remote_client_algs(Config) ->
Parent = self(),
Ref = make_ref(),
@@ -719,7 +976,7 @@ remote_client_algs(Config) ->
Parent ! {addr,Ref,IP,Port},
{ok,S} = gen_tcp:accept(Sl),
ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
- Parent ! {Ref,receive_hello(S, <<>>)}
+ Parent ! {Ref,receive_hello(S)}
end),
receive
{addr,Ref,IP,Port} ->
@@ -732,14 +989,28 @@ remote_client_algs(Config) ->
receive
{Ref, Result} ->
Result
- after 15000 ->
- {error, timeout2}
+ after 5000 ->
+ {error, {timeout,2}}
end
- after 15000 ->
- {error, timeout1}
+ after 5000 ->
+ {error, {timeout,1}}
end.
+%%% Receive a few packets from the remote server or client and find what is supported:
+
+receive_hello(S) ->
+ try
+ receive_hello(S, <<>>)
+ of
+ Result ->
+ Result
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
receive_hello(S, Ack) ->
%% The Ack is to collect bytes until the full message is received
@@ -747,20 +1018,19 @@ receive_hello(S, Ack) ->
{tcp, S, Bin0} when is_binary(Bin0) ->
case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
[Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
- ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ %% ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
{ok, {Hello, receive_kexinit(S, NextPacket)}};
[Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
- ct:comment("Old SSH ~s",["1.99"]),
- ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ %% ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
{ok, {Hello, receive_kexinit(S, NextPacket)}};
[Bin] when size(Bin) < 256 ->
- ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
- [io_lib:format('~2.16.0b ',[C])
- || C <- binary_to_list(Bin0)
- ]
- ]),
+ %% ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ %% [io_lib:format('~2.16.0b ',[C])
+ %% || C <- binary_to_list(Bin0)
+ %% ]
+ %% ]),
receive_hello(S, Bin0);
_ ->
@@ -804,11 +1074,326 @@ receive_kexinit(S, Ack) ->
throw(timeout)
end.
+%%%----------------------------------------------------------------
+%%% Test of sftp from the OpenSSH client side
+%%%
+sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
+ try
+ Cmnds = prepare_local_directory(ServerRootDir),
+ call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
+ check_local_directory(ServerRootDir)
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
-host_ip() ->
- {ok,Name} = inet:gethostname(),
- {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
- IP.
+prepare_local_directory(ServerRootDir) ->
+ file:write_file(filename:join(ServerRootDir,"tst1"),
+ <<"Some test text">>
+ ),
+ ["get tst1",
+ "put tst1 tst2",
+ "put tst1 tst3",
+ "rename tst1 ex_tst1",
+ "rm tst3",
+ "mkdir mydir",
+ "cd mydir",
+ "put tst1 file_1",
+ "put tst1 unreadable_file",
+ "chmod 222 unreadable_file",
+ "exit"].
+
+check_local_directory(ServerRootDir) ->
+ case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of
+ ["ex_tst1","mydir","tst2"] ->
+ {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")),
+ case file:read_file(filename:join(ServerRootDir,"tst2")) of
+ {ok,Expect} ->
+ case lists:sort(ok(file:list_dir(filename:join(ServerRootDir,"mydir"))) -- [".",".."]) of
+ ["file_1","unreadable_file"] ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","file_1"])) of
+ {ok,Expect} ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","unreadable_file"])) of
+ {error,_} ->
+ ok;
+ {ok,_} ->
+ {error, {could_read_unreadable,"mydir/unreadable_file"}}
+ end;
+ {ok,Other} ->
+ ct:log("file_1:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"mydir/file_1"}}
+ end;
+ Other ->
+ ct:log("Directory ~s~n~p",[filename:join(ServerRootDir,"mydir"),Other]),
+ {error,{bad_dir_contents,"mydir"}}
+ end;
+ {ok,Other} ->
+ ct:log("tst2:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"tst2"}}
+ end;
+ ["tst1"] ->
+ {error,{missing_file,"tst2"}};
+ Other ->
+ ct:log("Directory ~s~n~p",[ServerRootDir,Other]),
+ {error,{bad_dir_contents,"/"}}
+ end.
+
+call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, UserDir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Make commands for "expect" in the docker:
+ PreExpectCmnds = ["spawn /buildroot/ssh/bin/sftp -oPort="++integer_to_list(ServerPort)++
+ " -oCheckHostIP=no -oStrictHostKeyChecking=no " ++
+ iptoa(ServerIP)++"\n"
+ ],
+ PostExpectCmnds= [],
+ ExpectCmnds =
+ PreExpectCmnds ++
+ ["expect \"sftp>\" {send \""++Cmnd++"\n\"}\n" || Cmnd <- Cmnds] ++
+ PostExpectCmnds,
+
+ %% Make an commands file in the docker
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ ok = ssh_sftp:write_file(Ch, "commands", erlang:iolist_to_binary(ExpectCmnds)),
+ ok = ssh_sftp:stop_channel(Ch),
+
+ %% Call expect in the docker
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ Kex1 = renegotiate_test(init, C),
+ success = ssh_connection:exec(C, Ch1, "expect commands", infinity),
+
+ renegotiate_test(Kex1, C),
+ recv_log_msgs(C, Ch1),
+
+ %% Done.
+ ssh:close(C).
+
+recv_log_msgs(C, Ch) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %% ct:log("Channel closed ~p",[{closed,1}]),
+ ok;
+ {ssh_cm,C,{data,Ch,1,Msg}} ->
+ ct:log("*** ERROR from docker:~n~s",[Msg]),
+ recv_log_msgs(C, Ch);
+ {ssh_cm,C,_Msg} ->
+ %% ct:log("Got ~p",[_Msg]),
+ recv_log_msgs(C, Ch)
+ end.
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+%%%
+%%% Tests from the Erlang client side
+%%%
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+test_erl_client_reneg({ok,C}, Spec) ->
+ %% Start the test processes on the connection C:
+ Parent = self(),
+ Pids = [spawn(
+ fun() ->
+ Parent ! {self(), TestType, Id, one_test_erl_client(TestType,Id,C)}
+ end
+ )
+ || {TestType,N} <- Spec,
+ Id <- lists:seq(1,N)],
+
+ Kex1 = renegotiate_test(init, C),
+
+ %% Collect the results:
+ case lists:filter(
+ fun(R) -> R=/=ok end,
+ [receive
+ {Pid,_TestType,_Id,ok} ->
+ %% ct:log("Test ~p:~p passed!", [_TestType,_Id]),
+ ok;
+ {Pid,TestType,Id,OtherResult} ->
+ ct:log("~p:~p ~p ~p~n~p",[?MODULE,?LINE,TestType,Id,OtherResult]),
+ {error,TestType,Id}
+ end || Pid <- Pids])
+ of
+ [] ->
+ renegotiate_test(Kex1, C),
+ {ok,C};
+ Other ->
+ renegotiate_test(Kex1, C),
+ Other
+ end;
+
+test_erl_client_reneg(Error, _) ->
+ Error.
+
+
+one_test_erl_client(exec, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch, "echo Hi there", 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,<<"Hi there\n">>} ->
+ ok;
+ Other ->
+ ct:pal("exec Got other ~p", [Other]),
+ {error, {exec,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(no_subsyst, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ case ssh_connection:subsystem(C, Ch, "foo", infinity) of
+ failure ->
+ ok;
+ Other ->
+ ct:pal("no_subsyst Got other ~p", [Other]),
+ {error, {no_subsyst,Id,bad_ret,Other,undefined}}
+ end;
+
+one_test_erl_client(setenv, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ Var = "ENV_TEST",
+ Value = lists:concat(["env_test_",Id,"_",erlang:system_time()]),
+ Env = case ssh_connection:setenv(C, Ch, Var, Value, infinity) of
+ success -> binary_to_list(Value++"\n");
+ failure -> <<"\n">>
+ end,
+ success = ssh_connection:exec(C, Ch, "echo $"++Var, 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,Env} ->
+ ok;
+ Other ->
+ ct:pal("setenv Got other ~p", [Other]),
+ {error, {setenv,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
+ try
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ %% A new fresh name of a new file tree:
+ RootDir = lists:concat(["r_",Id,"_",erlang:system_time()]),
+ %% Check that it does not exist:
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ %% Create it:
+ ok = ssh_sftp:make_dir(Ch, RootDir),
+ {ok, #file_info{type=directory, access=read_write}} = ssh_sftp:read_file_info(Ch, RootDir),
+ R = do_sftp_tests_erl_client(SFTP, C, Ch, Id, RootDir),
+ catch ssh_sftp:stop_channel(Ch),
+ R
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {SFTP,Id,Class,Error,ST}}
+ end.
+
+
+
+do_sftp_tests_erl_client(sftp_async, _C, Ch, _Id, RootDir) ->
+ FileName1 = "boring_name",
+ F1 = filename:join(RootDir, FileName1),
+ %% Open a new handle and start writing:
+ {ok,Handle1} = ssh_sftp:open(Ch, F1, [write,binary]),
+ {async,Aref1} = ssh_sftp:awrite(Ch, Handle1, <<0:250000/unsigned-unit:8>>),
+ wait_for_async_result(Aref1);
+
+do_sftp_tests_erl_client(sftp, _C, Ch, _Id, RootDir) ->
+ FileName0 = "f0",
+ F0 = filename:join(RootDir, FileName0),
+
+ %% Create and write a file:
+ ok = ssh_sftp:write_file(Ch,
+ F0 = filename:join(RootDir, FileName0),
+ Data0 = mkbin(1234,240)),
+ {ok,Data0} = ssh_sftp:read_file(Ch, F0),
+ {ok, #file_info{type=regular, access=read_write, size=1234}} = ssh_sftp:read_file_info(Ch, F0),
+
+ %% Re-write:
+ {ok,Handle0} = ssh_sftp:open(Ch, F0, [write,read,binary]),
+ ok = ssh_sftp:pwrite(Ch, Handle0, 16, Data0_1=mkbin(10,255)),
+
+ <<B1:16/binary, _:10/binary, B2:(1234-26)/binary>> = Data0,
+ FileContents = <<B1:16/binary, Data0_1:10/binary, B2:(1234-26)/binary>>,
+
+ <<_:1/binary, Part:25/binary, _/binary>> = FileContents,
+ {ok, Part} = ssh_sftp:pread(Ch, Handle0, 1, 25),
+
+ %% Check:
+ {ok, FileContents} = ssh_sftp:pread(Ch, Handle0, 0, 1234),
+ ok = ssh_sftp:close(Ch, Handle0),
+
+ %% Check in another way:
+ {ok, FileContents} = ssh_sftp:read_file(Ch, F0),
+
+ %% Remove write access rights and check that it can't be written:
+ ok = ssh_sftp:write_file_info(Ch, F0, #file_info{mode=8#400}), %read}),
+ {ok, #file_info{type=regular, access=read}} = ssh_sftp:read_file_info(Ch, F0),
+ {error,permission_denied} = ssh_sftp:write_file(Ch, F0, mkbin(10,14)),
+
+ %% Test deletion of file and dir:
+ [FileName0] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:delete(Ch, F0),
+ [] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:del_dir(Ch, RootDir),
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ ok.
+
+
+wait_for_async_result(Aref) ->
+ receive
+ {async_reply, Aref, Result} ->
+ Result
+ after
+ 60000 ->
+ timeout
+ end.
+
+
+mkbin(Size, Byte) ->
+ list_to_binary(lists:duplicate(Size,Byte)).
+
+ok({ok,X}) -> X.
+
+%%%----------------------------------------------------------------
+renegotiate_test(init, ConnectionRef) ->
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ %%ct:log("Renegotiate test initiated!",[]),
+ Kex1;
+
+renegotiate_test(Kex1, ConnectionRef) ->
+ case ssh_test_lib:get_kex_init(ConnectionRef) of
+ Kex1 ->
+ ct:log("Renegotiate test failed, Kex1 == Kex2!",[]),
+ error(renegotiate_failed);
+ _ ->
+ %% ct:log("Renegotiate test passed!",[]),
+ ok
+ end.
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
new file mode 100755
index 0000000000..85973081d0
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ WORKDIR /buildroot
+
+ RUN apt-get -y update
+ RUN apt-get -y upgrade
+ RUN apt-get -y install openssh-sftp-server
+%% RUN echo 81 | apt-get -y install dropbear
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh-dropbear -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
new file mode 100755
index 0000000000..d98c0cfaa3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh-run
+
+VER=v2016.72
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-ssh-dropbear-installed:${VER}
+
+ WORKDIR /buildroot
+
+ CMD dropbear -F -p 1234
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:dropbear${VER} -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
index 983c57b18b..2e08408841 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -47,7 +47,8 @@ cat - > TempDockerFile <<EOF
RUN ./configure --without-pie \
--prefix=/buildroot/ssh \
--with-ssl-dir=/buildroot/ssl \
- --with-pam
+ --with-pam \
+ LDFLAGS=-Wl,-R/buildroot/ssl/lib
RUN make
RUN make install
RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
index 66f8358b8a..4ab2a8bddc 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -23,6 +23,16 @@ case "$1" in
;;
esac
+case $1$2 in
+ openssl0.9.8[a-l])
+ CONFIG_FLAGS=no-asm
+ ;;
+ *)
+ CONFIG_FLAGS=
+ ;;
+esac
+
+
# This way of fetching the tar-file separate from the docker commands makes
# http-proxy handling way easier. The wget command handles the $https_proxy
# variable while the docker command must have /etc/docker/something changed
@@ -42,10 +52,10 @@ cat - > TempDockerFile <<EOF
WORKDIR ${FAM}-${VER}
- RUN ./config --prefix=/buildroot/ssl
+ RUN ./config --prefix=/buildroot/ssl ${CONFIG_FLAGS}
RUN make
- RUN make install
+ RUN make install_sw
RUN echo Built ${FAM}-${VER}
EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
index 16b9c21d9f..0dcf8cb570 100755
--- a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -3,19 +3,21 @@
UBUNTU_VERSION=16.04
SSH_SSL_VERSIONS=(\
- openssh 4.4p1 openssl 0.9.8zh \
- openssh 4.5p1 openssl 0.9.8zh \
- openssh 5.0p1 openssl 0.9.8zh \
- openssh 6.2p2 openssl 0.9.8zh \
- openssh 6.3p1 openssl 0.9.8zh \
- \
- openssh 7.1p1 openssl 1.0.0t \
- \
- openssh 7.1p1 openssl 1.0.1p \
- \
- openssh 6.6p1 openssl 1.0.2n \
- openssh 7.1p1 openssl 1.0.2n \
- openssh 7.6p1 openssl 1.0.2n \
+ openssh 4.4p1 openssl 0.9.8c \
+ openssh 4.5p1 openssl 0.9.8m \
+ openssh 5.0p1 openssl 0.9.8za \
+ openssh 6.2p2 openssl 0.9.8c \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0a \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ \
+ openssh 7.6p1 libressl 2.6.4 \
)
if [ "x$1" == "x-b" ]
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index ba4518cfe6..257f2f70d7 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,9 +45,18 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
+ start_shell_exec_fun2,
+ start_shell_exec_fun3,
+ start_shell_exec_direct_fun,
+ start_shell_exec_direct_fun2,
+ start_shell_exec_direct_fun3,
+ start_shell_exec_direct_fun1_error,
+ start_shell_exec_direct_fun1_error_type,
start_shell_sock_exec_fun,
start_shell_sock_daemon_exec,
connect_sock_not_tcp,
@@ -85,6 +94,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(Config).
end_per_suite(Config) ->
+ catch ssh:stop(),
Config.
%%--------------------------------------------------------------------
@@ -519,7 +529,7 @@ start_shell_exec(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, {?MODULE,ssh_exec,[]}} ]),
+ {exec, {?MODULE,ssh_exec_echo,[]}} ]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -532,7 +542,7 @@ start_shell_exec(Config) when is_list(Config) ->
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -542,10 +552,42 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
-start_shell_exec_fun() ->
- [{doc, "start shell to exec command"}].
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ 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"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
-start_shell_exec_fun(Config) when is_list(Config) ->
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
file:make_dir(UserDir),
@@ -553,24 +595,109 @@ start_shell_exec_fun(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
{password, "morot"},
{user_interaction, true},
- {user_dir, UserDir}]),
+ {user_dir, UserDir}
+ ]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "testing", infinity),
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+start_shell_exec_fun(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/1,
+ "testing", <<"echo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_fun2(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/2,
+ "testing", <<"echo foo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_fun3(Config) ->
+ do_start_shell_exec_fun(fun ssh_exec_echo/3,
+ "testing", <<"echo foo testing\r\n">>, 0,
+ Config).
+
+start_shell_exec_direct_fun(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/1},
+ "testing", <<"echo testing\n">>, 0,
+ Config).
+
+start_shell_exec_direct_fun2(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/2},
+ "testing", <<"echo foo testing">>, 0,
+ Config).
+
+start_shell_exec_direct_fun3(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo/3},
+ "testing", <<"echo foo testing">>, 0,
+ Config).
+
+start_shell_exec_direct_fun1_error(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo_error_return/1},
+ "testing", <<"Error in \"testing\": {bad}\n">>, 1,
+ Config).
+
+start_shell_exec_direct_fun1_error_type(Config) ->
+ do_start_shell_exec_fun({direct, fun ssh_exec_direct_echo_error_return_type/1},
+ "testing", <<"Error in \"testing\": Bad exec-plugin return: very_bad\n">>, 1,
+ Config).
+
+
+
+do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ 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, Fun}]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0, Command, infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, ExpectType, Expect}} ->
ok
after 5000 ->
- ct:fail("Exec Timeout")
+ receive
+ Other ->
+ ct:pal("Received other:~n~p",[Other]),
+ ct:fail("Unexpected response")
+ after 0 ->
+ ct:fail("Exec Timeout")
+ end
end,
ssh:close(ConnectionRef),
@@ -588,7 +715,7 @@ start_shell_sock_exec_fun(Config) when is_list(Config) ->
{Pid, HostD, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {exec, fun ssh_exec_echo/1}]),
Host = ssh_test_lib:ntoa(ssh_test_lib:mangle_connect_address(HostD)),
{ok, Sock} = ssh_test_lib:gen_tcp_connect(Host, Port, [{active,false}]),
@@ -604,7 +731,7 @@ start_shell_sock_exec_fun(Config) when is_list(Config) ->
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -628,7 +755,7 @@ start_shell_sock_daemon_exec(Config) ->
{ok, _Pid} = ssh:daemon(Ss, [{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}])
+ {exec, fun ssh_exec_echo/1}])
end),
{ok,Sc} = gen_tcp:accept(Sl),
{ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true},
@@ -643,7 +770,7 @@ start_shell_sock_daemon_exec(Config) ->
"testing", infinity),
receive
- {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -754,7 +881,7 @@ stop_listener(Config) when is_list(Config) ->
{Pid0, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
{password, "morot"},
- {exec, fun ssh_exec/1}]),
+ {exec, fun ssh_exec_echo/1}]),
ConnectionRef0 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -774,7 +901,7 @@ stop_listener(Config) when is_list(Config) ->
success = ssh_connection:exec(ConnectionRef0, ChannelId0,
"testing", infinity),
receive
- {ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"testing\r\n">>}} ->
+ {ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"echo testing\r\n">>}} ->
ok
after 5000 ->
ct:fail("Exec Timeout")
@@ -783,7 +910,7 @@ stop_listener(Config) when is_list(Config) ->
case ssh_test_lib:daemon(Port, [{system_dir, SysDir},
{user_dir, UserDir},
{password, "potatis"},
- {exec, fun ssh_exec/1}]) of
+ {exec, fun ssh_exec_echo/1}]) of
{Pid1, Host, Port} ->
ConnectionRef1 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
{user, "foo"},
@@ -800,6 +927,8 @@ stop_listener(Config) when is_list(Config) ->
ssh:stop_daemon(Pid0),
ssh:stop_daemon(Pid1);
Error ->
+ ssh:close(ConnectionRef0),
+ ssh:stop_daemon(Pid0),
ct:fail({unexpected, Error})
end.
@@ -819,11 +948,22 @@ start_subsystem_on_closed_channel(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, ChannelId),
+ {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId1),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId1, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId1, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId1, "exit().\n", 5000),
- {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+ %% Test that there could be a gap between close and an operation (Bugfix OTP-14939):
+ {ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId2),
+ timer:sleep(2000),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId2, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId2, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId2, "exit().\n", 5000),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
@@ -981,7 +1121,22 @@ start_our_shell(_User, _Peer) ->
%% Don't actually loop, just exit
end).
-ssh_exec(Cmd) ->
+
+ssh_exec_echo(Cmd) ->
+ spawn(fun() ->
+ io:format("echo "++Cmd ++ "\n")
+ end).
+
+ssh_exec_echo(Cmd, User) ->
spawn(fun() ->
- io:format(Cmd ++ "\n")
+ io:format(io_lib:format("echo ~s ~s\n",[User,Cmd]))
end).
+ssh_exec_echo(Cmd, User, _PeerAddr) ->
+ ssh_exec_echo(Cmd,User).
+
+ssh_exec_direct_echo(Cmd) -> {ok, io_lib:format("echo ~s~n",[Cmd])}.
+ssh_exec_direct_echo(Cmd, User) -> {ok, io_lib:format("echo ~s ~s",[User,Cmd])}.
+ssh_exec_direct_echo(Cmd, User, _PeerAddr) -> ssh_exec_direct_echo(Cmd,User).
+
+ssh_exec_direct_echo_error_return(_Cmd) -> {error, {bad}}.
+ssh_exec_direct_echo_error_return_type(_Cmd) -> very_bad.
diff --git a/lib/ssh/test/ssh_engine_SUITE.erl b/lib/ssh/test/ssh_engine_SUITE.erl
index daf93891e9..c131a70973 100644
--- a/lib/ssh/test/ssh_engine_SUITE.erl
+++ b/lib/ssh/test/ssh_engine_SUITE.erl
@@ -55,16 +55,22 @@ basic_tests() ->
init_per_suite(Config) ->
ssh:start(),
?CHECK_CRYPTO(
- case load_engine() of
- {ok,E} ->
- [{engine,E}|Config];
- {error, notsup} ->
- {skip, "Engine not supported on this OpenSSL version"};
- {error, bad_engine_id} ->
- {skip, "Dynamic Engine not supported"};
- Other ->
- ct:log("Engine load failed: ~p",[Other]),
- {fail, "Engine load failed"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Strange Engine stuff"};
+
+ _ ->
+ case load_engine() of
+ {ok,E} ->
+ [{engine,E}|Config];
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end
end
).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 144ec7f8fd..12a85c40aa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -70,7 +70,8 @@
hostkey_fingerprint_check_sha256/1,
hostkey_fingerprint_check_sha384/1,
hostkey_fingerprint_check_sha512/1,
- hostkey_fingerprint_check_list/1
+ hostkey_fingerprint_check_list/1,
+ save_accepted_host_option/1
]).
%%% Common test callbacks
@@ -124,6 +125,7 @@ all() ->
id_string_own_string_server,
id_string_own_string_server_trail_space,
id_string_random_server,
+ save_accepted_host_option,
{group, hardening_tests}
].
@@ -206,32 +208,23 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
- Config.
-
-end_per_testcase(TestCase, Config) when TestCase == server_password_option;
- TestCase == server_userpassword_option;
- TestCase == server_pwdfun_option;
- TestCase == server_pwdfun_4_option ->
+ %% Create a clean user_dir
UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
+ file:make_dir(UserDir),
+ [{user_dir,UserDir}|Config].
-end_per_testcase(_Config) ->
+end_per_testcase(_TestCase, Config) ->
ssh:stop(),
ok.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% validate to server that uses the 'password' option
server_password_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -262,12 +255,10 @@ server_password_option(Config) when is_list(Config) ->
%%% validate to server that uses the 'password' option
server_userpassword_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
ConnectionRef =
@@ -297,15 +288,13 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun' option
server_pwdfun_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
CHKPWD = fun("foo",Pwd) -> Pwd=="bar";
(_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,CHKPWD}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -335,9 +324,7 @@ server_pwdfun_option(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun/4' option
server_pwdfun_4_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar";
("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state};
@@ -345,7 +332,7 @@ server_pwdfun_4_option(Config) ->
(_,_,_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,PWDFUN}]),
ConnectionRef1 =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -395,9 +382,7 @@ server_pwdfun_4_option(Config) ->
%%--------------------------------------------------------------------
server_pwdfun_4_option_repeat(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
%% Test that the state works
Parent = self(),
@@ -406,7 +391,7 @@ server_pwdfun_4_option_repeat(Config) ->
(_,P,_,S) -> Parent!{P,S}, {false,S+1}
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{auth_methods,"keyboard-interactive"},
{pwdfun,PWDFUN}]),
@@ -490,9 +475,7 @@ user_dir_option(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
@@ -530,9 +513,7 @@ ssh_msg_debug_fun_option_client(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -576,9 +557,7 @@ connectfun_disconnectfun_server(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -607,9 +586,7 @@ connectfun_disconnectfun_client(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -651,9 +628,7 @@ ssh_msg_debug_fun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -686,9 +661,7 @@ disconnectfun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -720,9 +693,7 @@ disconnectfun_option_client(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -763,9 +734,7 @@ unexpectedfun_option_server(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -840,14 +809,9 @@ supported_hash(HashAlg) ->
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDirServer),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- UserDirClient =
- ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
-
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
FPs0 = [case HashAlg of
@@ -873,7 +837,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% Start daemon with the public keys that we got fingerprints from
{Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, UserDirServer},
+ {user_dir, UserDir},
{password, "morot"}]),
Host = ssh_test_lib:ntoa(Host0),
FP_check_fun = fun(PeerName, FP) ->
@@ -896,7 +860,8 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
end},
{user, "foo"},
{password, "morot"},
- {user_dir, UserDirClient},
+ {user_dir, UserDir},
+ {save_accepted_host, false}, % Ensure no 'known_hosts' disturbs
{user_interaction, false}]),
ssh:stop_daemon(Pid).
@@ -987,9 +952,7 @@ ms_passed(T0) ->
%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
@@ -1314,6 +1277,33 @@ try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
end.
%%--------------------------------------------------------------------
+save_accepted_host_option(Config) ->
+ UserDir = proplists:get_value(user_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"vego", "morot"}]}
+ ]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {save_accepted_host, false},
+ {user_dir, UserDir}]),
+ {error,enoent} = file:read_file(KnownHosts),
+
+ {ok,_C2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ {ok,_} = file:read_file(KnownHosts),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
deleted file mode 100644
index 74bbc291b2..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ /dev/null
@@ -1,237 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(ssh_renegotiate_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include("ssh_test_lib.hrl").
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--define(REKEY_DATA_TMO, 65000).
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-
-suite() -> [{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
-
-all() -> [{group,default_algs},
- {group,aes_gcm}
- ].
-
-groups() -> [{default_algs, [], tests()},
- {aes_gcm, [], tests()}
- ].
-
-tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2].
-
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- ?CHECK_CRYPTO(Config).
-
-end_per_suite(_Config) ->
- ssh:stop().
-
-%%--------------------------------------------------------------------
-init_per_group(aes_gcm, Config) ->
- case lists:member({client2server,['[email protected]']},
- ssh_transport:supported_algorithms(cipher)) of
- true ->
- [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']},
- {server2client,['[email protected]']}]}]}
- | Config];
- false ->
- {skip, "aes_gcm not supported"}
- end;
-init_per_group(_, Config) ->
- [{preferred_algorithms, ssh:default_algorithms()} | Config].
-
-
-end_per_group(_, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-init_per_testcase(_TestCase, Config) ->
- ssh:start(),
- Config.
-
-end_per_testcase(_TestCase, _Config) ->
- ssh:stop(),
- ok.
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-
-%%% Idle timeout test
-rekey() -> [{timetrap,{seconds,90}}].
-
-rekey(Config) ->
- {Pid, Host, Port} =
- ssh_test_lib:std_daemon(Config,
- [{rekey_limit, 0}]),
- ConnectionRef =
- ssh_test_lib:std_connect(Config, Host, Port,
- [{rekey_limit, 0}]),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- receive
- after ?REKEY_DATA_TMO ->
- %%By this time rekeying would have been done
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- false = (Kex2 == Kex1),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid)
- end.
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying by data volume
-
-rekey_limit() -> [{timetrap,{seconds,400}}].
-
-rekey_limit(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "rekey.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000},
- {max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- Data = lists:duplicate(159000,1),
- ok = ssh_sftp:write_file(SftpPid, DataFile, Data),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- ok = ssh_sftp:write_file(SftpPid, DataFile, "hi\n"),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- timer:sleep(?REKEY_DATA_TMO),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying with simulataneous send request
-
-renegotiate1(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "renegotiate1.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- RPort = ssh_test_lib:inet_port(),
- {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
-
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
-
- ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
-
- ssh_relay:hold(RelayPid, rx, 20, 1000),
- ssh_connection_handler:renegotiate(ConnectionRef),
- spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
-
- timer:sleep(2000),
-
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- ssh_relay:stop(RelayPid),
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-
-%%% Test rekeying with inflight messages from peer
-
-renegotiate2(Config) ->
- UserDir = proplists:get_value(priv_dir, Config),
- DataFile = filename:join(UserDir, "renegotiate2.data"),
-
- Algs = proplists:get_value(preferred_algorithms, Config),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
- {preferred_algorithms,Algs}]),
-
- RPort = ssh_test_lib:inet_port(),
- {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
-
- ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]),
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
-
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- {ok, Handle} = ssh_sftp:open(SftpPid, DataFile, [write]),
-
- ok = ssh_sftp:write(SftpPid, Handle, "hi\n"),
-
- ssh_relay:hold(RelayPid, rx, 20, infinity),
- spawn(fun() -> ok=ssh_sftp:write(SftpPid, Handle, "another hi\n") end),
- %% need a small pause here to ensure ssh_sftp:write is executed
- ct:sleep(10),
- ssh_connection_handler:renegotiate(ConnectionRef),
- ssh_relay:release(RelayPid, rx),
-
- timer:sleep(2000),
-
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
-
- false = (Kex2 == Kex1),
-
- ssh_relay:stop(RelayPid),
- ssh_sftp:stop_channel(SftpPid),
- ssh:close(ConnectionRef),
- ssh:stop_daemon(Pid).
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa b/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa
deleted file mode 100644
index d306f8b26e..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_dsa
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDfi2flSTZZofwT4yQT0NikX/LGNT7UPeB/XEWe/xovEYCElfaQ
-APFixXvEgXwoojmZ5kiQRKzLM39wBP0jPERLbnZXfOOD0PDnw0haMh7dD7XKVMod
-/EigVgHf/qBdM2M8yz1s/rRF7n1UpLSypziKjkzCm7JoSQ2zbWIPdmBIXwIVAMgP
-kpr7Sq3O7sHdb8D601DRjoExAoGAMOQxDfB2Fd8ouz6G96f/UOzRMI/Kdv8kYYKW
-JIGY+pRYrLPyYzUeJznwZreOJgrczAX+luHnKFWJ2Dnk5CyeXk67Wsr7pJ/4MBMD
-OKeIS0S8qoSBN8+Krp79fgA+yS3IfqbkJLtLu4EBaCX4mKQIX4++k44d4U5lc8pt
-+9hlEI8CgYEAznKxx9kyC6bVo7LUYKaGhofRFt0SYFc5PVmT2VUGRs1R6+6DPD+e
-uEO6IhFct7JFSRbP9p0JD4Uk+3zlZF+XX6b2PsZkeV8f/02xlNGUSmEzCSiNg1AX
-Cy/WusYhul0MncWCHMcOZB5rIvU/aP5EJJtn3xrRaz6u0SThF6AnT34CFQC63czE
-ZU8w8Q+H7z0j+a+70x2iAw==
------END DSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa b/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa
deleted file mode 100644
index 9d7e0dd5fb..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/id_rsa
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQD1OET+3O/Bvj/dtjxDTXmj1oiJt4sIph5kGy0RfjoPrZfaS+CU
-DhakCmS6t2ivxWFgtpKWaoGMZMJqWj6F6ZsumyFl3FPBtujwY/35cgifrI9Ns4Tl
-zR1uuengNBmV+WRQ5cd9F2qS6Z8aDQihzt0r8JUqLcK+VQbrmNzboCCQQwIDAQAB
-AoGAPQEyqPTt8JUT7mRXuaacjFXiweAXhp9NEDpyi9eLOjtFe9lElZCrsUOkq47V
-TGUeRKEm9qSodfTbKPoqc8YaBJGJPhUaTAcha+7QcDdfHBvIsgxvU7ePVnlpXRp3
-CCUEMPhlnx6xBoTYP+fRU0e3+xJIPVyVCqX1jAdUMkzfRoECQQD6ux7B1QJAIWyK
-SGkbDUbBilNmzCFNgIpOP6PA+bwfi5d16diTpra5AX09keQABAo/KaP1PdV8Vg0p
-z4P3A7G3AkEA+l+AKG6m0kQTTBMJDqOdVPYwe+5GxunMaqmhokpEbuGsrZBl5Dvd
-WpcBjR7jmenrhKZRIuA+Fz5HPo/UQJPl1QJBAKxstDkeED8j/S2XoFhPKAJ+6t39
-sUVICVTIZQeXdmzHJXCcUSkw8+WEhakqw/3SyW0oaK2FSWQJFWJUZ+8eJj8CQEh3
-xeduB5kKnS9CvzdeghZqX6QvVosSdtlUmfUYW/BgH5PpHKTP8wTaeld3XldZTpMJ
-dKiMkUw2+XYROVUrubUCQD+Na1LhULlpn4ISEtIEfqpdlUhxDgO15Wg8USmsng+x
-ICliVOSQtwaZjm8kwaFt0W7XnpnDxbRs37vIEbIMWak=
------END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key
deleted file mode 100644
index 51ab6fbd88..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBuwIBAAKBgQCClaHzE2ul0gKSUxah5W0W8UiJLy4hXngKEqpaUq9SSdVdY2LK
-wVfKH1gt5iuaf1FfzOhsIC9G/GLnjYttXZc92cv/Gfe3gR+s0ni2++MX+T++mE/Q
-diltXv/Hp27PybS67SmiFW7I+RWnT2OKlMPtw2oUuKeztCe5UWjaj/y5FQIVAPLA
-l9RpiU30Z87NRAHY3NTRaqtrAoGANMRxw8UfdtNVR0CrQj3AgPaXOGE4d+G4Gp4X
-skvnCHycSVAjtYxebUkzUzt5Q6f/IabuLUdge3gXrc8BetvrcKbp+XZgM0/Vj2CF
-Ymmy3in6kzGZq7Fw1sZaku6AOU8vLa5woBT2vAcHLLT1bLAzj7viL048T6MfjrOP
-ef8nHvACgYBhDWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah
-/XcF3DeRF+eEoz48wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+U
-ykSTXYUbtsfTNRFQGBW2/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0CgIVAN4wtL5W
-Lv62jKcdskxNyz2NQoBx
------END DSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub
deleted file mode 100644
index 4dbb1305b0..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_dsa_key.pub
+++ /dev/null
@@ -1,11 +0,0 @@
----- BEGIN SSH2 PUBLIC KEY ----
-AAAAB3NzaC1kc3MAAACBAIKVofMTa6XSApJTFqHlbRbxSIkvLiFeeAoSqlpSr1JJ1V1j
-YsrBV8ofWC3mK5p/UV/M6GwgL0b8YueNi21dlz3Zy/8Z97eBH6zSeLb74xf5P76YT9B2
-KW1e/8enbs/JtLrtKaIVbsj5FadPY4qUw+3DahS4p7O0J7lRaNqP/LkVAAAAFQDywJfU
-aYlN9GfOzUQB2NzU0WqrawAAAIA0xHHDxR9201VHQKtCPcCA9pc4YTh34bganheyS+cI
-fJxJUCO1jF5tSTNTO3lDp/8hpu4tR2B7eBetzwF62+twpun5dmAzT9WPYIViabLeKfqT
-MZmrsXDWxlqS7oA5Ty8trnCgFPa8BwcstPVssDOPu+IvTjxPox+Os495/yce8AAAAIBh
-DWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah/XcF3DeRF+eEoz48
-wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+UykSTXYUbtsfTNRFQGBW2
-/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0Cg==
----- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key
deleted file mode 100644
index 79968bdd7d..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
-zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
-6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
-AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
-NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
-udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
-WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
-n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
-sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
-+SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
-64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
-m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
-tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
------END RSA PRIVATE KEY-----
-
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub
deleted file mode 100644
index 75d2025c71..0000000000
--- a/lib/ssh/test/ssh_renegotiate_SUITE_data/ssh_host_rsa_key.pub
+++ /dev/null
@@ -1,5 +0,0 @@
----- BEGIN SSH2 PUBLIC KEY ----
-AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
-semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
-RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
----- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 3920a1c592..b145066c36 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -42,7 +42,9 @@ suite() ->
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile,
- killed_acceptor_restarts].
+ killed_acceptor_restarts,
+ shell_channel_tree
+ ].
groups() ->
[].
@@ -199,8 +201,6 @@ killed_acceptor_restarts(Config) ->
Port2 = ssh_test_lib:daemon_port(DaemonPid2),
true = (Port /= Port2),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
-
{ok,[{AccPid,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
{ok,[{AccPid2,ListenAddr,Port2}]} = acceptor_pid(DaemonPid2),
@@ -214,37 +214,143 @@ killed_acceptor_restarts(Config) ->
{user_dir, UserDir}]),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
+
%% Make acceptor restart:
exit(AccPid, kill),
+ ?wait_match(undefined, process_info(AccPid)),
- %% Check it is a new acceptor:
- {ok,[{AccPid1,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
- true = (AccPid /= AccPid1),
- true = (AccPid2 /= AccPid1),
+ %% Check it is a new acceptor and wait if it is not:
+ ?wait_match({ok,[{AccPid1,ListenAddr,Port}]}, AccPid1=/=AccPid,
+ acceptor_pid(DaemonPid),
+ AccPid1,
+ 500, 30),
+
+ true = (AccPid1 =/= AccPid2),
%% Connect second client and check it is alive:
- {ok,C2} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
- {user_interaction, false},
- {user, ?USER},
- {password, ?PASSWD},
- {user_dir, UserDir}]),
+ C2 =
+ case ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+ {user_interaction, false},
+ {user, ?USER},
+ {password, ?PASSWD},
+ {user_dir, UserDir}]) of
+ {ok,_C2} ->
+ _C2;
+ _Other ->
+ ct:log("new connect failed: ~p~n~n~s",[_Other,lists:flatten(ssh_info:string())]),
+ ct:fail("Re-connect failed!", [])
+ end,
+
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Check first client is still alive:
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
ok = ssh:stop_daemon(DaemonPid2),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
ok = ssh:stop_daemon(DaemonPid),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
{error,closed} = ssh:connection_info(C1,[client_version]),
{error,closed} = ssh:connection_info(C2,[client_version]).
+
+%%-------------------------------------------------------------------------
+shell_channel_tree(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ TimeoutShell =
+ fun() ->
+ io:format("TimeoutShell started!~n",[]),
+ timer:sleep(5000),
+ ct:log("~p TIMEOUT!",[self()])
+ end,
+ {Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(_User) ->
+ spawn(TimeoutShell)
+ end
+ }
+ ]),
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon),
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef,ChannelId0),
+
+ ?wait_match([{_, GroupPid,worker,[ssh_channel]}],
+ supervisor:which_children(ChannelSup),
+ [GroupPid]),
+ {links,GroupLinks} = erlang:process_info(GroupPid, links),
+ [ShellPid] = GroupLinks--[ChannelSup],
+ ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
+
+ receive
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
+ receive
+ %%---- wait for the subsystem to terminate
+ {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
+ ct:log("Subsystem terminated",[]),
+ case {chk_empty_con_daemon(Daemon),
+ process_info(GroupPid),
+ process_info(ShellPid)} of
+ {Sups0, undefined, undefined} ->
+ %% SUCCESS
+ ssh:stop_daemon(Daemon);
+ {Sups0, _, undefined} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Group proc lives!");
+ {Sups0, undefined, _} ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Shell proc lives!");
+ _ ->
+ ssh:stop_daemon(Daemon),
+ ct:fail("Sup tree changed!")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end
+ after 10000 ->
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Daemon),
+ ct:fail("CLI Timeout")
+ end.
+
+
+chk_empty_con_daemon(Daemon) ->
+ ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
+ {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+ supervisor:which_children(Daemon),
+ [SubSysSup,AccSup]),
+ ?wait_match([{{server,ssh_connection_sup, _,_},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{server,ssh_channel_sup,_ ,_},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
+ ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
+ supervisor:which_children(AccSup)),
+ ?wait_match([{_, _, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ [ChannelSup, ConnectionSup, SubSysSup, AccSup].
+
%%-------------------------------------------------------------------------
%% Help functions
%%-------------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 2d7bf75847..57ae2dbac2 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -56,7 +56,9 @@ daemon(Host, Port, Options) ->
ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
- {ok,L} = ssh:daemon_info(Pid),
+ R = ssh:daemon_info(Pid),
+ ct:log("~p:~p ssh:daemon_info(~p) ->~n ~p",[?MODULE,?LINE,Pid,R]),
+ {ok,L} = R,
ListenPort = proplists:get_value(port, L),
ListenIP = proplists:get_value(ip, L),
{Pid, ListenIP, ListenPort};
@@ -199,15 +201,17 @@ init_io_server(TestCase) ->
loop_io_server(TestCase, Buff0) ->
receive
- {input, TestCase, Line} ->
+ {input, TestCase, Line} = _INP ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_INP]),
loop_io_server(TestCase, Buff0 ++ [Line]);
- {io_request, From, ReplyAs, Request} ->
+ {io_request, From, ReplyAs, Request} = _REQ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_REQ]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
{'EXIT',_, _} = _Exit ->
-%% ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
+ ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
ok
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index eaf856e6e8..4b6579bd71 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -16,12 +16,12 @@
%%-------------------------------------------------------------------------
%% Help macro
%%-------------------------------------------------------------------------
--define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+-define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
Bind =
(fun() ->
F = fun(N, F1) ->
case FunctionCall of
- Pattern -> Bind;
+ Pattern when Guard -> Bind;
_ when N>0 ->
ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
timer:sleep(Timeout),
@@ -34,6 +34,9 @@
end)()
).
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
+
-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index b20764ce47..9df404d7ed 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -48,19 +48,9 @@ all() ->
end.
groups() ->
- [{erlang_client, [], [erlang_shell_client_openssh_server,
- erlang_client_openssh_server_exec_compressed,
- erlang_client_openssh_server_setenv,
- erlang_client_openssh_server_publickey_dsa,
- erlang_client_openssh_server_publickey_rsa,
- erlang_client_openssh_server_password,
- erlang_client_openssh_server_kexs,
- erlang_client_openssh_server_nonexistent_subsystem,
- erlang_client_openssh_server_renegotiate
+ [{erlang_client, [], [erlang_shell_client_openssh_server
]},
- {erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
- erlang_server_openssh_client_public_key_rsa,
- erlang_server_openssh_client_renegotiate
+ {erlang_server, [], [erlang_server_openssh_client_renegotiate
]}
].
@@ -100,15 +90,6 @@ end_per_group(_, Config) ->
Config.
-init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
- chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
- chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
- chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_rsa, Config) ->
- chk_key(sshd, 'ssh-rsa', ".ssh/id_rsa", Config);
-
init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
case os:type() of
{unix,_} -> ssh:start(), Config;
@@ -122,27 +103,6 @@ end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
-
-chk_key(Pgm, Name, File, Config) ->
- case ssh_test_lib:openssh_supports(Pgm, public_key, Name) of
- false ->
- {skip,lists:concat(["openssh client does not support ",Name])};
- true ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, File),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- init_per_testcase('__default__',Config);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, lists:concat(["no ~/",File])}
- end
- end.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -160,219 +120,6 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
receive_logout(),
receive_normal_exit(Shell).
-%--------------------------------------------------------------------
-erlang_client_openssh_server_exec() ->
- [{doc, "Test api function ssh_connection:exec"}].
-
-erlang_client_openssh_server_exec(Config) when is_list(Config) ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "echo testing", infinity),
- Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data0) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
- = ExitStatus0} ->
- ct:log("0: Collected data ~p", [ExitStatus0]),
- ssh_test_lib:receive_exec_result(Data0,
- ConnectionRef, ChannelId0);
- Other0 ->
- ct:fail(Other0)
- end,
-
- {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId1,
- "echo testing1", infinity),
- Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"testing1\n">>}},
- case ssh_test_lib:receive_exec_result(Data1) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
- = ExitStatus1} ->
- ct:log("0: Collected data ~p", [ExitStatus1]),
- ssh_test_lib:receive_exec_result(Data1,
- ConnectionRef, ChannelId1);
- Other1 ->
- ct:fail(Other1)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_exec_compressed() ->
- [{doc, "Test that compression option works"}].
-
-erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
- CompressAlgs = [zlib, '[email protected]',none],
- case ssh_test_lib:ssh_supports(CompressAlgs, compression) of
- {false,L} ->
- {skip, io_lib:format("~p compression is not supported",[L])};
-
- true ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{compression,CompressAlgs}]}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_kexs() ->
- [{doc, "Test that we can connect with different KEXs."}].
-
-erlang_client_openssh_server_kexs(Config) when is_list(Config) ->
- KexAlgos = try proplists:get_value(kex, proplists:get_value(common_algs,Config))
- catch _:_ -> []
- end,
- comment(KexAlgos),
- case KexAlgos of
- [] -> {skip, "No common kex algorithms"};
- _ ->
- Success =
- lists:foldl(
- fun(Kex, Acc) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{kex,[Kex]}]}]),
-
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- success =
- ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
-
- ExpectedData = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(ExpectedData) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
- Acc;
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(ExpectedData, ConnectionRef, ChannelId),
- Acc;
- Other ->
- ct:log("~p failed: ~p",[Kex,Other]),
- false
- end
- end, true, KexAlgos),
- case Success of
- true ->
- ok;
- false ->
- {fail, "Kex failed for one or more algos"}
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_setenv() ->
- [{doc, "Test api function ssh_connection:setenv"}].
-
-erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- Env = case ssh_connection:setenv(ConnectionRef, ChannelId,
- "ENV_TEST", "testing_setenv",
- infinity) of
- success ->
- <<"tesing_setenv\n">>;
- failure ->
- <<"\n">>
- end,
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo $ENV_TEST", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, Env}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {data,0,1, UnxpectedData}}} ->
- %% Some os may return things as
- %% ENV_TEST: Undefined variable.\n"
- ct:log("UnxpectedData: ~p", [UnxpectedData]),
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
- = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data,
- ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end.
-
-%%--------------------------------------------------------------------
-
-%% setenv not meaningfull on erlang ssh daemon!
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_rsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-rsa').
-
-erlang_client_openssh_server_publickey_dsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-dss').
-
-
-erlang_client_openssh_server_publickey_X(_Config, Alg) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, [Alg]},
- {user_interaction, false},
- {auth_methods, "publickey"},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef).
-
-%%--------------------------------------------------------------------
-erlang_server_openssh_client_public_key_dsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-dss').
-
-erlang_server_openssh_client_public_key_rsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-rsa').
-
-
-erlang_server_openssh_client_public_key_X(Config, Alg) ->
- SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- KnownHosts = filename:join(PrivDir, "known_hosts"),
- {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {preferred_algorithms,[{public_key, [Alg]}]},
- {auth_methods, "publickey"},
- {failfun, fun ssh_test_lib:failfun/2}]),
- ct:sleep(500),
-
- Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
- [" -o UserKnownHostsFile=", KnownHosts,
- " -o StrictHostKeyChecking=no"],
- "1+1."),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT),
- ssh:stop_daemon(Pid).
-
%%--------------------------------------------------------------------
%% Test that the Erlang/OTP server can renegotiate with openSSH
erlang_server_openssh_client_renegotiate(Config) ->
@@ -430,108 +177,6 @@ erlang_server_openssh_client_renegotiate(Config) ->
end.
%%--------------------------------------------------------------------
-erlang_client_openssh_server_renegotiate(_Config) ->
- process_flag(trap_exit, true),
- IO = ssh_test_lib:start_io_server(),
- Ref = make_ref(),
- Parent = self(),
-
- Shell =
- spawn_link(
- fun() ->
- Host = ssh_test_lib:hostname(),
- Options = [{user_interaction, false},
- {silently_accept_hosts,true}],
- group_leader(IO, self()),
- {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options),
- ct:log("Parent = ~p, IO = ~p, Shell = ~p, ConnRef = ~p~n",[Parent, IO, self(), ConnRef]),
- case ssh_connection:session_channel(ConnRef, infinity) of
- {ok,ChannelId} ->
- success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
- {init_args,[ConnRef, ChannelId]},
- {cm, ConnRef}, {channel_id, ChannelId}],
- {ok, State} = ssh_channel:init([Args]),
- Parent ! {ok, Ref, ConnRef},
- ssh_channel:enter_loop(State);
- Error ->
- Parent ! {error, Ref, Error}
- end,
- receive
- nothing -> ok
- end
- end),
-
- receive
- {error, Ref, Error} ->
- ct:fail("Error=~p",[Error]);
- {ok, Ref, ConnectionRef} ->
- IO ! {input, self(), "echo Hej1\n"},
- receive_data("Hej1", ConnectionRef),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- ssh_connection_handler:renegotiate(ConnectionRef),
- IO ! {input, self(), "echo Hej2\n"},
- receive_data("Hej2", ConnectionRef),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- IO ! {input, self(), "exit\n"},
- receive_logout(),
- receive_normal_exit(Shell),
- true = (Kex1 =/= Kex2)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_password() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_password(Config) when is_list(Config) ->
- %% to make sure we don't public-key-auth
- UserDir = proplists:get_value(data_dir, Config),
- {error, Reason0} =
- ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- ct:log("Test of user foo that does not exist. "
- "Error msg: ~p~n", [Reason0]),
-
- User = string:strip(os:cmd("whoami"), right, $\n),
-
- case length(string:tokens(User, " ")) of
- 1 ->
- {error, Reason1} =
- ssh:connect(any, ?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- ct:log("Test of wrong Pasword. "
- "Error msg: ~p~n", [Reason1]);
- _ ->
- ct:log("Whoami failed reason: ~n", [])
- end.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssh_server_nonexistent_subsystem() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) ->
-
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{user_interaction, false},
- silently_accept_hosts]),
-
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-
- failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "foo", infinity).
-
-%%--------------------------------------------------------------------
-%
-%% Not possible to send password with openssh without user interaction
-%%
-%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
receive_data(Data, Conn) ->