diff options
Diffstat (limited to 'lib/ssh/test')
55 files changed, 1984 insertions, 471 deletions
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 96c74c6c8a..9cd98f069f 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -35,9 +35,8 @@ MODULES= \ ssh_algorithms_SUITE \ ssh_options_SUITE \ ssh_renegotiate_SUITE \ - \ ssh_basic_SUITE \ - \ + ssh_benchmark_SUITE \ ssh_connection_SUITE \ ssh_protocol_SUITE \ ssh_sftp_SUITE \ @@ -47,6 +46,8 @@ MODULES= \ ssh_to_openssh_SUITE \ ssh_upgrade_SUITE \ ssh_test_lib \ + ssh_key_cb \ + ssh_key_cb_options \ ssh_trpt_test_lib \ ssh_echo_server \ ssh_peername_sockname_server \ @@ -127,7 +128,7 @@ release_spec: opt release_tests_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) ssh.spec ssh.cover "$(RELSYSDIR)" + $(INSTALL_DATA) ssh.spec ssh_bench.spec ssh.cover "$(RELSYSDIR)" $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/ssh/test/ssh.spec b/lib/ssh/test/ssh.spec index 8de0fe44e4..0076fc275e 100644 --- a/lib/ssh/test/ssh.spec +++ b/lib/ssh/test/ssh.spec @@ -1,7 +1,6 @@ {suites,"../ssh_test",all}. -{skip_cases,"../ssh_test",ssh_ssh_SUITE, - [ssh], - "Current implementation is timingdependent and\nhence will succeed/fail on a whim"}. -{skip_cases,"../ssh_test",ssh_ssh_SUITE, - [ssh_compressed], - "Current implementation is timingdependent hence will succeed/fail on a whim"}. + +{skip_suites, "../ssh_test", [ssh_benchmark_SUITE], + "Benchmarks run separately"}. + + diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index e67fa2469f..f0ac92fef6 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -23,6 +23,7 @@ -module(ssh_algorithms_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("ssh/src/ssh_transport.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -57,7 +58,7 @@ groups() -> ], AlgoTcSet = - [{Alg, [], specific_test_cases(Tag,Alg,SshcAlgos,SshdAlgos)} + [{Alg, [parallel], specific_test_cases(Tag,Alg,SshcAlgos,SshdAlgos)} || {Tag,Algs} <- ErlAlgos ++ DoubleAlgos, Alg <- Algs], @@ -68,15 +69,26 @@ two_way_tags() -> [cipher,mac,compression]. %%-------------------------------------------------------------------- init_per_suite(Config) -> + ct:log("os:getenv(\"HOME\") = ~p~n" + "init:get_argument(home) = ~p", + [os:getenv("HOME"), init:get_argument(home)]), ct:log("~n~n" "OS ssh:~n=======~n~p~n~n~n" "Erl ssh:~n========~n~p~n~n~n" "Installed ssh client:~n=====================~n~p~n~n~n" - "Installed ssh server:~n=====================~n~p~n~n~n", - [os:cmd("ssh -V"), + "Installed ssh server:~n=====================~n~p~n~n~n" + "Misc values:~n============~n" + " -- Default dh group exchange parameters ({min,def,max}): ~p~n" + " -- dh_default_groups: ~p~n" + " -- Max num algorithms: ~p~n" + ,[os:cmd("ssh -V"), ssh:default_algorithms(), ssh_test_lib:default_algorithms(sshc), - ssh_test_lib:default_algorithms(sshd)]), + ssh_test_lib:default_algorithms(sshd), + {?DEFAULT_DH_GROUP_MIN,?DEFAULT_DH_GROUP_NBITS,?DEFAULT_DH_GROUP_MAX}, + public_key:dh_gex_group_sizes(), + ?MAX_NUM_ALGORITHMS + ]), ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]), catch crypto:stop(), case catch crypto:start() of @@ -101,7 +113,8 @@ init_per_group(Group, Config) -> Config; false -> %% An algorithm group - [[{name,Tag}]|_] = ?config(tc_group_path, Config), + Tag = proplists:get_value(name, + hd(?config(tc_group_path, Config))), Alg = Group, PA = case split(Alg) of @@ -162,6 +175,52 @@ simple_exec(Config) -> ssh_test_lib:std_simple_exec(Host, Port, Config). %%-------------------------------------------------------------------- +%% Testing if no group matches +simple_exec_groups_no_match_too_small(Config) -> + try simple_exec_group({400,500,600}, Config) + of + _ -> ct:fail("Exec though no group available") + catch + error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> + ok + end. + +simple_exec_groups_no_match_too_large(Config) -> + try simple_exec_group({9200,9500,9700}, Config) + of + _ -> ct:fail("Exec though no group available") + catch + error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> + ok + end. + +%%-------------------------------------------------------------------- +%% Testing all default groups +simple_exec_groups(Config) -> + Sizes = interpolate( public_key:dh_gex_group_sizes() ), + lists:foreach( + fun(Sz) -> + ct:log("Try size ~p",[Sz]), + ct:comment(Sz), + case simple_exec_group(Sz, Config) of + expected -> ct:log("Size ~p ok",[Sz]); + _ -> ct:log("Size ~p not ok",[Sz]) + end + end, Sizes), + ct:comment("~p",[lists:map(fun({_,I,_}) -> I; + (I) -> I + end,Sizes)]). + + +interpolate([I1,I2|Is]) -> + OneThird = (I2-I1) div 3, + [I1, + {I1, I1 + OneThird, I2}, + {I1, I1 + 2*OneThird, I2} | interpolate([I2|Is])]; +interpolate(Is) -> + Is. + +%%-------------------------------------------------------------------- %% Use the ssh client of the OS to connect sshc_simple_exec(Config) -> PrivDir = ?config(priv_dir, Config), @@ -254,6 +313,16 @@ specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos) -> [sshd_simple_exec]; _ -> [] + end ++ + case {Tag,Alg} of + {kex,_} when Alg == 'diffie-hellman-group-exchange-sha1' ; + Alg == 'diffie-hellman-group-exchange-sha256' -> + [simple_exec_groups, + simple_exec_groups_no_match_too_large, + simple_exec_groups_no_match_too_small + ]; + _ -> + [] end. supports(Tag, Alg, Algos) -> @@ -292,6 +361,16 @@ start_pubkey_daemon(Opts, Config) -> setup_pubkey(Config) -> DataDir = ?config(data_dir, Config), UserDir = ?config(priv_dir, Config), - ssh_test_lib:setup_dsa_known_host(DataDir, UserDir), + ssh_test_lib:setup_dsa(DataDir, UserDir), + ssh_test_lib:setup_rsa(DataDir, UserDir), + ssh_test_lib:setup_ecdsa("256", DataDir, UserDir), Config. + +simple_exec_group(I, Config) when is_integer(I) -> + simple_exec_group({I,I,I}, Config); +simple_exec_group({Min,I,Max}, Config) -> + {Host,Port} = ?config(srvr_addr, Config), + ssh_test_lib:std_simple_exec(Host, Port, Config, + [{dh_gex_limits,{Min,I,Max}}]). + diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa new file mode 100644 index 0000000000..4b1eb12eaa --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49 +AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br +N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa.pub new file mode 100644 index 0000000000..a0147e60fa --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256 b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256 new file mode 100644 index 0000000000..4b1eb12eaa --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49 +AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br +N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256.pub new file mode 100644 index 0000000000..a0147e60fa --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256 new file mode 100644 index 0000000000..2979ea88ed --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMe4MDoit0t8RzSVPwkCBemQ9fhXL+xnTSAWISw8HNCioAoGCCqGSM49 +AwEHoUQDQgAEo2q7U3P6r0W5WGOLtM78UQtofM9UalEhiZeDdiyylsR/RR17Op0s +VPGSADLmzzgcucLEKy17j2S+oz42VUJy5A== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256.pub new file mode 100644 index 0000000000..85dc419345 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKNqu1Nz+q9FuVhji7TO/FELaHzPVGpRIYmXg3YsspbEf0UdezqdLFTxkgAy5s84HLnCxCste49kvqM+NlVCcuQ= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 51431da48e..6c4c215b3d 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -36,9 +36,15 @@ 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, idle_time/1, inet6_option/1, inet_option/1, @@ -52,8 +58,10 @@ send/1, shell/1, shell_no_unicode/1, - shell_unicode_string/1, - ssh_info_print/1 + shell_unicode_string/1, + ssh_info_print/1, + key_callback/1, + key_callback_options/1 ]). %%% Common test callbacks @@ -77,11 +85,18 @@ all() -> 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, host_user_key_differs}, + {group, key_cb}, {group, internal_error}, daemon_already_started, double_close, + daemon_opt_fd, + multi_daemon_opt_fd, packet_size_zero, ssh_info_print ]. @@ -89,8 +104,16 @@ all() -> 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()}, + {host_user_key_differs, [], [exec_key_differs1, + exec_key_differs2, + exec_key_differs3, + exec_key_differs_fail]}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, + {key_cb, [], [key_callback, key_callback_options]}, {internal_error, [], [internal_error]} ]. @@ -117,8 +140,6 @@ end_per_suite(_Config) -> ssh:stop(), crypto:stop(). %%-------------------------------------------------------------------- -init_per_group(hardening_tests, Config) -> - init_per_group(dsa_key, Config); init_per_group(dsa_key, Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -129,6 +150,39 @@ init_per_group(rsa_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:setup_rsa(DataDir, PrivDir), Config; +init_per_group(ecdsa_sha2_nistp256_key, Config) -> + case lists:member('ecdsa-sha2-nistp256', + ssh_transport:default_algorithms(public_key)) of + true -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_ecdsa("256", DataDir, PrivDir), + Config; + false -> + {skip, unsupported_pub_key} + end; +init_per_group(ecdsa_sha2_nistp384_key, Config) -> + case lists:member('ecdsa-sha2-nistp384', + ssh_transport:default_algorithms(public_key)) of + true -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_ecdsa("384", DataDir, PrivDir), + Config; + false -> + {skip, unsupported_pub_key} + end; +init_per_group(ecdsa_sha2_nistp521_key, Config) -> + case lists:member('ecdsa-sha2-nistp521', + ssh_transport:default_algorithms(public_key)) of + true -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_ecdsa("521", DataDir, PrivDir), + Config; + false -> + {skip, unsupported_pub_key} + end; init_per_group(rsa_pass_key, Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -139,6 +193,26 @@ init_per_group(dsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:setup_dsa_pass_pharse(DataDir, PrivDir, "Password"), [{pass_phrase, {dsa_pass_phrase, "Password"}}| Config]; +init_per_group(host_user_key_differs, Config) -> + Data = ?config(data_dir, Config), + Sys = filename:join(?config(priv_dir, Config), system_rsa), + SysUsr = filename:join(Sys, user), + Usr = filename:join(?config(priv_dir, Config), user_ecdsa_256), + file:make_dir(Sys), + file:make_dir(SysUsr), + file:make_dir(Usr), + file:copy(filename:join(Data, "ssh_host_rsa_key"), filename:join(Sys, "ssh_host_rsa_key")), + file:copy(filename:join(Data, "ssh_host_rsa_key.pub"), filename:join(Sys, "ssh_host_rsa_key.pub")), + file:copy(filename:join(Data, "id_ecdsa256"), filename:join(Usr, "id_ecdsa")), + file:copy(filename:join(Data, "id_ecdsa256.pub"), filename:join(Usr, "id_ecdsa.pub")), + ssh_test_lib:setup_ecdsa_auth_keys("256", Usr, SysUsr), + ssh_test_lib:setup_rsa_known_host(Sys, Usr), + Config; +init_per_group(key_cb, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + Config; init_per_group(internal_error, Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -190,8 +264,6 @@ init_per_group(dir_options, Config) -> init_per_group(_, Config) -> Config. -end_per_group(hardening_tests, Config) -> - end_per_group(dsa_key, Config); end_per_group(dsa_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_dsa(PrivDir), @@ -208,6 +280,10 @@ end_per_group(rsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_rsa(PrivDir), Config; +end_per_group(key_cb, Config) -> + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:clean_dsa(PrivDir), + Config; end_per_group(internal_error, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_dsa(PrivDir), @@ -235,6 +311,14 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ; ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", [file:native_name_encoding(),io:getopts()]), wait_for_erlang_first_line([{io,IO}, {shell,Shell}, {sftpd, Sftpd} | Config]); + +init_per_testcase(inet6_option, Config) -> + case ssh_test_lib:has_inet6_address() of + true -> + init_per_testcase('__default__', Config); + false -> + {skip,"No ipv6 interface address"} + end; init_per_testcase(_TestCase, Config) -> ssh:start(), Config. @@ -362,30 +446,36 @@ exec(Config) when is_list(Config) -> %%-------------------------------------------------------------------- %%% Test that compression option works exec_compressed(Config) when is_list(Config) -> - process_flag(trap_exit, true), - SystemDir = filename:join(?config(priv_dir, Config), system), - UserDir = ?config(priv_dir, Config), - - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {preferred_algorithms,[{compression, [zlib]}]}, - {failfun, fun ssh_test_lib:failfun/2}]), + case ssh_test_lib:ssh_supports(zlib, compression) of + false -> + {skip, "zlib compression is not supported"}; + + true -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, + {preferred_algorithms,[{compression, [zlib]}]}, + {failfun, fun ssh_test_lib:failfun/2}]), - ConnectionRef = - ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, - {user_dir, UserDir}, - {user_interaction, false}]), - {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - success = ssh_connection:exec(ConnectionRef, ChannelId, - "1+1.", infinity), - Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"2\n">>}}, - case ssh_test_lib:receive_exec_result(Data) of - expected -> - ok; - Other -> - ct:fail(Other) - end, - ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId), - ssh:stop_daemon(Pid). + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId, + "1+1.", infinity), + Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"2\n">>}}, + case ssh_test_lib:receive_exec_result(Data) of + expected -> + ok; + Other -> + ct:fail(Other) + end, + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId), + ssh:stop_daemon(Pid) + end. %%-------------------------------------------------------------------- %%% Idle timeout test @@ -428,6 +518,82 @@ shell(Config) when is_list(Config) -> ErlShellStart -> ct:log("Erlang shell start: ~p~n", [ErlShellStart]), do_shell(IO, Shell) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end. + +%%-------------------------------------------------------------------- +%%% Test that we could user different types of host pubkey and user pubkey +exec_key_differs1(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp256']). + +exec_key_differs2(Config) -> exec_key_differs(Config, ['ssh-dss','ecdsa-sha2-nistp256']). + +exec_key_differs3(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp384','ecdsa-sha2-nistp256']). + + + +exec_key_differs(Config, UserPKAlgs) -> + case lists:usort(['ssh-rsa'|UserPKAlgs]) + -- ssh_transport:supported_algorithms(public_key) + of + [] -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system_rsa), + SystemUserDir = filename:join(SystemDir, user), + UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256), + + {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, SystemUserDir}, + {preferred_algorithms, + [{public_key,['ssh-rsa']}]}]), + ct:sleep(500), + + IO = ssh_test_lib:start_io_server(), + Shell = ssh_test_lib:start_shell(Port, IO, UserDir, + [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,UserPKAlgs} + ]), + + + receive + {'EXIT', _, _} -> + ct:fail(no_ssh_connection); + ErlShellStart -> + ct:log("Erlang shell start: ~p~n", [ErlShellStart]), + do_shell(IO, Shell) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end; + + UnsupportedPubKeys -> + {skip, io_lib:format("~p unsupported",[UnsupportedPubKeys])} + end. + +%%-------------------------------------------------------------------- +exec_key_differs_fail(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system_rsa), + SystemUserDir = filename:join(SystemDir, user), + UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256), + + {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, SystemUserDir}, + {preferred_algorithms, + [{public_key,['ssh-rsa']}]}]), + ct:sleep(500), + + IO = ssh_test_lib:start_io_server(), + ssh_test_lib:start_shell(Port, IO, UserDir, + [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,['ssh-dss']}]), + receive + {'EXIT', _, _} -> + ok; + ErlShellStart -> + ct:log("Erlang shell start: ~p~n", [ErlShellStart]), + ct:fail(connection_not_rejected) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- @@ -456,11 +622,15 @@ cli(Config) when is_list(Config) -> {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">>) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef,{closed, ChannelId}} -> ok + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- @@ -524,6 +694,56 @@ pass_phrase(Config) when is_list(Config) -> {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), ssh:stop_daemon(Pid). +%%-------------------------------------------------------------------- +%%% Test that we can use key callback +key_callback(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + NoPubKeyDir = filename:join(UserDir, "nopubkey"), + file:make_dir(NoPubKeyDir), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + ConnectOpts = [{silently_accept_hosts, true}, + {user_dir, NoPubKeyDir}, + {user_interaction, false}, + {key_cb, ssh_key_cb}], + + ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts), + + {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ssh:stop_daemon(Pid). + + +%%-------------------------------------------------------------------- +%%% Test that we can use key callback with callback options +key_callback_options(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + NoPubKeyDir = filename:join(UserDir, "nopubkey"), + file:make_dir(NoPubKeyDir), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + {ok, PrivKey} = file:read_file(filename:join(UserDir, "id_dsa")), + + ConnectOpts = [{silently_accept_hosts, true}, + {user_dir, NoPubKeyDir}, + {user_interaction, false}, + {key_cb, {ssh_key_cb_options, [{priv_key, PrivKey}]}}], + + ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts), + + {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ssh:stop_daemon(Pid). + %%-------------------------------------------------------------------- %%% Test that client does not hang if disconnects due to internal error @@ -599,7 +819,7 @@ peername_sockname(Config) when is_list(Config) -> host_equal(HostSockSrv, Host), PortSockSrv = Port after 10000 -> - throw(timeout) + ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. host_equal(H1, H2) -> @@ -633,7 +853,7 @@ close(Config) when is_list(Config) -> {ssh_cm, Client,{closed, ChannelId}} -> ok after 5000 -> - ct:fail(timeout) + ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- @@ -658,6 +878,68 @@ double_close(Config) when is_list(Config) -> ok = ssh:close(CM). %%-------------------------------------------------------------------- +daemon_opt_fd(Config) -> + SystemDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + + {ok,S1} = gen_tcp:listen(0,[]), + {ok,Fd1} = prim_inet:getfd(S1), + + {ok,Pid1} = ssh:daemon(0, [{system_dir, SystemDir}, + {fd,Fd1}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {failfun, fun ssh_test_lib:failfun/2}]), + + {ok,{_Host1,Port1}} = inet:sockname(S1), + {ok, C1} = ssh:connect("localhost", Port1, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}]), + exit(C1, {shutdown, normal}), + ssh:stop_daemon(Pid1), + gen_tcp:close(S1). + + +%%-------------------------------------------------------------------- +multi_daemon_opt_fd(Config) -> + SystemDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + + Test = + fun() -> + {ok,S} = gen_tcp:listen(0,[]), + {ok,Fd} = prim_inet:getfd(S), + + {ok,Pid} = ssh:daemon(0, [{system_dir, SystemDir}, + {fd,Fd}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {failfun, fun ssh_test_lib:failfun/2}]), + + {ok,{_Host,Port}} = inet:sockname(S), + {ok, C} = ssh:connect("localhost", Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}]), + {S,Pid,C} + end, + + Tests = [Test(),Test(),Test(),Test(),Test(),Test()], + + [begin + gen_tcp:close(S), + ssh:stop_daemon(Pid), + exit(C, {shutdown, normal}) + end || {S,Pid,C} <- Tests]. + +%%-------------------------------------------------------------------- packet_size_zero(Config) -> SystemDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -708,22 +990,28 @@ shell_unicode_string(Config) -> %%-------------------------------------------------------------------- %%% Test basic connection with openssh_zlib openssh_zlib_basic_test(Config) -> - SystemDir = filename:join(?config(priv_dir, Config), system), - UserDir = ?config(priv_dir, Config), + case ssh_test_lib:ssh_supports(['[email protected]',none], compression) of + {false,L} -> + {skip, io_lib:format("~p compression is not supported",[L])}; - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {user_dir, UserDir}, - {preferred_algorithms,[{compression, ['[email protected]']}]}, - {failfun, fun ssh_test_lib:failfun/2}]), - ConnectionRef = - ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, - {user_dir, UserDir}, - {user_interaction, false}, - {preferred_algorithms,[{compression, ['[email protected]', - none]}]} - ]), - ok = ssh:close(ConnectionRef), - ssh:stop_daemon(Pid). + true -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {preferred_algorithms,[{compression, ['[email protected]']}]}, + {failfun, fun ssh_test_lib:failfun/2}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}, + {preferred_algorithms,[{compression, ['[email protected]', + none]}]} + ]), + ok = ssh:close(ConnectionRef), + ssh:stop_daemon(Pid) + end. %%-------------------------------------------------------------------- ssh_info_print(Config) -> @@ -825,22 +1113,32 @@ do_shell(IO, Shell) -> receive Echo0 -> ct:log("Echo: ~p ~n", [Echo0]) + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive ?NEWLINE -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive Result0 = <<"2">> -> ct:log("Result: ~p~n", [Result0]) + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive ?NEWLINE -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive ErlPrompt1 -> ct:log("Erlang prompt: ~p~n", [ErlPrompt1]) + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, exit(Shell, kill). %%Does not seem to work in the testserver! diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256 new file mode 100644 index 0000000000..4b1eb12eaa --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49 +AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br +N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub new file mode 100644 index 0000000000..a0147e60fa --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384 new file mode 100644 index 0000000000..4e8aa40959 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCYXb6OSAZyXRfLXOtMo43za197Hdc/T0YKjgQQjwDt6rlRwqTh7v7S +PV2kXwNGdWigBwYFK4EEACKhZANiAARN2khlJUOOIiwsWHEALwDieeZR96qL4pUd +ci7aeGaczdUK5jOA9D9zmBZtSYTfO8Cr7ekVghDlcWAIJ/BXcswgQwSEQ6wyfaTF +8FYfyr4l3u9IirsnyaFzeIgeoNis8Gw= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub new file mode 100644 index 0000000000..41e722e545 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBE3aSGUlQ44iLCxYcQAvAOJ55lH3qovilR1yLtp4ZpzN1QrmM4D0P3OYFm1JhN87wKvt6RWCEOVxYAgn8FdyzCBDBIRDrDJ9pMXwVh/KviXe70iKuyfJoXN4iB6g2KzwbA== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521 new file mode 100644 index 0000000000..7196f46e97 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHbAgEBBEFMadoz4ckEcClfqXa2tiUuYkJdDfwq+/iFQcpt8ESuEd26IY/vm47Q +9UzbPkO4ou8xkNsQ3WvCRQBBWtn5O2kUU6AHBgUrgQQAI6GBiQOBhgAEAde5BRu5 +01/jS0jRk212xsb2DxPrxNpgp6IMCV8TA4Eps+8bSqHB091nLiBcP422HXYfuCd7 +XDjSs8ihcmhp0hCRASLqZR9EzW9W/SOt876May1Huj5X+WSO6RLe7vPn9vmf7kHf +pip6m7M7qp2qGgQ3q2vRwS2K/O6156ohiOlmuuFs +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub new file mode 100644 index 0000000000..8f059120bc --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHXuQUbudNf40tI0ZNtdsbG9g8T68TaYKeiDAlfEwOBKbPvG0qhwdPdZy4gXD+Nth12H7gne1w40rPIoXJoadIQkQEi6mUfRM1vVv0jrfO+jGstR7o+V/lkjukS3u7z5/b5n+5B36YqepuzO6qdqhoEN6tr0cEtivzuteeqIYjpZrrhbA== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256 new file mode 100644 index 0000000000..2979ea88ed --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMe4MDoit0t8RzSVPwkCBemQ9fhXL+xnTSAWISw8HNCioAoGCCqGSM49 +AwEHoUQDQgAEo2q7U3P6r0W5WGOLtM78UQtofM9UalEhiZeDdiyylsR/RR17Op0s +VPGSADLmzzgcucLEKy17j2S+oz42VUJy5A== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub new file mode 100644 index 0000000000..85dc419345 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKNqu1Nz+q9FuVhji7TO/FELaHzPVGpRIYmXg3YsspbEf0UdezqdLFTxkgAy5s84HLnCxCste49kvqM+NlVCcuQ= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384 new file mode 100644 index 0000000000..fb1a862ded --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDArxbDfh3p1okrD9wQw6jJ4d4DdlBPD5GqXE8bIeRJiK41Sh40LgvPw +mkqEDSXK++CgBwYFK4EEACKhZANiAAScl43Ih2lWTDKrSox5ve5uiTXil4smsup3 +CfS1XPjKxgBAmlfBim8izbdrT0BFdQzz2joduNMtpt61wO4rGs6jm0UP7Kim9PC7 +Hneb/99fIYopdMH5NMnk60zGO1uZ2vc= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub new file mode 100644 index 0000000000..428d5fb7d7 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJyXjciHaVZMMqtKjHm97m6JNeKXiyay6ncJ9LVc+MrGAECaV8GKbyLNt2tPQEV1DPPaOh240y2m3rXA7isazqObRQ/sqKb08Lsed5v/318hiil0wfk0yeTrTMY7W5na9w== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521 new file mode 100644 index 0000000000..3e51ec2ecd --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIB8O1BFkl2HQjQLRLonEZ97da/h39DMa9/0/hvPZWAI8gUPEQcHxRx +U7b09p3Zh+EBbMFq8+1ae9ds+ZTxE4WFSvKgBwYFK4EEACOhgYkDgYYABAAlWVjq +Bzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/ +vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5 +ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub new file mode 100644 index 0000000000..017a29f4da --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAlWVjqBzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_bench.spec b/lib/ssh/test/ssh_bench.spec new file mode 100644 index 0000000000..029f0bd074 --- /dev/null +++ b/lib/ssh/test/ssh_bench.spec @@ -0,0 +1 @@ +{suites,"../ssh_test",[ssh_benchmark_SUITE]}. diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl new file mode 100644 index 0000000000..2add99de97 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE.erl @@ -0,0 +1,426 @@ +%%%------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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_benchmark_SUITE). +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-include_lib("ssh/src/ssh.hrl"). +-include_lib("ssh/src/ssh_transport.hrl"). +-include_lib("ssh/src/ssh_connect.hrl"). +-include_lib("ssh/src/ssh_userauth.hrl"). + + +suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. +%%suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [{group, opensshc_erld} +%% {group, erlc_opensshd} + ]. + +groups() -> + [{opensshc_erld, [{repeat, 3}], [openssh_client_shell]}, + {erlc_opensshd, [{repeat, 3}], [erl_shell]} + ]. + + +init_per_suite(Config) -> + catch ssh:stop(), + catch crypto:stop(), + try + ok = crypto:start(), + report_client_algorithms(), + ok = ssh:start(), + {ok,TracerPid} = erlang_trace(), + [{tracer_pid,TracerPid} | Config] + catch + C:E -> + {skip, io_lib:format("Couldn't start ~p:~p",[C,E])} + end. + +end_per_suite(_Config) -> + catch ssh:stop(), + catch crypto:stop(), + ok. + + + +init_per_group(opensshc_erld, Config) -> + case ssh_test_lib:ssh_type() of + openSSH -> + DataDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, UserDir), + ssh_test_lib:setup_rsa(DataDir, UserDir), + ssh_test_lib:setup_ecdsa("256", DataDir, UserDir), + [{c_kexs, ssh_test_lib:sshc(kex)}, + {c_ciphers, ssh_test_lib:sshc(cipher)} + | Config]; + _ -> + {skip, "No OpenSsh client found"} + end; + +init_per_group(erlc_opensshd, _) -> + {skip, "Group erlc_opensshd not implemented"}; + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, _Config) -> + ok. + + +init_per_testcase(_Func, Conf) -> + Conf. + +end_per_testcase(_Func, _Conf) -> + ok. + +%%%================================================================ +openssh_client_shell(Config) -> + CommonAlgs = ssh_test_lib:intersect_bi_dir( + ssh_test_lib:intersection(ssh:default_algorithms(), + ssh_test_lib:default_algorithms(sshc))), + KexVariants = + [ [{kex,[Kex]}] + || Kex <- proplists:get_value(kex, CommonAlgs)], + CipherVariants = + [ [{cipher,[{client2server,[Cipher]}, + {server2client,[Cipher]}]}] + || Cipher <- proplists:get_value(cipher, CommonAlgs)], + + + lists:foreach( + fun(PrefAlgs=[{kex,[Kex]}]) when Kex == 'diffie-hellman-group-exchange-sha256' -> + lists:foreach( + fun(Grp) -> + openssh_client_shell(Config, + [{preferred_algorithms, PrefAlgs}, + {dh_gex_groups, [Grp]} + ]) + end, moduli()); + (PrefAlgs) -> + openssh_client_shell(Config, + [{preferred_algorithms, PrefAlgs}]) + end, KexVariants ++ CipherVariants). + + +openssh_client_shell(Config, Options) -> + SystemDir = ?config(data_dir, Config), + UserDir = ?config(priv_dir, Config), + KnownHosts = filename:join(UserDir, "known_hosts"), + + {ok, TracerPid} = erlang_trace(), + {ServerPid, _Host, Port} = + ssh_test_lib:daemon([{system_dir, SystemDir}, + {public_key_alg, ssh_dsa}, + {failfun, fun ssh_test_lib:failfun/2} | + Options]), + ct:sleep(500), + + Data = lists:duplicate(100000, $a), + Cmd = lists:concat(["ssh -p ",Port, + " -o UserKnownHostsFile=", KnownHosts, + " -o \"StrictHostKeyChecking no\"", + " localhost '\"",Data,"\"'."]), +%% ct:pal("Cmd ="++Cmd), + + Parent = self(), + SlavePid = spawn(fun() -> + Parent ! {self(),os:cmd(Cmd)} + end), + receive + {SlavePid, _ClientResponse} -> +%% ct:pal("ClientResponse = ~p",[_ClientResponse]), + {ok, List} = get_trace_list(TracerPid), + Times = find_times(List), + Algs = find_algs(List), + ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]), + lists:foreach( + fun({Tag,Value,Unit}) -> + EventData = + case Tag of + {A,B} when A==encrypt ; A==decrypt -> + [{value, Value}, + {suite, ?MODULE}, + {name, mk_name(["Cipher ",A," ",B," [",Unit,"]"])} + ]; + kex -> + KexAlgStr = fmt_alg(Algs#alg.kex, List), + [{value, Value}, + {suite, ?MODULE}, + {name, mk_name(["Erl server kex ",KexAlgStr," [",Unit,"]"])} + ]; + _ when is_atom(Tag) -> + [{value, Value}, + {suite, ?MODULE}, + {name, mk_name(["Erl server ",Tag," [",Unit,"]"])} + ] + end, + ct:pal("ct_event:notify ~p",[EventData]), + ct_event:notify(#event{name = benchmark_data, + data = EventData}) + end, Times), + ssh:stop_daemon(ServerPid), + ok + after 10000 -> + ssh:stop_daemon(ServerPid), + exit(SlavePid, kill), + {fail, timeout} + end. + + +%%%================================================================ +fmt_alg(Alg, List) when is_atom(Alg) -> + fmt_alg(atom_to_list(Alg), List); +fmt_alg(Alg = "diffie-hellman-group-exchange-sha" ++ _, List) -> + try + integer_to_list(find_gex_size_string(List)) + of + GexSize -> lists:concat([Alg," ",GexSize]) + catch + _:_ -> Alg + end; +fmt_alg(Alg, List) -> + Alg. + +%%%---------------------------------------------------------------- +mk_name(Name) -> [char(C) || C <- lists:concat(Name)]. + +char($-) -> $_; +char(C) -> C. + +%%%---------------------------------------------------------------- +find_times(L) -> + Xs = [accept_to_hello, kex, kex_to_auth, auth, to_prompt], + [find_time(X,L) || X <- Xs] ++ + crypto_algs_times_sizes([encrypt,decrypt], L). + +-record(call, { + mfa, + pid, + t_call, + t_return, + args, + result + }). + +%%%---------------- +-define(send(M), fun(C=#call{mfa = {ssh_message,encode,1}, + args = [M]}) -> + C#call.t_return + end). + +-define(recv(M), fun(C=#call{mfa = {ssh_message,decode,1}, + result = M}) -> + C#call.t_call + end). + +find_time(accept_to_hello, L) -> + [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) -> + C#call.t_call + end, + fun(C=#call{mfa = {ssh_connection_handler,hello,_}, + args = [socket_control|_]}) -> + C#call.t_return + end + ], L, []), + {accept_to_hello, now2micro_sec(now_diff(T1,T0)), microsec}; +find_time(kex, L) -> + [T0,T1] = find([fun(C=#call{mfa = {ssh_connection_handler,hello,_}, + args = [socket_control|_]}) -> + C#call.t_call + end, + ?send(#ssh_msg_newkeys{}) + ], L, []), + {kex, now2micro_sec(now_diff(T1,T0)), microsec}; +find_time(kex_to_auth, L) -> + [T0,T1] = find([?send(#ssh_msg_newkeys{}), + ?recv(#ssh_msg_userauth_request{}) + ], L, []), + {kex_to_auth, now2micro_sec(now_diff(T1,T0)), microsec}; +find_time(auth, L) -> + [T0,T1] = find([?recv(#ssh_msg_userauth_request{}), + ?send(#ssh_msg_userauth_success{}) + ], L, []), + {auth, now2micro_sec(now_diff(T1,T0)), microsec}; +find_time(to_prompt, L) -> + [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) -> + C#call.t_call + end, + ?recv(#ssh_msg_channel_request{request_type="env"}) + ], L, []), + {to_prompt, now2micro_sec(now_diff(T1,T0)), microsec}. + + +find([F|Fs], [C|Cs], Acc) when is_function(F,1) -> + try + F(C) + of + T -> find(Fs, Cs, [T|Acc]) + catch + _:_ -> find([F|Fs], Cs, Acc) + end; +find([], _, Acc) -> + lists:reverse(Acc). + + +find_algs(L) -> + {value, #call{result={ok,Algs}}} = + lists:keysearch({ssh_transport,select_algorithm,3}, #call.mfa, L), + Algs. + +find_gex_size_string(L) -> + %% server + {value, #call{result={ok,{Size, _}}}} = + lists:keysearch({public_key,dh_gex_group,4}, #call.mfa, L), + Size. + +%%%---------------- +crypto_algs_times_sizes(EncDecs, L) -> + Raw = [{_Algorithm = case EncDec of + encrypt -> {encrypt,S#ssh.encrypt}; + decrypt -> {decrypt,S#ssh.decrypt} + end, + size(Data), + now2micro_sec(now_diff(T1, T0)) + } + || EncDec <- EncDecs, + #call{mfa = {ssh_transport,ED,2}, + args = [S,Data], + t_call = T0, + t_return = T1} <- L, + ED == EncDec + ], + [{Alg, round(1024*Time/Size), "microsec per kbyte"} % Microseconds per 1k bytes. + || {Alg,Size,Time} <- lists:foldl(fun increment/2, [], Raw)]. + +increment({Alg,Sz,T}, [{Alg,SumSz,SumT}|Acc]) -> + [{Alg,SumSz+Sz,SumT+T} | Acc]; +increment(Spec, [X|Acc]) -> + [X | increment(Spec,Acc)]; % Not so many Alg, 2 or 3 +increment({Alg,Sz,T},[]) -> + [{Alg,Sz,T}]. + +%%%---------------------------------------------------------------- +%%% +%%% API for the traceing +%%% +get_trace_list(TracerPid) -> + TracerPid ! {get_trace_list,self()}, + receive + {trace_list,L} -> {ok, pair_events(lists:reverse(L))} + after 5000 -> {error,no_reply} + end. + +erlang_trace() -> + TracerPid = spawn(fun trace_loop/0), + 0 = erlang:trace(new, true, [call,timestamp,{tracer,TracerPid}]), + [init_trace(MFA, tp(MFA)) + || MFA <- [{ssh_acceptor,handle_connection,5}, + {ssh_connection_handler,hello,2}, + {ssh_message,encode,1}, + {ssh_message,decode,1}, + {ssh_transport,select_algorithm,3}, + {ssh_transport,encrypt,2}, + {ssh_transport,decrypt,2}, + {public_key,dh_gex_group,4} % To find dh_gex group size + ]], + {ok, TracerPid}. + +tp({_M,_F,Arity}) -> + [{lists:duplicate(Arity,'_'), [], [{return_trace}]}]. + +%%%---------------------------------------------------------------- +init_trace(MFA = {Module,_,_}, TP) -> + case code:is_loaded(Module) of + false -> code:load_file(Module); + _ -> ok + end, + erlang:trace_pattern(MFA, TP, [local]). + + +trace_loop() -> + trace_loop([]). + +trace_loop(L) -> + receive + {get_trace_list, From} -> + From ! {trace_list, L}, + trace_loop(L); + Ev -> + trace_loop([Ev|L]) + end. + +pair_events(L) -> + pair_events(L, []). + +pair_events([{trace_ts,Pid,call,{M,F,Args},TS0} | L], Acc) -> + Arity = length(Args), + {ReturnValue,TS1} = find_return(Pid, {M,F,Arity}, L), + pair_events(L, [#call{mfa = {M,F,Arity}, + pid = Pid, + t_call = TS0, + t_return = TS1, + args = Args, + result = ReturnValue} | Acc]); +pair_events([_|L], Acc) -> + pair_events(L, Acc); +pair_events([], Acc) -> + lists:reverse(Acc). + + +find_return(Pid, MFA, + [{trace_ts, Pid, return_from, MFA, ReturnValue, TS}|_]) -> + {ReturnValue, TS}; +find_return(Pid, MFA, [_|L]) -> + find_return(Pid, MFA, L); +find_return(_, _, []) -> + {undefined, undefined}. + +%%%---------------------------------------------------------------- +report_client_algorithms() -> + try + ssh_test_lib:extract_algos( ssh_test_lib:default_algorithms(sshc) ) + of + ClientAlgs -> + ct:pal("The client supports:~n~p",[ClientAlgs]) + catch + Cls:Err -> + ct:pal("Testing client about algorithms failed:~n~p ~p",[Cls,Err]) + end. + +%%%---------------------------------------------------------------- + + +now2sec({A,B,C}) -> A*1000000 + B + C/1000000. + +now2micro_sec({A,B,C}) -> (A*1000000 + B)*1000000 + C. + +now_diff({A1,B1,C1}, {A0,B0,C0}) -> {A1-A0, B1-B0, C1-C0}. + +%%%================================================================ +moduli() -> + [{1023, 5, 16#CF973CD39DC7D62F2C45AAC5180491104C76E0FE5D80A10E6C06AE442F1F373167B0FCBC931F3C157B10A5557008FDE20D68051E6A4DB11CEE0B0749F76D7134B937A59DA998C42BC234A5C1A3CFCD70E624D253D7694076F7B1FD7B8D3427849C9377B3555796ACA58C69DFF542EEEC9859D3ADCE5CC88DF6F7817C9D182EB7}, + {2047, 5, 16#F7693FC11FDDEAA493D3BA36F1FFF9264AA9952209203192A88A697BE9D0E306E306A27430BD87AB9EE9DB4BC78C41950C2EB0E5E4C686E8B1BA6D6A2B1FE91EF40C5EA32C51018323E1D305FE637F35ACABDBFC40AD683F779570A76869EB90015A342B2D1F7C81602688081FCAAA8D623090258D9C5C729C8CDDC0C12CA2D561DD987DB79B6AD7A2A509EBC383BF223FD95BC5A2FCC26FB3F3A0DD3FDC1228E338D3290235A596F9465F7BF490974847E616229A9E60B8F4AA161C52F655843CCCAE8821B40C426B535DE087964778652BBD4EC601C0456AE7128B593FCC64402C891227AE6EE88CC839416FBF462B4852999C646BE0BED7D8CF2BE5E381EF}, + {4095, 2, 16#C8842271626E53546E0C712FA265713F2EE073C20A0723C96B6B182B1EAACC96233D4A199BD0E85F264078A513AD2454F284B8DF543D85019D1E70F2FF54BA43EFBC64AF465C170C3E376F5EC328F98E33E1ED8BED84FA097ABE584152B0E9827ED5CC2B1D4F5ECF2DC46F45C59816D02698EA26F319311E2B6973E83C37021CC8B416AEF653896A1764EE0CEE718A45E8B47CB960BD5907D0E843E8A8E7D4698363C3C3FB3ADC512368B72CAF16510C69052EA2AF51BE00BC8CA04DF1F00A00CC2CA4D74254A1E8738460FD244DDB446CB36554B0A24EEF3710E44DBCF39881E7D3F9AE223388084E7A49A3CB12612AE36416C0EB5628DF1477FEE4A5CF77CDC09AA0E2C989C0B7D1310AFA44B81DA79A65226C7EA510057991EABF9388DC5EA9F52FEA5D3B0872843F50878740794E523E9DC60E0EA1FC8746A7B2AA31FCA89AAA2FA907BED116C69D98F912DD5089BECF28577064225DE96FC214ED1794E7CCE8024F94036D915A123A464C951DA96A5ED7F286F205BEE71BDE2D133FD1891B31178FF25D31611A5B7839F0E68EAF0F8901A571E6917C580F31842A9F19C47E0638483B7947DDCD7864660AC2F8B2C430F1E7FC0F22FA51F96F0499332C5AD3FF9DC7F4332DD5BCCA820CC779B90C0F4C5F0CA52E96FAA187361753FBADC5C80D0492CD80A3EEA5D578772DA9FC1C0E10A0203098AF36D0ED2156BA7321EB}, + {6143, 5, 16#FD9E6B52785CD7BE64D396A599DA4B97CD0BB49183F932A97694D80CA553354DBC26E77B8A0EC002257AADDF6AD27819CE64A06416E4A80B6EA92F28EA8D5B96C774109EEE5816B4B18F84368D1B41864C11AA73D6881675D779B174F6B4E344303F3EFD11BD7DE468467242372FD00908F296F5A2B20E2684F9122D08A46D647B05E298F0BCDAB60468349CCA6DA1B9FEBBC69D256FB9A3F1980F68466364FCEF1C98C1405191A6737A3627BA7F7313A8A18FC0B8521BF3430B1C6805CB44BCEB39904DD30130D24B225B598ED83C5FD757B80189FD9D5C2F9596687C40BAB1C6ED6244944629849D074A4C33FB15DDB3F9760FC59C44BEBB0EC032177147F61789769DAAAE2123CE488F7ECF19BDA051925BA9ED11EAA72DF70C9ECC8F714B4C35728E6679E66A1B56CCAE0FBBD3F9EBF950D4D623ED78E77CC3AD604E91F304EA78CE876F036214BD6F1977BD04C9ADD707D7A3BCCE87AD5D5A11C95E7025B0EA9C649DCB37942A3970A4FB04C284E4DDB4DC90163353B98B1C254FFD28443353F17A87C02E0BDB9F05424CC44C86309F1D73706F039CDAAC3EDC1A64F38FB42707D351DB5360C2680ADC1CC8D1C4AD312ACC904382C26BE33DA0E61429A5940820356ED28586BEB629ED1521D12D25B4DA01926295F3DA504DC9F431B719AC63277BE675E6F6DD4F7499CA11A23744577D653941963E8DAB610F7F226DB52CE5C683F72AEED2B6CE35ED07C29410397A6F7F606477CCC0EDE18CD0D96A7863BC4606193A8799B5AC1EEE6AC5EE36AC3077EC8DAB30EE94434B45B78BC13D96F74D6C4056EAA528CD3C68D308344808819B12F2BFB95A5C1A7DEEE188BF139216DDB7D757D7A50D3C46CE18881D776D617DCFFAA62276045373AA4D9446D7570338F99C0CA8A08851B4F9D388B4C275D3F9B7BA25F235D4329F63F7457C2EB5C68CE2A96D19766F0ED8E19F66DF3C5E29A38795B2F92291BB6EAB6F70A7E89DC9691F28486E9CF87FF11D5DF2E6B030A30B5D476AD59A34EE7262712ED96CEF4A5CAC3F08B3563D44683F746DA094C9CDB34427AF8D8CC2AE1B23C3BEB637}, + {8191, 2, 16#DC61EF13E4F3FC10CC946EEABC33F83EFCB35E0F47E4EC25C1CCBB2C7B502B2EFB0691AA231C8476DD51BA73204E6EA10B1A970FE2CF14AF01E72E1AEA87519A91D00D1499189F94A6CDA9E29C05F11F17FE74A4919A710A2787E180744465DF81C62AA65662FDA46FA6175E8A31E5B29E66DED6701C8FC4217E91D733FE94380F046680967D4CEA7BAC8F3916CDF96AA2C474FAD9650F48403FD0B5B756D34667D36A07767FA33027AE55484D0F701C3CA16632F413A14E4B8645AFAF15B78978C19A7661EDC569BEC72394B1204B166A48FCD5F56BE29840C7794CA6D3440356F15858CDCA9B429C7EA92E17242893FDC8C9C63841A382C32F20CFAB121B4BCAFD7BF9EF07FBF7CDFFECA0CEF3A49C3E2B24FA836F3318435255655E1B281071F62D5E4CD63361299B7828F72936E3FEA9E8044562A6F6ADD5321187C3101E4669C6271598FE1A866C93FE2870A4CEB9254BA32A4719E439317EA42200A335B5CFFA7946A7D0F1BD1A69AA11288B73C71C80B77FE3707CB077DDDEA5CA36A449FAB230C9625A0B12F8275D3FF82F5DA380E7A3F11B6F155FE7E91AC960BD95D9B13F7423AB9B15CC3C4DC34EF296033F009468EA16A721AD659F56C18516025050749ABF05E6D3EBD9778142A530979291F46DAA399A86B7BCDF09CC3E6EEF101419762A306DB45AEFC96C64E83F28338D55905F6A387E0F515E580C3A9B35330E21C32198CDEE3AFB355967A098F635FCA7C49CB4E1E82464B2B390EF1F259E40B9A06235C0273F76284FE6BD534EF3AF7CB01A4A5252B8B94CADC2850B2E56D53F9A31D7C029DF967D0A30C05BC64E119BED6076818FABC8CDD93F3255693E14EFC1A740A5D63A5E847FFE87BAB1DDE0506E1762EA61EFA9F9756151ECCCADD91B98A961A901A2D8B01ABDDD29EC804E8C8D28214BBA26048F924CA66316696E51A49D02FF034D20E44914B1115339CAD3819E0CB1640F0084886FEDDE5E28C29DC48ED30A8C3D789734338F5A9DF42584326E536FD1CF30BC85B8DCBD6120D127C98FE4B3614074F13C2CA4854E6D794156C185C40EB3DA7619CE96ADAF0941BD5499848B034C2B11DFECC0BDFA81C594241F759EF53FC7CDE7F2DE4F23CF81A5A0B7D62E31DABB9198D40307F7824DD130B7D1B80E9B6D322FEEDB5ACE34944F0BFB7D016762A9B2E173BFDD69303766AFBAB45FAB75D05430B4A3515858C4B7F04E23414E4AD03842CB0A20D8FF4B59B7C852BA9A5BE982A8ADA5CB70C36CE2A4D2C31A7015C9F3275E43D192C1B2924424088907A057DA7F2D32A2149922AB2E33F2147D637A3508911CB3FEA5E1AAB4525BACF27B6DD7A3E0AFA978FC3A39DE8882FB22688C3CCC92B6E69ACB0BBF575AB3368E51A2F6A20C414C6F146727CC0045F29061E695D29F7C030CE6929EB3AD11A5CBD0CDEE37347869A3}]. diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa b/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa new file mode 100644 index 0000000000..d306f8b26e --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa @@ -0,0 +1,13 @@ +-----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_benchmark_SUITE_data/id_ecdsa256 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256 new file mode 100644 index 0000000000..4b1eb12eaa --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49 +AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br +N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub new file mode 100644 index 0000000000..a0147e60fa --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384 new file mode 100644 index 0000000000..4e8aa40959 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCYXb6OSAZyXRfLXOtMo43za197Hdc/T0YKjgQQjwDt6rlRwqTh7v7S +PV2kXwNGdWigBwYFK4EEACKhZANiAARN2khlJUOOIiwsWHEALwDieeZR96qL4pUd +ci7aeGaczdUK5jOA9D9zmBZtSYTfO8Cr7ekVghDlcWAIJ/BXcswgQwSEQ6wyfaTF +8FYfyr4l3u9IirsnyaFzeIgeoNis8Gw= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub new file mode 100644 index 0000000000..41e722e545 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBE3aSGUlQ44iLCxYcQAvAOJ55lH3qovilR1yLtp4ZpzN1QrmM4D0P3OYFm1JhN87wKvt6RWCEOVxYAgn8FdyzCBDBIRDrDJ9pMXwVh/KviXe70iKuyfJoXN4iB6g2KzwbA== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521 new file mode 100644 index 0000000000..7196f46e97 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHbAgEBBEFMadoz4ckEcClfqXa2tiUuYkJdDfwq+/iFQcpt8ESuEd26IY/vm47Q +9UzbPkO4ou8xkNsQ3WvCRQBBWtn5O2kUU6AHBgUrgQQAI6GBiQOBhgAEAde5BRu5 +01/jS0jRk212xsb2DxPrxNpgp6IMCV8TA4Eps+8bSqHB091nLiBcP422HXYfuCd7 +XDjSs8ihcmhp0hCRASLqZR9EzW9W/SOt876May1Huj5X+WSO6RLe7vPn9vmf7kHf +pip6m7M7qp2qGgQ3q2vRwS2K/O6156ohiOlmuuFs +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub new file mode 100644 index 0000000000..8f059120bc --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHXuQUbudNf40tI0ZNtdsbG9g8T68TaYKeiDAlfEwOBKbPvG0qhwdPdZy4gXD+Nth12H7gne1w40rPIoXJoadIQkQEi6mUfRM1vVv0jrfO+jGstR7o+V/lkjukS3u7z5/b5n+5B36YqepuzO6qdqhoEN6tr0cEtivzuteeqIYjpZrrhbA== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa b/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa new file mode 100644 index 0000000000..9d7e0dd5fb --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa @@ -0,0 +1,15 @@ +-----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_benchmark_SUITE_data/ssh_host_dsa_key b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key new file mode 100644 index 0000000000..51ab6fbd88 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key @@ -0,0 +1,13 @@ +-----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_benchmark_SUITE_data/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub new file mode 100644 index 0000000000..4dbb1305b0 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub @@ -0,0 +1,11 @@ +---- 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_benchmark_SUITE_data/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256 new file mode 100644 index 0000000000..2979ea88ed --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMe4MDoit0t8RzSVPwkCBemQ9fhXL+xnTSAWISw8HNCioAoGCCqGSM49 +AwEHoUQDQgAEo2q7U3P6r0W5WGOLtM78UQtofM9UalEhiZeDdiyylsR/RR17Op0s +VPGSADLmzzgcucLEKy17j2S+oz42VUJy5A== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub new file mode 100644 index 0000000000..85dc419345 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKNqu1Nz+q9FuVhji7TO/FELaHzPVGpRIYmXg3YsspbEf0UdezqdLFTxkgAy5s84HLnCxCste49kvqM+NlVCcuQ= uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384 new file mode 100644 index 0000000000..fb1a862ded --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDArxbDfh3p1okrD9wQw6jJ4d4DdlBPD5GqXE8bIeRJiK41Sh40LgvPw +mkqEDSXK++CgBwYFK4EEACKhZANiAAScl43Ih2lWTDKrSox5ve5uiTXil4smsup3 +CfS1XPjKxgBAmlfBim8izbdrT0BFdQzz2joduNMtpt61wO4rGs6jm0UP7Kim9PC7 +Hneb/99fIYopdMH5NMnk60zGO1uZ2vc= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub new file mode 100644 index 0000000000..428d5fb7d7 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJyXjciHaVZMMqtKjHm97m6JNeKXiyay6ncJ9LVc+MrGAECaV8GKbyLNt2tPQEV1DPPaOh240y2m3rXA7isazqObRQ/sqKb08Lsed5v/318hiil0wfk0yeTrTMY7W5na9w== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521 new file mode 100644 index 0000000000..3e51ec2ecd --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIB8O1BFkl2HQjQLRLonEZ97da/h39DMa9/0/hvPZWAI8gUPEQcHxRx +U7b09p3Zh+EBbMFq8+1ae9ds+ZTxE4WFSvKgBwYFK4EEACOhgYkDgYYABAAlWVjq +Bzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/ +vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5 +ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub new file mode 100644 index 0000000000..017a29f4da --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAlWVjqBzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key new file mode 100644 index 0000000000..79968bdd7d --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key @@ -0,0 +1,16 @@ +-----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_benchmark_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub new file mode 100644 index 0000000000..75d2025c71 --- /dev/null +++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub @@ -0,0 +1,5 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8 +semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW +RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q== +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index fbcf06290a..1b93cc9c32 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -48,7 +48,8 @@ all() -> gracefull_invalid_long_start, gracefull_invalid_long_start_no_nl, stop_listener, - start_subsystem_on_closed_channel + start_subsystem_on_closed_channel, + max_channels_option ]. groups() -> [{openssh, [], payload() ++ ptty()}]. @@ -119,20 +120,28 @@ simple_exec(Config) when is_list(Config) -> receive {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, %% receive close messages receive {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- @@ -154,20 +163,28 @@ small_cat(Config) when is_list(Config) -> receive {ssh_cm, ConnectionRef, {data, ChannelId0, 0, Data}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, %% receive close messages receive {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- big_cat() -> @@ -211,11 +228,15 @@ big_cat(Config) when is_list(Config) -> %% receive close messages (eof already consumed) receive {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> - ok + ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- @@ -234,14 +255,20 @@ send_after_exit(Config) when is_list(Config) -> receive {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _ExitStatus}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, receive {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> ok + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of {error, closed} -> ok; @@ -455,6 +482,8 @@ gracefull_invalid_version(Config) when is_list(Config) -> {tcp_closed, S} -> ok end + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. gracefull_invalid_start(Config) when is_list(Config) -> @@ -475,6 +504,8 @@ gracefull_invalid_start(Config) when is_list(Config) -> {tcp_closed, S} -> ok end + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. gracefull_invalid_long_start(Config) when is_list(Config) -> @@ -495,6 +526,8 @@ gracefull_invalid_long_start(Config) when is_list(Config) -> {tcp_closed, S} -> ok end + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. @@ -516,6 +549,8 @@ gracefull_invalid_long_start_no_nl(Config) when is_list(Config) -> {tcp_closed, S} -> ok end + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. stop_listener() -> @@ -606,6 +641,88 @@ start_subsystem_on_closed_channel(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +max_channels_option() -> + [{doc, "Test max_channels option"}]. + +max_channels_option(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {max_channels, 3}, + {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]} + ]), + + 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), + {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity), + {ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity), + {ok, ChannelId3} = ssh_connection:session_channel(ConnectionRef, infinity), + {ok, ChannelId4} = ssh_connection:session_channel(ConnectionRef, infinity), + {ok, ChannelId5} = ssh_connection:session_channel(ConnectionRef, infinity), + {ok, _ChannelId6} = ssh_connection:session_channel(ConnectionRef, infinity), + + %%%---- shell + ok = ssh_connection:shell(ConnectionRef,ChannelId0), + receive + {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"Eshell",_/binary>>}} -> + ok + after 5000 -> + ct:fail("CLI Timeout") + end, + + %%%---- subsystem "echo_n" + success = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", infinity), + + %%%---- exec #1 + success = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", infinity), + receive + {ssh_cm, ConnectionRef, {data, ChannelId2, 0, <<"testing1",_/binary>>}} -> + ok + after 5000 -> + ct:fail("Exec #1 Timeout") + end, + + %%%---- ptty + success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId3, []), + + %%%---- exec #2 + failure = ssh_connection:exec(ConnectionRef, ChannelId4, "testing2.\n", infinity), + + %%%---- close the shell + ok = ssh_connection:send(ConnectionRef, ChannelId0, "exit().\n", 5000), + + %%%---- wait for the subsystem to terminate + receive + {ssh_cm,ConnectionRef,{closed,ChannelId0}} -> ok + after 5000 -> + ct:log("Timeout waiting for '{ssh_cm,~p,{closed,~p}}'~n" + "Message queue:~n~p", + [ConnectionRef,ChannelId0,erlang:process_info(self(),messages)]), + ct:fail("exit Timeout",[]) + end, + + %%%---- exec #3 + success = ssh_connection:exec(ConnectionRef, ChannelId5, "testing3.\n", infinity), + receive + {ssh_cm, ConnectionRef, {data, ChannelId5, 0, <<"testing3",_/binary>>}} -> + ok + after 5000 -> + ct:fail("Exec #3 Timeout") + end, + + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- big_cat_rx(ConnectionRef, ChannelId) -> diff --git a/lib/ssh/test/ssh_key_cb.erl b/lib/ssh/test/ssh_key_cb.erl new file mode 100644 index 0000000000..388ec2ecc1 --- /dev/null +++ b/lib/ssh/test/ssh_key_cb.erl @@ -0,0 +1,45 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% + +%% +%%---------------------------------------------------------------------- + +%% Note: This module is used by ssh_basic_SUITE + +-module(ssh_key_cb). +-behaviour(ssh_client_key_api). +-compile(export_all). + +add_host_key(_, _, _) -> + ok. + +is_host_key(_, _, _, _) -> + true. + +user_key('ssh-dss', Opts) -> + UserDir = proplists:get_value(user_dir, Opts), + KeyFile = filename:join(filename:dirname(UserDir), "id_dsa"), + {ok, KeyBin} = file:read_file(KeyFile), + [Entry] = public_key:pem_decode(KeyBin), + Key = public_key:pem_entry_decode(Entry), + {ok, Key}; + +user_key(_Alg, _Opt) -> + {error, "Not Supported"}. diff --git a/lib/ssh/test/ssh_key_cb_options.erl b/lib/ssh/test/ssh_key_cb_options.erl new file mode 100644 index 0000000000..afccb34f0f --- /dev/null +++ b/lib/ssh/test/ssh_key_cb_options.erl @@ -0,0 +1,44 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% + +%% +%%---------------------------------------------------------------------- + +%% Note: This module is used by ssh_basic_SUITE + +-module(ssh_key_cb_options). +-behaviour(ssh_client_key_api). +-compile(export_all). + +add_host_key(_, _, _) -> + ok. + +is_host_key(_, _, _, _) -> + true. + +user_key('ssh-dss', Opts) -> + KeyCbOpts = proplists:get_value(key_cb_private, Opts), + KeyBin = proplists:get_value(priv_key, KeyCbOpts), + [Entry] = public_key:pem_decode(KeyBin), + Key = public_key:pem_entry_decode(Entry), + {ok, Key}; + +user_key(_Alg, _Opt) -> + {error, "Not Supported"}. diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index d64c78da35..6a201d401f 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -45,6 +45,9 @@ max_sessions_ssh_connect_sequential/1, server_password_option/1, server_userpassword_option/1, + server_pwdfun_option/1, + server_pwdfun_4_option/1, + server_pwdfun_4_option_repeat/1, ssh_connect_arg4_timeout/1, ssh_connect_negtimeout_parallel/1, ssh_connect_negtimeout_sequential/1, @@ -83,6 +86,9 @@ all() -> connectfun_disconnectfun_client, server_password_option, server_userpassword_option, + server_pwdfun_option, + server_pwdfun_4_option, + server_pwdfun_4_option_repeat, {group, dir_options}, ssh_connect_timeout, ssh_connect_arg4_timeout, @@ -188,7 +194,9 @@ init_per_testcase(_TestCase, Config) -> Config. end_per_testcase(TestCase, Config) when TestCase == server_password_option; - TestCase == server_userpassword_option -> + TestCase == server_userpassword_option; + TestCase == server_pwdfun_option; + TestCase == server_pwdfun_4_option -> UserDir = filename:join(?config(priv_dir, Config), nopubkey), ssh_test_lib:del_dirs(UserDir), end_per_testcase(Config); @@ -272,6 +280,157 @@ server_userpassword_option(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +%%% validate to server that uses the 'pwdfun' option +server_pwdfun_option(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + CHKPWD = fun("foo",Pwd) -> Pwd=="bar"; + (_,_) -> false + end, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, PrivDir}, + {pwdfun,CHKPWD}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "bar"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:close(ConnectionRef), + + Reason = "Unable to connect using the available authentication methods", + + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:stop_daemon(Pid). + + +%%-------------------------------------------------------------------- +%%% validate to server that uses the 'pwdfun/4' option +server_pwdfun_4_option(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar"; + ("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state}; + ("bandit",_,_,_) -> disconnect; + (_,_,_,_) -> false + end, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, PrivDir}, + {pwdfun,PWDFUN}]), + ConnectionRef1 = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "bar"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:close(ConnectionRef1), + + ConnectionRef2 = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "fie"}, + {password, "bar"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:close(ConnectionRef2), + + Reason = "Unable to connect using the available authentication methods", + + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "fie"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), + + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "bandit"}, + {password, "pwd breaking"}, + {user_interaction, false}, + {user_dir, UserDir}]), + ssh:stop_daemon(Pid). + + +%%-------------------------------------------------------------------- +server_pwdfun_4_option_repeat(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + %% Test that the state works + Parent = self(), + PWDFUN = fun("foo",P="bar",_,S) -> Parent!{P,S},true; + (_,P,_,S=undefined) -> Parent!{P,S},{false,1}; + (_,P,_,S) -> Parent!{P,S}, {false,S+1} + end, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, PrivDir}, + {auth_methods,"keyboard-interactive"}, + {pwdfun,PWDFUN}]), + + %% Try with passwords "incorrect", "Bad again" and finally "bar" + KIFFUN = fun(_,_,_) -> + K={k,self()}, + case get(K) of + undefined -> + put(K,1), + ["incorrect"]; + 2 -> + put(K,3), + ["bar"]; + S-> + put(K,S+1), + ["Bad again"] + end + end, + + ConnectionRef2 = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {keyboard_interact_fun, KIFFUN}, + {user_dir, UserDir}]), + ssh:close(ConnectionRef2), + ssh:stop_daemon(Pid), + + lists:foreach(fun(Expect) -> + receive + Expect -> ok; + Other -> ct:fail("Expect: ~p~nReceived ~p",[Expect,Other]) + after + 2000 -> ct:fail("Timeout expecting ~p",[Expect]) + end + end, [{"incorrect",undefined}, + {"Bad again",1}, + {"bar",2}]). + +%%-------------------------------------------------------------------- system_dir_option(Config) -> DirUnread = proplists:get_value(unreadable_dir,Config), FileRead = proplists:get_value(readable_file,Config), @@ -656,6 +815,8 @@ ssh_connect_arg4_timeout(_Config) -> %% Get listening port Port = receive {port,Server,ServerPort} -> ServerPort + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, %% try to connect with a timeout, but "supervise" it @@ -861,6 +1022,8 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) -> ct:sleep(round(Factor * NegTimeOut)), one_shell_op(IO, NegTimeOut) + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, exit(Shell, kill). @@ -869,13 +1032,13 @@ one_shell_op(IO, TimeOut) -> ct:log("One shell op: Waiting for prompter"), receive ErlPrompt0 -> ct:log("Erlang prompt: ~p~n", [ErlPrompt0]) - after TimeOut -> ct:fail("Timeout waiting for promter") + after TimeOut -> ct:fail("Timeout waiting for promter") end, IO ! {input, self(), "2*3*7.\r\n"}, receive Echo0 -> ct:log("Echo: ~p ~n", [Echo0]) - after TimeOut -> ct:fail("Timeout waiting for echo") + after TimeOut -> ct:fail("Timeout waiting for echo") end, receive @@ -888,7 +1051,7 @@ one_shell_op(IO, TimeOut) -> receive Result0 -> ct:log("Result: ~p~n", [Result0]) - after TimeOut -> ct:fail("Timeout waiting for result") + after TimeOut -> ct:fail("Timeout waiting for result") end. %%-------------------------------------------------------------------- @@ -1016,9 +1179,13 @@ fake_daemon(_Config) -> {ok,S} = Rsa, receive {tcp, S, Id} -> Parent ! {id,self(),Id} + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end end), %% Get listening host and port receive {sockname,Server,ServerHost,ServerPort} -> {Server, ServerHost, ServerPort} + after + 10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index d8e99799e2..4c088acabf 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -46,7 +46,10 @@ suite() -> all() -> [{group,tool_tests}, - {group,kex} + {group,kex}, + {group,service_requests}, + {group,packet_size_error}, + {group,field_size_error} ]. groups() -> @@ -55,13 +58,27 @@ groups() -> lib_match, lib_no_match ]}, + {packet_size_error, [], [packet_length_too_large, + packet_length_too_short]}, + + {field_size_error, [], [service_name_length_too_large, + service_name_length_too_short]}, + {kex, [], [no_common_alg_server_disconnects, no_common_alg_client_disconnects, - gex_client_init_default_noexact, - gex_client_init_default_exact, gex_client_init_option_groups, - gex_client_init_option_groups_file - ]} + gex_server_gex_limit, + gex_client_init_option_groups_moduli_file, + gex_client_init_option_groups_file, + gex_client_old_request_exact, + gex_client_old_request_noexact + ]}, + {service_requests, [], [bad_service_name, + bad_long_service_name, + bad_very_long_service_name, + empty_service_name, + bad_service_name_then_correct + ]} ]. @@ -76,10 +93,12 @@ end_per_suite(Config) -> init_per_testcase(no_common_alg_server_disconnects, Config) -> start_std_daemon(Config, [{preferred_algorithms,[{public_key,['ssh-rsa']}]}]); -init_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ; - TC == gex_client_init_default_exact ; - TC == gex_client_init_option_groups ; - TC == gex_client_init_option_groups_file -> +init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; + TC == gex_client_init_option_groups_moduli_file ; + TC == gex_client_init_option_groups_file ; + TC == gex_server_gex_limit ; + TC == gex_client_old_request_exact ; + TC == gex_client_old_request_noexact -> Opts = case TC of gex_client_init_option_groups -> [{dh_gex_groups, [{2345, 3, 41}]}]; @@ -87,21 +106,35 @@ init_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ; DataDir = ?config(data_dir, Config), F = filename:join(DataDir, "dh_group_test"), [{dh_gex_groups, {file,F}}]; + gex_client_init_option_groups_moduli_file -> + DataDir = ?config(data_dir, Config), + F = filename:join(DataDir, "dh_group_test.moduli"), + [{dh_gex_groups, {ssh_moduli_file,F}}]; + _ when TC == gex_server_gex_limit ; + TC == gex_client_old_request_exact ; + TC == gex_client_old_request_noexact -> + [{dh_gex_groups, [{ 500, 3, 17}, + {1000, 7, 91}, + {3000, 5, 61}]}, + {dh_gex_limits,{500,1500}} + ]; _ -> [] end, start_std_daemon(Config, - [{preferred_algorithms, ssh_transport:supported_algorithms()} + [{preferred_algorithms, ssh:default_algorithms()} | Opts]); init_per_testcase(_TestCase, Config) -> check_std_daemon_works(Config, ?LINE). end_per_testcase(no_common_alg_server_disconnects, Config) -> stop_std_daemon(Config); -end_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ; - TC == gex_client_init_default_exact ; - TC == gex_client_init_option_groups ; - TC == gex_client_init_option_groups_file -> +end_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; + TC == gex_client_init_option_groups_moduli_file ; + TC == gex_client_init_option_groups_file ; + TC == gex_server_gex_limit ; + TC == gex_client_old_request_exact ; + TC == gex_client_old_request_noexact -> stop_std_daemon(Config); end_per_testcase(_TestCase, Config) -> check_std_daemon_works(Config, ?LINE). @@ -114,25 +147,10 @@ end_per_testcase(_TestCase, Config) -> %%% Connect to an erlang server and check that the testlib acts as a client. lib_works_as_client(Config) -> %% Connect and negotiate keys - {ok,InitialState} = - ssh_trpt_test_lib:exec( - [{set_options, [print_ops, print_seqnums, print_messages]}, - {connect, - server_host(Config),server_port(Config), - [{preferred_algorithms,[{kex,['diffie-hellman-group1-sha1']}]}, - {silently_accept_hosts, true}, - {user_dir, user_dir(Config)}, - {user_interaction, false}]}, - receive_hello, - {send, hello}, - {send, ssh_msg_kexinit}, - {match, #ssh_msg_kexinit{_='_'}, receive_msg}, - {send, ssh_msg_kexdh_init}, - {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg}, - {send, #ssh_msg_newkeys{}}, - {match, #ssh_msg_newkeys{_='_'}, receive_msg} - ] - ), + {ok,InitialState} = ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}] + ), + {ok,AfterKexState} = connect_and_kex(Config, InitialState), %% Do the authentcation {User,Pwd} = server_user_password(Config), @@ -147,7 +165,7 @@ lib_works_as_client(Config) -> ?STRING(unicode:characters_to_binary(Pwd))>> }}, {match, #ssh_msg_userauth_success{_='_'}, receive_msg} - ], InitialState), + ], AfterKexState), %% Disconnect {ok,_} = @@ -259,10 +277,7 @@ no_common_alg_server_disconnects(Config) -> {send, hello}, {match, #ssh_msg_kexinit{_='_'}, receive_msg}, {send, ssh_msg_kexinit}, % with server unsupported 'ssh-dss' ! - {match, - {'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'}, - tcp_closed]}, - receive_msg} + {match, disconnect(), receive_msg} ] ). @@ -303,10 +318,7 @@ no_common_alg_client_disconnects(Config) -> first_kex_packet_follows = false, reserved = 0 }}, - {match, - {'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'}, - tcp_closed]}, - receive_msg} + {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg} ], InitialState) } @@ -327,31 +339,29 @@ no_common_alg_client_disconnects(Config) -> X -> ct:log("¤¤¤¤¤"), ct:fail(X) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%%-------------------------------------------------------------------- -gex_client_init_default_noexact(Config) -> - do_gex_client_init(Config, {2000, 3000, 4000}, - %% Warning, app knowledege: - ?dh_group15). - - -gex_client_init_default_exact(Config) -> - do_gex_client_init(Config, {2000, 2048, 4000}, - %% Warning, app knowledege: - ?dh_group14). - - gex_client_init_option_groups(Config) -> do_gex_client_init(Config, {2000, 2048, 4000}, - {'n/a',{3,41}}). - + {3,41}). gex_client_init_option_groups_file(Config) -> do_gex_client_init(Config, {2000, 2048, 4000}, - {'n/a',{5,61}}). + {5,61}). + +gex_client_init_option_groups_moduli_file(Config) -> + do_gex_client_init(Config, {2000, 2048, 4000}, + {5,16#B7}). + +gex_server_gex_limit(Config) -> + do_gex_client_init(Config, {1000, 3000, 4000}, + {7,91}). + -do_gex_client_init(Config, {Min,N,Max}, {_,{G,P}}) -> +do_gex_client_init(Config, {Min,N,Max}, {G,P}) -> {ok,_} = ssh_trpt_test_lib:exec( [{set_options, [print_ops, print_seqnums, print_messages]}, @@ -373,6 +383,117 @@ do_gex_client_init(Config, {Min,N,Max}, {_,{G,P}}) -> ] ). +%%%-------------------------------------------------------------------- +gex_client_old_request_exact(Config) -> do_gex_client_init_old(Config, 500, {3,17}). +gex_client_old_request_noexact(Config) -> do_gex_client_init_old(Config, 800, {7,91}). + +do_gex_client_init_old(Config, N, {G,P}) -> + {ok,_} = + ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}, + {connect, + server_host(Config),server_port(Config), + [{silently_accept_hosts, true}, + {user_dir, user_dir(Config)}, + {user_interaction, false}, + {preferred_algorithms,[{kex,['diffie-hellman-group-exchange-sha1']}]} + ]}, + receive_hello, + {send, hello}, + {send, ssh_msg_kexinit}, + {match, #ssh_msg_kexinit{_='_'}, receive_msg}, + {send, #ssh_msg_kex_dh_gex_request_old{n = N}}, + {match, #ssh_msg_kex_dh_gex_group{p=P, g=G, _='_'}, receive_msg} + ] + ). + +%%%-------------------------------------------------------------------- +bad_service_name(Config) -> + bad_service_name(Config, "kfglkjf"). + +bad_long_service_name(Config) -> + bad_service_name(Config, + lists:duplicate(?SSH_MAX_PACKET_SIZE div 2, $a)). + +bad_very_long_service_name(Config) -> + bad_service_name(Config, + lists:duplicate(4*?SSH_MAX_PACKET_SIZE, $a)). + +empty_service_name(Config) -> + bad_service_name(Config, ""). + +bad_service_name_then_correct(Config) -> + {ok,InitialState} = connect_and_kex(Config), + {ok,_} = + ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}, + {send, #ssh_msg_service_request{name = "kdjglkfdjgkldfjglkdfjglkfdjglkj"}}, + {send, #ssh_msg_service_request{name = "ssh-connection"}}, + {match, disconnect(), receive_msg} + ], InitialState). + + +bad_service_name(Config, Name) -> + {ok,InitialState} = connect_and_kex(Config), + {ok,_} = + ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}, + {send, #ssh_msg_service_request{name = Name}}, + {match, disconnect(), receive_msg} + ], InitialState). + +%%%-------------------------------------------------------------------- +packet_length_too_large(Config) -> bad_packet_length(Config, +4). + +packet_length_too_short(Config) -> bad_packet_length(Config, -4). + +bad_packet_length(Config, LengthExcess) -> + PacketFun = + fun(Msg, Ssh) -> + BinMsg = ssh_message:encode(Msg), + ssh_transport:pack(BinMsg, Ssh, LengthExcess) + end, + {ok,InitialState} = connect_and_kex(Config), + {ok,_} = + ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}, + {send, {special, + #ssh_msg_service_request{name="ssh-userauth"}, + PacketFun}}, + %% Prohibit remote decoder starvation: + {send, #ssh_msg_service_request{name="ssh-userauth"}}, + {match, disconnect(), receive_msg} + ], InitialState). + +%%%-------------------------------------------------------------------- +service_name_length_too_large(Config) -> bad_service_name_length(Config, +4). + +service_name_length_too_short(Config) -> bad_service_name_length(Config, -4). + + +bad_service_name_length(Config, LengthExcess) -> + PacketFun = + fun(#ssh_msg_service_request{name=Service}, Ssh) -> + BinName = list_to_binary(Service), + BinMsg = + <<?BYTE(?SSH_MSG_SERVICE_REQUEST), + %% A bad string encoding of Service: + ?UINT32(size(BinName)+LengthExcess), BinName/binary + >>, + ssh_transport:pack(BinMsg, Ssh) + end, + {ok,InitialState} = connect_and_kex(Config), + {ok,_} = + ssh_trpt_test_lib:exec( + [{set_options, [print_ops, print_seqnums, print_messages]}, + {send, {special, + #ssh_msg_service_request{name="ssh-userauth"}, + PacketFun} }, + %% Prohibit remote decoder starvation: + {send, #ssh_msg_service_request{name="ssh-userauth"}}, + {match, disconnect(), receive_msg} + ], InitialState). + %%%================================================================ %%%==== Internal functions ======================================== %%%================================================================ @@ -480,3 +601,37 @@ std_connect(Host, Port, Config, Opts) -> 30000). %%%---------------------------------------------------------------- +connect_and_kex(Config) -> + connect_and_kex(Config, ssh_trpt_test_lib:exec([]) ). + +connect_and_kex(Config, InitialState) -> + ssh_trpt_test_lib:exec( + [{connect, + server_host(Config),server_port(Config), + [{preferred_algorithms,[{kex,['diffie-hellman-group1-sha1']}]}, + {silently_accept_hosts, true}, + {user_dir, user_dir(Config)}, + {user_interaction, false}]}, + receive_hello, + {send, hello}, + {send, ssh_msg_kexinit}, + {match, #ssh_msg_kexinit{_='_'}, receive_msg}, + {send, ssh_msg_kexdh_init}, + {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg}, + {send, #ssh_msg_newkeys{}}, + {match, #ssh_msg_newkeys{_='_'}, receive_msg} + ], + InitialState). + +%%%---------------------------------------------------------------- + +%%% For matching peer disconnection +disconnect() -> + disconnect('_'). + +disconnect(Code) -> + {'or',[#ssh_msg_disconnect{code = Code, + _='_'}, + tcp_closed, + {tcp_error,econnaborted} + ]}. diff --git a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli new file mode 100644 index 0000000000..f6995ba4c9 --- /dev/null +++ b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test.moduli @@ -0,0 +1,3 @@ +20151021104105 2 6 100 2222 5 B7 +20151021104106 2 6 100 1111 5 4F + diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 9daa6efc02..e5cfa58bad 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -32,9 +32,15 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> [rekey, rekey_limit, renegotiate1, renegotiate2]. +all() -> [{group,default_algs}, + {group,aes_gcm} + ]. -groups() -> []. +groups() -> [{default_algs, [], tests()}, + {aes_gcm, [], tests()} + ]. + +tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2]. %%-------------------------------------------------------------------- init_per_suite(Config) -> @@ -50,6 +56,24 @@ end_per_suite(_Config) -> crypto: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. @@ -89,9 +113,12 @@ rekey_limit(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "rekey.data"), - {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[]), + Algs = ?config(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, 4500}]), + 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 = get_kex_init(ConnectionRef), @@ -132,13 +159,15 @@ renegotiate1(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate1.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[]), + Algs = ?config(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, []), + ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), Kex1 = get_kex_init(ConnectionRef), @@ -170,12 +199,14 @@ renegotiate2(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate2.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[]), + Algs = ?config(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, []), + ConnectionRef = ssh_test_lib:std_connect(Config, Host, RPort, [{max_random_length_padding,0}]), {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef), Kex1 = get_kex_init(ConnectionRef), diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 32fdec9842..698af259c8 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -526,6 +526,8 @@ async_read(Config) when is_list(Config) -> ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. %%-------------------------------------------------------------------- async_write() -> @@ -593,6 +595,8 @@ pos_read(Config) when is_list(Config) -> ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, NewData1 = "hopp", @@ -618,6 +622,8 @@ pos_write(Config) when is_list(Config) -> ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")), diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 94a54ec9db..6b03a2b763 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -683,6 +683,8 @@ reply(Cm, Channel, RBuf) -> closed; {ssh_cm, Cm, Msg} -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 6d568125bb..2db55b97b4 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -93,9 +93,12 @@ std_connect(Config, Host, Port, ExtraOpts) -> | ExtraOpts]). std_simple_sftp(Host, Port, Config) -> + std_simple_sftp(Host, Port, Config, []). + +std_simple_sftp(Host, Port, Config, Opts) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "test.data"), - ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, []), + ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, Opts), {ok, ChannelRef} = ssh_sftp:start_channel(ConnectionRef), Data = crypto:rand_bytes(proplists:get_value(std_simple_sftp_size,Config,10)), ok = ssh_sftp:write_file(ChannelRef, DataFile, Data), @@ -104,7 +107,10 @@ std_simple_sftp(Host, Port, Config) -> Data == ReadData. std_simple_exec(Host, Port, Config) -> - ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, []), + std_simple_exec(Host, Port, Config, []). + +std_simple_exec(Host, Port, Config, Opts) -> + ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, Opts), {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), success = ssh_connection:exec(ConnectionRef, ChannelId, "23+21-2.", infinity), Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"42\n">>}}, @@ -157,7 +163,9 @@ loop_io_server(TestCase, Buff0) -> {'EXIT',_, _} -> erlang:display('ssh_test_lib:loop_io_server/2 EXIT'), ok - end. + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end. io_request({put_chars, Chars}, TestCase, _, _, Buff) -> reply(TestCase, Chars), @@ -206,6 +214,8 @@ receive_exec_result(Msg) -> Other -> ct:log("Other ~p", [Other]), {unexpected_msg, Other} + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. @@ -286,6 +296,7 @@ setup_dsa(DataDir, UserDir) -> file:make_dir(System), file:copy(filename:join(DataDir, "ssh_host_dsa_key"), filename:join(System, "ssh_host_dsa_key")), file:copy(filename:join(DataDir, "ssh_host_dsa_key.pub"), filename:join(System, "ssh_host_dsa_key.pub")), +ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]), setup_dsa_known_host(DataDir, UserDir), setup_dsa_auth_keys(DataDir, UserDir). @@ -294,10 +305,21 @@ setup_rsa(DataDir, UserDir) -> System = filename:join(UserDir, "system"), file:make_dir(System), file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key")), - file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key.pub")), + file:copy(filename:join(DataDir, "ssh_host_rsa_key.pub"), filename:join(System, "ssh_host_rsa_key.pub")), +ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]), setup_rsa_known_host(DataDir, UserDir), setup_rsa_auth_keys(DataDir, UserDir). +setup_ecdsa(Size, DataDir, UserDir) -> + file:copy(filename:join(DataDir, "id_ecdsa"++Size), filename:join(UserDir, "id_ecdsa")), + System = filename:join(UserDir, "system"), + file:make_dir(System), + file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size), filename:join(System, "ssh_host_ecdsa_key")), + file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size++".pub"), filename:join(System, "ssh_host_ecdsa_key.pub")), +ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]), + setup_ecdsa_known_host(Size, System, UserDir), + setup_ecdsa_auth_keys(Size, UserDir, UserDir). + clean_dsa(UserDir) -> del_dirs(filename:join(UserDir, "system")), file:delete(filename:join(UserDir,"id_dsa")), @@ -349,6 +371,11 @@ setup_rsa_known_host(SystemDir, UserDir) -> [{Key, _}] = public_key:ssh_decode(SshBin, public_key), setup_known_hosts(Key, UserDir). +setup_ecdsa_known_host(_Size, SystemDir, UserDir) -> + {ok, SshBin} = file:read_file(filename:join(SystemDir, "ssh_host_ecdsa_key.pub")), + [{Key, _}] = public_key:ssh_decode(SshBin, public_key), + setup_known_hosts(Key, UserDir). + setup_known_hosts(Key, UserDir) -> {ok, Hostname} = inet:gethostname(), {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), @@ -376,6 +403,14 @@ setup_rsa_auth_keys(Dir, UserDir) -> PKey = #'RSAPublicKey'{publicExponent = E, modulus = N}, setup_auth_keys([{ PKey, [{comment, "Test"}]}], UserDir). +setup_ecdsa_auth_keys(_Size, Dir, UserDir) -> + {ok, Pem} = file:read_file(filename:join(Dir, "id_ecdsa")), + ECDSA = public_key:pem_entry_decode(hd(public_key:pem_decode(Pem))), + #'ECPrivateKey'{publicKey = Q, + parameters = Param = {namedCurve,_Id0}} = ECDSA, + PKey = #'ECPoint'{point = Q}, + setup_auth_keys([{ {PKey,Param}, [{comment, "Test"}]}], UserDir). + setup_auth_keys(Keys, Dir) -> AuthKeys = public_key:ssh_encode(Keys, auth_keys), AuthKeysFile = filename:join(Dir, "authorized_keys"), @@ -424,6 +459,14 @@ openssh_sanity_check(Config) -> {skip, Str} end. +openssh_supports(ClientOrServer, Tag, Alg) when ClientOrServer == sshc ; + ClientOrServer == sshd -> + SSH_algos = ssh_test_lib:default_algorithms(ClientOrServer), + L = proplists:get_value(Tag, SSH_algos, []), + lists:member(Alg, L) orelse + lists:member(Alg, proplists:get_value(client2server, L, [])) orelse + lists:member(Alg, proplists:get_value(server2client, L, [])). + %%-------------------------------------------------------------------- %% Check if we have a "newer" ssh client that supports these test cases @@ -443,7 +486,62 @@ check_ssh_client_support2(P) -> -1 end. -default_algorithms(Host, Port) -> +%%%-------------------------------------------------------------------- +%%% Probe a server or a client about algorithm support + +default_algorithms(sshd) -> + default_algorithms(sshd, "localhost", 22); + +default_algorithms(sshc) -> + default_algorithms(sshc, []). + +default_algorithms(sshd, Host, Port) -> + try run_fake_ssh( + ssh_trpt_test_lib:exec( + [{connect,Host,Port, [{silently_accept_hosts, true}, + {user_interaction, false}]}])) + catch + _C:_E -> + ct:log("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]), + [] + end. + +default_algorithms(sshc, DaemonOptions) -> + Parent = self(), + %% Start a process handling one connection on the server side: + Srvr = + spawn_link( + fun() -> + Parent ! + {result, self(), + try + {ok,InitialState} = ssh_trpt_test_lib:exec(listen), + Parent ! {hostport,self(),ssh_trpt_test_lib:server_host_port(InitialState)}, + run_fake_ssh( + ssh_trpt_test_lib:exec([{accept, DaemonOptions}], + InitialState)) + catch + _C:_E -> + ct:log("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]), + [] + end} + end), + + receive + {hostport,Srvr,{_Host,Port}} -> + spawn(fun()-> os:cmd(lists:concat(["ssh -o \"StrictHostKeyChecking no\" -p ",Port," localhost"])) end) + after ?TIMEOUT -> + ct:fail("No server respons 1") + end, + + receive + {result,Srvr,L} -> + L + after ?TIMEOUT -> + ct:fail("No server respons 2") + end. + +run_fake_ssh({ok,InitialState}) -> KexInitPattern = #ssh_msg_kexinit{ kex_algorithms = '$kex_algorithms', @@ -456,61 +554,69 @@ default_algorithms(Host, Port) -> compression_algorithms_server_to_client = '$compression_algorithms_server_to_client', _ = '_' }, + {ok,E} = ssh_trpt_test_lib:exec([{set_options,[silent]}, + {send, hello}, + receive_hello, + {send, ssh_msg_kexinit}, + {match, KexInitPattern, receive_msg}, + close_socket + ], + InitialState), + [Kex, PubKey, EncC2S, EncS2C, MacC2S, MacS2C, CompC2S, CompS2C] = + ssh_trpt_test_lib:instantiate(['$kex_algorithms', + '$server_host_key_algorithms', + '$encryption_algorithms_client_to_server', + '$encryption_algorithms_server_to_client', + '$mac_algorithms_client_to_server', + '$mac_algorithms_server_to_client', + '$compression_algorithms_client_to_server', + '$compression_algorithms_server_to_client' + ], E), + [{kex, to_atoms(Kex)}, + {public_key, to_atoms(PubKey)}, + {cipher, [{client2server, to_atoms(EncC2S)}, + {server2client, to_atoms(EncS2C)}]}, + {mac, [{client2server, to_atoms(MacC2S)}, + {server2client, to_atoms(MacS2C)}]}, + {compression, [{client2server, to_atoms(CompC2S)}, + {server2client, to_atoms(CompS2C)}]}]. + - try ssh_trpt_test_lib:exec( - [{connect,Host,Port, [{silently_accept_hosts, true}, - {user_interaction, false}]}, - {send,hello}, - receive_hello, - {send, ssh_msg_kexinit}, - {match, KexInitPattern, receive_msg}, - close_socket]) - of - {ok,E} -> - [Kex, PubKey, EncC2S, EncS2C, MacC2S, MacS2C, CompC2S, CompS2C] = - ssh_trpt_test_lib:instantiate(['$kex_algorithms', - '$server_host_key_algorithms', - '$encryption_algorithms_client_to_server', - '$encryption_algorithms_server_to_client', - '$mac_algorithms_client_to_server', - '$mac_algorithms_server_to_client', - '$compression_algorithms_client_to_server', - '$compression_algorithms_server_to_client' - ], E), - [{kex, to_atoms(Kex)}, - {public_key, to_atoms(PubKey)}, - {cipher, [{client2server, to_atoms(EncC2S)}, - {server2client, to_atoms(EncS2C)}]}, - {mac, [{client2server, to_atoms(MacC2S)}, - {server2client, to_atoms(MacS2C)}]}, - {compression, [{client2server, to_atoms(CompC2S)}, - {server2client, to_atoms(CompS2C)}]}]; - _ -> - [] - catch - _:_ -> - [] - end. +%%%---------------------------------------------------------------- +extract_algos(Spec) -> + [{Tag,get_atoms(List)} || {Tag,List} <- Spec]. +get_atoms(L) -> + lists:usort( + [ A || X <- L, + A <- case X of + {_,L1} when is_list(L1) -> L1; + Y when is_atom(Y) -> [Y] + end]). -default_algorithms(sshd) -> - default_algorithms("localhost", 22); -default_algorithms(sshc) -> - case os:find_executable("ssh") of - false -> - []; - _ -> - Cipher = sshc(cipher), - Mac = sshc(mac), - [{kex, sshc(kex)}, - {public_key, sshc(key)}, - {cipher, [{client2server, Cipher}, - {server2client, Cipher}]}, - {mac, [{client2server, Mac}, - {server2client, Mac}]} - ] - end. +intersection(AlgoSpec1, AlgoSpec2) -> intersect(sort_spec(AlgoSpec1), sort_spec(AlgoSpec2)). + +intersect([{Tag,S1}|Ss1], [{Tag,S2}|Ss2]) -> + [{Tag,intersect(S1,S2)} | intersect(Ss1,Ss2)]; +intersect(L1=[A1|_], L2=[A2|_]) when is_atom(A1),is_atom(A2) -> + Diff = L1 -- L2, + L1 -- Diff; +intersect(_, _) -> + []. + +intersect_bi_dir([{Tag,[{client2server,L1},{server2client,L2}]}|T]) -> + [{Tag,intersect(L1,L2)} | intersect_bi_dir(T)]; +intersect_bi_dir([H={_,[A|_]}|T]) when is_atom(A) -> + [H | intersect_bi_dir(T)]; +intersect_bi_dir([]) -> + []. + + +sort_spec(L = [{_,_}|_] ) -> [{Tag,sort_spec(Es)} || {Tag,Es} <- L]; +sort_spec(L) -> lists:usort(L). + +%%-------------------------------------------------------------------- sshc(Tag) -> to_atoms( string:tokens(os:cmd(lists:concat(["ssh -Q ",Tag])), "\n") @@ -552,4 +658,36 @@ algo_intersection(_, _) -> to_atoms(L) -> lists:map(fun erlang:list_to_atom/1, L). - +%%%---------------------------------------------------------------- +ssh_supports(Alg, SshDefaultAlg_tag) -> + SupAlgs = + case proplists:get_value(SshDefaultAlg_tag, + ssh:default_algorithms()) of + [{_K1,L1}, {_K2,L2}] -> + lists:usort(L1++L2); + L -> + L + end, + if + is_atom(Alg) -> + lists:member(Alg, SupAlgs); + is_list(Alg) -> + case Alg--SupAlgs of + [] -> + true; + UnSup -> + {false,UnSup} + end + end. + +%%%---------------------------------------------------------------- +has_inet6_address() -> + try + [throw(6) || {ok,L} <- [inet:getifaddrs()], + {_,L1} <- L, + {addr,{_,_,_,_,_,_,_,_}} <- L1] + of + [] -> false + catch + throw:6 -> true + end. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index 104c1f9107..67a61d3c11 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -45,7 +45,6 @@ all() -> groups() -> [{erlang_client, [], [erlang_shell_client_openssh_server, - erlang_client_openssh_server_exec, erlang_client_openssh_server_exec_compressed, erlang_client_openssh_server_setenv, erlang_client_openssh_server_publickey_rsa, @@ -54,12 +53,7 @@ groups() -> erlang_client_openssh_server_kexs, erlang_client_openssh_server_nonexistent_subsystem ]}, - {erlang_server, [], [erlang_server_openssh_client_exec, - erlang_server_openssh_client_exec_compressed, - erlang_server_openssh_client_pulic_key_dsa, - erlang_server_openssh_client_cipher_suites, - erlang_server_openssh_client_macs, - erlang_server_openssh_client_kexs]} + {erlang_server, [], [erlang_server_openssh_client_public_key_dsa]} ]. init_per_suite(Config) -> @@ -88,7 +82,7 @@ init_per_group(erlang_server, Config) -> init_per_group(erlang_client, Config) -> CommonAlgs = ssh_test_lib:algo_intersection( ssh:default_algorithms(), - ssh_test_lib:default_algorithms("localhost", 22)), + ssh_test_lib:default_algorithms(sshd)), [{common_algs,CommonAlgs} | Config]; init_per_group(_, Config) -> Config. @@ -100,18 +94,11 @@ end_per_group(erlang_server, Config) -> end_per_group(_, Config) -> Config. -init_per_testcase(erlang_server_openssh_client_cipher_suites, Config) -> - check_ssh_client_support(Config); - -init_per_testcase(erlang_server_openssh_client_macs, Config) -> - check_ssh_client_support(Config); - -init_per_testcase(erlang_server_openssh_client_kexs, Config) -> - check_ssh_client_support(Config); - -init_per_testcase(erlang_client_openssh_server_kexs, Config) -> - check_ssh_client_support(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_client_openssh_server_publickey_dsa, Config) -> + chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config); init_per_testcase(_TestCase, Config) -> ssh:start(), Config. @@ -120,6 +107,27 @@ 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 -------------------------------------------------------- %%-------------------------------------------------------------------- @@ -182,23 +190,29 @@ erlang_client_openssh_server_exec_compressed() -> erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) -> CompressAlgs = [zlib, '[email protected]',none], - 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) + 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. %%-------------------------------------------------------------------- @@ -252,202 +266,6 @@ erlang_client_openssh_server_kexs(Config) when is_list(Config) -> end. %%-------------------------------------------------------------------- -erlang_server_openssh_client_exec() -> - [{doc, "Test that exec command works."}]. - -erlang_server_openssh_client_exec(Config) when is_list(Config) -> - SystemDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - KnownHosts = filename:join(PrivDir, "known_hosts"), - - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}]), - - - ct:sleep(500), - - Cmd = "ssh -p " ++ integer_to_list(Port) ++ - " -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " 1+1.", - - ct:log("Cmd: ~p~n", [Cmd]), - - SshPort = open_port({spawn, Cmd}, [binary]), - - receive - {SshPort,{data, <<"2\n">>}} -> - ok - after ?TIMEOUT -> - ct:fail("Did not receive answer") - - end, - ssh:stop_daemon(Pid). - -%%-------------------------------------------------------------------- -erlang_server_openssh_client_cipher_suites() -> - [{doc, "Test that we can connect with different cipher suites."}]. - -erlang_server_openssh_client_cipher_suites(Config) when is_list(Config) -> - SystemDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - KnownHosts = filename:join(PrivDir, "known_hosts"), - - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}]), - - ct:sleep(500), - - OpenSshCiphers = - ssh_test_lib:to_atoms( - string:tokens(os:cmd("ssh -Q cipher"), "\n")), - ErlCiphers = - proplists:get_value(client2server, - proplists:get_value(cipher, ssh:default_algorithms())), - CommonCiphers = - ssh_test_lib:algo_intersection(ErlCiphers, OpenSshCiphers), - - comment(CommonCiphers), - - lists:foreach( - fun(Cipher) -> - Cmd = lists:concat(["ssh -p ",Port, - " -o UserKnownHostsFile=",KnownHosts," ",Host," ", - " -c ",Cipher," 1+1."]), - ct:log("Cmd: ~p~n", [Cmd]), - - SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]), - - receive - {SshPort,{data, <<"2\n">>}} -> - ok - after ?TIMEOUT -> - ct:fail("~p Did not receive answer",[Cipher]) - end - end, CommonCiphers), - - ssh:stop_daemon(Pid). - -%%-------------------------------------------------------------------- -erlang_server_openssh_client_macs() -> - [{doc, "Test that we can connect with different MACs."}]. - -erlang_server_openssh_client_macs(Config) when is_list(Config) -> - SystemDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - KnownHosts = filename:join(PrivDir, "known_hosts"), - - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}]), - - - ct:sleep(500), - - OpenSshMacs = - ssh_test_lib:to_atoms( - string:tokens(os:cmd("ssh -Q mac"), "\n")), - ErlMacs = - proplists:get_value(client2server, - proplists:get_value(mac, ssh:default_algorithms())), - CommonMacs = - ssh_test_lib:algo_intersection(ErlMacs, OpenSshMacs), - - comment(CommonMacs), - - lists:foreach( - fun(MAC) -> - Cmd = lists:concat(["ssh -p ",Port, - " -o UserKnownHostsFile=",KnownHosts," ",Host," ", - " -o MACs=",MAC," 1+1."]), - ct:log("Cmd: ~p~n", [Cmd]), - - SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]), - - receive - {SshPort,{data, <<"2\n">>}} -> - ok - after ?TIMEOUT -> - ct:fail("~p Did not receive answer",[MAC]) - end - end, CommonMacs), - - ssh:stop_daemon(Pid). - -%%-------------------------------------------------------------------- -erlang_server_openssh_client_kexs() -> - [{doc, "Test that we can connect with different KEXs."}]. - -erlang_server_openssh_client_kexs(Config) when is_list(Config) -> - SystemDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - KnownHosts = filename:join(PrivDir, "known_hosts"), - - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {failfun, fun ssh_test_lib:failfun/2}, - {preferred_algorithms, - [{kex,ssh_transport:supported_algorithms(kex)}]} - ]), - ct:sleep(500), - - OpenSshKexs = - ssh_test_lib:to_atoms( - string:tokens(os:cmd("ssh -Q kex"), "\n")), - ErlKexs = - proplists:get_value(kex, ssh:default_algorithms()), - CommonKexs = - ssh_test_lib:algo_intersection(ErlKexs, OpenSshKexs), - - comment(CommonKexs), - - lists:foreach( - fun(Kex) -> - Cmd = lists:concat(["ssh -p ",Port, - " -o UserKnownHostsFile=",KnownHosts," ",Host," ", - " -o KexAlgorithms=",Kex," 1+1."]), - ct:log("Cmd: ~p~n", [Cmd]), - - SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]), - - receive - {SshPort,{data, <<"2\n">>}} -> - ok - after ?TIMEOUT -> - ct:log("~p Did not receive answer",[Kex]) - end - end, CommonKexs), - - ssh:stop_daemon(Pid). - -%%-------------------------------------------------------------------- -erlang_server_openssh_client_exec_compressed() -> - [{doc, "Test that exec command works."}]. - -erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) -> - SystemDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - KnownHosts = filename:join(PrivDir, "known_hosts"), - -%% CompressAlgs = [zlib, '[email protected]'], % Does not work - CompressAlgs = [zlib], - {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {preferred_algorithms, - [{compression, CompressAlgs}]}, - {failfun, fun ssh_test_lib:failfun/2}]), - - ct:sleep(500), - - Cmd = "ssh -p " ++ integer_to_list(Port) ++ - " -o UserKnownHostsFile=" ++ KnownHosts ++ " -C "++ Host ++ " 1+1.", - SshPort = open_port({spawn, Cmd}, [binary]), - - receive - {SshPort,{data, <<"2\n">>}} -> - ok - after ?TIMEOUT -> - ct:fail("Did not receive answer") - - end, - ssh:stop_daemon(Pid). - -%%-------------------------------------------------------------------- erlang_client_openssh_server_setenv() -> [{doc, "Test api function ssh_connection:setenv"}]. @@ -521,31 +339,20 @@ erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) -> erlang_client_openssh_server_publickey_dsa() -> [{doc, "Validate using dsa publickey."}]. erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) -> - {ok,[[Home]]} = init:get_argument(home), - KeyFile = filename:join(Home, ".ssh/id_dsa"), - case file:read_file(KeyFile) of - {ok, Pem} -> - case public_key:pem_decode(Pem) of - [{_,_, not_encrypted}] -> - ConnectionRef = - ssh_test_lib:connect(?SSH_DEFAULT_PORT, - [{public_key_alg, ssh_dsa}, - {user_interaction, false}, - silently_accept_hosts]), - {ok, Channel} = - ssh_connection:session_channel(ConnectionRef, infinity), - ok = ssh_connection:close(ConnectionRef, Channel), - ok = ssh:close(ConnectionRef); - _ -> - {skip, {error, "Has pass phrase can not be used by automated test case"}} - end; - _ -> - {skip, "no ~/.ssh/id_dsa"} - end. + ConnectionRef = + ssh_test_lib:connect(?SSH_DEFAULT_PORT, + [{public_key_alg, ssh_dsa}, + {user_interaction, false}, + 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_pulic_key_dsa() -> +erlang_server_openssh_client_public_key_dsa() -> [{doc, "Validate using dsa publickey."}]. -erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) -> +erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) -> SystemDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), @@ -553,21 +360,25 @@ erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {public_key_alg, ssh_dsa}, {failfun, fun ssh_test_lib:failfun/2}]), - + ct:sleep(500), Cmd = "ssh -p " ++ integer_to_list(Port) ++ " -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " 1+1.", - SshPort = open_port({spawn, Cmd}, [binary]), + SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]), receive - {SshPort,{data, <<"2\n">>}} -> + {SshPort,{data, <<"2\n">>}} -> ok after ?TIMEOUT -> - ct:fail("Did not receive answer") + receive + X -> ct:fail("Received: ~p",[X]) + after 0 -> + ct:fail("Did not receive answer") + end end, - ssh:stop_daemon(Pid). + ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- erlang_client_openssh_server_password() -> @@ -577,10 +388,10 @@ erlang_client_openssh_server_password(Config) when is_list(Config) -> UserDir = ?config(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}]), + {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]), @@ -642,6 +453,8 @@ receive_hej() -> ct:log("Extra info: ~p~n", [Info]), receive_hej() end + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. receive_logout() -> @@ -651,11 +464,15 @@ receive_logout() -> receive <<"Connection closed">> -> ok + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end; Info -> ct:log("Extra info when logging out: ~p~n", [Info]), receive_logout() - end. + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end. receive_normal_exit(Shell) -> receive @@ -665,6 +482,8 @@ receive_normal_exit(Shell) -> receive_normal_exit(Shell); Other -> ct:fail({unexpected_msg, Other}) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. extra_logout() -> diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl index caf9bac3b6..4269529ae8 100644 --- a/lib/ssh/test/ssh_trpt_test_lib.erl +++ b/lib/ssh/test/ssh_trpt_test_lib.erl @@ -73,7 +73,10 @@ exec(Op, S0=#s{}) -> op(Op, S1)) of S = #s{} -> - print_traces(S), + case proplists:get_value(silent,S#s.opts) of + true -> ok; + _ -> print_traces(S) + end, {ok,S} catch {fail,Reason,Se} -> @@ -383,7 +386,14 @@ send(S0, Line) when is_binary(Line) -> fun(X) when X==true;X==detail -> {"Send line~n~p~n",[Line]} end), send_bytes(Line, S#s{return_value = Line}); -%%% Msg = #ssh_msg_*{} +send(S0, {special,Msg,PacketFun}) when is_tuple(Msg), + is_function(PacketFun,2) -> + S = opt(print_messages, S0, + fun(X) when X==true;X==detail -> {"Send~n~s~n",[format_msg(Msg)]} end), + {Packet, C} = PacketFun(Msg, S#s.ssh), + send_bytes(Packet, S#s{ssh = C, %%inc_send_seq_num(C), + return_value = Msg}); + send(S0, Msg) when is_tuple(Msg) -> S = opt(print_messages, S0, fun(X) when X==true;X==detail -> {"Send~n~s~n",[format_msg(Msg)]} end), @@ -743,7 +753,7 @@ print_traces(S) -> [case Len-length(Acc)-1 of 0 -> io_lib:format(Fmt,Args); - N -> + _N -> io_lib:format(lists:concat(['~p --------~n',Fmt]), [Len-length(Acc)-1|Args]) end | Acc] diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl index 85f4d36258..0d936c118b 100644 --- a/lib/ssh/test/ssh_upgrade_SUITE.erl +++ b/lib/ssh/test/ssh_upgrade_SUITE.erl @@ -46,20 +46,17 @@ all() -> init_per_suite(Config0) -> catch crypto:stop(), - try {crypto:start(), erlang:system_info({wordsize, internal}) == - erlang:system_info({wordsize, external})} of - {ok, true} -> - case ct_release_test:init(Config0) of - {skip, Reason} -> - {skip, Reason}; - Config -> - ssh:start(), - Config - end; - {ok, false} -> - {skip, "Test server will not handle halfwordemulator correctly. Skip as halfwordemulator is deprecated"} + try crypto:start() of + ok -> + case ct_release_test:init(Config0) of + {skip, Reason} -> + {skip, Reason}; + Config -> + ssh:start(), + Config + end catch _:_ -> - {skip, "Crypto did not start"} + {skip, "Crypto did not start"} end. end_per_suite(Config) -> |