aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2017-02-10 14:37:41 +0100
committerHans Nilsson <[email protected]>2017-02-14 16:24:16 +0100
commit9f23065062eb724e58f39a65e416e5b0e1e9d95d (patch)
tree287688dd9783ac8295a2c71677cb2cbf8b3ccd8a
parentf7bae98de902c597d9f1e5861745e90aecc58191 (diff)
downloadotp-9f23065062eb724e58f39a65e416e5b0e1e9d95d.tar.gz
otp-9f23065062eb724e58f39a65e416e5b0e1e9d95d.tar.bz2
otp-9f23065062eb724e58f39a65e416e5b0e1e9d95d.zip
ssh: allow a list of fingerprint algos in silently_accept_hosts option
-rw-r--r--lib/ssh/doc/src/ssh.xml13
-rw-r--r--lib/ssh/src/ssh.erl21
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl26
3 files changed, 45 insertions, 15 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 6b49f89449..1a6bac8355 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -175,9 +175,11 @@
supplied with this option.
</p>
</item>
- <tag><c><![CDATA[{silently_accept_hosts, boolean() | accept_fun() | {crypto:digest_type(), accept_fun()} }]]></c>
+ <tag><c><![CDATA[{silently_accept_hosts, boolean() | accept_fun() | {HashAlgoSpec, accept_fun()} }]]></c>
<br/>
- <c><![CDATA[accept_fun() :: fun(PeerName::string(), FingerPrint::string()) -> boolean()]]></c>
+ <c><![CDATA[HashAlgoSpec :: crypto:digest_type() | [crypto:digest_type()] ]]></c>
+ <br/>
+ <c><![CDATA[accept_fun() :: fun(PeerName::string(), FingerPrint:: string()|[string()]) -> boolean()]]></c>
</tag>
<item>
<p>When <c>true</c>, hosts are added to the
@@ -188,8 +190,13 @@
<c>(PeerName, PeerHostKeyFingerPrint)</c>. The fingerprint is calculated on the Peer's Host Key with
<seealso marker="public_key:public_key#ssh_hostkey_fingerprint-1">public_key:ssh_hostkey_fingerprint/1</seealso>.
</p>
- <p>If the <c>crypto:digest_type()</c> is present, the fingerprint is calculated with that digest type by the function
+ <p>If the <c>HashAlgoSpec</c> is present and is an <c>crypto:digest_type()</c>, the fingerprint is calculated
+ with that digest type by the function
<seealso marker="public_key:public_key#ssh_hostkey_fingerprint-2">public_key:ssh_hostkey_fingerprint/2</seealso>.
+ </p>
+ <p>If the <c>HashAlgoSpec</c> is present and is a list of <c>crypto:digest_type()</c>, the fingerprint is calulated for
+ each digest_type and <c>PeerHostKeyFingerPrint</c> is the list of the results in order corresponding to the
+ <c>HashAlgoSpec</c>.
</p>
</item>
<tag><c><![CDATA[{user_interaction, boolean()}]]></c></tag>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 31e343e81b..f408086c0f 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -620,11 +620,22 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) -
handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_function(Value,2) ->
Opt;
handle_ssh_option({silently_accept_hosts, {DigestAlg,Value}} = Opt) when is_function(Value,2) ->
- case lists:member(DigestAlg, [md5, sha, sha224, sha256, sha384, sha512]) of
- true ->
- Opt;
- false ->
- throw({error, {eoptions, Opt}})
+ Algs = if is_atom(DigestAlg) -> [DigestAlg];
+ is_list(DigestAlg) -> DigestAlg;
+ true -> throw({error, {eoptions, Opt}})
+ end,
+ case [A || A <- Algs,
+ not lists:member(A, [md5, sha, sha224, sha256, sha384, sha512])] of
+ [_|_] = UnSup1 ->
+ throw({error, {{eoptions, Opt}, {not_fingerprint_algos,UnSup1}}});
+ [] ->
+ CryptoHashAlgs = proplists:get_value(hashs, crypto:supports(), []),
+ case [A || A <- Algs,
+ not lists:member(A, CryptoHashAlgs)] of
+ [_|_] = UnSup2 ->
+ throw({error, {{eoptions, Opt}, {unsupported_algo,UnSup2}}});
+ [] -> Opt
+ end
end;
handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 86f5cb1746..d07c596411 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -67,7 +67,8 @@
hostkey_fingerprint_check_sha/1,
hostkey_fingerprint_check_sha256/1,
hostkey_fingerprint_check_sha384/1,
- hostkey_fingerprint_check_sha512/1
+ hostkey_fingerprint_check_sha512/1,
+ hostkey_fingerprint_check_list/1
]).
%%% Common test callbacks
@@ -112,6 +113,7 @@ all() ->
hostkey_fingerprint_check_sha256,
hostkey_fingerprint_check_sha384,
hostkey_fingerprint_check_sha512,
+ hostkey_fingerprint_check_list,
id_string_no_opt_client,
id_string_own_string_client,
id_string_random_client,
@@ -812,6 +814,8 @@ hostkey_fingerprint_check_sha384(Config) ->
hostkey_fingerprint_check_sha512(Config) ->
do_hostkey_fingerprint_check(Config, sha512).
+hostkey_fingerprint_check_list(Config) ->
+ do_hostkey_fingerprint_check(Config, [sha,md5,sha256]).
%%%----
do_hostkey_fingerprint_check(Config, HashAlg) ->
@@ -824,9 +828,10 @@ do_hostkey_fingerprint_check(Config, HashAlg) ->
supported_hash(old) -> true;
supported_hash(HashAlg) ->
- proplists:get_value(HashAlg,
- proplists:get_value(hashs, crypto:supports(), []),
- false).
+ Hs = if is_atom(HashAlg) -> [HashAlg];
+ is_list(HashAlg) -> HashAlg
+ end,
+ [] == (Hs -- proplists:get_value(hashs, crypto:supports(), [])).
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
@@ -840,7 +845,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
- FPs = [case HashAlg of
+ FPs0 = [case HashAlg of
old -> public_key:ssh_hostkey_fingerprint(Key);
_ -> public_key:ssh_hostkey_fingerprint(HashAlg, Key)
end
@@ -856,6 +861,9 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
_:_ -> []
end
end],
+ FPs = if is_atom(HashAlg) -> FPs0;
+ is_list(HashAlg) -> lists:concat(FPs0)
+ end,
ct:log("Fingerprints(~p) = ~p",[HashAlg,FPs]),
%% Start daemon with the public keys that we got fingerprints from
@@ -866,8 +874,12 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
FP_check_fun = fun(PeerName, FP) ->
ct:pal("PeerName = ~p, FP = ~p",[PeerName,FP]),
HostCheck = (Host == PeerName),
- FPCheck = lists:member(FP, FPs),
- ct:log("check ~p == ~p (~p) and ~n~p in ~p (~p)~n",
+ FPCheck =
+ if is_atom(HashAlg) -> lists:member(FP, FPs);
+ is_list(HashAlg) -> lists:all(fun(FP1) -> lists:member(FP1,FPs) end,
+ FP)
+ end,
+ ct:log("check ~p == ~p (~p) and ~n~p~n in ~p (~p)~n",
[PeerName,Host,HostCheck,FP,FPs,FPCheck]),
HostCheck and FPCheck
end,