From f7bae98de902c597d9f1e5861745e90aecc58191 Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Fri, 10 Feb 2017 14:34:52 +0100
Subject: public_key: generate a list of ssh fingerprints on request
---
lib/public_key/doc/src/public_key.xml | 5 +++++
lib/public_key/src/public_key.erl | 26 ++++++++++++++++--------
lib/public_key/test/public_key_SUITE.erl | 34 +++++++++++++++++++++-----------
3 files changed, 45 insertions(+), 20 deletions(-)
(limited to 'lib')
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 37aa05e0fd..c97ec361d1 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -857,6 +857,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
ssh_hostkey_fingerprint(HostKey) -> string()
ssh_hostkey_fingerprint(DigestType, HostKey) -> string()
+ ssh_hostkey_fingerprint([DigestType], HostKey) -> [string()]
Calculates a ssh fingerprint for a hostkey.
Key = public_key()
@@ -880,6 +881,10 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
5> public_key:ssh_hostkey_fingerprint(sha256,Key).
"SHA256:aZGXhabfbf4oxglxltItWeHU7ub3Dc31NcNw2cMJePQ"
+
+ 6> public_key:ssh_hostkey_fingerprint([sha,sha256],Key).
+ ["SHA1:bSLY/C4QXLDL/Iwmhyg0PGW9UbY",
+ "SHA256:aZGXhabfbf4oxglxltItWeHU7ub3Dc31NcNw2cMJePQ"]
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 402f514803..adc35073d6 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -893,21 +893,31 @@ oid2ssh_curvename(?'secp521r1') -> <<"nistp521">>.
%%--------------------------------------------------------------------
-spec ssh_hostkey_fingerprint(public_key()) -> string().
--spec ssh_hostkey_fingerprint(digest_type(), public_key()) -> string().
+-spec ssh_hostkey_fingerprint( digest_type(), public_key()) -> string()
+ ; ([digest_type()], public_key()) -> [string()]
+ .
ssh_hostkey_fingerprint(Key) ->
- sshfp_string(md5, Key).
+ sshfp_string(md5, public_key:ssh_encode(Key,ssh2_pubkey) ).
+
+ssh_hostkey_fingerprint(HashAlgs, Key) when is_list(HashAlgs) ->
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ [sshfp_full_string(HashAlg,EncKey) || HashAlg <- HashAlgs];
+ssh_hostkey_fingerprint(HashAlg, Key) when is_atom(HashAlg) ->
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ sshfp_full_string(HashAlg, EncKey).
-ssh_hostkey_fingerprint(HashAlg, Key) ->
- lists:concat([sshfp_alg_name(HashAlg),
- [$: | sshfp_string(HashAlg, Key)]
- ]).
-sshfp_string(HashAlg, Key) ->
+sshfp_string(HashAlg, EncodedKey) ->
%% Other HashAlgs than md5 will be printed with
%% other formats than hextstr by
%% ssh-keygen -E -lf
- fp_fmt(sshfp_fmt(HashAlg), crypto:hash(HashAlg, public_key:ssh_encode(Key,ssh2_pubkey))).
+ fp_fmt(sshfp_fmt(HashAlg), crypto:hash(HashAlg, EncodedKey)).
+
+sshfp_full_string(HashAlg, EncKey) ->
+ lists:concat([sshfp_alg_name(HashAlg),
+ [$: | sshfp_string(HashAlg, EncKey)]
+ ]).
sshfp_alg_name(sha) -> "SHA1";
sshfp_alg_name(Alg) -> string:to_upper(atom_to_list(Alg)).
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 615ff32539..68aa152911 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -54,7 +54,8 @@ all() ->
ssh_hostkey_fingerprint_sha,
ssh_hostkey_fingerprint_sha256,
ssh_hostkey_fingerprint_sha384,
- ssh_hostkey_fingerprint_sha512
+ ssh_hostkey_fingerprint_sha512,
+ ssh_hostkey_fingerprint_list
].
groups() ->
@@ -93,20 +94,21 @@ end_per_group(_GroupName, Config) ->
%%-------------------------------------------------------------------
init_per_testcase(TestCase, Config) ->
case TestCase of
- ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase(md5, Config);
- ssh_hostkey_fingerprint_md5 -> init_fingerprint_testcase(md5, Config);
- ssh_hostkey_fingerprint_sha -> init_fingerprint_testcase(sha, Config);
- ssh_hostkey_fingerprint_sha256 -> init_fingerprint_testcase(sha256, Config);
- ssh_hostkey_fingerprint_sha384 -> init_fingerprint_testcase(sha384, Config);
- ssh_hostkey_fingerprint_sha512 -> init_fingerprint_testcase(sha512, Config);
+ ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase([md5], Config);
+ ssh_hostkey_fingerprint_md5 -> init_fingerprint_testcase([md5], Config);
+ ssh_hostkey_fingerprint_sha -> init_fingerprint_testcase([sha], Config);
+ ssh_hostkey_fingerprint_sha256 -> init_fingerprint_testcase([sha256], Config);
+ ssh_hostkey_fingerprint_sha384 -> init_fingerprint_testcase([sha384], Config);
+ ssh_hostkey_fingerprint_sha512 -> init_fingerprint_testcase([sha512], Config);
+ ssh_hostkey_fingerprint_list -> init_fingerprint_testcase([sha,md5], Config);
_ -> init_common_per_testcase(Config)
end.
-init_fingerprint_testcase(Alg, Config) ->
- CryptoSupports = lists:member(Alg, proplists:get_value(hashs, crypto:supports())),
- case CryptoSupports of
- false -> {skip,{Alg,not_supported}};
- true -> init_common_per_testcase(Config)
+init_fingerprint_testcase(Algs, Config) ->
+ Hashs = proplists:get_value(hashs, crypto:supports(), []),
+ case Algs -- Hashs of
+ [] -> init_common_per_testcase(Config);
+ UnsupportedAlgs -> {skip,{UnsupportedAlgs,not_supported}}
end.
init_common_per_testcase(Config0) ->
@@ -599,6 +601,14 @@ ssh_hostkey_fingerprint_sha512(_Config) ->
Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA",
Expected = public_key:ssh_hostkey_fingerprint(sha512, ssh_hostkey(rsa)).
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_list(_Config) ->
+ Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk",
+ "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"],
+ Expected = public_key:ssh_hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)).
+
%%--------------------------------------------------------------------
encrypt_decrypt() ->
[{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}].
--
cgit v1.2.3
From 9f23065062eb724e58f39a65e416e5b0e1e9d95d Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Fri, 10 Feb 2017 14:37:41 +0100
Subject: ssh: allow a list of fingerprint algos in silently_accept_hosts
option
---
lib/ssh/doc/src/ssh.xml | 13 ++++++++++---
lib/ssh/src/ssh.erl | 21 ++++++++++++++++-----
lib/ssh/test/ssh_options_SUITE.erl | 26 +++++++++++++++++++-------
3 files changed, 45 insertions(+), 15 deletions(-)
(limited to 'lib')
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.
-
+
- boolean()]]>
+
+
+ boolean()]]>
-
When true, hosts are added to the
@@ -188,8 +190,13 @@
(PeerName, PeerHostKeyFingerPrint). The fingerprint is calculated on the Peer's Host Key with
public_key:ssh_hostkey_fingerprint/1.
- If the crypto:digest_type() is present, the fingerprint is calculated with that digest type by the function
+
If the HashAlgoSpec is present and is an crypto:digest_type(), the fingerprint is calculated
+ with that digest type by the function
public_key:ssh_hostkey_fingerprint/2.
+
+ If the HashAlgoSpec is present and is a list of crypto:digest_type(), the fingerprint is calulated for
+ each digest_type and PeerHostKeyFingerPrint is the list of the results in order corresponding to the
+ HashAlgoSpec.
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,
--
cgit v1.2.3
From 82a5b5f3b8824ab7c1da403c3a40bcf0fc98c690 Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Wed, 15 Feb 2017 13:26:18 +0100
Subject: ssh: reword documentation
---
lib/ssh/doc/src/ssh.xml | 61 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 40 insertions(+), 21 deletions(-)
(limited to 'lib')
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 1a6bac8355..e42f16ebd0 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -153,7 +153,7 @@
-
IP version to use.
-
+
-
Sets the user directory, that is, the directory containing
ssh configuration files for the user, such as
@@ -175,29 +175,48 @@
supplied with this option.
-
-
-
-
- boolean()]]>
+
+
+
+
+
+
+ boolean()]]>
+
+
-
-
When true, hosts are added to the
- file without asking the user.
- Defaults to false which will give a user question on stdio of whether to accept or reject a previously
- unseen host.
- If the option value is has an accept_fun(), that fun will called with the arguments
- (PeerName, PeerHostKeyFingerPrint). The fingerprint is calculated on the Peer's Host Key with
- public_key:ssh_hostkey_fingerprint/1.
-
- If the HashAlgoSpec is present and is an crypto:digest_type(), the fingerprint is calculated
- with that digest type by the function
- public_key:ssh_hostkey_fingerprint/2.
+
This option guides the connect function how to act when the connected server presents a Host
+ Key that the client has not seen before. The default is to ask the user with a question on stdio of whether to
+ accept or reject the new Host Key.
+ See also the option user_dir
+ for the path to the file known_hosts where previously accepted Host Keys are recorded.
- If the HashAlgoSpec is present and is a list of crypto:digest_type(), the fingerprint is calulated for
- each digest_type and PeerHostKeyFingerPrint is the list of the results in order corresponding to the
- HashAlgoSpec.
-
+ The option can be given in three different forms as seen above:
+
+ - The value is a boolean(). The value true will make the client accept any unknown
+ Host Key without any user interaction. The value false keeps the default behaviour of asking the
+ the user on stdio.
+
+ - A CallbackFun will be called and the boolean return value true will make the client
+ accept the Host Key. A reurn value of false will make the client to reject the Host Key and therefore
+ also the connection will be closed. The arguments to the fun are:
+
+ - PeerName - a string with the name or address of the remote host.
+ - FingerPrint - the fingerprint of the Host Key as
+ public_key:ssh_hostkey_fingerprint/1
+ calculates it.
+
+
+
+ - A tuple {HashAlgoSpec, CallbackFun}. The HashAlgoSpec specifies which hash algorithm
+ shall be used to calculate the fingerprint used in the call of the CallbackFun. The HashALgoSpec
+ is either an atom or a list of atoms as the first argument in
+ public_key:ssh_hostkey_fingerprint/2.
+ If it is a list of hash algorithm names, the FingerPrint argument in the CallbackFun will be
+ a list of fingerprints in the same order as the corresponding name in the HashAlgoSpec list.
+
+
-
--
cgit v1.2.3
From 2869472d38814d8ab5f034e383c7aa063aab4618 Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Wed, 15 Feb 2017 13:29:32 +0100
Subject: ssh: speling error
---
lib/ssh/doc/src/ssh.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index e42f16ebd0..20508a73a6 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -226,7 +226,7 @@
supplying a password. Defaults to true.
Even if user interaction is allowed it can be
suppressed by other options, such as silently_accept_hosts
- and password. However, those optins are not always desirable
+ and password. However, those options are not always desirable
to use from a security point of view.
--
cgit v1.2.3
From 5fa9ae3f7cce55047061b94f35940d6eaf94d9ee Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Wed, 15 Feb 2017 13:34:16 +0100
Subject: ssh: speling error
---
lib/ssh/doc/src/ssh.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'lib')
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 20508a73a6..f6e26f5ee8 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -199,7 +199,7 @@
the user on stdio.
- A CallbackFun will be called and the boolean return value true will make the client
- accept the Host Key. A reurn value of false will make the client to reject the Host Key and therefore
+ accept the Host Key. A return value of false will make the client to reject the Host Key and therefore
also the connection will be closed. The arguments to the fun are:
- PeerName - a string with the name or address of the remote host.
--
cgit v1.2.3