diff options
author | Hans Nilsson <hans@erlang.org> | 2021-03-08 12:56:13 +0100 |
---|---|---|
committer | Hans Nilsson <hans@erlang.org> | 2021-03-19 09:48:22 +0100 |
commit | 59285df73841273adb111996cdb590ae1b86742b (patch) | |
tree | 327170541272c38b8c817e4f82dab45888bca496 | |
parent | df795ee384763c96d0f09b84f65d7139bf7f8567 (diff) | |
download | erlang-59285df73841273adb111996cdb590ae1b86742b.tar.gz |
ssh: Disable public_key ssh-rsa per default
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 47 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 111 |
2 files changed, 100 insertions, 58 deletions
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 5bc2859b4e..fd0ca11e6e 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -172,26 +172,28 @@ publickey_msg([SigAlg, #ssh{user = User, SigAlgStr = atom_to_list(SigAlg), SigData = build_sig_data(SessionId, User, Service, PubKeyBlob, SigAlgStr), - Sig = case Key of - {ssh2_pubkey, PubKeyBlob} -> - ssh_transport:call_KeyCb(sign, [PubKeyBlob, SigData], Opts); - - {PrivKey, PubKeyBlob} -> - Hash = ssh_transport:sha(SigAlg), - ssh_transport:sign(SigData, Hash, PrivKey) - end, - - SigBlob = list_to_binary([?string(SigAlgStr), - ?binary(Sig)]), - - {#ssh_msg_userauth_request{user = User, - service = Service, - method = "publickey", - data = [?TRUE, - ?string(SigAlgStr), - ?binary(PubKeyBlob), - ?binary(SigBlob)]}, - Ssh}; + SigRes = case Key of + {ssh2_pubkey, PubKeyBlob} -> + {ok, ssh_transport:call_KeyCb(sign, [PubKeyBlob, SigData], Opts)}; + {PrivKey, PubKeyBlob} -> + ssh_transport:sign(SigData, SigAlg, PrivKey, Ssh) + end, + case SigRes of + {ok,Sig} -> + SigBlob = list_to_binary([?string(SigAlgStr), + ?binary(Sig)]), + + {#ssh_msg_userauth_request{user = User, + service = Service, + method = "publickey", + data = [?TRUE, + ?string(SigAlgStr), + ?binary(PubKeyBlob), + ?binary(SigBlob)]}, + Ssh}; + {error,_} -> + {not_ok, Ssh} + end; _ -> {not_ok, Ssh} @@ -545,13 +547,16 @@ pre_verify_sig(User, KeyBlob, #ssh{opts=Opts}) -> verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, #ssh{opts=Opts} = Ssh) -> try Alg = binary_to_list(AlgBin), + true = lists:member(list_to_existing_atom(Alg), + proplists:get_value(public_key, + ?GET_OPT(preferred_algorithms,Opts))), Key = ssh_message:ssh2_pubkey_decode(KeyBlob), % or exception true = ssh_transport:call_KeyCb(is_auth_key, [Key, User], Opts), PlainText = build_sig_data(SessionId, User, Service, KeyBlob, Alg), <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, <<?UINT32(AlgLen), _Alg:AlgLen/binary, ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, - ssh_transport:verify(PlainText, ssh_transport:sha(Alg), Sig, Key, Ssh) + ssh_transport:verify(PlainText, list_to_existing_atom(Alg), Sig, Key, Ssh) catch _:_ -> false diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 813d5ad350..65d695ecb1 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -52,7 +52,9 @@ extract_public_key/1, ssh_packet/2, pack/2, valid_key_sha_alg/3, - sha/1, sign/3, verify/5, + sign/3, sign/4, + verify/5, + sha/1, get_host_key/2, call_KeyCb/3, public_algo/1]). @@ -166,6 +168,7 @@ default_algorithms1(mac) -> default_algorithms1(public_key) -> supported_algorithms(public_key, [ + 'ssh-rsa', %% Gone in OpenSSH 7.3.p1: 'ssh-dss' ]); @@ -488,17 +491,22 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, MyPrivHostKey = get_host_key(SignAlg, Opts), MyPubHostKey = extract_public_key(MyPrivHostKey), 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}, - f = Public, - h_sig = H_SIG - }, Ssh0), - {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}, - shared_secret = ssh_bits:mpint(K), - exchanged_hash = H, - session_id = sid(Ssh1, H)}}; - + case sign(H, SignAlg, MyPrivHostKey, Ssh0) of + {ok,H_SIG} -> + {SshPacket, Ssh1} = + ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg}, + f = Public, + h_sig = H_SIG + }, Ssh0), + {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}, + shared_secret = ssh_bits:mpint(K), + exchanged_hash = H, + session_id = sid(Ssh1, H)}}; + {error,unsupported_sign_alg} -> + ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + io_lib:format("Unsupported algorithm ~p", [SignAlg]) + ) + end; true -> ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, io_lib:format("Kexdh init failed, received 'e' out of bounds~n E=~p~n P=~p", @@ -635,15 +643,21 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E}, MyPrivHostKey = get_host_key(SignAlg, Opts), MyPubHostKey = extract_public_key(MyPrivHostKey), 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}, - f = Public, - h_sig = H_SIG}, Ssh0), - {ok, SshPacket, Ssh#ssh{shared_secret = ssh_bits:mpint(K), - exchanged_hash = H, - session_id = sid(Ssh, H) - }}; + case sign(H, SignAlg, MyPrivHostKey, Ssh0) of + {ok,H_SIG} -> + {SshPacket, Ssh} = + ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg}, + f = Public, + h_sig = H_SIG}, Ssh0), + {ok, SshPacket, Ssh#ssh{shared_secret = ssh_bits:mpint(K), + exchanged_hash = H, + session_id = sid(Ssh, H) + }}; + {error,unsupported_sign_alg} -> + ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + io_lib:format("Unsupported algorithm ~p", [SignAlg]) + ) + end; true -> ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "Kexdh init failed, received 'k' out of bounds" @@ -712,16 +726,22 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic}, MyPrivHostKey = get_host_key(SignAlg, Opts), MyPubHostKey = extract_public_key(MyPrivHostKey), 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}, - q_s = MyPublic, - h_sig = H_SIG}, - Ssh0), - {ok, SshPacket, Ssh1#ssh{keyex_key = {{MyPublic,MyPrivate},Curve}, - shared_secret = ssh_bits:mpint(K), - exchanged_hash = H, - session_id = sid(Ssh1, H)}} + case sign(H, SignAlg, MyPrivHostKey, Ssh0) of + {ok,H_SIG} -> + {SshPacket, Ssh1} = + ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg}, + q_s = MyPublic, + h_sig = H_SIG}, + Ssh0), + {ok, SshPacket, Ssh1#ssh{keyex_key = {{MyPublic,MyPrivate},Curve}, + shared_secret = ssh_bits:mpint(K), + exchanged_hash = H, + session_id = sid(Ssh1, H)}}; + {error,unsupported_sign_alg} -> + ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + io_lib:format("Unsupported algorithm ~p", [SignAlg]) + ) + end catch Class:Error -> ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, @@ -864,7 +884,7 @@ extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) -> verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) -> case atom_to_list(Alg#alg.hkey) of AlgStr -> - case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey, SSH) of + case verify(Digest, Alg#alg.hkey, Signature, PublicKey, SSH) of false -> {error, bad_signature}; true -> @@ -1402,6 +1422,19 @@ payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) -> Payload. %%%---------------------------------------------------------------- +%% sign(SigData, SignAlg, Key, Opts) when is_list(SignAlg) -> +%% sign(SigData, list_to_existing_atom(SignAlg), Key, Opts); + +sign(SigData, SignAlg, Key, #ssh{opts=Opts}) when is_atom(SignAlg) -> + case lists:member(SignAlg, + proplists:get_value(public_key, + ?GET_OPT(preferred_algorithms,Opts,[]))) of + true -> + {ok, sign(SigData, sha(SignAlg), Key)}; + false -> + {error, unsupported_sign_alg} + end. + sign(SigData, HashAlg, #{algorithm:=dss} = Key) -> mk_dss_sig(crypto:sign(dss, HashAlg, SigData, Key)); sign(SigData, HashAlg, #{algorithm:=SigAlg} = Key) -> @@ -1421,7 +1454,11 @@ mk_dss_sig(DerSignature) -> <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>. %%%---------------------------------------------------------------- -verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key, _) -> +verify(PlainText, Alg, Sig, Key, Ssh) -> + do_verify(PlainText, sha(Alg), Sig, Key, Ssh). + + +do_verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key, _) -> case Sig of <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> -> Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}), @@ -1429,7 +1466,7 @@ verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key, _) -> _ -> false end; -verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) -> +do_verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) -> case Sig of <<?UINT32(Rlen),R:Rlen/big-signed-integer-unit:8, ?UINT32(Slen),S:Slen/big-signed-integer-unit:8>> -> @@ -1440,14 +1477,14 @@ verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) -> false end; -verify(PlainText, HashAlg, Sig, #'RSAPublicKey'{}=Key, #ssh{role = server, - c_version = "SSH-2.0-OpenSSH_7."++_}) +do_verify(PlainText, HashAlg, Sig, #'RSAPublicKey'{}=Key, #ssh{role = server, + c_version = "SSH-2.0-OpenSSH_7."++_}) when HashAlg == sha256; HashAlg == sha512 -> %% Public key signing bug in in OpenSSH >= 7.2 public_key:verify(PlainText, HashAlg, Sig, Key) orelse public_key:verify(PlainText, sha, Sig, Key); -verify(PlainText, HashAlg, Sig, Key, _) -> +do_verify(PlainText, HashAlg, Sig, Key, _) -> public_key:verify(PlainText, HashAlg, Sig, Key). |