aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src')
-rw-r--r--lib/ssh/src/Makefile3
-rw-r--r--lib/ssh/src/ssh.app.src1
-rw-r--r--lib/ssh/src/ssh.appup.src44
-rw-r--r--lib/ssh/src/ssh.erl53
-rw-r--r--lib/ssh/src/ssh_acceptor.erl4
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl13
-rw-r--r--lib/ssh/src/ssh_auth.erl110
-rw-r--r--lib/ssh/src/ssh_channel.erl14
-rw-r--r--lib/ssh/src/ssh_cli.erl63
-rw-r--r--lib/ssh/src/ssh_connect.hrl3
-rw-r--r--lib/ssh/src/ssh_connection.erl19
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl197
-rw-r--r--lib/ssh/src/ssh_info.erl193
-rw-r--r--lib/ssh/src/ssh_io.erl6
-rw-r--r--lib/ssh/src/ssh_message.erl45
-rw-r--r--lib/ssh/src/ssh_sftp.erl35
-rw-r--r--lib/ssh/src/ssh_system_sup.erl8
-rw-r--r--lib/ssh/src/ssh_transport.erl107
-rw-r--r--lib/ssh/src/ssh_xfer.erl8
19 files changed, 715 insertions, 211 deletions
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 2ef2859fd7..90d71107ad 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -65,6 +65,7 @@ MODULES= \
ssh_cli \
ssh_file \
ssh_io \
+ ssh_info \
ssh_math \
ssh_message \
ssh_no_io \
@@ -115,7 +116,7 @@ $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index e0a51b3574..4ad55b34ca 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -23,6 +23,7 @@
sshd_sup,
ssh_file,
ssh_io,
+ ssh_info,
ssh_math,
ssh_no_io,
ssh_server_key_api,
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 42eb2167e0..600c01454c 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,13 +19,49 @@
{"%VSN%",
[
- {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
- {load_module, ssh_acceptor, soft_purge, soft_purge, []}]},
+ {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
{<<".*">>, [{restart_application, ssh}]}
],
[
- {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
- {load_module, ssh_acceptor, soft_purge, soft_purge, []}]},
+ {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
{<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 240de69eff..de047d3c83 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -74,8 +74,7 @@ connect(Host, Port, Options, Timeout) ->
{_, Transport, _} = TransportOpts =
proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
ConnectionTimeout = proplists:get_value(connect_timeout, Options, infinity),
- Inet = proplists:get_value(inet, SshOptions, inet),
- try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], ConnectionTimeout) of
+ try Transport:connect(Host, Port, [ {active, false} | SocketOptions], ConnectionTimeout) of
{ok, Socket} ->
Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
@@ -235,29 +234,33 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
{port, Port}, {role, server},
{socket_opts, SocketOptions},
{ssh_opts, Options}]) of
- {ok, SysSup} ->
- {ok, SysSup};
{error, {already_started, _}} ->
{error, eaddrinuse};
- {error, R} ->
- {error, R}
+ Result = {Code, _} when (Code == ok) or (Code == error) ->
+ Result
catch
exit:{noproc, _} ->
{error, ssh_not_started}
end;
Sup ->
- case ssh_system_sup:restart_acceptor(Host, Port) of
+ AccPid = ssh_system_sup:acceptor_supervisor(Sup),
+ case ssh_acceptor_sup:start_child(AccPid, [{address, Host},
+ {port, Port}, {role, server},
+ {socket_opts, SocketOptions},
+ {ssh_opts, Options}]) of
+ {error, {already_started, _}} ->
+ {error, eaddrinuse};
{ok, _} ->
{ok, Sup};
- _ ->
- {error, eaddrinuse}
+ Other ->
+ Other
end
end.
handle_options(Opts) ->
try handle_option(proplists:unfold(Opts), [], []) of
- {_,_} = Options ->
- Options
+ {Inet, Ssh} ->
+ {handle_ip(Inet), Ssh}
catch
throw:Error ->
Error
@@ -393,7 +396,8 @@ handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
-
+ Opt;
+handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
@@ -433,13 +437,14 @@ handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
handle_inet_option({active, _} = Opt) ->
- throw({error, {{eoptions, Opt}, "Ssh has built in flow control, "
- "and activ is handled internaly user is not allowd"
+ throw({error, {{eoptions, Opt}, "SSH has built in flow control, "
+ "and active is handled internally, user is not allowed"
"to specify this option"}});
-handle_inet_option({inet, Value} = Opt) when (Value == inet) or (Value == inet6) ->
- Opt;
+
+handle_inet_option({inet, Value}) when (Value == inet) or (Value == inet6) ->
+ Value;
handle_inet_option({reuseaddr, _} = Opt) ->
- throw({error, {{eoptions, Opt},"Is set internaly user is not allowd"
+ throw({error, {{eoptions, Opt},"Is set internally, user is not allowed"
"to specify this option"}});
%% Option verified by inet
handle_inet_option(Opt) ->
@@ -460,3 +465,17 @@ handle_pref_algs([H|T], Acc) ->
_ ->
false
end.
+
+handle_ip(Inet) -> %% Default to ipv4
+ case lists:member(inet, Inet) of
+ true ->
+ Inet;
+ false ->
+ case lists:member(inet6, Inet) of
+ true ->
+ Inet;
+ false ->
+ [inet | Inet]
+ end
+ end.
+
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 7302196674..6c443eeb9c 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -22,7 +22,8 @@
-module(ssh_acceptor).
%% Internal application API
--export([start_link/5]).
+-export([start_link/5,
+ number_of_connections/1]).
%% spawn export
-export([acceptor_init/6, acceptor_loop/6]).
@@ -140,5 +141,6 @@ handle_error(Reason) ->
number_of_connections(SystemSup) ->
length([X ||
{R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
+ is_pid(X),
is_reference(R)
]).
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index 2be729d305..46fdef07d0 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
-module(ssh_acceptor_sup).
-behaviour(supervisor).
--export([start_link/1, start_child/2, stop_child/2]).
+-export([start_link/1, start_child/2, stop_child/3]).
%% Supervisor callback
-export([init/1]).
@@ -45,18 +45,17 @@ start_child(AccSup, ServerOpts) ->
{error, already_present} ->
Address = proplists:get_value(address, ServerOpts),
Port = proplists:get_value(port, ServerOpts),
- Name = id(Address, Port),
- supervisor:delete_child(?MODULE, Name),
+ stop_child(AccSup, Address, Port),
supervisor:start_child(AccSup, Spec);
Reply ->
Reply
end.
-stop_child(Address, Port) ->
+stop_child(AccSup, Address, Port) ->
Name = id(Address, Port),
- case supervisor:terminate_child(?MODULE, Name) of
+ case supervisor:terminate_child(AccSup, Name) of
ok ->
- supervisor:delete_child(?MODULE, Name);
+ supervisor:delete_child(AccSup, Name);
Error ->
Error
end.
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 45fd907383..45c4d52d7e 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -119,8 +119,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
false ->
FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG),
SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg, SecondAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
@@ -130,15 +129,13 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
case length(Algs) =:= 2 of
true ->
SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg, SecondAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
service = "ssh-connection"});
_ ->
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
@@ -187,9 +184,8 @@ handle_userauth_request(#ssh_msg_service_request{name =
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
method = "password",
- data = Data}, _,
+ data = <<?FALSE, ?UINT32(Sz), BinPwd:Sz/binary>>}, _,
#ssh{opts = Opts} = Ssh) ->
- <<_:8, ?UINT32(Sz), BinPwd:Sz/binary>> = Data,
Password = unicode:characters_to_list(BinPwd),
case check_password(User, Password, Opts) of
true ->
@@ -204,6 +200,27 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
+ method = "password",
+ data = <<?TRUE,
+ _/binary
+ %% ?UINT32(Sz1), OldBinPwd:Sz1/binary,
+ %% ?UINT32(Sz2), NewBinPwd:Sz2/binary
+ >>
+ }, _,
+ 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
+ %% has not been changed. Either password changing was not supported,
+ %% or the old password was bad.
+
+ {not_authorized, {User, {error,"Password change not supported"}},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = "",
+ partial_success = false}, Ssh)};
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
method = "none"}, _,
#ssh{userauth_supported_methods = Methods} = Ssh) ->
{not_authorized, {User, undefined},
@@ -256,15 +273,12 @@ handle_userauth_info_request(
data = Data}, IoCb,
#ssh{opts = Opts} = Ssh) ->
PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data),
- Resps = keyboard_interact_get_responses(IoCb, Opts,
+ Responses = keyboard_interact_get_responses(IoCb, Opts,
Name, Instr, PromptInfos),
- RespBin = list_to_binary(
- lists:map(fun(S) -> <<?STRING(list_to_binary(S))>> end,
- Resps)),
{ok,
ssh_transport:ssh_packet(
#ssh_msg_userauth_info_response{num_responses = NumPrompts,
- data = RespBin}, Ssh)}.
+ data = Responses}, Ssh)}.
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
@@ -276,25 +290,16 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{},
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-method_preference(Alg1, Alg2, true) ->
+method_preference(Alg1, Alg2) ->
[{"publickey", ?MODULE, publickey_msg, [Alg1]},
{"publickey", ?MODULE, publickey_msg,[Alg2]},
{"password", ?MODULE, password_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ];
-method_preference(Alg1, Alg2, false) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"publickey", ?MODULE, publickey_msg,[Alg2]},
- {"password", ?MODULE, password_msg, []}
].
-method_preference(Alg1, true) ->
+method_preference(Alg1) ->
[{"publickey", ?MODULE, publickey_msg, [Alg1]},
{"password", ?MODULE, password_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ];
-method_preference(Alg1, false) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"password", ?MODULE, password_msg, []}
].
user_name(Opts) ->
@@ -362,35 +367,29 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
algorithm_string('ssh-rsa') ->
"ssh-rsa";
algorithm_string('ssh-dss') ->
- "ssh-dss".
+ "ssh-dss".
decode_keyboard_interactive_prompts(_NumPrompts, Data) ->
ssh_message:decode_keyboard_interactive_prompts(Data, []).
keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
NumPrompts = length(PromptInfos),
- case proplists:get_value(keyboard_interact_fun, Opts) of
- undefined when NumPrompts == 1 ->
- %% 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, Opts);
- PW -> [PW]
- end;
- undefined ->
- keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
- KbdInteractFun ->
- Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
- PromptInfos),
- case KbdInteractFun(Name, Instr, Prompts) of
- Rs when length(Rs) == NumPrompts ->
- Rs;
- Rs ->
- erlang:error({mismatching_number_of_responses,
- {got,Rs},
- {expected,NumPrompts}})
- end
- end.
+ keyboard_interact_get_responses(proplists:get_value(user_interaction, Opts, true),
+ proplists:get_value(keyboard_interact_fun, Opts),
+ proplists:get_value(password, Opts, undefined), IoCb, Name,
+ Instr, PromptInfos, Opts, NumPrompts).
+
+keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
+ 1) when Password =/= undefined ->
+ [Password]; %% Password auth implemented with keyboard-interaction and passwd is known
+keyboard_interact_get_responses(_, _, _, _, _, _, _, _, 0) ->
+ [""];
+keyboard_interact_get_responses(false, undefined, undefined, _, _, _, [Prompt|_], Opts, _) ->
+ ssh_no_io:read_line(Prompt, Opts); %% Throws error as keyboard interaction is not allowed
+keyboard_interact_get_responses(true, undefined, _,IoCb, Name, Instr, PromptInfos, Opts, _) ->
+ keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
+keyboard_interact_get_responses(true, Fun, _, Name, Instr, PromptInfos, _, _, NumPrompts) ->
+ keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts).
keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
if Name /= "" -> IoCb:format("~s", [Name]);
@@ -404,6 +403,21 @@ keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
end,
Prompts).
+keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
+ Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
+ PromptInfos),
+ case KbdInteractFun(Name, Instr, Prompts) of
+ Rs when length(Rs) == NumPrompts ->
+ Rs;
+ Rs ->
+ throw({mismatching_number_of_responses,
+ {got,Rs},
+ {expected, NumPrompts},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction failed",
+ language = "en"}})
+ end.
+
other_alg('ssh-rsa') ->
'ssh-dss';
other_alg('ssh-dss') ->
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 508ae637cf..5c24f362b1 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -67,7 +67,8 @@
%% Internal application API
-export([cache_create/0, cache_lookup/2, cache_update/2,
cache_delete/1, cache_delete/2, cache_foldl/3,
- cache_find/2]).
+ cache_find/2,
+ get_print_info/1]).
-record(state, {
cm,
@@ -190,6 +191,14 @@ init([Options]) ->
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
+handle_call(get_print_info, _From, State) ->
+ Reply =
+ {{State#state.cm,
+ State#state.channel_id},
+ io_lib:format('CB=~p',[State#state.channel_cb])
+ },
+ {reply, Reply, State};
+
handle_call(Request, From, #state{channel_cb = Module,
channel_state = ChannelState} = State) ->
try Module:handle_call(Request, From, ChannelState) of
@@ -333,6 +342,9 @@ cache_find(ChannelPid, Cache) ->
Channel
end.
+get_print_info(Pid) ->
+ call(Pid, get_print_info, 1000).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 77453e8fd7..de6d246403 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -98,7 +98,7 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
Pty = Pty0#ssh_pty{width = Width, height = Height,
pixel_width = PixWidth,
pixel_height = PixHeight},
- {Chars, NewBuf} = io_request({window_change, Pty0}, Buf, Pty),
+ {Chars, NewBuf} = io_request({window_change, Pty0}, Buf, Pty, undefined),
write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{pty = Pty, buf = NewBuf}};
@@ -188,7 +188,7 @@ handle_msg({Group, tty_geometry}, #state{group = Group,
handle_msg({Group, Req}, #state{group = Group, buf = Buf, pty = Pty,
cm = ConnectionHandler,
channel = ChannelId} = State) ->
- {Chars, NewBuf} = io_request(Req, Buf, Pty),
+ {Chars, NewBuf} = io_request(Req, Buf, Pty, Group),
write_chars(ConnectionHandler, ChannelId, Chars),
{ok, State#state{buf = NewBuf}};
@@ -263,40 +263,49 @@ eval(Error) ->
%%% displaying device...
%%% We are *not* really unicode aware yet, we just filter away characters
%%% beyond the latin1 range. We however handle the unicode binaries...
-io_request({window_change, OldTty}, Buf, Tty) ->
+io_request({window_change, OldTty}, Buf, Tty, _Group) ->
window_change(Tty, OldTty, Buf);
-io_request({put_chars, Cs}, Buf, Tty) ->
+io_request({put_chars, Cs}, Buf, Tty, _Group) ->
put_chars(bin_to_list(Cs), Buf, Tty);
-io_request({put_chars, unicode, Cs}, Buf, Tty) ->
+io_request({put_chars, unicode, Cs}, Buf, Tty, _Group) ->
put_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
-io_request({insert_chars, Cs}, Buf, Tty) ->
+io_request({insert_chars, Cs}, Buf, Tty, _Group) ->
insert_chars(bin_to_list(Cs), Buf, Tty);
-io_request({insert_chars, unicode, Cs}, Buf, Tty) ->
+io_request({insert_chars, unicode, Cs}, Buf, Tty, _Group) ->
insert_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
-io_request({move_rel, N}, Buf, Tty) ->
+io_request({move_rel, N}, Buf, Tty, _Group) ->
move_rel(N, Buf, Tty);
-io_request({delete_chars,N}, Buf, Tty) ->
+io_request({delete_chars,N}, Buf, Tty, _Group) ->
delete_chars(N, Buf, Tty);
-io_request(beep, Buf, _Tty) ->
+io_request(beep, Buf, _Tty, _Group) ->
{[7], Buf};
%% New in R12
-io_request({get_geometry,columns},Buf,Tty) ->
+io_request({get_geometry,columns},Buf,Tty, _Group) ->
{ok, Tty#ssh_pty.width, Buf};
-io_request({get_geometry,rows},Buf,Tty) ->
+io_request({get_geometry,rows},Buf,Tty, _Group) ->
{ok, Tty#ssh_pty.height, Buf};
-io_request({requests,Rs}, Buf, Tty) ->
- io_requests(Rs, Buf, Tty, []);
-io_request(tty_geometry, Buf, Tty) ->
- io_requests([{move_rel, 0}, {put_chars, unicode, [10]}], Buf, Tty, []);
+io_request({requests,Rs}, Buf, Tty, Group) ->
+ io_requests(Rs, Buf, Tty, [], Group);
+io_request(tty_geometry, Buf, Tty, Group) ->
+ io_requests([{move_rel, 0}, {put_chars, unicode, [10]}],
+ Buf, Tty, [], Group);
%{[], Buf};
-io_request(_R, Buf, _Tty) ->
+
+%% New in 18
+io_request({put_chars_sync, Class, Cs, Reply}, Buf, Tty, Group) ->
+ %% We handle these asynchronous for now, if we need output guarantees
+ %% we have to handle these synchronously
+ Group ! {reply, Reply},
+ io_request({put_chars, Class, Cs}, Buf, Tty, Group);
+
+io_request(_R, Buf, _Tty, _Group) ->
{[], Buf}.
-io_requests([R|Rs], Buf, Tty, Acc) ->
- {Chars, NewBuf} = io_request(R, Buf, Tty),
- io_requests(Rs, NewBuf, Tty, [Acc|Chars]);
-io_requests([], Buf, _Tty, Acc) ->
+io_requests([R|Rs], Buf, Tty, Acc, Group) ->
+ {Chars, NewBuf} = io_request(R, Buf, Tty, Group),
+ io_requests(Rs, NewBuf, Tty, [Acc|Chars], Group);
+io_requests([], Buf, _Tty, Acc, _Group) ->
{Acc, Buf}.
%%% return commands for cursor navigation, assume everything is ansi
@@ -457,17 +466,17 @@ bin_to_list(I) when is_integer(I) ->
start_shell(ConnectionHandler, State) ->
Shell = State#state.shell,
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
ShellFun = case is_function(Shell) of
true ->
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
fun() -> Shell(User) end;
{arity, 2} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(User, PeerAddr) end;
_ ->
@@ -485,9 +494,9 @@ start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
State#state{group = Group, buf = empty_buf()};
start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
ShellFun =
case erlang:fun_info(Shell, arity) of
@@ -496,7 +505,7 @@ start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function
{arity, 2} ->
fun() -> Shell(Cmd, User) end;
{arity, 3} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(Cmd, User, PeerAddr) end;
_ ->
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 8421b07167..9307dbbad0 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@
-define(DEFAULT_PACKET_SIZE, 32768).
-define(DEFAULT_WINDOW_SIZE, 2*?DEFAULT_PACKET_SIZE).
-define(DEFAULT_TIMEOUT, 5000).
+-define(MAX_PROTO_VERSION, 255).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index b377614949..f3ff9ae67a 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -107,9 +107,15 @@ shell(ConnectionHandler, ChannelId) ->
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
- ssh_connection_handler:request(ConnectionHandler, self(),
- ChannelId, "subsystem",
- true, [?string(SubSystem)], TimeOut).
+ case ssh_connection_handler:request(ConnectionHandler, self(),
+ ChannelId, "subsystem",
+ true, [?string(SubSystem)], TimeOut) of
+ success -> success;
+ failure -> failure;
+ {error,timeout} -> {error,timeout};
+ _ -> failure
+ end.
+
%%--------------------------------------------------------------------
-spec send(pid(), channel_id(), iodata()) ->
ok | {error, closed}.
@@ -782,9 +788,8 @@ handle_cli_msg(#connection{channel_cache = Cache} = Connection,
erlang:monitor(process, Pid),
Channel = Channel0#channel{user = Pid},
ssh_channel:cache_update(Cache, Channel),
- Reply = {connection_reply,
- channel_success_msg(RemoteId)},
- {{replies, [{channel_data, Pid, Reply0}, Reply]}, Connection};
+ {Reply, Connection1} = reply_msg(Channel, Connection, Reply0),
+ {{replies, [Reply]}, Connection1};
_Other ->
Reply = {connection_reply,
channel_failure_msg(RemoteId)},
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 06866392da..fa107be1b1 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,14 +41,16 @@
global_request/4, send/5, send_eof/2, info/1, info/2,
connection_info/2, channel_info/3,
adjust_window/3, close/2, stop/1, renegotiate/1, renegotiate_data/1,
- start_connection/4]).
+ start_connection/4,
+ get_print_info/1]).
%% gen_fsm callbacks
-export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
- userauth/2, connected/2]).
+ userauth/2, connected/2,
+ error/2]).
-export([init/1, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+ handle_sync_event/4, handle_info/3, terminate/3, format_status/2, code_change/4]).
-record(state, {
role,
@@ -71,7 +73,8 @@
connection_queue,
address,
port,
- opts
+ opts,
+ recbuf
}).
-type state_name() :: hello | kexinit | key_exchange | new_keys | userauth | connection.
@@ -103,22 +106,22 @@ start_connection(client = Role, Socket, Options, Timeout) ->
end;
start_connection(server = Role, Socket, Options, Timeout) ->
+ SSH_Opts = proplists:get_value(ssh_opts, Options, []),
try
- Sups = proplists:get_value(supervisors, Options),
- ConnectionSup = proplists:get_value(connection_sup, Sups),
- Opts = [{supervisors, Sups}, {user_pid, self()} | proplists:get_value(ssh_opts, Options, [])],
- {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
- {_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
- socket_control(Socket, Pid, Callback),
- case proplists:get_value(parallel_login, Opts, false) of
+ case proplists:get_value(parallel_login, SSH_Opts, false) of
true ->
- spawn(fun() ->
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
- end);
+ HandshakerPid =
+ spawn_link(fun() ->
+ receive
+ {do_handshake, Pid} ->
+ handshake(Pid, erlang:monitor(process,Pid), Timeout)
+ end
+ end),
+ ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
+ HandshakerPid ! {do_handshake, ChildPid};
false ->
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
end
catch
exit:{noproc, _} ->
@@ -127,6 +130,16 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{error, Error}
end.
+start_the_connection_child(UserPid, Role, Socket, Options) ->
+ Sups = proplists:get_value(supervisors, Options),
+ ConnectionSup = proplists:get_value(connection_sup, Sups),
+ Opts = [{supervisors, Sups}, {user_pid, UserPid} | proplists:get_value(ssh_opts, Options, [])],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
+ {_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ socket_control(Socket, Pid, Callback),
+ Pid.
+
+
start_link(Role, Socket, Options) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Socket, Options]])}.
@@ -160,9 +173,23 @@ init([Role, Socket, SshOpts]) ->
State#state{ssh_params = Ssh})
catch
_:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error, State0})
+ gen_fsm:enter_loop(?MODULE, [], error, {Error, State})
end.
+%% Temporary fix for the Nessus error. SYN-> <-SYNACK ACK-> RST-> ?
+error(_Event, {Error,State=#state{}}) ->
+ case Error of
+ {badmatch,{error,enotconn}} ->
+ %% {error,enotconn} probably from inet:peername in
+ %% init_ssh(server,..)/5 called from init/1
+ {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}, State};
+ _ ->
+ {stop, {shutdown,{init,Error}}, State}
+ end;
+error(Event, State) ->
+ %% State deliberately not checked beeing #state. This is a panic-clause...
+ {stop, {shutdown,{init,{spurious_error,Event}}}, State}.
+
%%--------------------------------------------------------------------
-spec open_channel(pid(), string(), iodata(), integer(), integer(),
timeout()) -> {open, channel_id()} | {error, term()}.
@@ -229,6 +256,9 @@ send_eof(ConnectionHandler, ChannelId) ->
%%--------------------------------------------------------------------
-spec connection_info(pid(), [atom()]) -> proplists:proplist().
%%--------------------------------------------------------------------
+get_print_info(ConnectionHandler) ->
+ sync_send_all_state_event(ConnectionHandler, get_print_info, 1000).
+
connection_info(ConnectionHandler, Options) ->
sync_send_all_state_event(ConnectionHandler, {connection_info, Options}).
@@ -291,28 +321,39 @@ info(ConnectionHandler, ChannelProcess) ->
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- inet:setopts(Socket, [{packet, line}, {active, once}]),
- {next_state, hello, State};
+ {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]),
+ inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
+ {next_state, hello, State#state{recbuf = Size}};
-hello({info_line, _Line},#state{socket = Socket} = State) ->
+hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
+ %% The server may send info lines before the version_exchange
inet:setopts(Socket, [{active, once}]),
{next_state, hello, State};
+hello({info_line, _Line},#state{role = server} = State) ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code =
+ ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Did not receive expected protocol version exchange",
+ language = "en"},
+ handle_disconnect(DisconnectMsg, State);
+
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
- socket = Socket} = State) ->
+ socket = Socket,
+ recbuf = Size} = State) ->
{NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
case handle_version(NumVsn, StrVsn, Ssh0) of
{ok, Ssh1} ->
- inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}]),
+ inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}, {recbuf, Size}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
send_msg(SshPacket, State),
{next_state, kexinit, next_packet(State#state{ssh_params = Ssh,
key_exchange_init_msg =
KeyInitMsg})};
not_supported ->
- DisconnectMsg =
+ DisconnectMsg =
#ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
description = "Protocol version " ++ StrVsn
++ " not supported",
language = "en"},
@@ -528,7 +569,7 @@ connected({#ssh_msg_kexinit{}, _Payload} = Event, State) ->
%%--------------------------------------------------------------------
handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName, #state{} = State) ->
- handle_disconnect(DisconnectMsg, State),
+ handle_disconnect(peer, DisconnectMsg, State),
{stop, {shutdown, Desc}, State};
handle_event(#ssh_msg_ignore{}, StateName, State) ->
@@ -736,6 +777,20 @@ handle_sync_event({recv_window, ChannelId}, _From, StateName,
end,
{reply, Reply, StateName, next_packet(State)};
+handle_sync_event(get_print_info, _From, StateName, State) ->
+ Reply =
+ try
+ {inet:sockname(State#state.socket),
+ inet:peername(State#state.socket)
+ }
+ of
+ {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])};
+ _ -> {{"-",0},"-"}
+ catch
+ _:_ -> {{"?",0},"?"}
+ end,
+ {reply, Reply, StateName, State};
+
handle_sync_event({connection_info, Options}, _From, StateName, State) ->
Info = ssh_info(Options, State, []),
{reply, Info, StateName, State};
@@ -914,6 +969,10 @@ terminate(normal, _, #state{transport_cb = Transport,
(catch Transport:close(Socket)),
ok;
+terminate({shutdown,{init,Reason}}, StateName, State) ->
+ error_logger:info_report(io_lib:format("Erlang ssh in connection handler init: ~p~n",[Reason])),
+ terminate(normal, StateName, State);
+
%% Terminated by supervisor
terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
DisconnectMsg =
@@ -929,8 +988,10 @@ terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName,
{SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh});
+
terminate({shutdown, _}, StateName, State) ->
terminate(normal, StateName, State);
+
terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
connection_state = Connection} = State) ->
terminate_subsytem(Connection),
@@ -943,12 +1004,43 @@ terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh}).
+
terminate_subsytem(#connection{system_supervisor = SysSup,
sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) ->
ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
terminate_subsytem(_) ->
ok.
+format_status(normal, [_, State]) ->
+ [{data, [{"StateData", State}]}];
+format_status(terminate, [_, State]) ->
+ SshParams0 = (State#state.ssh_params),
+ SshParams = SshParams0#ssh{c_keyinit = "***",
+ s_keyinit = "***",
+ send_mac_key = "***",
+ send_mac_size = "***",
+ recv_mac_key = "***",
+ recv_mac_size = "***",
+ encrypt_keys = "***",
+ encrypt_ctx = "***",
+ decrypt_keys = "***",
+ decrypt_ctx = "***",
+ compress_ctx = "***",
+ decompress_ctx = "***",
+ shared_secret = "***",
+ exchanged_hash = "***",
+ session_id = "***",
+ keyex_key = "***",
+ keyex_info = "***",
+ available_host_keys = "***"},
+ [{data, [{"StateData", State#state{decoded_data_buffer = "***",
+ encoded_data_buffer = "***",
+ key_exchange_init_msg = "***",
+ opts = "***",
+ recbuf = "***",
+ ssh_params = SshParams
+ }}]}].
+
%%--------------------------------------------------------------------
-spec code_change(OldVsn::term(), state_name(), Oldstate::term(), Extra::term()) ->
{ok, state_name(), #state{}}.
@@ -1109,7 +1201,10 @@ send_all_state_event(FsmPid, Event) ->
gen_fsm:send_all_state_event(FsmPid, Event).
sync_send_all_state_event(FsmPid, Event) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
+ sync_send_all_state_event(FsmPid, Event, infinity).
+
+sync_send_all_state_event(FsmPid, Event, Timeout) ->
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
catch
exit:{noproc, _} ->
{error, closed};
@@ -1206,13 +1301,23 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
generate_event(Msg, StateName, State0, EncData) ->
Event = ssh_message:decode(Msg),
State = generate_event_new_state(State0, EncData),
- case Event of
- #ssh_msg_kexinit{} ->
- %% We need payload for verification later.
- event({Event, Msg}, StateName, State);
- _ ->
- event(Event, StateName, State)
- end.
+ try
+ case Event of
+ #ssh_msg_kexinit{} ->
+ %% We need payload for verification later.
+ event({Event, Msg}, StateName, State);
+ _ ->
+ event(Event, StateName, State)
+ end
+ catch
+ _:_ ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Encountered unexpected input",
+ language = "en"},
+ handle_disconnect(DisconnectMsg, State)
+ end.
+
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
@@ -1390,17 +1495,27 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
handle_disconnect(DisconnectMsg, State0)
end.
-handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
- role = Role} = State0) ->
+handle_disconnect(DisconnectMsg, State) ->
+ handle_disconnect(own, DisconnectMsg, State).
+
+handle_disconnect(#ssh_msg_disconnect{} = DisconnectMsg, State, Error) ->
+ handle_disconnect(own, DisconnectMsg, State, Error);
+handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, role = Role} = State0) ->
{disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(Replies, State0),
+ State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
{stop, {shutdown, Desc}, State#state{connection_state = Connection}}.
-handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
- role = Role} = State0, ErrorMsg) ->
+
+handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
+ role = Role} = State0, ErrorMsg) ->
{disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(Replies, State0),
+ State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
{stop, {shutdown, {Desc, ErrorMsg}}, State#state{connection_state = Connection}}.
+disconnect_replies(own, Msg, Replies) ->
+ [{connection_reply, Msg} | Replies];
+disconnect_replies(peer, _, Replies) ->
+ Replies.
+
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
new file mode 100644
index 0000000000..9ed598b3ab
--- /dev/null
+++ b/lib/ssh/src/ssh_info.erl
@@ -0,0 +1,193 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Print some info of a running ssh aplication.
+%%----------------------------------------------------------------------
+
+-module(ssh_info).
+
+-compile(export_all).
+
+print() ->
+ try supervisor:which_children(ssh_sup)
+ of
+ _ ->
+ io:nl(),
+ print_general(),
+ io:nl(),
+ underline("Client part", $=),
+ print_clients(),
+ io:nl(),
+ underline("Server part", $=),
+ print_servers(),
+ io:nl(),
+ %% case os:type() of
+ %% {unix,_} ->
+ %% io:nl(),
+ %% underline("Linux part", $=),
+ %% underline("Listening"),
+ %% catch io:format(os:cmd("netstat -tpln")),
+ %% io:nl(),
+ %% underline("Other"),
+ %% catch io:format(os:cmd("netstat -tpn"));
+ %% _ -> ok
+ %% end,
+ underline("Supervisors", $=),
+ walk_sups(ssh_sup),
+ io:nl()
+ catch
+ _:_ ->
+ io:format("Ssh not found~n",[])
+ end.
+
+%%%================================================================
+print_general() ->
+ {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()),
+ underline(io_lib:format("~s ~s", [Slogan, Ver]), $=),
+ io:format('This printout is generated ~s. ~n',[datetime()]).
+
+%%%================================================================
+print_clients() ->
+ try
+ lists:foreach(fun print_client/1, supervisor:which_children(sshc_sup))
+ catch
+ C:E ->
+ io:format('***FAILED: ~p:~p~n',[C,E])
+ end.
+
+print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
+ {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid),
+ io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+print_client(Other) ->
+ io:format(" [[Other 1: ~p]]~n",[Other]).
+
+
+%%%================================================================
+print_servers() ->
+ try
+ lists:foreach(fun print_server/1, supervisor:which_children(sshd_sup))
+ catch
+ C:E ->
+ io:format('***FAILED: ~p:~p~n',[C,E])
+ end.
+
+print_server({{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
+ io:format('Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}),
+ ssh_acceptor:number_of_connections(Pid)]),
+ lists:foreach(fun print_system_sup/1, supervisor:which_children(Pid));
+print_server(Other) ->
+ io:format(" [[Other 2: ~p]]~n",[Other]).
+
+print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref),
+ is_pid(Pid) ->
+ lists:foreach(fun print_channels/1, supervisor:which_children(Pid));
+print_system_sup({{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) ->
+ io:format(" [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]);
+print_system_sup(Other) ->
+ io:format(" [[Other 3: ~p]]~n",[Other]).
+
+print_channels({{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
+ lists:foreach(fun print_channel/1, supervisor:which_children(Pid));
+print_channels(Other) ->
+ io:format(" [[Other 4: ~p]]~n",[Other]).
+
+
+print_channel({Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref),
+ is_pid(Pid) ->
+ {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid),
+ {{Local,Remote},StrM} = ssh_connection_handler:get_print_info(ConnManager),
+ io:format(' ch ~p: ~s ~s',[ChannelID, StrM, Str]),
+ io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+print_channel(Other) ->
+ io:format(" [[Other 5: ~p]]~n",[Other]).
+
+%%%================================================================
+-define(inc(N), (N+4)).
+
+walk_sups(StartPid) ->
+ io:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]),
+ walk_sups(children(StartPid), _Indent=?inc(0)).
+
+walk_sups([H={_,Pid,SupOrWorker,_}|T], Indent) ->
+ indent(Indent), io:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]),
+ case SupOrWorker of
+ supervisor -> walk_sups(children(Pid), ?inc(Indent));
+ _ -> ok
+ end,
+ walk_sups(T, Indent);
+walk_sups([], _) ->
+ ok.
+
+dead_or_alive(Name) when is_atom(Name) ->
+ case whereis(Name) of
+ undefined ->
+ "**UNDEFINED**";
+ Pid ->
+ dead_or_alive(Pid)
+ end;
+dead_or_alive(Pid) when is_pid(Pid) ->
+ case process_info(Pid) of
+ undefined -> "**DEAD**";
+ _ -> "alive"
+ end.
+
+indent(I) -> io:format('~*c',[I,$ ]).
+
+children(Pid) ->
+ Parent = self(),
+ Helper = spawn(fun() ->
+ Parent ! {self(),supervisor:which_children(Pid)}
+ end),
+ receive
+ {Helper,L} when is_list(L) ->
+ L
+ after
+ 2000 ->
+ catch exit(Helper, kill),
+ []
+ end.
+
+%%%================================================================
+underline(Str) ->
+ underline(Str, $-).
+
+underline(Str, LineChar) ->
+ Len = lists:flatlength(Str),
+ io:format('~s~n',[Str]),
+ line(Len,LineChar).
+
+line(Len, Char) ->
+ io:format('~*c~n', [Len,Char]).
+
+
+datetime() ->
+ {{YYYY,MM,DD}, {H,M,S}} = calendar:now_to_universal_time(now()),
+ lists:flatten(io_lib:format('~4w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w UTC',[YYYY,MM,DD, H,M,S])).
+
+
+fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]);
+fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]).
+
+
+
+nyi() ->
+ io:format('Not yet implemented~n',[]),
+ nyi.
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 35336bce8b..97e2dee27a 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -73,7 +73,9 @@ read_password(Prompt, Ssh) ->
listify(A) when is_atom(A) ->
atom_to_list(A);
listify(L) when is_list(L) ->
- L.
+ L;
+listify(B) when is_binary(B) ->
+ binary_to_list(B).
format(Fmt, Args) ->
io:format(Fmt, Args).
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 8d6c77c0ed..66e7717095 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -162,8 +162,15 @@ encode(#ssh_msg_userauth_info_request{
encode(#ssh_msg_userauth_info_response{
num_responses = Num,
data = Data}) ->
- ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_RESPONSE, Num, Data],
- [byte, uint32, '...']);
+ Responses = lists:map(fun("") ->
+ <<>>;
+ (Response) ->
+ ssh_bits:encode([Response], [string])
+ end, Data),
+ Start = ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_RESPONSE, Num],
+ [byte, uint32]),
+ iolist_to_binary([Start, Responses]);
+
encode(#ssh_msg_disconnect{
code = Code,
description = Desc,
@@ -255,7 +262,7 @@ encode(#ssh_msg_ignore{data = Data}) ->
ssh_bits:encode([?SSH_MSG_IGNORE, Data], [byte, string]);
encode(#ssh_msg_unimplemented{sequence = Seq}) ->
- ssh_bits:encode([?SSH_MSG_IGNORE, Seq], [byte, uint32]);
+ ssh_bits:encode([?SSH_MSG_UNIMPLEMENTED, Seq], [byte, uint32]);
encode(#ssh_msg_debug{always_display = Bool,
message = Msg,
@@ -391,13 +398,6 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
data = Data};
%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
-decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
- #ssh_msg_userauth_pk_ok{
- algorithm_name = Alg,
- key_blob = KeyBlob
- };
-
-%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
?UINT32(Len1), Lang:Len1/binary>>) ->
#ssh_msg_userauth_passwd_changereq{
@@ -405,6 +405,13 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/b
languge = Lang
};
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
+ #ssh_msg_userauth_pk_ok{
+ algorithm_name = Alg,
+ key_blob = KeyBlob
+ };
+
decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
#ssh_msg_userauth_info_response{
num_responses = Num,
@@ -461,10 +468,19 @@ decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
language = Lang
};
+%% Accept bad disconnects from ancient openssh clients that doesn't send language tag. Use english as a work-around.
+decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
+ ?UINT32(Len0), Desc:Len0/binary>>) ->
+ #ssh_msg_disconnect{
+ code = Code,
+ description = unicode:characters_to_list(Desc),
+ language = <<"en">>
+ };
+
decode(<<?SSH_MSG_NEWKEYS>>) ->
#ssh_msg_newkeys{};
-decode(<<?BYTE(?SSH_MSG_IGNORE), Data/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_IGNORE), ?UINT32(Len), Data:Len/binary>>) ->
#ssh_msg_ignore{data = Data};
decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) ->
@@ -489,6 +505,11 @@ erl_boolean(1) ->
decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) ->
list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
+decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) ->
+ %% The mandatory trailing UINT32 is missing. Assume the value it anyhow must have
+ %% See rfc 4253 7.1
+ X = 0,
+ list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) ->
Names = string:tokens(unicode:characters_to_list(Data), ","),
decode_kex_init(Rest, [Names | Acc], N -1).
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 0ea2366ac7..721146c509 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,7 +57,8 @@
rep_buf = <<>>,
req_id,
req_list = [], %% {ReqId, Fun}
- inf %% list of fileinf
+ inf, %% list of fileinf,
+ opts
}).
-record(fileinf,
@@ -85,10 +86,11 @@ start_channel(Host) when is_list(Host) ->
start_channel(Host, []).
start_channel(Cm, Opts) when is_pid(Cm) ->
Timeout = proplists:get_value(timeout, Opts, infinity),
+ {_, SftpOpts} = handle_options(Opts, [], []),
case ssh_xfer:attach(Cm, []) of
{ok, ChannelId, Cm} ->
case ssh_channel:start(Cm, ChannelId,
- ?MODULE, [Cm, ChannelId, Timeout]) of
+ ?MODULE, [Cm, ChannelId, SftpOpts]) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -108,11 +110,12 @@ start_channel(Cm, Opts) when is_pid(Cm) ->
start_channel(Host, Opts) ->
start_channel(Host, 22, Opts).
start_channel(Host, Port, Opts) ->
- Timeout = proplists:get_value(timeout, Opts, infinity),
- case ssh_xfer:connect(Host, Port, proplists:delete(timeout, Opts)) of
+ {SshOpts, SftpOpts} = handle_options(Opts, [], []),
+ Timeout = proplists:get_value(timeout, SftpOpts, infinity),
+ case ssh_xfer:connect(Host, Port, SshOpts) of
{ok, ChannelId, Cm} ->
case ssh_channel:start(Cm, ChannelId, ?MODULE, [Cm,
- ChannelId, Timeout]) of
+ ChannelId, SftpOpts]) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -392,7 +395,8 @@ write_file_loop(Pid, Handle, Pos, Bin, Remain, PacketSz, FileOpTimeout) ->
%%
%% Description:
%%--------------------------------------------------------------------
-init([Cm, ChannelId, Timeout]) ->
+init([Cm, ChannelId, Options]) ->
+ Timeout = proplists:get_value(timeout, Options, infinity),
erlang:monitor(process, Cm),
case ssh_connection:subsystem(Cm, ChannelId, "sftp", Timeout) of
success ->
@@ -401,7 +405,8 @@ init([Cm, ChannelId, Timeout]) ->
{ok, #state{xf = Xf,
req_id = 0,
rep_buf = <<>>,
- inf = new_inf()}};
+ inf = new_inf(),
+ opts = Options}};
failure ->
{stop, "server failed to start sftp subsystem"};
Error ->
@@ -707,8 +712,9 @@ handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State0) ->
%%
%% Description: Handles channel messages
%%--------------------------------------------------------------------
-handle_msg({ssh_channel_up, _, _}, #state{xf = Xf} = State) ->
- ssh_xfer:protocol_version_request(Xf),
+handle_msg({ssh_channel_up, _, _}, #state{opts = Options, xf = Xf} = State) ->
+ Version = proplists:get_value(sftp_vsn, Options, ?SSH_SFTP_PROTOCOL_VERSION),
+ ssh_xfer:protocol_version_request(Xf, Version),
{ok, State};
%% Version negotiation timed out
@@ -754,6 +760,15 @@ terminate(_Reason, State) ->
%%====================================================================
%% Internal functions
%%====================================================================
+handle_options([], Sftp, Ssh) ->
+ {Ssh, Sftp};
+handle_options([{timeout, _} = Opt | Rest], Sftp, Ssh) ->
+ handle_options(Rest, [Opt | Sftp], Ssh);
+handle_options([{sftp_vsn, _} = Opt| Rest], Sftp, Ssh) ->
+ handle_options(Rest, [Opt | Sftp], Ssh);
+handle_options([Opt | Rest], Sftp, Ssh) ->
+ handle_options(Rest, Sftp, [Opt | Ssh]).
+
call(Pid, Msg, TimeOut) ->
ssh_channel:call(Pid, {{timeout, TimeOut}, Msg}, infinity).
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 848133f838..660fe8bb65 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -173,8 +173,8 @@ ssh_acceptor_sup([_ | Rest]) ->
ssh_acceptor_sup(Rest).
stop_acceptor(Sup) ->
- [Name] =
- [SupName || {SupName, _, _, [ssh_acceptor_sup]} <-
+ [{Name, AcceptorSup}] =
+ [{SupName, ASup} || {SupName, ASup, _, [ssh_acceptor_sup]} <-
supervisor:which_children(Sup)],
- supervisor:terminate_child(Sup, Name).
+ supervisor:terminate_child(AcceptorSup, Name).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 27723dc870..76fa776113 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -82,16 +82,21 @@ format_version({Major,Minor}) ->
integer_to_list(Minor) ++ "-Erlang".
handle_hello_version(Version) ->
- StrVersion = trim_tail(Version),
- case string:tokens(Version, "-") of
- [_, "2.0" | _] ->
- {{2,0}, StrVersion};
- [_, "1.99" | _] ->
- {{2,0}, StrVersion};
- [_, "1.3" | _] ->
- {{1,3}, StrVersion};
- [_, "1.5" | _] ->
- {{1,5}, StrVersion}
+ try
+ StrVersion = trim_tail(Version),
+ case string:tokens(Version, "-") of
+ [_, "2.0" | _] ->
+ {{2,0}, StrVersion};
+ [_, "1.99" | _] ->
+ {{2,0}, StrVersion};
+ [_, "1.3" | _] ->
+ {{1,3}, StrVersion};
+ [_, "1.5" | _] ->
+ {{1,5}, StrVersion}
+ end
+ catch
+ error:_ ->
+ {undefined, "unknown version"}
end.
key_exchange_init_msg(Ssh0) ->
@@ -113,15 +118,28 @@ key_init(client, Ssh, Value) ->
key_init(server, Ssh, Value) ->
Ssh#ssh{s_keyinit = Value}.
+available_ssh_algos() ->
+ Supports = crypto:supports(),
+ CipherAlgos = [{aes_ctr, "aes128-ctr"}, {aes_cbc128, "aes128-cbc"}, {des3_cbc, "3des-cbc"}],
+ Ciphers = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- CipherAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
+ HashAlgos = [{sha256, "hmac-sha2-256"}, {sha, "hmac-sha1"}],
+ Hashs = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- HashAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
+ {Ciphers, Hashs}.
+
kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -129,14 +147,15 @@ kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
};
kexinit_messsage(server, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -636,7 +655,21 @@ encrypt_init(#ssh{encrypt = 'aes128-cbc', role = server} = Ssh) ->
<<K:16/binary>> = hash(Ssh, "D", 128),
{ok, Ssh#ssh{encrypt_keys = K,
encrypt_block_size = 16,
- encrypt_ctx = IV}}.
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}}.
encrypt_final(Ssh) ->
{ok, Ssh#ssh{encrypt = none,
@@ -658,7 +691,11 @@ encrypt(#ssh{encrypt = 'aes128-cbc',
encrypt_ctx = IV0} = Ssh, Data) ->
Enc = crypto:block_encrypt(aes_cbc128, K,IV0,Data),
IV = crypto:next_iv(aes_cbc, Enc),
- {Ssh#ssh{encrypt_ctx = IV}, Enc}.
+ {Ssh#ssh{encrypt_ctx = IV}, Enc};
+encrypt(#ssh{encrypt = 'aes128-ctr',
+ encrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_encrypt(State0,Data),
+ {Ssh#ssh{encrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -690,7 +727,21 @@ decrypt_init(#ssh{decrypt = 'aes128-cbc', role = server} = Ssh) ->
hash(Ssh, "C", 128)},
<<K:16/binary>> = KD,
{ok, Ssh#ssh{decrypt_keys = K, decrypt_ctx = IV,
- decrypt_block_size = 16}}.
+ decrypt_block_size = 16}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}}.
decrypt_final(Ssh) ->
@@ -711,7 +762,11 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key,
decrypt_ctx = IV0} = Ssh, Data) ->
Dec = crypto:block_decrypt(aes_cbc128, Key,IV0,Data),
IV = crypto:next_iv(aes_cbc, Data),
- {Ssh#ssh{decrypt_ctx = IV}, Dec}.
+ {Ssh#ssh{decrypt_ctx = IV}, Dec};
+decrypt(#ssh{decrypt = 'aes128-ctr',
+ decrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_decrypt(State0,Data),
+ {Ssh#ssh{decrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
@@ -846,7 +901,9 @@ mac('hmac-sha1-96', Key, SeqNum, Data) ->
mac('hmac-md5', Key, SeqNum, Data) ->
crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-md5-96', Key, SeqNum, Data) ->
- crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96')).
+ crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96'));
+mac('hmac-sha2-256', Key, SeqNum, Data) ->
+ crypto:hmac(sha256, Key, [<<?UINT32(SeqNum)>>, Data]).
%% return N hash bytes (HASH)
hash(SSH, Char, Bits) ->
@@ -911,12 +968,14 @@ mac_key_size('hmac-sha1') -> 20*8;
mac_key_size('hmac-sha1-96') -> 20*8;
mac_key_size('hmac-md5') -> 16*8;
mac_key_size('hmac-md5-96') -> 16*8;
+mac_key_size('hmac-sha2-256')-> 32*8;
mac_key_size(none) -> 0.
mac_digest_size('hmac-sha1') -> 20;
mac_digest_size('hmac-sha1-96') -> 12;
mac_digest_size('hmac-md5') -> 20;
mac_digest_size('hmac-md5-96') -> 12;
+mac_digest_size('hmac-sha2-256') -> 32;
mac_digest_size(none) -> 0.
peer_name({Host, _}) ->
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index 63d01fd9de..1881392db8 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
rename/5, remove/3, mkdir/4, rmdir/3, realpath/3, extended/4,
stat/4, fstat/4, lstat/4, setstat/4,
readlink/3, fsetstat/4, symlink/4,
- protocol_version_request/1,
+ protocol_version_request/2,
xf_reply/2,
xf_send_reply/3, xf_send_names/3, xf_send_name/4,
xf_send_status/3, xf_send_status/4, xf_send_status/5,
@@ -67,8 +67,8 @@ open_xfer(CM, Opts) ->
Error
end.
-protocol_version_request(XF) ->
- xf_request(XF, ?SSH_FXP_INIT, <<?UINT32(?SSH_SFTP_PROTOCOL_VERSION)>>).
+protocol_version_request(XF, Version) ->
+ xf_request(XF, ?SSH_FXP_INIT, <<?UINT32(Version)>>).
open(XF, ReqID, FileName, Access, Flags, Attrs) ->
Vsn = XF#ssh_xfer.vsn,