From 1e3637220afb2269a829ea0a15b98b05f38344a8 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 8 Jun 2015 21:18:43 +0200 Subject: ssh: make server check auth_methods when rec request --- lib/ssh/src/ssh_auth.erl | 125 ++++++++++++++++++--------------- lib/ssh/src/ssh_connection_handler.erl | 35 +++++---- 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index df9a97c8f8..5610be5878 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -169,7 +169,8 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "password", data = <>}, _, - #ssh{opts = Opts} = Ssh) -> + #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> Password = unicode:characters_to_list(BinPwd), case check_password(User, Password, Opts) of true -> @@ -178,7 +179,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, false -> {not_authorized, {User, {error,"Bad user or password"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = "", + authentications = Methods, partial_success = false}, Ssh)} end; @@ -191,7 +192,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, %% ?UINT32(Sz2), NewBinPwd:Sz2/binary >> }, _, - Ssh) -> + #ssh{userauth_supported_methods = Methods} = Ssh) -> %% Password change without us having sent SSH_MSG_USERAUTH_PASSWD_CHANGEREQ (because we never do) %% RFC 4252 says: %% SSH_MSG_USERAUTH_FAILURE without partial success - The password @@ -200,7 +201,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, {not_authorized, {User, {error,"Password change not supported"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = "", + authentications = Methods, partial_success = false}, Ssh)}; handle_userauth_request(#ssh_msg_userauth_request{user = User, @@ -216,7 +217,9 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "publickey", data = Data}, - SessionId, #ssh{opts = Opts} = Ssh) -> + SessionId, + #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> <> = Data, Alg = binary_to_list(BAlg), @@ -231,7 +234,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, false -> {not_authorized, {User, undefined}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications="publickey,password", + authentications = Methods, partial_success = false}, Ssh)} end; ?FALSE -> @@ -245,49 +248,59 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "keyboard-interactive", data = _}, - _, #ssh{opts = Opts} = Ssh) -> - %% RFC4256 - %% The data field contains: - %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip - %% it for simplicity. - %% - submethods. "... the user can give a hint of which actual methods - %% he wants to use. ...". It's a "MAY use" so we skip - %% it. It also needs an understanding between the client - %% and the server. - %% - %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS, - %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message." - Default = {"SSH server", - "Enter password for \""++User++"\"", - "pwd: ", - false}, - - {Name, Instruction, Prompt, Echo} = - case proplists:get_value(auth_method_kb_interactive_data, Opts) of - undefined -> - Default; - {_,_,_,_}=V -> - V; - F when is_function(F) -> - {_,PeerName} = Ssh#ssh.peer, - F(PeerName, User, "ssh-connection") - end, - EchoEnc = case Echo of - true -> <>; - false -> <> - end, - Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name), - instruction = unicode:characters_to_list(Instruction), - language_tag = "", - num_prompts = 1, - data = <> - }, - {not_authorized, {User, undefined}, - ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, - opts = [{max_kb_tries,3},{kb_userauth_info_msg,Msg}|Opts] - })}; + _, #ssh{opts = Opts, + userauth_supported_methods = Methods} = Ssh) -> + case proplists:get_value(max_kb_tries, Opts, 0) of + N when N<1 -> + {not_authorized, {User, {authmethod, "keyboard-interactive"}}, + ssh_transport:ssh_packet( + #ssh_msg_userauth_failure{authentications = Methods, + partial_success = false}, Ssh)}; + + _ -> + %% RFC4256 + %% The data field contains: + %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip + %% it for simplicity. + %% - submethods. "... the user can give a hint of which actual methods + %% he wants to use. ...". It's a "MAY use" so we skip + %% it. It also needs an understanding between the client + %% and the server. + %% + %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS, + %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message." + Default = {"SSH server", + "Enter password for \""++User++"\"", + "pwd: ", + false}, + + {Name, Instruction, Prompt, Echo} = + case proplists:get_value(auth_method_kb_interactive_data, Opts) of + undefined -> + Default; + {_,_,_,_}=V -> + V; + F when is_function(F) -> + {_,PeerName} = Ssh#ssh.peer, + F(PeerName, User, "ssh-connection") + end, + EchoEnc = case Echo of + true -> <>; + false -> <> + end, + Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name), + instruction = unicode:characters_to_list(Instruction), + language_tag = "", + num_prompts = 1, + data = <> + }, + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, + opts = [{kb_userauth_info_msg,Msg}|Opts] + })} + end; handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", @@ -315,7 +328,8 @@ handle_userauth_info_request( handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, data = <>}, #ssh{opts = Opts0, - user = User} = Ssh) -> + user = User, + userauth_supported_methods = Methods} = Ssh) -> NumTriesLeft = proplists:get_value(max_kb_tries, Opts0, 0) - 1, Opts = lists:keydelete(max_kb_tries,1,Opts0), case check_password(User, unicode:characters_to_list(Password), Opts) of @@ -327,10 +341,11 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, (proplists:get_value(kb_userauth_info_msg,Opts)) #ssh_msg_userauth_info_request{name = "", instruction = - lists:concat( - ["Bad user or password, try again. ", - integer_to_list(NumTriesLeft), - " tries left."])}, + lists:concat( + ["Bad user or password, try again. ", + integer_to_list(NumTriesLeft), + " tries left."]) + }, {not_authorized, {User, undefined}, ssh_transport:ssh_packet(UserAuthInfoMsg, Ssh#ssh{opts = [{max_kb_tries,NumTriesLeft}|Opts]})}; @@ -338,7 +353,7 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, false -> {not_authorized, {User, {error,"Bad user or password"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ - authentications = "", + authentications = Methods, partial_success = false}, Ssh#ssh{opts = lists:keydelete(kb_userauth_info_msg,1,Opts)} )} diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index ab1fc93a1b..9bd49c5c05 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -483,17 +483,22 @@ userauth(#ssh_msg_userauth_request{service = "ssh-connection", service = "ssh-connection", peer = {_, Address}} = Ssh0, opts = Opts, starter = Pid} = State) -> - case ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of - {authorized, User, {Reply, Ssh}} -> - send_msg(Reply, State), - Pid ! ssh_connected, - connected_fun(User, Address, Method, Opts), - {next_state, connected, - next_packet(State#state{auth_user = User, ssh_params = Ssh})}; - {not_authorized, {User, Reason}, {Reply, Ssh}} -> - retry_fun(User, Address, Reason, Opts), - send_msg(Reply, State), - {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} + case lists:member(Method, Ssh0#ssh.userauth_methods) of + true -> + case ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0) of + {authorized, User, {Reply, Ssh}} -> + send_msg(Reply, State), + Pid ! ssh_connected, + connected_fun(User, Address, Method, Opts), + {next_state, connected, + next_packet(State#state{auth_user = User, ssh_params = Ssh})}; + {not_authorized, {User, Reason}, {Reply, Ssh}} -> + retry_fun(User, Address, Reason, Opts), + send_msg(Reply, State), + {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} + end; + false -> + userauth(Msg#ssh_msg_userauth_request{method="none"}, State) end; userauth(#ssh_msg_userauth_info_request{} = Msg, @@ -1148,9 +1153,9 @@ init_ssh(client = Role, Vsn, Version, Options, Socket) -> }; init_ssh(server = Role, Vsn, Version, Options, Socket) -> - AuthMethods = proplists:get_value(auth_methods, Options, ?SUPPORTED_AUTH_METHODS), + AuthMethodsAsList = string:tokens(AuthMethods, ","), {ok, PeerAddr} = inet:peername(Socket), KeyCb = proplists:get_value(key_cb, Options, ssh_file), @@ -1159,8 +1164,12 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) -> s_version = Version, key_cb = KeyCb, io_cb = proplists:get_value(io_cb, Options, ssh_io), - opts = Options, + opts = case lists:member("keyboard-interactive",AuthMethodsAsList) of + true -> [{max_kb_tries,3}|Options]; + false -> Options + end, userauth_supported_methods = AuthMethods, + userauth_methods = AuthMethodsAsList, peer = {undefined, PeerAddr}, available_host_keys = supported_host_keys(Role, KeyCb, Options) }. -- cgit v1.2.3