aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src/ssh.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src/ssh.erl')
-rw-r--r--lib/ssh/src/ssh.erl261
1 files changed, 99 insertions, 162 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index e5c016eb3f..2685b1553b 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2013. 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
@@ -31,41 +31,36 @@
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
--deprecated({sign_data, 2, next_major_release}).
--deprecated({verify_data, 3, next_major_release}).
-
--export([sign_data/2, verify_data/3]).
-
%%--------------------------------------------------------------------
-%% Function: start([, Type]) -> ok
-%%
-%% Type = permanent | transient | temporary
+-spec start() -> ok.
+-spec start(permanent | transient | temporary) -> ok.
%%
-%% Description: Starts the inets application. Default type
+%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
%%--------------------------------------------------------------------
start() ->
+ application:start(crypto),
+ application:start(asn1),
+ application:start(public_key),
application:start(ssh).
start(Type) ->
+ application:start(crypto, Type),
+ application:start(asn1),
+ application:start(public_key, Type),
application:start(ssh, Type).
%%--------------------------------------------------------------------
-%% Function: stop() -> ok
+-spec stop() -> ok.
%%
-%% Description: Stops the inets application.
+%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
stop() ->
application:stop(ssh).
%%--------------------------------------------------------------------
-%% Function: connect(Host, Port, Options) ->
-%% connect(Host, Port, Options, Timeout -> ConnectionRef | {error, Reason}
-%%
-%% Host - string()
-%% Port - integer()
-%% Options - [{Option, Value}]
-%% Timeout - infinity | integer().
+-spec connect(string(), integer(), proplists:proplists()) -> {ok, pid()} | {error, term()}.
+-spec connect(string(), integer(), proplists:proplists(), timeout()) -> {ok, pid()} | {error, term()}.
%%
%% Description: Starts an ssh connection.
%%--------------------------------------------------------------------
@@ -76,83 +71,52 @@ connect(Host, Port, Options, Timeout) ->
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions} ->
- DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false),
- Inet = inetopt(DisableIpv6),
- do_connect(Host, Port, [Inet | SocketOptions],
- [{user_pid, self()}, {host, Host} | SshOptions], Timeout, DisableIpv6)
+ {_, Transport, _} = TransportOpts =
+ proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ Inet = proplists:get_value(inet, SshOptions, inet),
+ try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], Timeout) of
+ {ok, Socket} ->
+ Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
+ ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{function_clause, _} ->
+ {error, {options, {transport, TransportOpts}}};
+ exit:badarg ->
+ {error, {options, {socket_options, SocketOptions}}}
+ end
end.
-do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
- try sshc_sup:start_child([[{address, Host}, {port, Port},
- {role, client},
- {channel_pid, self()},
- {socket_opts, SocketOptions},
- {ssh_opts, SshOptions}]]) of
- {ok, ConnectionSup} ->
- {ok, Manager} =
- ssh_connection_sup:connection_manager(ConnectionSup),
- 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
+-spec close(pid()) -> ok.
%%
%% Description: Closes an ssh connection.
%%--------------------------------------------------------------------
close(ConnectionRef) ->
- ssh_connection_manager:stop(ConnectionRef).
+ ssh_connection_handler:stop(ConnectionRef).
%%--------------------------------------------------------------------
-%% Function: connection_info(ConnectionRef) -> [{Option, Value}]
+-spec connection_info(pid(), [atom()]) -> [{atom(), term()}].
%%
%% Description: Retrieves information about a connection.
%%--------------------------------------------------------------------
connection_info(ConnectionRef, Options) ->
- ssh_connection_manager:connection_info(ConnectionRef, Options).
+ ssh_connection_handler:connection_info(ConnectionRef, Options).
%%--------------------------------------------------------------------
-%% Function: channel_info(ConnectionRef) -> [{Option, Value}]
+-spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}].
%%
%% Description: Retrieves information about a connection.
%%--------------------------------------------------------------------
channel_info(ConnectionRef, ChannelId, Options) ->
- ssh_connection_manager:channel_info(ConnectionRef, ChannelId, Options).
+ ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options).
%%--------------------------------------------------------------------
-%% Function: daemon(Port) ->
-%% daemon(Port, Options) ->
-%% daemon(Address, Port, Options) -> SshSystemRef
-%%
+-spec daemon(integer()) -> {ok, pid()}.
+-spec daemon(integer(), proplists:proplist()) -> {ok, pid()}.
+-spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()}.
+
%% Description: Starts a server listening for SSH connections
%% on the given port.
%%--------------------------------------------------------------------
@@ -169,11 +133,11 @@ daemon(HostAddr, Port, Options0) ->
_ ->
Options0
end,
- DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false),
+
{Host, Inet, Options} = case HostAddr of
any ->
{ok, Host0} = inet:gethostname(),
- {Host0, inetopt(DisableIpv6), Options1};
+ {Host0, proplists:get_value(inet, Options1, inet), Options1};
{_,_,_,_} ->
{HostAddr, inet,
[{ip, HostAddr} | Options1]};
@@ -184,9 +148,8 @@ daemon(HostAddr, Port, Options0) ->
start_daemon(Host, Port, Options, Inet).
%%--------------------------------------------------------------------
-%% Function: stop_listener(SysRef) -> ok
-%% stop_listener(Address, Port) -> ok
-%%
+-spec stop_listener(pid()) -> ok.
+-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
@@ -197,9 +160,8 @@ stop_listener(Address, Port) ->
ssh_system_sup:stop_listener(Address, Port).
%%--------------------------------------------------------------------
-%% Function: stop_daemon(SysRef) -> ok
-%%% stop_daemon(Address, Port) -> ok
-%%
+-spec stop_daemon(pid()) -> ok.
+-spec stop_daemon(inet:ip_address(), integer()) -> ok.
%%
%% Description: Stops the listener and all connections started by
%% the listener.
@@ -210,9 +172,10 @@ stop_daemon(Address, Port) ->
ssh_system_sup:stop_system(Address, Port).
%%--------------------------------------------------------------------
-%% Function: shell(Host [,Port,Options]) -> {ok, ConnectionRef} |
-%% {error, Reason}
-%%
+-spec shell(string()) -> _.
+-spec shell(string(), proplists:proplist()) -> _.
+-spec shell(string(), integer(), proplists:proplist()) -> _.
+
%% Host = string()
%% Port = integer()
%% Options = [{Option, Value}]
@@ -246,6 +209,13 @@ shell(Host, Port, Options) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+fix_idle_time(SshOptions) ->
+ case proplists:get_value(idle_time, SshOptions) of
+ undefined ->
+ [{idle_time, infinity}|SshOptions];
+ _ ->
+ SshOptions
+ end.
start_daemon(Host, Port, Options, Inet) ->
case handle_options(Options) of
{error, _Reason} = Error ->
@@ -257,7 +227,7 @@ start_daemon(Host, Port, Options, Inet) ->
do_start_daemon(Host, Port, Options, SocketOptions) ->
case ssh_system_sup:system_supervisor(Host, Port) of
undefined ->
- %% TODO: It would proably make more sense to call the
+ %% It would proably make more sense to call the
%% address option host but that is a too big change at the
%% monent. The name is a legacy name!
try sshd_sup:start_child([{address, Host},
@@ -267,7 +237,9 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
{ok, SysSup} ->
{ok, SysSup};
{error, {already_started, _}} ->
- {error, eaddrinuse}
+ {error, eaddrinuse};
+ {error, R} ->
+ {error, R}
catch
exit:{noproc, _} ->
{error, ssh_not_started}
@@ -318,8 +290,6 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -337,7 +307,10 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) ->
+%%Backwards compatibility should not be underscore between ip and v6 in API
+handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]);
+handle_option([{ipv6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
@@ -355,6 +328,10 @@ handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOption
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([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{rekey_limit, _} = 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).
@@ -364,16 +341,20 @@ handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Value == false ->
+handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false ->
+handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa ->
+handle_ssh_option({public_key_alg, ssh_dsa}) ->
+ {public_key_alg, 'ssh-dss'};
+handle_ssh_option({public_key_alg, ssh_rsa}) ->
+ {public_key_alg, 'ssh-rsa'};
+handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' ->
Opt;
handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 ->
- case check_pref_algs(Value) of
- true ->
- Opt;
+ case handle_pref_algs(Value, []) of
+ {true, NewOpts} ->
+ NewOpts;
_ ->
throw({error, {eoptions, Opt}})
end;
@@ -391,8 +372,6 @@ handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)->
Opt;
handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) ->
- Opt;
handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
@@ -411,8 +390,9 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_boolean(Value) ->
- Opt;
+
+handle_ssh_option({ipv6_disabled, Value} = Opt) when is_boolean(Value) ->
+ throw({error, {{ipv6_disabled, Opt}, option_no_longer_valid_use_inet_option_instead}});
handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol),
is_atom(Cb),
is_atom(ClosTag) ->
@@ -421,13 +401,18 @@ handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({ssh_cli, {Cb, _}}= Opt) when is_atom(Cb) ->
Opt;
+handle_ssh_option({ssh_cli, no_cli} = Opt) ->
+ Opt;
handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
Opt;
handle_ssh_option({shell, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({quiet_mode, Value} = Opt) when Value == true;
- Value == false ->
+handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) ->
+ Opt;
+handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 ->
+ Opt;
+handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) ->
Opt;
handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
@@ -436,10 +421,8 @@ handle_inet_option({active, _} = Opt) ->
throw({error, {{eoptions, Opt}, "Ssh has built in flow control, "
"and activ is handled internaly user is not allowd"
"to specify this option"}});
-handle_inet_option({inet, _} = Opt) ->
- throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to"
- " enforce iv4 in the server, client will fallback to ipv4 if"
- " it can not use ipv6"}});
+handle_inet_option({inet, Value} = Opt) when (Value == inet) or (Value == inet6) ->
+ Opt;
handle_inet_option({reuseaddr, _} = Opt) ->
throw({error, {{eoptions, Opt},"Is set internaly user is not allowd"
"to specify this option"}});
@@ -447,64 +430,18 @@ handle_inet_option({reuseaddr, _} = Opt) ->
handle_inet_option(Opt) ->
Opt.
%% Check preferred algs
-check_pref_algs([]) ->
- true;
-check_pref_algs([H|T]) ->
+handle_pref_algs([], Acc) ->
+ {true, lists:reverse(Acc)};
+handle_pref_algs([H|T], Acc) ->
case H of
ssh_dsa ->
- check_pref_algs(T);
+ handle_pref_algs(T, ['ssh-dss'| Acc]);
ssh_rsa ->
- check_pref_algs(T);
+ handle_pref_algs(T, ['ssh-rsa'| Acc]);
+ 'ssh-dss' ->
+ handle_pref_algs(T, ['ssh-dss'| Acc]);
+ 'ssh-rsa' ->
+ handle_pref_algs(T, ['ssh-rsa'| Acc]);
_ ->
false
end.
-%% Has IPv6 been disabled?
-inetopt(true) ->
- inet;
-inetopt(false) ->
- case gen_tcp:listen(0, [inet6]) of
- {ok, Dummyport} ->
- gen_tcp:close(Dummyport),
- inet6;
- _ ->
- inet
- end.
-
-%%%
-%% Deprecated
-%%%
-
-%%--------------------------------------------------------------------
-%% Function: sign_data(Data, Algorithm) -> binary() |
-%% {error, Reason}
-%%
-%% Data = binary()
-%% Algorithm = "ssh-rsa"
-%%
-%% Description: Use SSH key to sign data.
-%%--------------------------------------------------------------------
-sign_data(Data, Algorithm) when is_binary(Data) ->
- case ssh_file:user_key(Algorithm,[]) of
- {ok, Key} when Algorithm == "ssh-rsa" ->
- public_key:sign(Data, sha, Key);
- Error ->
- Error
- end.
-
-%%--------------------------------------------------------------------
-%% Function: verify_data(Data, Signature, Algorithm) -> ok |
-%% {error, Reason}
-%%
-%% Data = binary()
-%% Signature = binary()
-%% Algorithm = "ssh-rsa"
-%%
-%% Description: Use SSH signature to verify data.
-%%--------------------------------------------------------------------
-verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) ->
- case ssh_file:user_key(Algorithm, []) of
- {ok, #'RSAPrivateKey'{publicExponent = E, modulus = N}} when Algorithm == "ssh-rsa" ->
- public_key:verify(Data, sha, Signature, #'RSAPublicKey'{publicExponent = E, modulus = N});
- Error ->
- Error
- end.