aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/notes.xml18
-rw-r--r--lib/ssh/src/ssh_auth.erl2
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl6
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl183
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl81
-rw-r--r--lib/ssh/vsn.mk2
6 files changed, 252 insertions, 40 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 8fb689fdd5..75e1615c09 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,24 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The authentication method 'keyboard-interactive' failed
+ in the Erlang client when the server after successful
+ authentication continued by asking for zero more
+ passwords.</p>
+ <p>
+ Own Id: OTP-13225</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index fdbb5c152a..b71bed033a 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -477,7 +477,7 @@ keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
1) when Password =/= undefined ->
[Password]; %% Password auth implemented with keyboard-interaction and passwd is known
keyboard_interact_get_responses(_, _, _, _, _, _, _, _, 0) ->
- [""];
+ [];
keyboard_interact_get_responses(false, undefined, undefined, _, _, _, [Prompt|_], Opts, _) ->
ssh_no_io:read_line(Prompt, Opts); %% Throws error as keyboard interaction is not allowed
keyboard_interact_get_responses(true, undefined, _,IoCb, Name, Instr, PromptInfos, Opts, _) ->
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index f082db136c..ce1931e4f4 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -648,10 +648,12 @@ userauth_keyboard_interactive(Msg = #ssh_msg_userauth_failure{},
userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_failure{},
#state{ssh_params = #ssh{role = client}} = State) ->
userauth(Msg, State);
-
userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_success{},
#state{ssh_params = #ssh{role = client}} = State) ->
- userauth(Msg, State).
+ userauth(Msg, State);
+userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_info_request{},
+ #state{ssh_params = #ssh{role = client}} = State) ->
+ userauth_keyboard_interactive(Msg, State).
%%--------------------------------------------------------------------
-spec connected({#ssh_msg_kexinit{}, binary()}, %%| %% #ssh_msg_kexdh_init{},
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index 2add99de97..e90bfa3d16 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -37,8 +37,8 @@ all() -> [{group, opensshc_erld}
].
groups() ->
- [{opensshc_erld, [{repeat, 3}], [openssh_client_shell]},
- {erlc_opensshd, [{repeat, 3}], [erl_shell]}
+ [{opensshc_erld, [{repeat, 3}], [openssh_client_shell,
+ openssh_client_sftp]}
].
@@ -50,7 +50,7 @@ init_per_suite(Config) ->
report_client_algorithms(),
ok = ssh:start(),
{ok,TracerPid} = erlang_trace(),
- [{tracer_pid,TracerPid} | Config]
+ [{tracer_pid,TracerPid} | init_sftp_dirs(Config)]
catch
C:E ->
{skip, io_lib:format("Couldn't start ~p:~p",[C,E])}
@@ -71,8 +71,12 @@ init_per_group(opensshc_erld, Config) ->
ssh_test_lib:setup_dsa(DataDir, UserDir),
ssh_test_lib:setup_rsa(DataDir, UserDir),
ssh_test_lib:setup_ecdsa("256", DataDir, UserDir),
+ Common = ssh_test_lib:intersect_bi_dir(
+ ssh_test_lib:intersection(ssh:default_algorithms(),
+ ssh_test_lib:default_algorithms(sshc))),
[{c_kexs, ssh_test_lib:sshc(kex)},
- {c_ciphers, ssh_test_lib:sshc(cipher)}
+ {c_ciphers, ssh_test_lib:sshc(cipher)},
+ {common_algs, Common}
| Config];
_ ->
{skip, "No OpenSsh client found"}
@@ -94,20 +98,21 @@ init_per_testcase(_Func, Conf) ->
end_per_testcase(_Func, _Conf) ->
ok.
+
+init_sftp_dirs(Config) ->
+ UserDir = ?config(priv_dir, Config),
+ SrcDir = filename:join(UserDir, "sftp_src"),
+ ok = file:make_dir(SrcDir),
+ SrcFile = "big_data",
+ DstDir = filename:join(UserDir, "sftp_dst"),
+ ok = file:make_dir(DstDir),
+ N = 100 * 1024*1024,
+ ok = file:write_file(filename:join(SrcDir,SrcFile), crypto:rand_bytes(N)),
+ [{sftp_src_dir,SrcDir}, {sftp_dst_dir,DstDir}, {src_file,SrcFile}, {sftp_size,N}
+ | Config].
+
%%%================================================================
openssh_client_shell(Config) ->
- CommonAlgs = ssh_test_lib:intersect_bi_dir(
- ssh_test_lib:intersection(ssh:default_algorithms(),
- ssh_test_lib:default_algorithms(sshc))),
- KexVariants =
- [ [{kex,[Kex]}]
- || Kex <- proplists:get_value(kex, CommonAlgs)],
- CipherVariants =
- [ [{cipher,[{client2server,[Cipher]},
- {server2client,[Cipher]}]}]
- || Cipher <- proplists:get_value(cipher, CommonAlgs)],
-
-
lists:foreach(
fun(PrefAlgs=[{kex,[Kex]}]) when Kex == 'diffie-hellman-group-exchange-sha256' ->
lists:foreach(
@@ -120,7 +125,8 @@ openssh_client_shell(Config) ->
(PrefAlgs) ->
openssh_client_shell(Config,
[{preferred_algorithms, PrefAlgs}])
- end, KexVariants ++ CipherVariants).
+ end, variants(kex,Config) ++ variants(cipher,Config)
+ ).
openssh_client_shell(Config, Options) ->
@@ -151,7 +157,7 @@ openssh_client_shell(Config, Options) ->
{SlavePid, _ClientResponse} ->
%% ct:pal("ClientResponse = ~p",[_ClientResponse]),
{ok, List} = get_trace_list(TracerPid),
- Times = find_times(List),
+ Times = find_times(List, [accept_to_hello, kex, kex_to_auth, auth, to_prompt]),
Algs = find_algs(List),
ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]),
lists:foreach(
@@ -189,6 +195,96 @@ openssh_client_shell(Config, Options) ->
%%%================================================================
+openssh_client_sftp(Config) ->
+ lists:foreach(
+ fun(PrefAlgs) ->
+ openssh_client_sftp(Config, [{preferred_algorithms,PrefAlgs}])
+ end, variants(cipher,Config)).
+
+
+openssh_client_sftp(Config, Options) ->
+ SystemDir = ?config(data_dir, Config),
+ UserDir = ?config(priv_dir, Config),
+ SftpSrcDir = ?config(sftp_src_dir, Config),
+ SrcFile = ?config(src_file, Config),
+ SrcSize = ?config(sftp_size, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+
+ {ok, TracerPid} = erlang_trace(),
+ {ServerPid, _Host, Port} =
+ ssh_test_lib:daemon([{system_dir, SystemDir},
+ {public_key_alg, ssh_dsa},
+ {subsystems,[ssh_sftpd:subsystem_spec([%{cwd, SftpSrcDir},
+ {root, SftpSrcDir}])]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Options]),
+ ct:sleep(500),
+ Cmd = lists:concat(["sftp",
+ " -b -",
+ " -P ",Port,
+ " -o UserKnownHostsFile=", KnownHosts,
+ " -o \"StrictHostKeyChecking no\"",
+ " localhost:",SrcFile
+ ]),
+%% ct:pal("Cmd = ~p",[Cmd]),
+
+ Parent = self(),
+ SlavePid = spawn(fun() ->
+ Parent ! {self(),os:cmd(Cmd)}
+ end),
+ receive
+ {SlavePid, _ClientResponse} ->
+ ct:pal("ClientResponse = ~p",[_ClientResponse]),
+ {ok, List} = get_trace_list(TracerPid),
+%%ct:pal("List=~p",[List]),
+ Times = find_times(List, [channel_open_close]),
+ Algs = find_algs(List),
+ ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]),
+ lists:foreach(
+ fun({{A,B},Value,Unit}) when A==encrypt ; A==decrypt ->
+ Data = [{value, Value},
+ {suite, ?MODULE},
+ {name, mk_name(["Sftp Cipher ",A," ",B," [",Unit,"]"])}
+ ],
+ ct:pal("sftp ct_event:notify ~p",[Data]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = Data});
+ ({channel_open_close,Value,Unit}) ->
+ Cipher = fmt_alg(Algs#alg.encrypt, List),
+ Data = [{value, round( (1024*Value) / SrcSize )},
+ {suite, ?MODULE},
+ {name, mk_name(["Sftp transfer ",Cipher," [",Unit," per kbyte]"])}
+ ],
+ ct:pal("sftp ct_event:notify ~p",[Data]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = Data});
+ (_) ->
+ skip
+ end, Times),
+ ssh:stop_daemon(ServerPid),
+ ok
+ after 10000 ->
+ ssh:stop_daemon(ServerPid),
+ exit(SlavePid, kill),
+ {fail, timeout}
+ end.
+
+%%%================================================================
+variants(Tag, Config) ->
+ TagType =
+ case proplists:get_value(Tag, ssh:default_algorithms()) of
+ [{_,_}|_] -> one_way;
+ [A|_] when is_atom(A) -> two_way
+ end,
+ [ [{Tag,tag_value(TagType,Alg)}]
+ || Alg <- proplists:get_value(Tag, ?config(common_algs,Config))
+ ].
+
+tag_value(two_way, Alg) -> [Alg];
+tag_value(one_way, Alg) -> [{client2server,[Alg]},
+ {server2client,[Alg]}].
+
+%%%----------------------------------------------------------------
fmt_alg(Alg, List) when is_atom(Alg) ->
fmt_alg(atom_to_list(Alg), List);
fmt_alg(Alg = "diffie-hellman-group-exchange-sha" ++ _, List) ->
@@ -199,7 +295,7 @@ fmt_alg(Alg = "diffie-hellman-group-exchange-sha" ++ _, List) ->
catch
_:_ -> Alg
end;
-fmt_alg(Alg, List) ->
+fmt_alg(Alg, _List) ->
Alg.
%%%----------------------------------------------------------------
@@ -209,10 +305,12 @@ char($-) -> $_;
char(C) -> C.
%%%----------------------------------------------------------------
-find_times(L) ->
- Xs = [accept_to_hello, kex, kex_to_auth, auth, to_prompt],
+find_times(L, Xs) ->
[find_time(X,L) || X <- Xs] ++
- crypto_algs_times_sizes([encrypt,decrypt], L).
+ function_algs_times_sizes([{ssh_transport,encrypt,2},
+ {ssh_transport,decrypt,2},
+ {ssh_message,decode,1},
+ {ssh_message,encode,1}], L).
-record(call, {
mfa,
@@ -268,7 +366,13 @@ find_time(to_prompt, L) ->
end,
?recv(#ssh_msg_channel_request{request_type="env"})
], L, []),
- {to_prompt, now2micro_sec(now_diff(T1,T0)), microsec}.
+ {to_prompt, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(channel_open_close, L) ->
+ [T0,T1] = find([?recv(#ssh_msg_channel_request{request_type="subsystem"}),
+ ?send(#ssh_msg_channel_close{})
+ ], L, []),
+ {channel_open_close, now2micro_sec(now_diff(T1,T0)), microsec}.
+
find([F|Fs], [C|Cs], Acc) when is_function(F,1) ->
@@ -295,24 +399,31 @@ find_gex_size_string(L) ->
Size.
%%%----------------
-crypto_algs_times_sizes(EncDecs, L) ->
- Raw = [{_Algorithm = case EncDec of
- encrypt -> {encrypt,S#ssh.encrypt};
- decrypt -> {decrypt,S#ssh.decrypt}
- end,
- size(Data),
- now2micro_sec(now_diff(T1, T0))
- }
+function_algs_times_sizes(EncDecs, L) ->
+ Raw = [begin
+ {Tag,Size} = function_ats_result(EncDec, C),
+ {Tag, Size, now2micro_sec(now_diff(T1,T0))}
+ end
|| EncDec <- EncDecs,
- #call{mfa = {ssh_transport,ED,2},
- args = [S,Data],
- t_call = T0,
- t_return = T1} <- L,
+ C = #call{mfa = ED,
+ args = Args, %%[S,Data],
+ t_call = T0,
+ t_return = T1} <- L,
ED == EncDec
],
[{Alg, round(1024*Time/Size), "microsec per kbyte"} % Microseconds per 1k bytes.
|| {Alg,Size,Time} <- lists:foldl(fun increment/2, [], Raw)].
+function_ats_result({ssh_transport,encrypt,2}, #call{args=[S,Data]}) ->
+ {{encrypt,S#ssh.encrypt}, size(Data)};
+function_ats_result({ssh_transport,decrypt,2}, #call{args=[S,Data]}) ->
+ {{decrypt,S#ssh.decrypt}, size(Data)};
+function_ats_result({ssh_message,encode,1}, #call{result=Data}) ->
+ {encode, size(Data)};
+function_ats_result({ssh_message,decode,1}, #call{args=[Data]}) ->
+ {decode, size(Data)}.
+
+
increment({Alg,Sz,T}, [{Alg,SumSz,SumT}|Acc]) ->
[{Alg,SumSz+Sz,SumT+T} | Acc];
increment(Spec, [X|Acc]) ->
@@ -342,6 +453,8 @@ erlang_trace() ->
{ssh_transport,select_algorithm,3},
{ssh_transport,encrypt,2},
{ssh_transport,decrypt,2},
+ {ssh_message,encode,1},
+ {ssh_message,decode,1},
{public_key,dh_gex_group,4} % To find dh_gex group size
]],
{ok, TracerPid}.
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 4c088acabf..fe197f8672 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -48,6 +48,7 @@ all() ->
[{group,tool_tests},
{group,kex},
{group,service_requests},
+ {group,authentication},
{group,packet_size_error},
{group,field_size_error}
].
@@ -78,7 +79,9 @@ groups() ->
bad_very_long_service_name,
empty_service_name,
bad_service_name_then_correct
- ]}
+ ]},
+ {authentication, [], [client_handles_keyboard_interactive_0_pwds
+ ]}
].
@@ -494,6 +497,82 @@ bad_service_name_length(Config, LengthExcess) ->
{match, disconnect(), receive_msg}
], InitialState).
+%%%--------------------------------------------------------------------
+%%% This is due to a fault report (OTP-13255) with OpenSSH-6.6.1
+client_handles_keyboard_interactive_0_pwds(Config) ->
+ {User,_Pwd} = server_user_password(Config),
+
+ %% Create a listening socket as server socket:
+ {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+ HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+
+ %% Start a process handling one connection on the server side:
+ spawn_link(
+ fun() ->
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{set_options, [print_ops, print_messages]},
+ {accept, [{system_dir, system_dir(Config)},
+ {user_dir, user_dir(Config)}]},
+ receive_hello,
+ {send, hello},
+
+ {send, ssh_msg_kexinit},
+ {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+
+ {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+ {send, ssh_msg_kexdh_reply},
+
+ {send, #ssh_msg_newkeys{}},
+ {match, #ssh_msg_newkeys{_='_'}, receive_msg},
+
+ {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+ {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+
+ {match, #ssh_msg_userauth_request{service="ssh-connection",
+ method="none",
+ user=User,
+ _='_'}, receive_msg},
+ {send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive",
+ partial_success = false}},
+
+ {match, #ssh_msg_userauth_request{service="ssh-connection",
+ method="keyboard-interactive",
+ user=User,
+ _='_'}, receive_msg},
+ {send, #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 1,
+ data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>>
+ }},
+ {match, #ssh_msg_userauth_info_response{num_responses = 1,
+ _='_'}, receive_msg},
+
+ %% the next is strange, but openssh 6.6.1 does this and this is what this testcase is about
+ {send, #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 0,
+ data = <<>>
+ }},
+ {match, #ssh_msg_userauth_info_response{num_responses = 0,
+ data = <<>>,
+ _='_'}, receive_msg},
+ %% Here we know that the tested fault is fixed
+ {send, #ssh_msg_userauth_success{}},
+ close_socket,
+ print_state
+ ],
+ InitialState)
+ end),
+
+ %% and finally connect to it with a regular Erlang SSH client:
+ {ok,_} = std_connect(HostPort, Config,
+ [{preferred_algorithms,[{kex,['diffie-hellman-group1-sha1']}]}]
+ ).
+
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 25b19133b1..55d12abffe 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.2
+SSH_VSN = 4.2.1
APP_VSN = "ssh-$(SSH_VSN)"