diff options
Diffstat (limited to 'lib/ssh')
22 files changed, 497 insertions, 134 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index bddae00dd2..f93753f1d2 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,176 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.5</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The internal handling of SSH options is re-written.</p> + <p> + Previously there were no checks if a client option was + given to a daemon or vice versa. This is corrected now. + If your code has e.g. a client-only option in a call to + start a daemon, the call will fail.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-12872</p> + </item> + <item> + <p> + Modernization of key exchange algorithms. See + draft-ietf-curdle-ssh-kex-sha2 for a discussion.</p> + <p> + Removed an outdated weak algorithm and added stronger + replacements to keep interoperability with other modern + ssh clients and servers. The default ordering of the + algorithms is also adjusted.</p> + <p> + Retired: The nowadays unsecure key-exchange + <c>diffie-hellman-group1-sha1</c> is not enabled by + default, but can be enabled with the option + <c>preferred-algorithms</c>.</p> + <p> + Added: The new stronger key-exchange + <c>diffie-hellman-group16-sha512</c>, + <c>diffie-hellman-group18-sha512</c> and + <c>diffie-hellman-group14-sha256</c> are added and + enabled by default.</p> + <p> + The questionable [RFC 6194] sha1-based algorithms + <c>diffie-hellman-group-exchange-sha1</c> and + <c>diffie-hellman-group14-sha1</c> are however still kept + enabled by default for compatibility with ancient clients + and servers that lack modern key-exchange alternatives. + When the draft-ietf-curdle-ssh-kex-sha2 becomes an rfc, + those sha1-based algorithms and + <c>diffie-hellman-group1-sha1</c> will be deprecated by + IETF. They might then be removed from the default list in + Erlang/OTP.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-14110</p> + </item> + <item> + <p> + Modernized internal representation of sftp by use of + maps.</p> + <p> + Own Id: OTP-14117</p> + </item> + <item> + <p> + The Extension Negotiation Mechanism and the extension + <c>server-sig-algs</c> in + draft-ietf-curdle-ssh-ext-info-05 are implemented.</p> + <p> + The related draft-ietf-curdle-rsa-sha2-05 is implemented + and introduces the signature algorithms + <c>rsa-sha2-256</c> and <c>rsa-sha2-512</c>.</p> + <p> + Own Id: OTP-14193</p> + </item> + <item> + <p> + The 'timeout' and 'connect_timeout' handling in + ssh_sftp:start_channel documentation is clarified.</p> + <p> + Own Id: OTP-14216</p> + </item> + <item> + <p> + The functions <c>ssh:connect</c>, <c>ssh:shell</c> and + <c>ssh:start_channel</c> now accept an IP-tuple as Host + destination argument.</p> + <p> + Own Id: OTP-14243</p> + </item> + <item> + <p> + The function <c>ssh:daemon_info/1</c> now returns Host + and Profile as well as the Port info in the property + list.</p> + <p> + Own Id: OTP-14259</p> + </item> + <item> + <p> + Removed the option <c>public_key_alg</c> which was + deprecated in 18.2. Use <c>pref_public_key_algs</c> + instead.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-14263</p> + </item> + <item> + <p> + The SSH application is refactored regarding daemon + starting. The resolution of contradicting <c>Host</c> + argument and <c>ip</c> option were not described. There + were also strange corner cases when the <c>'any'</c> + value was used in <c>Host</c> argument or <c>ip</c> + option. This is (hopefully) resolved now, but it may + cause incompatibilities for code using both <c>Host</c> + and the <c>ip</c> option. The value 'loopback' has been + added for a correct way of naming those addresses.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-14264</p> + </item> + <item> + <p> + The supervisor code is refactored. The naming of + listening IP-Port-Profile triples are slightly changed to + improve consistency in strange corner cases as resolved + by OTP-14264</p> + <p> + Own Id: OTP-14267 Aux Id: OTP-14266 </p> + </item> + <item> + <p> + The <c>idle_time</c> option can now be used in daemons.</p> + <p> + Own Id: OTP-14312</p> + </item> + <item> + <p> + Added test cases for IETF-CURDLE Extension Negotiation + (ext-info)</p> + <p> + Own Id: OTP-14361</p> + </item> + <item> + <p> + Testcases for IETF-CURDLE extension + <c>server-sig-algs</c> including <c>rsa-sha2-*</c></p> + <p> + Own Id: OTP-14362 Aux Id: OTP-14361 </p> + </item> + <item> + <p> + The option <c>auth_methods</c> can now also be used in + clients to select which authentication options that are + used and in which order.</p> + <p> + Own Id: OTP-14399</p> + </item> + <item> + <p> + Checks that a ECDSA public key (<c>ecdsa-sha2-nistp*</c>) + stored in a file has the correct size.</p> + <p> + Own Id: OTP-14410</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.4.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 5c9ce3d5fb..ea7e975ef5 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -320,6 +320,29 @@ attempted.</p> </item> + <!--tag><c><![CDATA[{send_ext_info, boolean()}]]></c></tag> + <item> + <p>Send a list of extensions to the server if the server has asked for it. See + <url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info">Draft-ietf-curdle-ssh-ext-info (work in progress)</url> for details. + </p> + <p>Currently the client do not react on any extensions. + </p> + <p>Default value is <c>true</c>. + </p> + </item--> + + <tag><c><![CDATA[{recv_ext_info, boolean()}]]></c></tag> + <item> + <p>Tell the server that the client accepts extension negotiation. See + <url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info">Draft-ietf-curdle-ssh-ext-info (work in progress)</url> for details. + </p> + <p>Currently implemented extension is <c>server-sig-algs</c> which is the list of the server's preferred + user's public key algorithms. + </p> + <p>Default value is <c>true</c>. + </p> + </item> + <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag> <item> <p>Module implementing the behaviour <seealso @@ -685,6 +708,27 @@ </p> </item> + <tag><c><![CDATA[{send_ext_info, boolean()}]]></c></tag> + <item> + <p>Send a list of extensions to the client if the client has asked for it. See + <url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info">Draft-ietf-curdle-ssh-ext-info (work in progress)</url> for details. + </p> + <p>Currently implemented extension is sending <c>server-sig-algs</c> which is the list of the server's preferred + user's public key algorithms. + </p> + <p>Default value is <c>true</c>. + </p> + </item> + + <!--tag><c><![CDATA[{recv_ext_info, boolean()}]]></c></tag> + <item> + <p>Tell the client that the server accepts extension negotiation. See + <url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info">Draft-ietf-curdle-ssh-ext-info (work in progress)</url> for details. + </p> + <p>Default value is <c>true</c>. + </p> + </item--> + <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag> <item> <p>Module implementing the behaviour <seealso diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml index 74c4111338..33ec7aaee0 100644 --- a/lib/ssh/doc/src/ssh_app.xml +++ b/lib/ssh/doc/src/ssh_app.xml @@ -161,6 +161,8 @@ <item>ecdsa-sha2-nistp521</item> <item>ssh-rsa</item> <item>ssh-dss</item> + <item>rsa-sha2-256</item> + <item>rsa-sha2-512</item> </list> </item> @@ -176,21 +178,23 @@ <tag>Encryption algorithms (ciphers)</tag> <item> <list type="bulleted"> - <item>[email protected] (AEAD_AES_128_GCM)</item> - <item>[email protected] (AEAD_AES_256_GCM)</item> + <item>[email protected]</item> + <item>[email protected]</item> <item>aes128-ctr</item> <item>aes192-ctr</item> <item>aes256-ctr</item> <item>aes128-cbc</item> <item>3des-cbc</item> + <item>(AEAD_AES_128_GCM, not enabled per default)</item> + <item>(AEAD_AES_256_GCM, not enabled per default)</item> </list> + <p>See the text at the description of <seealso marker="#rfc5647_note">the rfc 5647 further down</seealso> + for more information regarding AEAD_AES_*_GCM. + </p> <p>Following the internet de-facto standard, the cipher and mac algorithm AEAD_AES_128_GCM is selected when the cipher [email protected] is negotiated. The cipher and mac algorithm AEAD_AES_256_GCM is selected when the cipher [email protected] is negotiated. </p> - <p>See the text at the description of <seealso marker="#rfc5647_note">the rfc 5647 further down</seealso> - for more information. - </p> </item> <tag>Compression algorithms</tag> @@ -235,7 +239,11 @@ </item> <item><url href="https://tools.ietf.org/html/rfc4253">RFC 4253</url>, The Secure Shell (SSH) Transport Layer Protocol. - <p></p> + <p>Except</p> + <list type="bulleted"> + <item>8.1. diffie-hellman-group1-sha1. Disabled by default, can be enabled with the <c>preferred_algorithms</c> option.</item> + </list> + <p/> </item> <item><url href="https://tools.ietf.org/html/rfc4254">RFC 4254</url>, The Secure Shell (SSH) Connection Protocol. @@ -310,7 +318,29 @@ </p> </item> - <item>Work in progress: <url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-kex-sha2">https://tools.ietf.org/html/draft-ietf-curdle-ssh-kex-sha2-05</url>, Key Exchange (KEX) Method Updates and Recommendations for Secure Shell (SSH)</item> + <item><url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-kex-sha2">Draft-ietf-curdle-ssh-kex-sha2 (work in progress)</url>, Key Exchange (KEX) Method Updates and Recommendations for Secure Shell (SSH). + <p>Deviations:</p> + <list type="bulleted"> + <item>The <c>diffie-hellman-group1-sha1</c> is not enabled by default, but is still supported and can be enabled + with the option <c>preferred-algorithms</c></item> + <item>The questionable sha1-based algorithms <c>diffie-hellman-group-exchange-sha1</c> and + <c>diffie-hellman-group14-sha1</c> are still enabled by default for compatibility with ancient clients and servers. + They can be disabled with the option <c>preferred-algorithms</c></item> + </list> + <p/> + </item> + + <item><url href="https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2">Draft-ietf-curdle-rsa-sha2 (work in progress)</url>, Use of RSA Keys with SHA-2 256 and 512 in Secure Shell (SSH). + </item> + + <item><url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-ext-info">Draft-ietf-curdle-ssh-ext-info (work in progress)</url>, Extension Negotiation in Secure Shell (SSH). + <p>Implemented are:</p> + <list type="bulleted"> + <item>The Extension Negotiation Mechanism</item> + <item>The extension <c>server-sig-algs</c></item> + </list> + <p/> + </item> </list> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index eb6f43d417..ed7fbf9cf3 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>2016</year> + <year>2005</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -558,8 +558,14 @@ <taglist> <tag><c><![CDATA[{timeout, timeout()}]]></c></tag> <item> - <p>The time-out is passed to the <c>ssh_channel</c> start function, - and defaults to <c>infinity</c>.</p> + <p>There are two ways to set a timeout for the underlying ssh connection:</p> + <list> + <item>If the connection timeout option <c>connect_timeout</c> is set, that value + is used also for the negotiation timeout and this option (<c>timeout</c>) is ignored.</item> + <item>Otherwise, this option (<c>timeout</c>) is used as the negotiation timeout + only and there is no connection timeout set</item> + </list> + <p>The value defaults to <c>infinity</c>.</p> </item> <tag> <c><![CDATA[{sftp_vsn, integer()}]]></c> diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 6cf659f830..ac64a7bf14 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -28,7 +28,8 @@ -include("ssh_auth.hrl"). -include("ssh_transport.hrl"). --export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1, +-export([get_public_key/2, + publickey_msg/1, password_msg/1, keyboard_interactive_msg/1, service_request_msg/1, init_userauth_request_msg/1, userauth_request_msg/1, handle_userauth_request/3, handle_userauth_info_request/2, handle_userauth_info_response/2 @@ -136,41 +137,49 @@ keyboard_interactive_msg([#ssh{user = User, Ssh) end. -publickey_msg([SigAlg, #ssh{user = User, - session_id = SessionId, - service = Service, - opts = Opts} = Ssh]) -> - Hash = ssh_transport:sha(SigAlg), + +get_public_key(SigAlg, #ssh{opts = Opts}) -> KeyAlg = key_alg(SigAlg), {KeyCb,KeyCbOpts} = ?GET_OPT(key_cb, Opts), UserOpts = ?GET_OPT(user_options, Opts), case KeyCb:user_key(KeyAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of - {ok, PrivKey} -> - SigAlgStr = atom_to_list(SigAlg), + {ok, PrivKey} -> try Key = ssh_transport:extract_public_key(PrivKey), public_key:ssh_encode(Key, ssh2_pubkey) of - PubKeyBlob -> - SigData = build_sig_data(SessionId, User, Service, - PubKeyBlob, SigAlgStr), - Sig = ssh_transport:sign(SigData, Hash, PrivKey), - SigBlob = list_to_binary([?string(SigAlgStr), - ?binary(Sig)]), - ssh_transport:ssh_packet( - #ssh_msg_userauth_request{user = User, - service = Service, - method = "publickey", - data = [?TRUE, - ?string(SigAlgStr), - ?binary(PubKeyBlob), - ?binary(SigBlob)]}, - Ssh) + PubKeyBlob -> {ok,{PrivKey,PubKeyBlob}} catch _:_ -> - {not_ok, Ssh} + not_ok end; - _Error -> + _Error -> + not_ok + end. + + +publickey_msg([SigAlg, #ssh{user = User, + session_id = SessionId, + service = Service} = Ssh]) -> + case get_public_key(SigAlg, Ssh) of + {ok, {PrivKey,PubKeyBlob}} -> + SigAlgStr = atom_to_list(SigAlg), + SigData = build_sig_data(SessionId, User, Service, + PubKeyBlob, SigAlgStr), + Hash = ssh_transport:sha(SigAlg), + Sig = ssh_transport:sign(SigData, Hash, PrivKey), + SigBlob = list_to_binary([?string(SigAlgStr), + ?binary(Sig)]), + ssh_transport:ssh_packet( + #ssh_msg_userauth_request{user = User, + service = Service, + method = "publickey", + data = [?TRUE, + ?string(SigAlgStr), + ?binary(PubKeyBlob), + ?binary(SigBlob)]}, + Ssh); + _ -> {not_ok, Ssh} end. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index f1ce337947..8d3ddb09a4 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -453,16 +453,20 @@ init_ssh_record(Role, _Socket, PeerAddr, Opts) -> PeerName0 when is_list(PeerName0) -> PeerName0 end, - S0#ssh{c_vsn = Vsn, - c_version = Version, - io_cb = case ?GET_OPT(user_interaction, Opts) of - true -> ssh_io; - false -> ssh_no_io - end, - userauth_pubkeys = ?GET_OPT(pref_public_key_algs, Opts), - userauth_quiet_mode = ?GET_OPT(quiet_mode, Opts), - peer = {PeerName, PeerAddr} - }; + S1 = + S0#ssh{c_vsn = Vsn, + c_version = Version, + io_cb = case ?GET_OPT(user_interaction, Opts) of + true -> ssh_io; + false -> ssh_no_io + end, + userauth_quiet_mode = ?GET_OPT(quiet_mode, Opts), + peer = {PeerName, PeerAddr} + }, + S1#ssh{userauth_pubkeys = [K || K <- ?GET_OPT(pref_public_key_algs, Opts), + is_usable_user_pubkey(K, S1) + ] + }; server -> S0#ssh{s_vsn = Vsn, @@ -1700,29 +1704,49 @@ handle_ssh_msg_ext_info(#ssh_msg_ext_info{data=Data}, D0) -> lists:foldl(fun ext_info/2, D0, Data). -ext_info({"server-sig-algs",SigAlgs}, D0 = #data{ssh_params=#ssh{role=client, - userauth_pubkeys=ClientSigAlgs}=Ssh0}) -> - %% Make strings to eliminate risk of beeing bombed with odd strings that fills the atom table: - SupportedAlgs = lists:map(fun erlang:atom_to_list/1, ssh_transport:supported_algorithms(public_key)), - ServerSigAlgs = [list_to_atom(SigAlg) || SigAlg <- string:tokens(SigAlgs,","), - %% length of SigAlg is implicitly checked by the comparison - %% in member/2: - lists:member(SigAlg, SupportedAlgs) - ], - CommonAlgs = [Alg || Alg <- ServerSigAlgs, - lists:member(Alg, ClientSigAlgs)], - SelectedAlgs = - case CommonAlgs of - [] -> ClientSigAlgs; % server-sig-algs value is just an advice - _ -> CommonAlgs - end, - D0#data{ssh_params = Ssh0#ssh{userauth_pubkeys = SelectedAlgs} }; +ext_info({"server-sig-algs",SigAlgsStr}, + D0 = #data{ssh_params=#ssh{role=client, + userauth_pubkeys=ClientSigAlgs}=Ssh0}) -> + %% ClientSigAlgs are the pub_key algortithms that: + %% 1) is usable, that is, the user has such a public key and + %% 2) is either the default list or set by the caller + %% with the client option 'pref_public_key_algs' + %% + %% The list is already checked for duplicates. + + SigAlgs = [A || Astr <- string:tokens(SigAlgsStr, ","), + A <- try [list_to_existing_atom(Astr)] + %% list_to_existing_atom will fail for unknown algorithms + catch _:_ -> [] + end], + + CommonAlgs = [A || A <- SigAlgs, + lists:member(A, ClientSigAlgs)], + + %% Re-arrange the client supported public-key algorithms so that the server + %% preferred ones are tried first. + %% Trying algorithms not mentioned by the server is ok, since the server can't know + %% if the client supports 'server-sig-algs' or not. + + D0#data{ + ssh_params = + Ssh0#ssh{ + userauth_pubkeys = + CommonAlgs ++ (ClientSigAlgs -- CommonAlgs) + }}; ext_info(_, D0) -> %% Not implemented D0. %%%---------------------------------------------------------------- +is_usable_user_pubkey(A, Ssh) -> + case ssh_auth:get_public_key(A, Ssh) of + {ok,_} -> true; + _ -> false + end. + +%%%---------------------------------------------------------------- handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) -> case ssh_channel:cache_lookup(cache(D), ChannelId) of #channel{remote_id = Id} = Channel -> diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl index 003b3856e6..3f742ad9b6 100644 --- a/lib/ssh/src/ssh_dbg.erl +++ b/lib/ssh/src/ssh_dbg.erl @@ -22,10 +22,8 @@ -module(ssh_dbg). --export([messages/0, messages/1, messages/2, - ct_messages/0, - auth/0, auth/1, auth/2, - ct_auth/0, +-export([messages/0, messages/1, messages/2, messages/3, + auth/0, auth/1, auth/2, auth/3, stop/0 ]). @@ -37,50 +35,52 @@ -include("ssh_connect.hrl"). -include("ssh_auth.hrl"). --record(data, { - writer, - acc = []}). %%%================================================================ -messages() -> - messages(fun(String,_D) -> io:format(String) end). +messages() -> start(msg). +messages(F) -> start(msg,F). +messages(F,X) -> start(msg,F,X). +messages(F,M,I) -> start(msg,F,M,I). -ct_messages() -> - messages(fun(String,_D) -> ct:log(String,[]) end). +auth() -> start(auth). +auth(F) -> start(auth,F). +auth(F,X) -> start(auth,F,X). +auth(F,M,I) -> start(auth,F,M,I). -messages(Write) when is_function(Write,2) -> - messages(Write, fun(X) -> X end). - -messages(Write, MangleArg) when is_function(Write,2), - is_function(MangleArg,1) -> - cond_start(msg, Write, MangleArg), - dbg_ssh_messages(), - dbg_ssh_auth(). +stop() -> dbg:stop(). +%%%---------------------------------------------------------------- +start(Type) -> start(Type, fun io:format/2). -auth() -> - auth(fun(String,_D) -> io:format(String) end). +start(Type, F) when is_function(F,2) -> start(Type, fmt_fun(F)); +start(Type, F) when is_function(F,3) -> start(Type, F, id_fun()). -ct_auth() -> - auth(fun(String,_D) -> ct:log(String,[]) end). +start(Type, WriteFun, MangleArgFun) when is_function(WriteFun, 3), + is_function(MangleArgFun, 1) -> + start(Type, WriteFun, MangleArgFun, []); +start(Type, WriteFun, InitValue) -> + start(Type, WriteFun, id_fun(), InitValue). -auth(Write) when is_function(Write,2) -> - auth(Write, fun(X) -> X end). +start(Type, WriteFun, MangleArgFun, InitValue) when is_function(WriteFun, 3), + is_function(MangleArgFun, 1) -> + cond_start(Type, WriteFun, MangleArgFun, InitValue), + dbg_ssh(Type). -auth(Write, MangleArg) when is_function(Write,2), - is_function(MangleArg,1) -> - cond_start(auth, Write, MangleArg), - dbg_ssh_auth(). +%%%---------------------------------------------------------------- +fmt_fun(F) -> fun(Fmt,Args,Data) -> F(Fmt,Args), Data end. +id_fun() -> fun(X) -> X end. -dbg_ssh_messages() -> +%%%---------------------------------------------------------------- +dbg_ssh(msg) -> + dbg_ssh(auth), dbg:tp(ssh_message,encode,1, x), dbg:tp(ssh_message,decode,1, x), dbg:tpl(ssh_transport,select_algorithm,4, x), dbg:tp(ssh_transport,hello_version_msg,1, x), dbg:tp(ssh_transport,handle_hello_version,1, x), - dbg:tpl(ssh_connection_handler,ext_info,2, x). + dbg:tpl(ssh_connection_handler,ext_info,2, x); -dbg_ssh_auth() -> +dbg_ssh(auth) -> dbg:tp(ssh_transport,hello_version_msg,1, x), dbg:tp(ssh_transport,handle_hello_version,1, x), dbg:tp(ssh_message,encode,1, x), @@ -89,15 +89,11 @@ dbg_ssh_auth() -> lists:foreach(fun(F) -> dbg:tp(ssh_auth, F, x) end, [publickey_msg, password_msg, keyboard_interactive_msg]). -%%%---------------------------------------------------------------- -stop() -> - dbg:stop(). - %%%================================================================ -cond_start(Type, Write, MangleArg) -> +cond_start(Type, WriteFun, MangleArgFun, Init) -> try dbg:start(), - setup_tracer(Type, Write, MangleArg), + setup_tracer(Type, WriteFun, MangleArgFun, Init), dbg:p(new,[c,timestamp]) catch _:_ -> ok @@ -136,12 +132,13 @@ msg_formater(_, {trace_ts,Pid,call,{ssh_transport,handle_hello_version,[Hello]}, msg_formater(_, {trace_ts,_Pid,return_from,{ssh_transport,handle_hello_version,1},_,_TS}, D) -> D; -msg_formater(_, {trace_ts,Pid,call,{ssh_connection_handler,ext_info,[{"server-sig-algs",_SigAlgs},State]},TS}, D) -> +msg_formater(_, {trace_ts,Pid,call,{ssh_connection_handler,ext_info,[{"server-sig-algs",SigAlgs},State]},TS}, D) -> try lists:keyfind(ssh, 1, tuple_to_list(State)) of false -> D; #ssh{userauth_pubkeys = PKs} -> - fmt("~n~s ~p Client got suggestion to use user public key sig-algs~n ~p~n", [ts(TS),Pid,PKs], D) + fmt("~n~s ~p Client got suggestion to use user public key sig-algs~n ~p~n and can use~n ~p~n", + [ts(TS),Pid,string:tokens(SigAlgs,","),PKs], D) catch _:_ -> D @@ -206,21 +203,25 @@ msg_formater(msg, {trace_ts,Pid,'receive',ErlangMsg,TS}, D) -> msg_formater(_, _, D) -> D. +%%%---------------------------------------------------------------- +-record(data, {writer, + acc}). -fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) -> - D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}. +fmt(Fmt, Args, D=#data{writer=Write, acc=Acc}) -> + D#data{acc = Write(Fmt,Args,Acc)}. ts({_,_,Usec}=Now) -> {_Date,{HH,MM,SS}} = calendar:now_to_local_time(Now), io_lib:format("~.2.0w:~.2.0w:~.2.0w.~.6.0w",[HH,MM,SS,Usec]); ts(_) -> "-". -%%%---------------------------------------------------------------- -setup_tracer(Type, Write, MangleArg) -> + +setup_tracer(Type, WriteFun, MangleArgFun, Init) -> Handler = fun(Arg, D) -> - msg_formater(Type, MangleArg(Arg), D) + msg_formater(Type, MangleArgFun(Arg), D) end, - InitialData = #data{writer = Write}, + InitialData = #data{writer = WriteFun, + acc = Init}, {ok,_} = dbg:tracer(process, {Handler, InitialData}), ok. diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl index 8ba759ad60..a7cd1daeec 100644 --- a/lib/ssh/src/ssh_io.erl +++ b/lib/ssh/src/ssh_io.erl @@ -31,8 +31,8 @@ read_line(Prompt, Opts) -> format("~s", [listify(Prompt)]), ?GET_INTERNAL_OPT(user_pid, Opts) ! {self(), question}, receive - Answer when is_list(Answer) -> - Answer + Answer when is_list(Answer) or is_binary(Answer) -> + unicode:characters_to_list(Answer) end. yes_no(Prompt, Opts) -> @@ -44,7 +44,7 @@ yes_no(Prompt, Opts) -> y -> yes; n -> no; - Answer when is_list(Answer) -> + Answer when is_list(Answer) or is_binary(Answer) -> case trim(Answer) of "y" -> yes; "n" -> no; @@ -60,7 +60,7 @@ read_password(Prompt, Opts) -> format("~s", [listify(Prompt)]), ?GET_INTERNAL_OPT(user_pid, Opts) ! {self(), user_password}, receive - Answer when is_list(Answer) -> + Answer when is_list(Answer) or is_binary(Answer) -> case trim(Answer) of "" -> read_password(Prompt, Opts); diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl index aebb5a7062..7eeed70739 100644 --- a/lib/ssh/src/ssh_options.erl +++ b/lib/ssh/src/ssh_options.erl @@ -674,7 +674,11 @@ check_pref_public_key_algs(V) -> PKs = ssh_transport:supported_algorithms(public_key), CHK = fun(A, Ack) -> case lists:member(A, PKs) of - true -> [A|Ack]; + true -> + case lists:member(A,Ack) of + false -> [A|Ack]; + true -> Ack % Remove duplicates + end; false -> error_in_check(A, "Not supported public key") end end, diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index 0f69910e40..98964a2c8a 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -131,9 +131,14 @@ init_per_group(public_key=Tag, Alg, Config) -> ct:log("Init tests for public_key ~p",[Alg]), PrefAlgs = {preferred_algorithms,[{Tag,[Alg]}]}, %% Daemon started later in init_per_testcase - [{pref_algs,PrefAlgs}, - {tag_alg,{Tag,Alg}} - | Config]; + try + setup_pubkey(Alg, + [{pref_algs,PrefAlgs}, + {tag_alg,{Tag,Alg}} + | Config]) + catch + _:_ -> {skip, io_lib:format("Unsupported: ~p",[Alg])} + end; init_per_group(Tag, Alg, Config) -> PA = @@ -167,18 +172,24 @@ init_per_testcase(TC, Config) -> init_per_testcase(TC, proplists:get_value(tag_alg,Config), Config). -init_per_testcase(_, {public_key,Alg}, Config) -> - Opts = pubkey_opts(Config), +init_per_testcase(TC, {public_key,Alg}, Config) -> + ExtraOpts = case TC of + simple_connect -> + [{user_dir, proplists:get_value(priv_dir,Config)}]; + _ -> + [] + end, + Opts = pubkey_opts(Config) ++ ExtraOpts, case {ssh_file:user_key(Alg,Opts), ssh_file:host_key(Alg,Opts)} of {{ok,_}, {ok,_}} -> - ssh_dbg:ct_auth(), - start_pubkey_daemon([proplists:get_value(pref_algs,Config)], + start_pubkey_daemon([proplists:get_value(pref_algs,Config) + | ExtraOpts], [{extra_daemon,true}|Config]); - {{ok,_}, _} -> - {skip, "No host key"}; + {{ok,_}, {error,Err}} -> + {skip, io_lib:format("No host key: ~p",[Err])}; - {_, {ok,_}} -> - {skip, "No user key"}; + {{error,Err}, {ok,_}} -> + {skip, io_lib:format("No user key: ~p",[Err])}; _ -> {skip, "Neither host nor user key"} @@ -193,7 +204,6 @@ init_per_testcase(_, _, Config) -> end_per_testcase(_TC, Config) -> - catch ssh_dbg:stop(), case proplists:get_value(extra_daemon, Config, false) of true -> case proplists:get_value(srvr_pid,Config) of @@ -223,6 +233,19 @@ simple_exec(Config) -> ssh_test_lib:std_simple_exec(Host, Port, Config). %%-------------------------------------------------------------------- +%% A simple exec call +simple_connect(Config) -> + {Host,Port} = proplists:get_value(srvr_addr, Config), + Opts = + case proplists:get_value(tag_alg, Config) of + {public_key,Alg} -> [{pref_public_key_algs,[Alg]}]; + _ -> [] + end, + ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, Opts), + ct:log("~p:~p connected! ~p",[?MODULE,?LINE,ConnectionRef]), + ssh:close(ConnectionRef). + +%%-------------------------------------------------------------------- %% Testing if no group matches simple_exec_groups_no_match_too_small(Config) -> try_exec_simple_group({400,500,600}, Config). @@ -304,9 +327,15 @@ sshc_simple_exec_os_cmd(Config) -> %%-------------------------------------------------------------------- %% Connect to the ssh server of the OS sshd_simple_exec(Config) -> + ClientPubKeyOpts = + case proplists:get_value(tag_alg,Config) of + {public_key,Alg} -> [{pref_public_key_algs,[Alg]}]; + _ -> [] + end, ConnectionRef = ssh_test_lib:connect(22, [{silently_accept_hosts, true}, proplists:get_value(pref_algs,Config), - {user_interaction, false}]), + {user_interaction, false} + | ClientPubKeyOpts]), {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), success = ssh_connection:exec(ConnectionRef, ChannelId0, "echo testing", infinity), @@ -363,8 +392,8 @@ split(Alg) -> ssh_test_lib:to_atoms(string:tokens(atom_to_list(Alg), " + ")). specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos, TypeSSH) -> case Tag of - public_key -> []; - _ -> [simple_exec, simple_sftp] + public_key -> [simple_connect]; + _ -> [simple_connect, simple_exec, simple_sftp] end ++ case supports(Tag, Alg, SshcAlgos) of true when TypeSSH == openSSH -> @@ -439,10 +468,26 @@ setup_pubkey(Config) -> Keys = [ssh_test_lib:setup_dsa(DataDir, UserDir), ssh_test_lib:setup_rsa(DataDir, UserDir), - ssh_test_lib:setup_ecdsa("256", DataDir, UserDir)], + ssh_test_lib:setup_ecdsa("256", DataDir, UserDir) + ], ssh_test_lib:write_auth_keys(Keys, UserDir), % 'authorized_keys' shall contain ALL pub keys Config. +setup_pubkey(Alg, Config) -> + DataDir = proplists:get_value(data_dir, Config), + UserDir = proplists:get_value(priv_dir, Config), + ct:log("Setup keys for ~p",[Alg]), + case Alg of + 'ssh-dss' -> ssh_test_lib:setup_dsa(DataDir, UserDir); + 'ssh-rsa' -> ssh_test_lib:setup_rsa(DataDir, UserDir); + 'rsa-sha2-256' -> ssh_test_lib:setup_rsa(DataDir, UserDir); + 'rsa-sha2-512' -> ssh_test_lib:setup_rsa(DataDir, UserDir); + 'ecdsa-sha2-nistp256' -> ssh_test_lib:setup_ecdsa("256", DataDir, UserDir); + 'ecdsa-sha2-nistp384' -> ssh_test_lib:setup_ecdsa("384", DataDir, UserDir); + 'ecdsa-sha2-nistp521' -> ssh_test_lib:setup_ecdsa("521", DataDir, UserDir) + end, + Config. + simple_exec_group(I, Config) when is_integer(I) -> simple_exec_group({I,I,I}, Config); diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384 b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384 new file mode 100644 index 0000000000..4c39e916e9 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAughXu55DNyhxe6x+MNjv4oZKWUDh7bhi4CqjvxhCp9KMpsybltcq+ +lsuKTarzTdKgBwYFK4EEACKhZANiAASu1vvDL0SQoXGtzlltaPHPyDfEVMG/sKLA +pqv8vfRN5Wcs7+yaRKw92nYEKGXfZLbhVX8ArFPMtXPWHcRHCntvL1Acn2kJQ8Gc +7iL4NAr8JhTIUBv4YMhHDa9Pv/CH2zk= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384.pub new file mode 100644 index 0000000000..caa9604c84 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBK7W+8MvRJChca3OWW1o8c/IN8RUwb+wosCmq/y99E3lZyzv7JpErD3adgQoZd9ktuFVfwCsU8y1c9YdxEcKe28vUByfaQlDwZzuIvg0CvwmFMhQG/hgyEcNr0+/8IfbOQ== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521 b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521 new file mode 100644 index 0000000000..1e16fcbd57 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHbAgEBBEEWXGoVLiNwQVUwAGZWxOu6uxtU8ntxyZNlcWU4Z8pze9kq3eK7a9XH +l/wxL75Vk1QdOiR/rE3s/L/zOuChp44o1aAHBgUrgQQAI6GBiQOBhgAEAfCrtwjO +kQYKr4/F3uanS7Eby1+SYDdRl1ABuDFhNC3CivVBFt4CnRneV+Mf0viDAxD+HEpd +/GaE2CdsFoVpglN5AVG+fEePY2PiCLHmjc4/pBuR+tWhErzcWAd0KLBCBuc4OAvl +aLLYV1NAJI6COnnfGTCVvYYE5nKMG4LLX0zaWtWl +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521.pub new file mode 100644 index 0000000000..069683eba7 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/id_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHwq7cIzpEGCq+Pxd7mp0uxG8tfkmA3UZdQAbgxYTQtwor1QRbeAp0Z3lfjH9L4gwMQ/hxKXfxmhNgnbBaFaYJTeQFRvnxHj2Nj4gix5o3OP6QbkfrVoRK83FgHdCiwQgbnODgL5Wiy2FdTQCSOgjp53xkwlb2GBOZyjBuCy19M2lrVpQ== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384 new file mode 100644 index 0000000000..5835bcd74c --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDB+l0+SMLYgQ3ZRzg2Pn5u+1ZwKbEnJzXsTKTJM9QSJbKkbA7uCnjdS +CvEW+66CoHqgBwYFK4EEACKhZANiAAT6awCCIrcCr9H4wq0bJ/rQou3tpLHyyf33 +c8D6FPn48/hNqinpx7b0le/0D+Rrhdl9edIplAf6oki7yoFFGl4yuzWtv7rag9jB +vv6w1508ChOmyQ094rFt/xj4KVBhEHI= +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384.pub new file mode 100644 index 0000000000..714fc4eb89 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPprAIIitwKv0fjCrRsn+tCi7e2ksfLJ/fdzwPoU+fjz+E2qKenHtvSV7/QP5GuF2X150imUB/qiSLvKgUUaXjK7Na2/utqD2MG+/rDXnTwKE6bJDT3isW3/GPgpUGEQcg== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521 new file mode 100644 index 0000000000..81aa8df39f --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHbAgEBBEHHxgYEfDclsu5bW+pZfg+bkaqWpgEpXtuzLVm++FFPjhAPhMkurSRj +WQ+CuI2TxgYkBbYFNjn9JqgdMF7FzaiojKAHBgUrgQQAI6GBiQOBhgAEAFTM8TKG +xexxmfAGuyl/Tpk4wytB/OyuVfkF+Q3H1v17HLcpMacA5xUFr80+D5XnjxGttBsS ++X0uexR7QbPbhhPqADgQzFqvTsB1mUNAZnJBD6QNCZkfWwRRwFYQWSmisb43H6G3 +iUTKqiCXMXO8drKLA+Wi+L7VyfoI1CvatBBlDHbV +-----END EC PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521.pub new file mode 100644 index 0000000000..17b9a1d834 --- /dev/null +++ b/lib/ssh/test/ssh_algorithms_SUITE_data/ssh_host_ecdsa_key521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABUzPEyhsXscZnwBrspf06ZOMMrQfzsrlX5BfkNx9b9exy3KTGnAOcVBa/NPg+V548RrbQbEvl9LnsUe0Gz24YT6gA4EMxar07AdZlDQGZyQQ+kDQmZH1sEUcBWEFkporG+Nx+ht4lEyqoglzFzvHayiwPlovi+1cn6CNQr2rQQZQx21Q== uabhnil@elxadlj3q32 diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index b710ca8fb7..8b454ffe5d 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -555,14 +555,14 @@ connectfun_disconnectfun_server(Config) -> {disconnect,Ref,R} -> ct:log("Disconnect result: ~p",[R]), ssh:stop_daemon(Pid) - after 5000 -> + after 10000 -> receive X -> ct:log("received ~p",[X]) after 0 -> ok end, {fail, "No disconnectfun action"} end - after 5000 -> + after 10000 -> receive X -> ct:log("received ~p",[X]) after 0 -> ok diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl index 5ea60d8a8f..3318b86d39 100644 --- a/lib/ssh/test/ssh_property_test_SUITE.erl +++ b/lib/ssh/test/ssh_property_test_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2017. 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. diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 0385e30ad1..0837fe7eaf 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -466,7 +466,7 @@ bad_long_service_name(Config) -> bad_very_long_service_name(Config) -> bad_service_name(Config, - lists:duplicate(4*?SSH_MAX_PACKET_SIZE, $a)). + lists:duplicate(?SSH_MAX_PACKET_SIZE+5, $a)). empty_service_name(Config) -> bad_service_name(Config, ""). diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 48332d2e5a..7208baca6e 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.4.2 +SSH_VSN = 4.5 APP_VSN = "ssh-$(SSH_VSN)" |