diff options
Diffstat (limited to 'lib/ssh/src/ssh_transport.erl')
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 101 |
1 files changed, 74 insertions, 27 deletions
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 412f5de9de..e92c727559 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -34,6 +34,8 @@ -export([next_seqnum/1, supported_algorithms/0, supported_algorithms/1, default_algorithms/0, default_algorithms/1, + algo_classes/0, algo_class/1, + algo_two_spec_classes/0, algo_two_spec_class/1, handle_packet_part/4, handle_hello_version/1, key_exchange_init_msg/1, @@ -81,6 +83,23 @@ default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()]. algo_classes() -> [kex, public_key, cipher, mac, compression]. +algo_class(kex) -> true; +algo_class(public_key) -> true; +algo_class(cipher) -> true; +algo_class(mac) -> true; +algo_class(compression) -> true; +algo_class(_) -> false. + + +algo_two_spec_classes() -> [cipher, mac, compression]. + +algo_two_spec_class(cipher) -> true; +algo_two_spec_class(mac) -> true; +algo_two_spec_class(compression) -> true; +algo_two_spec_class(_) -> false. + + + default_algorithms(kex) -> supported_algorithms(kex, [ 'diffie-hellman-group1-sha1' % Gone in OpenSSH 7.3.p1 @@ -232,9 +251,9 @@ key_exchange_init_msg(Ssh0) -> {SshPacket, Ssh} = ssh_packet(Msg, Ssh0), {Msg, SshPacket, Ssh}. -kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> +kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs} = Ssh) -> Random = ssh_bits:random(16), - PrefAlgs = ?GET_OPT(preferred_algorithms, Opts), + PrefAlgs = adjust_algs_for_peer_version(Role, ?GET_OPT(preferred_algorithms, Opts), Ssh), kexinit_message(Role, Random, PrefAlgs, HostKeyAlgs, Opts). key_init(client, Ssh, Value) -> @@ -242,7 +261,22 @@ key_init(client, Ssh, Value) -> key_init(server, Ssh, Value) -> Ssh#ssh{s_keyinit = Value}. - +adjust_algs_for_peer_version(client, PrefAlgs, #ssh{s_version=V}) -> + adjust_algs_for_peer_version(V, PrefAlgs); +adjust_algs_for_peer_version(server, PrefAlgs, #ssh{c_version=V}) -> + adjust_algs_for_peer_version(V, PrefAlgs). +%% +adjust_algs_for_peer_version("SSH-2.0-OpenSSH_6.2"++_, PrefAlgs) -> + C0 = proplists:get_value(cipher, PrefAlgs, same([])), + C = [{D,L} || D <- [client2server, server2client], + L <- [[K || K <- proplists:get_value(D, C0, []), + K =/= '[email protected]', + K =/= '[email protected]']] + ], + lists:keyreplace(cipher, 1, PrefAlgs, {cipher,C}); +adjust_algs_for_peer_version(_, PrefAlgs) -> + PrefAlgs. + kexinit_message(Role, Random, Algs, HostKeyAlgs, Opts) -> #ssh_msg_kexinit{ cookie = Random, @@ -392,7 +426,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, K = compute_key(dh, E, Private, [P,G]), MyPrivHostKey = get_host_key(Ssh0, SignAlg), MyPubHostKey = extract_public_key(MyPrivHostKey), - H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {E,Public,K}), + H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {E,Public,K}), H_SIG = sign(H, sha(SignAlg), MyPrivHostKey), {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg}, @@ -417,13 +451,12 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey, f = F, h_sig = H_SIG}, #ssh{keyex_key = {{Private, Public}, {G, P}}, - algorithms = #alg{kex=Kex, - hkey=SignAlg}} = Ssh0) -> + algorithms = #alg{kex=Kex}} = Ssh0) -> %% client if 1=<F, F=<(P-1)-> K = compute_key(dh, F, Private, [P,G]), - H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Public,F,K}), + H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Public,F,K}), case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -556,7 +589,7 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E}, 1<K, K<(P-1) -> MyPrivHostKey = get_host_key(Ssh0, SignAlg), MyPubHostKey = extract_public_key(MyPrivHostKey), - H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}), + H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}), H_SIG = sign(H, sha(SignAlg), MyPrivHostKey), {SshPacket, Ssh} = ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg}, @@ -586,8 +619,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK h_sig = H_SIG}, #ssh{keyex_key = {{Private, Public}, {G, P}}, keyex_info = {Min, Max, NBits}, - algorithms = #alg{kex=Kex, - hkey=SignAlg}} = + algorithms = #alg{kex=Kex}} = Ssh0) -> %% client if @@ -595,7 +627,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK K = compute_key(dh, F, Private, [P,G]), if 1<K, K<(P-1) -> - H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}), + H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}), case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -642,7 +674,7 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic}, K -> MyPrivHostKey = get_host_key(Ssh0, SignAlg), MyPubHostKey = extract_public_key(MyPrivHostKey), - H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Curve), {PeerPublic, MyPublic, K}), + H = kex_hash(Ssh0, MyPubHostKey, sha(Curve), {PeerPublic, MyPublic, K}), H_SIG = sign(H, sha(SignAlg), MyPrivHostKey), {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg}, @@ -665,15 +697,15 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic}, handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey, q_s = PeerPublic, h_sig = H_SIG}, - #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve}, - algorithms = #alg{hkey=SignAlg}} = Ssh0 + #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve} + } = Ssh0 ) -> %% at client try compute_key(ecdh, PeerPublic, MyPrivate, Curve) of K -> - H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Curve), {MyPublic,PeerPublic,K}), + H = kex_hash(Ssh0, PeerPubHostKey, sha(Curve), {MyPublic,PeerPublic,K}), case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -790,6 +822,7 @@ verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) end. +%%% -> boolean() | {error,_} accepted_host(Ssh, PeerName, Public, Opts) -> case ?GET_OPT(silently_accept_hosts, Opts) of @@ -811,11 +844,16 @@ accepted_host(Ssh, PeerName, Public, Opts) -> %% Call-back alternatives: A user provided fun is called for the decision: F when is_function(F,2) -> - true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public))); + case catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public)) of + true -> true; + _ -> {error, fingerprint_check_failed} + end; {DigestAlg,F} when is_function(F,2) -> - true == (catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public))) - + case catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)) of + true -> true; + _ -> {error, {fingerprint_check_failed,DigestAlg}} + end end. @@ -833,18 +871,27 @@ fmt_hostkey(X) -> X. known_host_key(#ssh{opts = Opts, key_cb = {KeyCb,KeyCbOpts}, peer = {PeerName,_}} = Ssh, Public, Alg) -> UserOpts = ?GET_OPT(user_options, Opts), - case KeyCb:is_host_key(Public, PeerName, Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of - true -> + case is_host_key(KeyCb, Public, PeerName, Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of + {_,true} -> ok; - false -> + {_,false} -> case accepted_host(Ssh, PeerName, Public, Opts) of true -> - KeyCb:add_host_key(PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]); + {_,R} = add_host_key(KeyCb, PeerName, Public, [{key_cb_private,KeyCbOpts}|UserOpts]), + R; false -> - {error, rejected} + {error, rejected_by_user}; + {error,E} -> + {error,E} end end. +is_host_key(KeyCb, Public, PeerName, Alg, Data) -> + {KeyCb, KeyCb:is_host_key(Public, PeerName, Alg, Data)}. + +add_host_key(KeyCb, PeerName, Public, Data) -> + {KeyCb, KeyCb:add_host_key(PeerName, Public, Data)}. + %% Each of the algorithm strings MUST be a comma-separated list of %% algorithm names (see ''Algorithm Naming'' in [SSH-ARCH]). Each @@ -1745,11 +1792,11 @@ hash(K, H, Ki, N, HashAlg) -> hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HashAlg). %%%---------------------------------------------------------------- -kex_hash(SSH, Key, SignAlg, HashAlg, Args) -> - crypto:hash(HashAlg, kex_plaintext(SSH,Key,SignAlg,Args)). +kex_hash(SSH, Key, HashAlg, Args) -> + crypto:hash(HashAlg, kex_plaintext(SSH,Key,Args)). -kex_plaintext(SSH, Key, SignAlg, Args) -> - EncodedKey = public_key:ssh_encode({Key,SignAlg}, ssh2_pubkey), +kex_plaintext(SSH, Key, Args) -> + EncodedKey = public_key:ssh_encode(Key, ssh2_pubkey), <<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version), ?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit), ?Ebinary(EncodedKey), |