diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0a80fdc --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.gz filter=lfs diff=lfs merge=lfs -text diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 0000000..d70ba93 --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://artlfs.openeuler.openatom.cn/src-openEuler/erlang diff --git a/CVE-2025-30211-1.patch b/CVE-2025-30211-1.patch new file mode 100644 index 0000000..6488586 --- /dev/null +++ b/CVE-2025-30211-1.patch @@ -0,0 +1,45 @@ +diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl +index 65f3c21..8cf18b1 100644 +--- a/lib/ssh/src/ssh_message.erl ++++ b/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"). +@@ -51,6 +52,7 @@ + -define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). + -define(Empint(X), (ssh_bits:mpint(X))/binary ). + -define(Ebinary(X), ?STRING(X) ). ++-define(ALG_NAME_LIMIT, 64). + + ucl(B) -> + try unicode:characters_to_list(B) of +@@ -591,8 +593,22 @@ decode_kex_init(<>, Acc, 0) -> + X = 0, + list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc])); + decode_kex_init(<>, 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). + + + %%%================================================================ diff --git a/CVE-2025-30211-2.patch b/CVE-2025-30211-2.patch new file mode 100644 index 0000000..695bcbf --- /dev/null +++ b/CVE-2025-30211-2.patch @@ -0,0 +1,37 @@ +From: Jakub Witczak +Date: Fri, 21 Mar 2025 17:50:21 +0100 +Subject: [PATCH] ssh: use chars_limit for bad packets error messages + +origin: backport, https://github.com/erlang/otp/commit/d64d9fb0688092356a336e38a8717499113312a0 +bug: https://github.com/erlang/otp/security/advisories/GHSA-vvr3-fjhh-cfwc +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1101713 +--- + lib/ssh/src/ssh_connection_handler.erl | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl +index 1d9151b..859db8e 100644 +--- a/lib/ssh/src/ssh_connection_handler.erl ++++ b/lib/ssh/src/ssh_connection_handler.erl +@@ -1379,8 +1379,8 @@ handle_event(info, {Proto, Sock, NewData}, StateName, + 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; +@@ -1414,8 +1414,8 @@ handle_event(info, {Proto, Sock, NewData}, StateName, + 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; diff --git a/CVE-2025-30211-3.patch b/CVE-2025-30211-3.patch new file mode 100644 index 0000000..e04583a --- /dev/null +++ b/CVE-2025-30211-3.patch @@ -0,0 +1,132 @@ +From: Jakub Witczak +Date: Mon, 24 Mar 2025 11:31:39 +0100 +Subject: [PATCH] ssh: custom_kexinit test added + +origin: backport, https://github.com/erlang/otp/commit/5ee26eb412a76ba1c6afdf4524b62939a48d1bce +bug: https://github.com/erlang/otp/security/advisories/GHSA-vvr3-fjhh-cfwc +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1101713 +--- + lib/ssh/test/ssh_protocol_SUITE.erl | 89 +++++++++++++++++++++++++++++++++++-- + 1 file changed, 86 insertions(+), 3 deletions(-) + +Index: erlang-22.2.7+dfsg/lib/ssh/test/ssh_protocol_SUITE.erl +=================================================================== +--- erlang-22.2.7+dfsg.orig/lib/ssh/test/ssh_protocol_SUITE.erl ++++ erlang-22.2.7+dfsg/lib/ssh/test/ssh_protocol_SUITE.erl +@@ -80,7 +80,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, +@@ -119,7 +120,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} + ]}]); +@@ -165,7 +166,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; +@@ -326,6 +327,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(260000, 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 = <>, ++ <> = KeyAlgsBin0, ++ KeyAlgsBin = <>, ++ <>. ++ + %%-------------------------------------------------------------------- + %%% Algo negotiation fail. This should result in a ssh_msg_disconnect + %%% being sent from the client. diff --git a/CVE-2025-30211-pre1.patch b/CVE-2025-30211-pre1.patch new file mode 100644 index 0000000..80ed418 --- /dev/null +++ b/CVE-2025-30211-pre1.patch @@ -0,0 +1,57 @@ +From: Jakub Witczak +Date: Fri, 27 Jan 2023 17:13:31 +0100 +Subject: [PATCH] ssh: reduce log length + +origin: backport, https://github.com/erlang/otp/commit/e93e40cf8150539338e7320b9fd9bad825b0a6d0 +bug: https://github.com/erlang/otp/security/advisories/GHSA-vvr3-fjhh-cfwc +bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1101713 +--- + lib/ssh/src/ssh_connection_handler.erl | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl +index ee397ac..1d9151b 100644 +--- a/lib/ssh/src/ssh_connection_handler.erl ++++ b/lib/ssh/src/ssh_connection_handler.erl +@@ -1329,8 +1329,10 @@ handle_event(info, {Proto, Sock, Info}, {hello,_}, #data{socket = Sock, + end; + + +-handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock, +- transport_protocol = Proto}) -> ++handle_event(info, {Proto, Sock, NewData}, StateName, ++ D0 = #data{socket = Sock, ++ transport_protocol = Proto, ++ ssh_params = SshParams}) -> + try ssh_transport:handle_packet_part( + D0#data.decrypted_data_buffer, + <<(D0#data.encrypted_data_buffer)/binary, NewData/binary>>, +@@ -1374,10 +1376,11 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock, + ]} + catch + C:E:ST -> +- {Shutdown, D} = ++ 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]), ++ io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~P", ++ [C,E,ST,MaxLogItemLen]), + StateName, D1), + {stop, Shutdown, D} + end; +@@ -1408,9 +1411,11 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock, + {stop, Shutdown, D} + catch + C:E:ST -> +- {Shutdown, D} = ++ 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]), ++ io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~P", ++ [C,E,ST,MaxLogItemLen]), + StateName, D0), + {stop, Shutdown, D} + end; diff --git a/CVE-2025-32433-pre1.patch b/CVE-2025-32433-pre1.patch new file mode 100644 index 0000000..f16bd53 --- /dev/null +++ b/CVE-2025-32433-pre1.patch @@ -0,0 +1,302 @@ +Origin: https://github.com/erlang/otp/commit/c9e4b22bc4b1faffef07c94ef8e4751b1a36870d +Reviewed-by: Sylvain Beucler +Last-Update: 2025-04-22 + +From c9e4b22bc4b1faffef07c94ef8e4751b1a36870d Mon Sep 17 00:00:00 2001 +From: Hans Nilsson +Date: Thu, 14 Jan 2021 17:25:39 +0100 +Subject: [PATCH] ssh: Add 4th argument (#ssh) to ssh_connection:handl_msg + +to be able to use the hello-string whens settting pty options. +--- + lib/ssh/src/ssh_connection.erl | 76 +++++++++++++------------- + lib/ssh/src/ssh_connection_handler.erl | 4 +- + 2 files changed, 40 insertions(+), 40 deletions(-) + +Index: erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection.erl +=================================================================== +--- erlang-22.2.7+dfsg.orig/lib/ssh/src/ssh_connection.erl ++++ erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection.erl +@@ -42,7 +42,7 @@ + + %% Internal SSH application API + -export([channel_data/5, +- handle_msg/3, ++ handle_msg/4, + handle_stop/1, + + channel_adjust_window_msg/2, +@@ -450,7 +450,7 @@ handle_msg(#ssh_msg_channel_open_confirm + sender_channel = RemoteId, + initial_window_size = WindowSz, + maximum_packet_size = PacketSz}, +- #connection{channel_cache = Cache} = Connection0, _) -> ++ #connection{channel_cache = Cache} = Connection0, _, _SSH) -> + + #channel{remote_id = undefined} = Channel = + ssh_client_channel:cache_lookup(Cache, ChannelId), +@@ -468,22 +468,22 @@ handle_msg(#ssh_msg_channel_open_failure + reason = Reason, + description = Descr, + lang = Lang}, +- #connection{channel_cache = Cache} = Connection0, _) -> ++ #connection{channel_cache = Cache} = Connection0, _, _SSH) -> + Channel = ssh_client_channel:cache_lookup(Cache, ChannelId), + ssh_client_channel:cache_delete(Cache, ChannelId), + reply_msg(Channel, Connection0, {open_error, Reason, Descr, Lang}); + +-handle_msg(#ssh_msg_channel_success{recipient_channel = ChannelId}, Connection, _) -> ++handle_msg(#ssh_msg_channel_success{recipient_channel = ChannelId}, Connection, _, _SSH) -> + reply_msg(ChannelId, Connection, success); + +-handle_msg(#ssh_msg_channel_failure{recipient_channel = ChannelId}, Connection, _) -> ++handle_msg(#ssh_msg_channel_failure{recipient_channel = ChannelId}, Connection, _, _SSH) -> + reply_msg(ChannelId, Connection, failure); + +-handle_msg(#ssh_msg_channel_eof{recipient_channel = ChannelId}, Connection, _) -> ++handle_msg(#ssh_msg_channel_eof{recipient_channel = ChannelId}, Connection, _, _SSH) -> + reply_msg(ChannelId, Connection, {eof, ChannelId}); + + handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId}, +- #connection{channel_cache = Cache} = Connection0, _) -> ++ #connection{channel_cache = Cache} = Connection0, _, _SSH) -> + + case ssh_client_channel:cache_lookup(Cache, ChannelId) of + #channel{sent_close = Closed, remote_id = RemoteId, +@@ -516,18 +516,18 @@ handle_msg(#ssh_msg_channel_close{recipi + + handle_msg(#ssh_msg_channel_data{recipient_channel = ChannelId, + data = Data}, +- Connection, _) -> ++ Connection, _, _SSH) -> + channel_data_reply_msg(ChannelId, Connection, 0, Data); + + handle_msg(#ssh_msg_channel_extended_data{recipient_channel = ChannelId, + data_type_code = DataType, + data = Data}, +- Connection, _) -> ++ Connection, _, _SSH) -> + channel_data_reply_msg(ChannelId, Connection, DataType, Data); + + handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId, + bytes_to_add = Add}, +- #connection{channel_cache = Cache} = Connection, _) -> ++ #connection{channel_cache = Cache} = Connection, _, _SSH) -> + #channel{send_window_size = Size, remote_id = RemoteId} = + Channel0 = ssh_client_channel:cache_lookup(Cache, ChannelId), + +@@ -546,7 +546,7 @@ handle_msg(#ssh_msg_channel_open{channel + initial_window_size = WindowSz, + maximum_packet_size = PacketSz}, + #connection{options = SSHopts} = Connection0, +- server) -> ++ server, _SSH) -> + MinAcceptedPackSz = + ?GET_OPT(minimal_remote_max_packet_size, SSHopts), + +@@ -574,7 +574,7 @@ handle_msg(#ssh_msg_channel_open{channel + handle_msg(#ssh_msg_channel_open{channel_type = "session", + sender_channel = RemoteId}, + Connection, +- client) -> ++ client, _SSH) -> + %% Client implementations SHOULD reject any session channel open + %% requests to make it more difficult for a corrupt server to attack the + %% client. See See RFC 4254 6.1. +@@ -583,7 +583,7 @@ handle_msg(#ssh_msg_channel_open{channel + "Connection refused", "en"), + {[{connection_reply, FailMsg}], Connection}; + +-handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, _) -> ++handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, _, _SSH) -> + FailMsg = channel_open_failure_msg(RemoteId, + ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, + "Not allowed", "en"), +@@ -592,7 +592,7 @@ handle_msg(#ssh_msg_channel_open{sender_ + handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, + request_type = "exit-status", + data = Data}, +- Connection, _) -> ++ Connection, _, _SSH) -> + <> = Data, + reply_msg(ChannelId, Connection, {exit_status, ChannelId, Status}); + +@@ -600,7 +600,7 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "exit-signal", + want_reply = false, + data = Data}, +- #connection{channel_cache = Cache} = Connection0, _) -> ++ #connection{channel_cache = Cache} = Connection0, _, _SSH) -> + < ++ Connection, _, _SSH) -> + <> = Data, + reply_msg(ChannelId, Connection, {xon_xoff, ChannelId, CDo=/= 0}); + +@@ -627,7 +627,7 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "window-change", + want_reply = false, + data = Data}, +- Connection0, _) -> ++ Connection0, _, _SSH) -> + <> = Data, + reply_msg(ChannelId, Connection0, {window_change, ChannelId, +@@ -637,7 +637,7 @@ handle_msg(#ssh_msg_channel_request{reci + handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, + request_type = "signal", + data = Data}, +- Connection0, _) -> ++ Connection0, _, _SSH) -> + <> = Data, + reply_msg(ChannelId, Connection0, {signal, ChannelId, + binary_to_list(SigName)}); +@@ -646,7 +646,7 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "subsystem", + want_reply = WantReply, + data = Data}, +- #connection{channel_cache = Cache} = Connection, server) -> ++ #connection{channel_cache = Cache} = Connection, server, _SSH) -> + <> = Data, + #channel{remote_id=RemoteId} = Channel = + ssh_client_channel:cache_lookup(Cache, ChannelId), +@@ -668,7 +668,7 @@ handle_msg(#ssh_msg_channel_request{reci + {[{connection_reply,Reply}], Connection}; + + handle_msg(#ssh_msg_channel_request{request_type = "subsystem"}, +- Connection, client) -> ++ Connection, client, _SSH) -> + %% The client SHOULD ignore subsystem requests. See RFC 4254 6.5. + {[], Connection}; + +@@ -676,7 +676,7 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "pty-req", + want_reply = WantReply, + data = Data}, +- Connection, server) -> ++ Connection, server, _SSH) -> + < ++ Connection, client, _SSH) -> + %% The client SHOULD ignore pty requests. See RFC 4254 6.2. + {[], Connection}; + + handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, + request_type = "shell", + want_reply = WantReply}, +- Connection, server) -> ++ Connection, server, _SSH) -> + handle_cli_msg(Connection, ChannelId, + {shell, ChannelId, WantReply}); + + handle_msg(#ssh_msg_channel_request{request_type = "shell"}, +- Connection, client) -> ++ Connection, client, _SSH) -> + %% The client SHOULD ignore shell requests. See RFC 4254 6.5. + {[], Connection}; + +@@ -708,13 +708,13 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "exec", + want_reply = WantReply, + data = Data}, +- Connection, server) -> ++ Connection, server, _SSH) -> + <> = Data, + handle_cli_msg(Connection, ChannelId, + {exec, ChannelId, WantReply, binary_to_list(Command)}); + + handle_msg(#ssh_msg_channel_request{request_type = "exec"}, +- Connection, client) -> ++ Connection, client, _SSH) -> + %% The client SHOULD ignore exec requests. See RFC 4254 6.5. + {[], Connection}; + +@@ -722,20 +722,20 @@ handle_msg(#ssh_msg_channel_request{reci + request_type = "env", + want_reply = WantReply, + data = Data}, +- Connection, server) -> ++ Connection, server, _SSH) -> + <> = Data, + handle_cli_msg(Connection, ChannelId, + {env, ChannelId, WantReply, Var, Value}); + + handle_msg(#ssh_msg_channel_request{request_type = "env"}, +- Connection, client) -> ++ Connection, client, _SSH) -> + %% The client SHOULD ignore env requests. + {[], Connection}; + + handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, + request_type = _Other, + want_reply = WantReply}, +- #connection{channel_cache = Cache} = Connection, _) -> ++ #connection{channel_cache = Cache} = Connection, _, _SSH) -> + if WantReply == true -> + case ssh_client_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = RemoteId} -> +@@ -750,7 +750,7 @@ handle_msg(#ssh_msg_channel_request{reci + + handle_msg(#ssh_msg_global_request{name = _Type, + want_reply = WantReply, +- data = _Data}, Connection, _) -> ++ data = _Data}, Connection, _, _SSH) -> + if WantReply == true -> + FailMsg = request_failure_msg(), + {[{connection_reply, FailMsg}], Connection}; +@@ -759,18 +759,18 @@ handle_msg(#ssh_msg_global_request{name + end; + + handle_msg(#ssh_msg_request_failure{}, +- #connection{requests = [{_, From} | Rest]} = Connection, _) -> ++ #connection{requests = [{_, From} | Rest]} = Connection, _, _SSH) -> + {[{channel_request_reply, From, {failure, <<>>}}], + Connection#connection{requests = Rest}}; + + handle_msg(#ssh_msg_request_success{data = Data}, +- #connection{requests = [{_, From} | Rest]} = Connection, _) -> ++ #connection{requests = [{_, From} | Rest]} = Connection, _, _SSH) -> + {[{channel_request_reply, From, {success, Data}}], + Connection#connection{requests = Rest}}; + + handle_msg(#ssh_msg_disconnect{code = Code, + description = Description}, +- Connection, _) -> ++ Connection, _, _SSH) -> + {disconnect, {Code, Description}, handle_stop(Connection)}. + + +Index: erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection_handler.erl +=================================================================== +--- erlang-22.2.7+dfsg.orig/lib/ssh/src/ssh_connection_handler.erl ++++ erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection_handler.erl +@@ -1006,7 +1006,7 @@ handle_event(_, {#ssh_msg_kexinit{},_}, + + handle_event(_, #ssh_msg_disconnect{description=Desc} = Msg, StateName, D0) -> + {disconnect, _, RepliesCon} = +- ssh_connection:handle_msg(Msg, D0#data.connection_state, role(StateName)), ++ ssh_connection:handle_msg(Msg, D0#data.connection_state, role(StateName), D0#data.ssh_params), + {Actions,D} = send_replies(RepliesCon, D0), + disconnect_fun("Received disconnect: "++Desc, D), + {stop_and_reply, {shutdown,Desc}, Actions, D}; +@@ -1036,7 +1036,7 @@ handle_event(internal, {conn_msg,Msg}, S + event_queue = Qev0} = D0) -> + Role = role(StateName), + Rengotation = renegotiation(StateName), +- try ssh_connection:handle_msg(Msg, Connection0, Role) of ++ try ssh_connection:handle_msg(Msg, Connection0, Role, D0#data.ssh_params) of + {disconnect, Reason0, RepliesConn} -> + {Repls, D} = send_replies(RepliesConn, D0), + case {Reason0,Role} of diff --git a/CVE-2025-32433.patch b/CVE-2025-32433.patch new file mode 100644 index 0000000..18391d0 --- /dev/null +++ b/CVE-2025-32433.patch @@ -0,0 +1,214 @@ +Origin: https://github.com/erlang/otp/commit/0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12 +Reviewed-by: Sylvain Beucler +Last-Update: 2025-04-22 + +From 0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12 Mon Sep 17 00:00:00 2001 +From: Jakub Witczak +Date: Mon, 14 Apr 2025 16:33:21 +0200 +Subject: [PATCH] ssh: early RCE fix + +- disconnect when connection protocol message arrives +- when user is not authenticated for connection +- see RFC4252 sec.6 +--- + lib/ssh/src/ssh_connection.erl | 28 ++++++++-- + lib/ssh/test/ssh_protocol_SUITE.erl | 86 +++++++++++++++-------------- + 2 files changed, 67 insertions(+), 47 deletions(-) + +Index: erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection.erl +=================================================================== +--- erlang-22.2.7+dfsg.orig/lib/ssh/src/ssh_connection.erl ++++ erlang-22.2.7+dfsg/lib/ssh/src/ssh_connection.erl +@@ -26,6 +26,8 @@ + + -module(ssh_connection). + ++-include_lib("kernel/include/logger.hrl"). ++ + -include("ssh.hrl"). + -include("ssh_connect.hrl"). + -include("ssh_transport.hrl"). +@@ -446,6 +448,25 @@ channel_data(ChannelId, DataType, Data0, + %%% Replies {Reply, UpdatedConnection} + %%% + ++handle_msg(#ssh_msg_disconnect{code = Code, description = Description}, Connection, _, _SSH) -> ++ {disconnect, {Code, Description}, handle_stop(Connection)}; ++ ++handle_msg(Msg, Connection, server, Ssh = #ssh{authenticated = false}) -> ++ %% See RFC4252 6. ++ %% Message numbers of 80 and higher are reserved for protocols running ++ %% after this authentication protocol, so receiving one of them before ++ %% authentication is complete is an error, to which the server MUST ++ %% respond by disconnecting, preferably with a proper disconnect message ++ %% sent to ease troubleshooting. ++ MsgFun = fun(M) -> ++ MaxLogItemLen = ?GET_OPT(max_log_item_len, Ssh#ssh.opts), ++ io_lib:format("Connection terminated. Unexpected message for unauthenticated user." ++ " Message: ~w", [M], ++ [{chars_limit, MaxLogItemLen}]) ++ end, ++ ?LOG_DEBUG(MsgFun, [Msg]), ++ {disconnect, {?SSH_DISCONNECT_PROTOCOL_ERROR, "Connection refused"}, handle_stop(Connection)}; ++ + handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId, + sender_channel = RemoteId, + initial_window_size = WindowSz, +@@ -766,12 +787,7 @@ handle_msg(#ssh_msg_request_failure{}, + handle_msg(#ssh_msg_request_success{data = Data}, + #connection{requests = [{_, From} | Rest]} = Connection, _, _SSH) -> + {[{channel_request_reply, From, {success, Data}}], +- Connection#connection{requests = Rest}}; +- +-handle_msg(#ssh_msg_disconnect{code = Code, +- description = Description}, +- Connection, _, _SSH) -> +- {disconnect, {Code, Description}, handle_stop(Connection)}. ++ Connection#connection{requests = Rest}}. + + + %%%---------------------------------------------------------------- +Index: erlang-22.2.7+dfsg/lib/ssh/test/ssh_protocol_SUITE.erl +=================================================================== +--- erlang-22.2.7+dfsg.orig/lib/ssh/test/ssh_protocol_SUITE.erl ++++ erlang-22.2.7+dfsg/lib/ssh/test/ssh_protocol_SUITE.erl +@@ -59,6 +59,7 @@ suite() -> + all() -> + [{group,tool_tests}, + client_info_line, ++ early_rce, + {group,kex}, + {group,service_requests}, + {group,authentication}, +@@ -76,10 +77,8 @@ groups() -> + ]}, + {packet_size_error, [], [packet_length_too_large, + packet_length_too_short]}, +- + {field_size_error, [], [service_name_length_too_large, + service_name_length_too_short]}, +- + {kex, [], [custom_kexinit, + no_common_alg_server_disconnects, + no_common_alg_client_disconnects, +@@ -120,7 +119,8 @@ init_per_suite(Config) -> + end_per_suite(Config) -> + stop_apps(Config). + +-init_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects; Tc == custom_kexinit -> ++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} + ]}]); +@@ -166,7 +166,8 @@ init_per_testcase(TC, Config) when TC == + init_per_testcase(_TestCase, Config) -> + check_std_daemon_works(Config, ?LINE). + +-end_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects; Tc == custom_kexinit -> ++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; +@@ -327,6 +328,44 @@ no_common_alg_server_disconnects(Config) + ] + ). + ++early_rce(Config) -> ++ {ok,InitialState} = ++ ssh_trpt_test_lib:exec([{set_options, [print_ops, print_seqnums, print_messages]}]), ++ TypeOpen = "session", ++ ChannelId = 0, ++ WinSz = 425984, ++ PktSz = 65536, ++ DataOpen = <<>>, ++ SshMsgChannelOpen = ssh_connection:channel_open_msg(TypeOpen, ChannelId, WinSz, PktSz, DataOpen), ++ ++ Id = 0, ++ TypeReq = "exec", ++ WantReply = true, ++ DataReq = <>)>>, ++ SshMsgChannelRequest = ++ ssh_connection:channel_request_msg(Id, TypeReq, WantReply, DataReq), ++ {ok,AfterKexState} = ++ ssh_trpt_test_lib:exec( ++ [{connect, ++ server_host(Config),server_port(Config), ++ [{preferred_algorithms,[{kex,[?DEFAULT_KEX]}, ++ {cipher,?DEFAULT_CIPHERS} ++ ]}, ++ {silently_accept_hosts, true}, ++ {recv_ext_info, false}, ++ {user_dir, user_dir(Config)}, ++ {user_interaction, false} ++ | proplists:get_value(extra_options,Config,[])]}, ++ receive_hello, ++ {send, hello}, ++ {send, ssh_msg_kexinit}, ++ {match, #ssh_msg_kexinit{_='_'}, receive_msg}, ++ {send, SshMsgChannelOpen}, ++ {send, SshMsgChannelRequest}, ++ {match, disconnect(), receive_msg} ++ ], InitialState), ++ ok. ++ + custom_kexinit(Config) -> + %% 16#C0 value causes unicode:characters_to_list to return a big error value + Trash = lists:duplicate(260000, 16#C0), +@@ -353,11 +392,6 @@ custom_kexinit(Config) -> + 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}]}, +@@ -373,42 +407,11 @@ custom_kexinit(Config) -> + receive_hello, + {send, hello}, + {match, #ssh_msg_kexinit{_='_'}, receive_msg}, +- {send, {special, KexInit, PacketFun}}, % with server unsupported 'ssh-dss' ! ++ {send, KexInit}, % 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 = <>, +- <> = KeyAlgsBin0, +- KeyAlgsBin = <>, +- <>. +- + %%-------------------------------------------------------------------- + %%% Algo negotiation fail. This should result in a ssh_msg_disconnect + %%% being sent from the client. diff --git a/erlang.spec b/erlang.spec index 7fa24ef..c9c4508 100644 --- a/erlang.spec +++ b/erlang.spec @@ -11,7 +11,7 @@ %global __with_wxwidgets 1 Name: erlang Version: 21.3.3 -Release: 5 +Release: 6 Summary: General-purpose programming language and runtime environment License: Apache-2.0 URL: https://www.erlang.org @@ -33,6 +33,14 @@ Patch9: otp-0009-Load-man-pages-from-system-wide-directory.patch Patch10: otp-0010-Improve-nodes-querying.patch Patch11: CVE-2023-48795-erlang21.patch Patch12: CVE-2025-26618.patch +# Patch13...18 comes from debian +# https://salsa.debian.org/erlang-team/packages/erlang/-/tree/buster/debian/patches +Patch13: CVE-2025-30211-pre1.patch +Patch14: CVE-2025-30211-1.patch +Patch15: CVE-2025-30211-2.patch +Patch16: CVE-2025-30211-3.patch +Patch17: CVE-2025-32433-pre1.patch +Patch18: CVE-2025-32433.patch BuildRequires: gcc gcc-c++ flex %if %{with doc} %if 0%{?need_bootstrap} < 1 @@ -1726,6 +1734,9 @@ useradd -r -g epmd -d /dev/null -s /sbin/nologin \ %endif %changelog +* Sat Apr 26 2025 Funda Wang - 21.3.3-6 +- fix CVE-2025-30211, CVE-2025-32433 + * Mon Feb 24 2025 yaoxin <1024769339@qq.com> - 21.3.3-5 - Fix CVE-2025-26618 diff --git a/otp-OTP-21.3.3.tar.gz b/otp-OTP-21.3.3.tar.gz index 0835e28..7042657 100644 Binary files a/otp-OTP-21.3.3.tar.gz and b/otp-OTP-21.3.3.tar.gz differ