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