%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
%%
%% SSH definitions
%%
-ifndef(SSH_HRL).
-define(SSH_HRL, 1).
-define(SSH_DEFAULT_PORT, 22).
-define(SSH_MAX_PACKET_SIZE, (256*1024)).
-define(REKEY_TIMOUT, 3600000).
-define(REKEY_DATA_TIMOUT, 60000).
-define(DEFAULT_PROFILE, default).
-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
-define(DEFAULT_SHELL, {shell, start, []} ).
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
-define(FALSE, 0).
-define(TRUE, 1).
%% basic binary constructors
-define(BOOLEAN(X), (X):8/unsigned-big-integer).
-define(BYTE(X), (X):8/unsigned-big-integer).
-define(UINT16(X), (X):16/unsigned-big-integer).
-define(UINT32(X), (X):32/unsigned-big-integer).
-define(UINT64(X), (X):64/unsigned-big-integer).
-define(STRING(X), ?UINT32((size(X))), (X)/binary).
-define(DEC_BIN(X,Len), ?UINT32(Len), X:Len/binary ).
-define(DEC_MPINT(I,Len), ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
%% building macros
-define(boolean(X),
case X of
true -> <<?BOOLEAN(1)>>;
false -> (<<?BOOLEAN(0)>>)
end).
-define(byte(X), << ?BYTE(X) >> ).
-define(uint16(X), << ?UINT16(X) >> ).
-define(uint32(X), << ?UINT32(X) >> ).
-define(uint64(X), << ?UINT64(X) >> ).
-define(string_utf8(X), << ?STRING(unicode:characters_to_binary(X)) >> ).
-define(string(X), ?string_utf8(X)).
-define(binary(X), << ?STRING(X) >>).
%% Cipher details
-define(SSH_CIPHER_NONE, 0).
-define(SSH_CIPHER_3DES, 3).
-define(SSH_CIPHER_AUTHFILE, ?SSH_CIPHER_3DES).
%% Option access macros
-define(do_get_opt(C,K,O), ssh_options:get_value(C,K,O, ?MODULE,?LINE)).
-define(do_get_opt(C,K,O,D), ssh_options:get_value(C,K,O,?LAZY(D),?MODULE,?LINE)).
-define(LAZY(D), fun()-> D end).
-define(GET_OPT(Key,Opts), ?do_get_opt(user_options, Key,Opts ) ).
-define(GET_OPT(Key,Opts,Def), ?do_get_opt(user_options, Key,Opts,Def) ).
-define(GET_INTERNAL_OPT(Key,Opts), ?do_get_opt(internal_options,Key,Opts ) ).
-define(GET_INTERNAL_OPT(Key,Opts,Def), ?do_get_opt(internal_options,Key,Opts,Def) ).
-define(GET_SOCKET_OPT(Key,Opts), ?do_get_opt(socket_options, Key,Opts ) ).
-define(GET_SOCKET_OPT(Key,Opts,Def), ?do_get_opt(socket_options, Key,Opts,Def) ).
-define(do_put_opt(C,KV,O), ssh_options:put_value(C,KV,O, ?MODULE,?LINE)).
-define(PUT_OPT(KeyVal,Opts), ?do_put_opt(user_options, KeyVal,Opts) ).
-define(PUT_INTERNAL_OPT(KeyVal,Opts), ?do_put_opt(internal_options,KeyVal,Opts) ).
-define(PUT_SOCKET_OPT(KeyVal,Opts), ?do_put_opt(socket_options, KeyVal,Opts) ).
-define(do_del_opt(C,K,O), ssh_options:delete_key(C,K,O, ?MODULE,?LINE)).
-define(DELETE_INTERNAL_OPT(Key,Opts), ?do_del_opt(internal_options,Key,Opts) ).
%% Types
-type role() :: client | server .
-type host() :: string() | inet:ip_address() | loopback .
-type open_socket() :: gen_tcp:socket().
-type subsystem_spec() :: {Name::string(), mod_args()} .
-type algs_list() :: list( alg_entry() ).
-type alg_entry() :: {kex, [kex_alg()]}
| {public_key, [pubkey_alg()]}
| {cipher, double_algs(cipher_alg())}
| {mac, double_algs(mac_alg())}
| {compression, double_algs(compression_alg())} .
-type kex_alg() :: 'diffie-hellman-group-exchange-sha1' |
'diffie-hellman-group-exchange-sha256' |
'diffie-hellman-group1-sha1' |
'diffie-hellman-group14-sha1' |
'diffie-hellman-group14-sha256' |
'diffie-hellman-group16-sha512' |
'diffie-hellman-group18-sha512' |
'ecdh-sha2-nistp256' |
'ecdh-sha2-nistp384' |
'ecdh-sha2-nistp521'
.
-type pubkey_alg() :: 'ecdsa-sha2-nistp256' |
'ecdsa-sha2-nistp384' |
'ecdsa-sha2-nistp521' |
'rsa-sha2-256' |
'rsa-sha2-512' |
'ssh-dss' |
'ssh-rsa'
.
-type cipher_alg() :: '3des-cbc' |
'AEAD_AES_128_GCM' |
'AEAD_AES_256_GCM' |
'aes128-cbc' |
'aes128-ctr' |
'[email protected]' |
'aes192-ctr' |
'aes256-ctr' |
'[email protected]'
.
-type mac_alg() :: 'AEAD_AES_128_GCM' |
'AEAD_AES_256_GCM' |
'hmac-sha1' |
'hmac-sha2-256' |
'hmac-sha2-512'
.
-type compression_alg() :: 'none' |
'zlib' |
'[email protected]'
.
-type double_algs(AlgType) :: list( {client2server,[AlgType]} | {server2client,[AlgType]} )
| [AlgType].
-type modify_algs_list() :: list( {append,algs_list()} | {prepend,algs_list()} | {rm,algs_list()} ) .
-type internal_options() :: ssh_options:private_options().
-type socket_options() :: [gen_tcp:connect_option() | gen_tcp:listen_option()].
-type client_options() :: [ client_option() ] .
-type daemon_options() :: [ daemon_option() ].
-type common_options() :: [ common_option() ].
-type common_option() ::
user_dir_common_option()
| profile_common_option()
| max_idle_time_common_option()
| key_cb_common_option()
| disconnectfun_common_option()
| unexpectedfun_common_option()
| ssh_msg_debug_fun_common_option()
| rekey_limit_common_option()
| id_string_common_option()
| preferred_algorithms_common_option()
| modify_algorithms_common_option()
| auth_methods_common_option()
| inet_common_option()
| fd_common_option()
.
-define(COMMON_OPTION, common_option()).
-type user_dir_common_option() :: {user_dir, false | string()}.
-type profile_common_option() :: {profile, atom() }.
-type max_idle_time_common_option() :: {idle_time, timeout()}.
-type rekey_limit_common_option() :: {rekey_limit, non_neg_integer() }.
-type key_cb_common_option() :: {key_cb, Module::atom() | {Module::atom(),Opts::[term()]} } .
-type disconnectfun_common_option() ::
{disconnectfun, fun((Reason::term()) -> void | any()) }.
-type unexpectedfun_common_option() ::
{unexpectedfun, fun((Message::term(),{Host::term(),Port::term()}) -> report | skip ) }.
-type ssh_msg_debug_fun_common_option() ::
{ssh_msg_debug_fun, fun((ssh:connection_ref(),AlwaysDisplay::boolean(),Msg::binary(),LanguageTag::binary()) -> any()) } .
-type id_string_common_option() :: {id_string, string() | random | {random,Nmin::pos_integer(),Nmax::pos_integer()} }.
-type preferred_algorithms_common_option():: {preferred_algorithms, algs_list()}.
-type modify_algorithms_common_option() :: {modify_algorithms, modify_algs_list()}.
-type auth_methods_common_option() :: {auth_methods, string() }.
-type inet_common_option() :: {inet, inet | inet6} .
-type fd_common_option() :: {fd, gen_tcp:socket()} .
-type opaque_common_options() ::
{transport, {atom(),atom(),atom()} }
| {vsn, {non_neg_integer(),non_neg_integer()} }
| {tstflg, list(term())}
| {user_dir_fun, fun()}
| {max_random_length_padding, non_neg_integer()} .
-type client_option() ::
pref_public_key_algs_client_option()
| pubkey_passphrase_client_options()
| host_accepting_client_options()
| authentication_client_options()
| diffie_hellman_group_exchange_client_option()
| connect_timeout_client_option()
| recv_ext_info_client_option()
| opaque_client_options()
| gen_tcp:connect_option()
| ?COMMON_OPTION .
-type opaque_client_options() ::
{keyboard_interact_fun, fun((term(),term(),term()) -> term())}
| opaque_common_options().
-type pref_public_key_algs_client_option() :: {pref_public_key_algs, [pubkey_alg()] } .
-type pubkey_passphrase_client_options() :: {dsa_pass_phrase, string()}
| {rsa_pass_phrase, string()}
| {ecdsa_pass_phrase, string()} .
-type host_accepting_client_options() ::
{silently_accept_hosts, accept_hosts()}
| {user_interaction, boolean()}
| {save_accepted_host, boolean()}
| {quiet_mode, boolean()} .
-type accept_hosts() :: boolean()
| accept_callback()
| {HashAlgoSpec::fp_digest_alg(), accept_callback()}.
-type fp_digest_alg() :: 'md5' |
'sha' |
'sha224' |
'sha256' |
'sha384' |
'sha512'
.
-type accept_callback() :: fun((PeerName::string(), fingerprint() ) -> boolean()) .
-type fingerprint() :: string() | [string()].
-type authentication_client_options() ::
{user, string()}
| {password, string()} .
-type diffie_hellman_group_exchange_client_option() ::
{dh_gex_limits, {Min::pos_integer(), I::pos_integer(), Max::pos_integer()} } .
-type connect_timeout_client_option() :: {connect_timeout, timeout()} .
-type recv_ext_info_client_option() :: {recv_ext_info, boolean()} .
-type daemon_option() ::
subsystem_daemon_option()
| shell_daemon_option()
| exec_daemon_option()
| ssh_cli_daemon_option()
| authentication_daemon_options()
| diffie_hellman_group_exchange_daemon_option()
| negotiation_timeout_daemon_option()
| hardening_daemon_options()
| callbacks_daemon_options()
| send_ext_info_daemon_option()
| opaque_daemon_options()
| gen_tcp:listen_option()
| ?COMMON_OPTION .
-type subsystem_daemon_option() :: {subsystems, subsystem_spec()}.
-type shell_daemon_option() :: {shell, mod_fun_args() | 'shell_fun/1'() | 'shell_fun/2'() }.
-type 'shell_fun/1'() :: fun((User::string()) -> pid()) .
-type 'shell_fun/2'() :: fun((User::string(), PeerAddr::inet:ip_address()) -> pid()).
-type exec_daemon_option() :: {exec, 'exec_fun/1'() | 'exec_fun/2'() | 'exec_fun/3'() }.
-type 'exec_fun/1'() :: fun((Cmd::string()) -> exec_result()) .
-type 'exec_fun/2'() :: fun((Cmd::string(), User::string()) -> exec_result()) .
-type 'exec_fun/3'() :: fun((Cmd::string(), User::string(), ClientAddr::ip_port()) -> exec_result()) .
-type exec_result() :: {ok,Result::term()} | {error,Reason::term()} .
-type ssh_cli_daemon_option() :: {ssh_cli, mod_args() | no_cli }.
-type send_ext_info_daemon_option() :: {send_ext_info, boolean()} .
-type authentication_daemon_options() ::
{system_dir, string()}
| {auth_method_kb_interactive_data, prompt_texts() }
| {user_passwords, [{UserName::string(),Pwd::string()}]}
| {password, string()}
| {pwdfun, pwdfun_2() | pwdfun_4()} .
-type prompt_texts() ::
kb_int_tuple()
| kb_int_fun_3()
.
-type kb_int_fun_3() :: fun((Peer::ip_port(), User::string(), Service::string()) -> kb_int_tuple()).
-type kb_int_tuple() :: {Name::string(), Instruction::string(), Prompt::string(), Echo::boolean()}.
-type pwdfun_2() :: fun((User::string(), Password::string()) -> boolean()) .
-type pwdfun_4() :: fun((User::string(),
Password::string(),
PeerAddress::ip_port(),
State::any()) ->
boolean() | disconnect | {boolean(),NewState::any()}
) .
-type diffie_hellman_group_exchange_daemon_option() ::
{dh_gex_groups, [explicit_group()] | explicit_group_file() | ssh_moduli_file()}
| {dh_gex_limits, {Min::pos_integer(), Max::pos_integer()} } .
-type explicit_group() :: {Size::pos_integer(),G::pos_integer(),P::pos_integer()} .
-type explicit_group_file() :: {file,string()} .
-type ssh_moduli_file() :: {ssh_moduli_file,string()}.
-type negotiation_timeout_daemon_option() :: {negotiation_timeout, timeout()} .
-type hardening_daemon_options() ::
{max_sessions, pos_integer()}
| {max_channels, pos_integer()}
| {parallel_login, boolean()}
| {minimal_remote_max_packet_size, pos_integer()}.
-type callbacks_daemon_options() ::
{failfun, fun((User::string(), PeerAddress::inet:ip_address(), Reason::term()) -> _)}
| {connectfun, fun((User::string(), PeerAddress::inet:ip_address(), Method::string()) ->_)} .
-type opaque_daemon_options() ::
{infofun, fun()}
| opaque_common_options().
-type ip_port() :: {inet:ip_address(), inet:port_number()} .
-type mod_args() :: {Module::atom(), Args::list()} .
-type mod_fun_args() :: {Module::atom(), Function::atom(), Args::list()} .
%% Records
-record(ssh,
{
role :: client | role(),
peer :: undefined |
{inet:hostname(),ip_port()}, %% string version of peer address
local, %% Local sockname. Need this AFTER a socket is closed by i.e. a crash
c_vsn, %% client version {Major,Minor}
s_vsn, %% server version {Major,Minor}
c_version, %% client version string
s_version, %% server version string
c_keyinit, %% binary payload of kexinit packet
s_keyinit, %% binary payload of kexinit packet
send_ext_info, %% May send ext-info to peer
recv_ext_info, %% Expect ext-info from peer
algorithms, %% #alg{}
key_cb, %% Private/Public key callback module
io_cb, %% Interaction callback module
send_mac = none, %% send MAC algorithm
send_mac_key, %% key used in send MAC algorithm
send_mac_size = 0,
recv_mac = none, %% recv MAC algorithm
recv_mac_key, %% key used in recv MAC algorithm
recv_mac_size = 0,
encrypt = none, %% encrypt algorithm
encrypt_keys, %% encrypt keys
encrypt_block_size = 8,
encrypt_ctx,
decrypt = none, %% decrypt algorithm
decrypt_keys, %% decrypt keys
decrypt_block_size = 8,
decrypt_ctx, %% Decryption context
compress = none,
compress_ctx,
decompress = none,
decompress_ctx,
c_lng=none, %% client to server languages
s_lng=none, %% server to client languages
user_ack = true, %% client
timeout = infinity,
shared_secret, %% K from key exchange
exchanged_hash, %% H from key exchange
session_id, %% same as FIRST exchanged_hash
opts = [],
send_sequence = 0,
recv_sequence = 0,
keyex_key,
keyex_info,
random_length_padding = ?MAX_RND_PADDING_LEN, % From RFC 4253 section 6.
%% User auth
user,
service,
userauth_quiet_mode, % boolean()
userauth_methods, % list( string() ) eg ["keyboard-interactive", "password"]
userauth_supported_methods, % string() eg "keyboard-interactive,password"
userauth_pubkeys,
kb_tries_left = 0, % integer(), num tries left for "keyboard-interactive"
userauth_preference,
available_host_keys,
pwdfun_user_state,
authenticated = false
}).
-record(alg,
{
kex,
hkey,
send_mac,
recv_mac,
encrypt,
decrypt,
compress,
decompress,
c_lng,
s_lng,
send_ext_info,
recv_ext_info
}).
-record(ssh_key,
{
type,
public,
private,
comment = ""
}).
-record(ssh_pty, {term = "", % e.g. "xterm"
width = 80,
height = 25,
pixel_width = 1024,
pixel_height = 768,
modes = <<>>}).
%% assertion macro
-define(ssh_assert(Expr, Reason),
case Expr of
true -> ok;
_ -> exit(Reason)
end).
%% dbg help macros
-define(wr_record(N,BlackList),
wr_record(R=#N{}) -> ssh_dbg:wr_record(R, record_info(fields,N), BlackList)
).
-define(wr_record(N), ?wr_record(N, [])).
-endif. % SSH_HRL defined