aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/ssh.xml18
-rw-r--r--lib/ssh/src/Makefile76
-rw-r--r--lib/ssh/src/ssh.app.src1
-rw-r--r--lib/ssh/src/ssh.erl31
-rw-r--r--lib/ssh/src/ssh.hrl1
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl52
-rw-r--r--lib/ssh/src/ssh_math.erl42
-rw-r--r--lib/ssh/src/ssh_message.erl17
-rw-r--r--lib/ssh/src/ssh_transport.erl391
-rw-r--r--lib/ssh/src/ssh_transport.hrl61
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl134
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl14
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl102
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test3
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl191
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt1
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt1
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl14
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl6
-rw-r--r--lib/ssh/test/ssh_test_lib.erl22
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl152
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl129
-rw-r--r--lib/ssh/test/ssh_upgrade_SUITE.erl10
23 files changed, 1063 insertions, 406 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index d24025ca4d..cf5e8f1aff 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -43,7 +43,7 @@
<item>Supported public key algorithms: ssh-rsa and ssh-dss.</item>
<item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1.</item>
<item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc.</item>
- <item>Supported key exchange algorithms: diffie-hellman-group1-sha1.</item>
+ <item>Supported key exchange algorithms: diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256.</item>
<item>Supported compression algorithms: none, zlib, [email protected],</item>
<item>Supports unicode filenames if the emulator and the underlaying OS support it.
See section DESCRIPTION in the
@@ -240,6 +240,13 @@ kex is implicit but public_key is set explicitly.</p>
</warning>
</item>
+ <tag><c><![CDATA[{dh_gex_limits,{Min=integer(),I=integer(),Max=integer()}}]]></c></tag>
+ <item>
+ <p>Sets the three diffie-hellman-group-exchange parameters that guides the connected server in choosing a group.
+ See RFC 4419 for the function of thoose. The default value is <c>{512, 1024, 4096}</c>.
+ </p>
+ </item>
+
<tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag>
<item>
<p>Sets a time-out on the transport layer
@@ -449,6 +456,15 @@ kex is implicit but public_key is set explicitly.</p>
</warning>
</item>
+ <tag><c><![CDATA[{dh_gex_groups, [{Size=integer(),G=integer(),P=integer()}] | {file,filename()} }]]></c></tag>
+ <item>
+ <p>Sets the groups that the server may choose among when diffie-hellman-group-exchange is negotiated.
+ See RFC 4419 for details.
+ </p>
+ <p>If the parameter is <c>{file,filename()}</c>, the file must exist and have one or more three-tuples terminated by a dot. The interpretation is as if the tuples had been given directly in the option. The file is read when the daemon starts.
+ </p>
+ </item>
+
<tag><c><![CDATA[{pwdfun, fun(User::string(), password::string()) -> boolean()}]]></c></tag>
<item>
<p>Provides a function for password validation. This function is called
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 61d71d2cf7..b44c8eef35 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -67,7 +67,6 @@ MODULES= \
ssh_file \
ssh_io \
ssh_info \
- ssh_math \
ssh_message \
ssh_no_io \
ssh_sftp \
@@ -145,3 +144,78 @@ release_spec: opt
release_docs_spec:
+
+deps:
+ erlc -M $(ERL_FILES) \
+ | sed 's@$(ERL_TOP)/lib@../..@g' \
+ | sed 's/\.$(EMULATOR)/\.$$\(EMULATOR\)/' \
+ | sed 's@^ssh_@$$(EBIN)/ssh_@'
+
+ssh.$(EMULATOR): ssh.erl ssh.hrl ssh_connect.hrl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ../../kernel/include/file.hrl
+$(EBIN)/ssh_sup.$(EMULATOR): ssh_sup.erl
+sshc_sup.$(EMULATOR): sshc_sup.erl
+sshd_sup.$(EMULATOR): sshd_sup.erl ssh.hrl
+$(EBIN)/ssh_connection_sup.$(EMULATOR): ssh_connection_sup.erl
+$(EBIN)/ssh_connection.$(EMULATOR): ssh_connection.erl ssh.hrl ssh_connect.hrl \
+ ssh_transport.hrl
+$(EBIN)/ssh_connection_handler.$(EMULATOR): ssh_connection_handler.erl ssh.hrl \
+ ssh_transport.hrl ssh_auth.hrl ssh_connect.hrl
+$(EBIN)/ssh_shell.$(EMULATOR): ssh_shell.erl ssh_connect.hrl
+$(EBIN)/ssh_system_sup.$(EMULATOR): ssh_system_sup.erl ssh.hrl
+$(EBIN)/ssh_subsystem_sup.$(EMULATOR): ssh_subsystem_sup.erl
+$(EBIN)/ssh_channel_sup.$(EMULATOR): ssh_channel_sup.erl
+$(EBIN)/ssh_acceptor_sup.$(EMULATOR): ssh_acceptor_sup.erl ssh.hrl
+$(EBIN)/ssh_acceptor.$(EMULATOR): ssh_acceptor.erl ssh.hrl
+$(EBIN)/ssh_app.$(EMULATOR): ssh_app.erl
+$(EBIN)/ssh_auth.$(EMULATOR): ssh_auth.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ssh.hrl ssh_auth.hrl ssh_transport.hrl
+$(EBIN)/ssh_bits.$(EMULATOR): ssh_bits.erl ssh.hrl
+$(EBIN)/ssh_cli.$(EMULATOR): ssh_cli.erl ssh.hrl ssh_connect.hrl
+$(EBIN)/ssh_file.$(EMULATOR): ssh_file.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ../../kernel/include/file.hrl ssh.hrl
+$(EBIN)/ssh_io.$(EMULATOR): ssh_io.erl ssh.hrl
+$(EBIN)/ssh_info.$(EMULATOR): ssh_info.erl
+$(EBIN)/ssh_message.$(EMULATOR): ssh_message.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ssh.hrl ssh_connect.hrl ssh_auth.hrl ssh_transport.hrl
+$(EBIN)/ssh_no_io.$(EMULATOR): ssh_no_io.erl ssh_transport.hrl
+$(EBIN)/ssh_sftp.$(EMULATOR): ssh_sftp.erl \
+ ../../kernel/include/file.hrl ssh.hrl \
+ ssh_xfer.hrl
+$(EBIN)/ssh_sftpd.$(EMULATOR): ssh_sftpd.erl \
+ ../../kernel/include/file.hrl ssh.hrl \
+ ssh_xfer.hrl
+$(EBIN)/ssh_sftpd_file.$(EMULATOR): ssh_sftpd_file.erl
+$(EBIN)/ssh_transport.$(EMULATOR): ssh_transport.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ../../kernel/include/inet.hrl \
+ ssh_transport.hrl ssh.hrl
+$(EBIN)/ssh_xfer.$(EMULATOR): ssh_xfer.erl ssh.hrl ssh_xfer.hrl
+$(EBIN)/ssh_sftpd_file_api.$(EMULATOR): ssh_sftpd_file_api.erl
+$(EBIN)/ssh_channel.$(EMULATOR): ssh_channel.erl ssh_connect.hrl
+$(EBIN)/ssh_daemon_channel.$(EMULATOR): ssh_daemon_channel.erl
+$(EBIN)/ssh_client_key_api.$(EMULATOR): ssh_client_key_api.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ssh.hrl
+$(EBIN)/ssh_server_key_api.$(EMULATOR): ssh_server_key_api.erl \
+ ../../public_key/include/public_key.hrl \
+ ../../public_key/include/OTP-PUB-KEY.hrl \
+ ../../public_key/include/PKCS-FRAME.hrl \
+ ssh.hrl
+
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index bc01c539e0..4a76fd9cd3 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -24,7 +24,6 @@
ssh_file,
ssh_io,
ssh_info,
- ssh_math,
ssh_no_io,
ssh_server_key_api,
ssh_sftp,
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 370f086600..132de71aed 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -373,6 +373,10 @@ handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{dh_gex_groups,_} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{dh_gex_limits,_} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -393,6 +397,8 @@ handle_option([{id_string, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{profile, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{max_random_length_padding, _Bool} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -411,6 +417,28 @@ handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
handle_pref_algs(Opt);
+handle_ssh_option({dh_gex_groups,L=[{I1,I2,I3}|_]}) when is_integer(I1), I1>0,
+ is_integer(I2), I2>0,
+ is_integer(I3), I3>0 ->
+ {dh_gex_groups, lists:map(fun({N,G,P}) -> {N,{G,P}} end, L)};
+handle_ssh_option({dh_gex_groups,{file,File=[C|_]}}=Opt) when is_integer(C), C>0 ->
+ %% A string, (file name)
+ case file:consult(File) of
+ {ok, List} ->
+ try handle_ssh_option({dh_gex_groups,List}) of
+ {dh_gex_groups,_} = NewOpt ->
+ NewOpt
+ catch
+ _:_ ->
+ throw({error, {{eoptions, Opt}, "Bad format in file"}})
+ end;
+ Error ->
+ throw({error, {{eoptions, Opt},{"Error reading file",Error}}})
+ end;
+handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
+ is_integer(I), I>=Min,
+ is_integer(Max), Max>=I ->
+ Opt;
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 ->
@@ -489,6 +517,9 @@ handle_ssh_option({id_string, random}) ->
{id_string, {random,2,5}}; %% 2 - 5 random characters
handle_ssh_option({id_string, ID} = Opt) when is_list(ID) ->
Opt;
+handle_ssh_option({max_random_length_padding, Value} = Opt) when is_integer(Value),
+ Value =< 255 ->
+ Opt;
handle_ssh_option({profile, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option(Opt) ->
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 8df5ee820c..462c98f503 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -124,6 +124,7 @@
recv_sequence = 0,
keyex_key,
keyex_info,
+ random_length_padding = 255, % From RFC 4253 section 6.
%% User auth
user,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index a9c60d0674..180698d741 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -46,7 +46,9 @@
get_print_info/1]).
%% gen_fsm callbacks
--export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
+-export([hello/2, kexinit/2, key_exchange/2,
+ key_exchange_dh_gex_init/2, key_exchange_dh_gex_reply/2,
+ new_keys/2,
userauth/2, connected/2,
error/2]).
@@ -417,27 +419,39 @@ key_exchange(#ssh_msg_kexdh_reply{} = Msg,
send_msg(NewKeys, State),
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
-key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
+key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
#state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, NextKexMsg, Ssh1} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0),
- send_msg(NextKexMsg, State),
- {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
- send_msg(NewKeys, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
+ {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0),
+ send_msg(GexGroup, State),
+ {next_state, key_exchange_dh_gex_init, next_packet(State#state{ssh_params = Ssh})};
-key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
+key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, NextKexMsg, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0),
- send_msg(NextKexMsg, State),
- {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})};
+ {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0),
+ send_msg(KexGexInit, State),
+ {next_state, key_exchange_dh_gex_reply, next_packet(State#state{ssh_params = Ssh})}.
-key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg,
- #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
- {ok, NewKeys, Ssh} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0),
+%%--------------------------------------------------------------------
+-spec key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{}, #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
+key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{} = Msg,
+ #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
+ {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, Ssh0),
+ send_msg(KexGexReply, State),
+ {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1),
send_msg(NewKeys, State),
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}.
%%--------------------------------------------------------------------
+-spec key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{}, #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
+key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{} = Msg,
+ #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
+ {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0),
+ send_msg(NewKeys, State),
+ {next_state, new_keys, next_packet(State#state{ssh_params = Ssh1})}.
+
+%%--------------------------------------------------------------------
-spec new_keys(#ssh_msg_newkeys{}, #state{}) -> gen_fsm_state_return().
%%--------------------------------------------------------------------
@@ -1173,7 +1187,10 @@ init_ssh(client = Role, Vsn, Version, Options, Socket) ->
opts = Options,
userauth_supported_methods = AuthMethods,
peer = {PeerName, PeerAddr},
- available_host_keys = supported_host_keys(Role, KeyCb, Options)
+ available_host_keys = supported_host_keys(Role, KeyCb, Options),
+ random_length_padding = proplists:get_value(max_random_length_padding,
+ Options,
+ (#ssh{})#ssh.random_length_padding)
};
init_ssh(server = Role, Vsn, Version, Options, Socket) ->
@@ -1193,7 +1210,10 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) ->
userauth_methods = AuthMethodsAsList,
kb_tries_left = 3,
peer = {undefined, PeerAddr},
- available_host_keys = supported_host_keys(Role, KeyCb, Options)
+ available_host_keys = supported_host_keys(Role, KeyCb, Options),
+ random_length_padding = proplists:get_value(max_random_length_padding,
+ Options,
+ (#ssh{})#ssh.random_length_padding)
}.
supported_host_keys(client, _, Options) ->
diff --git a/lib/ssh/src/ssh_math.erl b/lib/ssh/src/ssh_math.erl
deleted file mode 100644
index cace85bc93..0000000000
--- a/lib/ssh/src/ssh_math.erl
+++ /dev/null
@@ -1,42 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-
-%%% Description: SSH math utilities
-
--module(ssh_math).
-
--export([ipow/3]).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% INTEGER utils
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% calculate A^B mod M
-ipow(A, B, M) when M > 0, B >= 0 ->
- crypto:bytes_to_integer(crypto:mod_pow(A, B, M)).
-
-
-
-
-
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 1f0f6fb15f..7b786b8fff 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -237,7 +237,7 @@ encode(#ssh_msg_kex_dh_gex_request{
max = Max
}) ->
ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST, Min, N, Max],
- [byte, uint32, uint32, uint32, uint32]);
+ [byte, uint32, uint32, uint32]);
encode(#ssh_msg_kex_dh_gex_request_old{n = N}) ->
ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST_OLD, N],
[byte, uint32]);
@@ -257,7 +257,7 @@ encode(#ssh_msg_kex_dh_gex_reply{
}) ->
EncKey = encode_host_key(Key),
EncSign = encode_sign(Key, Signature),
- ssh_bits:encode([?SSH_MSG_KEXDH_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
+ ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
encode(#ssh_msg_ignore{data = Data}) ->
ssh_bits:encode([?SSH_MSG_IGNORE, Data], [byte, string]);
@@ -442,6 +442,19 @@ decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP),
p = Prime,
g = Generator
};
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?UINT32(Len), E:Len/big-signed-integer-unit:8>>) ->
+ #ssh_msg_kex_dh_gex_init{
+ e = E
+ };
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY),
+ ?UINT32(Len0), Key:Len0/binary,
+ ?UINT32(Len1), F:Len1/big-signed-integer-unit:8,
+ ?UINT32(Len2), Hashsign:Len2/binary>>) ->
+ #ssh_msg_kex_dh_gex_reply{
+ public_host_key = decode_host_key(Key),
+ f = F,
+ h_sig = decode_sign(Hashsign)
+ };
decode(<<?BYTE(?SSH_MSG_KEXDH_REPLY), ?UINT32(Len0), Key:Len0/binary,
?UINT32(Len1), F:Len1/big-signed-integer-unit:8,
?UINT32(Len2), Hashsign:Len2/binary>>) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 2e7391e1f8..9ed6c85ff7 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -39,7 +39,7 @@
key_exchange_init_msg/1,
key_init/3, new_keys_message/1,
handle_kexinit_msg/3, handle_kexdh_init/2,
- handle_kex_dh_gex_group/2, handle_kex_dh_gex_reply/2,
+ handle_kex_dh_gex_group/2, handle_kex_dh_gex_init/2, handle_kex_dh_gex_reply/2,
handle_new_keys/2, handle_kex_dh_gex_request/2,
handle_kexdh_reply/2,
unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1,
@@ -66,6 +66,8 @@ algo_classes() -> [kex, public_key, cipher, mac, compression].
default_algorithms(compression) ->
%% Do not announce '[email protected]' because there seem to be problems
supported_algorithms(compression, same(['[email protected]']));
+default_algorithms(kex) ->
+ supported_algorithms(kex, []);
default_algorithms(Alg) ->
supported_algorithms(Alg).
@@ -73,23 +75,29 @@ default_algorithms(Alg) ->
supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()].
supported_algorithms(kex) ->
- ['diffie-hellman-group1-sha1'];
+ select_crypto_supported(
+ [{'diffie-hellman-group14-sha1', [{hashs,sha}]},
+ {'diffie-hellman-group1-sha1', [{hashs,sha}]},
+ {'diffie-hellman-group-exchange-sha256', [{hashs,sha256}]},
+ {'diffie-hellman-group-exchange-sha1', [{hashs,sha}]}
+ ]);
supported_algorithms(public_key) ->
ssh_auth:default_public_key_algorithms();
supported_algorithms(cipher) ->
- Supports = crypto:supports(),
- CipherAlgos = [{aes_ctr, 'aes128-ctr'}, {aes_cbc128, 'aes128-cbc'}, {des3_cbc, '3des-cbc'}],
- Algs = [SshAlgo ||
- {CryptoAlgo, SshAlgo} <- CipherAlgos,
- lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
- same(Algs);
+ same(
+ select_crypto_supported(
+ [{'aes128-ctr', [{ciphers,aes_ctr}]},
+ {'aes128-cbc', [{ciphers,aes_cbc128}]},
+ {'3des-cbc', [{ciphers,des3_cbc}]}
+ ]
+ ));
supported_algorithms(mac) ->
- Supports = crypto:supports(),
- HashAlgos = [{sha256, 'hmac-sha2-256'}, {sha, 'hmac-sha1'}],
- Algs = [SshAlgo ||
- {CryptoAlgo, SshAlgo} <- HashAlgos,
- lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
- same(Algs);
+ same(
+ select_crypto_supported(
+ [{'hmac-sha2-256', [{hashs,sha256}]},
+ {'hmac-sha1', [{hashs,sha}]}
+ ]
+ ));
supported_algorithms(compression) ->
same(['none','zlib','[email protected]']).
@@ -100,7 +108,15 @@ supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
supported_algorithms(Key, BlackList) ->
supported_algorithms(Key) -- BlackList.
-
+select_crypto_supported(L) ->
+ Sup = crypto:supports(),
+ [Name || {Name,CryptoRequires} <- L,
+ crypto_supported(CryptoRequires, Sup)].
+
+crypto_supported(Conditions, Supported) ->
+ lists:all(fun({Tag,CryptoName}) ->
+ lists:member(CryptoName, proplists:get_value(Tag,Supported,[]))
+ end, Conditions).
same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
@@ -135,7 +151,7 @@ ssh_vsn() ->
_:_ -> ""
end.
-random_id(Nlo, Nup) ->
+random_id(Nlo, Nup) ->
[crypto:rand_uniform($a,$z+1) || _<- lists:duplicate(crypto:rand_uniform(Nlo,Nup+1),x) ].
hello_version_msg(Data) ->
@@ -144,7 +160,7 @@ hello_version_msg(Data) ->
next_seqnum(SeqNum) ->
(SeqNum + 1) band 16#ffffffff.
-decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) ->
+decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) ->
<<EncBlock:BlockSize/binary, EncData/binary>> = Bin,
{Ssh, <<?UINT32(PacketLen), _/binary>> = DecData} =
decrypt(Ssh0, EncBlock),
@@ -279,34 +295,50 @@ verify_algorithm(#alg{compress = undefined}) -> false;
verify_algorithm(#alg{decompress = undefined}) -> false;
verify_algorithm(#alg{kex = 'diffie-hellman-group1-sha1'}) -> true;
+verify_algorithm(#alg{kex = 'diffie-hellman-group14-sha1'}) -> true;
verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha1'}) -> true;
+verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha256'}) -> true;
verify_algorithm(_) -> false.
-key_exchange_first_msg('diffie-hellman-group1-sha1', Ssh0) ->
- {G, P} = dh_group1(),
+%%%----------------------------------------------------------------
+%%%
+%%% Key exchange initialization
+%%%
+key_exchange_first_msg(Kex, Ssh0) when Kex == 'diffie-hellman-group1-sha1' ;
+ Kex == 'diffie-hellman-group14-sha1' ->
+ {G, P} = dh_group(Kex),
{Private, Public} = dh_gen_key(G, P, 1024),
+ %% Public = G^Private mod P (def)
{SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_init{e = Public}, Ssh0),
{ok, SshPacket,
Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}};
-key_exchange_first_msg('diffie-hellman-group-exchange-sha1', Ssh0) ->
+key_exchange_first_msg(Kex, Ssh0) when Kex == 'diffie-hellman-group-exchange-sha1' ;
+ Kex == 'diffie-hellman-group-exchange-sha256' ->
Min = ?DEFAULT_DH_GROUP_MIN,
NBits = ?DEFAULT_DH_GROUP_NBITS,
Max = ?DEFAULT_DH_GROUP_MAX,
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kex_dh_gex_request{min = Min,
- n = NBits, max = Max},
+ n = NBits,
+ max = Max},
Ssh0),
{ok, SshPacket,
Ssh1#ssh{keyex_info = {Min, Max, NBits}}}.
-
-handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) ->
- {G, P} = dh_group1(),
+%%%----------------------------------------------------------------
+%%%
+%%% diffie-hellman-group1-sha1
+%%% diffie-hellman-group14-sha1
+%%%
+handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
+ Ssh0 = #ssh{algorithms = #alg{kex=Kex}}) ->
+ %% server
+ {G, P} = dh_group(Kex),
if
1=<E, E=<(P-1) ->
{Private, Public} = dh_gen_key(G, P, 1024),
- K = ssh_math:ipow(E, Private, P),
+ K = dh_compute_key(G, P, E, Private),
Key = get_host_key(Ssh0),
H = kex_h(Ssh0, Key, E, Public, K),
H_SIG = sign_host_key(Ssh0, Key, H),
@@ -314,27 +346,173 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) ->
f = Public,
h_sig = H_SIG
}, Ssh0),
-
{ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}},
shared_secret = K,
exchanged_hash = H,
session_id = sid(Ssh1, H)}};
+
true ->
- Error = {error,bad_e_from_peer},
- Disconnect = #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'f' out of bounds",
- language = "en"},
- throw({Error, Disconnect})
+ throw({{error,bad_e_from_peer},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'e' out of bounds",
+ language = ""}
+ })
end.
+handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey,
+ f = F,
+ h_sig = H_SIG},
+ #ssh{keyex_key = {{Private, Public}, {G, P}}} = Ssh0) ->
+ %% client
+ if
+ 1=<F, F=<(P-1)->
+ K = dh_compute_key(G, P, F, Private),
+ H = kex_h(Ssh0, HostKey, Public, F, K),
+
+ case verify_host_key(Ssh0, HostKey, H, H_SIG) of
+ ok ->
+ {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
+ {ok, SshPacket, Ssh#ssh{shared_secret = K,
+ exchanged_hash = H,
+ session_id = sid(Ssh, H)}};
+ Error ->
+ throw({Error,
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed",
+ language = "en"}
+ })
+ end;
+
+ true ->
+ throw({{error,bad_f_from_peer},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'f' out of bounds",
+ language = ""}
+ })
+ end.
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% diffie-hellman-group-exchange-sha1
+%%%
+handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min,
+ n = NBits,
+ max = Max},
+ Ssh0=#ssh{opts=Opts}) when Min=<NBits, NBits=<Max ->
+ %% server
+ {G, P} = dh_gex_group(Min, NBits, Max, proplists:get_value(dh_gex_groups,Opts)),
+ {Private, Public} = dh_gen_key(G, P, 1024),
+ {SshPacket, Ssh} =
+ ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0),
+ {ok, SshPacket,
+ Ssh#ssh{keyex_key = {{Private, Public}, {G, P}},
+ keyex_info = {Min, Max, NBits}
+ }};
+handle_kex_dh_gex_request(_, _) ->
+ throw({{error,bad_ssh_msg_kex_dh_gex_request},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, bad values in ssh_msg_kex_dh_gex_request",
+ language = ""}
+ }).
+
handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) ->
- {Private, Public} = dh_gen_key(G,P,1024),
+ %% client
+ {Private, Public} = dh_gen_key(G, P, 1024),
{SshPacket, Ssh1} =
- ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0),
+ ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0), % Pub = G^Priv mod P (def)
+
{ok, SshPacket,
Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}.
+handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
+ #ssh{keyex_key = {{Private, Public}, {G, P}},
+ keyex_info = {Min, Max, NBits}} =
+ Ssh0) ->
+ %% server
+ if
+ 1=<E, E=<(P-1) ->
+ K = dh_compute_key(G, P, E, Private),
+ if
+ 1<K, K<(P-1) ->
+ HostKey = get_host_key(Ssh0),
+ H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, E, Public, K),
+ H_SIG = sign_host_key(Ssh0, HostKey, H),
+ {SshPacket, Ssh} =
+ ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
+ f = Public,
+ h_sig = H_SIG}, Ssh0),
+ {ok, SshPacket, Ssh#ssh{shared_secret = K,
+ exchanged_hash = H,
+ session_id = sid(Ssh, H)
+ }};
+ true ->
+ throw({{error,bad_K},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'K' out of bounds",
+ language = ""}
+ })
+ end;
+ true ->
+ throw({{error,bad_e_from_peer},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'e' out of bounds",
+ language = ""}
+ })
+ end.
+
+handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
+ f = F,
+ h_sig = H_SIG},
+ #ssh{keyex_key = {{Private, Public}, {G, P}},
+ keyex_info = {Min, Max, NBits}} =
+ Ssh0) ->
+ %% client
+ if
+ 1=<F, F=<(P-1)->
+ K = dh_compute_key(G, P, F, Private),
+ if
+ 1<K, K<(P-1) ->
+ H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K),
+
+ case verify_host_key(Ssh0, HostKey, H, H_SIG) of
+ ok ->
+ {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
+ {ok, SshPacket, Ssh#ssh{shared_secret = K,
+ exchanged_hash = H,
+ session_id = sid(Ssh, H)}};
+ _Error ->
+ throw(#ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed",
+ language = ""}
+ )
+ end;
+
+ true ->
+ throw({{error,bad_K},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'K' out of bounds",
+ language = ""}
+ })
+ end;
+ true ->
+ throw({{error,bad_f_from_peer},
+ #ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = "Key exchange failed, 'f' out of bounds",
+ language = ""}
+ })
+ end.
+
+%%%----------------------------------------------------------------
handle_new_keys(#ssh_msg_newkeys{}, Ssh0) ->
try install_alg(Ssh0) of
#ssh{} = Ssh ->
@@ -346,69 +524,6 @@ handle_new_keys(#ssh_msg_newkeys{}, Ssh0) ->
language = "en"})
end.
-
-%% %% Select algorithms
-handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F,
- h_sig = H_SIG},
- #ssh{keyex_key = {{Private, Public}, {_G, P}}} = Ssh0) when 1=<F, F=<(P-1)->
- K = ssh_math:ipow(F, Private, P),
- H = kex_h(Ssh0, HostKey, Public, F, K),
-
- case verify_host_key(Ssh0, HostKey, H, H_SIG) of
- ok ->
- {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
- {ok, SshPacket, Ssh#ssh{shared_secret = K,
- exchanged_hash = H,
- session_id = sid(Ssh, H)}};
- Error ->
- Disconnect = #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed",
- language = "en"},
- throw({Error, Disconnect})
- end;
-handle_kexdh_reply(#ssh_msg_kexdh_reply{}, _SSH) ->
- Error = {error,bad_f_from_peer},
- Disconnect = #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed, 'f' out of bounds",
- language = "en"},
- throw({Error, Disconnect}).
-
-
-handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = _Min,
- n = _NBits,
- max = _Max}, Ssh0) ->
- {G,P} = dh_group1(), %% TODO real imp this seems to be a hack?!
- {Private, Public} = dh_gen_key(G, P, 1024),
- {SshPacket, Ssh} =
- ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0),
- {ok, SshPacket,
- Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}}}.
-
-handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
- f = F,
- h_sig = H_SIG},
- #ssh{keyex_key = {{Private, Public}, {G, P}},
- keyex_info = {Min, Max, NBits}} =
- Ssh0) ->
- K = ssh_math:ipow(F, Private, P),
- H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K),
-
- case verify_host_key(Ssh0, HostKey, H, H_SIG) of
- ok ->
- {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
- {ok, SshPacket, Ssh#ssh{shared_secret = K,
- exchanged_hash = H,
- session_id = sid(Ssh, H)}};
- _Error ->
- Disconnect = #ssh_msg_disconnect{
- code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
- description = "Key exchange failed",
- language = "en"},
- throw(Disconnect)
- end.
-
%% select session id
sid(#ssh{session_id = undefined}, H) ->
H;
@@ -511,7 +626,6 @@ select_algorithm(Role, Client, Server) ->
decompress = Decompression,
c_lng = C_Lng,
s_lng = S_Lng},
-%%ct:pal("~p~n Client=~p~n Server=~p~n Alg=~p~n",[Role,Client,Server,Alg]),
{ok, Alg}.
select_encrypt_decrypt(client, Client, Server) ->
@@ -631,13 +745,20 @@ ssh_packet(Msg, Ssh) ->
pack(Data0, #ssh{encrypt_block_size = BlockSize,
send_sequence = SeqNum, send_mac = MacAlg,
- send_mac_key = MacKey}
+ send_mac_key = MacKey,
+ random_length_padding = RandomLengthPadding}
= Ssh0) when is_binary(Data0) ->
{Ssh1, Data} = compress(Ssh0, Data0),
PL = (BlockSize - ((4 + 1 + size(Data)) rem BlockSize)) rem BlockSize,
- PaddingLen = if PL < 4 -> PL + BlockSize;
- true -> PL
- end,
+ MinPaddingLen = if PL < 4 -> PL + BlockSize;
+ true -> PL
+ end,
+ PadBlockSize = max(BlockSize,4),
+ MaxExtraBlocks = (max(RandomLengthPadding,MinPaddingLen) - MinPaddingLen) div PadBlockSize,
+ ExtraPaddingLen = try crypto:rand_uniform(0,MaxExtraBlocks)*PadBlockSize
+ catch _:_ -> 0
+ end,
+ PaddingLen = MinPaddingLen + ExtraPaddingLen,
Padding = ssh_bits:random(PaddingLen),
PacketLen = 1 + PaddingLen + size(Data),
PacketData = <<?UINT32(PacketLen),?BYTE(PaddingLen),
@@ -706,6 +827,7 @@ verify(PlainText, Hash, Sig, Key) ->
%% key exchange
%%
%% diffie-hellman-group1-sha1 REQUIRED
+%% diffie-hellman-group14-sha1 REQUIRED
%%
%%
@@ -1001,7 +1123,7 @@ recv_mac_init(SSH) ->
recv_mac_final(SSH) ->
{ok, SSH#ssh { recv_mac = none, recv_mac_key = undefined }}.
-mac(none, _ , _, _) ->
+mac(none, _ , _, _) ->
<<>>;
mac('hmac-sha1', Key, SeqNum, Data) ->
crypto:hmac(sha, Key, [<<?UINT32(SeqNum)>>, Data]);
@@ -1020,8 +1142,12 @@ hash(SSH, Char, Bits) ->
case SSH#ssh.kex of
'diffie-hellman-group1-sha1' ->
fun(Data) -> crypto:hash(sha, Data) end;
+ 'diffie-hellman-group14-sha1' ->
+ fun(Data) -> crypto:hash(sha, Data) end;
'diffie-hellman-group-exchange-sha1' ->
fun(Data) -> crypto:hash(sha, Data) end;
+ 'diffie-hellman-group-exchange-sha256' ->
+ fun(Data) -> crypto:hash(sha256, Data) end;
_ ->
exit({bad_algorithm,SSH#ssh.kex})
end,
@@ -1071,8 +1197,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
ssh_message:encode_host_key(Key), Min, NBits, Max,
Prime, Gen, E,F,K], Ts)
end,
- crypto:hash(sha,L).
+ crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L).
+sha('diffie-hellman-group-exchange-sha1') -> sha;
+sha('diffie-hellman-group-exchange-sha256') -> sha256.
+
mac_key_size('hmac-sha1') -> 20*8;
mac_key_size('hmac-sha1-96') -> 20*8;
mac_key_size('hmac-md5') -> 16*8;
@@ -1096,13 +1225,63 @@ peer_name({Host, _}) ->
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-dh_group1() ->
- {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}.
+dh_group('diffie-hellman-group1-sha1') -> ?dh_group1;
+dh_group('diffie-hellman-group14-sha1') -> ?dh_group14.
+
+dh_gex_default_groups() ->
+ [{1024, ?dh_group1 },
+ {2048, ?dh_group14},
+ {3072, ?dh_group15},
+ {4096, ?dh_group16}].
+
+
+dh_gex_group(Min, N, Max, undefined) ->
+ dh_gex_group(Min, N, Max, dh_gex_default_groups());
+dh_gex_group(Min, N, Max, Groups) ->
+ %% First try to find an exact match. If not an exact match, select the largest possible.
+ {_,Group} =
+ lists:foldl(
+ fun(_, {I,G}) when I==N ->
+ %% If we have an exact match already: use that one
+ {I,G};
+ ({I,G}, _) when I==N ->
+ %% If we now found an exact match: use that very one
+ {I,G};
+ ({I,G}, {Imax,_Gmax}) when Min=<I,I=<Max, % a) {I,G} fullfills the requirements
+ I>Imax -> % b) {I,G} is larger than current max
+ %% A group within the limits and better than the one we have
+ {I,G};
+ (_, IGmax) ->
+ %% Keep the one we have
+ IGmax
+ end, {-1,undefined}, Groups),
+
+ case Group of
+ undefined ->
+ throw(#ssh_msg_disconnect{
+ code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "No possible diffie-hellman-group-exchange group found",
+ language = ""});
+ _ ->
+ Group
+ end.
+
dh_gen_key(G, P, _) ->
{Public, Private} = crypto:generate_key(dh, [P, G]),
{crypto:bytes_to_integer(Private), crypto:bytes_to_integer(Public)}.
+dh_compute_key(G, P, OthersPublic, MyPrivate) ->
+ crypto:bytes_to_integer(
+ crypto:compute_key(dh, OthersPublic, MyPrivate, [P,G])
+ ).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Other utils
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
trim_tail(Str) ->
lists:reverse(trim_head(lists:reverse(Str))).
diff --git a/lib/ssh/src/ssh_transport.hrl b/lib/ssh/src/ssh_transport.hrl
index 2faf8a9316..9e1de171c2 100644
--- a/lib/ssh/src/ssh_transport.hrl
+++ b/lib/ssh/src/ssh_transport.hrl
@@ -188,49 +188,24 @@
-define(SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 14).
-define(SSH_DISCONNECT_ILLEGAL_USER_NAME, 15).
+%% groups
+
+%%% rfc 2489, ch 6.2
+-define(dh_group1,
+ {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}).
+
+%%% rfc 3526, ch3
+-define(dh_group14,
+ {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF}).
+
+%%% rfc 3526, ch4
+-define(dh_group15,
+ {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF}).
+
+%%% rfc 3526, ch5
+-define(dh_group16,
+ {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF}).
+
-%%%----------------------------------------------------------------------
-%%% # DH_14_xxx
-%%% Description: Oakley group 14 prime numbers and generator. Used in
-%%% diffie-hellman-group1-sha1 key exchange method.
-%%%----------------------------------------------------------------------
-%%%----------------------------------------------------------------------
-%%% # DH_14_P
-%%% Description: Prime for this group
-%%%----------------------------------------------------------------------
-
--define(DH_14_P,
- <<000,000,000,129,000,255,255,255,255,255,255,255,255,201,015,218,
- 162,033,104,194,052,196,198,098,139,128,220,028,209,041,002,078,
- 008,138,103,204,116,002,011,190,166,059,019,155,034,081,074,008,
- 121,142,052,004,221,239,149,025,179,205,058,067,027,048,043,010,
- 109,242,095,020,055,079,225,053,109,109,081,194,069,228,133,181,
- 118,098,094,126,198,244,076,066,233,166,055,237,107,011,255,092,
- 182,244,006,183,237,238,056,107,251,090,137,159,165,174,159,036,
- 017,124,075,031,230,073,040,102,081,236,230,083,129,255,255,255,
- 255,255,255,255,255>>).
-
-%%%----------------------------------------------------------------------
-%%% # DH_14_G
-%%% Description: Generator for DH_14_P.
-%%%----------------------------------------------------------------------
-
--define(DH_14_G, <<0,0,0,1,2>>).
-
-%%%----------------------------------------------------------------------
-%%% # DH_14_Q
-%%% Description: Group order (DH_14_P - 1) / 2.
-%%%----------------------------------------------------------------------
-
--define(DH_14_Q,
- <<000,000,000,128,127,255,255,255,255,255,255,255,228,135,237,081,
- 016,180,097,026,098,099,049,069,192,110,014,104,148,129,039,004,
- 069,051,230,058,001,005,223,083,029,137,205,145,040,165,004,060,
- 199,026,002,110,247,202,140,217,230,157,033,141,152,021,133,054,
- 249,047,138,027,167,240,154,182,182,168,225,034,242,066,218,187,
- 049,047,063,099,122,038,033,116,211,027,246,181,133,255,174,091,
- 122,003,091,246,247,028,053,253,173,068,207,210,215,079,146,008,
- 190,037,143,243,036,148,051,040,246,115,041,192,255,255,255,255,
- 255,255,255,255>>).
-endif. % -ifdef(ssh_transport).
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index bf9fe54c57..6dfff945ac 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -42,6 +42,7 @@ suite() ->
all() ->
[app_test,
appup_test,
+ {group, key_exchange},
{group, dsa_key},
{group, rsa_key},
{group, dsa_pass_key},
@@ -92,6 +93,11 @@ groups() ->
max_sessions_sftp_start_channel_parallel,
max_sessions_sftp_start_channel_sequential
]},
+ {key_exchange, [], ['diffie-hellman-group-exchange-sha1',
+ 'diffie-hellman-group-exchange-sha256',
+ 'diffie-hellman-group1-sha1',
+ 'diffie-hellman-group14-sha1'
+ ]},
{dir_options, [], [user_dir_option,
system_dir_option]}
].
@@ -146,6 +152,11 @@ init_per_group(internal_error, Config) ->
ssh_test_lib:setup_dsa(DataDir, PrivDir),
file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
Config;
+init_per_group(key_exchange, Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_rsa(DataDir, PrivDir),
+ Config;
init_per_group(dir_options, Config) ->
PrivDir = ?config(priv_dir, Config),
%% Make unreadable dir:
@@ -193,6 +204,8 @@ init_per_group(_, Config) ->
end_per_group(hardening_tests, Config) ->
end_per_group(dsa_key, Config);
+end_per_group(key_exchange, Config) ->
+ end_per_group(rsa_key, Config);
end_per_group(dsa_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(PrivDir),
@@ -232,8 +245,8 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ;
Shell = ssh_test_lib:start_shell(Port, IO, UserDir,
[{silently_accept_hosts, true},
{user,"foo"},{password,"bar"}]),
- ct:pal("IO=~p, Shell=~p, self()=~p",[IO,Shell,self()]),
- ct:pal("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
+ ct:log("IO=~p, Shell=~p, self()=~p",[IO,Shell,self()]),
+ ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
[file:native_name_encoding(),io:getopts()]),
wait_for_erlang_first_line([{io,IO}, {shell,Shell}, {sftpd, Sftpd} | Config]);
init_per_testcase(_TestCase, Config) ->
@@ -453,6 +466,7 @@ rekey_limit(Config) ->
DataFile = filename:join(UserDir, "rekey.data"),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {max_random_length_padding, 0},
{user_dir, UserDir},
{user_passwords,
[{"simon", "says"}]}]),
@@ -462,6 +476,7 @@ rekey_limit(Config) ->
{user, "simon"},
{password, "says"},
{rekey_limit, 2500},
+ {max_random_length_padding, 0},
{user_interaction, false},
{silently_accept_hosts, true}]),
@@ -607,7 +622,7 @@ shell(Config) when is_list(Config) ->
{'EXIT', _, _} ->
ct:fail(no_ssh_connection);
ErlShellStart ->
- ct:pal("Erlang shell start: ~p~n", [ErlShellStart]),
+ ct:log("Erlang shell start: ~p~n", [ErlShellStart]),
do_shell(IO, Shell)
end.
@@ -691,7 +706,7 @@ server_password_option(Config) when is_list(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- ct:pal("Test of wrong password: Error msg: ~p ~n", [Reason]),
+ ct:log("Test of wrong password: Error msg: ~p ~n", [Reason]),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
@@ -818,6 +833,59 @@ ssh_msg_debug_fun_option_client(Config) ->
end.
%%--------------------------------------------------------------------
+'diffie-hellman-group-exchange-sha1'(Config) ->
+ kextest('diffie-hellman-group-exchange-sha1',Config).
+
+'diffie-hellman-group-exchange-sha256'(Config) ->
+ kextest('diffie-hellman-group-exchange-sha256',Config).
+
+'diffie-hellman-group1-sha1'(Config) ->
+ kextest('diffie-hellman-group1-sha1',Config).
+
+'diffie-hellman-group14-sha1'(Config) ->
+ kextest('diffie-hellman-group14-sha1',Config).
+
+
+kextest(Kex, Config) ->
+ case lists:member(Kex, ssh_transport:supported_algorithms(kex)) of
+ true ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {user_passwords, [{"foo", "bar"}]},
+ {preferred_algorithms,
+ [{kex, [Kex]}]},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "bar"},
+ {user_dir, UserDir},
+ {preferred_algorithms,
+ [{kex, [Kex]}]},
+ {user_interaction, false}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:exec(ConnectionRef, ChannelId,
+ "1+1.", infinity),
+ Data = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"2\n">>}},
+ case ssh_test_lib:receive_exec_result(Data) of
+ expected ->
+ ok;
+ Other ->
+ ct:fail(Other)
+ end,
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ ssh:stop_daemon(Pid);
+ false ->
+ {skip, lists:concat([Kex, " is not supported"])}
+ end.
+
+%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1193,13 +1261,13 @@ peername_sockname(Config) when is_list(Config) ->
ssh:connection_info(ConnectionRef, [peer]),
[{sockname, {HostSockClient,PortSockClient} = ClientSock}] =
ssh:connection_info(ConnectionRef, [sockname]),
- ct:pal("Client: ~p ~p", [ClientPeer, ClientSock]),
+ ct:log("Client: ~p ~p", [ClientPeer, ClientSock]),
receive
{ssh_cm, ConnectionRef, {data, ChannelId, _, Response}} ->
{PeerNameSrv,SockNameSrv} = binary_to_term(Response),
{HostPeerSrv,PortPeerSrv} = PeerNameSrv,
{HostSockSrv,PortSockSrv} = SockNameSrv,
- ct:pal("Server: ~p ~p", [PeerNameSrv, SockNameSrv]),
+ ct:log("Server: ~p ~p", [PeerNameSrv, SockNameSrv]),
host_equal(HostPeerSrv, HostSockClient),
PortPeerSrv = PortSockClient,
host_equal(HostSockSrv, HostPeerClient),
@@ -1377,7 +1445,7 @@ packet_size_zero(Config) ->
receive
{ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:pal("Got ~p",[M]),
+ ct:log("Got ~p",[M]),
ct:fail(doesnt_obey_max_packet_size_0)
after 5000 ->
ok
@@ -1454,7 +1522,7 @@ chan_exec(ConnectionRef, Cmnd, Expected) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
= ExitStatus0} ->
- ct:pal("0: Collected data ~p", [ExitStatus0]),
+ ct:log("0: Collected data ~p", [ExitStatus0]),
ssh_test_lib:receive_exec_result(Data0,
ConnectionRef, ChannelId0);
Other0 ->
@@ -1546,7 +1614,7 @@ ssh_connect_negtimeout(Config, Parallel) ->
{ok,Socket} = gen_tcp:connect(Host, Port, []),
Factor = 2,
- ct:pal("And now sleeping ~p*NegTimeOut (~p ms)...", [Factor, round(Factor * NegTimeOut)]),
+ ct:log("And now sleeping ~p*NegTimeOut (~p ms)...", [Factor, round(Factor * NegTimeOut)]),
ct:sleep(round(Factor * NegTimeOut)),
case inet:sockname(Socket) of
@@ -1577,22 +1645,22 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) ->
{parallel_login, Parallel},
{negotiation_timeout, NegTimeOut},
{failfun, fun ssh_test_lib:failfun/2}]),
- ct:pal("~p Listen ~p:~p",[_Pid,_Host,Port]),
+ ct:log("~p Listen ~p:~p",[_Pid,_Host,Port]),
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
receive
Error = {'EXIT', _, _} ->
- ct:pal("~p",[Error]),
+ ct:log("~p",[Error]),
ct:fail(no_ssh_connection);
ErlShellStart ->
- ct:pal("---Erlang shell start: ~p~n", [ErlShellStart]),
+ ct:log("---Erlang shell start: ~p~n", [ErlShellStart]),
one_shell_op(IO, NegTimeOut),
one_shell_op(IO, NegTimeOut),
Factor = 2,
- ct:pal("And now sleeping ~p*NegTimeOut (~p ms)...", [Factor, round(Factor * NegTimeOut)]),
+ ct:log("And now sleeping ~p*NegTimeOut (~p ms)...", [Factor, round(Factor * NegTimeOut)]),
ct:sleep(round(Factor * NegTimeOut)),
one_shell_op(IO, NegTimeOut)
@@ -1601,7 +1669,7 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) ->
one_shell_op(IO, TimeOut) ->
- ct:pal("One shell op: Waiting for prompter"),
+ ct:log("One shell op: Waiting for prompter"),
receive
ErlPrompt0 -> ct:log("Erlang prompt: ~p~n", [ErlPrompt0])
after TimeOut -> ct:fail("Timeout waiting for promter")
@@ -1704,7 +1772,7 @@ connect_fun(ssh_sftp__start_channel, _Config) ->
max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
Connect = fun(Host,Port) ->
R = Connect0(Host,Port),
- ct:pal("Connect(~p,~p) -> ~p",[Host,Port,R]),
+ ct:log("Connect(~p,~p) -> ~p",[Host,Port,R]),
R
end,
SystemDir = filename:join(?config(priv_dir, Config), system),
@@ -1717,7 +1785,7 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
{parallel_login, ParallelLogin},
{max_sessions, MaxSessions}
]),
- ct:pal("~p Listen ~p:~p for max ~p sessions",[Pid,Host,Port,MaxSessions]),
+ ct:log("~p Listen ~p:~p for max ~p sessions",[Pid,Host,Port,MaxSessions]),
try [Connect(Host,Port) || _ <- lists:seq(1,MaxSessions)]
of
Connections ->
@@ -1726,7 +1794,7 @@ max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
[_|_] = Connections,
%% Now try one more than alowed:
- ct:pal("Info Report might come here...",[]),
+ ct:log("Info Report might come here...",[]),
try Connect(Host,Port)
of
_ConnectionRef1 ->
@@ -1855,12 +1923,12 @@ basic_test(Config) ->
do_shell(IO, Shell) ->
receive
ErlPrompt0 ->
- ct:pal("Erlang prompt: ~p~n", [ErlPrompt0])
+ ct:log("Erlang prompt: ~p~n", [ErlPrompt0])
end,
IO ! {input, self(), "1+1.\r\n"},
receive
Echo0 ->
- ct:pal("Echo: ~p ~n", [Echo0])
+ ct:log("Echo: ~p ~n", [Echo0])
end,
receive
?NEWLINE ->
@@ -1868,7 +1936,7 @@ do_shell(IO, Shell) ->
end,
receive
Result0 = <<"2">> ->
- ct:pal("Result: ~p~n", [Result0])
+ ct:log("Result: ~p~n", [Result0])
end,
receive
?NEWLINE ->
@@ -1876,7 +1944,7 @@ do_shell(IO, Shell) ->
end,
receive
ErlPrompt1 ->
- ct:pal("Erlang prompt: ~p~n", [ErlPrompt1])
+ ct:log("Erlang prompt: ~p~n", [ErlPrompt1])
end,
exit(Shell, kill).
%%Does not seem to work in the testserver!
@@ -1887,7 +1955,7 @@ do_shell(IO, Shell) ->
%% end,
%% receive
%% Echo1 ->
- %% ct:pal("Echo: ~p ~n", [Echo1])
+ %% ct:log("Echo: ~p ~n", [Echo1])
%% end,
%% receive
%% ?NEWLINE ->
@@ -1895,7 +1963,7 @@ do_shell(IO, Shell) ->
%% end,
%% receive
%% Result1 ->
- %% ct:pal("Result: ~p~n", [Result1])
+ %% ct:log("Result: ~p~n", [Result1])
%% end,
%% receive
%% {'EXIT', Shell, killed} ->
@@ -1909,13 +1977,13 @@ wait_for_erlang_first_line(Config) ->
{'EXIT', _, _} ->
{fail,no_ssh_connection};
<<"Eshell ",_/binary>> = _ErlShellStart ->
- ct:pal("Erlang shell start: ~p~n", [_ErlShellStart]),
+ ct:log("Erlang shell start: ~p~n", [_ErlShellStart]),
Config;
Other ->
- ct:pal("Unexpected answer from ssh server: ~p",[Other]),
+ ct:log("Unexpected answer from ssh server: ~p",[Other]),
{fail,unexpected_answer}
after 10000 ->
- ct:pal("No answer from ssh-server"),
+ ct:log("No answer from ssh-server"),
{fail,timeout}
end.
@@ -1931,7 +1999,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
PfxSize = size(Pfx),
receive
_X = <<"\r\n">> ->
- ct:pal("Skip newline ~p",[_X]),
+ ct:log("Skip newline ~p",[_X]),
new_do_shell(IO, N, Ops);
<<Pfx:PfxSize/binary,P1,"> ">> when (P1-$0)==N ->
@@ -1947,15 +2015,15 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("new_do_shell error: ~p~n",[Err]);
RecBin when Order==expect ; Order==expect_echo ->
- ct:pal("received ~p",[RecBin]),
+ ct:log("received ~p",[RecBin]),
RecStr = string:strip(unicode:characters_to_list(RecBin)),
ExpStr = string:strip(Arg),
case lists:prefix(ExpStr, RecStr) of
true when Order==expect ->
- ct:pal("Matched ~ts",[RecStr]),
+ ct:log("Matched ~ts",[RecStr]),
new_do_shell(IO, N, More);
true when Order==expect_echo ->
- ct:pal("Matched echo ~ts",[RecStr]),
+ ct:log("Matched echo ~ts",[RecStr]),
new_do_shell(IO, N, More);
false ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
@@ -1981,12 +2049,12 @@ prompt_prefix() ->
new_do_shell_prompt(IO, N, type, Str, More) ->
- ct:pal("Matched prompt ~p to trigger sending of next line to server",[N]),
+ ct:log("Matched prompt ~p to trigger sending of next line to server",[N]),
IO ! {input, self(), Str++"\r\n"},
- ct:pal("Promt '~p> ', Sent ~ts",[N,Str++"\r\n"]),
+ ct:log("Promt '~p> ', Sent ~ts",[N,Str++"\r\n"]),
new_do_shell(IO, N, [{expect_echo,Str}|More]); % expect echo of the sent line
new_do_shell_prompt(IO, N, Op, Str, More) ->
- ct:pal("Matched prompt ~p",[N]),
+ ct:log("Matched prompt ~p",[N]),
new_do_shell(IO, N, [{Op,Str}|More]).
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index eb7c641d8a..fbcf06290a 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -186,7 +186,7 @@ big_cat(Config) when is_list(Config) ->
%% pre-adjust receive window so the other end doesn't block
ssh_connection:adjust_window(ConnectionRef, ChannelId0, size(Data)),
- ct:pal("sending ~p byte binary~n",[size(Data)]),
+ ct:log("sending ~p byte binary~n",[size(Data)]),
ok = ssh_connection:send(ConnectionRef, ChannelId0, Data, 10000),
ok = ssh_connection:send_eof(ConnectionRef, ChannelId0),
@@ -197,10 +197,10 @@ big_cat(Config) when is_list(Config) ->
{ok, Other} ->
case size(Data) =:= size(Other) of
true ->
- ct:pal("received and sent data are same"
+ ct:log("received and sent data are same"
"size but do not match~n",[]);
false ->
- ct:pal("sent ~p but only received ~p~n",
+ ct:log("sent ~p but only received ~p~n",
[size(Data), size(Other)])
end,
ct:fail(receive_data_mismatch);
@@ -450,7 +450,7 @@ gracefull_invalid_version(Config) when is_list(Config) ->
ok = gen_tcp:send(S, ["SSH-8.-1","\r\n"]),
receive
Verstring ->
- ct:pal("Server version: ~p~n", [Verstring]),
+ ct:log("Server version: ~p~n", [Verstring]),
receive
{tcp_closed, S} ->
ok
@@ -470,7 +470,7 @@ gracefull_invalid_start(Config) when is_list(Config) ->
ok = gen_tcp:send(S, ["foobar","\r\n"]),
receive
Verstring ->
- ct:pal("Server version: ~p~n", [Verstring]),
+ ct:log("Server version: ~p~n", [Verstring]),
receive
{tcp_closed, S} ->
ok
@@ -490,7 +490,7 @@ gracefull_invalid_long_start(Config) when is_list(Config) ->
ok = gen_tcp:send(S, [lists:duplicate(257, $a), "\r\n"]),
receive
Verstring ->
- ct:pal("Server version: ~p~n", [Verstring]),
+ ct:log("Server version: ~p~n", [Verstring]),
receive
{tcp_closed, S} ->
ok
@@ -511,7 +511,7 @@ gracefull_invalid_long_start_no_nl(Config) when is_list(Config) ->
ok = gen_tcp:send(S, [lists:duplicate(257, $a), "\r\n"]),
receive
Verstring ->
- ct:pal("Server version: ~p~n", [Verstring]),
+ ct:log("Server version: ~p~n", [Verstring]),
receive
{tcp_closed, S} ->
ok
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index d82cdaf2c7..dc02b940d7 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -56,7 +56,11 @@ groups() ->
lib_no_match
]},
{kex, [], [no_common_alg_server_disconnects,
- no_common_alg_client_disconnects
+ no_common_alg_client_disconnects,
+ gex_client_init_default_noexact,
+ gex_client_init_default_exact,
+ gex_client_init_option_groups,
+ gex_client_init_option_groups_file
]}
].
@@ -68,9 +72,32 @@ end_per_suite(Config) ->
stop_apps(Config).
+
+init_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ;
+ TC == gex_client_init_default_exact ;
+ TC == gex_client_init_option_groups ;
+ TC == gex_client_init_option_groups_file ->
+ Opts = case TC of
+ gex_client_init_option_groups ->
+ [{dh_gex_groups, [{2345, 3, 41}]}];
+ gex_client_init_option_groups_file ->
+ DataDir = ?config(data_dir, Config),
+ F = filename:join(DataDir, "dh_group_test"),
+ [{dh_gex_groups, {file,F}}];
+ _ ->
+ []
+ end,
+ start_std_daemon(Config,
+ [{preferred_algorithms, ssh_transport:supported_algorithms()}
+ | Opts]);
init_per_testcase(_TestCase, Config) ->
check_std_daemon_works(Config, ?LINE).
+end_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ;
+ TC == gex_client_init_default_exact ;
+ TC == gex_client_init_option_groups ;
+ TC == gex_client_init_option_groups_file ->
+ stop_std_daemon(Config);
end_per_testcase(_TestCase, Config) ->
check_std_daemon_works(Config, ?LINE).
@@ -213,7 +240,7 @@ lib_no_match(_Config) ->
no_common_alg_server_disconnects(Config) ->
{ok,_} =
ssh_trpt_test_lib:exec(
- [{set_options, [print_ops, print_seqnums, print_messages]},
+ [{set_options, [print_ops, {print_messages,detail}]},
{connect,
server_host(Config),server_port(Config),
[{silently_accept_hosts, true},
@@ -224,7 +251,7 @@ no_common_alg_server_disconnects(Config) ->
receive_hello,
{send, hello},
{match, #ssh_msg_kexinit{_='_'}, receive_msg},
- {send, ssh_msg_kexinit},
+ {send, ssh_msg_kexinit}, % with server unsupported 'ssh-dss' !
{match,
{'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'},
tcp_closed]},
@@ -248,17 +275,16 @@ no_common_alg_client_disconnects(Config) ->
Parent !
{result,self(),
ssh_trpt_test_lib:exec(
- [{set_options, [print_ops, print_messages]},
+ [{set_options, [print_ops, {print_messages,detail}]},
{accept, [{system_dir, system_dir(Config)},
{user_dir, user_dir(Config)}]},
receive_hello,
{send, hello},
-
{match, #ssh_msg_kexinit{_='_'}, receive_msg},
- {send, #ssh_msg_kexinit{
+ {send, #ssh_msg_kexinit{ % with unsupported "SOME-UNSUPPORTED"
cookie = 247381486335508958743193106082599558706,
kex_algorithms = ["diffie-hellman-group1-sha1"],
- server_host_key_algorithms = ["some-unknown"],
+ server_host_key_algorithms = ["SOME-UNSUPPORTED"], % SIC!
encryption_algorithms_client_to_server = ["aes128-ctr"],
encryption_algorithms_server_to_client = ["aes128-ctr"],
mac_algorithms_client_to_server = ["hmac-sha2-256"],
@@ -279,7 +305,8 @@ no_common_alg_client_disconnects(Config) ->
}
end),
- %% and finally connect to it with a regular Erlang SSH client:
+ %% and finally connect to it with a regular Erlang SSH client
+ %% which of course does not support SOME-UNSUPPORTED as pub key algo:
Result = std_connect(HostPort, Config, [{preferred_algorithms,[{public_key,['ssh-dss']}]}]),
ct:log("Result of connect is ~p",[Result]),
@@ -287,12 +314,56 @@ no_common_alg_client_disconnects(Config) ->
{result,Pid,{ok,_}} ->
ok;
{result,Pid,{error,{Op,ExecResult,S}}} ->
- ct:pal("ERROR!~nOp = ~p~nExecResult = ~p~nState =~n~s",
+ ct:log("ERROR!~nOp = ~p~nExecResult = ~p~nState =~n~s",
[Op,ExecResult,ssh_trpt_test_lib:format_msg(S)]),
{fail, ExecResult};
- X -> ct:fail(X)
+ X ->
+ ct:log("¤¤¤¤¤"),
+ ct:fail(X)
end.
+%%%--------------------------------------------------------------------
+gex_client_init_default_noexact(Config) ->
+ do_gex_client_init(Config, {2000, 3000, 4000},
+ %% Warning, app knowledege:
+ ?dh_group15).
+
+
+gex_client_init_default_exact(Config) ->
+ do_gex_client_init(Config, {2000, 2048, 4000},
+ %% Warning, app knowledege:
+ ?dh_group14).
+
+
+gex_client_init_option_groups(Config) ->
+ do_gex_client_init(Config, {2000, 2048, 4000}, {3,41}).
+
+
+gex_client_init_option_groups_file(Config) ->
+ do_gex_client_init(Config, {2000, 2048, 4000}, {5,61}).
+
+do_gex_client_init(Config, {Min,N,Max}, {G,P}) ->
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{set_options, [print_ops, print_seqnums, print_messages]},
+ {connect,
+ server_host(Config),server_port(Config),
+ [{silently_accept_hosts, true},
+ {user_dir, user_dir(Config)},
+ {user_interaction, false},
+ {preferred_algorithms,[{kex,['diffie-hellman-group-exchange-sha1']}]}
+ ]},
+ receive_hello,
+ {send, hello},
+ {send, ssh_msg_kexinit},
+ {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+ {send, #ssh_msg_kex_dh_gex_request{min = Min,
+ n = N,
+ max = Max}},
+ {match, #ssh_msg_kex_dh_gex_group{p=P, g=G, _='_'}, receive_msg}
+ ]
+ ).
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
@@ -353,6 +424,7 @@ stop_std_daemon(Config) ->
ct:log("Std server ~p at ~p:~p stopped", [server_pid(Config), server_host(Config), server_port(Config)]),
lists:keydelete(server, 1, Config).
+
check_std_daemon_works(Config, Line) ->
case std_connect(Config) of
{ok,C} ->
@@ -362,13 +434,9 @@ check_std_daemon_works(Config, Line) ->
ok = ssh:close(C),
Config;
Error = {error,_} ->
- {fail,
- lists:flatten(
- io_lib:format("Standard server ~p:~p ~p is ill at line ~p: ~p",
- [server_host(Config), server_port(Config),
- server_pid(Config), Line, Error])
- )
- }
+ ct:fail("Standard server ~p:~p ~p is ill at line ~p: ~p",
+ [server_host(Config), server_port(Config),
+ server_pid(Config), Line, Error])
end.
server_pid(Config) -> element(1,?v(server,Config)).
diff --git a/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test
new file mode 100644
index 0000000000..2887bb4b60
--- /dev/null
+++ b/lib/ssh/test/ssh_protocol_SUITE_data/dh_group_test
@@ -0,0 +1,3 @@
+{2222, 5, 61}.
+{1111, 7, 91}.
+
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 6c631e6f6e..8d0b887d83 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -47,7 +47,7 @@ init_per_suite(Config) ->
catch crypto:stop(),
case (catch crypto:start()) of
ok ->
- ct:pal("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
+ ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
[file:native_name_encoding(),io:getopts()]),
ssh:start(),
Config;
@@ -64,12 +64,20 @@ end_per_suite(Config) ->
groups() ->
[{not_unicode, [], [{group,erlang_server},
{group,openssh_server},
+ {group,'diffie-hellman-group-exchange-sha1'},
+ {group,'diffie-hellman-group-exchange-sha256'},
sftp_nonexistent_subsystem]},
{unicode, [], [{group,erlang_server},
{group,openssh_server},
sftp_nonexistent_subsystem]},
+ {'diffie-hellman-group-exchange-sha1', [], [{group,erlang_server},
+ {group,openssh_server}]},
+
+ {'diffie-hellman-group-exchange-sha256', [], [{group,erlang_server},
+ {group,openssh_server}]},
+
{erlang_server, [], [{group,write_read_tests},
version_option,
{group,remote_tar}]},
@@ -77,7 +85,12 @@ groups() ->
{openssh_server, [], [{group,write_read_tests},
{group,remote_tar}]},
- {remote_tar, [], [create_empty_tar, files_to_tar, big_file_to_tar, files_chunked_to_tar,
+ {remote_tar, [], [create_empty_tar,
+ ascii_filename_ascii_contents_to_tar,
+ ascii_filename_unicode_contents_to_tar,
+ unicode_filename_ascii_contents_to_tar,
+ files_to_tar,
+ big_file_to_tar, files_chunked_to_tar,
directory_to_tar, binaries_to_tar, null_crypto_tar,
simple_crypto_tar_small, simple_crypto_tar_big,
read_tar, read_null_crypto_tar, read_crypto_tar,
@@ -113,23 +126,35 @@ init_per_group(unicode, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
- [{user, "åke高兴"},
- {passwd, "ärlig日本じん"},
- {data, <<"foobar å 一二三四いちにさんち">>},
- {filename, filename:join(PrivDir, "sftp瑞点.txt")},
- {testfile, filename:join(PrivDir, "testハンス.txt")},
- {linktest, filename:join(PrivDir, "link_test語.txt")},
- {tar_filename, filename:join(PrivDir, "sftp_tar_test一二三.tar")},
- {tar_F1_txt, "F一.txt"},
- {datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")}
- | lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end,
- Config,
- [user, passwd, data,
- filename, testfile, linktest,
- tar_filename, tar_F1_txt, datadir_tar
- ]
- )
- ];
+ NewConfig =
+ [{user, "åke高兴"},
+ {passwd, "ärlig日本じん"},
+ {data, <<"foobar å 一二三四いちにさんち">>},
+ {filename, filename:join(PrivDir, "sftp瑞点.txt")},
+ {testfile, filename:join(PrivDir, "testハンス.txt")},
+ {linktest, filename:join(PrivDir, "link_test語.txt")},
+ {tar_filename, filename:join(PrivDir, "sftp_tar_test一二三.tar")},
+ {tar_F1_txt, "F一.txt"},
+ {tar_F3_txt, "f3.txt"},
+ {tar_F4_txt, "g四.txt"},
+ {datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")}
+ | lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end,
+ Config,
+ [user, passwd, data,
+ filename, testfile, linktest,
+ tar_filename, tar_F1_txt, datadir_tar
+ ]
+ )
+ ],
+ FN = fn(?config(tar_F1_txt,NewConfig), NewConfig),
+ case catch file:read_file(FN) of
+ {ok,FN_contents} ->
+ ct:log("Readable file:read_file(~tp) ->~n~tp",[FN,FN_contents]),
+ NewConfig;
+ Other ->
+ ct:log("Unreadable file:read_file(~tp) ->~n~p",[FN,Other]),
+ {skip, "Not unicode file reading"}
+ end;
_ ->
{skip, "Not unicode file encoding"}
@@ -142,22 +167,26 @@ init_per_group(erlang_server, Config) ->
User = ?config(user, Config),
Passwd = ?config(passwd, Config),
Sftpd = {_, HostX, PortX} =
- ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
- {user_passwords,
- [{User, Passwd}]}]),
+ ssh_test_lib:daemon(extra_opts(Config) ++
+ [{system_dir, SysDir},
+ {user_dir, PrivDir},
+ {user_passwords,
+ [{User, Passwd}]}]),
[{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config];
init_per_group(openssh_server, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
Host = ssh_test_lib:hostname(),
- case (catch ssh_sftp:start_channel(Host,
- [{user_interaction, false},
- {silently_accept_hosts, true}])) of
+ case (catch ssh_sftp:start_channel(Host,
+ extra_opts(Config) ++
+ [{user_interaction, false},
+ {silently_accept_hosts, true}])) of
{ok, _ChannelPid, Connection} ->
[{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
ssh:close(Connection),
[{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
+ {error,"Key exchange failed"} ->
+ {skip, "openssh server doesn't support the tested kex algorithm"};
_ ->
{skip, "No openssh server"}
end;
@@ -172,10 +201,11 @@ init_per_group(remote_tar, Config) ->
case ?config(group, Config) of
erlang_server ->
ssh:connect(Host, Port,
- [{user, User},
- {password, Passwd},
- {user_interaction, false},
- {silently_accept_hosts, true}]);
+ extra_opts(Config) ++
+ [{user, User},
+ {password, Passwd},
+ {user_interaction, false},
+ {silently_accept_hosts, true}]);
openssh_server ->
ssh:connect(Host, Port,
[{user_interaction, false},
@@ -184,6 +214,28 @@ init_per_group(remote_tar, Config) ->
[{remote_tar, true},
{connection, Connection} | Config];
+init_per_group('diffie-hellman-group-exchange-sha1', Config) ->
+ case lists:member('diffie-hellman-group-exchange-sha1',
+ ssh_transport:supported_algorithms(kex)) of
+ true ->
+ [{extra_opts, [{preferred_algorithms, [{kex,['diffie-hellman-group-exchange-sha1']}]}]}
+ | Config];
+
+ false ->
+ {skip,"'diffie-hellman-group-exchange-sha1' not supported by this version of erlang ssh"}
+ end;
+
+init_per_group('diffie-hellman-group-exchange-sha256', Config) ->
+ case lists:member('diffie-hellman-group-exchange-sha256',
+ ssh_transport:supported_algorithms(kex)) of
+ true ->
+ [{extra_opts, [{preferred_algorithms, [{kex,['diffie-hellman-group-exchange-sha256']}]}]}
+ | Config];
+
+ false ->
+ {skip,"'diffie-hellman-group-exchange-sha256' not supported by this version of erlang ssh"}
+ end;
+
init_per_group(write_read_tests, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
Config.
@@ -194,7 +246,6 @@ grps(Config) ->
lists:flatten([proplists:get_value(tc_group_properties,Config,[]),
proplists:get_value(tc_group_path,Config,[])])).
-
end_per_group(erlang_server, Config) ->
ct:comment("End ~p",[grps(Config)]),
Config;
@@ -227,11 +278,12 @@ init_per_testcase(version_option, Config) ->
Passwd = ?config(passwd, Config),
{ok, ChannelPid, Connection} =
ssh_sftp:start_channel(Host, Port,
- [{sftp_vsn, 3},
- {user, User},
- {password, Passwd},
- {user_interaction, false},
- {silently_accept_hosts, true}]),
+ extra_opts(Config) ++
+ [{sftp_vsn, 3},
+ {user, User},
+ {password, Passwd},
+ {user_interaction, false},
+ {silently_accept_hosts, true}]),
Sftp = {ChannelPid, Connection},
[{sftp,Sftp}, {watchdog, Dog} | TmpConfig];
@@ -239,7 +291,7 @@ init_per_testcase(Case, Config0) ->
prep(Config0),
Config1 = lists:keydelete(watchdog, 1, Config0),
Config2 = lists:keydelete(sftp, 1, Config1),
- Dog = ct:timetrap(?default_timeout),
+ Dog = ct:timetrap(2 * ?default_timeout),
User = ?config(user, Config0),
Passwd = ?config(passwd, Config0),
@@ -249,10 +301,12 @@ init_per_testcase(Case, Config0) ->
{_,Host, Port} = ?config(sftpd, Config2),
{ok, ChannelPid, Connection} =
ssh_sftp:start_channel(Host, Port,
- [{user, User},
- {password, Passwd},
- {user_interaction, false},
- {silently_accept_hosts, true}]),
+ extra_opts(Config2) ++
+ [{user, User},
+ {password, Passwd},
+ {user_interaction, false},
+ {silently_accept_hosts, true}]
+ ),
Sftp = {ChannelPid, Connection},
[{sftp, Sftp}, {watchdog, Dog} | Config2];
openssh_server when Case == links ->
@@ -261,8 +315,9 @@ init_per_testcase(Case, Config0) ->
Host = ssh_test_lib:hostname(),
{ok, ChannelPid, Connection} =
ssh_sftp:start_channel(Host,
- [{user_interaction, false},
- {silently_accept_hosts, true}]),
+ extra_opts(Config2) ++
+ [{user_interaction, false},
+ {silently_accept_hosts, true}]),
Sftp = {ChannelPid, Connection},
[{sftp, Sftp}, {watchdog, Dog} | Config2]
end,
@@ -342,7 +397,7 @@ read_dir(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
- ct:pal("sftp list dir: ~p~n", [Files]).
+ ct:log("sftp list dir: ~p~n", [Files]).
%%--------------------------------------------------------------------
write_file() ->
@@ -423,12 +478,12 @@ rename_file(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
- ct:pal("FileName: ~p, Files: ~p~n", [FileName, Files]),
+ ct:log("FileName: ~p, Files: ~p~n", [FileName, Files]),
true = lists:member(filename:basename(FileName), Files),
false = lists:member(filename:basename(NewFileName), Files),
ok = ssh_sftp:rename(Sftp, FileName, NewFileName),
{ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
- ct:pal("FileName: ~p, Files: ~p~n", [FileName, NewFiles]),
+ ct:log("FileName: ~p, Files: ~p~n", [FileName, NewFiles]),
false = lists:member(filename:basename(FileName), NewFiles),
true = lists:member(filename:basename(NewFileName), NewFiles).
@@ -474,7 +529,7 @@ retrieve_attributes(Config) when is_list(Config) ->
{ok, NewFileInfo} = file:read_file_info(FileName),
%% TODO comparison. There are some differences now is that ok?
- ct:pal("SFTP: ~p FILE: ~p~n", [FileInfo, NewFileInfo]).
+ ct:log("SFTP: ~p FILE: ~p~n", [FileInfo, NewFileInfo]).
%%--------------------------------------------------------------------
set_attributes() ->
@@ -503,7 +558,7 @@ async_read(Config) when is_list(Config) ->
receive
{async_reply, Ref, {ok, Data}} ->
- ct:pal("Data: ~p~n", [Data]),
+ ct:log("Data: ~p~n", [Data]),
ok;
Msg ->
ct:fail(Msg)
@@ -648,6 +703,43 @@ files_to_tar(Config) ->
chk_tar([F1, "f2.txt"], Config).
%%--------------------------------------------------------------------
+ascii_filename_ascii_contents_to_tar(Config) ->
+ ChPid2 = ?config(channel_pid2, Config),
+ TarFileName = ?config(tar_filename, Config),
+ {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]),
+ ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", [verbose]),
+ ok = erl_tar:close(Handle),
+ chk_tar(["f2.txt"], Config).
+
+%%--------------------------------------------------------------------
+ascii_filename_unicode_contents_to_tar(Config) ->
+ case ?config(tar_F3_txt, Config) of
+ undefined ->
+ {skip, "Unicode test"};
+ Fn ->
+ ChPid2 = ?config(channel_pid2, Config),
+ TarFileName = ?config(tar_filename, Config),
+ {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]),
+ ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]),
+ ok = erl_tar:close(Handle),
+ chk_tar([Fn], Config)
+ end.
+
+%%--------------------------------------------------------------------
+unicode_filename_ascii_contents_to_tar(Config) ->
+ case ?config(tar_F4_txt, Config) of
+ undefined ->
+ {skip, "Unicode test"};
+ Fn ->
+ ChPid2 = ?config(channel_pid2, Config),
+ TarFileName = ?config(tar_filename, Config),
+ {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]),
+ ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]),
+ ok = erl_tar:close(Handle),
+ chk_tar([Fn], Config)
+ end.
+
+%%--------------------------------------------------------------------
big_file_to_tar(Config) ->
ChPid2 = ?config(channel_pid2, Config),
TarFileName = ?config(tar_filename, Config),
@@ -910,7 +1002,8 @@ prep(Config) ->
ok = file:write_file_info(TestFile,
FileInfo#file_info{mode = Mode}).
-
+extra_opts(Config) ->
+ proplists:get_value(extra_opts, Config, []).
chk_tar(Items, Config) ->
chk_tar(Items, Config, []).
diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt
new file mode 100644
index 0000000000..e6076a05b5
--- /dev/null
+++ b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/f3.txt
@@ -0,0 +1 @@
+你好
diff --git a/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt
new file mode 100644
index 0000000000..d18c6b11fc
--- /dev/null
+++ b/lib/ssh/test/ssh_sftp_SUITE_data/sftp_tar_test_data_高兴/g四.txt
@@ -0,0 +1 @@
+How are you?
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index f38fcc5521..94a54ec9db 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -152,7 +152,7 @@ init_per_testcase(TestCase, Config) ->
{ok, <<?SSH_FXP_VERSION, ?UINT32(Version), _Ext/binary>>, _}
= reply(Cm, Channel),
- ct:pal("Client: ~p Server ~p~n", [ProtocolVer, Version]),
+ ct:log("Client: ~p Server ~p~n", [ProtocolVer, Version]),
[{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config].
@@ -418,7 +418,7 @@ real_path(Config) when is_list(Config) ->
RealPath = filename:absname(binary_to_list(Path)),
AbsPrivDir = filename:absname(PrivDir),
- ct:pal("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]),
+ ct:log("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]),
true = RealPath == AbsPrivDir
end.
@@ -447,7 +447,7 @@ links(Config) when is_list(Config) ->
true = binary_to_list(Path) == FileName,
- ct:pal("Path: ~p~n", [binary_to_list(Path)])
+ ct:log("Path: ~p~n", [binary_to_list(Path)])
end.
%%--------------------------------------------------------------------
@@ -548,10 +548,10 @@ set_attributes(Config) when is_list(Config) ->
%% Can not test that NewPermissions = Permissions as
%% on Unix platforms, other bits than those listed in the
%% API may be set.
- ct:pal("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]),
+ ct:log("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]),
true = OrigPermissions =/= NewPermissions,
- ct:pal("Try to open the file"),
+ ct:log("Try to open the file"),
NewReqId = 2,
{ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId), Handle/binary>>, _} =
open_file(FileName, Cm, Channel, NewReqId,
@@ -563,7 +563,7 @@ set_attributes(Config) when is_list(Config) ->
NewReqId1 = 3,
- ct:pal("Set original permissions on the now open file"),
+ ct:log("Set original permissions on the now open file"),
{ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1),
?UINT32(?SSH_FX_OK), _/binary>>, _} =
@@ -786,7 +786,7 @@ read_dir(Handle, Cm, Channel, ReqId) ->
case reply(Cm, Channel) of
{ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count),
?UINT32(Len), Listing:Len/binary, _/binary>>, _} ->
- ct:pal("Count: ~p Listing: ~p~n",
+ ct:log("Count: ~p Listing: ~p~n",
[Count, binary_to_list(Listing)]),
read_dir(Handle, Cm, Channel, ReqId);
{ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index 321e3546cf..7a025a6518 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -159,7 +159,7 @@ close_file(Config) when is_list(Config) ->
NumOfPorts = length(erlang:ports()),
- ct:pal("Number of open ports: ~p~n", [NumOfPorts]),
+ ct:log("Number of open ports: ~p~n", [NumOfPorts]),
{ok, <<_/binary>>} = ssh_sftp:read_file(Sftp, FileName),
@@ -255,14 +255,14 @@ root_dir(Config) when is_list(Config) ->
{ok, Bin} = ssh_sftp:read_file(Sftp, FileName),
{ok, Listing} =
ssh_sftp:list_dir(Sftp, "."),
- ct:pal("Listing: ~p~n", [Listing]).
+ ct:log("Listing: ~p~n", [Listing]).
%%--------------------------------------------------------------------
list_dir_limited(Config) when is_list(Config) ->
{Sftp, _} = ?config(sftp, Config),
{ok, Listing} =
ssh_sftp:list_dir(Sftp, "."),
- ct:pal("Listing: ~p~n", [Listing]).
+ ct:log("Listing: ~p~n", [Listing]).
%%--------------------------------------------------------------------
ver6_basic() ->
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 97c35e549c..988ea47bd8 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -97,10 +97,10 @@ loop_io_server(TestCase, Buff0) ->
{input, TestCase, Line} ->
loop_io_server(TestCase, Buff0 ++ [Line]);
{io_request, From, ReplyAs, Request} ->
-%%ct:pal("~p",[{io_request, From, ReplyAs, Request}]),
+%%ct:log("~p",[{io_request, From, ReplyAs, Request}]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
-%%ct:pal("io_request(~p)-->~p",[Request,{ok, Reply, Buff}]),
+%%ct:log("io_request(~p)-->~p",[Request,{ok, Reply, Buff}]),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
{'EXIT',_, _} ->
@@ -134,26 +134,26 @@ io_request({get_line, _Enc,_}, _, _, _, [Line | Buff]) ->
io_reply(_, _, []) ->
ok;
io_reply(From, ReplyAs, Reply) ->
-%%ct:pal("io_reply ~p sending ~p ! ~p",[self(),From, {io_reply, ReplyAs, Reply}]),
+%%ct:log("io_reply ~p sending ~p ! ~p",[self(),From, {io_reply, ReplyAs, Reply}]),
From ! {io_reply, ReplyAs, Reply}.
reply(_, []) ->
ok;
reply(TestCase, Result) ->
-%%ct:pal("reply ~p sending ~p ! ~p",[self(), TestCase, Result]),
+%%ct:log("reply ~p sending ~p ! ~p",[self(), TestCase, Result]),
TestCase ! Result.
receive_exec_result(Msg) ->
- ct:pal("Expect data! ~p", [Msg]),
+ ct:log("Expect data! ~p", [Msg]),
receive
{ssh_cm,_,{data,_,1, Data}} ->
- ct:pal("StdErr: ~p~n", [Data]),
+ ct:log("StdErr: ~p~n", [Data]),
receive_exec_result(Msg);
Msg ->
- ct:pal("1: Collected data ~p", [Msg]),
+ ct:log("1: Collected data ~p", [Msg]),
expected;
Other ->
- ct:pal("Other ~p", [Other]),
+ ct:log("Other ~p", [Other]),
{unexpected_msg, Other}
end.
@@ -165,15 +165,15 @@ receive_exec_end(ConnectionRef, ChannelId) ->
case receive_exec_result(ExitStatus) of
{unexpected_msg, Eof} -> %% Open ssh seems to not allways send these messages
%% in the same order!
- ct:pal("2: Collected data ~p", [Eof]),
+ ct:log("2: Collected data ~p", [Eof]),
case receive_exec_result(ExitStatus) of
expected ->
expected = receive_exec_result(Closed);
{unexpected_msg, Closed} ->
- ct:pal("3: Collected data ~p", [Closed])
+ ct:log("3: Collected data ~p", [Closed])
end;
expected ->
- ct:pal("4: Collected data ~p", [ExitStatus]),
+ ct:log("4: Collected data ~p", [ExitStatus]),
expected = receive_exec_result(Eof),
expected = receive_exec_result(Closed);
Other ->
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index b7283202a3..06bf264033 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -51,13 +51,15 @@ groups() ->
erlang_client_openssh_server_publickey_rsa,
erlang_client_openssh_server_publickey_dsa,
erlang_client_openssh_server_password,
+ erlang_client_openssh_server_kexs,
erlang_client_openssh_server_nonexistent_subsystem
]},
{erlang_server, [], [erlang_server_openssh_client_exec,
erlang_server_openssh_client_exec_compressed,
erlang_server_openssh_client_pulic_key_dsa,
erlang_server_openssh_client_cipher_suites,
- erlang_server_openssh_client_macs]}
+ erlang_server_openssh_client_macs,
+ erlang_server_openssh_client_kexs]}
].
init_per_suite(Config) ->
@@ -99,6 +101,12 @@ init_per_testcase(erlang_server_openssh_client_cipher_suites, Config) ->
init_per_testcase(erlang_server_openssh_client_macs, Config) ->
check_ssh_client_support(Config);
+init_per_testcase(erlang_server_openssh_client_kexs, Config) ->
+ check_ssh_client_support(Config);
+
+init_per_testcase(erlang_client_openssh_server_kexs, Config) ->
+ check_ssh_client_support(Config);
+
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
@@ -140,7 +148,7 @@ erlang_client_openssh_server_exec(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}}
= ExitStatus0} ->
- ct:pal("0: Collected data ~p", [ExitStatus0]),
+ ct:log("0: Collected data ~p", [ExitStatus0]),
ssh_test_lib:receive_exec_result(Data0,
ConnectionRef, ChannelId0);
Other0 ->
@@ -156,7 +164,7 @@ erlang_client_openssh_server_exec(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId1, 0}}
= ExitStatus1} ->
- ct:pal("0: Collected data ~p", [ExitStatus1]),
+ ct:log("0: Collected data ~p", [ExitStatus1]),
ssh_test_lib:receive_exec_result(Data1,
ConnectionRef, ChannelId1);
Other1 ->
@@ -182,13 +190,55 @@ erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
{unexpected_msg,{ssh_cm, ConnectionRef,
{exit_status, ChannelId, 0}} = ExitStatus} ->
- ct:pal("0: Collected data ~p", [ExitStatus]),
+ ct:log("0: Collected data ~p", [ExitStatus]),
ssh_test_lib:receive_exec_result(Data, ConnectionRef, ChannelId);
Other ->
ct:fail(Other)
end.
%%--------------------------------------------------------------------
+erlang_client_openssh_server_kexs() ->
+ [{doc, "Test that we can connect with different KEXs."}].
+
+erlang_client_openssh_server_kexs(Config) when is_list(Config) ->
+ Success =
+ lists:foldl(
+ fun(Kex, Acc) ->
+ ConnectionRef =
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false},
+ {preferred_algorithms,
+ [{kex,[Kex]}]}]),
+
+ {ok, ChannelId} =
+ ssh_connection:session_channel(ConnectionRef, infinity),
+ success =
+ ssh_connection:exec(ConnectionRef, ChannelId,
+ "echo testing", infinity),
+
+ ExpectedData = {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<"testing\n">>}},
+ case ssh_test_lib:receive_exec_result(ExpectedData) of
+ expected ->
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ Acc;
+ {unexpected_msg,{ssh_cm, ConnectionRef,
+ {exit_status, ChannelId, 0}} = ExitStatus} ->
+ ct:log("0: Collected data ~p", [ExitStatus]),
+ ssh_test_lib:receive_exec_result(ExpectedData, ConnectionRef, ChannelId),
+ Acc;
+ Other ->
+ ct:log("~p failed: ~p",[Kex,Other]),
+ false
+ end
+ end, true, ssh_transport:supported_algorithms(kex)),
+ case Success of
+ true ->
+ ok;
+ false ->
+ {fail, "Kex failed for one or more algos"}
+ end.
+
+%%--------------------------------------------------------------------
erlang_server_openssh_client_exec() ->
[{doc, "Test that exec command works."}].
@@ -206,7 +256,7 @@ erlang_server_openssh_client_exec(Config) when is_list(Config) ->
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " 1+1.",
- ct:pal("Cmd: ~p~n", [Cmd]),
+ ct:log("Cmd: ~p~n", [Cmd]),
SshPort = open_port({spawn, Cmd}, [binary]),
@@ -247,7 +297,7 @@ erlang_server_openssh_client_cipher_suites(Config) when is_list(Config) ->
" -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " " ++
" -c " ++ Cipher ++ " 1+1.",
- ct:pal("Cmd: ~p~n", [Cmd]),
+ ct:log("Cmd: ~p~n", [Cmd]),
SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
@@ -297,7 +347,7 @@ erlang_server_openssh_client_macs(Config) when is_list(Config) ->
" -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " " ++
" -o MACs=" ++ MAC ++ " 1+1.",
- ct:pal("Cmd: ~p~n", [Cmd]),
+ ct:log("Cmd: ~p~n", [Cmd]),
SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
@@ -322,6 +372,70 @@ erlang_server_openssh_client_macs(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+erlang_server_openssh_client_kexs() ->
+ [{doc, "Test that we can connect with different KEXs."}].
+
+erlang_server_openssh_client_kexs(Config) when is_list(Config) ->
+ SystemDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {preferred_algorithms,
+ [{kex,ssh_transport:supported_algorithms(kex)}]}
+ ]),
+ ct:sleep(500),
+
+ ErlKexs = lists:map(fun erlang:atom_to_list/1,
+ ssh_transport:supported_algorithms(kex)),
+ OpenSshKexs = string:tokens(os:cmd("ssh -Q kex"), "\n"),
+
+ Kexs = [{OpenSshKex,lists:member(OpenSshKex,ErlKexs)}
+ || OpenSshKex <- OpenSshKexs],
+
+ Success =
+ lists:foldl(
+ fun({Kex, Expect}, Acc) ->
+ Cmd = "ssh -p " ++ integer_to_list(Port) ++
+ " -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " " ++
+ " -o KexAlgorithms=" ++ Kex ++ " 1+1.",
+
+ ct:log("Cmd: ~p~n", [Cmd]),
+
+ SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
+
+ case Expect of
+ true ->
+ receive
+ {SshPort,{data, <<"2\n">>}} ->
+ Acc
+ after ?TIMEOUT ->
+ ct:log("Did not receive answer for ~p",[Kex]),
+ false
+ end;
+ false ->
+ receive
+ {SshPort,{data, <<"Unable to negotiate a key exchange method", _/binary>>}} ->
+ Acc
+ after ?TIMEOUT ->
+ ct:log("Did not receive no matching kex message for ~p",[Kex]),
+ false
+ end
+ end
+ end, true, Kexs),
+
+ ssh:stop_daemon(Pid),
+
+ case Success of
+ true ->
+ ok;
+ false ->
+ {fail, "Kex failed for one or more algos"}
+ end.
+
+
+%%--------------------------------------------------------------------
erlang_server_openssh_client_exec_compressed() ->
[{doc, "Test that exec command works."}].
@@ -380,11 +494,11 @@ erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
{data,0,1, UnxpectedData}}} ->
%% Some os may return things as
%% ENV_TEST: Undefined variable.\n"
- ct:pal("UnxpectedData: ~p", [UnxpectedData]),
+ ct:log("UnxpectedData: ~p", [UnxpectedData]),
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId);
{unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId, 0}}
= ExitStatus} ->
- ct:pal("0: Collected data ~p", [ExitStatus]),
+ ct:log("0: Collected data ~p", [ExitStatus]),
ssh_test_lib:receive_exec_result(Data,
ConnectionRef, ChannelId);
Other ->
@@ -487,7 +601,7 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- ct:pal("Test of user foo that does not exist. "
+ ct:log("Test of user foo that does not exist. "
"Error msg: ~p~n", [Reason0]),
User = string:strip(os:cmd("whoami"), right, $\n),
@@ -501,10 +615,10 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
{password, "foo"},
{user_interaction, false},
{user_dir, UserDir}]),
- ct:pal("Test of wrong Pasword. "
+ ct:log("Test of wrong Pasword. "
"Error msg: ~p~n", [Reason1]);
_ ->
- ct:pal("Whoami failed reason: ~n", [])
+ ct:log("Whoami failed reason: ~n", [])
end.
%%--------------------------------------------------------------------
@@ -532,19 +646,19 @@ erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config)
receive_hej() ->
receive
<<"Hej", _binary>> = Hej ->
- ct:pal("Expected result: ~p~n", [Hej]);
+ ct:log("Expected result: ~p~n", [Hej]);
<<"Hej\n", _binary>> = Hej ->
- ct:pal("Expected result: ~p~n", [Hej]);
+ ct:log("Expected result: ~p~n", [Hej]);
<<"Hej\r\n", _/binary>> = Hej ->
- ct:pal("Expected result: ~p~n", [Hej]);
+ ct:log("Expected result: ~p~n", [Hej]);
Info ->
Lines = binary:split(Info, [<<"\r\n">>], [global]),
case lists:member(<<"Hej">>, Lines) of
true ->
- ct:pal("Expected result found in lines: ~p~n", [Lines]),
+ ct:log("Expected result found in lines: ~p~n", [Lines]),
ok;
false ->
- ct:pal("Extra info: ~p~n", [Info]),
+ ct:log("Extra info: ~p~n", [Info]),
receive_hej()
end
end.
@@ -558,7 +672,7 @@ receive_logout() ->
ok
end;
Info ->
- ct:pal("Extra info when logging out: ~p~n", [Info]),
+ ct:log("Extra info when logging out: ~p~n", [Info]),
receive_logout()
end.
@@ -601,6 +715,6 @@ check_ssh_client_support2(P) ->
{P, {exit_status, E}} ->
E
after 5000 ->
- ct:pal("Openssh command timed out ~n"),
+ ct:log("Openssh command timed out ~n"),
-1
end.
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index 75f495b6d7..38b2789742 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -42,8 +42,8 @@
seen_hello = false,
enc = <<>>,
ssh = #ssh{}, % #ssh{}
- own_kexinit,
- peer_kexinit,
+ alg_neg = {undefined,undefined}, % {own_kexinit, peer_kexinit}
+ alg, % #alg{}
vars = dict:new(),
reply = [], % Some repy msgs are generated hidden in ssh_transport :[
prints = [],
@@ -140,7 +140,10 @@ op(receive_hello, S0) when S0#s.seen_hello =/= true ->
op(receive_msg, S) when S#s.seen_hello == true ->
try recv(S)
catch
- {tcp,Exc} -> S#s{return_value=Exc}
+ {tcp,Exc} ->
+ S1 = opt(print_messages, S,
+ fun(X) when X==true;X==detail -> {"Recv~n~p~n",[Exc]} end),
+ S1#s{return_value=Exc}
end;
@@ -206,6 +209,9 @@ op_val(E, S0) ->
end.
+fail(Reason, {Fmt,Args}, S) when is_list(Fmt), is_list(Args) ->
+ fail(Reason, save_prints({Fmt,Args}, S)).
+
fail(Reason, S) ->
throw({fail, Reason, S}).
@@ -312,38 +318,51 @@ send(S=#s{ssh=C}, hello) ->
send(S, list_to_binary(Hello));
send(S0, ssh_msg_kexinit) ->
- {Msg, Bytes, C0} = ssh_transport:key_exchange_init_msg(S0#s.ssh),
- S1 = opt(print_messages, S0,
- fun(X) when X==true;X==detail -> {"Send~n~s~n",[format_msg(Msg)]} end),
- S = case ?role(S1) of
- server when is_record(S1#s.peer_kexinit, ssh_msg_kexinit) ->
- {ok, C} =
- ssh_transport:handle_kexinit_msg(S1#s.peer_kexinit, Msg, C0),
- S1#s{peer_kexinit = used,
- own_kexinit = used,
- ssh = C};
- _ ->
- S1#s{ssh = C0,
- own_kexinit = Msg}
- end,
- send_bytes(Bytes, S#s{return_value = Msg});
+ {Msg, _Bytes, _C0} = ssh_transport:key_exchange_init_msg(S0#s.ssh),
+ send(S0, Msg);
-send(S0, ssh_msg_kexdh_init) when ?role(S0) == client,
- is_record(S0#s.peer_kexinit, ssh_msg_kexinit),
- is_record(S0#s.own_kexinit, ssh_msg_kexinit) ->
+send(S0=#s{alg_neg={undefined,PeerMsg}}, Msg=#ssh_msg_kexinit{}) ->
+ S1 = opt(print_messages, S0,
+ fun(X) when X==true;X==detail -> {"Send~n~s~n",[format_msg(Msg)]} end),
+ S2 = case PeerMsg of
+ #ssh_msg_kexinit{} ->
+ try ssh_transport:handle_kexinit_msg(PeerMsg, Msg, S1#s.ssh) of
+ {ok,Cx} when ?role(S1) == server ->
+ S1#s{alg = Cx#ssh.algorithms};
+ {ok,_NextKexMsgBin,Cx} when ?role(S1) == client ->
+ S1#s{alg = Cx#ssh.algorithms}
+ catch
+ Class:Exc ->
+ save_prints({"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
+ [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(Msg)]},
+ S1)
+ end;
+ undefined ->
+ S1
+ end,
+ {Bytes, C} = ssh_transport:ssh_packet(Msg, S2#s.ssh),
+ send_bytes(Bytes, S2#s{return_value = Msg,
+ alg_neg = {Msg,PeerMsg},
+ ssh = C});
+
+send(S0, ssh_msg_kexdh_init) when ?role(S0) == client ->
+ {OwnMsg, PeerMsg} = S0#s.alg_neg,
{ok, NextKexMsgBin, C} =
- ssh_transport:handle_kexinit_msg(S0#s.peer_kexinit, S0#s.own_kexinit, S0#s.ssh),
-
+ try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S0#s.ssh)
+ catch
+ Class:Exc ->
+ fail("Algoritm negotiation failed!",
+ {"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s",
+ [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
+ S0)
+ end,
S = opt(print_messages, S0,
fun(X) when X==true;X==detail ->
#ssh{keyex_key = {{_Private, Public}, {_G, _P}}} = C,
Msg = #ssh_msg_kexdh_init{e = Public},
{"Send (reconstructed)~n~s~n",[format_msg(Msg)]}
end),
-
- send_bytes(NextKexMsgBin, S#s{ssh = C,
- peer_kexinit = used,
- own_kexinit = used});
+ send_bytes(NextKexMsgBin, S#s{ssh = C});
send(S0, ssh_msg_kexdh_reply) ->
Bytes = proplists:get_value(ssh_msg_kexdh_reply, S0#s.reply),
@@ -389,26 +408,42 @@ recv(S0 = #s{}) ->
true ->
%% Has seen hello, therefore no more crlf-messages are alowed.
S = receive_binary_msg(S1),
- case M=S#s.return_value of
- #ssh_msg_kexinit{} when ?role(S) == server,
- S#s.own_kexinit =/= undefined ->
- {ok, C} =
- ssh_transport:handle_kexinit_msg(M, S#s.own_kexinit, S#s.ssh),
- S#s{peer_kexinit = used,
- own_kexinit = used,
- ssh = C};
- #ssh_msg_kexinit{} ->
- S#s{peer_kexinit = M};
+ case PeerMsg = S#s.return_value of
+ #ssh_msg_kexinit{} ->
+ case S#s.alg_neg of
+ {undefined,undefined} ->
+ S#s{alg_neg = {undefined,PeerMsg}};
+
+ {undefined,_} ->
+ fail("2 kexint received!!", S);
+
+ {OwnMsg, _} ->
+ try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S#s.ssh) of
+ {ok,C} when ?role(S) == server ->
+ S#s{alg_neg = {OwnMsg, PeerMsg},
+ alg = C#ssh.algorithms,
+ ssh = C};
+ {ok,_NextKexMsgBin,C} when ?role(S) == client ->
+ S#s{alg_neg = {OwnMsg, PeerMsg},
+ alg = C#ssh.algorithms}
+ catch
+ Class:Exc ->
+ save_prints({"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
+ [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
+ S#s{alg_neg = {OwnMsg, PeerMsg}})
+ end
+ end;
+
#ssh_msg_kexdh_init{} -> % Always the server
- {ok, Reply, C} = ssh_transport:handle_kexdh_init(M, S#s.ssh),
+ {ok, Reply, C} = ssh_transport:handle_kexdh_init(PeerMsg, S#s.ssh),
S#s{ssh = C,
reply = [{ssh_msg_kexdh_reply,Reply} | S#s.reply]
};
#ssh_msg_kexdh_reply{} ->
- {ok, _NewKeys, C} = ssh_transport:handle_kexdh_reply(M, S#s.ssh),
+ {ok, _NewKeys, C} = ssh_transport:handle_kexdh_reply(PeerMsg, S#s.ssh),
S#s{ssh=C#ssh{send_sequence=S#s.ssh#ssh.send_sequence}}; % Back the number
#ssh_msg_newkeys{} ->
- {ok, C} = ssh_transport:handle_new_keys(M, S#s.ssh),
+ {ok, C} = ssh_transport:handle_new_keys(PeerMsg, S#s.ssh),
S#s{ssh=C};
_ ->
S
@@ -682,10 +717,18 @@ seqnum_trace(S) ->
print_traces(S) when S#s.prints == [] -> S;
print_traces(S) ->
+ Len = length(S#s.prints),
ct:log("~s",
- [lists:foldl(fun({Fmt,Args}, Acc) ->
- [io_lib:format(Fmt,Args) | Acc]
- end, "", S#s.prints)]
+ [lists:foldl(
+ fun({Fmt,Args}, Acc) ->
+ [case Len-length(Acc)-1 of
+ 0 ->
+ io_lib:format(Fmt,Args);
+ N ->
+ io_lib:format(lists:concat(['~p --------~n',Fmt]),
+ [Len-length(Acc)-1|Args])
+ end | Acc]
+ end, "", S#s.prints)]
).
opt(Flag, S, Fun) when is_function(Fun,1) ->
diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl
index c0645f3b01..85f4d36258 100644
--- a/lib/ssh/test/ssh_upgrade_SUITE.erl
+++ b/lib/ssh/test/ssh_upgrade_SUITE.erl
@@ -94,8 +94,8 @@ minor_upgrade(Config) when is_list(Config) ->
%%% Called by ct_release_test:upgrade/4
upgrade_init(CTData, State) ->
{ok, AppUp={_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssh),
- ct:pal("AppUp: ~p", [AppUp]),
- ct:pal("Up: ~p", [Up]),
+ ct:log("AppUp: ~p", [AppUp]),
+ ct:log("Up: ~p", [Up]),
case Soft = is_soft(Up) of
%% It is symmetrical, if upgrade is soft so is downgrade
true ->
@@ -134,12 +134,12 @@ is_soft(_) ->
test_hard(State0, FileName) ->
- ct:pal("test_hard State0=~p, FileName=~p",[State0, FileName]),
+ ct:log("test_hard State0=~p, FileName=~p",[State0, FileName]),
State = setup_server_client(State0),
test_connection(FileName, random_contents(), State).
test_soft(State0, FileName) ->
- ct:pal("test_soft State0=~p, FileName=~p",[State0, FileName]),
+ ct:log("test_soft State0=~p, FileName=~p",[State0, FileName]),
State = test_connection(FileName, random_contents(), State0),
setup_server_client( close(State) ).
@@ -171,7 +171,7 @@ setup_server_client(#state{config=Config} = State) ->
test_connection(FileName, FileContents,
#state{client = ChannelPid,
root_dir = FtpRootDir} = State) ->
- ct:pal("test_connection Writing with ssh_sftp:write_file",[]),
+ ct:log("test_connection Writing with ssh_sftp:write_file",[]),
case ssh_sftp:write_file(ChannelPid, FileName, FileContents) of
ok ->
case ssh_sftp:read_file(ChannelPid, FileName) of