diff options
Diffstat (limited to 'lib/ssh')
-rw-r--r-- | lib/ssh/doc/src/ssh.xml | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh.appup.src | 6 | ||||
-rw-r--r-- | lib/ssh/src/ssh.erl | 56 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 18 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_manager.erl | 43 | ||||
-rw-r--r-- | lib/ssh/src/ssh_io.erl | 61 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 2 | ||||
-rw-r--r-- | lib/ssh/vsn.mk | 2 |
8 files changed, 135 insertions, 57 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 4ab25a8f9b..57f09c0cf0 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -183,6 +183,10 @@ <c><![CDATA[add_host_key/3]]></c>. This is considered somewhat experimental and will be better documented later on.</p> </item> + <tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag> + <item> + <p>If true, the client will not print out anything on authorization.</p> + </item> <tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag> <item> <p>Allow an existing file-descriptor to be used diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index d08dbafc32..6ba32e018f 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,10 +19,12 @@ {"%VSN%", [ + {<<"2.1.1">>, [{restart_application, ssh}]}, {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, {load_module, ssh_connection, soft_purge, soft_purge, []}, {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, {load_module, ssh_auth, soft_purge, soft_purge, []}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, {load_module, ssh_channel, soft_purge, soft_purge, []}, {load_module, ssh_file, soft_purge, soft_purge, []}]}, {load_module, ssh, soft_purge, soft_purge, []}]}, @@ -30,14 +32,16 @@ {<<"1\\.*">>, [{restart_application, ssh}]} ], [ + {<<"2.1.1">>, [{restart_application, ssh}]}, {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, {load_module, ssh_connection, soft_purge, soft_purge, []}, {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, {load_module, ssh_auth, soft_purge, soft_purge, []}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, {load_module, ssh_channel, soft_purge, soft_purge, []}, {load_module, ssh_file, soft_purge, soft_purge, []}]}, {load_module, ssh, soft_purge, soft_purge, []}]}, {<<"2.0\\.*">>, [{restart_application, ssh}]}, {<<"1\\.*">>, [{restart_application, ssh}]} ] -}. +}.
\ No newline at end of file diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 66d1d024ed..7d68b1d7bd 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -79,7 +79,7 @@ connect(Host, Port, Options, Timeout) -> DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false), Inet = inetopt(DisableIpv6), do_connect(Host, Port, [Inet | SocketOptions], - [{host, Host} | SshOptions], Timeout, DisableIpv6) + [{user_pid, self()}, {host, Host} | SshOptions], Timeout, DisableIpv6) end. do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> @@ -91,30 +91,39 @@ do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> {ok, ConnectionSup} -> {ok, Manager} = ssh_connection_sup:connection_manager(ConnectionSup), - receive - {Manager, is_connected} -> - {ok, Manager}; - %% When the connection fails - %% ssh_connection_sup:connection_manager - %% might return undefined as the connection manager - %% could allready have terminated, so we will not - %% match the Manager in this case - {_, not_connected, {error, econnrefused}} when DisableIpv6 == false -> - do_connect(Host, Port, proplists:delete(inet6, SocketOptions), - SshOptions, Timeout, true); - {_, not_connected, {error, Reason}} -> - {error, Reason}; - {_, not_connected, Other} -> - {error, Other} - after Timeout -> - ssh_connection_manager:stop(Manager), - {error, timeout} - end + msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout) catch exit:{noproc, _} -> {error, ssh_not_started} end. - +msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout) -> + receive + {Manager, is_connected} -> + {ok, Manager}; + %% When the connection fails + %% ssh_connection_sup:connection_manager + %% might return undefined as the connection manager + %% could allready have terminated, so we will not + %% match the Manager in this case + {_, not_connected, {error, econnrefused}} when DisableIpv6 == false -> + do_connect(Host, Port, proplists:delete(inet6, SocketOptions), + SshOptions, Timeout, true); + {_, not_connected, {error, Reason}} -> + {error, Reason}; + {_, not_connected, Other} -> + {error, Other}; + {From, user_password} -> + Pass = io:get_password(), + From ! Pass, + msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout); + {From, question} -> + Answer = io:get_line(""), + From ! Answer, + msg_loop(Manager, DisableIpv6, Host, Port, SocketOptions, SshOptions, Timeout) + after Timeout -> + ssh_connection_manager:stop(Manager), + {error, timeout} + end. %%-------------------------------------------------------------------- %% Function: close(ConnectionRef) -> ok %% @@ -344,6 +353,8 @@ handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -416,6 +427,9 @@ handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module), Opt; handle_ssh_option({shell, Value} = Opt) when is_function(Value) -> Opt; +handle_ssh_option({quiet_mode, Value} = Opt) when Value == true; + Value == false -> + Opt; handle_ssh_option(Opt) -> throw({error, {eoptions, Opt}}). diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 27e44df554..c436793dc4 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -71,7 +71,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, ssh_bits:install_messages(userauth_passwd_messages()), Password = case proplists:get_value(password, Opts) of undefined -> - user_interaction(IoCb); + user_interaction(IoCb, Ssh); PW -> PW end, @@ -89,10 +89,10 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, Ssh) end. -user_interaction(ssh_no_io) -> +user_interaction(ssh_no_io, _) -> not_ok; -user_interaction(IoCb) -> - IoCb:read_password("ssh password: "). +user_interaction(IoCb, Ssh) -> + IoCb:read_password("ssh password: ", Ssh). %% See RFC 4256 for info on keyboard-interactive @@ -401,11 +401,11 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) -> %% Special case/fallback for just one prompt %% (assumed to be the password prompt) case proplists:get_value(password, Opts) of - undefined -> keyboard_interact(IoCb, Name, Instr, PromptInfos); + undefined -> keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts); PW -> [PW] end; undefined -> - keyboard_interact(IoCb, Name, Instr, PromptInfos); + keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts); KbdInteractFun -> Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end, PromptInfos), @@ -419,15 +419,15 @@ keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) -> end end. -keyboard_interact(IoCb, Name, Instr, Prompts) -> +keyboard_interact(IoCb, Name, Instr, Prompts, Opts) -> if Name /= "" -> IoCb:format("~s", [Name]); true -> ok end, if Instr /= "" -> IoCb:format("~s", [Instr]); true -> ok end, - lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt); - ({Prompt, false}) -> IoCb:read_password(Prompt) + lists:map(fun({Prompt, true}) -> IoCb:read_line(Prompt, Opts); + ({Prompt, false}) -> IoCb:read_password(Prompt, Opts) end, Prompts). diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index e53cd4f4f7..5aa79f978c 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -523,7 +523,7 @@ handle_info({same_user, _}, State) -> handle_info(ssh_connected, #state{role = client, client = Pid} = State) -> Pid ! {self(), is_connected}, - {noreply, State#state{connected = true}}; + {noreply, State#state{connected = true, opts = handle_password(State#state.opts)}}; handle_info(ssh_connected, #state{role = server} = State) -> {noreply, State#state{connected = true}}; @@ -536,6 +536,47 @@ handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) -> handle_info({'EXIT', _Sup, Reason}, State) -> {stop, Reason, State}. +handle_password(Opts) -> + handle_rsa_password(handle_dsa_password(handle_normal_password(Opts))). +handle_normal_password(Opts) -> + case proplists:get_value(ssh_opts, Opts, false) of + false -> + Opts; + SshOpts -> + case proplists:get_value(password, SshOpts, false) of + false -> + Opts; + _Password -> + NewOpts = [{password, undefined}|lists:keydelete(password, 1, SshOpts)], + [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)] + end + end. +handle_dsa_password(Opts) -> + case proplists:get_value(ssh_opts, Opts, false) of + false -> + Opts; + SshOpts -> + case proplists:get_value(dsa_pass_phrase, SshOpts, false) of + false -> + Opts; + _Password -> + NewOpts = [{dsa_pass_phrase, undefined}|lists:keydelete(dsa_pass_phrase, 1, SshOpts)], + [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)] + end + end. +handle_rsa_password(Opts) -> + case proplists:get_value(ssh_opts, Opts, false) of + false -> + Opts; + SshOpts -> + case proplists:get_value(rsa_pass_phrase, SshOpts, false) of + false -> + Opts; + _Password -> + NewOpts = [{rsa_pass_phrase, undefined}|lists:keydelete(rsa_pass_phrase, 1, SshOpts)], + [{ssh_opts, NewOpts}|lists:keydelete(ssh_opts, 1, Opts)] + end + end. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl index 1dbd097423..17a7cebb4a 100644 --- a/lib/ssh/src/ssh_io.erl +++ b/lib/ssh/src/ssh_io.erl @@ -23,37 +23,52 @@ -module(ssh_io). --export([yes_no/1, read_password/1, read_line/1, format/2]). +-export([yes_no/2, read_password/2, read_line/2, format/2]). -import(lists, [reverse/1]). +-include("ssh.hrl"). +read_line(Prompt, Ssh) -> + format("~s", [listify(Prompt)]), + proplists:get_value(user_pid, Ssh) ! {self(), question}, + receive + Answer -> + Answer + end. -read_line(Prompt) when is_list(Prompt) -> - io:get_line(list_to_atom(Prompt)); -read_line(Prompt) when is_atom(Prompt) -> - io:get_line(Prompt). - -read_ln(Prompt) -> - trim(read_line(Prompt)). - -yes_no(Prompt) -> +yes_no(Prompt, Ssh) -> io:format("~s [y/n]?", [Prompt]), - case read_ln('') of - "y" -> yes; - "n" -> no; - "Y" -> yes; - "N" -> no; - _ -> - io:format("please answer y or n\n"), - yes_no(Prompt) + proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), question}, + receive + Answer -> + case trim(Answer) of + "y" -> yes; + "n" -> no; + "Y" -> yes; + "N" -> no; + y -> yes; + n -> no; + _ -> + io:format("please answer y or n\n"), + yes_no(Prompt, Ssh) + end end. -read_password(Prompt) -> +read_password(Prompt, Ssh) -> format("~s", [listify(Prompt)]), - case io:get_password() of - "" -> - read_password(Prompt); - Pass -> Pass + case is_list(Ssh) of + false -> + proplists:get_value(user_pid, Ssh#ssh.opts) ! {self(), user_password}; + _ -> + proplists:get_value(user_pid, Ssh) ! {self(), user_password} + end, + receive + Answer -> + case Answer of + "" -> + read_password(Prompt, Ssh); + Pass -> Pass + end end. listify(A) when is_atom(A) -> diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 1f912c9bdf..7f6e7d9946 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -133,7 +133,7 @@ kex_dh_gex_messages() -> ]. yes_no(Ssh, Prompt) -> - (Ssh#ssh.io_cb):yes_no(Prompt). + (Ssh#ssh.io_cb):yes_no(Prompt, Ssh). connect(ConnectionSup, Address, Port, SocketOpts, Opts) -> Timeout = proplists:get_value(connect_timeout, Opts, infinity), diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index defa47f824..921ec2206a 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.1.1 +SSH_VSN = 2.1.2 APP_VSN = "ssh-$(SSH_VSN)" |