diff options
Diffstat (limited to 'lib/ssh/test')
30 files changed, 998 insertions, 411 deletions
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..0a5964c560 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -36,6 +36,8 @@ 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, @@ -77,11 +79,16 @@ 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, internal_error}, daemon_already_started, double_close, + daemon_opt_fd, + multi_daemon_opt_fd, packet_size_zero, ssh_info_print ]. @@ -89,6 +96,9 @@ 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()}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {internal_error, [], [internal_error]} @@ -117,8 +127,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 +137,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), @@ -190,8 +231,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), @@ -362,30 +401,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 +473,8 @@ 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. %%-------------------------------------------------------------------- @@ -456,11 +503,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. %%-------------------------------------------------------------------- @@ -599,7 +650,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 +684,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 +709,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 +821,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 +944,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_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index 37bba07440..1b93cc9c32 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -120,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. %%-------------------------------------------------------------------- @@ -155,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() -> @@ -212,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. %%-------------------------------------------------------------------- @@ -235,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; @@ -456,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) -> @@ -476,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) -> @@ -496,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. @@ -517,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() -> @@ -666,6 +700,16 @@ max_channels_option(Config) when is_list(Config) -> %%%---- 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 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..4639904061 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,_} = @@ -327,31 +345,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}). -do_gex_client_init(Config, {Min,N,Max}, {_,{G,P}}) -> +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}) -> {ok,_} = ssh_trpt_test_lib:exec( [{set_options, [print_ops, print_seqnums, print_messages]}, @@ -373,6 +389,129 @@ 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, {'or',[#ssh_msg_disconnect{_='_'}, + tcp_closed + ]}, + 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, {'or',[#ssh_msg_disconnect{_='_'}, + tcp_closed + ]}, + 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, {'or',[#ssh_msg_disconnect{_='_'}, + tcp_closed + ]}, + 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, {'or',[#ssh_msg_disconnect{_='_'}, + tcp_closed + ]}, + receive_msg} + ], InitialState). + %%%================================================================ %%%==== Internal functions ======================================== %%%================================================================ @@ -480,3 +619,24 @@ 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). 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..227dfcddcd 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,18 @@ end_per_suite(_Config) -> crypto:stop(). %%-------------------------------------------------------------------- +init_per_group(aes_gcm, Config) -> + [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']}, + {server2client,['[email protected]']}]}]} + | Config]; +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 +107,10 @@ rekey_limit(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "rekey.data"), - {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[]), + {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), - 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 +151,13 @@ renegotiate1(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate1.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[]), + {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), 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 +189,12 @@ renegotiate2(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate2.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[]), + {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), 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..424afc76fe 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,63 @@ 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 +555,35 @@ 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. - - -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. - +%%-------------------------------------------------------------------- sshc(Tag) -> to_atoms( string:tokens(os:cmd(lists:concat(["ssh -Q ",Tag])), "\n") @@ -552,4 +625,24 @@ 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. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index 104c1f9107..d1dfa2efdf 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,21 @@ 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) -> + case ssh_test_lib:openssh_supports(sshc, public_key, 'ssh-dss') of + true -> + init_per_testcase('__default__',Config); + false -> + {skip,"openssh client does not support DSA"} + end; +init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) -> + case ssh_test_lib:openssh_supports(sshd, public_key, 'ssh-dss') of + true -> + init_per_testcase('__default__',Config); + false -> + {skip,"openssh client does not support DSA"} + end; init_per_testcase(_TestCase, Config) -> ssh:start(), Config. @@ -182,23 +179,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 +255,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"}]. @@ -543,9 +350,9 @@ erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) -> {skip, "no ~/.ssh/id_dsa"} end. %%-------------------------------------------------------------------- -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"), @@ -642,6 +449,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 +460,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 +478,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] |