aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/test')
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl10
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1398
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image38
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh28
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run27
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image72
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image71
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all89
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub1
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl5
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl6
-rw-r--r--lib/ssh/test/ssh_test_lib.erl39
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl5
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl362
36 files changed, 1899 insertions, 383 deletions
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index a18383d148..21359a0386 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -37,6 +37,7 @@ MODULES= \
ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
+ ssh_compat_SUITE \
ssh_connection_SUITE \
ssh_engine_SUITE \
ssh_protocol_SUITE \
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 98964a2c8a..de6e448ebd 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -29,15 +29,13 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 35000).
-
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{seconds,40}}].
+ {timetrap,{seconds,round(1.5*?TIMEOUT/1000)}}].
all() ->
%% [{group,kex},{group,cipher}... etc
@@ -90,7 +88,7 @@ init_per_suite(Config) ->
" -- Max num algorithms: ~p~n"
,[os:getenv("HOME"),
init:get_argument(home),
- os:cmd("ssh -V"),
+ ssh_test_lib:installed_ssh_version("TIMEOUT"),
ssh:default_algorithms(),
crypto:info_lib(),
ssh_test_lib:default_algorithms(sshc),
@@ -318,10 +316,10 @@ sshc_simple_exec_os_cmd(Config) ->
ok;
false ->
ct:log("Bad result: ~p~nExpected: ~p~nMangled result: ~p", [RawResult,Expect,Lines]),
- {fail, "Bad result"}
+ {fail, "Bad result (see log in testcase)"}
end
after ?TIMEOUT ->
- ct:fail("Did not receive answer")
+ ct:fail("Did not receive answer (timeout)")
end.
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
new file mode 100644
index 0000000000..82b83dd83d
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -0,0 +1,1398 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. 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_compat_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssh/src/ssh_transport.hrl"). % #ssh_msg_kexinit{}
+-include_lib("kernel/include/inet.hrl"). % #hostent{}
+-include_lib("kernel/include/file.hrl"). % #file_info{}
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(USER,"sshtester").
+-define(PASSWD, "foobar").
+-define(BAD_PASSWD, "NOT-"?PASSWD).
+-define(DOCKER_PFX, "ssh_compat_suite-ssh").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [%%{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+%% [check_docker_present] ++
+ [{group,G} || G <- ssh_image_versions()].
+
+groups() ->
+ [{otp_client, [], [login_otp_is_client,
+ all_algorithms_sftp_exec_reneg_otp_is_client,
+ send_recv_big_with_renegotiate_otp_is_client
+ ]},
+ {otp_server, [], [login_otp_is_server,
+ all_algorithms_sftp_exec_reneg_otp_is_server
+ ]} |
+ [{G, [], [{group,otp_client}, {group,otp_server}]} || G <- ssh_image_versions()]
+ ].
+
+
+ssh_image_versions() ->
+ try
+ %% Find all useful containers in such a way that undefined command, too low
+ %% priviliges, no containers and containers found give meaningful result:
+ L0 = ["REPOSITORY"++_|_] = string:tokens(os:cmd("docker images"), "\r\n"),
+ [["REPOSITORY","TAG"|_]|L1] = [string:tokens(E, " ") || E<-L0],
+ [list_to_atom(V) || [?DOCKER_PFX,V|_] <- L1]
+ of
+ Vs ->
+ lists:sort(Vs)
+ catch
+ error:{badmatch,_} ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ case os:find_executable("docker") of
+ false ->
+ {skip, "No docker"};
+ _ ->
+ ssh:start(),
+ ct:log("Crypto info: ~p",[crypto:info_lib()]),
+ Config
+ end).
+
+end_per_suite(Config) ->
+ %% Remove all containers that are not running:
+%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
+ %% Remove dangling images:
+%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ Config.
+
+
+init_per_group(otp_server, Config) ->
+ case proplists:get_value(common_remote_client_algs, Config) of
+ undefined ->
+ SSHver = proplists:get_value(ssh_version, Config, ""),
+ {skip,"No "++SSHver++ " client found in docker"};
+ _ ->
+ Config
+ end;
+
+init_per_group(otp_client, Config) ->
+ Config;
+
+init_per_group(G, Config0) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ %% This group is for one of the images
+ Vssh = atom_to_list(G),
+ Cmnt = io_lib:format("+++ ~s +++",[Vssh]),
+ ct:comment("~s",[Cmnt]),
+ try start_docker(G) of
+ {ok,ID} ->
+ ct:log("==> ~p started",[G]),
+ %% Find the algorithms that both client and server supports:
+ {IP,Port} = ip_port([{id,ID}]),
+ ct:log("Try contact ~p:~p",[IP,Port]),
+ Config1 = [{id,ID},
+ {ssh_version,Vssh}
+ | Config0],
+ try common_algs(Config1, IP, Port) of
+ {ok, ServerHello, RemoteServerCommon, ClientHello, RemoteClientCommon} ->
+ case chk_hellos([ServerHello,ClientHello], Cmnt) of
+ Cmnt ->
+ ok;
+ NewCmnt ->
+ ct:comment("~s",[NewCmnt])
+ end,
+ AuthMethods =
+ %% This should be obtained by quering the peer, but that
+ %% is a bit hard. It is possible with ssh_protocol_SUITE
+ %% techniques, but it can wait.
+ case Vssh of
+ "dropbear" ++ _ ->
+ [password, publickey];
+ _ ->
+ [password, 'keyboard-interactive', publickey]
+ end,
+ [{common_remote_server_algs,RemoteServerCommon},
+ {common_remote_client_algs,RemoteClientCommon},
+ {common_authmethods,AuthMethods}
+ |Config1];
+ Other ->
+ ct:log("Error in init_per_group: ~p",[Other]),
+ stop_docker(ID),
+ {fail, "Can't contact docker sshd"}
+ catch
+ Class:Exc ->
+ ST = erlang:get_stacktrace(),
+ ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
+ stop_docker(ID),
+ {fail, "Failed during setup"}
+ end
+ catch
+ cant_start_docker ->
+ {skip, "Can't start docker"};
+
+ C:E ->
+ ST = erlang:get_stacktrace(),
+ ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
+ {skip, "Can't start docker"}
+ end;
+
+ false ->
+ Config0
+ end.
+
+end_per_group(G, Config) ->
+ case lists:member(G, ssh_image_versions()) of
+ true ->
+ catch stop_docker(proplists:get_value(id,Config));
+ false ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+check_docker_present(_Config) ->
+ ct:log("This testcase is just to show in Monitor that we have a test host with docker installed",[]),
+ {fail, "Test is OK: just showing docker is available"}.
+
+%%--------------------------------------------------------------------
+login_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_server_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,Dir} =
+ case AuthMethod of
+ publickey ->
+ {[], setup_remote_auth_keys_and_local_priv(Alg, Config)};
+ _ ->
+ {[{password,?PASSWD}], new_dir(Config)}
+ end,
+ ssh:connect(IP, Port, [{auth_methods, atom_to_list(AuthMethod)},
+ {user,?USER},
+ {user_dir, Dir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ | Opts
+ ])
+ end).
+
+
+%%--------------------------------------------------------------------
+login_otp_is_server(Config) ->
+ PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_client_algs, Config)],
+ CommonAuths =
+ [{AuthMethod,Alg} || AuthMethod <- proplists:get_value(common_authmethods, Config),
+ Alg <- case AuthMethod of
+ publickey ->
+ PublicKeyAlgs;
+ _ ->
+ [' ']
+ end
+ ],
+ SysDir = setup_local_hostdir(hd(PublicKeyAlgs), Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAuths, Config,
+ fun(AuthMethod,Alg) ->
+ {Opts,UsrDir} =
+ case AuthMethod of
+ publickey ->
+ {[{user_passwords, [{?USER,?BAD_PASSWD}]}],
+ setup_remote_priv_and_local_auth_keys(Alg, Config)
+ };
+ _ ->
+ {[{user_passwords, [{?USER,?PASSWD}]}],
+ new_dir(Config)
+ }
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, atom_to_list(AuthMethod)},
+ {system_dir, SysDir},
+ {user_dir, UsrDir},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Opts
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_sftp_exec_reneg_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_server_algs, Config),
+ {IP,Port} = ip_port(Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
+ fun(Tag, Alg) ->
+ ConnRes =
+ ssh:connect(IP, Port,
+ [{user,?USER},
+ {password,?PASSWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]) ,
+ test_erl_client_reneg(ConnRes, % Seems that max 10 channels may be open in sshd
+ [{exec,1},
+ {sftp,5},
+ {no_subsyst,1},
+ {setenv, 1},
+ {sftp_async,1}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_sftp_exec_reneg_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_remote_client_algs, Config),
+ UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
+ chk_all_algos(?FUNCTION_NAME, CommonAlgs, Config,
+ fun(Tag,Alg) ->
+ HostKeyAlg = case Tag of
+ public_key -> Alg;
+ _ -> 'ssh-rsa'
+ end,
+ SftpRootDir = new_dir(Config),
+ %% ct:log("Rootdir = ~p",[SftpRootDir]),
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{preferred_algorithms, [{Tag,[Alg]}]},
+ {system_dir, setup_local_hostdir(HostKeyAlg, Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PASSWD}]},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {subsystems,
+ [ssh_sftpd:subsystem_spec([{cwd,SftpRootDir},
+ {root,SftpRootDir}]),
+ {"echo_10",{ssh_echo_server,[10,[{dbg,true}]]}}
+ ]}
+ ]),
+ R = do([fun() ->
+ exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ "")
+ end,
+ fun() ->
+ sftp_tests_erl_server(Config, Host, HostPort, SftpRootDir, UserDir)
+ end
+ ]),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+send_recv_big_with_renegotiate_otp_is_client(Config) ->
+ %% Connect to the remote openssh server:
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, setup_remote_auth_keys_and_local_priv('ssh-rsa', Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Open a channel and exec the Linux 'cat' command at the openssh side.
+ %% This 'cat' will read stdin and write to stdout until an eof is read from stdin.
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch1, "cat", infinity),
+
+ %% Build big binary
+ HalfSizeBytes = 100*1000*1000,
+ Data = << <<X:32>> || X <- lists:seq(1, HalfSizeBytes div 4)>>,
+
+ %% Send the data. Must spawn a process to avoid deadlock. The client will block
+ %% until all is sent through the send window. But the server will stop receiveing
+ %% when the servers send-window towards the client is full.
+ %% Since the client can't receive before the server has received all but 655k from the client
+ %% ssh_connection:send/4 is blocking...
+ spawn_link(
+ fun() ->
+ ct:comment("Sending ~p Mbytes with renegotiation in the middle",[2*byte_size(Data)/1000000]),
+ %% ct:log("sending first ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Init renegotiation test",[]),
+ Kex1 = renegotiate_test(init, C),
+ %% ct:log("sending next ~p bytes",[byte_size(Data)]),
+ ok = ssh_connection:send(C, Ch1, Data, 10000),
+ %% ct:log("Finnish renegotiation test",[]),
+ renegotiate_test(Kex1, C),
+ %% ct:log("sending eof",[]),
+ ok = ssh_connection:send_eof(C, Ch1)
+ %%, ct:log("READY, sent ~p bytes",[2*byte_size(Data)])
+ end),
+
+ {eof,ReceivedData} =
+ loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ %%ct:log("Get more ~p",[ ExpectedSize-byte_size(Acc) ]),
+ receive
+ {ssh_cm, C, {eof,Ch}} when Ch==Ch1 ->
+ %% ct:log("eof received",[]),
+ {eof,Acc};
+
+ {ssh_cm, C, {data,Ch,0,B}} when Ch==Ch1,
+ is_binary(B) ->
+ %% ct:log("(1) Received ~p bytes (total ~p), missing ~p bytes",
+ %% [byte_size(B),
+ %% byte_size(B)+byte_size(Acc),
+ %% 2*byte_size(Data)-(byte_size(B)+byte_size(Acc))]),
+ ssh_connection:adjust_window(C, Ch1, byte_size(B)),
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>),
+
+ ExpectedData = <<Data/binary, Data/binary>>,
+ case ReceivedData of
+ ExpectedData ->
+ %% ct:log("Correct data returned",[]),
+ %% receive close messages
+ loop_until(fun(Left) -> %% ct:log("Expect: ~p",[Left]),
+ Left == []
+ end,
+ fun([Next|Rest]) ->
+ receive
+ {ssh_cm,C,Next} -> Rest
+ end
+ end,
+ [%% Already received: {eof, Ch1},
+ {exit_status,Ch1,0},
+ {closed,Ch1}]
+ ),
+ ok;
+ _ when is_binary(ReceivedData) ->
+ ct:fail("~p bytes echoed but ~p expected", [byte_size(ReceivedData), 2*byte_size(Data)])
+ end.
+
+%%--------------------------------------------------------------------
+%% Utilities ---------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%%
+%% A practical meta function
+%%
+loop_until(CondFun, DoFun, Acc) ->
+ case CondFun(Acc) of
+ true ->
+ Acc;
+ false ->
+ loop_until(CondFun, DoFun, DoFun(Acc))
+ end.
+
+%%--------------------------------------------------------------------
+%%
+%% Exec the Command in the docker. Add the arguments ExtraSshArg in the
+%% ssh command.
+%%
+%% If Expects is returned, then return 'ok', else return {fail,Msg}.
+%%
+exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
+ is_list(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg, Config),
+ ssh:close(C),
+ R.
+
+exec_from_docker(C, DestIP, DestPort, Command, Expects, ExtraSshArg, Config) when is_binary(hd(Expects)) ->
+ ExecCommand =
+ lists:concat(
+ ["sshpass -p ",?PASSWD," "
+ | case proplists:get_value(ssh_version,Config) of
+ "dropbear" ++ _ ->
+ ["dbclient -y -y -p ",DestPort," ",ExtraSshArg," ",iptoa(DestIP)," "];
+
+ _ -> %% OpenSSH or compatible
+ ["/buildroot/ssh/bin/ssh -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," -p ",DestPort," ",iptoa(DestIP)," "]
+ end]) ++ Command,
+
+ case exec(C, ExecCommand) of
+ {ok,{ExitStatus,Result}} = R when ExitStatus == 0 ->
+ case binary:match(Result, Expects) of
+ nomatch ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Bad answer"};
+ _ ->
+ ok
+ end;
+ {ok,_} = R ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Exit status =/= 0"};
+ R ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Couldn't login to host"}
+ end.
+
+
+exec(C, Cmd) ->
+ %% ct:log("~s",[Cmd]),
+ {ok,Ch} = ssh_connection:session_channel(C, 10000),
+ success = ssh_connection:exec(C, Ch, Cmd, 10000),
+ result_of_exec(C, Ch).
+
+
+result_of_exec(C, Ch) ->
+ result_of_exec(C, Ch, undefined, <<>>).
+
+result_of_exec(C, Ch, ExitStatus, Acc) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %%ct:log("CHAN ~p got *closed*",[Ch]),
+ {ok, {ExitStatus, Acc}};
+
+ {ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
+ result_of_exec(C, Ch, ExStat, Acc);
+
+ {ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got ~p",[Ch,_X]),
+ result_of_exec(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+
+ _Other ->
+ %%ct:log("OTHER: ~p",[_Other]),
+ result_of_exec(C, Ch, ExitStatus, Acc)
+
+ after 5000 ->
+ ct:log("NO MORE, received so far:~n~s",[Acc]),
+ {error, timeout}
+ end.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Loop through all {Tag,Alg} pairs in CommonAlgs, call DoTestFun(Tag,Alg) which
+%% returns one of {ok,C}, ok, or Other.
+%%
+%% The chk_all_algos returns 'ok' or {fail,FaledAlgosList}
+%%
+
+chk_all_algos(FunctionName, CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+ ct:comment("~p algorithms",[length(CommonAlgs)]),
+ %% Check each algorithm
+ Failed =
+ lists:foldl(
+ fun({Tag,Alg}, FailedAlgos) ->
+ %% ct:log("Try ~p",[Alg]),
+ case DoTestFun(Tag,Alg) of
+ {ok,C} ->
+ ssh:close(C),
+ FailedAlgos;
+ ok ->
+ FailedAlgos;
+ Other ->
+ ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
+ [{Alg,Other}|FailedAlgos]
+ end
+ end, [], CommonAlgs),
+ ct:pal("~s", [format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed)]),
+ case Failed of
+ [] ->
+ ok;
+ _ ->
+ {fail, Failed}
+ end.
+
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Call all Funs as Fun() which returns 'ok', {ok,C} or Other.
+%%% do/1 returns 'ok' or the first encountered value that is not
+%%% successful.
+%%%
+
+do(Funs) ->
+ do(Funs, 1).
+
+do([Fun|Funs], N) ->
+ case Fun() of
+ ok ->
+ %% ct:log("Fun ~p ok",[N]),
+ do(Funs, N-1);
+ {ok,C} ->
+ %% ct:log("Fun ~p {ok,C}",[N]),
+ ssh:close(C),
+ do(Funs, N-1);
+ Other ->
+ ct:log("Fun ~p FAILED:~n~p",[N, Other]),
+ Other
+ end;
+
+do([], _) ->
+ %% ct:log("All Funs ok",[]),
+ ok.
+
+%%--------------------------------------------------------------------
+%%
+%% Functions to set up local and remote host's and user's keys and directories
+%%
+
+setup_local_hostdir(KeyAlg, Config) ->
+ setup_local_hostdir(KeyAlg, new_dir(Config), Config).
+setup_local_hostdir(KeyAlg, HostDir, Config) ->
+ {ok, {Priv,Publ}} = host_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public key
+ DstFile = filename:join(HostDir, dst_filename(host,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ HostDir.
+
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, Config) ->
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public keys
+ DstFile = filename:join(UserDir, dst_filename(user,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ %% Remote auth_methods with public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PASSWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ ok = ssh_sftp:write_file(Ch, ".ssh/authorized_keys", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh/authorized_keys", #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, Config) ->
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local auth_methods with public key
+ AuthKeyFile = filename:join(UserDir, "authorized_keys"),
+ ok = file:write_file(AuthKeyFile, Publ),
+ %% Remote private and public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PASSWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ DstFile = filename:join(".ssh", dst_filename(user,KeyAlg)),
+ ok = ssh_sftp:write_file(Ch, DstFile, Priv),
+ ok = ssh_sftp:write_file_info(Ch, DstFile, #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file(Ch, DstFile++".pub", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+user_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("users_keys", user, Config, KeyAlg).
+host_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("host_keys", host, Config, KeyAlg).
+
+priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
+ KeyDir = filename:join(proplists:get_value(data_dir,Config), KeySubDir),
+ {ok,Priv} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg))),
+ {ok,Publ} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg)++".pub")),
+ {ok, {Priv,Publ}}.
+
+
+%%%---------------- The default filenames
+src_filename(user, 'ssh-rsa' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+src_filename(user, 'ssh-dss' ) -> "id_dsa";
+src_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa256";
+src_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa384";
+src_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa521";
+src_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+src_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+src_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key256";
+src_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key384";
+src_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key521".
+
+dst_filename(user, 'ssh-rsa' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+dst_filename(user, 'ssh-dss' ) -> "id_dsa";
+dst_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa";
+dst_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
+
+
+%%--------------------------------------------------------------------
+%%
+%% Format the result table for chk_all_algos/4
+%%
+format_result_table_use_all_algos(FunctionName, Config, CommonAlgs, Failed) ->
+ %% Write a nice table with the result
+ AlgHead = 'Algorithm',
+ AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
+ {ResultTable,_} =
+ lists:mapfoldl(
+ fun({T,A}, Tprev) ->
+ Tag = case T of
+ Tprev -> "";
+ _ -> io_lib:format('~s~n',[T])
+ end,
+ {io_lib:format('~s ~*s ~s~n',
+ [Tag, -AlgWidth, A,
+ case proplists:get_value(A,Failed) of
+ undefined -> "(ok)";
+ Err -> io_lib:format("<<<< FAIL <<<< ~p",[Err])
+ end]),
+ T}
+ end, undefined, CommonAlgs),
+
+ Vssh = proplists:get_value(ssh_version,Config,""),
+ io_lib:format("~nResults of ~p, Peer version: ~s~n~n"
+ "Tag ~*s Result~n"
+ "=====~*..=s=======~n~s"
+ ,[FunctionName, Vssh,
+ -AlgWidth, AlgHead,
+ AlgWidth, "", ResultTable]).
+
+%%--------------------------------------------------------------------
+%%
+%% Docker handling: start_docker/1 and stop_docker/1
+%%
+start_docker(Ver) ->
+ Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
+ Id0 = os:cmd(Cmnd),
+ ct:log("Ver = ~p, Cmnd ~p~n-> ~p",[Ver,Cmnd,Id0]),
+ case is_docker_sha(Id0) of
+ true ->
+ Id = hd(string:tokens(Id0, "\n")),
+ IP = ip(Id),
+ Port = 1234,
+ {ok, {Ver,{IP,Port},Id}};
+ false ->
+ throw(cant_start_docker)
+ end.
+
+
+stop_docker({_Ver,_,Id}) ->
+ Cmnd = lists:concat(["docker kill ",Id]),
+ os:cmd(Cmnd).
+
+is_docker_sha(L) ->
+ lists:all(fun(C) when $a =< C,C =< $z -> true;
+ (C) when $0 =< C,C =< $9 -> true;
+ ($\n) -> true;
+ (_) -> false
+ end, L).
+
+%%--------------------------------------------------------------------
+%%
+%% Misc docker info functions
+
+ip_port(Config) ->
+ {_Ver,{IP,Port},_} = proplists:get_value(id,Config),
+ {IP,Port}.
+
+port_mapped_to(Id) ->
+ Cmnd = lists:concat(["docker ps --format \"{{.Ports}}\" --filter id=",Id]),
+ [_, PortStr | _] = string:tokens(os:cmd(Cmnd), ":->/"),
+ list_to_integer(PortStr).
+
+ip(Id) ->
+ Cmnd = lists:concat(["docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ",
+ Id]),
+ IPstr0 = os:cmd(Cmnd),
+ ct:log("Cmnd ~p~n-> ~p",[Cmnd,IPstr0]),
+ IPstr = hd(string:tokens(IPstr0, "\n")),
+ {ok,IP} = inet:parse_address(IPstr),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Normalize the host returned from ssh_test_lib
+
+iptoa({0,0,0,0}) -> inet_parse:ntoa(host_ip());
+iptoa(IP) -> inet_parse:ntoa(IP).
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+%%--------------------------------------------------------------------
+%%
+%% Create a new fresh directory or clear an existing one
+%%
+
+new_dir(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SubDirName = integer_to_list(erlang:system_time()),
+ Dir = filename:join(PrivDir, SubDirName),
+ case file:read_file_info(Dir) of
+ {error,enoent} ->
+ ok = file:make_dir(Dir),
+ Dir;
+ _ ->
+ timer:sleep(25),
+ new_dir(Config)
+ end.
+
+clear_dir(Dir) ->
+ delete_all_contents(Dir),
+ {ok,[]} = file:list_dir(Dir),
+ Dir.
+
+delete_all_contents(Dir) ->
+ {ok,Fs} = file:list_dir(Dir),
+ lists:map(fun(F0) ->
+ F = filename:join(Dir, F0),
+ case filelib:is_file(F) of
+ true ->
+ file:delete(F);
+ false ->
+ case filelib:is_dir(F) of
+ true ->
+ delete_all_contents(F),
+ file:del_dir(F);
+ false ->
+ ct:log("Neither file nor dir: ~p",[F])
+ end
+ end
+ end, Fs).
+
+%%--------------------------------------------------------------------
+%%
+%% Find the intersection of algoritms for otp ssh and the docker ssh.
+%% Returns {ok, ServerHello, Server, ClientHello, Client} where Server are the algorithms common
+%% with the docker server and analogous for Client.
+%%
+%% Client may be undefined if no usable client is found.
+%%
+%% Both Server and Client are lists of {Tag,AlgName}.
+%%
+
+common_algs(Config, IP, Port) ->
+ case remote_server_algs(IP, Port) of
+ {ok, {ServerHello, RemoteServerKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(ServerHello)),
+ ct:log("Remote server:~n~p~n~p",[ServerHello, RemoteServerAlgs]),
+ case remote_client_algs(Config) of
+ {ok,{ClientHello,RemoteClientKexInit}} ->
+ RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
+ Client = find_common_algs(RemoteClientAlgs,
+ use_algorithms(ClientHello)),
+ ct:log("Remote client:~n~p~n~p",[ClientHello, RemoteClientAlgs]),
+ {ok, ServerHello, Server, ClientHello, Client};
+ {error,_} =TO ->
+ ct:log("Remote client algs can't be found: ~p",[TO]),
+ {ok, ServerHello, Server, undefined, undefined};
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end.
+
+
+chk_hellos(Hs, Str) ->
+ lists:foldl(
+ fun(H, Acc) ->
+ try binary:split(H, <<"-">>, [global])
+ of
+ %% [<<"SSH">>,<<"2.0">>|_] ->
+ %% Acc;
+ [<<"SSH">>,OldVer = <<"1.",_/binary>>|_] ->
+ io_lib:format("~s, Old SSH ver ~s",[Acc,OldVer]);
+ _ ->
+ Acc
+ catch
+ _:_ ->
+ Acc
+ end
+ end, Str, Hs).
+
+
+find_common_algs(Remote, Local) ->
+ [{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
+ ssh_test_lib:intersection(Remote,
+ Local)),
+ V <- Vs].
+
+
+use_algorithms(RemoteHelloBin) ->
+ MyAlgos = ssh:chk_algos_opts(
+ [{modify_algorithms,
+ [{append,
+ [{kex,['diffie-hellman-group1-sha1']}
+ ]}
+ ]}
+ ]),
+ ssh_transport:adjust_algs_for_peer_version(binary_to_list(RemoteHelloBin)++"\r\n",
+ MyAlgos).
+
+kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
+ server_host_key_algorithms = PubKey,
+ encryption_algorithms_client_to_server = CipherC2S,
+ encryption_algorithms_server_to_client = CipherS2C,
+ mac_algorithms_client_to_server = MacC2S,
+ mac_algorithms_server_to_client = MacS2C,
+ compression_algorithms_client_to_server = CompC2S,
+ compression_algorithms_server_to_client = CompS2C
+ }) ->
+ [{kex, ssh_test_lib:to_atoms(Kex)},
+ {public_key, ssh_test_lib:to_atoms(PubKey)},
+ {cipher, [{client2server,ssh_test_lib:to_atoms(CipherC2S)},
+ {server2client,ssh_test_lib:to_atoms(CipherS2C)}]},
+ {mac, [{client2server,ssh_test_lib:to_atoms(MacC2S)},
+ {server2client,ssh_test_lib:to_atoms(MacS2C)}]},
+ {compression, [{client2server,ssh_test_lib:to_atoms(CompC2S)},
+ {server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
+
+
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote server
+%%
+%% Connect with tcp to the server, send a hello and read the returned
+%% server hello and kexinit message.
+%%
+remote_server_algs(IP, Port) ->
+ case try_gen_tcp_connect(IP, Port, 5) of
+ {ok,S} ->
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ receive_hello(S);
+ {error,Error} ->
+ {error,Error}
+ end.
+
+try_gen_tcp_connect(IP, Port, N) when N>0 ->
+ case gen_tcp:connect(IP, Port, [binary]) of
+ {ok,S} ->
+ {ok,S};
+ {error,_Error} when N>1 ->
+ receive after 1000 -> ok end,
+ try_gen_tcp_connect(IP, Port, N-1);
+ {error,Error} ->
+ {error,Error}
+ end;
+try_gen_tcp_connect(_, _, _) ->
+ {error, "No contact"}.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Find the algorithms supported by the remote client
+%%
+%% Set up a fake ssh server and make the remote client connect to it. Use
+%% hello message and the kexinit message.
+%%
+remote_client_algs(Config) ->
+ Parent = self(),
+ Ref = make_ref(),
+ spawn(
+ fun() ->
+ {ok,Sl} = gen_tcp:listen(0, [binary]),
+ {ok,{IP,Port}} = inet:sockname(Sl),
+ Parent ! {addr,Ref,IP,Port},
+ {ok,S} = gen_tcp:accept(Sl),
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ Parent ! {Ref,receive_hello(S)}
+ end),
+ receive
+ {addr,Ref,IP,Port} ->
+ spawn(fun() ->
+ exec_from_docker(Config, IP, Port,
+ "howdy.\r\n",
+ [<<"howdy">>],
+ "")
+ end),
+ receive
+ {Ref, Result} ->
+ Result
+ after 5000 ->
+ {error, {timeout,2}}
+ end
+ after 5000 ->
+ {error, {timeout,1}}
+ end.
+
+
+%%% Receive a few packets from the remote server or client and find what is supported:
+
+receive_hello(S) ->
+ try
+ receive_hello(S, <<>>)
+ of
+ Result ->
+ Result
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
+
+receive_hello(S, Ack) ->
+ %% The Ack is to collect bytes until the full message is received
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
+ [Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
+ %% ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
+ %% ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Bin] when size(Bin) < 256 ->
+ %% ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ %% [io_lib:format('~2.16.0b ',[C])
+ %% || C <- binary_to_list(Bin0)
+ %% ]
+ %% ]),
+ receive_hello(S, Bin0);
+
+ _ ->
+ ct:log("Bad hello string (line ~p, ~p chars):~n~s~n~s",[?LINE,size(Bin0),Bin0,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ ct:fail("Bad hello string received")
+ end;
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for hello!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+receive_kexinit(_S, <<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>)
+ when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decodeing get out of sync
+ size(PayloadAndPadding) >= (PacketLen-1) % Need more bytes?
+ ->
+ ct:log("Has all ~p packet bytes",[PacketLen]),
+ PayloadLen = PacketLen - PaddingLen - 1,
+ <<Payload:PayloadLen/binary, _Padding:PaddingLen/binary>> = PayloadAndPadding,
+ ssh_message:decode(Payload);
+
+receive_kexinit(S, Ack) ->
+ ct:log("Has ~p bytes, need more",[size(Ack)]),
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ receive_kexinit(S, <<Ack/binary, Bin0/binary>>);
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for kexinit!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+%%%----------------------------------------------------------------
+%%% Test of sftp from the OpenSSH client side
+%%%
+
+sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
+ try
+ Cmnds = prepare_local_directory(ServerRootDir),
+ call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
+ check_local_directory(ServerRootDir)
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {Class,Error,ST}}
+ end.
+
+
+prepare_local_directory(ServerRootDir) ->
+ file:write_file(filename:join(ServerRootDir,"tst1"),
+ <<"Some test text">>
+ ),
+ ["get tst1",
+ "put tst1 tst2",
+ "put tst1 tst3",
+ "rename tst1 ex_tst1",
+ "rm tst3",
+ "mkdir mydir",
+ "cd mydir",
+ "put tst1 file_1",
+ "put tst1 unreadable_file",
+ "chmod 222 unreadable_file",
+ "exit"].
+
+check_local_directory(ServerRootDir) ->
+ case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of
+ ["ex_tst1","mydir","tst2"] ->
+ {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")),
+ case file:read_file(filename:join(ServerRootDir,"tst2")) of
+ {ok,Expect} ->
+ case lists:sort(ok(file:list_dir(filename:join(ServerRootDir,"mydir"))) -- [".",".."]) of
+ ["file_1","unreadable_file"] ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","file_1"])) of
+ {ok,Expect} ->
+ case file:read_file(filename:join([ServerRootDir,"mydir","unreadable_file"])) of
+ {error,_} ->
+ ok;
+ {ok,_} ->
+ {error, {could_read_unreadable,"mydir/unreadable_file"}}
+ end;
+ {ok,Other} ->
+ ct:log("file_1:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"mydir/file_1"}}
+ end;
+ Other ->
+ ct:log("Directory ~s~n~p",[filename:join(ServerRootDir,"mydir"),Other]),
+ {error,{bad_dir_contents,"mydir"}}
+ end;
+ {ok,Other} ->
+ ct:log("tst2:~n~s~nExpected:~n~s",[Other,Expect]),
+ {error, {bad_contents_in_file,"tst2"}}
+ end;
+ ["tst1"] ->
+ {error,{missing_file,"tst2"}};
+ Other ->
+ ct:log("Directory ~s~n~p",[ServerRootDir,Other]),
+ {error,{bad_dir_contents,"/"}}
+ end.
+
+call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PASSWD},
+ {user_dir, UserDir},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+
+ %% Make commands for "expect" in the docker:
+ PreExpectCmnds = ["spawn /buildroot/ssh/bin/sftp -oPort="++integer_to_list(ServerPort)++
+ " -oCheckHostIP=no -oStrictHostKeyChecking=no " ++
+ iptoa(ServerIP)++"\n"
+ ],
+ PostExpectCmnds= [],
+ ExpectCmnds =
+ PreExpectCmnds ++
+ ["expect \"sftp>\" {send \""++Cmnd++"\n\"}\n" || Cmnd <- Cmnds] ++
+ PostExpectCmnds,
+
+ %% Make an commands file in the docker
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ ok = ssh_sftp:write_file(Ch, "commands", erlang:iolist_to_binary(ExpectCmnds)),
+ ok = ssh_sftp:stop_channel(Ch),
+
+ %% Call expect in the docker
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+ Kex1 = renegotiate_test(init, C),
+ success = ssh_connection:exec(C, Ch1, "expect commands", infinity),
+
+ renegotiate_test(Kex1, C),
+ recv_log_msgs(C, Ch1),
+
+ %% Done.
+ ssh:close(C).
+
+recv_log_msgs(C, Ch) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %% ct:log("Channel closed ~p",[{closed,1}]),
+ ok;
+ {ssh_cm,C,{data,Ch,1,Msg}} ->
+ ct:log("*** ERROR from docker:~n~s",[Msg]),
+ recv_log_msgs(C, Ch);
+ {ssh_cm,C,_Msg} ->
+ %% ct:log("Got ~p",[_Msg]),
+ recv_log_msgs(C, Ch)
+ end.
+
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+%%%
+%%% Tests from the Erlang client side
+%%%
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
+test_erl_client_reneg({ok,C}, Spec) ->
+ %% Start the test processes on the connection C:
+ Parent = self(),
+ Pids = [spawn(
+ fun() ->
+ Parent ! {self(), TestType, Id, one_test_erl_client(TestType,Id,C)}
+ end
+ )
+ || {TestType,N} <- Spec,
+ Id <- lists:seq(1,N)],
+
+ Kex1 = renegotiate_test(init, C),
+
+ %% Collect the results:
+ case lists:filter(
+ fun(R) -> R=/=ok end,
+ [receive
+ {Pid,_TestType,_Id,ok} ->
+ %% ct:log("Test ~p:~p passed!", [_TestType,_Id]),
+ ok;
+ {Pid,TestType,Id,OtherResult} ->
+ ct:log("~p:~p ~p ~p~n~p",[?MODULE,?LINE,TestType,Id,OtherResult]),
+ {error,TestType,Id}
+ end || Pid <- Pids])
+ of
+ [] ->
+ renegotiate_test(Kex1, C),
+ {ok,C};
+ Other ->
+ renegotiate_test(Kex1, C),
+ Other
+ end;
+
+test_erl_client_reneg(Error, _) ->
+ Error.
+
+
+one_test_erl_client(exec, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ success = ssh_connection:exec(C, Ch, "echo Hi there", 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,<<"Hi there\n">>} ->
+ ok;
+ Other ->
+ ct:pal("exec Got other ~p", [Other]),
+ {error, {exec,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(no_subsyst, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ case ssh_connection:subsystem(C, Ch, "foo", infinity) of
+ failure ->
+ ok;
+ Other ->
+ ct:pal("no_subsyst Got other ~p", [Other]),
+ {error, {no_subsyst,Id,bad_ret,Other,undefined}}
+ end;
+
+one_test_erl_client(setenv, Id, C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ Var = "ENV_TEST",
+ Value = lists:concat(["env_test_",Id,"_",erlang:system_time()]),
+ Env = case ssh_connection:setenv(C, Ch, Var, Value, infinity) of
+ success -> binary_to_list(Value++"\n");
+ failure -> <<"\n">>
+ end,
+ success = ssh_connection:exec(C, Ch, "echo $"++Var, 5000),
+ case loop_until(fun({eof,_}) -> true;
+ (_ ) -> false
+ end,
+ fun(Acc) ->
+ receive
+ {ssh_cm, C, {eof,Ch}} ->
+ {eof,Acc};
+ {ssh_cm, C, {data,Ch,0,B}} when is_binary(B) ->
+ <<Acc/binary, B/binary>>
+ end
+ end,
+ <<>>) of
+ {eof,Env} ->
+ ok;
+ Other ->
+ ct:pal("setenv Got other ~p", [Other]),
+ {error, {setenv,Id,bad_msg,Other,undefined}}
+ end;
+
+one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
+ try
+ {ok,Ch} = ssh_sftp:start_channel(C, [{timeout,10000}]),
+ %% A new fresh name of a new file tree:
+ RootDir = lists:concat(["r_",Id,"_",erlang:system_time()]),
+ %% Check that it does not exist:
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ %% Create it:
+ ok = ssh_sftp:make_dir(Ch, RootDir),
+ {ok, #file_info{type=directory, access=read_write}} = ssh_sftp:read_file_info(Ch, RootDir),
+ R = do_sftp_tests_erl_client(SFTP, C, Ch, Id, RootDir),
+ catch ssh_sftp:stop_channel(Ch),
+ R
+ catch
+ Class:Error ->
+ ST = erlang:get_stacktrace(),
+ {error, {SFTP,Id,Class,Error,ST}}
+ end.
+
+
+
+do_sftp_tests_erl_client(sftp_async, _C, Ch, _Id, RootDir) ->
+ FileName1 = "boring_name",
+ F1 = filename:join(RootDir, FileName1),
+ %% Open a new handle and start writing:
+ {ok,Handle1} = ssh_sftp:open(Ch, F1, [write,binary]),
+ {async,Aref1} = ssh_sftp:awrite(Ch, Handle1, <<0:250000/unsigned-unit:8>>),
+ wait_for_async_result(Aref1);
+
+do_sftp_tests_erl_client(sftp, _C, Ch, _Id, RootDir) ->
+ FileName0 = "f0",
+ F0 = filename:join(RootDir, FileName0),
+
+ %% Create and write a file:
+ ok = ssh_sftp:write_file(Ch,
+ F0 = filename:join(RootDir, FileName0),
+ Data0 = mkbin(1234,240)),
+ {ok,Data0} = ssh_sftp:read_file(Ch, F0),
+ {ok, #file_info{type=regular, access=read_write, size=1234}} = ssh_sftp:read_file_info(Ch, F0),
+
+ %% Re-write:
+ {ok,Handle0} = ssh_sftp:open(Ch, F0, [write,read,binary]),
+ ok = ssh_sftp:pwrite(Ch, Handle0, 16, Data0_1=mkbin(10,255)),
+
+ <<B1:16/binary, _:10/binary, B2:(1234-26)/binary>> = Data0,
+ FileContents = <<B1:16/binary, Data0_1:10/binary, B2:(1234-26)/binary>>,
+
+ <<_:1/binary, Part:25/binary, _/binary>> = FileContents,
+ {ok, Part} = ssh_sftp:pread(Ch, Handle0, 1, 25),
+
+ %% Check:
+ {ok, FileContents} = ssh_sftp:pread(Ch, Handle0, 0, 1234),
+ ok = ssh_sftp:close(Ch, Handle0),
+
+ %% Check in another way:
+ {ok, FileContents} = ssh_sftp:read_file(Ch, F0),
+
+ %% Remove write access rights and check that it can't be written:
+ ok = ssh_sftp:write_file_info(Ch, F0, #file_info{mode=8#400}), %read}),
+ {ok, #file_info{type=regular, access=read}} = ssh_sftp:read_file_info(Ch, F0),
+ {error,permission_denied} = ssh_sftp:write_file(Ch, F0, mkbin(10,14)),
+
+ %% Test deletion of file and dir:
+ [FileName0] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:delete(Ch, F0),
+ [] = ok(ssh_sftp:list_dir(Ch, RootDir)) -- [".", ".."],
+ ok = ssh_sftp:del_dir(Ch, RootDir),
+ false = lists:member(RootDir, ok(ssh_sftp:list_dir(Ch, "."))),
+ ok.
+
+
+wait_for_async_result(Aref) ->
+ receive
+ {async_reply, Aref, Result} ->
+ Result
+ after
+ 60000 ->
+ timeout
+ end.
+
+
+mkbin(Size, Byte) ->
+ list_to_binary(lists:duplicate(Size,Byte)).
+
+ok({ok,X}) -> X.
+
+%%%----------------------------------------------------------------
+renegotiate_test(init, ConnectionRef) ->
+ Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
+ ssh_connection_handler:renegotiate(ConnectionRef),
+ %%ct:log("Renegotiate test initiated!",[]),
+ Kex1;
+
+renegotiate_test(Kex1, ConnectionRef) ->
+ case ssh_test_lib:get_kex_init(ConnectionRef) of
+ Kex1 ->
+ ct:log("Renegotiate test failed, Kex1 == Kex2!",[]),
+ error(renegotiate_failed);
+ _ ->
+ %% ct:log("Renegotiate test passed!",[]),
+ ok
+ end.
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
new file mode 100755
index 0000000000..1cb7bf33e1
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+UBUNTU_VER=${1:-16.04}
+
+USER=sshtester
+PWD=foobar
+
+docker build \
+ -t ubuntubuildbase \
+ --build-arg https_proxy=$HTTPS_PROXY \
+ --build-arg http_proxy=$HTTP_PROXY \
+ - <<EOF
+
+ FROM ubuntu:$UBUNTU_VER
+ WORKDIR /buildroot
+
+ # Prepare for installing OpenSSH
+ RUN apt-get update
+ RUN apt-get upgrade -y
+ RUN apt-get -y install apt-utils
+ RUN apt-get -y install build-essential zlib1g-dev
+ RUN apt-get -y install sudo iputils-ping tcptraceroute net-tools
+ RUN apt-get -y install sshpass expect
+ RUN apt-get -y install libpam0g-dev
+
+ # A user for the tests
+ RUN (echo $PWD; echo $PWD; echo; echo; echo; echo; echo; echo ) | adduser $USER
+ RUN adduser $USER sudo
+
+ # Prepare the privsep preauth environment for openssh
+ RUN mkdir -p /var/empty
+ RUN chown root:sys /var/empty
+ RUN chmod 755 /var/empty
+ RUN groupadd -f sshd
+ RUN ls /bin/false
+ RUN id -u sshd 2> /dev/null || useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd
+
+EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
new file mode 100755
index 0000000000..85973081d0
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ WORKDIR /buildroot
+
+ RUN apt-get -y update
+ RUN apt-get -y upgrade
+ RUN apt-get -y install openssh-sftp-server
+%% RUN echo 81 | apt-get -y install dropbear
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh-dropbear -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
new file mode 100755
index 0000000000..d98c0cfaa3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-dropbear-ssh-run
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# ./create-dropbear-ssh-run
+
+VER=v2016.72
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-ssh-dropbear-installed:${VER}
+
+ WORKDIR /buildroot
+
+ CMD dropbear -F -p 1234
+
+EOF
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:dropbear${VER} -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
new file mode 100755
index 0000000000..2e08408841
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+# ./create-image openssh 7.3p1 openssl 1.0.2m
+
+set -x
+
+case $1 in
+ openssh)
+ FAMssh=openssh
+ VERssh=$2
+ PFX=https://ftp.eu.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo "Unsupported: $1"
+ exit
+esac
+
+FAMssl=$3
+VERssl=$4
+
+VER=${FAMssh}${VERssh}-${FAMssl}${VERssl}
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-${FAMssl}:${VERssl}
+
+ LABEL openssh-version=${VER}
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ # Build and install
+
+ WORKDIR ${FAMssh}-${VERssh}
+
+ # Probably VERY OpenSSH dependent...:
+ RUN ./configure --without-pie \
+ --prefix=/buildroot/ssh \
+ --with-ssl-dir=/buildroot/ssl \
+ --with-pam \
+ LDFLAGS=-Wl,-R/buildroot/ssl/lib
+ RUN make
+ RUN make install
+ RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
+
+ RUN echo Built ${VER}
+
+ # Start the daemon, but keep it in foreground to avoid killing the container
+ CMD /buildroot/ssh/sbin/sshd -D -p 1234
+
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VERssh$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
new file mode 100755
index 0000000000..4ab2a8bddc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# ./create-image openssl 1.0.2m
+
+case "$1" in
+ "openssl")
+ FAM=openssl
+ VER=$2
+ PFX=https://www.openssl.org/source/openssl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ "libressl")
+ FAM=libressl
+ VER=$2
+ PFX=https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo No lib type
+ exit
+ ;;
+esac
+
+case $1$2 in
+ openssl0.9.8[a-l])
+ CONFIG_FLAGS=no-asm
+ ;;
+ *)
+ CONFIG_FLAGS=
+ ;;
+esac
+
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ LABEL version=$FAM-$VER
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ WORKDIR ${FAM}-${VER}
+
+ RUN ./config --prefix=/buildroot/ssl ${CONFIG_FLAGS}
+
+ RUN make
+ RUN make install_sw
+
+ RUN echo Built ${FAM}-${VER}
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VER$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-$FAM:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
new file mode 100755
index 0000000000..0dcf8cb570
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+UBUNTU_VERSION=16.04
+
+SSH_SSL_VERSIONS=(\
+ openssh 4.4p1 openssl 0.9.8c \
+ openssh 4.5p1 openssl 0.9.8m \
+ openssh 5.0p1 openssl 0.9.8za \
+ openssh 6.2p2 openssl 0.9.8c \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0a \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ \
+ openssh 7.6p1 libressl 2.6.4 \
+ )
+
+if [ "x$1" == "x-b" ]
+then
+ shift
+ SKIP_CREATE_BASE=true
+fi
+
+WHAT_TO_DO=$1
+
+function create_one_image ()
+{
+ SSH_FAM=$1
+ SSH_VER=$2
+ SSL_FAM=$3
+ SSL_VER=$4
+
+ [ "x$SKIP_CREATE_BASE" == "xtrue" ] || ./create-base-image || (echo "Create base failed." >&2; exit 1)
+ ./create-ssl-image $SSL_FAM $SSL_VER \
+ || (echo "Create $SSL_FAM $SSL_VER failed." >&2; exit 2)
+
+ ./create-ssh-image $SSH_FAM $SSH_VER $SSL_FAM $SSL_VER \
+ || (echo "Create $SSH_FAM $SSH_VER on $SSL_FAM $SSL_VER failed." >&2; exit 3)
+}
+
+
+case ${WHAT_TO_DO} in
+ list)
+ ;;
+ listatoms)
+ PRE="["
+ POST="]"
+ C=\'
+ COMMA=,
+ ;;
+ build_one)
+ if [ $# != 5 ]
+ then
+ echo "$0 build_one openssh SSH_ver openssl SSL_ver " && exit
+ else
+ create_one_image $2 $3 $4 $5
+ exit
+ fi
+ ;;
+ build_all)
+ ;;
+ *)
+ echo "$0 [-b] list | listatoms | build_one openssh SSH_ver openssl SSL_ver | build_all" && exit
+ ;;
+esac
+
+
+echo -n $PRE
+i=0
+while [ "x${SSH_SSL_VERSIONS[i]}" != "x" ]
+do
+ case ${WHAT_TO_DO} in
+ list*)
+ [ $i -eq 0 ] || echo $COMMA
+ echo -n $C${SSH_SSL_VERSIONS[$i]}${SSH_SSL_VERSIONS[$(( $i + 1 ))]}-${SSH_SSL_VERSIONS[$(( $i + 2 ))]}${SSH_SSL_VERSIONS[$(( $i + 3 ))]}$C
+ ;;
+ build_all)
+ create_one_image ${SSH_SSL_VERSIONS[$i]} ${SSH_SSL_VERSIONS[$(( $i + 1 ))]} ${SSH_SSL_VERSIONS[$(( $i + 2 ))]} ${SSH_SSL_VERSIONS[$(( $i + 3 ))]}
+ ;;
+ esac
+
+ i=$(( $i + 4 ))
+done
+echo $POST
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
new file mode 100644
index 0000000000..8b2354a7ea
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQDlXDEddxFbTtPsu2bRTbSONFVKMxe430iqBoXoKK2Gyhlqn7J8
+SRGlmvTN7T06+9iFqgJi+x+dlSJGlNEY/v67Z8C7rWfJynYuRier4TujLwP452RT
+YrsnCq47pGJXHb9xAWr7UGMv85uDrECUiIdK4xIrwpW/gMb5zPSThDGNiwIVANts
+B9nBX0NH/B0lXthVCg2jRSkpAoGAIS3vG8VmjQNYrGfdcdvQtGubFXs4jZJO6iDe
+9u9/O95dcnH4ZIL4y3ZPHbw73dCKXFe5NlqI/POmn3MyFdpyqH5FTHWB/aAFrma6
+qo00F1mv83DkQCEfg6fwE/SaaBjDecr5I14hWOtocpYqlY1/x1aspahwK6NLPp/D
+A4aAt78CgYAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7Xmyq
+blfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZ
+iEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpgIURgW8SZGj
+X0mMkMJv/Ltdt0gYx60=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..9116493472
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOVcMR13EVtO0+y7ZtFNtI40VUozF7jfSKoGhegorYbKGWqfsnxJEaWa9M3tPTr72IWqAmL7H52VIkaU0Rj+/rtnwLutZ8nKdi5GJ6vhO6MvA/jnZFNiuycKrjukYlcdv3EBavtQYy/zm4OsQJSIh0rjEivClb+AxvnM9JOEMY2LAAAAFQDbbAfZwV9DR/wdJV7YVQoNo0UpKQAAAIAhLe8bxWaNA1isZ91x29C0a5sVeziNkk7qIN7273873l1ycfhkgvjLdk8dvDvd0IpcV7k2Woj886afczIV2nKofkVMdYH9oAWuZrqqjTQXWa/zcORAIR+Dp/AT9JpoGMN5yvkjXiFY62hyliqVjX/HVqylqHAro0s+n8MDhoC3vwAAAIAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7XmyqblfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZiEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..5ed2b361cc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILwQIf+Jul+oygeJn7cBSvn2LGqnW1ZfiHDQMDXZ96mooAoGCCqGSM49
+AwEHoUQDQgAEJUo0gCIhXEPJYvxec23IAjq7BjV1xw8deI8JV9vL5BMCZNhyj5Vt
+NbFPbKPuL/Sikn8p4YP/5y336ug7szvYrg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..240387d901
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCVKNIAiIVxDyWL8XnNtyAI6uwY1dccPHXiPCVfby+QTAmTYco+VbTWxT2yj7i/0opJ/KeGD/+ct9+roO7M72K4= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..9d31d75cd5
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBw+P1sic2i41wTGQgjyUlBtxQfnY77L8TFcDngoRiVrbCugnDrioNo
+JogqymWhSC+gBwYFK4EEACKhZANiAATwaqEp3vyLzfb08kqgIZLv/mAYJyGD+JMt
+f11OswGs3uFkbHZOErFCgeLuBvarSTAFkOlMR9GZGaDEfcrPBTtvKj+jEaAvh6yr
+JxS97rtwk2uadDMem2x4w9Ga4jw4S8E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..cca85bda72
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPBqoSne/IvN9vTySqAhku/+YBgnIYP4ky1/XU6zAaze4WRsdk4SsUKB4u4G9qtJMAWQ6UxH0ZkZoMR9ys8FO28qP6MRoC+HrKsnFL3uu3CTa5p0Mx6bbHjD0ZriPDhLwQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..b698be1ec9
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBtGVvyn7kGX7BfWAYHK2ZXmhWscTOV0J0mAfab0u0ZMw0id2a3O9s
+sBjJoCqoAXTJ7d/OUw85qqQNDE5GDQpDFq6gBwYFK4EEACOhgYkDgYYABAHPWfUD
+tQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJ
+yO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuh
+RPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..d181d30d69
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHPWfUDtQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJyO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuhRPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
new file mode 100644
index 0000000000..84096298ca
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuC6uxC0P8voYQCrwJzczo9iSiwsovPv4etd2BLnu8cKWdnjR
+34tWvtguw2kO+iDyt4hFGGfDBQf2SXl+ZEsE2N1RlSp5A73me2byw/L4MreX2rbU
+TwyNXF3TBvKb3Gbpx7PoiB9frcb9RCMxtypBvGQD6bx6h5UWKuSkYzARaRLv3kbB
+swcqfrA3PfWybkIoaa2RO1Ca86u6K0v+a4r0OfRxTnghuakZkH6CD7+uU3irliPI
+UFt2wTI/qWmnDrMFh4RffToHK0QZHXdkq4ama5kRZdZ0svSorxqkl8EWGPhReoUj
+Yrz0bCNevSlDxHCxLi8epRxuv+AhZHW0YdMCCwIDAQABAoIBAHUyj1aZbfqolWHP
+cL0jbSKnHqiHU0bd9sED9T8QqTEBJwj/3Fwop+wMV8VURol3CbsrZPwgmoHLDTa3
+rmtXKSBtxAns2tA8uDpxyaxSIQj0thYgHHyoehL6SNu06OSYP84pdp+XhyRm6KXA
+11O7+dRMuAi1PCql/VMR5mCPJ6T5qWAVYHFyEBvMm4q5yYSRSPaAaZHC6WbEsxHN
+jGzcyl3tvmOyN0+M7v0U86lQ+H2tSXH+nQg/Ig6hWgFGg8AYoos/9yUGOY+e9bUE
+serYdsuiyxBfo4CgoSeDsjwNp1lAZ5UOrIDdRqK9C8jGVkHDzwfmmtczWXkVVzGZ
+Bd05izECgYEA31yHzSA/umamyZAQbi/5psk1Fc5m6MzsgmJmB6jm7hUZ0EbpSV4C
+6b1nOrk/IAtA12rvDHgWy0zpkJbC5b03C77RnBgTRgLQyolrcpLDJ47+Kxf/AHGk
+m63KaCpwZEQ4f9ARBXySD/jNoW9gz5S6Xa3RnHOC70DsIIk5VOCjWk0CgYEA0xiM
+Ay27PJcbAG/4tnjH8DZfHb8SULfnfUj8kMe3V2SDPDWbhY8zheo45wTBDRflFU5I
+XyGmfuZ7PTTnFVrJz8ua3mAMOzkFn4MmdaRCX9XtuE4YWq3lFvxlrJvfXSjEL0km
+8UwlhJMixaEPqFQjsKc9BHwWKRiKcF4zFQ1DybcCgYB46yfdhYLaj23lmqc6b6Bw
+iWbCql2N1DqJj2l65hY2d5fk6C6s+EcNcOrsoJKq70yoEgzdrDlyz+11yBg0tU2S
+fzgMkAAHG8kajHBts0QRK1kvzSrQe7VITjpQUAFOVpxbnTFJzhloqiHwLlKzremC
+g3IBh4svqO7r4j32VDI61QKBgQCQL4gS872cWSncVp7vI/iNHtZBHy2HbNX1QVEi
+Iwgb7U+mZIdh5roukhlj0l96bgPPVbUhJX7v1sX+vI/KikSmZk/V7IzuNrich5xR
+ZmzfwOOqq8z+wyBjXuqjx6P9oca+9Zxf3L8Tmtx5WNW1CCOImfKXiZopX9XPgsgp
+bPIMaQKBgQCql4uTSacSQ5s6rEEdvR+y6nTohF3zxhOQ+6xivm3Hf1mgTk40lQ+t
+sr6HsSTv8j/ZbhhtaUUb2efro3pDztjlxXFvITar9ZDB2B4QMlpSsDR9UNk8xKGY
+J9aYLr4fJC6J6VA7Wf0yq6LpjSXRH/2GeNtmMl5rFRsHt+VU7GZK9g==
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..4ac6e7b124
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4Lq7ELQ/y+hhAKvAnNzOj2JKLCyi8+/h613YEue7xwpZ2eNHfi1a+2C7DaQ76IPK3iEUYZ8MFB/ZJeX5kSwTY3VGVKnkDveZ7ZvLD8vgyt5fattRPDI1cXdMG8pvcZunHs+iIH1+txv1EIzG3KkG8ZAPpvHqHlRYq5KRjMBFpEu/eRsGzByp+sDc99bJuQihprZE7UJrzq7orS/5rivQ59HFOeCG5qRmQfoIPv65TeKuWI8hQW3bBMj+paacOswWHhF99OgcrRBkdd2SrhqZrmRFl1nSy9KivGqSXwRYY+FF6hSNivPRsI169KUPEcLEuLx6lHG6/4CFkdbRh0wIL uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
new file mode 100644
index 0000000000..01a88acea2
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQC97XncQDaa9PQYEWK7llBxZQ2suVYTz1eadw2HtY+Y8ZKdUBLd
+9LUQ2lymUC9yq66rb5pBBR13k/9Zcbu8I0nafrZT4wJ4H0YGD6Ob5O4HR4EHjO5q
+hgnMJ17e1XnzI31MW5xAuAHTLLClNvnG05T1jaU+tRAsVSCHin3+sOenowIVAMSe
+ANBvw7fm5+Lw+ziOAHPjeYzRAoGBALkWCGpKmlJ65F3Y/RcownHQvsrDAllzKF/a
+cSfriCVVP5qVZ3Ach28ZZ9BFEnRE2SKqVsyBAiceb/+ISlu8CqKEvvoNIMJAu5rU
+MwZh+PeHN4ES6tWTwBGAwu84ke6N4BgV+6Q4qkcyywHsT5oU0EdVbn2zzAZw8c7v
+BpbsJ1KsAoGABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CI
+TjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJ
+MIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+WsCFQCRJayH
+P4vM1XUOVEeX7u04K1EAFg==
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
new file mode 100644
index 0000000000..30661d5adf
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL3tedxANpr09BgRYruWUHFlDay5VhPPV5p3DYe1j5jxkp1QEt30tRDaXKZQL3KrrqtvmkEFHXeT/1lxu7wjSdp+tlPjAngfRgYPo5vk7gdHgQeM7mqGCcwnXt7VefMjfUxbnEC4AdMssKU2+cbTlPWNpT61ECxVIIeKff6w56ejAAAAFQDEngDQb8O35ufi8Ps4jgBz43mM0QAAAIEAuRYIakqaUnrkXdj9FyjCcdC+ysMCWXMoX9pxJ+uIJVU/mpVncByHbxln0EUSdETZIqpWzIECJx5v/4hKW7wKooS++g0gwkC7mtQzBmH494c3gRLq1ZPAEYDC7ziR7o3gGBX7pDiqRzLLAexPmhTQR1VufbPMBnDxzu8GluwnUqwAAACABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CITjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJMIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+Ws= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
new file mode 100644
index 0000000000..b349d26da3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAtyeX0PPvMULKNJYQXaC3bw9eKErEIjLsHl6vg8tvuDI3cNv228NAr2xNviljXIMMUS8nDRnDAgXX/3DoDchpA= sshtester@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
new file mode 100644
index 0000000000..ece6c8f284
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBdgJs/xThHiy/aY1ymtQ4B0URNnRCm8l2WZMFjua57+nvq4Duf+igN
+pN/5p/+azLKgBwYFK4EEACKhZANiAATUw6pT/UW2HvTW6lL2BGY7NfUGEX285XVi
+9AcTXH1K+TOekbGmcpSirlGzSb15Wycajpmaae5vAzH1nnvcVd3FYODVdDXTHgV/
+FeXQ+vaw7CZnEAKZsr8mjXRX3fEyO1E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
new file mode 100644
index 0000000000..fd81e220f7
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNTDqlP9RbYe9NbqUvYEZjs19QYRfbzldWL0BxNcfUr5M56RsaZylKKuUbNJvXlbJxqOmZpp7m8DMfWee9xV3cVg4NV0NdMeBX8V5dD69rDsJmcQApmyvyaNdFfd8TI7UQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
new file mode 100644
index 0000000000..21c000ea03
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEEhm0w3xcGILU8eP61mThVBwCJfyzrFktGf7cCa1ciL4YLsukd20Q3Z
+yp0YcEDLcEm36CZGabgkEvblJ1Rx2lPTu6AHBgUrgQQAI6GBiQOBhgAEAYep8cX2
+7wUPw5pNYwFkWQXrJ2GSkmO8iHwkWJ6srRay/sF3WoPF/dyDVymFgirtsSTJ+D0u
+ex4qphOOJxkd1Yf+ANHvDFN9LoBvbgtNLTRJlpuNLCdWQlt+mEnPMDGMV/HWHHiz
+7/mWE+XUVIcQjhm5uv0ObI/wroZEurXMGEhTis3L
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
new file mode 100644
index 0000000000..d9830da5de
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGHqfHF9u8FD8OaTWMBZFkF6ydhkpJjvIh8JFierK0Wsv7Bd1qDxf3cg1cphYIq7bEkyfg9LnseKqYTjicZHdWH/gDR7wxTfS6Ab24LTS00SZabjSwnVkJbfphJzzAxjFfx1hx4s+/5lhPl1FSHEI4Zubr9DmyP8K6GRLq1zBhIU4rNyw== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
new file mode 100644
index 0000000000..2e50ac2304
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA7+C3gLoflKybq4I+clbg2SWf6cXyHpnLNDnZeMvIbOz2X/Ce
+XUm17DFeexTaVBs9Zq9WwDFOFkLQhbuXgpvB0shSY0nr+Em7InRM8AiRLxPe0txM
+mFFhL+v083dYwgaJOo1PthNM/tGRZJu+0sQDqrmN7CusFHdZg2NTzTzbwWqPiuP/
+mf3o7W4CWqDTBzbYTgpWlH7vRZf9FYwT4on5YWzLA8TvO2TwBGTfTMK5nswH++iO
+v4jKecoEwyBFMUSKqZ9UYHGw/kshHbltM65Ye/xjXEX0GxDdxu8ZyVKXd4acNbJJ
+P0tcxN4GzKJiR6zNYwCzDhjqDEbM5qCGhShhgQIDAQABAoIBAQCucdGBP9mvmUcs
+Fu+q3xttTztYGqfVMSrhtCA/BJOhA0K4ypegZ/Zw6gY3pBaSi6y/fEuuQSz0a2qR
+lra8OOFflGa15hBA4/2/NKyu8swCXITy+1qIesYev43HcMePcolhl1qcorSfq2/8
+pnbDd+Diy0Y2thvSVmk2b4mF+/gkUx3CHLhgRMcxCHLG1VeqIfLf+pa0jIw94tZ5
+CoIoI096pDTsneO9xhh1QxWQRRFVqdf3Q9zyiBgJCggpX+1fVsbQejuEK4hKRBKx
+SRPX/pX5aU+7+KSZ/DbtXGg1sCw9NUDFTIEV3UPmko4oWawNGv/CQAK80g3go28v
+UnVf11BBAoGBAP2amIFp+Ps33A5eesT7g/NNkGqBEi5W37K8qzYJxqXJvH0xmpFo
+8a3Je3PQRrzbTUJyISA6/XNnA62+bEvWiEXPiK3stQzNHoVz7ftCb19zgW4sLKRW
+Qhjq7QsGeRrdksJnZ7ekfzOv658vHJPElS1MdPu2WWhiNvAjtmdyFQulAoGBAPIk
+6831QAnCdp/ffH/K+cqV9vQYOFig8n4mQNNC+sLghrtZh9kbmTuuNKAhF56vdCCn
+ABD/+RiLXKVsF0PvQ5g9wRLKaiJubXI7XEBemCCLhjtESxGpWEV8GalslUgE1cKs
+d1pwSVjd0sYt0gOAf2VRhlbpSWhXA2xVll34xgetAoGAHaI089pYN7K9SgiMO/xP
+3NxRZcCTSUrpdM9LClN2HOVH2zEyqI8kvnPuswfBXEwb6QnBCS0bdKKy8Vhw+yOk
+ZNPtWrVwKoDFcj6rrlKDBRpQI3mR9doGezboYANvn04I2iKPIgxcuMNzuvQcWL/9
+1n86pDcYl3Pyi3kA1XGlN+kCgYEAz1boBxpqdDDsjGa8X1y5WUviAw8+KD3ghj5R
+IdTnjbjeBUxbc38bTawUac0MQZexE0iMWQImFGs4sHkGzufwdErkqSdjjAoMc1T6
+4C9fifaOwO7wbLYZ3J2wB4/vn5RsSV6OcIVXeN2wXnvbqZ38+A+/vWnSrqJbTwdW
+Uy7yup0CgYEA8M9vjpAoCr3XzNDwJyWRQcT7e+nRYUNDlXBl3jpQhHuJtnSnkoUv
+HXYXEwvp8peycNzeVz5OwFVMzCH8OG4WiGN4Pmo0rDWHED/W7eIRHIitHGTzZ+Qw
+gRxscoewblSLSkYMXidBLmQjr4U5bDBesRuGhm5NuLyMTa1f3Pc/90k=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
new file mode 100644
index 0000000000..26e560d4f8
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDv4LeAuh+UrJurgj5yVuDZJZ/pxfIemcs0Odl4y8hs7PZf8J5dSbXsMV57FNpUGz1mr1bAMU4WQtCFu5eCm8HSyFJjSev4SbsidEzwCJEvE97S3EyYUWEv6/Tzd1jCBok6jU+2E0z+0ZFkm77SxAOquY3sK6wUd1mDY1PNPNvBao+K4/+Z/ejtbgJaoNMHNthOClaUfu9Fl/0VjBPiiflhbMsDxO87ZPAEZN9MwrmezAf76I6/iMp5ygTDIEUxRIqpn1RgcbD+SyEduW0zrlh7/GNcRfQbEN3G7xnJUpd3hpw1skk/S1zE3gbMomJHrM1jALMOGOoMRszmoIaFKGGB uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 9bbd9da817..ba4518cfe6 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -91,7 +91,7 @@ end_per_suite(Config) ->
init_per_group(openssh, Config) ->
case ssh_test_lib:gen_tcp_connect("localhost", 22, []) of
{error,econnrefused} ->
- {skip,"No openssh deamon"};
+ {skip,"No openssh deamon (econnrefused)"};
{ok, Socket} ->
gen_tcp:close(Socket),
ssh_test_lib:openssh_sanity_check(Config)
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 7aa3d8a00a..c2f9c0eba8 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -181,8 +181,9 @@ init_per_group(openssh_server, Config) ->
[{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
{error,"Key exchange failed"} ->
{skip, "openssh server doesn't support the tested kex algorithm"};
- _ ->
- {skip, "No openssh server"}
+ Other ->
+ ct:log("No openssh server. Cause:~n~p~n",[Other]),
+ {skip, "No openssh daemon (see log in testcase)"}
end;
init_per_group(remote_tar, Config) ->
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 763649a12f..5fc948fbed 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -34,7 +34,7 @@
-define(PASSWD, "Sesame").
-define(XFER_PACKET_SIZE, 32768).
-define(XFER_WINDOW_SIZE, 4*?XFER_PACKET_SIZE).
--define(TIMEOUT, 10000).
+-define(SSH_TIMEOUT, 10000).
-define(REG_ATTERS, <<0,0,0,0,1>>).
-define(UNIX_EPOCH, 62167219200).
@@ -161,9 +161,9 @@ init_per_testcase(TestCase, Config) ->
{silently_accept_hosts, true}]),
{ok, Channel} =
ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE,
- ?XFER_PACKET_SIZE, ?TIMEOUT),
+ ?XFER_PACKET_SIZE, ?SSH_TIMEOUT),
- success = ssh_connection:subsystem(Cm, Channel, "sftp", ?TIMEOUT),
+ success = ssh_connection:subsystem(Cm, Channel, "sftp", ?SSH_TIMEOUT),
ProtocolVer = case atom_to_list(TestCase) of
"ver3_" ++ _ ->
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 83819b97a5..f97c3b1352 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -28,9 +28,7 @@
-include_lib("public_key/include/public_key.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("ssh/src/ssh_transport.hrl").
-
-
--define(TIMEOUT, 50000).
+-include("ssh_test_lib.hrl").
%%%----------------------------------------------------------------
connect(Port, Options) when is_integer(Port) ->
@@ -55,7 +53,7 @@ daemon(Host, Options) ->
daemon(Host, Port, Options) ->
- ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
+ %% ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
{ok,L} = ssh:daemon_info(Pid),
@@ -201,15 +199,17 @@ init_io_server(TestCase) ->
loop_io_server(TestCase, Buff0) ->
receive
- {input, TestCase, Line} ->
+ {input, TestCase, Line} = _INP ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_INP]),
loop_io_server(TestCase, Buff0 ++ [Line]);
- {io_request, From, ReplyAs, Request} ->
+ {io_request, From, ReplyAs, Request} = _REQ->
+ %%ct:log("io_server ~p:~p ~p got ~p",[?MODULE,?LINE,self(),_REQ]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
{'EXIT',_, _} = _Exit ->
-%% ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
+ ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
ok
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
@@ -600,6 +600,7 @@ check_ssh_client_support2(P) ->
{P, {data, _A}} ->
check_ssh_client_support2(P);
{P, {exit_status, E}} ->
+ ct:log("~p:~p exit_status:~n~p",[?MODULE,?LINE,E]),
E
after 5000 ->
ct:log("Openssh command timed out ~n"),
@@ -651,14 +652,14 @@ default_algorithms(sshc, DaemonOptions) ->
{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")
+ ct:fail("No server respons (timeout) 1")
end,
receive
{result,Srvr,L} ->
L
after ?TIMEOUT ->
- ct:fail("No server respons 2")
+ ct:fail("No server respons (timeout) 2")
end.
run_fake_ssh({ok,InitialState}) ->
@@ -772,12 +773,12 @@ ssh_type1() ->
not_found;
Path ->
ct:log("~p:~p Found \"ssh\" at ~p",[?MODULE,?LINE,Path]),
- case os:cmd("ssh -V") of
+ case installed_ssh_version(timeout) of
Version = "OpenSSH" ++ _ ->
ct:log("~p:~p Found OpenSSH ~p",[?MODULE,?LINE,Version]),
openSSH;
- Str ->
- ct:log("ssh client ~p is unknown",[Str]),
+ Other ->
+ ct:log("ssh client ~p is unknown",[Other]),
unknown
end
end
@@ -787,6 +788,20 @@ ssh_type1() ->
not_found
end.
+installed_ssh_version(TimeoutReturn) ->
+ Parent = self(),
+ Pid = spawn(fun() ->
+ Parent ! {open_ssh_version, os:cmd("ssh -V")}
+ end),
+ receive
+ {open_ssh_version, V} ->
+ V
+ after ?TIMEOUT ->
+ exit(Pid, kill),
+ TimeoutReturn
+ end.
+
+
algo_intersection([], _) -> [];
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index 54c93b7e87..eaf856e6e8 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -1,4 +1,9 @@
%%-------------------------------------------------------------------------
+%% Timeout time in ms
+%%-------------------------------------------------------------------------
+-define(TIMEOUT, 27000).
+
+%%-------------------------------------------------------------------------
%% Check for usable crypt
%%-------------------------------------------------------------------------
-define(CHECK_CRYPTO(Available),
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 75d5b5e296..9df404d7ed 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -27,7 +27,6 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 50000).
-define(SSH_DEFAULT_PORT, 22).
-define(REKEY_DATA_TMO, 65000).
@@ -49,19 +48,9 @@ all() ->
end.
groups() ->
- [{erlang_client, [], [erlang_shell_client_openssh_server,
- erlang_client_openssh_server_exec_compressed,
- erlang_client_openssh_server_setenv,
- erlang_client_openssh_server_publickey_dsa,
- erlang_client_openssh_server_publickey_rsa,
- erlang_client_openssh_server_password,
- erlang_client_openssh_server_kexs,
- erlang_client_openssh_server_nonexistent_subsystem,
- erlang_client_openssh_server_renegotiate
+ [{erlang_client, [], [erlang_shell_client_openssh_server
]},
- {erlang_server, [], [erlang_server_openssh_client_public_key_dsa,
- erlang_server_openssh_client_public_key_rsa,
- erlang_server_openssh_client_renegotiate
+ {erlang_server, [], [erlang_server_openssh_client_renegotiate
]}
].
@@ -69,7 +58,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(
case gen_tcp:connect("localhost", 22, []) of
{error,econnrefused} ->
- {skip,"No openssh deamon"};
+ {skip,"No openssh deamon (econnrefused)"};
_ ->
ssh_test_lib:openssh_sanity_check(Config)
end
@@ -101,15 +90,6 @@ end_per_group(_, Config) ->
Config.
-init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
- chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_server_openssh_client_public_key_rsa, Config) ->
- chk_key(sshc, 'ssh-rsa', ".ssh/id_rsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
- chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
-init_per_testcase(erlang_client_openssh_server_publickey_rsa, Config) ->
- chk_key(sshd, 'ssh-rsa', ".ssh/id_rsa", Config);
-
init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
case os:type() of
{unix,_} -> ssh:start(), Config;
@@ -123,27 +103,6 @@ end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
-
-chk_key(Pgm, Name, File, Config) ->
- case ssh_test_lib:openssh_supports(Pgm, public_key, Name) of
- false ->
- {skip,lists:concat(["openssh client does not support ",Name])};
- true ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, File),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- init_per_testcase('__default__',Config);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, lists:concat(["no ~/",File])}
- end
- end.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -161,219 +120,6 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
receive_logout(),
receive_normal_exit(Shell).
-%--------------------------------------------------------------------
-erlang_client_openssh_server_exec() ->
- [{doc, "Test api function ssh_connection:exec"}].
-
-erlang_client_openssh_server_exec(Config) when is_list(Config) ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId0,
- "echo testing", infinity),
- Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data0) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
- = ExitStatus0} ->
- ct:log("0: Collected data ~p", [ExitStatus0]),
- ssh_test_lib:receive_exec_result(Data0,
- ConnectionRef, ChannelId0);
- Other0 ->
- ct:fail(Other0)
- end,
-
- {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId1,
- "echo testing1", infinity),
- Data1 = {ssh_cm, ConnectionRef, {data, ChannelId1, 0, <<"testing1\n">>}},
- case ssh_test_lib:receive_exec_result(Data1) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
- = ExitStatus1} ->
- ct:log("0: Collected data ~p", [ExitStatus1]),
- ssh_test_lib:receive_exec_result(Data1,
- ConnectionRef, ChannelId1);
- Other1 ->
- ct:fail(Other1)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_exec_compressed() ->
- [{doc, "Test that compression option works"}].
-
-erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
- CompressAlgs = [zlib, '[email protected]',none],
- case ssh_test_lib:ssh_supports(CompressAlgs, compression) of
- {false,L} ->
- {skip, io_lib:format("~p compression is not supported",[L])};
-
- true ->
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{compression,CompressAlgs}]}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_kexs() ->
- [{doc, "Test that we can connect with different KEXs."}].
-
-erlang_client_openssh_server_kexs(Config) when is_list(Config) ->
- KexAlgos = try proplists:get_value(kex, proplists:get_value(common_algs,Config))
- catch _:_ -> []
- end,
- comment(KexAlgos),
- case KexAlgos of
- [] -> {skip, "No common kex algorithms"};
- _ ->
- Success =
- lists:foldl(
- fun(Kex, Acc) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {preferred_algorithms,
- [{kex,[Kex]}]}]),
-
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- success =
- ssh_connection:exec(ConnectionRef, ChannelId,
- "echo testing", infinity),
-
- ExpectedData = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
- case ssh_test_lib:receive_exec_result(ExpectedData) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
- Acc;
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(ExpectedData, ConnectionRef, ChannelId),
- Acc;
- Other ->
- ct:log("~p failed: ~p",[Kex,Other]),
- false
- end
- end, true, KexAlgos),
- case Success of
- true ->
- ok;
- false ->
- {fail, "Kex failed for one or more algos"}
- end
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_setenv() ->
- [{doc, "Test api function ssh_connection:setenv"}].
-
-erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
- {ok, ChannelId} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- Env = case ssh_connection:setenv(ConnectionRef, ChannelId,
- "ENV_TEST", "testing_setenv",
- infinity) of
- success ->
- <<"tesing_setenv\n">>;
- failure ->
- <<"\n">>
- end,
- success = ssh_connection:exec(ConnectionRef, ChannelId,
- "echo $ENV_TEST", infinity),
- Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, Env}},
- case ssh_test_lib:receive_exec_result(Data) of
- expected ->
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef,
- {data,0,1, UnxpectedData}}} ->
- %% Some os may return things as
- %% ENV_TEST: Undefined variable.\n"
- ct:log("UnxpectedData: ~p", [UnxpectedData]),
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
- {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
- = ExitStatus} ->
- ct:log("0: Collected data ~p", [ExitStatus]),
- ssh_test_lib:receive_exec_result(Data,
- ConnectionRef, ChannelId);
- Other ->
- ct:fail(Other)
- end.
-
-%%--------------------------------------------------------------------
-
-%% setenv not meaningfull on erlang ssh daemon!
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_publickey_rsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-rsa').
-
-erlang_client_openssh_server_publickey_dsa(Config) ->
- erlang_client_openssh_server_publickey_X(Config, 'ssh-dss').
-
-
-erlang_client_openssh_server_publickey_X(_Config, Alg) ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{pref_public_key_algs, [Alg]},
- {user_interaction, false},
- {auth_methods, "publickey"},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef).
-
-%%--------------------------------------------------------------------
-erlang_server_openssh_client_public_key_dsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-dss').
-
-erlang_server_openssh_client_public_key_rsa() ->
- [{timetrap, {seconds,(?TIMEOUT div 1000)+10}}].
-erlang_server_openssh_client_public_key_rsa(Config) when is_list(Config) ->
- erlang_server_openssh_client_public_key_X(Config, 'ssh-rsa').
-
-
-erlang_server_openssh_client_public_key_X(Config, Alg) ->
- SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- KnownHosts = filename:join(PrivDir, "known_hosts"),
- {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
- {preferred_algorithms,[{public_key, [Alg]}]},
- {auth_methods, "publickey"},
- {failfun, fun ssh_test_lib:failfun/2}]),
- ct:sleep(500),
-
- Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
- [" -o UserKnownHostsFile=", KnownHosts,
- " -o StrictHostKeyChecking=no"],
- "1+1."),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT),
- ssh:stop_daemon(Pid).
-
%%--------------------------------------------------------------------
%% Test that the Erlang/OTP server can renegotiate with openSSH
erlang_server_openssh_client_renegotiate(Config) ->
@@ -431,108 +177,6 @@ erlang_server_openssh_client_renegotiate(Config) ->
end.
%%--------------------------------------------------------------------
-erlang_client_openssh_server_renegotiate(_Config) ->
- process_flag(trap_exit, true),
- IO = ssh_test_lib:start_io_server(),
- Ref = make_ref(),
- Parent = self(),
-
- Shell =
- spawn_link(
- fun() ->
- Host = ssh_test_lib:hostname(),
- Options = [{user_interaction, false},
- {silently_accept_hosts,true}],
- group_leader(IO, self()),
- {ok, ConnRef} = ssh:connect(Host, ?SSH_DEFAULT_PORT, Options),
- ct:log("Parent = ~p, IO = ~p, Shell = ~p, ConnRef = ~p~n",[Parent, IO, self(), ConnRef]),
- case ssh_connection:session_channel(ConnRef, infinity) of
- {ok,ChannelId} ->
- success = ssh_connection:ptty_alloc(ConnRef, ChannelId, []),
- Args = [{channel_cb, ssh_shell},
- {init_args,[ConnRef, ChannelId]},
- {cm, ConnRef}, {channel_id, ChannelId}],
- {ok, State} = ssh_channel:init([Args]),
- Parent ! {ok, Ref, ConnRef},
- ssh_channel:enter_loop(State);
- Error ->
- Parent ! {error, Ref, Error}
- end,
- receive
- nothing -> ok
- end
- end),
-
- receive
- {error, Ref, Error} ->
- ct:fail("Error=~p",[Error]);
- {ok, Ref, ConnectionRef} ->
- IO ! {input, self(), "echo Hej1\n"},
- receive_data("Hej1", ConnectionRef),
- Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
- ssh_connection_handler:renegotiate(ConnectionRef),
- IO ! {input, self(), "echo Hej2\n"},
- receive_data("Hej2", ConnectionRef),
- Kex2 = ssh_test_lib:get_kex_init(ConnectionRef),
- IO ! {input, self(), "exit\n"},
- receive_logout(),
- receive_normal_exit(Shell),
- true = (Kex1 =/= Kex2)
- end.
-
-%%--------------------------------------------------------------------
-erlang_client_openssh_server_password() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_password(Config) when is_list(Config) ->
- %% to make sure we don't public-key-auth
- UserDir = proplists:get_value(data_dir, Config),
- {error, Reason0} =
- ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- ct:log("Test of user foo that does not exist. "
- "Error msg: ~p~n", [Reason0]),
-
- User = string:strip(os:cmd("whoami"), right, $\n),
-
- case length(string:tokens(User, " ")) of
- 1 ->
- {error, Reason1} =
- ssh:connect(any, ?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- ct:log("Test of wrong Pasword. "
- "Error msg: ~p~n", [Reason1]);
- _ ->
- ct:log("Whoami failed reason: ~n", [])
- end.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssh_server_nonexistent_subsystem() ->
- [{doc, "Test client password option"}].
-erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) ->
-
- ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{user_interaction, false},
- silently_accept_hosts]),
-
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-
- failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "foo", infinity).
-
-%%--------------------------------------------------------------------
-%
-%% Not possible to send password with openssh without user interaction
-%%
-%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
receive_data(Data, Conn) ->