aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2017-04-19 12:01:26 +0200
committerHans Nilsson <[email protected]>2017-04-26 12:15:00 +0200
commit519f89016e7ce755775a88730814fa34af21676c (patch)
treece24d8d986534a184986916dfc3d771f0b91404e
parentb7cba805e37e591d8fa7d7df06f9563a9f926e23 (diff)
downloadotp-519f89016e7ce755775a88730814fa34af21676c.tar.gz
otp-519f89016e7ce755775a88730814fa34af21676c.tar.bz2
otp-519f89016e7ce755775a88730814fa34af21676c.zip
ssh: server-sig-algs, client side
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_auth.erl85
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl23
-rw-r--r--lib/ssh/src/ssh_options.erl20
4 files changed, 61 insertions, 69 deletions
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 1a95bb27e7..cf2a359e6c 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -38,7 +38,6 @@
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
--define(SUPPORTED_USER_KEYS, ['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']).
-define(FALSE, 0).
-define(TRUE, 1).
@@ -201,6 +200,7 @@
userauth_quiet_mode, % boolean()
userauth_methods, % list( string() ) eg ["keyboard-interactive", "password"]
userauth_supported_methods, % string() eg "keyboard-interactive,password"
+ userauth_pubkeys,
kb_tries_left = 0, % integer(), num tries left for "keyboard-interactive"
userauth_preference,
available_host_keys,
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 51df54341f..aadd1ad6dc 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -136,34 +136,40 @@ keyboard_interactive_msg([#ssh{user = User,
Ssh)
end.
-publickey_msg([Alg, #ssh{user = User,
+publickey_msg([SigAlg, #ssh{user = User,
session_id = SessionId,
service = Service,
opts = Opts} = Ssh]) ->
- Hash = ssh_transport:sha(Alg),
+ Hash = ssh_transport:sha(SigAlg),
+ KeyAlg = key_alg(SigAlg),
{KeyCb,KeyCbOpts} = ?GET_OPT(key_cb, Opts),
UserOpts = ?GET_OPT(user_options, Opts),
- case KeyCb:user_key(Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
+ case KeyCb:user_key(KeyAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
{ok, PrivKey} ->
- StrAlgo = atom_to_list(Alg),
- case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of
- not_ok ->
- {not_ok, Ssh};
+ SigAlgStr = atom_to_list(SigAlg),
+ try
+ Key = ssh_transport:extract_public_key(PrivKey),
+ public_key:ssh_encode(Key, ssh2_pubkey)
+ of
PubKeyBlob ->
- SigData = build_sig_data(SessionId,
- User, Service, PubKeyBlob, StrAlgo),
+ SigData = build_sig_data(SessionId, User, Service,
+ PubKeyBlob, SigAlgStr),
Sig = ssh_transport:sign(SigData, Hash, PrivKey),
- SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]),
+ SigBlob = list_to_binary([?string(SigAlgStr),
+ ?binary(Sig)]),
ssh_transport:ssh_packet(
#ssh_msg_userauth_request{user = User,
service = Service,
method = "publickey",
data = [?TRUE,
- ?string(StrAlgo),
+ ?string(SigAlgStr),
?binary(PubKeyBlob),
?binary(SigBlob)]},
Ssh)
- end;
+ catch
+ _:_ ->
+ {not_ok, Ssh}
+ end;
_Error ->
{not_ok, Ssh}
end.
@@ -190,8 +196,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
method = "none",
data = <<>>},
Ssh#ssh{user = User,
- userauth_preference =
- method_preference(?GET_OPT(pref_public_key_algs, Opts)),
+ userauth_preference = method_preference(Ssh#ssh.userauth_pubkeys),
userauth_methods = none,
service = "ssh-connection"}
)
@@ -265,8 +270,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
#ssh{opts = Opts,
userauth_supported_methods = Methods} = Ssh) ->
- case pre_verify_sig(User, binary_to_list(BAlg),
- KeyBlob, Opts) of
+ case pre_verify_sig(User, KeyBlob, Opts) of
true ->
{not_authorized, {User, undefined},
ssh_transport:ssh_packet(
@@ -446,10 +450,10 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{},
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-method_preference(PubKeyAlgs) ->
+method_preference(SigKeyAlgs) ->
%% PubKeyAlgs: List of user (client) public key algorithms to try to use.
%% All of the acceptable algorithms is the default values.
- PubKeyDefs = [{"publickey", ?MODULE, publickey_msg, [A]} || A <- PubKeyAlgs],
+ PubKeyDefs = [{"publickey", ?MODULE, publickey_msg, [A]} || A <- SigKeyAlgs],
NonPKmethods = [{"password", ?MODULE, password_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
],
@@ -492,9 +496,9 @@ get_password_option(Opts, User) ->
false -> ?GET_OPT(password, Opts)
end.
-pre_verify_sig(User, Alg, KeyBlob, Opts) ->
+pre_verify_sig(User, KeyBlob, Opts) ->
try
- {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
+ Key = public_key:ssh_decode(KeyBlob, ssh2_pubkey), % or exception
{KeyCb,KeyCbOpts} = ?GET_OPT(key_cb, Opts),
UserOpts = ?GET_OPT(user_options, Opts),
KeyCb:is_auth_key(Key, User, [{key_cb_private,KeyCbOpts}|UserOpts])
@@ -505,21 +509,19 @@ pre_verify_sig(User, Alg, KeyBlob, Opts) ->
verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
try
- {ok, Key} = decode_public_key_v2(KeyBlob, Alg),
-
{KeyCb,KeyCbOpts} = ?GET_OPT(key_cb, Opts),
UserOpts = ?GET_OPT(user_options, Opts),
- case KeyCb:is_auth_key(Key, User, [{key_cb_private,KeyCbOpts}|UserOpts]) 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, ssh_transport:sha(list_to_atom(Alg)), Sig, Key);
- false ->
- false
- end
+ Key0 = public_key:ssh_decode(KeyBlob, ssh2_pubkey), % or exception
+ true = KeyCb:is_auth_key(Key0, User, [{key_cb_private,KeyCbOpts}|UserOpts]),
+ Key0
+ 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,
+ ssh_transport:verify(PlainText, ssh_transport:sha(list_to_atom(Alg)), Sig, Key)
catch
_:_ ->
false
@@ -591,18 +593,7 @@ keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
language = "en"}})
end.
-decode_public_key_v2(Bin, _Type) ->
- try
- public_key:ssh_decode(Bin, ssh2_pubkey)
- of
- Key -> {ok, Key}
- catch
- _:_ -> {error, bad_format}
- end.
-encode_public_key(_Alg, Key) ->
- try
- public_key:ssh_encode(Key, ssh2_pubkey)
- catch
- _:_ -> not_ok
- end.
+key_alg('rsa-sha2-256') -> 'ssh-rsa';
+key_alg('rsa-sha2-512') -> 'ssh-rsa';
+key_alg(Alg) -> Alg.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 128a9175f5..ac1b792f32 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -464,6 +464,7 @@ init_ssh_record(Role, _Socket, PeerAddr, Opts) ->
true -> ssh_io;
false -> ssh_no_io
end,
+ userauth_pubkeys = ?GET_OPT(pref_public_key_algs, Opts),
userauth_quiet_mode = ?GET_OPT(quiet_mode, Opts),
peer = {PeerName, PeerAddr}
};
@@ -711,7 +712,7 @@ handle_event(internal, Msg, {ext_info,Role,init}, D) when is_tuple(Msg) ->
%% If something else arrives, goto next state and handle the event in that one
{next_state, {service_request,Role}, D, [postpone]};
-handle_event(internal, Msg, {ext_info,Role,renegotiate}, D) when is_tuple(Msg) ->
+handle_event(internal, Msg, {ext_info,Role,_ReNegFlag}, D) when is_tuple(Msg) ->
%% If something else arrives, goto next state and handle the event in that one
{next_state, {connected,Role}, D, [postpone]};
@@ -1131,6 +1132,7 @@ handle_event({call,From}, stop, StateName, D0) ->
{Repls,D} = send_replies(Replies, D0),
{stop_and_reply, normal, [{reply,From,ok}|Repls], D#data{connection_state=Connection}};
+
handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
{keep_state_and_data, [postpone]};
@@ -1380,12 +1382,16 @@ handle_event(info, UnexpectedMessage, StateName, D = #data{ssh_params = Ssh}) ->
handle_event(internal, {disconnect,Msg,_Reason}, StateName, D) ->
disconnect(Msg, StateName, D);
+handle_event(_Type, _Msg, {ext_info,Role,_ReNegFlag}, D) ->
+ %% If something else arrives, goto next state and handle the event in that one
+ {next_state, {connected,Role}, D, [postpone]};
+
handle_event(Type, Ev, StateName, D) ->
Descr =
case catch atom_to_list(element(1,Ev)) of
"ssh_msg_" ++_ when Type==internal ->
%% "Message in wrong state";
-lists:flatten(io_lib:format("Message ~p in wrong state (~p)", [element(1,Ev), StateName]));
+ lists:flatten(io_lib:format("Message ~p in wrong state (~p)", [element(1,Ev), StateName]));
_ ->
"Internal error"
end,
@@ -1689,11 +1695,20 @@ cache(#data{connection_state=C}) -> C#connection.channel_cache.
handle_ssh_msg_ext_info(#ssh_msg_ext_info{}, D=#data{ssh_params = #ssh{recv_ext_info=false}} ) ->
% The peer sent this although we didn't allow it!
D;
+
handle_ssh_msg_ext_info(#ssh_msg_ext_info{data=Data}, D0) ->
lists:foldl(fun ext_info/2, D0, Data).
-%% ext_info({ExtName,ExtValue}, D0) ->
-%% D0;
+
+ext_info({"server-sig-algs",SigAlgs}, D0 = #data{ssh_params=#ssh{role=client}=Ssh0}) ->
+ %% Make strings to eliminate risk of beeing bombed with odd strings that fills the atom table:
+ SupportedAlgs = lists:map(fun erlang:atom_to_list/1, ssh_transport:supported_algorithms(public_key)),
+ Ssh = Ssh0#ssh{userauth_pubkeys =
+ [list_to_atom(SigAlg) || SigAlg <- string:tokens(SigAlgs,","),
+ lists:member(SigAlg, SupportedAlgs)
+ ]},
+ D0#data{ssh_params = Ssh};
+
ext_info(_, D0) ->
%% Not implemented
D0.
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 6e898b4fde..0886d5b34d 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -437,9 +437,7 @@ default(client) ->
{pref_public_key_algs, def} =>
#{default =>
- %% Get dynamically supported keys in the order of the ?SUPPORTED_USER_KEYS
- [A || A <- ?SUPPORTED_USER_KEYS,
- lists:member(A, ssh_transport:supported_algorithms(public_key))],
+ ssh_transport:supported_algorithms(public_key),
chk =>
fun check_pref_public_key_algs/1,
class =>
@@ -670,20 +668,8 @@ check_pref_public_key_algs(V) ->
PKs = ssh_transport:supported_algorithms(public_key),
CHK = fun(A, Ack) ->
case lists:member(A, PKs) of
- true ->
- [A|Ack];
- false ->
- %% Check with the documented options, that is,
- %% the one we can handle
- case lists:member(A,?SUPPORTED_USER_KEYS) of
- false ->
- %% An algorithm ssh never can handle
- error_in_check(A, "Not supported public key");
- true ->
- %% An algorithm ssh can handle, but not in
- %% this very call
- Ack
- end
+ true -> [A|Ack];
+ false -> error_in_check(A, "Not supported public key")
end
end,
case lists:foldr(