diff options
Diffstat (limited to 'lib/ssh')
-rw-r--r-- | lib/ssh/doc/src/notes.xml | 185 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh.xml | 67 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_app.xml | 43 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_connection.xml | 14 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_server_key_api.xml | 4 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_sftp.xml | 31 | ||||
-rw-r--r-- | lib/ssh/src/ssh.erl | 74 | ||||
-rw-r--r-- | lib/ssh/src/ssh.hrl | 3 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 13 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.hrl | 1 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 100 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 5 | ||||
-rw-r--r-- | lib/ssh/test/ssh_basic_SUITE.erl | 106 | ||||
-rw-r--r-- | lib/ssh/test/ssh_protocol_SUITE.erl | 10 | ||||
-rw-r--r-- | lib/ssh/test/ssh_renegotiate_SUITE.erl | 24 | ||||
-rw-r--r-- | lib/ssh/test/ssh_test_lib.erl | 12 | ||||
-rw-r--r-- | lib/ssh/test/ssh_to_openssh_SUITE.erl | 88 |
17 files changed, 574 insertions, 206 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 010b1b15c7..8fb689fdd5 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,191 @@ <file>notes.xml</file> </header> +<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 + [email protected].</p> + <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 18bced2d1d..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> @@ -206,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> @@ -233,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, @@ -243,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 @@ -418,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> @@ -451,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, @@ -461,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 @@ -515,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> 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 1d29c95229..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) -> @@ -411,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) -> @@ -522,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 -> @@ -780,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..fdbb5c152a 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, 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..f082db136c 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -999,7 +999,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 +1022,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 +1408,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/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index d4cb03f2f2..6c4c215b3d 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, @@ -86,6 +90,7 @@ 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, @@ -102,6 +107,10 @@ 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]}, @@ -184,6 +193,21 @@ 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), @@ -287,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. @@ -491,6 +523,80 @@ 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), diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 4639904061..5af60adfae 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -279,7 +279,9 @@ no_common_alg_server_disconnects(Config) -> {send, ssh_msg_kexinit}, % with server unsupported 'ssh-dss' ! {match, {'or',[#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, _='_'}, - tcp_closed]}, + tcp_closed, + {tcp_error,econnaborted} + ]}, receive_msg} ] ). @@ -475,7 +477,8 @@ bad_packet_length(Config, LengthExcess) -> %% Prohibit remote decoder starvation: {send, #ssh_msg_service_request{name="ssh-userauth"}}, {match, {'or',[#ssh_msg_disconnect{_='_'}, - tcp_closed + tcp_closed, + {tcp_error,econnaborted} ]}, receive_msg} ], InitialState). @@ -507,7 +510,8 @@ bad_service_name_length(Config, LengthExcess) -> %% Prohibit remote decoder starvation: {send, #ssh_msg_service_request{name="ssh-userauth"}}, {match, {'or',[#ssh_msg_disconnect{_='_'}, - tcp_closed + tcp_closed, + {tcp_error,econnaborted} ]}, receive_msg} ], InitialState). 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_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 424afc76fe..ed76f4f795 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -646,3 +646,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..02cc79e4d5 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 + true -> + {skip,lists:concat(["openssh client does not support ",Name])}; + false -> + {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]), |