diff options
-rw-r--r-- | lib/ssh/src/ssh.erl | 9 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 74 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 6 | ||||
-rw-r--r-- | lib/ssh/src/ssh_file.erl | 89 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 69 |
5 files changed, 113 insertions, 134 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 29294b836f..eec17fd5d6 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -23,6 +23,7 @@ -include("ssh.hrl"). -include("ssh_connect.hrl"). +-include_lib("public_key/include/public_key.hrl"). -export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2, channel_info/3, @@ -367,7 +368,7 @@ inetopt(false) -> %% Description: Use SSH key to sign data. %%-------------------------------------------------------------------- sign_data(Data, Algorithm) when is_binary(Data) -> - case ssh_file:private_identity_key(Algorithm,[]) of + case ssh_file:user_key(Algorithm,[]) of {ok, Key} when Algorithm == "ssh-rsa" -> public_key:sign(Data, sha, Key); Error -> @@ -385,9 +386,9 @@ sign_data(Data, Algorithm) when is_binary(Data) -> %% Description: Use SSH signature to verify data. %%-------------------------------------------------------------------- verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) -> - case ssh_file:public_identity_key(Algorithm, []) of - {ok, Key} when Algorithm == "ssh-rsa" -> - public_key:verify(Data, sha, Signature, Key); + case ssh_file:user_key(Algorithm, []) of + {ok, #'RSAPrivateKey'{publicExponent = E, modulus = N}} when Algorithm == "ssh-rsa" -> + public_key:verify(Data, sha, Signature, #'RSAPublicKey'{publicExponent = E, modulus = N}); Error -> Error end. diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 3b087262d8..a2e74a12bb 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -41,16 +41,17 @@ publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> - + Hash = sha, %% Maybe option?! ssh_bits:install_messages(userauth_pk_messages()), - - case ssh_file:private_identity_key(Alg, Opts) of + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:user_key(Alg, Opts) of {ok, Key} -> - PubKeyBlob = ssh_file:encode_public_key(Key), + PubKeyBlob = encode_public_key(Key), SigData = build_sig_data(SessionId, User, Service, PubKeyBlob, Alg), - Sig = sign(SigData, Hash, Key), + Sig = ssh_transport:sign(SigData, Hash, Key), SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]), ssh_transport:ssh_packet( #ssh_msg_userauth_request{user = User, @@ -313,21 +314,19 @@ get_password_option(Opts, User) -> end. verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - {ok, Key} = ssh_file:decode_public_key_v2(KeyBlob, Alg), - case ssh_file:lookup_user_key(Key, User, Alg, Opts) of - {ok, OurKey} -> - case OurKey of - Key -> - 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, - verify(PlainText, sha, Sig, Key); - _ -> - {error, key_unacceptable} - end; - Error -> Error + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:is_auth_key(Key, User, Alg, Opts) of + true -> + 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, sha, Sig, Key); + false -> + {error, key_unacceptable} end. build_sig_data(SessionId, User, Service, KeyBlob, Alg) -> @@ -346,20 +345,6 @@ algorithm(ssh_rsa) -> algorithm(ssh_dsa) -> "ssh-dss". -sign(SigData, Hash, #'DSAPrivateKey'{} = Key) -> - DerSignature = public_key:sign(SigData, Hash, Key), - #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature), - <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>; -sign(SigData, Hash, Key) -> - public_key:sign(SigData, Hash, Key). - -verify(PlainText, Hash, Sig, {_, #'Dss-Parms'{}} = Key) -> - <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> = Sig, - Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}), - public_key:verify(PlainText, Hash, Signature, Key); -verify(PlainText, Hash, Sig, Key) -> - public_key:verify(PlainText, Hash, Sig, Key). - decode_keyboard_interactive_prompts(NumPrompts, Data) -> Types = lists:append(lists:duplicate(NumPrompts, [string, boolean])), pairwise_tuplify(ssh_bits:decode(Data, Types)). @@ -435,3 +420,24 @@ other_alg("ssh-rsa") -> "ssh-dss"; other_alg("ssh-dss") -> "ssh-rsa". +decode_public_key_v2(K_S, "ssh-rsa") -> + case ssh_bits:decode(K_S,[string,mpint,mpint]) of + ["ssh-rsa", E, N] -> + {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}}; + _ -> + {error, bad_format} + end; +decode_public_key_v2(K_S, "ssh-dss") -> + case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of + ["ssh-dss",P,Q,G,Y] -> + {ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}}; + _ -> + {error, bad_format} + end; +decode_public_key_v2(_, _) -> + {error, bad_format}. + +encode_public_key(#'RSAPrivateKey'{publicExponent = E, modulus = N}) -> + ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint]); +encode_public_key(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) -> + ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]). diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index a9bf23953d..9079089d5d 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -684,16 +684,14 @@ supported_host_keys(server, KeyCb, Options) -> ["ssh-dss", "ssh-rsa"]). available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> - Scope = proplists:get_value(key_scope, Opts, system), - case KeyCb:private_host_dsa_key(Scope, Opts) of + case KeyCb:host_key('ssh-dss', Opts) of {ok, _} -> Alg; Other -> Other end; available_host_key(KeyCb, "ssh-rsa" = Alg, Opts) -> - Scope = proplists:get_value(key_scope, Opts, system), - case KeyCb:private_host_rsa_key(Scope, Opts) of + case KeyCb:host_key('ssh-rsa', Opts) of {ok, _} -> Alg; Other -> diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index d5cacc3294..97ebf77e82 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -28,63 +28,58 @@ -include("ssh.hrl"). --export([public_host_dsa_key/2,private_host_dsa_key/2, - public_host_rsa_key/2,private_host_rsa_key/2, - %%public_host_key/2, - private_host_key/2, - lookup_host_key/3, add_host_key/3, - lookup_user_key/4, ssh_dir/2, file_name/3]). +-export([host_key/2, + user_key/2, + is_host_key/4, + add_host_key/3, + is_auth_key/4]). --export([private_identity_key/2, - public_identity_key/2]). - --export([encode_public_key/1, decode_public_key_v2/2]). -define(PERM_700, 8#700). -define(PERM_644, 8#644). %% API -public_host_dsa_key(Type, Opts) -> - File = file_name(Type, "ssh_host_dsa_key.pub", Opts), - decode(File, public_key). -private_host_dsa_key(Type, Opts) -> - File = file_name(Type, "ssh_host_dsa_key", Opts), +%% Used by server +host_key(Algorithm, Opts) -> + File = file_name(system, file_base_name(Algorithm), Opts), Password = proplists:get_value(password, Opts, ignore), decode(File, Password). -public_host_rsa_key(Type, Opts) -> - File = file_name(Type, "ssh_host_rsa_key.pub", Opts), - decode(File, public_key). -private_host_rsa_key(Type, Opts) -> - File = file_name(Type, "ssh_host_rsa_key", Opts), - Password = proplists:get_value(password, Opts, ignore), - decode(File, Password). +is_auth_key(Key, User, Alg, Opts) -> + case lookup_user_key(Key, User, Alg, Opts) of + {ok, Key} -> + true; + _ -> + false + end. -%% public_host_key(Type, Opts) -> -%% File = file_name(Type, "ssh_host_key", Opts), -%% decode(File, public_key). - -private_host_key(Type, Opts) -> - File = file_name(Type, "ssh_host_key", Opts), - Password = proplists:get_value(password, Opts, ignore), - decode(File, Password). -private_identity_key(Alg, Opts) -> +%% Used by client +is_host_key(Key, PeerName, Algorithm, Opts) -> + case lookup_host_key(PeerName, Algorithm, Opts) of + {ok, Key} -> + true; + _ -> + false + end. + +user_key(Alg, Opts) -> File = file_name(user, identity_key_filename(Alg), Opts), Password = proplists:get_value(password, Opts, ignore), decode(File, Password). -public_identity_key(Alg, Opts) -> - File = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts), - decode(File, public_key). -encode_public_key(#'RSAPrivateKey'{publicExponent = E, modulus = N}) -> - ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint]); -encode_public_key(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) -> - ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]). +%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +file_base_name('ssh-rsa') -> + "ssh_host_rsa_key"; +file_base_name('ssh-dss') -> + "ssh_host_dsa_key"; +file_base_name(_) -> + "ssh_host_key". decode(File, Password) -> try @@ -184,7 +179,7 @@ file_name(Type, Name, Opts) -> FN = filename:join(ssh_dir(Type, Opts), Name), FN. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% in: "host" out: "host,1.2.3.4. add_ip(Host) -> @@ -216,22 +211,6 @@ do_lookup_host_key(Host, Alg, Opts) -> identity_key_filename("ssh-dss") -> "id_dsa"; identity_key_filename("ssh-rsa") -> "id_rsa". -decode_public_key_v2(K_S, "ssh-rsa") -> - case ssh_bits:decode(K_S,[string,mpint,mpint]) of - ["ssh-rsa", E, N] -> - {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}}; - _ -> - {error, bad_format} - end; -decode_public_key_v2(K_S, "ssh-dss") -> - case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of - ["ssh-dss",P,Q,G,Y] -> - {ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}}; - _A -> - {error, bad_format} - end; -decode_public_key_v2(_, _) -> - {error, bad_format}. lookup_host_key_fd(Fd, Host, KeyType) -> case io:get_line(Fd, '') of diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index f99b9c9ca7..6140c87f6e 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -39,7 +39,8 @@ handle_kex_dh_gex_group/2, handle_kex_dh_gex_reply/2, handle_new_keys/2, handle_kex_dh_gex_request/2, handle_kexdh_reply/2, - unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1]). + unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1, + sign/3, verify/4]). versions(client, Options)-> Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION), @@ -408,37 +409,25 @@ sid(#ssh{session_id = Id}, _) -> %% get_host_key(SSH) -> #ssh{key_cb = Mod, opts = Opts, algorithms = ALG} = SSH, - Scope = proplists:get_value(key_scope, Opts, system), - case ALG#alg.hkey of - 'ssh-rsa' -> - case Mod:private_host_rsa_key(Scope, Opts) of - {ok, #'RSAPrivateKey'{modulus = N, publicExponent = E} = Key} -> - {Key, - ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])}; - Error -> - exit(Error) - end; - 'ssh-dss' -> - case Mod:private_host_dsa_key(Scope, Opts) of - {ok, #'DSAPrivateKey'{y = Y, p = P, q = Q, g = G} = Key} -> - {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y], - [string,mpint,mpint,mpint,mpint])}; - Error -> - exit(Error) - end; - Foo -> - exit({error, {Foo, bad_key_type}}) + + case Mod:host_key(ALG#alg.hkey, Opts) of + {ok, #'RSAPrivateKey'{modulus = N, publicExponent = E} = Key} -> + {Key, + ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])}; + {ok, #'DSAPrivateKey'{y = Y, p = P, q = Q, g = G} = Key} -> + {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y], + [string,mpint,mpint,mpint,mpint])}; + Result -> + exit({error, {Result, unsupported_key_type}}) end. sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) -> Hash = sha, %% Option ?! - Signature = public_key:sign(H, Hash, Private), + Signature = sign(H, Hash, Private), ssh_bits:encode(["ssh-rsa", Signature],[string, binary]); sign_host_key(_Ssh, #'DSAPrivateKey'{} = Private, H) -> Hash = sha, %% Option ?! - DerSignature = public_key:sign(H, Hash, Private), - #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature), - RawSignature = <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>, + RawSignature = sign(H, Hash, Private), ssh_bits:encode(["ssh-dss", RawSignature],[string, binary]). verify_host_key(SSH, K_S, H, H_SIG) -> @@ -457,7 +446,7 @@ verify_host_key_rsa(SSH, K_S, H, H_SIG) -> ["ssh-rsa", E, N] -> ["ssh-rsa",SIG] = ssh_bits:decode(H_SIG,[string,binary]), Public = #'RSAPublicKey'{publicExponent = E, modulus = N}, - case public_key:verify(H, sha, SIG, Public) of + case verify(H, sha, SIG, Public) of false -> {error, bad_signature}; true -> @@ -472,9 +461,7 @@ verify_host_key_dss(SSH, K_S, H, H_SIG) -> ["ssh-dss",P,Q,G,Y] -> ["ssh-dss",SIG] = ssh_bits:decode(H_SIG,[string,binary]), Public = {Y, #'Dss-Parms'{p = P, q = Q, g = G}}, - <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> = SIG, - Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}), - case public_key:verify(H, sha, Signature, Public) of + case verify(H, sha, SIG, Public) of false -> {error, bad_signature}; true -> @@ -495,14 +482,10 @@ accepted_host(Ssh, PeerName, Opts) -> known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh, Public, Alg) -> PeerName = peer_name(Peer), - case Mod:lookup_host_key(PeerName, Alg, Opts) of - {ok, Public} -> + case Mod:is_host_key(Public, PeerName, Alg, Opts) of + true -> ok; - {ok, BadPublic} -> - error_logger:format("known_host_key: Public ~p BadPublic ~p\n", - [Public, BadPublic]), - {error, bad_public_key}; - {error, not_found} -> + false -> case accepted_host(Ssh, PeerName, Opts) of yes -> Mod:add_host_key(PeerName, Public, Opts); @@ -709,8 +692,20 @@ msg_data(PacketData) -> _:PaddingLen/binary>> = PacketData, Data. +sign(SigData, Hash, #'DSAPrivateKey'{} = Key) -> + DerSignature = public_key:sign(SigData, Hash, Key), + #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature), + <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>; +sign(SigData, Hash, Key) -> + public_key:sign(SigData, Hash, Key). + +verify(PlainText, Hash, Sig, {_, #'Dss-Parms'{}} = Key) -> + <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> = Sig, + Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}), + public_key:verify(PlainText, Hash, Signature, Key); +verify(PlainText, Hash, Sig, Key) -> + public_key:verify(PlainText, Hash, Sig, Key). - %% public key algorithms %% %% ssh-dss REQUIRED sign Raw DSS Key |