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.
openSUSE Build Service is sponsored by