summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Nilsson <hans@erlang.org>2021-03-08 12:56:13 +0100
committerHans Nilsson <hans@erlang.org>2021-03-19 09:48:22 +0100
commit59285df73841273adb111996cdb590ae1b86742b (patch)
tree327170541272c38b8c817e4f82dab45888bca496
parentdf795ee384763c96d0f09b84f65d7139bf7f8567 (diff)
downloaderlang-59285df73841273adb111996cdb590ae1b86742b.tar.gz
ssh: Disable public_key ssh-rsa per default
-rw-r--r--lib/ssh/src/ssh_auth.erl47
-rw-r--r--lib/ssh/src/ssh_transport.erl111
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).