aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src')
-rw-r--r--lib/ssh/src/ssh.app.src10
-rw-r--r--lib/ssh/src/ssh.erl2
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_channel.erl4
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl8
-rw-r--r--lib/ssh/src/ssh_dbg.erl73
-rw-r--r--lib/ssh/src/ssh_sftpd.erl2
-rw-r--r--lib/ssh/src/ssh_transport.erl93
8 files changed, 143 insertions, 51 deletions
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 410061cded..7449405d20 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -44,10 +44,10 @@
{env, []},
{mod, {ssh_app, []}},
{runtime_dependencies, [
- "crypto-4.2",
- "erts-6.0",
- "kernel-3.0",
- "public_key-1.5.2",
- "stdlib-3.3"
+ "crypto-@OTP-15644@",
+ "erts-9.0",
+ "kernel-5.3",
+ "public_key-1.6.1",
+ "stdlib-3.4.1"
]}]}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 9281bf84a7..ff5aee14d7 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -127,7 +127,7 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_port(Socket),
Options ->
case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
ok ->
- {ok, {Host,_Port}} = inet:sockname(Socket),
+ {ok, {Host,_Port}} = inet:peername(Socket),
Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout);
{error,SockError} ->
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 923e9309f4..04453e6ef0 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -396,11 +396,13 @@
recv_mac_size = 0,
encrypt = none, %% encrypt algorithm
+ encrypt_cipher, %% cipher. could be different from the algorithm
encrypt_keys, %% encrypt keys
encrypt_block_size = 8,
encrypt_ctx,
decrypt = none, %% decrypt algorithm
+ decrypt_cipher, %% cipher. could be different from the algorithm
decrypt_keys, %% decrypt keys
decrypt_block_size = 8,
decrypt_ctx, %% Decryption context
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 443bd05086..1d977e3bc9 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -58,6 +58,7 @@
State::term()}.
%%% API
-export([start/4, start/5, start_link/4, start_link/5, call/2, call/3,
+ init/1,
cast/2, reply/2, enter_loop/1]).
%%====================================================================
@@ -76,6 +77,9 @@ cast(ChannelPid, Msg) ->
reply(From, Msg) ->
ssh_client_channel:reply(From, Msg).
+init(Args) ->
+ ssh_client_channel:init(Args).
+
start(ConnectionManager, ChannelId, CallBack, CbInitArgs) ->
ssh_client_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs).
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 7c87591cf2..8f32966a12 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -594,7 +594,7 @@ handle_event(_, socket_control, {hello,_}=StateName, D) ->
{stop, {shutdown,{unexpected_getopts_return, Other}}}
end;
-handle_event(_, {info_line,_Line}, {hello,Role}=StateName, D) ->
+handle_event(_, {info_line,Line}, {hello,Role}=StateName, D) ->
case Role of
client ->
%% The server may send info lines to the client before the version_exchange
@@ -605,9 +605,9 @@ handle_event(_, {info_line,_Line}, {hello,Role}=StateName, D) ->
%% But the client may NOT send them to the server. Openssh answers with cleartext,
%% and so do we
send_bytes("Protocol mismatch.", D),
- ?call_disconnectfun_and_log_cond("Protocol mismatch.",
- "Protocol mismatch in version exchange. Client sent info lines.",
- StateName, D),
+ Msg = io_lib:format("Protocol mismatch in version exchange. Client sent info lines.~n~s",
+ [ssh_dbg:hex_dump(Line, 64)]),
+ ?call_disconnectfun_and_log_cond("Protocol mismatch.", Msg, StateName, D),
{stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
end;
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index 4fe15b24d3..43ac4c0ccf 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -60,6 +60,7 @@
cbuf_stop_clear/0,
cbuf_in/1,
cbuf_list/0,
+ hex_dump/1, hex_dump/2,
fmt_cbuf_items/0, fmt_cbuf_item/1
]).
@@ -439,3 +440,75 @@ fmt_value(#circ_buf_entry{module = M,
io_lib:format("~p:~p ~p/~p ~p~n~s",[M,L,F,A,Pid,fmt_value(V)]);
fmt_value(Value) ->
io_lib:format("~p",[Value]).
+
+%%%================================================================
+
+-record(h, {max_bytes = 65536,
+ bytes_per_line = 16,
+ address_len = 4
+ }).
+
+
+hex_dump(Data) -> hex_dump1(Data, hd_opts([])).
+
+hex_dump(X, Max) when is_integer(Max) ->
+ hex_dump(X, [{max_bytes,Max}]);
+hex_dump(X, OptList) when is_list(OptList) ->
+ hex_dump1(X, hd_opts(OptList)).
+
+hex_dump1(B, Opts) when is_binary(B) -> hex_dump1(binary_to_list(B), Opts);
+hex_dump1(L, Opts) when is_list(L), length(L) > Opts#h.max_bytes ->
+ io_lib:format("~s---- skip ~w bytes----~n", [hex_dump1(lists:sublist(L,Opts#h.max_bytes), Opts),
+ length(L) - Opts#h.max_bytes
+ ]);
+hex_dump1(L, Opts0) when is_list(L) ->
+ Opts = Opts0#h{address_len = num_hex_digits(Opts0#h.max_bytes)},
+ Result = hex_dump(L, [{0,[],[]}], Opts),
+ [io_lib:format("~*.s | ~*s | ~s~n"
+ "~*.c-+-~*c-+-~*c~n",
+ [Opts#h.address_len, lists:sublist("Address",Opts#h.address_len),
+ -3*Opts#h.bytes_per_line, lists:sublist("Hexdump",3*Opts#h.bytes_per_line),
+ "ASCII",
+ Opts#h.address_len, $-,
+ 3*Opts#h.bytes_per_line, $-,
+ Opts#h.bytes_per_line, $-
+ ]) |
+ [io_lib:format("~*.16.0b | ~s~*c | ~s~n",[Opts#h.address_len, N*Opts#h.bytes_per_line,
+ lists:reverse(Hexs),
+ 3*(Opts#h.bytes_per_line-length(Hexs)), $ ,
+ lists:reverse(Chars)])
+ || {N,Hexs,Chars} <- lists:reverse(Result)
+ ]
+ ].
+
+
+hd_opts(L) -> lists:foldl(fun hd_opt/2, #h{}, L).
+
+hd_opt({max_bytes,M}, O) -> O#h{max_bytes=M};
+hd_opt({bytes_per_line,M}, O) -> O#h{bytes_per_line=M}.
+
+
+num_hex_digits(N) when N<16 -> 1;
+num_hex_digits(N) -> trunc(math:ceil(math:log2(N)/4)).
+
+
+hex_dump([L|Cs], Result0, Opts) when is_list(L) ->
+ Result = hex_dump(L,Result0, Opts),
+ hex_dump(Cs, Result, Opts);
+
+hex_dump(Cs, [{N0,_,Chars}|_]=Lines, Opts) when length(Chars) == Opts#h.bytes_per_line ->
+ hex_dump(Cs, [{N0+1,[],[]}|Lines], Opts);
+
+hex_dump([C|Cs], [{N,Hexs,Chars}|Lines], Opts) ->
+ Asc = if
+ 16#20 =< C,C =< 16#7E -> C;
+ true -> $.
+ end,
+ Hex = io_lib:format("~2.16.0b ", [C]),
+ hex_dump(Cs, [{N, [Hex|Hexs], [Asc|Chars]} | Lines], Opts);
+
+hex_dump([], Result, _) ->
+ Result.
+
+
+
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index aa9ba0f9bb..5ec12e2d04 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -508,7 +508,7 @@ close_our_file({_,Fd}, FileMod, FS0) ->
FS1.
%%% stat: do the stat
-stat(Vsn, ReqId, Data, State, F) ->
+stat(_Vsn, ReqId, Data, State, F) ->
<<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data,
stat(ReqId, unicode:characters_to_list(BPath), State, F).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 1f4e281a30..eaab13433a 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -170,7 +170,7 @@ supported_algorithms(cipher) ->
{'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]},
{'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
{'aes128-cbc', [{ciphers,aes_128_cbc}]},
- {'3des-cbc', [{ciphers,des3_cbc}]}
+ {'3des-cbc', [{ciphers,des_ede3_cbc}]}
]
));
supported_algorithms(mac) ->
@@ -1328,25 +1328,27 @@ verify(PlainText, HashAlg, Sig, Key, _) ->
%%% Start of a more parameterized crypto handling.
cipher('AEAD_AES_128_GCM') ->
- #cipher{key_bytes = 16,
+ #cipher{impl = aes_128_gcm,
+ key_bytes = 16,
iv_bytes = 12,
block_bytes = 16,
pkt_type = aead};
cipher('AEAD_AES_256_GCM') ->
- #cipher{key_bytes = 32,
+ #cipher{impl = aes_256_gcm,
+ key_bytes = 32,
iv_bytes = 12,
block_bytes = 16,
pkt_type = aead};
cipher('3des-cbc') ->
- #cipher{impl = des3_cbc,
+ #cipher{impl = des_ede3_cbc,
key_bytes = 24,
iv_bytes = 8,
block_bytes = 8};
cipher('aes128-cbc') ->
- #cipher{impl = aes_cbc,
+ #cipher{impl = aes_128_cbc,
key_bytes = 16,
iv_bytes = 16,
block_bytes = 16};
@@ -1370,7 +1372,8 @@ cipher('aes256-ctr') ->
block_bytes = 16};
cipher('[email protected]') -> % FIXME: Verify!!
- #cipher{key_bytes = 32,
+ #cipher{impl = chacha20_poly1305,
+ key_bytes = 32,
iv_bytes = 12,
block_bytes = 8,
pkt_type = aead};
@@ -1407,12 +1410,14 @@ encrypt_init(#ssh{encrypt = '[email protected]', role = Role} = Ssh)
encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
SshCipher == 'AEAD_AES_256_GCM' ->
{IvMagic, KeyMagic} = encrypt_magic(Role),
- #cipher{key_bytes = KeyBytes,
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
iv_bytes = IvBytes,
block_bytes = BlockBytes} = cipher(SshCipher),
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
- {ok, Ssh#ssh{encrypt_keys = K,
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_keys = K,
encrypt_block_size = BlockBytes,
encrypt_ctx = IV}};
@@ -1425,11 +1430,12 @@ encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) ->
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, true),
- {ok, Ssh#ssh{encrypt_block_size = BlockBytes,
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_block_size = BlockBytes,
encrypt_ctx = Ctx0}}.
encrypt_final(Ssh) ->
- {ok, Ssh#ssh{encrypt = none,
+ {ok, Ssh#ssh{encrypt = none,
encrypt_keys = undefined,
encrypt_block_size = 8,
encrypt_ctx = undefined
@@ -1445,30 +1451,31 @@ encrypt(#ssh{encrypt = '[email protected]',
<<LenData:4/binary, PayloadData/binary>>) ->
%% Encrypt length
IV1 = <<0:8/unit:8, Seq:8/unit:8>>,
- EncLen = crypto:crypto_one_shot(chacha20, K1, IV1, LenData, true),
+ EncLen = crypto:crypto_one_time(chacha20, K1, IV1, LenData, true),
%% Encrypt payload
IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
- EncPayloadData = crypto:crypto_one_shot(chacha20, K2, IV2, PayloadData, true),
+ EncPayloadData = crypto:crypto_one_time(chacha20, K2, IV2, PayloadData, true),
%% MAC tag
- PolyKey = crypto:crypto_one_shot(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, true),
+ PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, true),
EncBytes = <<EncLen/binary,EncPayloadData/binary>>,
Ctag = crypto:poly1305(PolyKey, EncBytes),
%% Result
{Ssh, {EncBytes,Ctag}};
encrypt(#ssh{encrypt = SshCipher,
+ encrypt_cipher = CryptoCipher,
encrypt_keys = K,
encrypt_ctx = IV0} = Ssh,
<<LenData:4/binary, PayloadData/binary>>) when SshCipher == 'AEAD_AES_128_GCM' ;
SshCipher == 'AEAD_AES_256_GCM' ->
- {Ctext,Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, {LenData,PayloadData}),
+ {Ctext,Ctag} = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, PayloadData, LenData, true),
IV = next_gcm_iv(IV0),
{Ssh#ssh{encrypt_ctx = IV}, {<<LenData/binary,Ctext/binary>>,Ctag}};
encrypt(#ssh{encrypt_ctx = Ctx0} = Ssh, Data) ->
Enc = crypto:crypto_update(Ctx0, Data),
{Ssh, Enc}.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Decryption
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1485,12 +1492,14 @@ decrypt_init(#ssh{decrypt = '[email protected]', role = Role} = Ssh)
decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
SshCipher == 'AEAD_AES_256_GCM' ->
{IvMagic, KeyMagic} = decrypt_magic(Role),
- #cipher{key_bytes = KeyBytes,
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
iv_bytes = IvBytes,
block_bytes = BlockBytes} = cipher(SshCipher),
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
- {ok, Ssh#ssh{decrypt_keys = K,
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_keys = K,
decrypt_block_size = BlockBytes,
decrypt_ctx = IV}};
@@ -1503,9 +1512,11 @@ decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) ->
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, false),
- {ok, Ssh#ssh{decrypt_block_size = BlockBytes,
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_block_size = BlockBytes,
decrypt_ctx = Ctx0}}.
+
decrypt_final(Ssh) ->
{ok, Ssh#ssh {decrypt = none,
decrypt_keys = undefined,
@@ -1517,35 +1528,37 @@ decrypt(Ssh, <<>>) ->
{Ssh, <<>>};
decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {K1,_K2},
- recv_sequence = Seq} = Ssh, {length,EncryptedLen}) ->
- PacketLenBin = crypto:crypto_one_shot(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>, EncryptedLen, false),
- {Ssh, PacketLenBin};
-
-decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {_K1,K2},
- recv_sequence = Seq} = Ssh, {AAD,Ctext,Ctag}) ->
- %% The length is already decoded and used to divide the input
- %% Check the mac (important that it is timing-safe):
- PolyKey = crypto:crypto_one_shot(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
- case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
- true ->
- %% MAC is ok, decode
- IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
- PlainText = crypto:crypto_one_shot(chacha20, K2, IV2, Ctext, false),
- {Ssh, PlainText};
- false ->
- {Ssh,error}
+ decrypt_keys = {K1,K2},
+ recv_sequence = Seq} = Ssh, Data) ->
+ case Data of
+ {length,EncryptedLen} ->
+ %% The length is decrypted separately in a first step
+ PacketLenBin = crypto:crypto_one_time(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>, EncryptedLen, false),
+ {Ssh, PacketLenBin};
+ {AAD,Ctext,Ctag} ->
+ %% The length is already decrypted and used to divide the input
+ %% Check the mac (important that it is timing-safe):
+ PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
+ case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
+ true ->
+ %% MAC is ok, decode
+ IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
+ PlainText = crypto:crypto_one_time(chacha20, K2, IV2, Ctext, false),
+ {Ssh, PlainText};
+ false ->
+ {Ssh,error}
+ end
end;
decrypt(#ssh{decrypt = none} = Ssh, Data) ->
{Ssh, Data};
decrypt(#ssh{decrypt = SshCipher,
+ decrypt_cipher = CryptoCipher,
decrypt_keys = K,
- decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) when SshCipher == 'AEAD_AES_128_GCM' ;
- SshCipher == 'AEAD_AES_256_GCM' ->
- Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
+ decrypt_ctx = IV0} = Ssh, {AAD,Ctext,Ctag}) when SshCipher == 'AEAD_AES_128_GCM' ;
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ Dec = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, Ctext, AAD, Ctag, false),
IV = next_gcm_iv(IV0),
{Ssh#ssh{decrypt_ctx = IV}, Dec};