aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/notes.xml203
-rw-r--r--lib/ssh/doc/src/ssh.xml96
-rw-r--r--lib/ssh/doc/src/ssh_app.xml43
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml14
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml4
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml31
-rw-r--r--lib/ssh/src/ssh.erl88
-rw-r--r--lib/ssh/src/ssh.hrl3
-rw-r--r--lib/ssh/src/ssh_auth.erl15
-rw-r--r--lib/ssh/src/ssh_auth.hrl1
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl106
-rw-r--r--lib/ssh/src/ssh_transport.erl5
-rw-r--r--lib/ssh/test/Makefile7
-rw-r--r--lib/ssh/test/ssh.spec11
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl181
-rw-r--r--lib/ssh/test/ssh_bench.spec1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl539
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa13
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa15
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key13
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub11
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/ssh_key_cb.erl45
-rw-r--r--lib/ssh/test/ssh_key_cb_options.erl44
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl124
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl24
-rw-r--r--lib/ssh/test/ssh_test_cli.erl15
-rw-r--r--lib/ssh/test/ssh_test_lib.erl47
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl88
-rw-r--r--lib/ssh/vsn.mk2
43 files changed, 1591 insertions, 261 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 010b1b15c7..75e1615c09 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,209 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The authentication method 'keyboard-interactive' failed
+ in the Erlang client when the server after successful
+ authentication continued by asking for zero more
+ passwords.</p>
+ <p>
+ Own Id: OTP-13225</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Better error handling in ssh_file. There was some rare
+ errors when a NFS-mounted file was opened by ssh_file and
+ then remotely deleted during reading. That caused an
+ endless loop. </p>
+ <p>
+ That bug is now fixed.</p>
+ <p>
+ Own Id: OTP-12699 Aux Id: OTP-11688 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in the compression algorithm
+ <p>
+ Own Id: OTP-12759</p>
+ </item>
+ <item>
+ <p>
+ It is now possible to start more than one daemon with a
+ file descriptor given in option fd. Each daemon must of
+ course have a unique file descriptor.</p>
+ <p>
+ Own Id: OTP-12966 Aux Id: seq12945 </p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug that caused the option <c>dh_gex_limit</c> to
+ be ignored.</p>
+ <p>
+ Own Id: OTP-13029</p>
+ </item>
+ <item>
+ <p>
+ A problem is fixed with the <c>ssh:connect</c> option
+ <c>pref_public_key_algs</c> specifying user keys.</p>
+ <p>
+ Own Id: OTP-13158</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Document updates in the ssh reference manual: app doc
+ file and ssh_connection.</p>
+ <p>
+ Own Id: OTP-12003</p>
+ </item>
+ <item>
+ <p>
+ The authorization phase is made stateful to prevent ssh
+ acting on messages sent in wrong order.</p>
+ <p>
+ Own Id: OTP-12787</p>
+ </item>
+ <item>
+ <p>
+ Testcases for bad message lengths and for bad subfield
+ lengths added.</p>
+ <p>
+ Own Id: OTP-12792 Aux Id: Codenomicon #5214, 6166 </p>
+ </item>
+ <item>
+ <p>
+ The 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384' and
+ 'ecdsa-sha2-nistp521' signature algorithms for ssh are
+ implemented. See RFC 5656.</p>
+ <p>
+ Own Id: OTP-12936</p>
+ </item>
+ <item>
+ <p>
+ The crypto algorithms 'aes192-ctr' and 'aes256-ctr' are
+ implemented. See RFC 4344.</p>
+ <p>
+ Own Id: OTP-12939</p>
+ </item>
+ <item>
+ <p>
+ The ciphers and macs AEAD_AES_128_GCM and
+ AEAD_AES_256_GCM are implemented but not enabled per
+ default. See the SSH App Reference Manual and RFC5647 for
+ details.</p>
+ <p>
+ The ciphers [email protected] and
+ [email protected] are also implemented and available
+ in the default configuration.</p>
+ <p>
+ Own Id: OTP-13018</p>
+ </item>
+ <item>
+ <p>
+ The ssh:daemon option dh_gex_groups is extended to read a
+ user provided ssh moduli file with generator-modulus
+ pairs. The file is in openssh format.</p>
+ <p>
+ Own Id: OTP-13052 Aux Id: OTP-13054 </p>
+ </item>
+ <item>
+ <p>
+ There is now a file (public_key/priv/moduli) which lists
+ size-generator-modulus triples. The purpose is to give
+ servers the possibility to select the crypto primes
+ randomly among a list of pregenerated triples. This
+ reduces the risk for some attacks on diffie-hellman
+ negotiation.</p>
+ <p>
+ See the reference manual for public_key:dh_gex_group/4
+ where the handling of this is described.</p>
+ <p>
+ The ssh server (ssh:daemon) uses this.</p>
+ <p>
+ Own Id: OTP-13054 Aux Id: OTP-13052 </p>
+ </item>
+ <item>
+ <p>
+ The ssh:daemon option pwdfun now also takes a fun/4. This
+ enables the user to 1) check userid-password in another
+ way than the builtin algorithm, 2) implement rate
+ limiting per user or source IP or IP+Port, and 3)
+ implement blocking of missbehaving peers.</p>
+ <p>
+ The old fun/2 still works as previously.</p>
+ <p>
+ Own Id: OTP-13055 Aux Id: OTP-13053 </p>
+ </item>
+ <item>
+ <p>
+ There is now a new option to make the server limit the
+ size range of moduli available for the diffie-hellman
+ group exchange negotiation. See option <c>
+ {dh_gex_limits,{Min,Max}}</c> in ssh:daemon/3.</p>
+ <p>
+ Own Id: OTP-13066</p>
+ </item>
+ <item>
+ <p>
+ Ecdh key exchange now validates compressed and
+ uncompressed keys as defined in rfc5656</p>
+ <p>
+ Own Id: OTP-13067</p>
+ </item>
+ <item>
+ <p>
+ Search order for the .ssh directory are changed so
+ <c>$HOME</c> is tried before
+ <c>init:get_argument(home)</c>.</p>
+ <p>
+ Own Id: OTP-13109</p>
+ </item>
+ <item>
+ <p>
+ The sftp receive window handling is optimized so it will
+ not update the remote end too often. This makes "sftp
+ mget" considerable faster.</p>
+ <p>
+ Own Id: OTP-13130</p>
+ </item>
+ <item>
+ <p>
+ The option <c>key_cb</c> is extended to take an optional
+ list that is passed to the callback module as an option.
+ With this it is possible to have different keys depending
+ on which host that is connected. Another possibility is
+ to write a callback module that fetches keys etc from a
+ database.</p>
+ <p>
+ Thanks to Vipin Nair.</p>
+ <p>
+ Own Id: OTP-13156</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.1.3</title>
<section><title>Known Bugs and Problems</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 1e9acf4a99..850557444d 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2014</year>
+ <year>2004</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -85,6 +85,15 @@
<item><p><c>atom()</c> - Name of the Erlang module
implementing the subsystem using the <c>ssh_channel</c> behavior, see
<seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item>
+ <tag><c>key_cb() =</c></tag>
+ <item>
+ <p><c>atom() | {atom(), list()}</c></p>
+ <p><c>atom()</c> - Name of the erlang module implementing the behaviours
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> or
+ <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> as the
+ case maybe.</p>
+ <p><c>list()</c> - List of options that can be passed to the callback module.</p>
+ </item>
<tag><c>channel_init_args() =</c></tag>
<item><p><c>list()</c></p></item>
@@ -197,26 +206,25 @@
<tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag>
<item>
<note>
- <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c>
- option is used. The equivalence of <c>{public_key_alg,'ssh-dss'}</c> is
- <c>{preferred_algorithms, [{public_key,['ssh-dss','ssh-rsa']}]}</c>.</p>
+ <p>This option will be removed in OTP 20, but is kept for compatibility. It is ignored if
+ the preferred <c>pref_public_key_algs</c> option is used.</p>
</note>
<p>Sets the preferred public key algorithm to use for user
authentication. If the preferred algorithm fails,
- the other algorithm is tried. The default is
- to try <c><![CDATA['ssh-rsa']]></c> first.</p>
+ the other algorithm is tried. If <c>{public_key_alg, 'ssh-rsa'}</c> is set, it is translated
+ to <c>{pref_public_key_algs, ['ssh-rsa','ssh-dss']}</c>. If it is
+ <c>{public_key_alg, 'ssh-dss'}</c>, it is translated
+ to <c>{pref_public_key_algs, ['ssh-dss','ssh-rsa']}</c>.
+ </p>
</item>
<tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
<item>
- <note>
- <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c>
- option is used. The equivalence of <c>{pref_public_key_algs,['ssh-dss']}</c> is
- <c>{preferred_algorithms, [{public_key,['ssh-dss']}]}</c>.</p>
- </note>
- <p>List of public key algorithms to try to use.
- <c>'ssh-rsa'</c> and <c>'ssh-dss'</c> are available.
- Overrides <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p>
+ <p>List of user (client) public key algorithms to try to use.</p>
+ <p>The default value is
+ <c><![CDATA[['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'] ]]></c>
+ </p>
+ <p>If there is no public key of a specified type available, the corresponding entry is ignored.</p>
</item>
<tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag>
@@ -224,6 +232,7 @@
<p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can
be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>.
</p>
+ <p>If an alg_entry() is missing in the algs_list(), the default value is used for that entry.</p>
<p>Here is an example of this option:</p>
<code>
{preferred_algorithms,
@@ -234,9 +243,9 @@
{compression,[none,zlib]}
}
</code>
- <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same
-algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values,
-kex is implicit but public_key is set explicitly.</p>
+ <p>The example specifies different algorithms in the two directions (client2server and server2client),
+ for cipher but specifies the same algorithms for mac and compression in both directions.
+ The kex (key exchange) is implicit but public_key is set explicitly.</p>
<warning>
<p>Changing the values can make a connection less secure. Do not change unless you
@@ -272,11 +281,13 @@ kex is implicit but public_key is set explicitly.</p>
password, if the password authentication method is
attempted.</p>
</item>
- <tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
+ <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag>
<item>
- <p>Module implementing the behaviour
- <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>.
- Can be used to customize the handling of public keys.
+ <p>Module implementing the behaviour <seealso
+ marker="ssh_client_key_api">ssh_client_key_api</seealso>. Can be used to
+ customize the handling of public keys. If callback options are provided
+ along with the module name, they are made available to the callback
+ module via the options passed to it under the key 'key_cb_private'.
</p>
</item>
<tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag>
@@ -407,10 +418,10 @@ kex is implicit but public_key is set explicitly.</p>
<c><![CDATA["publickey,keyboard-interactive,password"]]></c></p>
</item>
- <tag><c><![CDATA[{auth_method_kb_interactive_data, PromptTexts}]]>
- <br/>where:
- <br/>PromptTexts = kb_int_tuple() | fun(Peer::{IP::tuple(),Port::integer()}, User::string(), Service::string()) -> kb_int_tuple()
- <br/>kb_int_tuple() = {Name::string(), Instruction::string(), Prompt::string(), Echo::boolean()}</c>
+ <tag><c><![CDATA[{auth_method_kb_interactive_data, PromptTexts}]]></c>
+ <br/><c>where:</c>
+ <br/><c>PromptTexts = kb_int_tuple() | fun(Peer::{IP::tuple(),Port::integer()}, User::string(), Service::string()) -> kb_int_tuple()</c>
+ <br/><c>kb_int_tuple() = {Name::string(), Instruction::string(), Prompt::string(), Echo::boolean()}</c>
</tag>
<item>
<p>Sets the text strings that the daemon sends to the client for presentation to the user when using <c>keyboar-interactive</c> authentication. If the fun/3 is used, it is called when the actual authentication occurs and may therefore return dynamic data like time, remote ip etc.</p>
@@ -440,6 +451,7 @@ kex is implicit but public_key is set explicitly.</p>
<p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can
be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>.
</p>
+ <p>If an alg_entry() is missing in the algs_list(), the default value is used for that entry.</p>
<p>Here is an example of this option:</p>
<code>
{preferred_algorithms,
@@ -450,9 +462,9 @@ kex is implicit but public_key is set explicitly.</p>
{compression,[none,zlib]}
}
</code>
- <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same
-algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values,
-kex is implicit but public_key is set explicitly.</p>
+ <p>The example specifies different algorithms in the two directions (client2server and server2client),
+ for cipher but specifies the same algorithms for mac and compression in both directions.
+ The kex (key exchange) is implicit but public_key is set explicitly.</p>
<warning>
<p>Changing the values can make a connection less secure. Do not change unless you
@@ -504,29 +516,29 @@ kex is implicit but public_key is set explicitly.</p>
<item>
<p>Provides a function for password validation. This could used for calling an external system or if
passwords should be stored as a hash. The fun returns:
- <list type="bulleted">
- <item><c>true</c> if the user and password is valid and</item>
- <item><c>false</c> otherwise.</item>
- </list>
</p>
+ <list type="bulleted">
+ <item><c>true</c> if the user and password is valid and</item>
+ <item><c>false</c> otherwise.</item>
+ </list>
<p>This fun can also be used to make delays in authentication tries for example by calling
<seealso marker="stdlib:timer#sleep/1">timer:sleep/1</seealso>. To facilitate counting of failed tries
the <c>State</c> variable could be used. This state is per connection only. The first time the pwdfun
is called for a connection, the <c>State</c> variable has the value <c>undefined</c>.
The pwdfun can return - in addition to the values above - a new state
as:
- <list type="bulleted">
- <item><c>{true, NewState:any()}</c> if the user and password is valid or</item>
- <item><c>{false, NewState:any()}</c> if the user or password is invalid</item>
- </list>
</p>
+ <list type="bulleted">
+ <item><c>{true, NewState:any()}</c> if the user and password is valid or</item>
+ <item><c>{false, NewState:any()}</c> if the user or password is invalid</item>
+ </list>
<p>A third usage is to block login attempts from a missbehaving peer. The <c>State</c> described above
can be used for this. In addition to the responses above, the following return value is introduced:
+ </p>
<list type="bulleted">
<item><c>disconnect</c> if the connection should be closed immediately after sending a SSH_MSG_DISCONNECT
message.</item>
</list>
- </p>
</item>
<tag><c><![CDATA[{pwdfun, fun(User::string(), Password::string()) -> boolean()}]]></c></tag>
@@ -607,11 +619,13 @@ kex is implicit but public_key is set explicitly.</p>
</p>
</item>
- <tag><c><![CDATA[{key_cb, atom()}]]></c></tag>
+ <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag>
<item>
- <p>Module implementing the behaviour
- <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>.
- Can be used to customize the handling of public keys.
+ <p>Module implementing the behaviour <seealso
+ marker="ssh_server_key_api">ssh_server_key_api</seealso>. Can be used to
+ customize the handling of public keys. If callback options are provided
+ along with the module name, they are made available to the callback
+ module via the options passed to it under the key 'key_cb_private'.
</p>
</item>
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
index 79dd1e210e..f6ce44c015 100644
--- a/lib/ssh/doc/src/ssh_app.xml
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>2012</year><year>2013</year>
+ <year>2012</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -214,21 +214,21 @@
<p>The following rfc:s are supported:</p>
<list type="bulleted">
<item><url href="https://tools.ietf.org/html/rfc4251">RFC 4251</url>, The Secure Shell (SSH) Protocol Architecture.
- <p>Except
+ <p>Except</p>
<list type="bulleted">
<item>9.4.6 Host-Based Authentication</item>
<item>9.5.2 Proxy Forwarding</item>
<item>9.5.3 X11 Forwarding</item>
</list>
- </p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc4252">RFC 4252</url>, The Secure Shell (SSH) Authentication Protocol.
- <p>Except
+ <p>Except</p>
<list type="bulleted">
<item>9. Host-Based Authentication: "hostbased"</item>
</list>
- </p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc4253">RFC 4253</url>, The Secure Shell (SSH) Transport Layer Protocol.
@@ -236,32 +236,32 @@
</item>
<item><url href="https://tools.ietf.org/html/rfc4254">RFC 4254</url>, The Secure Shell (SSH) Connection Protocol.
- <p>Except
+ <p>Except</p>
<list type="bulleted">
<item>6.3. X11 Forwarding</item>
<item>7. TCP/IP Port Forwarding</item>
</list>
- </p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc4256">RFC 4256</url>, Generic Message Exchange Authentication for
the Secure Shell Protocol (SSH).
- <p>Except
+ <p>Except</p>
<list type="bulleted">
<item><c>num-prompts > 1</c></item>
<item>password changing</item>
<item>other identification methods than userid-password</item>
</list>
- </p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc4419">RFC 4419</url>, Diffie-Hellman Group Exchange for
the Secure Shell (SSH) Transport Layer Protocol.
- <p></p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc4716">RFC 4716</url>, The Secure Shell (SSH) Public Key File Format.
- <p></p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc5647">RFC 5647</url>, AES Galois Counter Mode for
@@ -270,34 +270,35 @@
This is resolved by OpenSSH in the ciphers [email protected] and [email protected] which are implemented.
If the explicit ciphers and macs AEAD_AES_128_GCM or AEAD_AES_256_GCM are needed,
they could be enabled with the option preferred_algorithms.
+ </p>
<warning>
- If the client or the server is not Erlang/OTP, it is the users responsibility to check that
- other implementation has the same interpretation of AEAD_AES_*_GCM as the Erlang/OTP SSH before
- enabling them. The aes*[email protected] variants are always safe to use since they lack the
- ambiguity.
+ <p>
+ If the client or the server is not Erlang/OTP, it is the users responsibility to check that
+ other implementation has the same interpretation of AEAD_AES_*_GCM as the Erlang/OTP SSH before
+ enabling them. The aes*[email protected] variants are always safe to use since they lack the
+ ambiguity.
+ </p>
</warning>
- </p>
- <p>The second paragraph in section 5.1 is resolved as:
+ <p>The second paragraph in section 5.1 is resolved as:</p>
<list type="ordered">
<item>If the negotiated cipher is AEAD_AES_128_GCM, the mac algorithm is set to AEAD_AES_128_GCM.</item>
<item>If the negotiated cipher is AEAD_AES_256_GCM, the mac algorithm is set to AEAD_AES_256_GCM.</item>
<item>If the mac algorithm is AEAD_AES_128_GCM, the cipher is set to AEAD_AES_128_GCM.</item>
<item>If the mac algorithm is AEAD_AES_256_GCM, the cipher is set to AEAD_AES_256_GCM.</item>
</list>
- The first rule that matches when read in order from the top is applied
- </p>
+ <p>The first rule that matches when read in order from the top is applied</p>
</item>
<item><url href="https://tools.ietf.org/html/rfc5656">RFC 5656</url>, Elliptic Curve Algorithm Integration in
the Secure Shell Transport Layer.
- <p>Except
+ <p>Except</p>
<list type="bulleted">
<item>5. ECMQV Key Exchange</item>
<item>6.4. ECMQV Key Exchange and Verification Method Name</item>
<item>7.2. ECMQV Message Numbers</item>
<item>10.2. Recommended Curves</item>
</list>
- </p>
+ <p/>
</item>
<item><url href="https://tools.ietf.org/html/rfc6668">RFC 6668</url>, SHA-2 Data Integrity Verification for
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 064a623eb6..150d46a9a2 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2014</year>
+ <year>2015</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -31,15 +31,15 @@
<rev></rev>
</header>
<module>ssh_connection</module>
- <modulesummary>This module provides API functions to send
- <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url>
- events to the other side of an SSH channel.
+ <modulesummary>
+ This module provides API functions to send SSH Connection Protocol
+ events to the other side of an SSH channel.
</modulesummary>
<description>
- <p>The SSH Connection Protocol is used by clients and servers,
- that is, SSH channels, to communicate over the SSH connection. The
- API functions in this module send SSH Connection Protocol events,
+ <p>The <url href="http://www.ietf.org/rfc/rfc4254.txt">SSH Connection Protocol</url>
+ is used by clients and servers, that is, SSH channels, to communicate over the
+ SSH connection. The API functions in this module send SSH Connection Protocol events,
which are received as messages by the remote channel.
If the receiving channel is an Erlang process, the
messages have the format
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index efb2c436e8..a0694ca8d9 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2012</year>
- <year>2013</year>
+ <year>2015</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -75,7 +75,7 @@
<d>Host key algorithm. Is to support <c>'ssh-rsa' | 'ssh-dss'</c>, but more algorithms
can be handled.</d>
<v>DaemonOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
<v>Key = private_key()</v>
<d>Private key of the host matching the <c>Algorithm</c>.</d>
<v>Reason = term()</v>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index 17800fac5d..c6ca0f161a 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2005</year><year>2014</year>
+ <year>2005</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -61,20 +61,23 @@
<funcs>
<func>
- <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, Error}</name>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- <v>Reason = term()</v>
-
- <desc><p>The <c><![CDATA[apread]]></c> function reads from a specified position,
- combining the <c><![CDATA[position]]></c> and <c><![CDATA[aread]]></c> functions.</p>
+ <name>apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, Reason}</name>
+ <fsummary>Reads asynchronously from an open file.</fsummary>
+ <type>
+ <v>ChannelPid = pid()</v>
+ <v>Handle = term()</v>
+ <v>Position = integer()</v>
+ <v>Len = integer()</v>
+ <v>N = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc><p>The <c><![CDATA[apread]]></c> function reads from a specified position,
+ combining the <c><![CDATA[position]]></c> and <c><![CDATA[aread]]></c> functions.</p>
<p><seealso marker="#apread-4">ssh_sftp:apread/4</seealso></p> </desc>
- </func>
-
- <func>
+ </func>
+
+ <func>
<name>apwrite(ChannelPid, Handle, Position, Data) -> ok | {error, Reason}</name>
<fsummary>Writes asynchronously to an open file.</fsummary>
<type>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index bb50e436a3..54f94acbdc 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -297,13 +297,6 @@ find_hostport(Fd) ->
ok = inet:close(S),
HostPort.
-%% find_port(Fd) ->
-%% %% Hack....
-%% {ok,TmpSock} = gen_tcp:listen(0,[{fd,Fd}]),
-%% {ok, {_,ThePort}} = inet:sockname(TmpSock),
-%% gen_tcp:close(TmpSock),
-%% ThePort.
-
handle_options(Opts) ->
try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
@@ -315,32 +308,27 @@ handle_options(Opts) ->
end.
-algs_compatibility(Os) ->
+algs_compatibility(Os0) ->
%% Take care of old options 'public_key_alg' and 'pref_public_key_algs'
- comp_pk(proplists:get_value(preferred_algorithms,Os),
- proplists:get_value(pref_public_key_algs,Os),
- proplists:get_value(public_key_alg, Os),
- [{K,V} || {K,V} <- Os,
- K =/= public_key_alg,
- K =/= pref_public_key_algs]
- ).
-
-comp_pk(undefined, undefined, undefined, Os) -> Os;
-comp_pk( PrefAlgs, _, _, Os) when PrefAlgs =/= undefined -> Os;
-
-comp_pk(undefined, undefined, ssh_dsa, Os) -> comp_pk(undefined, undefined, 'ssh-dss', Os);
-comp_pk(undefined, undefined, ssh_rsa, Os) -> comp_pk(undefined, undefined, 'ssh-rsa', Os);
-comp_pk(undefined, undefined, PK, Os) ->
- PKs = [PK | ssh_transport:supported_algorithms(public_key)--[PK]],
- [{preferred_algorithms, [{public_key,PKs}] } | Os];
-
-comp_pk(undefined, PrefPKs, _, Os) when PrefPKs =/= undefined ->
- PKs = [case PK of
- ssh_dsa -> 'ssh-dss';
- ssh_rsa -> 'ssh-rsa';
- _ -> PK
- end || PK <- PrefPKs],
- [{preferred_algorithms, [{public_key,PKs}]} | Os].
+ case proplists:get_value(public_key_alg, Os0) of
+ undefined ->
+ Os0;
+ A when is_atom(A) ->
+ %% Skip public_key_alg if pref_public_key_algs is defined:
+ Os = lists:keydelete(public_key_alg, 1, Os0),
+ case proplists:get_value(pref_public_key_algs,Os) of
+ undefined when A == 'ssh-rsa' ; A==ssh_rsa ->
+ [{pref_public_key_algs,['ssh-rsa','ssh-dss']} | Os];
+ undefined when A == 'ssh-dss' ; A==ssh_dsa ->
+ [{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os];
+ undefined ->
+ throw({error, {eoptions, {public_key_alg,A} }});
+ _ ->
+ Os
+ end;
+ V ->
+ throw({error, {eoptions, {public_key_alg,V} }})
+ end.
handle_option([], SocketOptions, SshOptions) ->
@@ -369,8 +357,12 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{key_cb, {Module, Options}} | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({key_cb, Module}),
+ handle_ssh_priv_option({key_cb_private, Options}) |
+ SshOptions]);
+handle_option([{key_cb, Module} | Rest], SocketOptions, SshOptions) ->
+ handle_option([{key_cb, {Module, []}} | Rest], SocketOptions, SshOptions);
handle_option([{keyboard_interact_fun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
%%Backwards compatibility
@@ -407,6 +399,8 @@ handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ 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) ->
@@ -518,6 +512,13 @@ handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0
is_integer(Max), Max>=I ->
%% Client
Opt;
+handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 ->
+ case handle_user_pref_pubkey_algs(Value, []) of
+ {true, NewOpts} ->
+ {pref_public_key_algs, NewOpts};
+ _ ->
+ throw({error, {eoptions, Opt}})
+ end;
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 ->
@@ -544,6 +545,9 @@ handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,4) ->
Opt;
handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
Opt;
+handle_ssh_option({key_cb, {CallbackMod, CallbackOptions}} = Opt) when is_atom(CallbackMod),
+ is_list(CallbackOptions) ->
+ Opt;
handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3) ->
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
@@ -610,6 +614,9 @@ handle_ssh_option({profile, Value} = Opt) when is_atom(Value) ->
handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
+handle_ssh_priv_option({key_cb_private, Value} = Opt) when is_list(Value) ->
+ Opt.
+
handle_inet_option({active, _} = Opt) ->
throw({error, {{eoptions, Opt}, "SSH has built in flow control, "
"and active is handled internally, user is not allowed"
@@ -770,3 +777,16 @@ read_moduli_file(D, I, Acc) ->
end
end.
+handle_user_pref_pubkey_algs([], Acc) ->
+ {true, lists:reverse(Acc)};
+handle_user_pref_pubkey_algs([H|T], Acc) ->
+ case lists:member(H, ?SUPPORTED_USER_KEYS) of
+ true ->
+ handle_user_pref_pubkey_algs(T, [H| Acc]);
+
+ false when H==ssh_dsa -> handle_user_pref_pubkey_algs(T, ['ssh-dss'| Acc]);
+ false when H==ssh_rsa -> handle_user_pref_pubkey_algs(T, ['ssh-rsa'| Acc]);
+
+ false ->
+ false
+ end.
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 8efc743b67..f88098819d 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -33,6 +33,9 @@
-define(REKEY_DATA_TIMOUT, 60000).
-define(DEFAULT_PROFILE, default).
+-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
+-define(SUPPORTED_USER_KEYS, ['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']).
+
-define(FALSE, 0).
-define(TRUE, 1).
%% basic binary constructors
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 4967a2e4cd..b71bed033a 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -118,11 +118,16 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
service = "ssh-connection",
method = "none",
data = <<>>},
+ Algs0 = proplists:get_value(pref_public_key_algs, Opts, ?SUPPORTED_USER_KEYS),
+ %% The following line is not strictly correct. The call returns the
+ %% supported HOST key types while we are interested in USER keys. However,
+ %% they "happens" to be the same (for now). This could change....
+ %% There is no danger as long as the set of user keys is a subset of the set
+ %% of host keys.
+ CryptoSupported = ssh_transport:supported_algorithms(public_key),
+ Algs = [A || A <- Algs0,
+ lists:member(A, CryptoSupported)],
-
- Algs = proplists:get_value(public_key,
- proplists:get_value(preferred_algorithms, Opts, []),
- ssh_transport:default_algorithms(public_key)),
Prefs = method_preference(Algs),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
@@ -472,7 +477,7 @@ keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
1) when Password =/= undefined ->
[Password]; %% Password auth implemented with keyboard-interaction and passwd is known
keyboard_interact_get_responses(_, _, _, _, _, _, _, _, 0) ->
- [""];
+ [];
keyboard_interact_get_responses(false, undefined, undefined, _, _, _, [Prompt|_], Opts, _) ->
ssh_no_io:read_line(Prompt, Opts); %% Throws error as keyboard interaction is not allowed
keyboard_interact_get_responses(true, undefined, _,IoCb, Name, Instr, PromptInfos, Opts, _) ->
diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl
index 5197a42fa4..449bc4fa45 100644
--- a/lib/ssh/src/ssh_auth.hrl
+++ b/lib/ssh/src/ssh_auth.hrl
@@ -22,7 +22,6 @@
%%% Description: Ssh User Authentication Protocol
--define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
-define(SSH_MSG_USERAUTH_REQUEST, 50).
-define(SSH_MSG_USERAUTH_FAILURE, 51).
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 516a09bf6a..ce1931e4f4 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -648,10 +648,12 @@ userauth_keyboard_interactive(Msg = #ssh_msg_userauth_failure{},
userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_failure{},
#state{ssh_params = #ssh{role = client}} = State) ->
userauth(Msg, State);
-
userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_success{},
#state{ssh_params = #ssh{role = client}} = State) ->
- userauth(Msg, State).
+ userauth(Msg, State);
+userauth_keyboard_interactive_info_response(Msg=#ssh_msg_userauth_info_request{},
+ #state{ssh_params = #ssh{role = client}} = State) ->
+ userauth_keyboard_interactive(Msg, State).
%%--------------------------------------------------------------------
-spec connected({#ssh_msg_kexinit{}, binary()}, %%| %% #ssh_msg_kexdh_init{},
@@ -999,7 +1001,8 @@ handle_info({Protocol, Socket, Data}, StateName,
encoded_data_buffer = EncData0,
undecoded_packet_length = RemainingSshPacketLen0} = State0) ->
Encoded = <<EncData0/binary, Data/binary>>,
- case ssh_transport:handle_packet_part(DecData0, Encoded, RemainingSshPacketLen0, Ssh0) of
+ try ssh_transport:handle_packet_part(DecData0, Encoded, RemainingSshPacketLen0, Ssh0)
+ of
{get_more, DecBytes, EncDataRest, RemainingSshPacketLen, Ssh1} ->
{next_state, StateName,
next_packet(State0#state{encoded_data_buffer = EncDataRest,
@@ -1021,7 +1024,22 @@ handle_info({Protocol, Socket, Data}, StateName,
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
description = "Bad mac",
language = ""},
- handle_disconnect(DisconnectMsg, State0#state{ssh_params=Ssh1})
+ handle_disconnect(DisconnectMsg, State0#state{ssh_params=Ssh1});
+
+ {error, {exceeds_max_size,PacketLen}} ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet length "
+ ++ integer_to_list(PacketLen),
+ language = ""},
+ handle_disconnect(DisconnectMsg, State0)
+ catch
+ _:_ ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet",
+ language = ""},
+ handle_disconnect(DisconnectMsg, State0)
end;
handle_info({CloseTag, _Socket}, _StateName,
@@ -1392,44 +1410,54 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
Byte == ?SSH_MSG_CHANNEL_REQUEST;
Byte == ?SSH_MSG_CHANNEL_SUCCESS;
Byte == ?SSH_MSG_CHANNEL_FAILURE ->
- ConnectionMsg = ssh_message:decode(Msg),
- State1 = generate_event_new_state(State0, EncData),
- try ssh_connection:handle_msg(ConnectionMsg, Connection0, Role) of
- {{replies, Replies0}, Connection} ->
- if StateName == connected ->
- Replies = Replies0,
- State2 = State1;
- true ->
- {ConnReplies, Replies} =
- lists:splitwith(fun not_connected_filter/1, Replies0),
- Q = State1#state.event_queue ++ ConnReplies,
- State2 = State1#state{ event_queue = Q }
- end,
- State = send_replies(Replies, State2#state{connection_state = Connection}),
- {next_state, StateName, next_packet(State)};
- {noreply, Connection} ->
- {next_state, StateName, next_packet(State1#state{connection_state = Connection})};
- {disconnect, {_, Reason}, {{replies, Replies}, Connection}} when
- Role == client andalso ((StateName =/= connected) and (not Renegotiation)) ->
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- User ! {self(), not_connected, Reason},
- {stop, {shutdown, normal},
- next_packet(State#state{connection_state = Connection})};
- {disconnect, _Reason, {{replies, Replies}, Connection}} ->
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- {stop, {shutdown, normal}, State#state{connection_state = Connection}}
+ try
+ ssh_message:decode(Msg)
+ of
+ ConnectionMsg ->
+ State1 = generate_event_new_state(State0, EncData),
+ try ssh_connection:handle_msg(ConnectionMsg, Connection0, Role) of
+ {{replies, Replies0}, Connection} ->
+ if StateName == connected ->
+ Replies = Replies0,
+ State2 = State1;
+ true ->
+ {ConnReplies, Replies} =
+ lists:splitwith(fun not_connected_filter/1, Replies0),
+ Q = State1#state.event_queue ++ ConnReplies,
+ State2 = State1#state{ event_queue = Q }
+ end,
+ State = send_replies(Replies, State2#state{connection_state = Connection}),
+ {next_state, StateName, next_packet(State)};
+ {noreply, Connection} ->
+ {next_state, StateName, next_packet(State1#state{connection_state = Connection})};
+ {disconnect, {_, Reason}, {{replies, Replies}, Connection}} when
+ Role == client andalso ((StateName =/= connected) and (not Renegotiation)) ->
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ User ! {self(), not_connected, Reason},
+ {stop, {shutdown, normal},
+ next_packet(State#state{connection_state = Connection})};
+ {disconnect, _Reason, {{replies, Replies}, Connection}} ->
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ {stop, {shutdown, normal}, State#state{connection_state = Connection}}
+ catch
+ _:Error ->
+ {disconnect, _Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error",
+ language = "en"}, Connection0, Role),
+ State = send_replies(Replies, State1#state{connection_state = Connection}),
+ {stop, {shutdown, Error}, State#state{connection_state = Connection}}
+ end
+
catch
- _:Error ->
- {disconnect, _Reason, {{replies, Replies}, Connection}} =
- ssh_connection:handle_msg(
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Internal error",
- language = "en"}, Connection0, Role),
- State = send_replies(Replies, State1#state{connection_state = Connection}),
- {stop, {shutdown, Error}, State#state{connection_state = Connection}}
+ _:_ ->
+ handle_disconnect(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet received",
+ language = ""}, State0)
end;
-
generate_event(Msg, StateName, State0, EncData) ->
try
Event = ssh_message:decode(set_prefix_if_trouble(Msg,State0)),
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 67a0d29bb8..18037b8461 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1004,10 +1004,7 @@ handle_packet_part(<<>>, Encrypted0, undefined, #ssh{decrypt = CryptoAlg} = Ssh0
{ok, PacketLen, _, _, _} when PacketLen > ?SSH_MAX_PACKET_SIZE ->
%% far too long message than expected
- throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet length "
- ++ integer_to_list(PacketLen),
- language = ""});
+ {error, {exceeds_max_size,PacketLen}};
{ok, PacketLen, Decrypted, Encrypted1,
#ssh{recv_mac_size = MacSize} = Ssh1} ->
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 96c74c6c8a..9cd98f069f 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -35,9 +35,8 @@ MODULES= \
ssh_algorithms_SUITE \
ssh_options_SUITE \
ssh_renegotiate_SUITE \
- \
ssh_basic_SUITE \
- \
+ ssh_benchmark_SUITE \
ssh_connection_SUITE \
ssh_protocol_SUITE \
ssh_sftp_SUITE \
@@ -47,6 +46,8 @@ MODULES= \
ssh_to_openssh_SUITE \
ssh_upgrade_SUITE \
ssh_test_lib \
+ ssh_key_cb \
+ ssh_key_cb_options \
ssh_trpt_test_lib \
ssh_echo_server \
ssh_peername_sockname_server \
@@ -127,7 +128,7 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
- $(INSTALL_DATA) ssh.spec ssh.cover "$(RELSYSDIR)"
+ $(INSTALL_DATA) ssh.spec ssh_bench.spec ssh.cover "$(RELSYSDIR)"
$(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/ssh/test/ssh.spec b/lib/ssh/test/ssh.spec
index 8de0fe44e4..0076fc275e 100644
--- a/lib/ssh/test/ssh.spec
+++ b/lib/ssh/test/ssh.spec
@@ -1,7 +1,6 @@
{suites,"../ssh_test",all}.
-{skip_cases,"../ssh_test",ssh_ssh_SUITE,
- [ssh],
- "Current implementation is timingdependent and\nhence will succeed/fail on a whim"}.
-{skip_cases,"../ssh_test",ssh_ssh_SUITE,
- [ssh_compressed],
- "Current implementation is timingdependent hence will succeed/fail on a whim"}.
+
+{skip_suites, "../ssh_test", [ssh_benchmark_SUITE],
+ "Benchmarks run separately"}.
+
+
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 0a5964c560..4b53f6ec57 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -41,6 +41,10 @@
double_close/1,
exec/1,
exec_compressed/1,
+ exec_key_differs1/1,
+ exec_key_differs2/1,
+ exec_key_differs3/1,
+ exec_key_differs_fail/1,
idle_time/1,
inet6_option/1,
inet_option/1,
@@ -54,8 +58,10 @@
send/1,
shell/1,
shell_no_unicode/1,
- shell_unicode_string/1,
- ssh_info_print/1
+ shell_unicode_string/1,
+ ssh_info_print/1,
+ key_callback/1,
+ key_callback_options/1
]).
%%% Common test callbacks
@@ -84,6 +90,8 @@ all() ->
{group, ecdsa_sha2_nistp521_key},
{group, dsa_pass_key},
{group, rsa_pass_key},
+ {group, host_user_key_differs},
+ {group, key_cb},
{group, internal_error},
daemon_already_started,
double_close,
@@ -99,8 +107,13 @@ groups() ->
{ecdsa_sha2_nistp256_key, [], basic_tests()},
{ecdsa_sha2_nistp384_key, [], basic_tests()},
{ecdsa_sha2_nistp521_key, [], basic_tests()},
+ {host_user_key_differs, [], [exec_key_differs1,
+ exec_key_differs2,
+ exec_key_differs3,
+ exec_key_differs_fail]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
+ {key_cb, [], [key_callback, key_callback_options]},
{internal_error, [], [internal_error]}
].
@@ -180,6 +193,26 @@ init_per_group(dsa_pass_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:setup_dsa_pass_pharse(DataDir, PrivDir, "Password"),
[{pass_phrase, {dsa_pass_phrase, "Password"}}| Config];
+init_per_group(host_user_key_differs, Config) ->
+ Data = ?config(data_dir, Config),
+ Sys = filename:join(?config(priv_dir, Config), system_rsa),
+ SysUsr = filename:join(Sys, user),
+ Usr = filename:join(?config(priv_dir, Config), user_ecdsa_256),
+ file:make_dir(Sys),
+ file:make_dir(SysUsr),
+ file:make_dir(Usr),
+ file:copy(filename:join(Data, "ssh_host_rsa_key"), filename:join(Sys, "ssh_host_rsa_key")),
+ file:copy(filename:join(Data, "ssh_host_rsa_key.pub"), filename:join(Sys, "ssh_host_rsa_key.pub")),
+ file:copy(filename:join(Data, "id_ecdsa256"), filename:join(Usr, "id_ecdsa")),
+ file:copy(filename:join(Data, "id_ecdsa256.pub"), filename:join(Usr, "id_ecdsa.pub")),
+ ssh_test_lib:setup_ecdsa_auth_keys("256", Usr, SysUsr),
+ ssh_test_lib:setup_rsa_known_host(Sys, Usr),
+ Config;
+init_per_group(key_cb, Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_dsa(DataDir, PrivDir),
+ Config;
init_per_group(internal_error, Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -247,6 +280,10 @@ end_per_group(rsa_pass_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:clean_rsa(PrivDir),
Config;
+end_per_group(key_cb, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:clean_dsa(PrivDir),
+ Config;
end_per_group(internal_error, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(PrivDir),
@@ -274,6 +311,14 @@ init_per_testcase(TC, Config) when TC==shell_no_unicode ;
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(inet6_option, Config) ->
+ case ssh_test_lib:has_inet6_address() of
+ true ->
+ init_per_testcase('__default__', Config);
+ false ->
+ {skip,"No ipv6 interface address"}
+ end;
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
@@ -478,14 +523,92 @@ shell(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+%%% Test that we could user different types of host pubkey and user pubkey
+exec_key_differs1(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp256']).
+
+exec_key_differs2(Config) -> exec_key_differs(Config, ['ssh-dss','ecdsa-sha2-nistp256']).
+
+exec_key_differs3(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp384','ecdsa-sha2-nistp256']).
+
+
+
+exec_key_differs(Config, UserPKAlgs) ->
+ case lists:usort(['ssh-rsa'|UserPKAlgs])
+ -- ssh_transport:supported_algorithms(public_key)
+ of
+ [] ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system_rsa),
+ SystemUserDir = filename:join(SystemDir, user),
+ UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256),
+
+ {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, SystemUserDir},
+ {preferred_algorithms,
+ [{public_key,['ssh-rsa']}]}]),
+ ct:sleep(500),
+
+ IO = ssh_test_lib:start_io_server(),
+ Shell = ssh_test_lib:start_shell(Port, IO, UserDir,
+ [{preferred_algorithms,[{public_key,['ssh-rsa']}]},
+ {pref_public_key_algs,UserPKAlgs}
+ ]),
+
+
+ receive
+ {'EXIT', _, _} ->
+ ct:fail(no_ssh_connection);
+ ErlShellStart ->
+ ct:log("Erlang shell start: ~p~n", [ErlShellStart]),
+ do_shell(IO, Shell)
+ after
+ 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+ end;
+
+ UnsupportedPubKeys ->
+ {skip, io_lib:format("~p unsupported",[UnsupportedPubKeys])}
+ end.
+
+%%--------------------------------------------------------------------
+exec_key_differs_fail(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system_rsa),
+ SystemUserDir = filename:join(SystemDir, user),
+ UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256),
+
+ {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, SystemUserDir},
+ {preferred_algorithms,
+ [{public_key,['ssh-rsa']}]}]),
+ ct:sleep(500),
+
+ IO = ssh_test_lib:start_io_server(),
+ ssh_test_lib:start_shell(Port, IO, UserDir,
+ [{preferred_algorithms,[{public_key,['ssh-rsa']}]},
+ {pref_public_key_algs,['ssh-dss']}]),
+ receive
+ {'EXIT', _, _} ->
+ ok;
+ ErlShellStart ->
+ ct:log("Erlang shell start: ~p~n", [ErlShellStart]),
+ ct:fail(connection_not_rejected)
+ after
+ 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+ end.
+
+%%--------------------------------------------------------------------
cli(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
-
+
+ TmpDir = filename:join(?config(priv_dir,Config), "tmp"),
+ ok = ssh_test_lib:del_dirs(TmpDir),
+ ok = file:make_dir(TmpDir),
+
{_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir},
{password, "morot"},
- {ssh_cli, {ssh_test_cli, [cli]}},
+ {ssh_cli, {ssh_test_cli, [cli,TmpDir]}},
{subsystems, []},
{failfun, fun ssh_test_lib:failfun/2}]),
ct:sleep(500),
@@ -575,6 +698,56 @@ pass_phrase(Config) when is_list(Config) ->
{ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+%%% Test that we can use key callback
+key_callback(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+ NoPubKeyDir = filename:join(UserDir, "nopubkey"),
+ file:make_dir(NoPubKeyDir),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ ConnectOpts = [{silently_accept_hosts, true},
+ {user_dir, NoPubKeyDir},
+ {user_interaction, false},
+ {key_cb, ssh_key_cb}],
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts),
+
+ {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ssh:stop_daemon(Pid).
+
+
+%%--------------------------------------------------------------------
+%%% Test that we can use key callback with callback options
+key_callback_options(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ NoPubKeyDir = filename:join(UserDir, "nopubkey"),
+ file:make_dir(NoPubKeyDir),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ {ok, PrivKey} = file:read_file(filename:join(UserDir, "id_dsa")),
+
+ ConnectOpts = [{silently_accept_hosts, true},
+ {user_dir, NoPubKeyDir},
+ {user_interaction, false},
+ {key_cb, {ssh_key_cb_options, [{priv_key, PrivKey}]}}],
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts),
+
+ {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ssh:stop_daemon(Pid).
+
%%--------------------------------------------------------------------
%%% Test that client does not hang if disconnects due to internal error
diff --git a/lib/ssh/test/ssh_bench.spec b/lib/ssh/test/ssh_bench.spec
new file mode 100644
index 0000000000..029f0bd074
--- /dev/null
+++ b/lib/ssh/test/ssh_bench.spec
@@ -0,0 +1 @@
+{suites,"../ssh_test",[ssh_benchmark_SUITE]}.
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
new file mode 100644
index 0000000000..e90bfa3d16
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -0,0 +1,539 @@
+%%%-------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. 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%
+%%
+-module(ssh_benchmark_SUITE).
+-compile(export_all).
+
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+-include_lib("ssh/src/ssh.hrl").
+-include_lib("ssh/src/ssh_transport.hrl").
+-include_lib("ssh/src/ssh_connect.hrl").
+-include_lib("ssh/src/ssh_userauth.hrl").
+
+
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+%%suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() -> [{group, opensshc_erld}
+%% {group, erlc_opensshd}
+ ].
+
+groups() ->
+ [{opensshc_erld, [{repeat, 3}], [openssh_client_shell,
+ openssh_client_sftp]}
+ ].
+
+
+init_per_suite(Config) ->
+ catch ssh:stop(),
+ catch crypto:stop(),
+ try
+ ok = crypto:start(),
+ report_client_algorithms(),
+ ok = ssh:start(),
+ {ok,TracerPid} = erlang_trace(),
+ [{tracer_pid,TracerPid} | init_sftp_dirs(Config)]
+ catch
+ C:E ->
+ {skip, io_lib:format("Couldn't start ~p:~p",[C,E])}
+ end.
+
+end_per_suite(_Config) ->
+ catch ssh:stop(),
+ catch crypto:stop(),
+ ok.
+
+
+
+init_per_group(opensshc_erld, Config) ->
+ case ssh_test_lib:ssh_type() of
+ openSSH ->
+ DataDir = ?config(data_dir, Config),
+ UserDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_dsa(DataDir, UserDir),
+ ssh_test_lib:setup_rsa(DataDir, UserDir),
+ ssh_test_lib:setup_ecdsa("256", DataDir, UserDir),
+ Common = ssh_test_lib:intersect_bi_dir(
+ ssh_test_lib:intersection(ssh:default_algorithms(),
+ ssh_test_lib:default_algorithms(sshc))),
+ [{c_kexs, ssh_test_lib:sshc(kex)},
+ {c_ciphers, ssh_test_lib:sshc(cipher)},
+ {common_algs, Common}
+ | Config];
+ _ ->
+ {skip, "No OpenSsh client found"}
+ end;
+
+init_per_group(erlc_opensshd, _) ->
+ {skip, "Group erlc_opensshd not implemented"};
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+
+init_per_testcase(_Func, Conf) ->
+ Conf.
+
+end_per_testcase(_Func, _Conf) ->
+ ok.
+
+
+init_sftp_dirs(Config) ->
+ UserDir = ?config(priv_dir, Config),
+ SrcDir = filename:join(UserDir, "sftp_src"),
+ ok = file:make_dir(SrcDir),
+ SrcFile = "big_data",
+ DstDir = filename:join(UserDir, "sftp_dst"),
+ ok = file:make_dir(DstDir),
+ N = 100 * 1024*1024,
+ ok = file:write_file(filename:join(SrcDir,SrcFile), crypto:rand_bytes(N)),
+ [{sftp_src_dir,SrcDir}, {sftp_dst_dir,DstDir}, {src_file,SrcFile}, {sftp_size,N}
+ | Config].
+
+%%%================================================================
+openssh_client_shell(Config) ->
+ lists:foreach(
+ fun(PrefAlgs=[{kex,[Kex]}]) when Kex == 'diffie-hellman-group-exchange-sha256' ->
+ lists:foreach(
+ fun(Grp) ->
+ openssh_client_shell(Config,
+ [{preferred_algorithms, PrefAlgs},
+ {dh_gex_groups, [Grp]}
+ ])
+ end, moduli());
+ (PrefAlgs) ->
+ openssh_client_shell(Config,
+ [{preferred_algorithms, PrefAlgs}])
+ end, variants(kex,Config) ++ variants(cipher,Config)
+ ).
+
+
+openssh_client_shell(Config, Options) ->
+ SystemDir = ?config(data_dir, Config),
+ UserDir = ?config(priv_dir, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+
+ {ok, TracerPid} = erlang_trace(),
+ {ServerPid, _Host, Port} =
+ ssh_test_lib:daemon([{system_dir, SystemDir},
+ {public_key_alg, ssh_dsa},
+ {failfun, fun ssh_test_lib:failfun/2} |
+ Options]),
+ ct:sleep(500),
+
+ Data = lists:duplicate(100000, $a),
+ Cmd = lists:concat(["ssh -p ",Port,
+ " -o UserKnownHostsFile=", KnownHosts,
+ " -o \"StrictHostKeyChecking no\"",
+ " localhost '\"",Data,"\"'."]),
+%% ct:pal("Cmd ="++Cmd),
+
+ Parent = self(),
+ SlavePid = spawn(fun() ->
+ Parent ! {self(),os:cmd(Cmd)}
+ end),
+ receive
+ {SlavePid, _ClientResponse} ->
+%% ct:pal("ClientResponse = ~p",[_ClientResponse]),
+ {ok, List} = get_trace_list(TracerPid),
+ Times = find_times(List, [accept_to_hello, kex, kex_to_auth, auth, to_prompt]),
+ Algs = find_algs(List),
+ ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]),
+ lists:foreach(
+ fun({Tag,Value,Unit}) ->
+ EventData =
+ case Tag of
+ {A,B} when A==encrypt ; A==decrypt ->
+ [{value, Value},
+ {suite, ?MODULE},
+ {name, mk_name(["Cipher ",A," ",B," [",Unit,"]"])}
+ ];
+ kex ->
+ KexAlgStr = fmt_alg(Algs#alg.kex, List),
+ [{value, Value},
+ {suite, ?MODULE},
+ {name, mk_name(["Erl server kex ",KexAlgStr," [",Unit,"]"])}
+ ];
+ _ when is_atom(Tag) ->
+ [{value, Value},
+ {suite, ?MODULE},
+ {name, mk_name(["Erl server ",Tag," [",Unit,"]"])}
+ ]
+ end,
+ ct:pal("ct_event:notify ~p",[EventData]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = EventData})
+ end, Times),
+ ssh:stop_daemon(ServerPid),
+ ok
+ after 10000 ->
+ ssh:stop_daemon(ServerPid),
+ exit(SlavePid, kill),
+ {fail, timeout}
+ end.
+
+
+%%%================================================================
+openssh_client_sftp(Config) ->
+ lists:foreach(
+ fun(PrefAlgs) ->
+ openssh_client_sftp(Config, [{preferred_algorithms,PrefAlgs}])
+ end, variants(cipher,Config)).
+
+
+openssh_client_sftp(Config, Options) ->
+ SystemDir = ?config(data_dir, Config),
+ UserDir = ?config(priv_dir, Config),
+ SftpSrcDir = ?config(sftp_src_dir, Config),
+ SrcFile = ?config(src_file, Config),
+ SrcSize = ?config(sftp_size, Config),
+ KnownHosts = filename:join(UserDir, "known_hosts"),
+
+ {ok, TracerPid} = erlang_trace(),
+ {ServerPid, _Host, Port} =
+ ssh_test_lib:daemon([{system_dir, SystemDir},
+ {public_key_alg, ssh_dsa},
+ {subsystems,[ssh_sftpd:subsystem_spec([%{cwd, SftpSrcDir},
+ {root, SftpSrcDir}])]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ | Options]),
+ ct:sleep(500),
+ Cmd = lists:concat(["sftp",
+ " -b -",
+ " -P ",Port,
+ " -o UserKnownHostsFile=", KnownHosts,
+ " -o \"StrictHostKeyChecking no\"",
+ " localhost:",SrcFile
+ ]),
+%% ct:pal("Cmd = ~p",[Cmd]),
+
+ Parent = self(),
+ SlavePid = spawn(fun() ->
+ Parent ! {self(),os:cmd(Cmd)}
+ end),
+ receive
+ {SlavePid, _ClientResponse} ->
+ ct:pal("ClientResponse = ~p",[_ClientResponse]),
+ {ok, List} = get_trace_list(TracerPid),
+%%ct:pal("List=~p",[List]),
+ Times = find_times(List, [channel_open_close]),
+ Algs = find_algs(List),
+ ct:pal("Algorithms = ~p~n~nTimes = ~p",[Algs,Times]),
+ lists:foreach(
+ fun({{A,B},Value,Unit}) when A==encrypt ; A==decrypt ->
+ Data = [{value, Value},
+ {suite, ?MODULE},
+ {name, mk_name(["Sftp Cipher ",A," ",B," [",Unit,"]"])}
+ ],
+ ct:pal("sftp ct_event:notify ~p",[Data]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = Data});
+ ({channel_open_close,Value,Unit}) ->
+ Cipher = fmt_alg(Algs#alg.encrypt, List),
+ Data = [{value, round( (1024*Value) / SrcSize )},
+ {suite, ?MODULE},
+ {name, mk_name(["Sftp transfer ",Cipher," [",Unit," per kbyte]"])}
+ ],
+ ct:pal("sftp ct_event:notify ~p",[Data]),
+ ct_event:notify(#event{name = benchmark_data,
+ data = Data});
+ (_) ->
+ skip
+ end, Times),
+ ssh:stop_daemon(ServerPid),
+ ok
+ after 10000 ->
+ ssh:stop_daemon(ServerPid),
+ exit(SlavePid, kill),
+ {fail, timeout}
+ end.
+
+%%%================================================================
+variants(Tag, Config) ->
+ TagType =
+ case proplists:get_value(Tag, ssh:default_algorithms()) of
+ [{_,_}|_] -> one_way;
+ [A|_] when is_atom(A) -> two_way
+ end,
+ [ [{Tag,tag_value(TagType,Alg)}]
+ || Alg <- proplists:get_value(Tag, ?config(common_algs,Config))
+ ].
+
+tag_value(two_way, Alg) -> [Alg];
+tag_value(one_way, Alg) -> [{client2server,[Alg]},
+ {server2client,[Alg]}].
+
+%%%----------------------------------------------------------------
+fmt_alg(Alg, List) when is_atom(Alg) ->
+ fmt_alg(atom_to_list(Alg), List);
+fmt_alg(Alg = "diffie-hellman-group-exchange-sha" ++ _, List) ->
+ try
+ integer_to_list(find_gex_size_string(List))
+ of
+ GexSize -> lists:concat([Alg," ",GexSize])
+ catch
+ _:_ -> Alg
+ end;
+fmt_alg(Alg, _List) ->
+ Alg.
+
+%%%----------------------------------------------------------------
+mk_name(Name) -> [char(C) || C <- lists:concat(Name)].
+
+char($-) -> $_;
+char(C) -> C.
+
+%%%----------------------------------------------------------------
+find_times(L, Xs) ->
+ [find_time(X,L) || X <- Xs] ++
+ function_algs_times_sizes([{ssh_transport,encrypt,2},
+ {ssh_transport,decrypt,2},
+ {ssh_message,decode,1},
+ {ssh_message,encode,1}], L).
+
+-record(call, {
+ mfa,
+ pid,
+ t_call,
+ t_return,
+ args,
+ result
+ }).
+
+%%%----------------
+-define(send(M), fun(C=#call{mfa = {ssh_message,encode,1},
+ args = [M]}) ->
+ C#call.t_return
+ end).
+
+-define(recv(M), fun(C=#call{mfa = {ssh_message,decode,1},
+ result = M}) ->
+ C#call.t_call
+ end).
+
+find_time(accept_to_hello, L) ->
+ [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
+ C#call.t_call
+ end,
+ fun(C=#call{mfa = {ssh_connection_handler,hello,_},
+ args = [socket_control|_]}) ->
+ C#call.t_return
+ end
+ ], L, []),
+ {accept_to_hello, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(kex, L) ->
+ [T0,T1] = find([fun(C=#call{mfa = {ssh_connection_handler,hello,_},
+ args = [socket_control|_]}) ->
+ C#call.t_call
+ end,
+ ?send(#ssh_msg_newkeys{})
+ ], L, []),
+ {kex, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(kex_to_auth, L) ->
+ [T0,T1] = find([?send(#ssh_msg_newkeys{}),
+ ?recv(#ssh_msg_userauth_request{})
+ ], L, []),
+ {kex_to_auth, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(auth, L) ->
+ [T0,T1] = find([?recv(#ssh_msg_userauth_request{}),
+ ?send(#ssh_msg_userauth_success{})
+ ], L, []),
+ {auth, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(to_prompt, L) ->
+ [T0,T1] = find([fun(C=#call{mfa = {ssh_acceptor,handle_connection,5}}) ->
+ C#call.t_call
+ end,
+ ?recv(#ssh_msg_channel_request{request_type="env"})
+ ], L, []),
+ {to_prompt, now2micro_sec(now_diff(T1,T0)), microsec};
+find_time(channel_open_close, L) ->
+ [T0,T1] = find([?recv(#ssh_msg_channel_request{request_type="subsystem"}),
+ ?send(#ssh_msg_channel_close{})
+ ], L, []),
+ {channel_open_close, now2micro_sec(now_diff(T1,T0)), microsec}.
+
+
+
+find([F|Fs], [C|Cs], Acc) when is_function(F,1) ->
+ try
+ F(C)
+ of
+ T -> find(Fs, Cs, [T|Acc])
+ catch
+ _:_ -> find([F|Fs], Cs, Acc)
+ end;
+find([], _, Acc) ->
+ lists:reverse(Acc).
+
+
+find_algs(L) ->
+ {value, #call{result={ok,Algs}}} =
+ lists:keysearch({ssh_transport,select_algorithm,3}, #call.mfa, L),
+ Algs.
+
+find_gex_size_string(L) ->
+ %% server
+ {value, #call{result={ok,{Size, _}}}} =
+ lists:keysearch({public_key,dh_gex_group,4}, #call.mfa, L),
+ Size.
+
+%%%----------------
+function_algs_times_sizes(EncDecs, L) ->
+ Raw = [begin
+ {Tag,Size} = function_ats_result(EncDec, C),
+ {Tag, Size, now2micro_sec(now_diff(T1,T0))}
+ end
+ || EncDec <- EncDecs,
+ C = #call{mfa = ED,
+ args = Args, %%[S,Data],
+ t_call = T0,
+ t_return = T1} <- L,
+ ED == EncDec
+ ],
+ [{Alg, round(1024*Time/Size), "microsec per kbyte"} % Microseconds per 1k bytes.
+ || {Alg,Size,Time} <- lists:foldl(fun increment/2, [], Raw)].
+
+function_ats_result({ssh_transport,encrypt,2}, #call{args=[S,Data]}) ->
+ {{encrypt,S#ssh.encrypt}, size(Data)};
+function_ats_result({ssh_transport,decrypt,2}, #call{args=[S,Data]}) ->
+ {{decrypt,S#ssh.decrypt}, size(Data)};
+function_ats_result({ssh_message,encode,1}, #call{result=Data}) ->
+ {encode, size(Data)};
+function_ats_result({ssh_message,decode,1}, #call{args=[Data]}) ->
+ {decode, size(Data)}.
+
+
+increment({Alg,Sz,T}, [{Alg,SumSz,SumT}|Acc]) ->
+ [{Alg,SumSz+Sz,SumT+T} | Acc];
+increment(Spec, [X|Acc]) ->
+ [X | increment(Spec,Acc)]; % Not so many Alg, 2 or 3
+increment({Alg,Sz,T},[]) ->
+ [{Alg,Sz,T}].
+
+%%%----------------------------------------------------------------
+%%%
+%%% API for the traceing
+%%%
+get_trace_list(TracerPid) ->
+ TracerPid ! {get_trace_list,self()},
+ receive
+ {trace_list,L} -> {ok, pair_events(lists:reverse(L))}
+ after 5000 -> {error,no_reply}
+ end.
+
+erlang_trace() ->
+ TracerPid = spawn(fun trace_loop/0),
+ 0 = erlang:trace(new, true, [call,timestamp,{tracer,TracerPid}]),
+ [init_trace(MFA, tp(MFA))
+ || MFA <- [{ssh_acceptor,handle_connection,5},
+ {ssh_connection_handler,hello,2},
+ {ssh_message,encode,1},
+ {ssh_message,decode,1},
+ {ssh_transport,select_algorithm,3},
+ {ssh_transport,encrypt,2},
+ {ssh_transport,decrypt,2},
+ {ssh_message,encode,1},
+ {ssh_message,decode,1},
+ {public_key,dh_gex_group,4} % To find dh_gex group size
+ ]],
+ {ok, TracerPid}.
+
+tp({_M,_F,Arity}) ->
+ [{lists:duplicate(Arity,'_'), [], [{return_trace}]}].
+
+%%%----------------------------------------------------------------
+init_trace(MFA = {Module,_,_}, TP) ->
+ case code:is_loaded(Module) of
+ false -> code:load_file(Module);
+ _ -> ok
+ end,
+ erlang:trace_pattern(MFA, TP, [local]).
+
+
+trace_loop() ->
+ trace_loop([]).
+
+trace_loop(L) ->
+ receive
+ {get_trace_list, From} ->
+ From ! {trace_list, L},
+ trace_loop(L);
+ Ev ->
+ trace_loop([Ev|L])
+ end.
+
+pair_events(L) ->
+ pair_events(L, []).
+
+pair_events([{trace_ts,Pid,call,{M,F,Args},TS0} | L], Acc) ->
+ Arity = length(Args),
+ {ReturnValue,TS1} = find_return(Pid, {M,F,Arity}, L),
+ pair_events(L, [#call{mfa = {M,F,Arity},
+ pid = Pid,
+ t_call = TS0,
+ t_return = TS1,
+ args = Args,
+ result = ReturnValue} | Acc]);
+pair_events([_|L], Acc) ->
+ pair_events(L, Acc);
+pair_events([], Acc) ->
+ lists:reverse(Acc).
+
+
+find_return(Pid, MFA,
+ [{trace_ts, Pid, return_from, MFA, ReturnValue, TS}|_]) ->
+ {ReturnValue, TS};
+find_return(Pid, MFA, [_|L]) ->
+ find_return(Pid, MFA, L);
+find_return(_, _, []) ->
+ {undefined, undefined}.
+
+%%%----------------------------------------------------------------
+report_client_algorithms() ->
+ try
+ ssh_test_lib:extract_algos( ssh_test_lib:default_algorithms(sshc) )
+ of
+ ClientAlgs ->
+ ct:pal("The client supports:~n~p",[ClientAlgs])
+ catch
+ Cls:Err ->
+ ct:pal("Testing client about algorithms failed:~n~p ~p",[Cls,Err])
+ end.
+
+%%%----------------------------------------------------------------
+
+
+now2sec({A,B,C}) -> A*1000000 + B + C/1000000.
+
+now2micro_sec({A,B,C}) -> (A*1000000 + B)*1000000 + C.
+
+now_diff({A1,B1,C1}, {A0,B0,C0}) -> {A1-A0, B1-B0, C1-C0}.
+
+%%%================================================================
+moduli() ->
+ [{1023, 5, 16#CF973CD39DC7D62F2C45AAC5180491104C76E0FE5D80A10E6C06AE442F1F373167B0FCBC931F3C157B10A5557008FDE20D68051E6A4DB11CEE0B0749F76D7134B937A59DA998C42BC234A5C1A3CFCD70E624D253D7694076F7B1FD7B8D3427849C9377B3555796ACA58C69DFF542EEEC9859D3ADCE5CC88DF6F7817C9D182EB7},
+ {2047, 5, 16#F7693FC11FDDEAA493D3BA36F1FFF9264AA9952209203192A88A697BE9D0E306E306A27430BD87AB9EE9DB4BC78C41950C2EB0E5E4C686E8B1BA6D6A2B1FE91EF40C5EA32C51018323E1D305FE637F35ACABDBFC40AD683F779570A76869EB90015A342B2D1F7C81602688081FCAAA8D623090258D9C5C729C8CDDC0C12CA2D561DD987DB79B6AD7A2A509EBC383BF223FD95BC5A2FCC26FB3F3A0DD3FDC1228E338D3290235A596F9465F7BF490974847E616229A9E60B8F4AA161C52F655843CCCAE8821B40C426B535DE087964778652BBD4EC601C0456AE7128B593FCC64402C891227AE6EE88CC839416FBF462B4852999C646BE0BED7D8CF2BE5E381EF},
+ {4095, 2, 16#C8842271626E53546E0C712FA265713F2EE073C20A0723C96B6B182B1EAACC96233D4A199BD0E85F264078A513AD2454F284B8DF543D85019D1E70F2FF54BA43EFBC64AF465C170C3E376F5EC328F98E33E1ED8BED84FA097ABE584152B0E9827ED5CC2B1D4F5ECF2DC46F45C59816D02698EA26F319311E2B6973E83C37021CC8B416AEF653896A1764EE0CEE718A45E8B47CB960BD5907D0E843E8A8E7D4698363C3C3FB3ADC512368B72CAF16510C69052EA2AF51BE00BC8CA04DF1F00A00CC2CA4D74254A1E8738460FD244DDB446CB36554B0A24EEF3710E44DBCF39881E7D3F9AE223388084E7A49A3CB12612AE36416C0EB5628DF1477FEE4A5CF77CDC09AA0E2C989C0B7D1310AFA44B81DA79A65226C7EA510057991EABF9388DC5EA9F52FEA5D3B0872843F50878740794E523E9DC60E0EA1FC8746A7B2AA31FCA89AAA2FA907BED116C69D98F912DD5089BECF28577064225DE96FC214ED1794E7CCE8024F94036D915A123A464C951DA96A5ED7F286F205BEE71BDE2D133FD1891B31178FF25D31611A5B7839F0E68EAF0F8901A571E6917C580F31842A9F19C47E0638483B7947DDCD7864660AC2F8B2C430F1E7FC0F22FA51F96F0499332C5AD3FF9DC7F4332DD5BCCA820CC779B90C0F4C5F0CA52E96FAA187361753FBADC5C80D0492CD80A3EEA5D578772DA9FC1C0E10A0203098AF36D0ED2156BA7321EB},
+ {6143, 5, 16#FD9E6B52785CD7BE64D396A599DA4B97CD0BB49183F932A97694D80CA553354DBC26E77B8A0EC002257AADDF6AD27819CE64A06416E4A80B6EA92F28EA8D5B96C774109EEE5816B4B18F84368D1B41864C11AA73D6881675D779B174F6B4E344303F3EFD11BD7DE468467242372FD00908F296F5A2B20E2684F9122D08A46D647B05E298F0BCDAB60468349CCA6DA1B9FEBBC69D256FB9A3F1980F68466364FCEF1C98C1405191A6737A3627BA7F7313A8A18FC0B8521BF3430B1C6805CB44BCEB39904DD30130D24B225B598ED83C5FD757B80189FD9D5C2F9596687C40BAB1C6ED6244944629849D074A4C33FB15DDB3F9760FC59C44BEBB0EC032177147F61789769DAAAE2123CE488F7ECF19BDA051925BA9ED11EAA72DF70C9ECC8F714B4C35728E6679E66A1B56CCAE0FBBD3F9EBF950D4D623ED78E77CC3AD604E91F304EA78CE876F036214BD6F1977BD04C9ADD707D7A3BCCE87AD5D5A11C95E7025B0EA9C649DCB37942A3970A4FB04C284E4DDB4DC90163353B98B1C254FFD28443353F17A87C02E0BDB9F05424CC44C86309F1D73706F039CDAAC3EDC1A64F38FB42707D351DB5360C2680ADC1CC8D1C4AD312ACC904382C26BE33DA0E61429A5940820356ED28586BEB629ED1521D12D25B4DA01926295F3DA504DC9F431B719AC63277BE675E6F6DD4F7499CA11A23744577D653941963E8DAB610F7F226DB52CE5C683F72AEED2B6CE35ED07C29410397A6F7F606477CCC0EDE18CD0D96A7863BC4606193A8799B5AC1EEE6AC5EE36AC3077EC8DAB30EE94434B45B78BC13D96F74D6C4056EAA528CD3C68D308344808819B12F2BFB95A5C1A7DEEE188BF139216DDB7D757D7A50D3C46CE18881D776D617DCFFAA62276045373AA4D9446D7570338F99C0CA8A08851B4F9D388B4C275D3F9B7BA25F235D4329F63F7457C2EB5C68CE2A96D19766F0ED8E19F66DF3C5E29A38795B2F92291BB6EAB6F70A7E89DC9691F28486E9CF87FF11D5DF2E6B030A30B5D476AD59A34EE7262712ED96CEF4A5CAC3F08B3563D44683F746DA094C9CDB34427AF8D8CC2AE1B23C3BEB637},
+ {8191, 2, 16#DC61EF13E4F3FC10CC946EEABC33F83EFCB35E0F47E4EC25C1CCBB2C7B502B2EFB0691AA231C8476DD51BA73204E6EA10B1A970FE2CF14AF01E72E1AEA87519A91D00D1499189F94A6CDA9E29C05F11F17FE74A4919A710A2787E180744465DF81C62AA65662FDA46FA6175E8A31E5B29E66DED6701C8FC4217E91D733FE94380F046680967D4CEA7BAC8F3916CDF96AA2C474FAD9650F48403FD0B5B756D34667D36A07767FA33027AE55484D0F701C3CA16632F413A14E4B8645AFAF15B78978C19A7661EDC569BEC72394B1204B166A48FCD5F56BE29840C7794CA6D3440356F15858CDCA9B429C7EA92E17242893FDC8C9C63841A382C32F20CFAB121B4BCAFD7BF9EF07FBF7CDFFECA0CEF3A49C3E2B24FA836F3318435255655E1B281071F62D5E4CD63361299B7828F72936E3FEA9E8044562A6F6ADD5321187C3101E4669C6271598FE1A866C93FE2870A4CEB9254BA32A4719E439317EA42200A335B5CFFA7946A7D0F1BD1A69AA11288B73C71C80B77FE3707CB077DDDEA5CA36A449FAB230C9625A0B12F8275D3FF82F5DA380E7A3F11B6F155FE7E91AC960BD95D9B13F7423AB9B15CC3C4DC34EF296033F009468EA16A721AD659F56C18516025050749ABF05E6D3EBD9778142A530979291F46DAA399A86B7BCDF09CC3E6EEF101419762A306DB45AEFC96C64E83F28338D55905F6A387E0F515E580C3A9B35330E21C32198CDEE3AFB355967A098F635FCA7C49CB4E1E82464B2B390EF1F259E40B9A06235C0273F76284FE6BD534EF3AF7CB01A4A5252B8B94CADC2850B2E56D53F9A31D7C029DF967D0A30C05BC64E119BED6076818FABC8CDD93F3255693E14EFC1A740A5D63A5E847FFE87BAB1DDE0506E1762EA61EFA9F9756151ECCCADD91B98A961A901A2D8B01ABDDD29EC804E8C8D28214BBA26048F924CA66316696E51A49D02FF034D20E44914B1115339CAD3819E0CB1640F0084886FEDDE5E28C29DC48ED30A8C3D789734338F5A9DF42584326E536FD1CF30BC85B8DCBD6120D127C98FE4B3614074F13C2CA4854E6D794156C185C40EB3DA7619CE96ADAF0941BD5499848B034C2B11DFECC0BDFA81C594241F759EF53FC7CDE7F2DE4F23CF81A5A0B7D62E31DABB9198D40307F7824DD130B7D1B80E9B6D322FEEDB5ACE34944F0BFB7D016762A9B2E173BFDD69303766AFBAB45FAB75D05430B4A3515858C4B7F04E23414E4AD03842CB0A20D8FF4B59B7C852BA9A5BE982A8ADA5CB70C36CE2A4D2C31A7015C9F3275E43D192C1B2924424088907A057DA7F2D32A2149922AB2E33F2147D637A3508911CB3FEA5E1AAB4525BACF27B6DD7A3E0AFA978FC3A39DE8882FB22688C3CCC92B6E69ACB0BBF575AB3368E51A2F6A20C414C6F146727CC0045F29061E695D29F7C030CE6929EB3AD11A5CBD0CDEE37347869A3}].
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa b/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa
new file mode 100644
index 0000000000..d306f8b26e
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_dsa
@@ -0,0 +1,13 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDfi2flSTZZofwT4yQT0NikX/LGNT7UPeB/XEWe/xovEYCElfaQ
+APFixXvEgXwoojmZ5kiQRKzLM39wBP0jPERLbnZXfOOD0PDnw0haMh7dD7XKVMod
+/EigVgHf/qBdM2M8yz1s/rRF7n1UpLSypziKjkzCm7JoSQ2zbWIPdmBIXwIVAMgP
+kpr7Sq3O7sHdb8D601DRjoExAoGAMOQxDfB2Fd8ouz6G96f/UOzRMI/Kdv8kYYKW
+JIGY+pRYrLPyYzUeJznwZreOJgrczAX+luHnKFWJ2Dnk5CyeXk67Wsr7pJ/4MBMD
+OKeIS0S8qoSBN8+Krp79fgA+yS3IfqbkJLtLu4EBaCX4mKQIX4++k44d4U5lc8pt
++9hlEI8CgYEAznKxx9kyC6bVo7LUYKaGhofRFt0SYFc5PVmT2VUGRs1R6+6DPD+e
+uEO6IhFct7JFSRbP9p0JD4Uk+3zlZF+XX6b2PsZkeV8f/02xlNGUSmEzCSiNg1AX
+Cy/WusYhul0MncWCHMcOZB5rIvU/aP5EJJtn3xrRaz6u0SThF6AnT34CFQC63czE
+ZU8w8Q+H7z0j+a+70x2iAw==
+-----END DSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256
new file mode 100644
index 0000000000..4b1eb12eaa
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49
+AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br
+N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub
new file mode 100644
index 0000000000..a0147e60fa
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384
new file mode 100644
index 0000000000..4e8aa40959
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCYXb6OSAZyXRfLXOtMo43za197Hdc/T0YKjgQQjwDt6rlRwqTh7v7S
+PV2kXwNGdWigBwYFK4EEACKhZANiAARN2khlJUOOIiwsWHEALwDieeZR96qL4pUd
+ci7aeGaczdUK5jOA9D9zmBZtSYTfO8Cr7ekVghDlcWAIJ/BXcswgQwSEQ6wyfaTF
+8FYfyr4l3u9IirsnyaFzeIgeoNis8Gw=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub
new file mode 100644
index 0000000000..41e722e545
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBE3aSGUlQ44iLCxYcQAvAOJ55lH3qovilR1yLtp4ZpzN1QrmM4D0P3OYFm1JhN87wKvt6RWCEOVxYAgn8FdyzCBDBIRDrDJ9pMXwVh/KviXe70iKuyfJoXN4iB6g2KzwbA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521 b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521
new file mode 100644
index 0000000000..7196f46e97
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEFMadoz4ckEcClfqXa2tiUuYkJdDfwq+/iFQcpt8ESuEd26IY/vm47Q
+9UzbPkO4ou8xkNsQ3WvCRQBBWtn5O2kUU6AHBgUrgQQAI6GBiQOBhgAEAde5BRu5
+01/jS0jRk212xsb2DxPrxNpgp6IMCV8TA4Eps+8bSqHB091nLiBcP422HXYfuCd7
+XDjSs8ihcmhp0hCRASLqZR9EzW9W/SOt876May1Huj5X+WSO6RLe7vPn9vmf7kHf
+pip6m7M7qp2qGgQ3q2vRwS2K/O6156ohiOlmuuFs
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub
new file mode 100644
index 0000000000..8f059120bc
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHXuQUbudNf40tI0ZNtdsbG9g8T68TaYKeiDAlfEwOBKbPvG0qhwdPdZy4gXD+Nth12H7gne1w40rPIoXJoadIQkQEi6mUfRM1vVv0jrfO+jGstR7o+V/lkjukS3u7z5/b5n+5B36YqepuzO6qdqhoEN6tr0cEtivzuteeqIYjpZrrhbA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa b/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa
new file mode 100644
index 0000000000..9d7e0dd5fb
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/id_rsa
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQD1OET+3O/Bvj/dtjxDTXmj1oiJt4sIph5kGy0RfjoPrZfaS+CU
+DhakCmS6t2ivxWFgtpKWaoGMZMJqWj6F6ZsumyFl3FPBtujwY/35cgifrI9Ns4Tl
+zR1uuengNBmV+WRQ5cd9F2qS6Z8aDQihzt0r8JUqLcK+VQbrmNzboCCQQwIDAQAB
+AoGAPQEyqPTt8JUT7mRXuaacjFXiweAXhp9NEDpyi9eLOjtFe9lElZCrsUOkq47V
+TGUeRKEm9qSodfTbKPoqc8YaBJGJPhUaTAcha+7QcDdfHBvIsgxvU7ePVnlpXRp3
+CCUEMPhlnx6xBoTYP+fRU0e3+xJIPVyVCqX1jAdUMkzfRoECQQD6ux7B1QJAIWyK
+SGkbDUbBilNmzCFNgIpOP6PA+bwfi5d16diTpra5AX09keQABAo/KaP1PdV8Vg0p
+z4P3A7G3AkEA+l+AKG6m0kQTTBMJDqOdVPYwe+5GxunMaqmhokpEbuGsrZBl5Dvd
+WpcBjR7jmenrhKZRIuA+Fz5HPo/UQJPl1QJBAKxstDkeED8j/S2XoFhPKAJ+6t39
+sUVICVTIZQeXdmzHJXCcUSkw8+WEhakqw/3SyW0oaK2FSWQJFWJUZ+8eJj8CQEh3
+xeduB5kKnS9CvzdeghZqX6QvVosSdtlUmfUYW/BgH5PpHKTP8wTaeld3XldZTpMJ
+dKiMkUw2+XYROVUrubUCQD+Na1LhULlpn4ISEtIEfqpdlUhxDgO15Wg8USmsng+x
+ICliVOSQtwaZjm8kwaFt0W7XnpnDxbRs37vIEbIMWak=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key
new file mode 100644
index 0000000000..51ab6fbd88
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key
@@ -0,0 +1,13 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCClaHzE2ul0gKSUxah5W0W8UiJLy4hXngKEqpaUq9SSdVdY2LK
+wVfKH1gt5iuaf1FfzOhsIC9G/GLnjYttXZc92cv/Gfe3gR+s0ni2++MX+T++mE/Q
+diltXv/Hp27PybS67SmiFW7I+RWnT2OKlMPtw2oUuKeztCe5UWjaj/y5FQIVAPLA
+l9RpiU30Z87NRAHY3NTRaqtrAoGANMRxw8UfdtNVR0CrQj3AgPaXOGE4d+G4Gp4X
+skvnCHycSVAjtYxebUkzUzt5Q6f/IabuLUdge3gXrc8BetvrcKbp+XZgM0/Vj2CF
+Ymmy3in6kzGZq7Fw1sZaku6AOU8vLa5woBT2vAcHLLT1bLAzj7viL048T6MfjrOP
+ef8nHvACgYBhDWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah
+/XcF3DeRF+eEoz48wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+U
+ykSTXYUbtsfTNRFQGBW2/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0CgIVAN4wtL5W
+Lv62jKcdskxNyz2NQoBx
+-----END DSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..4dbb1305b0
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_dsa_key.pub
@@ -0,0 +1,11 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1kc3MAAACBAIKVofMTa6XSApJTFqHlbRbxSIkvLiFeeAoSqlpSr1JJ1V1j
+YsrBV8ofWC3mK5p/UV/M6GwgL0b8YueNi21dlz3Zy/8Z97eBH6zSeLb74xf5P76YT9B2
+KW1e/8enbs/JtLrtKaIVbsj5FadPY4qUw+3DahS4p7O0J7lRaNqP/LkVAAAAFQDywJfU
+aYlN9GfOzUQB2NzU0WqrawAAAIA0xHHDxR9201VHQKtCPcCA9pc4YTh34bganheyS+cI
+fJxJUCO1jF5tSTNTO3lDp/8hpu4tR2B7eBetzwF62+twpun5dmAzT9WPYIViabLeKfqT
+MZmrsXDWxlqS7oA5Ty8trnCgFPa8BwcstPVssDOPu+IvTjxPox+Os495/yce8AAAAIBh
+DWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah/XcF3DeRF+eEoz48
+wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+UykSTXYUbtsfTNRFQGBW2
+/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0Cg==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..2979ea88ed
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIMe4MDoit0t8RzSVPwkCBemQ9fhXL+xnTSAWISw8HNCioAoGCCqGSM49
+AwEHoUQDQgAEo2q7U3P6r0W5WGOLtM78UQtofM9UalEhiZeDdiyylsR/RR17Op0s
+VPGSADLmzzgcucLEKy17j2S+oz42VUJy5A==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..85dc419345
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKNqu1Nz+q9FuVhji7TO/FELaHzPVGpRIYmXg3YsspbEf0UdezqdLFTxkgAy5s84HLnCxCste49kvqM+NlVCcuQ= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..fb1a862ded
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDArxbDfh3p1okrD9wQw6jJ4d4DdlBPD5GqXE8bIeRJiK41Sh40LgvPw
+mkqEDSXK++CgBwYFK4EEACKhZANiAAScl43Ih2lWTDKrSox5ve5uiTXil4smsup3
+CfS1XPjKxgBAmlfBim8izbdrT0BFdQzz2joduNMtpt61wO4rGs6jm0UP7Kim9PC7
+Hneb/99fIYopdMH5NMnk60zGO1uZ2vc=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..428d5fb7d7
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJyXjciHaVZMMqtKjHm97m6JNeKXiyay6ncJ9LVc+MrGAECaV8GKbyLNt2tPQEV1DPPaOh240y2m3rXA7isazqObRQ/sqKb08Lsed5v/318hiil0wfk0yeTrTMY7W5na9w== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..3e51ec2ecd
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIB8O1BFkl2HQjQLRLonEZ97da/h39DMa9/0/hvPZWAI8gUPEQcHxRx
+U7b09p3Zh+EBbMFq8+1ae9ds+ZTxE4WFSvKgBwYFK4EEACOhgYkDgYYABAAlWVjq
+Bzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/
+vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5
+ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..017a29f4da
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAlWVjqBzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key
new file mode 100644
index 0000000000..79968bdd7d
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
+zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
+6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
+AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
+NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
+udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
+WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
+n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
+sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
+64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
+m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
+tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..75d2025c71
--- /dev/null
+++ b/lib/ssh/test/ssh_benchmark_SUITE_data/ssh_host_rsa_key.pub
@@ -0,0 +1,5 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
+semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
+RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_key_cb.erl b/lib/ssh/test/ssh_key_cb.erl
new file mode 100644
index 0000000000..388ec2ecc1
--- /dev/null
+++ b/lib/ssh/test/ssh_key_cb.erl
@@ -0,0 +1,45 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+
+%% Note: This module is used by ssh_basic_SUITE
+
+-module(ssh_key_cb).
+-behaviour(ssh_client_key_api).
+-compile(export_all).
+
+add_host_key(_, _, _) ->
+ ok.
+
+is_host_key(_, _, _, _) ->
+ true.
+
+user_key('ssh-dss', Opts) ->
+ UserDir = proplists:get_value(user_dir, Opts),
+ KeyFile = filename:join(filename:dirname(UserDir), "id_dsa"),
+ {ok, KeyBin} = file:read_file(KeyFile),
+ [Entry] = public_key:pem_decode(KeyBin),
+ Key = public_key:pem_entry_decode(Entry),
+ {ok, Key};
+
+user_key(_Alg, _Opt) ->
+ {error, "Not Supported"}.
diff --git a/lib/ssh/test/ssh_key_cb_options.erl b/lib/ssh/test/ssh_key_cb_options.erl
new file mode 100644
index 0000000000..afccb34f0f
--- /dev/null
+++ b/lib/ssh/test/ssh_key_cb_options.erl
@@ -0,0 +1,44 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+
+%% Note: This module is used by ssh_basic_SUITE
+
+-module(ssh_key_cb_options).
+-behaviour(ssh_client_key_api).
+-compile(export_all).
+
+add_host_key(_, _, _) ->
+ ok.
+
+is_host_key(_, _, _, _) ->
+ true.
+
+user_key('ssh-dss', Opts) ->
+ KeyCbOpts = proplists:get_value(key_cb_private, Opts),
+ KeyBin = proplists:get_value(priv_key, KeyCbOpts),
+ [Entry] = public_key:pem_decode(KeyBin),
+ Key = public_key:pem_entry_decode(Entry),
+ {ok, Key};
+
+user_key(_Alg, _Opt) ->
+ {error, "Not Supported"}.
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 4639904061..fe197f8672 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -48,6 +48,7 @@ all() ->
[{group,tool_tests},
{group,kex},
{group,service_requests},
+ {group,authentication},
{group,packet_size_error},
{group,field_size_error}
].
@@ -78,7 +79,9 @@ groups() ->
bad_very_long_service_name,
empty_service_name,
bad_service_name_then_correct
- ]}
+ ]},
+ {authentication, [], [client_handles_keyboard_interactive_0_pwds
+ ]}
].
@@ -277,10 +280,7 @@ no_common_alg_server_disconnects(Config) ->
{send, hello},
{match, #ssh_msg_kexinit{_='_'}, receive_msg},
{send, ssh_msg_kexinit}, % with server unsupported 'ssh-dss' !
- {match,
- {'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'},
- tcp_closed]},
- receive_msg}
+ {match, disconnect(), receive_msg}
]
).
@@ -321,10 +321,7 @@ no_common_alg_client_disconnects(Config) ->
first_kex_packet_follows = false,
reserved = 0
}},
- {match,
- {'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'},
- tcp_closed]},
- receive_msg}
+ {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}
],
InitialState)
}
@@ -435,10 +432,7 @@ bad_service_name_then_correct(Config) ->
[{set_options, [print_ops, print_seqnums, print_messages]},
{send, #ssh_msg_service_request{name = "kdjglkfdjgkldfjglkdfjglkfdjglkj"}},
{send, #ssh_msg_service_request{name = "ssh-connection"}},
- {match, {'or',[#ssh_msg_disconnect{_='_'},
- tcp_closed
- ]},
- receive_msg}
+ {match, disconnect(), receive_msg}
], InitialState).
@@ -448,10 +442,7 @@ bad_service_name(Config, Name) ->
ssh_trpt_test_lib:exec(
[{set_options, [print_ops, print_seqnums, print_messages]},
{send, #ssh_msg_service_request{name = Name}},
- {match, {'or',[#ssh_msg_disconnect{_='_'},
- tcp_closed
- ]},
- receive_msg}
+ {match, disconnect(), receive_msg}
], InitialState).
%%%--------------------------------------------------------------------
@@ -474,10 +465,7 @@ bad_packet_length(Config, LengthExcess) ->
PacketFun}},
%% Prohibit remote decoder starvation:
{send, #ssh_msg_service_request{name="ssh-userauth"}},
- {match, {'or',[#ssh_msg_disconnect{_='_'},
- tcp_closed
- ]},
- receive_msg}
+ {match, disconnect(), receive_msg}
], InitialState).
%%%--------------------------------------------------------------------
@@ -506,12 +494,85 @@ bad_service_name_length(Config, LengthExcess) ->
PacketFun} },
%% Prohibit remote decoder starvation:
{send, #ssh_msg_service_request{name="ssh-userauth"}},
- {match, {'or',[#ssh_msg_disconnect{_='_'},
- tcp_closed
- ]},
- receive_msg}
+ {match, disconnect(), receive_msg}
], InitialState).
+%%%--------------------------------------------------------------------
+%%% This is due to a fault report (OTP-13255) with OpenSSH-6.6.1
+client_handles_keyboard_interactive_0_pwds(Config) ->
+ {User,_Pwd} = server_user_password(Config),
+
+ %% Create a listening socket as server socket:
+ {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+ HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+
+ %% Start a process handling one connection on the server side:
+ spawn_link(
+ fun() ->
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{set_options, [print_ops, print_messages]},
+ {accept, [{system_dir, system_dir(Config)},
+ {user_dir, user_dir(Config)}]},
+ receive_hello,
+ {send, hello},
+
+ {send, ssh_msg_kexinit},
+ {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+
+ {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+ {send, ssh_msg_kexdh_reply},
+
+ {send, #ssh_msg_newkeys{}},
+ {match, #ssh_msg_newkeys{_='_'}, receive_msg},
+
+ {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+ {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+
+ {match, #ssh_msg_userauth_request{service="ssh-connection",
+ method="none",
+ user=User,
+ _='_'}, receive_msg},
+ {send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive",
+ partial_success = false}},
+
+ {match, #ssh_msg_userauth_request{service="ssh-connection",
+ method="keyboard-interactive",
+ user=User,
+ _='_'}, receive_msg},
+ {send, #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 1,
+ data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>>
+ }},
+ {match, #ssh_msg_userauth_info_response{num_responses = 1,
+ _='_'}, receive_msg},
+
+ %% the next is strange, but openssh 6.6.1 does this and this is what this testcase is about
+ {send, #ssh_msg_userauth_info_request{name = "",
+ instruction = "",
+ language_tag = "",
+ num_prompts = 0,
+ data = <<>>
+ }},
+ {match, #ssh_msg_userauth_info_response{num_responses = 0,
+ data = <<>>,
+ _='_'}, receive_msg},
+ %% Here we know that the tested fault is fixed
+ {send, #ssh_msg_userauth_success{}},
+ close_socket,
+ print_state
+ ],
+ InitialState)
+ end),
+
+ %% and finally connect to it with a regular Erlang SSH client:
+ {ok,_} = std_connect(HostPort, Config,
+ [{preferred_algorithms,[{kex,['diffie-hellman-group1-sha1']}]}]
+ ).
+
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
@@ -640,3 +701,16 @@ connect_and_kex(Config, InitialState) ->
{match, #ssh_msg_newkeys{_='_'}, receive_msg}
],
InitialState).
+
+%%%----------------------------------------------------------------
+
+%%% For matching peer disconnection
+disconnect() ->
+ disconnect('_').
+
+disconnect(Code) ->
+ {'or',[#ssh_msg_disconnect{code = Code,
+ _='_'},
+ tcp_closed,
+ {tcp_error,econnaborted}
+ ]}.
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
index 227dfcddcd..e5cfa58bad 100644
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl
@@ -57,9 +57,15 @@ end_per_suite(_Config) ->
%%--------------------------------------------------------------------
init_per_group(aes_gcm, Config) ->
- [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']},
- {server2client,['[email protected]']}]}]}
- | Config];
+ case lists:member({client2server,['[email protected]']},
+ ssh_transport:supported_algorithms(cipher)) of
+ true ->
+ [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']},
+ {server2client,['[email protected]']}]}]}
+ | Config];
+ false ->
+ {skip, "aes_gcm not supported"}
+ end;
init_per_group(_, Config) ->
[{preferred_algorithms, ssh:default_algorithms()} | Config].
@@ -107,7 +113,9 @@ rekey_limit(Config) ->
UserDir = ?config(priv_dir, Config),
DataFile = filename:join(UserDir, "rekey.data"),
- {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]),
+ Algs = ?config(preferred_algorithms, Config),
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000},
{max_random_length_padding,0}]),
@@ -151,7 +159,9 @@ renegotiate1(Config) ->
UserDir = ?config(priv_dir, Config),
DataFile = filename:join(UserDir, "renegotiate1.data"),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]),
+ Algs = ?config(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
RPort = ssh_test_lib:inet_port(),
{ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
@@ -189,7 +199,9 @@ renegotiate2(Config) ->
UserDir = ?config(priv_dir, Config),
DataFile = filename:join(UserDir, "renegotiate2.data"),
- {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]),
+ Algs = ?config(preferred_algorithms, Config),
+ {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0},
+ {preferred_algorithms,Algs}]),
RPort = ssh_test_lib:inet_port(),
{ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort),
diff --git a/lib/ssh/test/ssh_test_cli.erl b/lib/ssh/test/ssh_test_cli.erl
index cd9ad5f2ff..697ddb730d 100644
--- a/lib/ssh/test/ssh_test_cli.erl
+++ b/lib/ssh/test/ssh_test_cli.erl
@@ -4,20 +4,25 @@
-record(state, {
type,
+ tmpdir,
id,
ref,
port
}).
-init([Type]) ->
- {ok, #state{type = Type}}.
+
+init([Type]) -> init([Type,"/tmp"]);
+
+init([Type,TmpDir]) ->
+ {ok, #state{type = Type,
+ tmpdir = TmpDir}}.
handle_msg({ssh_channel_up, Id, Ref}, S) ->
User = get_ssh_user(Ref),
ok = ssh_connection:send(Ref,
Id,
<< "\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n" >>),
- Port = run_portprog(User, S#state.type),
+ Port = run_portprog(User, S#state.type, S#state.tmpdir),
{ok, S#state{port = Port, id = Id, ref = Ref}};
handle_msg({Port, {data, Data}}, S = #state{port = Port}) ->
@@ -68,10 +73,10 @@ handle_ssh_msg({ssh_cm, _, {exit_signal, Id, _, _, _}},
terminate(_Why, _S) ->
nop.
-run_portprog(User, cli) ->
+run_portprog(User, cli, TmpDir) ->
Pty_bin = os:find_executable("cat"),
open_port({spawn_executable, Pty_bin},
- [stream, {cd, "/tmp"}, {env, [{"USER", User}]},
+ [stream, {cd, TmpDir}, {env, [{"USER", User}]},
{args, []}, binary,
exit_status, use_stdio, stderr_to_stdout]).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 424afc76fe..2db55b97b4 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -541,7 +541,6 @@ default_algorithms(sshc, DaemonOptions) ->
ct:fail("No server respons 2")
end.
-
run_fake_ssh({ok,InitialState}) ->
KexInitPattern =
#ssh_msg_kexinit{
@@ -583,6 +582,40 @@ run_fake_ssh({ok,InitialState}) ->
{server2client, to_atoms(CompS2C)}]}].
+%%%----------------------------------------------------------------
+extract_algos(Spec) ->
+ [{Tag,get_atoms(List)} || {Tag,List} <- Spec].
+
+get_atoms(L) ->
+ lists:usort(
+ [ A || X <- L,
+ A <- case X of
+ {_,L1} when is_list(L1) -> L1;
+ Y when is_atom(Y) -> [Y]
+ end]).
+
+
+intersection(AlgoSpec1, AlgoSpec2) -> intersect(sort_spec(AlgoSpec1), sort_spec(AlgoSpec2)).
+
+intersect([{Tag,S1}|Ss1], [{Tag,S2}|Ss2]) ->
+ [{Tag,intersect(S1,S2)} | intersect(Ss1,Ss2)];
+intersect(L1=[A1|_], L2=[A2|_]) when is_atom(A1),is_atom(A2) ->
+ Diff = L1 -- L2,
+ L1 -- Diff;
+intersect(_, _) ->
+ [].
+
+intersect_bi_dir([{Tag,[{client2server,L1},{server2client,L2}]}|T]) ->
+ [{Tag,intersect(L1,L2)} | intersect_bi_dir(T)];
+intersect_bi_dir([H={_,[A|_]}|T]) when is_atom(A) ->
+ [H | intersect_bi_dir(T)];
+intersect_bi_dir([]) ->
+ [].
+
+
+sort_spec(L = [{_,_}|_] ) -> [{Tag,sort_spec(Es)} || {Tag,Es} <- L];
+sort_spec(L) -> lists:usort(L).
+
%%--------------------------------------------------------------------
sshc(Tag) ->
to_atoms(
@@ -646,3 +679,15 @@ ssh_supports(Alg, SshDefaultAlg_tag) ->
{false,UnSup}
end
end.
+
+%%%----------------------------------------------------------------
+has_inet6_address() ->
+ try
+ [throw(6) || {ok,L} <- [inet:getifaddrs()],
+ {_,L1} <- L,
+ {addr,{_,_,_,_,_,_,_,_}} <- L1]
+ of
+ [] -> false
+ catch
+ throw:6 -> true
+ end.
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index d1dfa2efdf..67a61d3c11 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -96,19 +96,9 @@ end_per_group(_, Config) ->
init_per_testcase(erlang_server_openssh_client_public_key_dsa, Config) ->
- case ssh_test_lib:openssh_supports(sshc, public_key, 'ssh-dss') of
- true ->
- init_per_testcase('__default__',Config);
- false ->
- {skip,"openssh client does not support DSA"}
- end;
+ chk_key(sshc, 'ssh-dss', ".ssh/id_dsa", Config);
init_per_testcase(erlang_client_openssh_server_publickey_dsa, Config) ->
- case ssh_test_lib:openssh_supports(sshd, public_key, 'ssh-dss') of
- true ->
- init_per_testcase('__default__',Config);
- false ->
- {skip,"openssh client does not support DSA"}
- end;
+ chk_key(sshd, 'ssh-dss', ".ssh/id_dsa", Config);
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
@@ -117,6 +107,27 @@ end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
+
+chk_key(Pgm, Name, File, Config) ->
+ case ssh_test_lib:openssh_supports(Pgm, public_key, Name) of
+ false ->
+ {skip,lists:concat(["openssh client does not support ",Name])};
+ true ->
+ {ok,[[Home]]} = init:get_argument(home),
+ KeyFile = filename:join(Home, File),
+ case file:read_file(KeyFile) of
+ {ok, Pem} ->
+ case public_key:pem_decode(Pem) of
+ [{_,_, not_encrypted}] ->
+ init_per_testcase('__default__',Config);
+ _ ->
+ {skip, {error, "Has pass phrase can not be used by automated test case"}}
+ end;
+ _ ->
+ {skip, lists:concat(["no ~/",File])}
+ end
+ end.
+
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -328,27 +339,16 @@ erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
erlang_client_openssh_server_publickey_dsa() ->
[{doc, "Validate using dsa publickey."}].
erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
- {ok,[[Home]]} = init:get_argument(home),
- KeyFile = filename:join(Home, ".ssh/id_dsa"),
- case file:read_file(KeyFile) of
- {ok, Pem} ->
- case public_key:pem_decode(Pem) of
- [{_,_, not_encrypted}] ->
- ConnectionRef =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{public_key_alg, ssh_dsa},
- {user_interaction, false},
- silently_accept_hosts]),
- {ok, Channel} =
- ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, Channel),
- ok = ssh:close(ConnectionRef);
- _ ->
- {skip, {error, "Has pass phrase can not be used by automated test case"}}
- end;
- _ ->
- {skip, "no ~/.ssh/id_dsa"}
- end.
+ ConnectionRef =
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{public_key_alg, ssh_dsa},
+ {user_interaction, false},
+ silently_accept_hosts]),
+ {ok, Channel} =
+ ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, Channel),
+ ok = ssh:close(ConnectionRef).
+
%%--------------------------------------------------------------------
erlang_server_openssh_client_public_key_dsa() ->
[{doc, "Validate using dsa publickey."}].
@@ -360,21 +360,25 @@ erlang_server_openssh_client_public_key_dsa(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{public_key_alg, ssh_dsa},
{failfun, fun ssh_test_lib:failfun/2}]),
-
+
ct:sleep(500),
Cmd = "ssh -p " ++ integer_to_list(Port) ++
" -o UserKnownHostsFile=" ++ KnownHosts ++
" " ++ Host ++ " 1+1.",
- SshPort = open_port({spawn, Cmd}, [binary]),
+ SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
receive
- {SshPort,{data, <<"2\n">>}} ->
+ {SshPort,{data, <<"2\n">>}} ->
ok
after ?TIMEOUT ->
- ct:fail("Did not receive answer")
+ receive
+ X -> ct:fail("Received: ~p",[X])
+ after 0 ->
+ ct:fail("Did not receive answer")
+ end
end,
- ssh:stop_daemon(Pid).
+ ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
erlang_client_openssh_server_password() ->
@@ -384,10 +388,10 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
UserDir = ?config(data_dir, Config),
{error, Reason0} =
ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
ct:log("Test of user foo that does not exist. "
"Error msg: ~p~n", [Reason0]),
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 25b19133b1..55d12abffe 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.2
+SSH_VSN = 4.2.1
APP_VSN = "ssh-$(SSH_VSN)"