From cb2a6b3f3c425d3f6ffe47171664043b4a8b418f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 6 May 2026 01:23:10 -0300 Subject: [PATCH 1/4] fix: reject IPMI packets with invalid CBC padding instead of crashing cbc_pad in decrypt mode reads the last byte as the pad count, then calls splice(@block, 0 - $count). If decrypted data is corrupt, the pad count can exceed the array size, crashing with "Modification of non-creatable array value attempted, subscript -16". Return empty string on invalid padding so the caller treats it as a decryption failure rather than accepting corrupted data as a valid IPMI response. Ref: #7511 --- xCAT-server/lib/perl/xCAT/IPMI.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xCAT-server/lib/perl/xCAT/IPMI.pm b/xCAT-server/lib/perl/xCAT/IPMI.pm index 759777e33..26933533a 100644 --- a/xCAT-server/lib/perl/xCAT/IPMI.pm +++ b/xCAT-server/lib/perl/xCAT/IPMI.pm @@ -763,6 +763,9 @@ sub cbc_pad { unless ($count) { return pack("C*", @block); } + if ($count > scalar @block) { + return ""; + } splice @block, 0 - $count; return pack("C*", @block); } From 2bcdc52f92ef9148cfcb0d4b3351599564e38142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 6 May 2026 01:25:09 -0300 Subject: [PATCH 2/4] fix: accept RMCP message tag 0 from OpenBMC with session ID correlation OpenBMC-based BMCs return message tag 0 in RAKP2/RAKP4 instead of echoing the tag from the request. xCAT rejected these as stale responses and retried indefinitely until timeout. Accept tag 0 but verify the remote console session ID in the response matches our current sidm. This prevents stale retries from corrupting session state while allowing OpenBMC responses through. Applied to got_rmcp_response, got_rakp2, and got_rakp4. Ref: #7511 --- xCAT-server/lib/perl/xCAT/IPMI.pm | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/xCAT-server/lib/perl/xCAT/IPMI.pm b/xCAT-server/lib/perl/xCAT/IPMI.pm index 26933533a..bd3389181 100644 --- a/xCAT-server/lib/perl/xCAT/IPMI.pm +++ b/xCAT-server/lib/perl/xCAT/IPMI.pm @@ -780,8 +780,12 @@ sub got_rmcp_response { #we would ignore an RMCP+ open session response if we are not in an IPMI2 negotiation, so we have to have *some* state that isn't established for this to be kosher return 9; #now's not the time for this response, ignore it } - unless ($byte == $self->{rmcptag}) { #make sure this rmcp response is specifically the last one we sent.... we don't want to happily proceed with the risk a retry request blew up our temp session id without letting us know - return 9; + unless ($byte == $self->{rmcptag}) { + return 9 unless $byte == 0; + my @sid_check = @data[3..6]; + unless (pack("C4", @sid_check) eq pack("C4", @{ $self->{sidm} })) { + return 9; + } } $byte = shift @data; unless ($byte == 0x00) { @@ -895,8 +899,12 @@ sub got_rakp4 { unless ($self->{sessionestablishmentcontext} == STATE_EXPECTINGRAKP4) { #ignore rakp4 unless we are explicitly expecting RAKP4 return 9; #now's not the time for this response, ignore it } - unless ($byte == $self->{rmcptag}) { #make sure this rmcp response is specifically the last one we sent.... we don't want to happily proceed with the risk a retry request blew up our temp session id without letting us know - return 9; + unless ($byte == $self->{rmcptag}) { + return 9 unless $byte == 0; + my @sid_check = @data[3..6]; + unless (pack("C4", @sid_check) eq pack("C4", @{ $self->{sidm} })) { + return 9; + } } $byte = shift @data; unless ($byte == 0x00) { @@ -955,8 +963,12 @@ sub got_rakp2 { #the reason being that if an old rakp1 retry actually made it and we were just too aggressive, then a previous rakp2 is invalidated and invalid session id or the integrity check value is bad return 9; #now's not the time for this response, ignore it } - unless ($byte == $self->{rmcptag}) { #make sure this rmcp response is specifically the last one we sent.... we don't want to happily proceed with the risk a retry request blew up our temp session id without letting us know - return 9; + unless ($byte == $self->{rmcptag}) { + return 9 unless $byte == 0; + my @sid_check = @data[3..6]; + unless (pack("C4", @sid_check) eq pack("C4", @{ $self->{sidm} })) { + return 9; + } } $byte = shift @data; unless ($byte == 0x00) { From 86f6a122646bd1c828825f73ce67d72a5e33ebb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 6 May 2026 01:25:55 -0300 Subject: [PATCH 3/4] fix: set IPMI name-only lookup bit in RAKP1 to match ipmitool Set bit 4 (0x10) of the requested privilege byte in RAKP Message 1 for name-only user lookup, matching ipmitool behavior. Use the same value consistently in all HMAC calculations (RAKP2 verification, RAKP3 auth code, SIK derivation). Without this, some BMCs fail user lookup with "Unauthorized name" even though the credentials are correct. Ref: #7511 --- xCAT-server/lib/perl/xCAT/IPMI.pm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xCAT-server/lib/perl/xCAT/IPMI.pm b/xCAT-server/lib/perl/xCAT/IPMI.pm index bd3389181..9f9a4b2b7 100644 --- a/xCAT-server/lib/perl/xCAT/IPMI.pm +++ b/xCAT-server/lib/perl/xCAT/IPMI.pm @@ -825,7 +825,7 @@ sub send_rakp3 { $self->{rmcptag} += 1; my @payload = ($self->{rmcptag}, 0, 0, 0, @{ $self->{pendingsessionid} }); my @user = unpack("C*", $self->{userid}); - push @payload, unpack("C*", $self->{hshfn}->(pack("C*", @{ $self->{remoterandomnumber} }, @{ $self->{sidm} }, $self->{privlevel}, scalar @user, @user), $self->{password})); + push @payload, unpack("C*", $self->{hshfn}->(pack("C*", @{ $self->{remoterandomnumber} }, @{ $self->{sidm} }, $self->{rakp_privbyte}, scalar @user, @user), $self->{password})); $self->sendpayload(payload => \@payload, type => $payload_types{'rakp3'}); } @@ -839,7 +839,8 @@ sub send_rakp1 { push @{ $self->{randomnumber} }, $randomnumber; } push @payload, @{ $self->{randomnumber} }; - push @payload, ($self->{privlevel}, 0, 0); # request priv + $self->{rakp_privbyte} = $self->{privlevel} | 0x10; + push @payload, ($self->{rakp_privbyte}, 0, 0); # request priv, with name-only lookup my @user = unpack("C*", $self->{userid}); push @payload, scalar @user; push @payload, @user; @@ -1007,7 +1008,7 @@ sub got_rakp2 { #Data now represents authcode.. sha1 only.. my @user = unpack("C*", $self->{userid}); my $ulength = scalar @user; - my $hmacdata = pack("C*", (@{ $self->{sidm} }, @{ $self->{pendingsessionid} }, @{ $self->{randomnumber} }, @{ $self->{remoterandomnumber} }, @{ $self->{remoteguid} }, $self->{privlevel}, $ulength, @user)); + my $hmacdata = pack("C*", (@{ $self->{sidm} }, @{ $self->{pendingsessionid} }, @{ $self->{randomnumber} }, @{ $self->{remoterandomnumber} }, @{ $self->{remoteguid} }, $self->{rakp_privbyte}, $ulength, @user)); my @expectedhash = (unpack("C*", $self->{hshfn}->($hmacdata, $self->{password}))); foreach (0 .. (scalar(@expectedhash) - 1)) { if ($expectedhash[$_] != $data[$_]) { @@ -1016,7 +1017,7 @@ sub got_rakp2 { return 9; } } - $self->{sik} = $self->{hshfn}->(pack("C*", @{ $self->{randomnumber} }, @{ $self->{remoterandomnumber} }, $self->{privlevel}, $ulength, @user), $self->{password}); + $self->{sik} = $self->{hshfn}->(pack("C*", @{ $self->{randomnumber} }, @{ $self->{remoterandomnumber} }, $self->{rakp_privbyte}, $ulength, @user), $self->{password}); $self->{k1} = $self->{hshfn}->(pack("C*", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), $self->{sik}); $self->{k2} = $self->{hshfn}->(pack("C*", 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), $self->{sik}); my @aeskey = unpack("C*", $self->{k2}); From c0e8b1730e602be2523ca317e5e0cb0a5d8bb872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 6 May 2026 01:26:29 -0300 Subject: [PATCH 4/4] fix: fall back from sha256 to sha1 on RAKP2 auth rejection Extend the existing sha256-to-sha1 fallback (already present in got_rmcp_response for Open Session errors) to also cover RAKP2 rejections with "Unauthorized name" (0x0d) or "Invalid role" (0x09). Ref: #7511 --- xCAT-server/lib/perl/xCAT/IPMI.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xCAT-server/lib/perl/xCAT/IPMI.pm b/xCAT-server/lib/perl/xCAT/IPMI.pm index 9f9a4b2b7..08d81fcd7 100644 --- a/xCAT-server/lib/perl/xCAT/IPMI.pm +++ b/xCAT-server/lib/perl/xCAT/IPMI.pm @@ -973,6 +973,12 @@ sub got_rakp2 { } $byte = shift @data; unless ($byte == 0x00) { + if (($byte == 0x9 or $byte == 0xd) and $self->{attempthash} == 256) { + $self->{attempthash} = 1; + $self->{sessionestablishmentcontext} = 0; + $self->open_rmcpplus_request(); + return; + } if (($byte == 0x9 or $byte == 0xd) and $self->{privlevel} == 4) { # this is probably an environment that wants to give us only operator