1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22-module(ssh_to_openssh_SUITE). 23 24-include_lib("common_test/include/ct.hrl"). 25-include("ssh_test_lib.hrl"). 26 27%% Note: This directive should only be used in test suites. 28-compile(export_all). 29 30-define(SSH_DEFAULT_PORT, 22). 31-define(REKEY_DATA_TMO, 65000). 32 33%%-------------------------------------------------------------------- 34%% Common Test interface functions ----------------------------------- 35%%-------------------------------------------------------------------- 36 37suite() -> 38 [{timetrap,{seconds,60}}]. 39 40all() -> 41 case os:find_executable("ssh") of 42 false -> 43 {skip, "openSSH not installed on host"}; 44 _ -> 45 [{group, erlang_client}, 46 {group, erlang_server} 47 ] 48 end. 49 50groups() -> 51 [{erlang_client, [], [erlang_shell_client_openssh_server 52 ]}, 53 {erlang_server, [], [erlang_server_openssh_client_renegotiate 54 ]} 55 ]. 56 57init_per_suite(Config) -> 58 ?CHECK_CRYPTO( 59 case gen_tcp:connect("localhost", 22, []) of 60 {error,econnrefused} -> 61 {skip,"No openssh deamon (econnrefused)"}; 62 _ -> 63 ssh_test_lib:openssh_sanity_check(Config) 64 end 65 ). 66 67end_per_suite(_Config) -> 68 ok. 69 70init_per_group(erlang_server, Config) -> 71 DataDir = proplists:get_value(data_dir, Config), 72 UserDir = proplists:get_value(priv_dir, Config), 73 ssh_test_lib:setup_dsa_known_host(DataDir, UserDir), 74 ssh_test_lib:setup_rsa_known_host(DataDir, UserDir), 75 Config; 76init_per_group(erlang_client, Config) -> 77 CommonAlgs = ssh_test_lib:algo_intersection( 78 ssh:default_algorithms(), 79 ssh_test_lib:default_algorithms(sshd)), 80 [{common_algs,CommonAlgs} | Config]; 81init_per_group(_, Config) -> 82 Config. 83 84end_per_group(erlang_server, Config) -> 85 UserDir = proplists:get_value(priv_dir, Config), 86 ssh_test_lib:clean_dsa(UserDir), 87 ssh_test_lib:clean_rsa(UserDir), 88 Config; 89end_per_group(_, Config) -> 90 Config. 91 92 93init_per_testcase(erlang_server_openssh_client_renegotiate, Config) -> 94 case os:type() of 95 {unix,_} -> ssh:start(), Config; 96 Type -> {skip, io_lib:format("Unsupported test on ~p",[Type])} 97 end; 98init_per_testcase(_TestCase, Config) -> 99 ssh:start(), 100 Config. 101 102end_per_testcase(_TestCase, _Config) -> 103 ssh:stop(), 104 ok. 105 106%%-------------------------------------------------------------------- 107%% Test Cases -------------------------------------------------------- 108%%-------------------------------------------------------------------- 109 110erlang_shell_client_openssh_server() -> 111 [{doc, "Test that ssh:shell/2 works"}]. 112 113erlang_shell_client_openssh_server(Config) when is_list(Config) -> 114 process_flag(trap_exit, true), 115 IO = ssh_test_lib:start_io_server(), 116 Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO), 117 IO ! {input, self(), "echo Hej\n"}, 118 receive_data("Hej", undefined), 119 IO ! {input, self(), "exit\n"}, 120 receive_logout(), 121 receive_normal_exit(Shell). 122 123%%-------------------------------------------------------------------- 124%% Test that the Erlang/OTP server can renegotiate with openSSH 125erlang_server_openssh_client_renegotiate(Config) -> 126 _PubKeyAlg = ssh_rsa, 127 SystemDir = proplists:get_value(data_dir, Config), 128 PrivDir = proplists:get_value(priv_dir, Config), 129 KnownHosts = filename:join(PrivDir, "known_hosts"), 130 131 {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, 132 {failfun, fun ssh_test_lib:failfun/2}]), 133 ct:sleep(500), 134 135 RenegLimitK = 3, 136 DataFile = filename:join(PrivDir, "renegotiate_openssh_client.data"), 137 Data = lists:duplicate(trunc(1.1*RenegLimitK*1024), $a), 138 ok = file:write_file(DataFile, Data), 139 140 Cmd = ssh_test_lib:open_sshc_cmd(Host, Port, 141 [" -o UserKnownHostsFile=", KnownHosts, 142 " -o StrictHostKeyChecking=no", 143 " -o RekeyLimit=",integer_to_list(RenegLimitK),"K"]), 144 145 146 OpenSsh = ssh_test_lib:open_port({spawn, Cmd++" < "++DataFile}), 147 148 Expect = fun({data,R}) -> 149 try 150 NonAlphaChars = [C || C<-lists:seq(1,255), 151 not lists:member(C,lists:seq($a,$z)), 152 not lists:member(C,lists:seq($A,$Z)) 153 ], 154 Lines = string:tokens(binary_to_list(R), NonAlphaChars), 155 lists:any(fun(L) -> length(L)>1 andalso lists:prefix(L, Data) end, 156 Lines) 157 catch 158 _:_ -> false 159 end; 160 161 ({exit_status,E}) when E=/=0 -> 162 ct:log("exit_status ~p",[E]), 163 throw({skip,"exit status"}); 164 165 (_) -> 166 false 167 end, 168 169 try 170 ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT) 171 of 172 _ -> 173 %% Unfortunately we can't check that there has been a renegotiation, just trust OpenSSH. 174 ssh:stop_daemon(Pid) 175 catch 176 throw:{skip,R} -> {skip,R} 177 end. 178 179%%-------------------------------------------------------------------- 180%%% Internal functions ----------------------------------------------- 181%%-------------------------------------------------------------------- 182receive_data(Data, Conn) -> 183 receive 184 Info when is_binary(Info) -> 185 Lines = string:tokens(binary_to_list(Info), "\r\n "), 186 case lists:member(Data, Lines) of 187 true -> 188 ct:log("Expected result ~p found in lines: ~p~n", [Data,Lines]), 189 ok; 190 false -> 191 ct:log("Extra info: ~p~n", [Info]), 192 receive_data(Data, Conn) 193 end; 194 Other -> 195 ct:log("Unexpected: ~p",[Other]), 196 receive_data(Data, Conn) 197 after 198 30000 -> 199 {State, _} = case Conn of 200 undefined -> {'??','??'}; 201 _ -> sys:get_state(Conn) 202 end, 203 ct:log("timeout ~p:~p~nExpect ~p~nState = ~p",[?MODULE,?LINE,Data,State]), 204 ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 205 end. 206 207receive_logout() -> 208 receive 209 <<"logout">> -> 210 extra_logout(), 211 receive 212 <<"Connection closed">> -> 213 ok 214 after 215 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 216 end; 217 Info -> 218 ct:log("Extra info when logging out: ~p~n", [Info]), 219 receive_logout() 220 after 221 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 222 end. 223 224receive_normal_exit(Shell) -> 225 receive 226 {'EXIT', Shell, normal} -> 227 ok; 228 <<"\r\n">> -> 229 receive_normal_exit(Shell); 230 Other -> 231 ct:fail({unexpected_msg, Other}) 232 after 233 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 234 end. 235 236extra_logout() -> 237 receive 238 <<"logout">> -> 239 ok 240 after 500 -> 241 ok 242 end. 243 244%%-------------------------------------------------------------------- 245%% Check if we have a "newer" ssh client that supports these test cases 246check_ssh_client_support(Config) -> 247 case ssh_test_lib:ssh_client_supports_Q() of 248 true -> 249 ssh:start(), 250 Config; 251 _ -> 252 {skip, "test case not supported by ssh client"} 253 end. 254 255comment(AtomList) -> 256 ct:comment( 257 string:join(lists:map(fun erlang:atom_to_list/1, AtomList), 258 ", ")). 259