File fix-CVE-2025-30211.patch of Package erlang26.39339
From df3aad2c5570847895562ff96a725190571f028c Mon Sep 17 00:00:00 2001
From: Jakub Witczak <kuba@erlang.org>
Date: Thu, 13 Mar 2025 13:38:29 +0100
Subject: [PATCH 1/4] ssh: reduce log processing for plain connections
- avoid unnecessary data processing
---
lib/ssh/src/ssh.hrl | 8 ++-
lib/ssh/src/ssh_acceptor.erl | 68 +++++++++++++++++---------
lib/ssh/src/ssh_connection_handler.erl | 12 +++--
3 files changed, 61 insertions(+), 27 deletions(-)
Index: otp-OTP-26.2.1/lib/ssh/src/ssh.hrl
===================================================================
--- otp-OTP-26.2.1.orig/lib/ssh/src/ssh.hrl
+++ otp-OTP-26.2.1/lib/ssh/src/ssh.hrl
@@ -561,5 +561,11 @@
-define(CIRC_BUF_IN_ONCE(VALUE),
((fun(V) -> ?CIRC_BUF_IN(V), V end)(VALUE))
).
-
+
+-define(SELECT_MSG(__Fun),
+ (fun() ->
+ #{level := __Level} = logger:get_primary_config(),
+ __Fun(__Level)
+ end)()).
+
-endif. % SSH_HRL defined
Index: otp-OTP-26.2.1/lib/ssh/src/ssh_acceptor.erl
===================================================================
--- otp-OTP-26.2.1.orig/lib/ssh/src/ssh_acceptor.erl
+++ otp-OTP-26.2.1/lib/ssh/src/ssh_acceptor.erl
@@ -206,39 +206,63 @@ handle_error(Reason, ToAddress, ToPort,
handle_error(Reason, ToAddress, ToPort, FromAddress, FromPort) ->
case Reason of
{max_sessions, MaxSessions} ->
- error_logger:info_report(
- lists:concat(["Ssh login attempt to ",ssh_lib:format_address_port(ToAddress,ToPort),
- " from ",ssh_lib:format_address_port(FromAddress,FromPort),
- " denied due to option max_sessions limits to ",
- MaxSessions, " sessions."
- ])
- );
-
+ MsgFun =
+ fun(debug) ->
+ lists:concat(["Ssh login attempt to ",
+ ssh_lib:format_address_port(ToAddress,ToPort),
+ " from ",
+ ssh_lib:format_address_port(FromAddress,FromPort),
+ " denied due to option max_sessions limits to ",
+ MaxSessions, " sessions."]);
+ (_) ->
+ ["Ssh login attempt denied max_session limits"]
+ end,
+ error_logger:info_report(?SELECT_MSG(MsgFun));
Limit when Limit==enfile ; Limit==emfile ->
%% Out of sockets...
- error_logger:info_report([atom_to_list(Limit),": out of accept sockets on ",
- ssh_lib:format_address_port(ToAddress, ToPort),
- " - retrying"]),
+ MsgFun =
+ fun(debug) ->
+ [atom_to_list(Limit),": out of accept sockets on ",
+ ssh_lib:format_address_port(ToAddress, ToPort),
+ " - retrying"];
+ (_) ->
+ ["Out of accept sockets on - retrying"]
+ end,
+ error_logger:info_report(?SELECT_MSG(MsgFun)),
timer:sleep(?SLEEP_TIME);
-
closed ->
- error_logger:info_report(["The ssh accept socket on ",ssh_lib:format_address_port(ToAddress,ToPort),
- "was closed by a third party."]
- );
-
+ MsgFun =
+ fun(debug) ->
+ ["The ssh accept socket on ", ssh_lib:format_address_port(ToAddress,ToPort),
+ "was closed by a third party."];
+ (_) ->
+ ["The ssh accept socket on was closed by a third party"]
+ end,
+ error_logger:info_report(?SELECT_MSG(MsgFun));
timeout ->
ok;
-
Error when is_list(Error) ->
ok;
Error when FromAddress=/=undefined,
FromPort=/=undefined ->
- error_logger:info_report(["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
- " for connect from ",ssh_lib:format_address_port(FromAddress,FromPort),
- io_lib:format(": ~p", [Error])]);
+ MsgFun =
+ fun(debug) ->
+ ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+ " for connect from ",ssh_lib:format_address_port(FromAddress,FromPort),
+ io_lib:format(": ~p", [Error])];
+ (_) ->
+ [io_lib:format("Accept failed on for connection: ~p", [Error])]
+ end,
+ error_logger:info_report(?SELECT_MSG(MsgFun));
Error ->
- error_logger:info_report(["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
- io_lib:format(": ~p", [Error])])
+ MsgFun =
+ fun(debug) ->
+ ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+ io_lib:format(": ~p", [Error])];
+ (_) ->
+ [io_lib:format("Accept failed on for connection: ~p", [Error])]
+ end,
+ error_logger:info_report(?SELECT_MSG(MsgFun))
end.
%%%----------------------------------------------------------------
Index: otp-OTP-26.2.1/lib/ssh/src/ssh_connection_handler.erl
===================================================================
--- otp-OTP-26.2.1.orig/lib/ssh/src/ssh_connection_handler.erl
+++ otp-OTP-26.2.1/lib/ssh/src/ssh_connection_handler.erl
@@ -653,11 +653,15 @@ handle_event(internal, {version_exchange
%%% timeout after tcp:connect but then nothing arrives
handle_event(state_timeout, no_hello_received, {hello,_Role}=StateName, D0 = #data{ssh_params = Ssh0}) ->
- Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts),
+ MsgFun =
+ fun (debug) ->
+ Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts),
+ lists:concat(["No HELLO received within ",ssh_lib:format_time_ms(Time)]);
+ (_) ->
+ ["No HELLO received within hello_timeout"]
+ end,
{Shutdown, D} =
- ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
- lists:concat(["No HELLO received within ",ssh_lib:format_time_ms(Time)]),
- StateName, D0),
+ ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, ?SELECT_MSG(MsgFun), StateName, D0),
{stop, Shutdown, D};
@@ -1159,8 +1163,8 @@ handle_event(info, {Proto, Sock, NewData
MaxLogItemLen = ?GET_OPT(max_log_item_len,SshParams#ssh.opts),
{Shutdown, D} =
?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
- io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~P",
- [C,E,ST,MaxLogItemLen]),
+ io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
+ [C,E,ST], [{chars_limit, MaxLogItemLen}]),
StateName, D1),
{stop, Shutdown, D}
end;
@@ -1194,8 +1198,8 @@ handle_event(info, {Proto, Sock, NewData
MaxLogItemLen = ?GET_OPT(max_log_item_len,SshParams#ssh.opts),
{Shutdown, D} =
?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
- io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~P",
- [C,E,ST,MaxLogItemLen]),
+ io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",
+ [C,E,ST], [{chars_limit, MaxLogItemLen}]),
StateName, D0),
{stop, Shutdown, D}
end;
Index: otp-OTP-26.2.1/lib/ssh/src/ssh_message.erl
===================================================================
--- otp-OTP-26.2.1.orig/lib/ssh/src/ssh_message.erl
+++ otp-OTP-26.2.1/lib/ssh/src/ssh_message.erl
@@ -24,6 +24,7 @@
-module(ssh_message).
-include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/logger.hrl").
-include("ssh.hrl").
-include("ssh_connect.hrl").
@@ -42,6 +43,7 @@
-behaviour(ssh_dbg).
-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
+-define(ALG_NAME_LIMIT, 64).
ucl(B) ->
try unicode:characters_to_list(B) of
@@ -820,8 +822,22 @@ decode_kex_init(<<?BYTE(Bool)>>, Acc, 0)
X = 0,
list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
- Names = string:tokens(?unicode_list(Data), ","),
- decode_kex_init(Rest, [Names | Acc], N -1).
+ BinParts = binary:split(Data, <<$,>>, [global]),
+ Process =
+ fun(<<>>, PAcc) ->
+ PAcc;
+ (Part, PAcc) ->
+ case byte_size(Part) > ?ALG_NAME_LIMIT of
+ true ->
+ ?LOG_DEBUG("Ignoring too long name", []),
+ PAcc;
+ false ->
+ Name = binary:bin_to_list(Part),
+ [Name | PAcc]
+ end
+ end,
+ Names = lists:foldr(Process, [], BinParts),
+ decode_kex_init(Rest, [Names | Acc], N - 1).
%%%================================================================
Index: otp-OTP-26.2.1/lib/ssh/test/ssh_protocol_SUITE.erl
===================================================================
--- otp-OTP-26.2.1.orig/lib/ssh/test/ssh_protocol_SUITE.erl
+++ otp-OTP-26.2.1/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -73,6 +73,7 @@
modify_rm/1,
no_common_alg_client_disconnects/1,
no_common_alg_server_disconnects/1,
+ custom_kexinit/1,
no_ext_info_s1/1,
no_ext_info_s2/1,
packet_length_too_large/1,
@@ -133,7 +134,8 @@ groups() ->
{field_size_error, [], [service_name_length_too_large,
service_name_length_too_short]},
- {kex, [], [no_common_alg_server_disconnects,
+ {kex, [], [custom_kexinit,
+ no_common_alg_server_disconnects,
no_common_alg_client_disconnects,
gex_client_init_option_groups,
gex_server_gex_limit,
@@ -174,7 +176,7 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
stop_apps(Config).
-init_per_testcase(no_common_alg_server_disconnects, Config) ->
+init_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects; Tc == custom_kexinit ->
start_std_daemon(Config, [{preferred_algorithms,[{public_key,['ssh-rsa']},
{cipher,?DEFAULT_CIPHERS}
]}]);
@@ -220,7 +222,7 @@ init_per_testcase(TC, Config) when TC ==
init_per_testcase(_TestCase, Config) ->
check_std_daemon_works(Config, ?LINE).
-end_per_testcase(no_common_alg_server_disconnects, Config) ->
+end_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects; Tc == custom_kexinit ->
stop_std_daemon(Config);
end_per_testcase(kex_strict_negotiated, Config) ->
Config;
@@ -381,6 +383,88 @@ no_common_alg_server_disconnects(Config)
]
).
+custom_kexinit(Config) ->
+ %% 16#C0 value causes unicode:characters_to_list to return a big error value
+ Trash = lists:duplicate(260_000, 16#C0),
+ FunnyAlg = "curve25519-sha256",
+ KexInit =
+ #ssh_msg_kexinit{cookie = <<"Ã/Ï!9zñKá:ñÀv¿JÜ">>,
+ kex_algorithms =
+ [FunnyAlg ++ Trash],
+ server_host_key_algorithms = ["ssh-rsa"],
+ encryption_algorithms_client_to_server =
+ ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+ encryption_algorithms_server_to_client =
+ ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+ mac_algorithms_client_to_server =
+ ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+ "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+ mac_algorithms_server_to_client =
+ ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+ "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+ compression_algorithms_client_to_server = ["none","zlib@openssh.com","zlib"],
+ compression_algorithms_server_to_client = ["none","zlib@openssh.com","zlib"],
+ languages_client_to_server = [],
+ languages_server_to_client = [],
+ first_kex_packet_follows = false,
+ reserved = 0
+ },
+ PacketFun =
+ fun(Msg, Ssh) ->
+ BinMsg = custom_encode(Msg),
+ ssh_transport:pack(BinMsg, Ssh, 0)
+ end,
+ {ok,_} =
+ ssh_trpt_test_lib:exec(
+ [{set_options, [print_ops, {print_messages,detail}]},
+ {connect,
+ server_host(Config),server_port(Config),
+ [{silently_accept_hosts, true},
+ {user_dir, user_dir(Config)},
+ {user_interaction, false},
+ {preferred_algorithms,[{public_key,['ssh-rsa']},
+ {cipher,?DEFAULT_CIPHERS}
+ ]}
+ ]},
+ receive_hello,
+ {send, hello},
+ {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+ {send, {special, KexInit, PacketFun}}, % with server unsupported 'ssh-dss' !
+ {match, disconnect(), receive_msg}
+ ]
+ ).
+
+custom_encode(#ssh_msg_kexinit{
+ cookie = Cookie,
+ kex_algorithms = KeyAlgs,
+ server_host_key_algorithms = HostKeyAlgs,
+ encryption_algorithms_client_to_server = EncAlgC2S,
+ encryption_algorithms_server_to_client = EncAlgS2C,
+ mac_algorithms_client_to_server = MacAlgC2S,
+ mac_algorithms_server_to_client = MacAlgS2C,
+ compression_algorithms_client_to_server = CompAlgS2C,
+ compression_algorithms_server_to_client = CompAlgC2S,
+ languages_client_to_server = LangC2S,
+ languages_server_to_client = LangS2C,
+ first_kex_packet_follows = Bool,
+ reserved = Reserved
+ }) ->
+ KeyAlgsBin0 = <<?Ename_list(KeyAlgs)>>,
+ <<?UINT32(Len0), Data:Len0/binary>> = KeyAlgsBin0,
+ KeyAlgsBin = <<?UINT32(Len0), Data/binary>>,
+ <<?Ebyte(?SSH_MSG_KEXINIT), Cookie/binary,
+ KeyAlgsBin/binary,
+ ?Ename_list(HostKeyAlgs),
+ ?Ename_list(EncAlgC2S),
+ ?Ename_list(EncAlgS2C),
+ ?Ename_list(MacAlgC2S),
+ ?Ename_list(MacAlgS2C),
+ ?Ename_list(CompAlgS2C),
+ ?Ename_list(CompAlgC2S),
+ ?Ename_list(LangC2S),
+ ?Ename_list(LangS2C),
+ ?Eboolean(Bool), ?Euint32(Reserved)>>.
+
%%--------------------------------------------------------------------
%%% Algo negotiation fail. This should result in a ssh_msg_disconnect
%%% being sent from the client.