1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2017. All Rights Reserved. 5%% 6%% The contents of this file are subject to the Erlang Public License, 7%% Version 1.1, (the "License"); you may not use this file except in 8%% compliance with the License. You should have received a copy of the 9%% Erlang Public License along with this software. If not, it can be 10%% retrieved online at http://www.erlang.org/. 11%% 12%% Software distributed under the License is distributed on an "AS IS" 13%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14%% the License for the specific language governing rights and limitations 15%% under the License. 16%% 17%% %CopyrightEnd% 18%% 19 20%% 21 22-module(ssh_protocol_SUITE). 23 24-include_lib("common_test/include/ct.hrl"). 25-include_lib("kernel/include/inet.hrl"). 26-include_lib("ssh/src/ssh.hrl"). % ?UINT32, ?BYTE, #ssh{} ... 27-include_lib("ssh/src/ssh_transport.hrl"). 28-include_lib("ssh/src/ssh_auth.hrl"). 29-include("ssh_test_lib.hrl"). 30 31%% Note: This directive should only be used in test suites. 32-compile(export_all). 33 34-define(NEWLINE, <<"\r\n">>). 35-define(REKEY_DATA_TMO, 65000). 36 37-define(DEFAULT_KEX, 'diffie-hellman-group14-sha256'). 38-define(EXTRA_KEX, 'diffie-hellman-group1-sha1'). 39 40-define(CIPHERS, ['aes256-ctr','aes192-ctr','aes128-ctr','aes128-cbc','3des-cbc']). 41-define(DEFAULT_CIPHERS, [{client2server,?CIPHERS}, {server2client,?CIPHERS}]). 42 43-define(v(Key, Config), proplists:get_value(Key, Config)). 44-define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)). 45 46 47%%-------------------------------------------------------------------- 48%% Common Test interface functions ----------------------------------- 49%%-------------------------------------------------------------------- 50 51suite() -> 52 [{ct_hooks,[ts_install_cth]}, 53 {timetrap,{seconds,40}}]. 54 55all() -> 56 [{group,tool_tests}, 57 client_info_line, 58 {group,kex}, 59 {group,service_requests}, 60 {group,authentication}, 61 {group,packet_size_error}, 62 {group,field_size_error}, 63 {group,ext_info}, 64 {group,preferred_algorithms} 65 ]. 66 67groups() -> 68 [{tool_tests, [], [lib_works_as_client, 69 lib_works_as_server, 70 lib_match, 71 lib_no_match 72 ]}, 73 {packet_size_error, [], [packet_length_too_large, 74 packet_length_too_short]}, 75 76 {field_size_error, [], [service_name_length_too_large, 77 service_name_length_too_short]}, 78 79 {kex, [], [no_common_alg_server_disconnects, 80 no_common_alg_client_disconnects, 81 gex_client_init_option_groups, 82 gex_server_gex_limit, 83 gex_client_init_option_groups_moduli_file, 84 gex_client_init_option_groups_file, 85 gex_client_old_request_exact, 86 gex_client_old_request_noexact 87 ]}, 88 {service_requests, [], [bad_service_name, 89 bad_long_service_name, 90 bad_very_long_service_name, 91 empty_service_name, 92 bad_service_name_then_correct 93 ]}, 94 {authentication, [], [client_handles_keyboard_interactive_0_pwds 95 ]}, 96 {ext_info, [], [no_ext_info_s1, 97 no_ext_info_s2, 98 ext_info_s, 99 ext_info_c 100 ]}, 101 {preferred_algorithms, [], [preferred_algorithms, 102 modify_append, 103 modify_prepend, 104 modify_rm, 105 modify_combo 106 ]} 107 ]. 108 109 110init_per_suite(Config) -> 111 ?CHECK_CRYPTO(start_std_daemon( setup_dirs( start_apps(Config)))). 112 113end_per_suite(Config) -> 114 stop_apps(Config). 115 116 117 118init_per_testcase(no_common_alg_server_disconnects, Config) -> 119 start_std_daemon(Config, [{preferred_algorithms,[{public_key,['ssh-rsa']}, 120 {cipher,?DEFAULT_CIPHERS} 121 ]}]); 122 123init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; 124 TC == gex_client_init_option_groups_moduli_file ; 125 TC == gex_client_init_option_groups_file ; 126 TC == gex_server_gex_limit ; 127 TC == gex_client_old_request_exact ; 128 TC == gex_client_old_request_noexact -> 129 Opts = case TC of 130 gex_client_init_option_groups -> 131 [{dh_gex_groups, 132 [{1023, 5, 133 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F 134 }]}]; 135 gex_client_init_option_groups_file -> 136 DataDir = proplists:get_value(data_dir, Config), 137 F = filename:join(DataDir, "dh_group_test"), 138 [{dh_gex_groups, {file,F}}]; 139 gex_client_init_option_groups_moduli_file -> 140 DataDir = proplists:get_value(data_dir, Config), 141 F = filename:join(DataDir, "dh_group_test.moduli"), 142 [{dh_gex_groups, {ssh_moduli_file,F}}]; 143 _ when TC == gex_server_gex_limit ; 144 TC == gex_client_old_request_exact ; 145 TC == gex_client_old_request_noexact -> 146 [{dh_gex_groups, 147 [{1023, 2, 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A771225323}, 148 {1535, 5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827}, 149 {3071, 2, 16#DFAA35D35531E0F524F0099877A482D2AC8D589F374394A262A8E81A8A4FB2F65FADBAB395E05D147B29D486DFAA41F41597A256DA82A8B6F76401AED53D0253F956CEC610D417E42E3B287F7938FC24D8821B40BFA218A956EB7401BED6C96C68C7FD64F8170A8A76B953DD2F05420118F6B144D8FE48060A2BCB85056B478EDEF96DBC70427053ECD2958C074169E9550DD877779A3CF17C5AC850598C7586BEEA9DCFE9DD2A5FB62DF5F33EA7BC00CDA31B9D2DD721F979EA85B6E63F0C4E30BDDCD3A335522F9004C4ED50B15DC537F55324DD4FA119FB3F101467C6D7E1699DE4B3E3C478A8679B8EB3FA5C9B826B44530FD3BE9AD3063B240B0C853EBDDBD68DD940332D98F148D5D9E1DC977D60A0D23D0CA1198637FEAE4E7FAAC173AF2B84313A666CFB4EE6972811921D0AD867CE57F3BBC8D6CB057E3B66757BB46C9F72662624D44E14528327E3A7100E81A12C43C4E236118318CD90C8AA185BBB0C764826DAEAEE8DD245C5B451B4944E6122CC522D1C335C2EEF9429825A2B} 150 ]}, 151 {dh_gex_limits, {1023,2000}} 152 ]; 153 _ -> 154 [] 155 end, 156 start_std_daemon(Config, 157 [{preferred_algorithms,[{cipher,?DEFAULT_CIPHERS} 158 ]} 159 | Opts]); 160init_per_testcase(_TestCase, Config) -> 161 check_std_daemon_works(Config, ?LINE). 162 163end_per_testcase(no_common_alg_server_disconnects, Config) -> 164 stop_std_daemon(Config); 165end_per_testcase(TC, Config) when TC == gex_client_init_option_groups ; 166 TC == gex_client_init_option_groups_moduli_file ; 167 TC == gex_client_init_option_groups_file ; 168 TC == gex_server_gex_limit ; 169 TC == gex_client_old_request_exact ; 170 TC == gex_client_old_request_noexact -> 171 stop_std_daemon(Config); 172end_per_testcase(_TestCase, Config) -> 173 check_std_daemon_works(Config, ?LINE). 174 175%%%-------------------------------------------------------------------- 176%%% Test Cases -------------------------------------------------------- 177%%%-------------------------------------------------------------------- 178 179%%%-------------------------------------------------------------------- 180%%% Connect to an erlang server and check that the testlib acts as a client. 181lib_works_as_client(Config) -> 182 %% Connect and negotiate keys 183 {ok,InitialState} = ssh_trpt_test_lib:exec( 184 [{set_options, [print_ops, print_seqnums, print_messages]}] 185 ), 186 {ok,AfterKexState} = connect_and_kex(Config, InitialState), 187 188 %% Do the authentcation 189 {User,Pwd} = server_user_password(Config), 190 {ok,EndState} = 191 ssh_trpt_test_lib:exec( 192 [{send, #ssh_msg_service_request{name = "ssh-userauth"}}, 193 {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg}, 194 {send, #ssh_msg_userauth_request{user = User, 195 service = "ssh-connection", 196 method = "password", 197 data = <<?BOOLEAN(?FALSE), 198 ?STRING(unicode:characters_to_binary(Pwd))>> 199 }}, 200 {match, #ssh_msg_userauth_success{_='_'}, receive_msg} 201 ], AfterKexState), 202 203 %% Disconnect 204 {ok,_} = 205 ssh_trpt_test_lib:exec( 206 [{send, #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, 207 description = "End of the fun", 208 language = "" 209 }}, 210 close_socket 211 ], EndState). 212 213 214%%-------------------------------------------------------------------- 215%%% Connect an erlang client and check that the testlib can act as a server. 216lib_works_as_server(Config) -> 217 {User,_Pwd} = server_user_password(Config), 218 219 %% Create a listening socket as server socket: 220 {ok,InitialState} = ssh_trpt_test_lib:exec(listen), 221 HostPort = ssh_trpt_test_lib:server_host_port(InitialState), 222 223 %% Start a process handling one connection on the server side: 224 spawn_link( 225 fun() -> 226 {ok,_} = 227 ssh_trpt_test_lib:exec( 228 [{set_options, [print_ops, print_messages]}, 229 {accept, [{system_dir, system_dir(Config)}, 230 {user_dir, user_dir(Config)}]}, 231 receive_hello, 232 {send, hello}, 233 234 {send, ssh_msg_kexinit}, 235 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 236 237 {match, #ssh_msg_kexdh_init{_='_'}, receive_msg}, 238 {send, ssh_msg_kexdh_reply}, 239 240 {send, #ssh_msg_newkeys{}}, 241 {match, #ssh_msg_newkeys{_='_'}, receive_msg}, 242 243 {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg}, 244 {send, #ssh_msg_service_accept{name="ssh-userauth"}}, 245 246 {match, #ssh_msg_userauth_request{service="ssh-connection", 247 method="none", 248 user=User, 249 _='_'}, receive_msg}, 250 251 {send, #ssh_msg_userauth_failure{authentications = "password", 252 partial_success = false}}, 253 254 {match, #ssh_msg_userauth_request{service="ssh-connection", 255 method="password", 256 user=User, 257 _='_'}, receive_msg}, 258 {send, #ssh_msg_userauth_success{}}, 259 close_socket, 260 print_state 261 ], 262 InitialState) 263 end), 264 265 %% and finally connect to it with a regular Erlang SSH client: 266 {ok,_} = std_connect(HostPort, Config, 267 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]}, 268 {cipher,?DEFAULT_CIPHERS} 269 ]} 270 ] 271 ). 272 273%%-------------------------------------------------------------------- 274%%% Matching 275lib_match(_Config) -> 276 {ok,_} = 277 ssh_trpt_test_lib:exec([{set_options, [print_ops]}, 278 {match, abc, abc}, 279 {match, '$a', {cde,fgh}}, 280 {match, {cde,fgh}, '$a'}, 281 {match, '_', {cde,fgh}}, 282 {match, [a,'$a',b], [a,{cde,fgh},b]}, 283 {match, [a,'$a'|'$b'], [a,{cde,fgh},b,c]}, 284 {match, '$b', [b,c]} 285 ]). 286 287%%-------------------------------------------------------------------- 288%%% Not matching 289lib_no_match(_Config) -> 290 case ssh_trpt_test_lib:exec([{set_options, [print_ops]}, 291 {match, '$x', b}, 292 {match, a, '$x'}]) 293 of 294 {ok,_} -> {fail,"Unexpected match"}; 295 {error, {_Op,{expected,a,b},_State}} -> ok 296 end. 297 298%%-------------------------------------------------------------------- 299%%% Algo negotiation fail. This should result in a ssh_msg_disconnect 300%%% being sent from the server. 301no_common_alg_server_disconnects(Config) -> 302 {ok,_} = 303 ssh_trpt_test_lib:exec( 304 [{set_options, [print_ops, {print_messages,detail}]}, 305 {connect, 306 server_host(Config),server_port(Config), 307 [{silently_accept_hosts, true}, 308 {user_dir, user_dir(Config)}, 309 {user_interaction, false}, 310 {preferred_algorithms,[{public_key,['ssh-dss']}, 311 {cipher,?DEFAULT_CIPHERS} 312 ]} 313 ]}, 314 receive_hello, 315 {send, hello}, 316 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 317 {send, ssh_msg_kexinit}, % with server unsupported 'ssh-dss' ! 318 {match, disconnect(), receive_msg} 319 ] 320 ). 321 322%%-------------------------------------------------------------------- 323%%% Algo negotiation fail. This should result in a ssh_msg_disconnect 324%%% being sent from the client. 325no_common_alg_client_disconnects(Config) -> 326 %% Create a listening socket as server socket: 327 {ok,InitialState} = ssh_trpt_test_lib:exec(listen), 328 HostPort = ssh_trpt_test_lib:server_host_port(InitialState), 329 Parent = self(), 330 331 %% Start a process handling one connection on the server side: 332 Pid = 333 spawn_link( 334 fun() -> 335 Parent ! 336 {result,self(), 337 ssh_trpt_test_lib:exec( 338 [{set_options, [print_ops, {print_messages,detail}]}, 339 {accept, [{system_dir, system_dir(Config)}, 340 {user_dir, user_dir(Config)}]}, 341 receive_hello, 342 {send, hello}, 343 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 344 {send, #ssh_msg_kexinit{ % with unsupported "SOME-UNSUPPORTED" 345 cookie = <<80,158,95,51,174,35,73,130,246,141,200,49,180,190,82,234>>, 346 kex_algorithms = [atom_to_list(?DEFAULT_KEX)], 347 server_host_key_algorithms = ["SOME-UNSUPPORTED"], % SIC! 348 encryption_algorithms_client_to_server = ["aes128-ctr"], 349 encryption_algorithms_server_to_client = ["aes128-ctr"], 350 mac_algorithms_client_to_server = ["hmac-sha2-256"], 351 mac_algorithms_server_to_client = ["hmac-sha2-256"], 352 compression_algorithms_client_to_server = ["none"], 353 compression_algorithms_server_to_client = ["none"], 354 languages_client_to_server = [], 355 languages_server_to_client = [], 356 first_kex_packet_follows = false, 357 reserved = 0 358 }}, 359 {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg} 360 ], 361 InitialState) 362 } 363 end), 364 365 %% and finally connect to it with a regular Erlang SSH client 366 %% which of course does not support SOME-UNSUPPORTED as pub key algo: 367 Result = std_connect(HostPort, Config, [{preferred_algorithms,[{public_key,['ssh-dss']}, 368 {cipher,?DEFAULT_CIPHERS} 369 ]}]), 370 ct:log("Result of connect is ~p",[Result]), 371 372 receive 373 {result,Pid,{ok,_}} -> 374 ok; 375 {result,Pid,{error,{Op,ExecResult,S}}} -> 376 ct:log("ERROR!~nOp = ~p~nExecResult = ~p~nState =~n~s", 377 [Op,ExecResult,ssh_trpt_test_lib:format_msg(S)]), 378 {fail, ExecResult}; 379 X -> 380 ct:log("¤¤¤¤¤"), 381 ct:fail(X) 382 after 383 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 384 end. 385 386%%%-------------------------------------------------------------------- 387gex_client_init_option_groups(Config) -> 388 do_gex_client_init(Config, {512, 2048, 4000}, 389 {5,16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F} 390 ). 391 392gex_client_init_option_groups_file(Config) -> 393 do_gex_client_init(Config, {2000, 2048, 4000}, 394 {5, 16#DFAA35D35531E0F524F0099877A482D2AC8D589F374394A262A8E81A8A4FB2F65FADBAB395E05D147B29D486DFAA41F41597A256DA82A8B6F76401AED53D0253F956CEC610D417E42E3B287F7938FC24D8821B40BFA218A956EB7401BED6C96C68C7FD64F8170A8A76B953DD2F05420118F6B144D8FE48060A2BCB85056B478EDEF96DBC70427053ECD2958C074169E9550DD877779A3CF17C5AC850598C7586BEEA9DCFE9DD2A5FB62DF5F33EA7BC00CDA31B9D2DD721F979EA85B6E63F0C4E30BDDCD3A335522F9004C4ED50B15DC537F55324DD4FA119FB3F101467C6D7E1699DE4B3E3C478A8679B8EB3FA5C9B826B44530FD3BE9AD3063B240B0C853EBDDBD68DD940332D98F148D5D9E1DC977D60A0D23D0CA1198637FEAE4E7FAAC173AF2B84313A666CFB4EE6972811921D0AD867CE57F3BBC8D6CB057E3B66757BB46C9F72662624D44E14528327E3A7100E81A12C43C4E236118318CD90C8AA185BBB0C764826DAEAEE8DD245C5B451B4944E6122CC522D1C335C2EEF9424273F1F} 395 ). 396 397gex_client_init_option_groups_moduli_file(Config) -> 398 do_gex_client_init(Config, {2000, 2048, 4000}, 399 {5, 16#DD2047CBDBB6F8E919BC63DE885B34D0FD6E3DB2887D8B46FE249886ACED6B46DFCD5553168185FD376122171CD8927E60120FA8D01F01D03E58281FEA9A1ABE97631C828E41815F34FDCDF787419FE13A3137649AA93D2584230DF5F24B5C00C88B7D7DE4367693428C730376F218A53E853B0851BAB7C53C15DA7839CBE1285DB63F6FA45C1BB59FE1C5BB918F0F8459D7EF60ACFF5C0FA0F3FCAD1C5F4CE4416D4F4B36B05CDCEBE4FB879E95847EFBC6449CD190248843BC7EDB145FBFC4EDBB1A3C959298F08F3BA2CFBE231BBE204BE6F906209D28BD4820AB3E7BE96C26AE8A809ADD8D1A5A0B008E9570FA4C4697E116B8119892C604293683A9635F} 400 ). 401 402gex_server_gex_limit(Config) -> 403 do_gex_client_init(Config, {1000, 3000, 4000}, 404 %% {7,91}). 405 {5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827} 406 ). 407 408 409do_gex_client_init(Config, {Min,N,Max}, {G,P}) -> 410 {ok,_} = 411 ssh_trpt_test_lib:exec( 412 [{set_options, [print_ops, print_seqnums, print_messages]}, 413 {connect, 414 server_host(Config),server_port(Config), 415 [{silently_accept_hosts, true}, 416 {user_dir, user_dir(Config)}, 417 {user_interaction, false}, 418 {preferred_algorithms,[{kex,['diffie-hellman-group-exchange-sha1']}, 419 {cipher,?DEFAULT_CIPHERS} 420 ]} 421 ]}, 422 receive_hello, 423 {send, hello}, 424 {send, ssh_msg_kexinit}, 425 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 426 {send, #ssh_msg_kex_dh_gex_request{min = Min, 427 n = N, 428 max = Max}}, 429 {match, #ssh_msg_kex_dh_gex_group{p=P, g=G, _='_'}, receive_msg} 430 ] 431 ). 432 433%%%-------------------------------------------------------------------- 434gex_client_old_request_exact(Config) -> 435 do_gex_client_init_old(Config, 1023, 436 {2, 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A771225323} 437 ). 438 439gex_client_old_request_noexact(Config) -> 440 do_gex_client_init_old(Config, 1400, 441 {5, 16#D1391174233D315398FE2830AC6B2B66BCCD01B0A634899F339B7879F1DB85712E9DC4E4B1C6C8355570C1D2DCB53493DF18175A9C53D1128B592B4C72D97136F5542FEB981CBFE8012FDD30361F288A42BD5EBB08BAB0A5640E1AC48763B2ABD1945FEE36B2D55E1D50A1C86CED9DD141C4E7BE2D32D9B562A0F8E2E927020E91F58B57EB9ACDDA106A59302D7E92AD5F6E851A45FA1CFE86029A0F727F65A8F475F33572E2FDAB6073F0C21B8B54C3823DB2EF068927E5D747498F96E1E827} 442 ). 443 444do_gex_client_init_old(Config, N, {G,P}) -> 445 {ok,_} = 446 ssh_trpt_test_lib:exec( 447 [{set_options, [print_ops, print_seqnums, print_messages]}, 448 {connect, 449 server_host(Config),server_port(Config), 450 [{silently_accept_hosts, true}, 451 {user_dir, user_dir(Config)}, 452 {user_interaction, false}, 453 {preferred_algorithms,[{kex,['diffie-hellman-group-exchange-sha1']}, 454 {cipher,?DEFAULT_CIPHERS} 455 ]} 456 ]}, 457 receive_hello, 458 {send, hello}, 459 {send, ssh_msg_kexinit}, 460 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 461 {send, #ssh_msg_kex_dh_gex_request_old{n = N}}, 462 {match, #ssh_msg_kex_dh_gex_group{p=P, g=G, _='_'}, receive_msg} 463 ] 464 ). 465 466%%%-------------------------------------------------------------------- 467bad_service_name(Config) -> 468 bad_service_name(Config, "kfglkjf"). 469 470bad_long_service_name(Config) -> 471 bad_service_name(Config, 472 lists:duplicate(?SSH_MAX_PACKET_SIZE div 2, $a)). 473 474bad_very_long_service_name(Config) -> 475 bad_service_name(Config, 476 lists:duplicate(?SSH_MAX_PACKET_SIZE+5, $a)). 477 478empty_service_name(Config) -> 479 bad_service_name(Config, ""). 480 481bad_service_name_then_correct(Config) -> 482 {ok,InitialState} = connect_and_kex(Config), 483 {ok,_} = 484 ssh_trpt_test_lib:exec( 485 [{set_options, [print_ops, print_seqnums, print_messages]}, 486 {send, #ssh_msg_service_request{name = "kdjglkfdjgkldfjglkdfjglkfdjglkj"}}, 487 {send, #ssh_msg_service_request{name = "ssh-connection"}}, 488 {match, disconnect(), receive_msg} 489 ], InitialState). 490 491 492bad_service_name(Config, Name) -> 493 {ok,InitialState} = connect_and_kex(Config), 494 {ok,_} = 495 ssh_trpt_test_lib:exec( 496 [{set_options, [print_ops, print_seqnums, print_messages]}, 497 {send, #ssh_msg_service_request{name = Name}}, 498 {match, disconnect(), receive_msg} 499 ], InitialState). 500 501%%%-------------------------------------------------------------------- 502packet_length_too_large(Config) -> bad_packet_length(Config, +4). 503 504packet_length_too_short(Config) -> bad_packet_length(Config, -4). 505 506bad_packet_length(Config, LengthExcess) -> 507 PacketFun = 508 fun(Msg, Ssh) -> 509 BinMsg = ssh_message:encode(Msg), 510 ssh_transport:pack(BinMsg, Ssh, LengthExcess) 511 end, 512 {ok,InitialState} = connect_and_kex(Config), 513 {ok,_} = 514 ssh_trpt_test_lib:exec( 515 [{set_options, [print_ops, print_seqnums, print_messages]}, 516 {send, {special, 517 #ssh_msg_service_request{name="ssh-userauth"}, 518 PacketFun}}, 519 %% Prohibit remote decoder starvation: 520 {send, #ssh_msg_service_request{name="ssh-userauth"}}, 521 {match, disconnect(), receive_msg} 522 ], InitialState). 523 524%%%-------------------------------------------------------------------- 525service_name_length_too_large(Config) -> bad_service_name_length(Config, +4). 526 527service_name_length_too_short(Config) -> bad_service_name_length(Config, -4). 528 529 530bad_service_name_length(Config, LengthExcess) -> 531 PacketFun = 532 fun(#ssh_msg_service_request{name=Service}, Ssh) -> 533 BinName = list_to_binary(Service), 534 BinMsg = 535 <<?BYTE(?SSH_MSG_SERVICE_REQUEST), 536 %% A bad string encoding of Service: 537 ?UINT32(size(BinName)+LengthExcess), BinName/binary 538 >>, 539 ssh_transport:pack(BinMsg, Ssh) 540 end, 541 {ok,InitialState} = connect_and_kex(Config), 542 {ok,_} = 543 ssh_trpt_test_lib:exec( 544 [{set_options, [print_ops, print_seqnums, print_messages]}, 545 {send, {special, 546 #ssh_msg_service_request{name="ssh-userauth"}, 547 PacketFun} }, 548 %% Prohibit remote decoder starvation: 549 {send, #ssh_msg_service_request{name="ssh-userauth"}}, 550 {match, disconnect(), receive_msg} 551 ], InitialState). 552 553%%%-------------------------------------------------------------------- 554%%% This is due to a fault report (OTP-13255) with OpenSSH-6.6.1 555client_handles_keyboard_interactive_0_pwds(Config) -> 556 {User,_Pwd} = server_user_password(Config), 557 558 %% Create a listening socket as server socket: 559 {ok,InitialState} = ssh_trpt_test_lib:exec(listen), 560 HostPort = ssh_trpt_test_lib:server_host_port(InitialState), 561 562 %% Start a process handling one connection on the server side: 563 spawn_link( 564 fun() -> 565 {ok,_} = 566 ssh_trpt_test_lib:exec( 567 [{set_options, [print_ops, print_messages]}, 568 {accept, [{system_dir, system_dir(Config)}, 569 {user_dir, user_dir(Config)}]}, 570 receive_hello, 571 {send, hello}, 572 573 {send, ssh_msg_kexinit}, 574 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 575 576 {match, #ssh_msg_kexdh_init{_='_'}, receive_msg}, 577 {send, ssh_msg_kexdh_reply}, 578 579 {send, #ssh_msg_newkeys{}}, 580 {match, #ssh_msg_newkeys{_='_'}, receive_msg}, 581 582 {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg}, 583 {send, #ssh_msg_service_accept{name="ssh-userauth"}}, 584 585 {match, #ssh_msg_userauth_request{service="ssh-connection", 586 method="none", 587 user=User, 588 _='_'}, receive_msg}, 589 {send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive", 590 partial_success = false}}, 591 592 {match, #ssh_msg_userauth_request{service="ssh-connection", 593 method="keyboard-interactive", 594 user=User, 595 _='_'}, receive_msg}, 596 {send, #ssh_msg_userauth_info_request{name = "", 597 instruction = "", 598 language_tag = "", 599 num_prompts = 1, 600 data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>> 601 }}, 602 {match, #ssh_msg_userauth_info_response{num_responses = 1, 603 _='_'}, receive_msg}, 604 605 %% the next is strange, but openssh 6.6.1 does this and this is what this testcase is about 606 {send, #ssh_msg_userauth_info_request{name = "", 607 instruction = "", 608 language_tag = "", 609 num_prompts = 0, 610 data = <<>> 611 }}, 612 {match, #ssh_msg_userauth_info_response{num_responses = 0, 613 data = <<>>, 614 _='_'}, receive_msg}, 615 %% Here we know that the tested fault is fixed 616 {send, #ssh_msg_userauth_success{}}, 617 close_socket, 618 print_state 619 ], 620 InitialState) 621 end), 622 623 %% and finally connect to it with a regular Erlang SSH client: 624 {ok,_} = std_connect(HostPort, Config, 625 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]}, 626 {cipher,?DEFAULT_CIPHERS} 627 ]}] 628 ). 629 630 631 632%%%-------------------------------------------------------------------- 633client_info_line(Config) -> 634 %% A client must not send an info-line. If it does, the server should handle 635 %% handle this gracefully 636 {ok,Pid} = ssh_eqc_event_handler:add_report_handler(), 637 DataDir = proplists:get_value(data_dir, Config), 638 {_, _, Port} = ssh_test_lib:daemon([{system_dir,DataDir}]), 639 640 %% Fake client: 641 {ok,S} = gen_tcp:connect("localhost",Port,[]), 642 gen_tcp:send(S,"An illegal info-string\r\n"), 643 gen_tcp:close(S), 644 645 %% wait for server to react: 646 timer:sleep(1000), 647 648 %% check if a badmatch was received: 649 {ok, Reports} = ssh_eqc_event_handler:get_reports(Pid), 650 case lists:any(fun({error_report,_,{_,supervisor_report,L}}) when is_list(L) -> 651 lists:member({reason,{badmatch,{error,closed}}}, L); 652 (_) -> 653 false 654 end, Reports) of 655 true -> 656 ct:fail("Bad error report on info_line from client"); 657 false -> 658 ok 659 end. 660 661%%%-------------------------------------------------------------------- 662%%% The server does not send the extension because 663%%% the client does not tell the server to send it 664no_ext_info_s1(Config) -> 665 %% Start the dameon 666 Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true}, 667 {system_dir, system_dir(Config)}]), 668 {ok,AfterKexState} = connect_and_kex([{server,Server}|Config]), 669 {ok,_} = 670 ssh_trpt_test_lib:exec( 671 [{send, #ssh_msg_service_request{name = "ssh-userauth"}}, 672 {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg} 673 ], AfterKexState), 674 ssh:stop_daemon(Pid). 675 676%%%-------------------------------------------------------------------- 677%%% The server does not send the extension because 678%%% the server is not configured to send it 679no_ext_info_s2(Config) -> 680 %% Start the dameon 681 Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,false}, 682 {system_dir, system_dir(Config)}]), 683 {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]}, 684 {server,Server} 685 | Config]), 686 {ok,_} = 687 ssh_trpt_test_lib:exec( 688 [{send, #ssh_msg_service_request{name = "ssh-userauth"}}, 689 {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg} 690 ], AfterKexState), 691 ssh:stop_daemon(Pid). 692 693%%%-------------------------------------------------------------------- 694%%% The server sends the extension 695ext_info_s(Config) -> 696 %% Start the dameon 697 Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true}, 698 {system_dir, system_dir(Config)}]), 699 {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]}, 700 {server,Server} 701 | Config]), 702 {ok,_} = 703 ssh_trpt_test_lib:exec( 704 [{match, #ssh_msg_ext_info{_='_'}, receive_msg} 705 ], 706 AfterKexState), 707 ssh:stop_daemon(Pid). 708 709%%%-------------------------------------------------------------------- 710%%% The client sends the extension 711ext_info_c(Config) -> 712 %% Create a listening socket as server socket: 713 {ok,InitialState} = ssh_trpt_test_lib:exec(listen), 714 HostPort = ssh_trpt_test_lib:server_host_port(InitialState), 715 716 Parent = self(), 717 %% Start a process handling one connection on the server side: 718 Pid = 719 spawn_link( 720 fun() -> 721 Result = 722 ssh_trpt_test_lib:exec( 723 [{set_options, [print_ops, print_messages]}, 724 {accept, [{system_dir, system_dir(Config)}, 725 {user_dir, user_dir(Config)}, 726 {recv_ext_info, true} 727 ]}, 728 receive_hello, 729 {send, hello}, 730 731 {send, ssh_msg_kexinit}, 732 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 733 734 {match, #ssh_msg_kexdh_init{_='_'}, receive_msg}, 735 {send, ssh_msg_kexdh_reply}, 736 737 {send, #ssh_msg_newkeys{}}, 738 {match, #ssh_msg_newkeys{_='_'}, receive_msg}, 739 740 {match, #ssh_msg_ext_info{_='_'}, receive_msg}, 741 742 close_socket, 743 print_state 744 ], 745 InitialState), 746 Parent ! {result,self(),Result} 747 end), 748 749 %% connect to it with a regular Erlang SSH client 750 %% (expect error due to the close_socket in daemon): 751 {error,_} = std_connect(HostPort, Config, 752 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]}, 753 {cipher,?DEFAULT_CIPHERS} 754 ]}, 755 {tstflg, [{ext_info_client,true}]}, 756 {send_ext_info, true} 757 ] 758 ), 759 760 %% Check that the daemon got expected result: 761 receive 762 {result, Pid, {ok,_}} -> ok; 763 {result, Pid, Error} -> ct:fail("Error: ~p",[Error]) 764 end. 765 766 767%%%---------------------------------------------------------------- 768%%% 769preferred_algorithms(Config) -> 770 Ciphers = filter_supported(cipher, ?CIPHERS), 771 {error,{eoptions,{{preferred_algorithms,{kex,[some_unknown_algo]}}, 772 "Unsupported value(s) found"}}} = 773 chk_pref_algs(Config, 774 [?DEFAULT_KEX], 775 Ciphers, 776 [{preferred_algorithms, [{kex,[some_unknown_algo,?DEFAULT_KEX]}, 777 {cipher,Ciphers} 778 ]} 779 ]). 780 781%%%---------------------------------------------------------------- 782%%% 783modify_append(Config) -> 784 Ciphers = filter_supported(cipher, ?CIPHERS), 785 {ok,_} = 786 chk_pref_algs(Config, 787 [?DEFAULT_KEX, ?EXTRA_KEX], 788 Ciphers, 789 [{preferred_algorithms, [{kex,[?DEFAULT_KEX]}, 790 {cipher,Ciphers} 791 ]}, 792 {modify_algorithms, [{append,[{kex,[some_unknown_algo,?EXTRA_KEX]}]}]} 793 ]). 794 795%%%---------------------------------------------------------------- 796%%% 797modify_prepend(Config) -> 798 Ciphers = filter_supported(cipher, ?CIPHERS), 799 {ok,_} = 800 chk_pref_algs(Config, 801 [?EXTRA_KEX, ?DEFAULT_KEX], 802 Ciphers, 803 [{preferred_algorithms, [{kex,[?DEFAULT_KEX]}, 804 {cipher,Ciphers} 805 ]}, 806 {modify_algorithms, [{prepend,[{kex,[some_unknown_algo,?EXTRA_KEX]}]}]} 807 ]). 808 809%%%---------------------------------------------------------------- 810%%% 811modify_rm(Config) -> 812 Ciphers = filter_supported(cipher, ?CIPHERS), 813 {ok,_} = 814 chk_pref_algs(Config, 815 [?DEFAULT_KEX], 816 tl(Ciphers), 817 [{preferred_algorithms, [{kex,[?DEFAULT_KEX,?EXTRA_KEX]}, 818 {cipher,Ciphers} 819 ]}, 820 {modify_algorithms, [{rm,[{kex,[some_unknown_algo,?EXTRA_KEX]}, 821 {cipher,[hd(Ciphers)]} 822 ]} 823 ]} 824 ]). 825 826 827%%%---------------------------------------------------------------- 828%%% 829modify_combo(Config) -> 830 Ciphers = filter_supported(cipher, ?CIPHERS), 831 LastC = lists:last(Ciphers), 832 {ok,_} = 833 chk_pref_algs(Config, 834 [?DEFAULT_KEX], 835 [LastC] ++ (tl(Ciphers)--[LastC]) ++ [hd(Ciphers)], 836 [{preferred_algorithms, [{kex,[?DEFAULT_KEX,?EXTRA_KEX]}, 837 {cipher,Ciphers} 838 ]}, 839 {modify_algorithms, [{rm,[{kex,[some_unknown_algo,?EXTRA_KEX]} 840 ]}, 841 {prepend,[{cipher,[{server2client,[LastC]}]} 842 ]}, 843 {append,[{cipher,[a,hd(Ciphers),b]} 844 ]} 845 ]} 846 ]). 847 848%%%================================================================ 849%%%==== Internal functions ======================================== 850%%%================================================================ 851 852chk_pref_algs(Config, 853 ExpectedKex, 854 ExpectedCiphers, 855 ServerPrefOpts) -> 856 %% Start the dameon 857 case ssh_test_lib:daemon( 858 [{send_ext_info,false}, 859 {recv_ext_info,false}, 860 {system_dir, system_dir(Config)} 861 | ServerPrefOpts]) 862 of 863 {_,Host,Port} -> 864 %% Check the Kex part 865 ssh_trpt_test_lib:exec( 866 [{set_options, [print_ops, {print_messages,detail}]}, 867 {connect, Host, Port, 868 [{silently_accept_hosts, true}, 869 {user_dir, user_dir(Config)}, 870 {user_interaction, false} 871 ]}, 872 {send, hello}, 873 receive_hello, 874 {match, 875 #ssh_msg_kexinit{ 876 kex_algorithms = to_lists(ExpectedKex), 877 encryption_algorithms_server_to_client = to_lists(ExpectedCiphers), 878 _ = '_'}, 879 receive_msg} 880 ]); 881 Error -> 882 Error 883 end. 884 885 886filter_supported(K, Algs) -> Algs -- (Algs--supported(K)). 887 888supported(_K) -> proplists:get_value( 889 server2client, 890 ssh_transport:supported_algorithms(cipher)). 891 892to_lists(L) -> lists:map(fun erlang:atom_to_list/1, L). 893 894 895%%%---- init_suite and end_suite --------------------------------------- 896start_apps(Config) -> 897 catch ssh:stop(), 898 ok = ssh:start(), 899 Config. 900 901stop_apps(_Config) -> 902 ssh:stop(). 903 904 905setup_dirs(Config) -> 906 DataDir = proplists:get_value(data_dir, Config), 907 PrivDir = proplists:get_value(priv_dir, Config), 908 ssh_test_lib:setup_dsa(DataDir, PrivDir), 909 ssh_test_lib:setup_rsa(DataDir, PrivDir), 910 Config. 911 912system_dir(Config) -> filename:join(proplists:get_value(priv_dir, Config), system). 913 914user_dir(Config) -> proplists:get_value(priv_dir, Config). 915 916%%%---------------------------------------------------------------- 917start_std_daemon(Config) -> 918 start_std_daemon(Config, []). 919 920start_std_daemon(Config, ExtraOpts) -> 921 PrivDir = proplists:get_value(priv_dir, Config), 922 UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth 923 file:make_dir(UserDir), 924 UserPasswords = [{"user1","pwd1"}], 925 Options = [%%{preferred_algorithms,[{public_key,['ssh-rsa']}]}, %% For some test cases 926 {system_dir, system_dir(Config)}, 927 {user_dir, UserDir}, 928 {user_passwords, UserPasswords}, 929 {failfun, fun ssh_test_lib:failfun/2} 930 | ExtraOpts], 931 Ref = {Server, Host, Port} = ssh_test_lib:daemon(Options), 932 ct:log("Std server ~p started at ~p:~p~nOptions=~p",[Server, Host, Port, Options]), 933 [{server,Ref}, {user_passwords, UserPasswords} | Config]. 934 935 936stop_std_daemon(Config) -> 937 ssh:stop_daemon(server_pid(Config)), 938 ct:log("Std server ~p at ~p:~p stopped", [server_pid(Config), server_host(Config), server_port(Config)]), 939 lists:keydelete(server, 1, Config). 940 941 942check_std_daemon_works(Config, Line) -> 943 case std_connect(Config) of 944 {ok,C} -> 945 ct:log("Server ~p:~p ~p is ok at line ~p", 946 [server_host(Config), server_port(Config), 947 server_pid(Config), Line]), 948 ok = ssh:close(C), 949 Config; 950 Error = {error,_} -> 951 ct:fail("Standard server ~p:~p ~p is ill at line ~p: ~p", 952 [server_host(Config), server_port(Config), 953 server_pid(Config), Line, Error]) 954 end. 955 956server_pid(Config) -> element(1,?v(server,Config)). 957server_host(Config) -> element(2,?v(server,Config)). 958server_port(Config) -> element(3,?v(server,Config)). 959 960server_user_password(Config) -> server_user_password(1, Config). 961 962server_user_password(N, Config) -> lists:nth(N, ?v(user_passwords,Config)). 963 964 965std_connect(Config) -> 966 std_connect({server_host(Config), server_port(Config)}, Config). 967 968std_connect({Host,Port}, Config) -> 969 std_connect({Host,Port}, Config, []). 970 971std_connect({Host,Port}, Config, Opts) -> 972 std_connect(Host, Port, Config, Opts). 973 974std_connect(Host, Port, Config, Opts) -> 975 {User,Pwd} = server_user_password(Config), 976 ssh:connect(Host, Port, 977 %% Prefere User's Opts to the default opts 978 [O || O = {Tag,_} <- [{user,User},{password,Pwd}, 979 {silently_accept_hosts, true}, 980 {user_dir, user_dir(Config)}, 981 {user_interaction, false}], 982 not lists:keymember(Tag, 1, Opts) 983 ] ++ Opts, 984 30000). 985 986%%%---------------------------------------------------------------- 987connect_and_kex(Config) -> 988 connect_and_kex(Config, ssh_trpt_test_lib:exec([]) ). 989 990connect_and_kex(Config, InitialState) -> 991 ssh_trpt_test_lib:exec( 992 [{connect, 993 server_host(Config),server_port(Config), 994 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]}, 995 {cipher,?DEFAULT_CIPHERS} 996 ]}, 997 {silently_accept_hosts, true}, 998 {recv_ext_info, false}, 999 {user_dir, user_dir(Config)}, 1000 {user_interaction, false} 1001 | proplists:get_value(extra_options,Config,[]) 1002 ]}, 1003 receive_hello, 1004 {send, hello}, 1005 {send, ssh_msg_kexinit}, 1006 {match, #ssh_msg_kexinit{_='_'}, receive_msg}, 1007 {send, ssh_msg_kexdh_init}, 1008 {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg}, 1009 {send, #ssh_msg_newkeys{}}, 1010 {match, #ssh_msg_newkeys{_='_'}, receive_msg} 1011 ], 1012 InitialState). 1013 1014%%%---------------------------------------------------------------- 1015 1016%%% For matching peer disconnection 1017disconnect() -> 1018 disconnect('_'). 1019 1020disconnect(Code) -> 1021 {'or',[#ssh_msg_disconnect{code = Code, 1022 _='_'}, 1023 tcp_closed, 1024 {tcp_error,econnaborted} 1025 ]}. 1026