diff --git a/xCAT-server/lib/perl/xCAT/IPMI.pm b/xCAT-server/lib/perl/xCAT/IPMI.pm index 98fb3780a..eff79a116 100644 --- a/xCAT-server/lib/perl/xCAT/IPMI.pm +++ b/xCAT-server/lib/perl/xCAT/IPMI.pm @@ -449,11 +449,13 @@ sub got_channel_auth_cap { } -sub retry_rmcpplus_after_zero_rakp2 { +sub retry_rmcpplus_with_sha1 { my $self = shift; - return 0 if ($self->{attempthash} != 256 or $self->{zero_rakp2_fallback}); + my %args = @_; + return 0 if ($self->{attempthash} != 256); + return 0 if ($args{persist_for_login} and $self->{zero_rakp2_fallback}); - $self->{zero_rakp2_fallback} = 1; + $self->{zero_rakp2_fallback} = 1 if $args{persist_for_login}; $self->{attempthash} = 1; $self->{sessionestablishmentcontext} = 0; $self->open_rmcpplus_request(); @@ -803,9 +805,7 @@ sub got_rmcp_response { } $byte = shift @data; unless ($byte == 0x00) { - if ($self->{attempthash} == 256) { - $self->{attempthash} = 1; - $self->open_rmcpplus_request(); + if ($self->retry_rmcpplus_with_sha1()) { return 9; } if ($rmcp_codes{$byte}) { @@ -987,10 +987,8 @@ 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(); + if (($byte == 0x9 or $byte == 0xd) and + $self->retry_rmcpplus_with_sha1()) { return; } if (($byte == 0x9 or $byte == 0xd) and $self->{privlevel} == 4) { @@ -1039,7 +1037,7 @@ sub got_rakp2 { # SHA256 RAKP2 code. Never accept it; retry suite 3 once. if ($zero_authcode and $self->{sessionestablishmentcontext} == STATE_EXPECTINGRAKP2 and - $self->retry_rmcpplus_after_zero_rakp2()) { + $self->retry_rmcpplus_with_sha1(persist_for_login => 1)) { return 9; } $self->{sessionestablishmentcontext} = STATE_FAILED; diff --git a/xCAT-test/unit/ipmi_rakp2.t b/xCAT-test/unit/ipmi_rakp2.t index 07a6a802a..93882bac4 100644 --- a/xCAT-test/unit/ipmi_rakp2.t +++ b/xCAT-test/unit/ipmi_rakp2.t @@ -129,10 +129,116 @@ my @captured_zero_rakp2 = unpack( is(scalar(@captured_zero_rakp2), 72, 'captured XCC RAKP2 payload is 72 bytes'); +subtest 'Open Session errors retry once with suite 3 without persisting' => sub { + my @cases = ( + [0x09, 'Invalid role'], + [0x11, 'No cipher suite match with proposed security algorithms'], + ); + + foreach my $case (@cases) { + my ($code, $error) = @{$case}; + my @errors; + my @sent; + my $session = new_session( + errors => \@errors, + sessionestablishmentcontext => xCAT::IPMI::STATE_OPENSESSION(), + ); + my $original_localsid = $session->{localsid}; + my $original_tag = $session->{rmcptag}; + local *xCAT::IPMI::sendpayload = sub { + my ($self, %args) = @_; + push @sent, \%args; + }; + + is( + $session->got_rmcp_response($original_tag, $code), + 9, + sprintf('Open Session code 0x%02x is rejected', $code), + ); + is(scalar(@sent), 1, 'one replacement Open Session request is sent'); + is($session->{attempthash}, 1, 'replacement request selects SHA1'); + ok(!exists($session->{zero_rakp2_fallback}), 'ordinary fallback is not persisted'); + is($session->{localsid}, $original_localsid + 1, 'replacement request uses a new console session ID'); + is($session->{rmcptag}, $original_tag + 1, 'replacement request uses a new message tag'); + is( + $session->{sessionestablishmentcontext}, + xCAT::IPMI::STATE_OPENSESSION(), + 'session waits for the replacement Open Session response', + ); + is_deeply( + [@{ $sent[0]->{payload} }[12, 20, 28]], + [1, 1, 1], + 'replacement request proposes suite 3 algorithms', + ); + is_deeply(\@errors, [], 'fallback does not invoke the login callback'); + + is( + $session->got_rmcp_response($session->{rmcptag}, $code), + 9, + 'the replacement rejection keeps the existing error return', + ); + is(scalar(@sent), 1, 'SHA1 is not downgraded a second time'); + is_deeply(\@errors, ["ERROR: $error"], 'the replacement rejection reports the existing error'); + } +}; + +subtest 'RAKP2 role errors retry once with suite 3 without persisting' => sub { + my @cases = ( + [0x09, 'Invalid role'], + [0x0d, 'Unauthorized name'], + ); + + foreach my $case (@cases) { + my ($code, $error) = @{$case}; + my @errors; + my @sent; + my $session = new_session(errors => \@errors, privlevel => 3); + my $original_localsid = $session->{localsid}; + my $original_tag = $session->{rmcptag}; + local *xCAT::IPMI::sendpayload = sub { + my ($self, %args) = @_; + push @sent, \%args; + }; + + is( + $session->got_rakp2($original_tag, $code), + undef, + sprintf('RAKP2 code 0x%02x keeps the retry return', $code), + ); + is(scalar(@sent), 1, 'one replacement Open Session request is sent'); + is($session->{attempthash}, 1, 'replacement request selects SHA1'); + ok(!exists($session->{zero_rakp2_fallback}), 'ordinary fallback is not persisted'); + is($session->{localsid}, $original_localsid + 1, 'replacement request uses a new console session ID'); + is($session->{rmcptag}, $original_tag + 1, 'replacement request uses a new message tag'); + is_deeply( + [@{ $sent[0]->{payload} }[12, 20, 28]], + [1, 1, 1], + 'replacement request proposes suite 3 algorithms', + ); + is_deeply(\@errors, [], 'fallback does not invoke the login callback'); + + $session->{sessionestablishmentcontext} = xCAT::IPMI::STATE_EXPECTINGRAKP2(); + is( + $session->got_rakp2($session->{rmcptag}, $code), + 9, + 'the replacement rejection keeps the existing error return', + ); + is(scalar(@sent), 1, 'SHA1 is not downgraded a second time'); + is_deeply(\@errors, ["ERROR: $error"], 'the replacement rejection reports the existing error'); + is( + $session->{sessionestablishmentcontext}, + xCAT::IPMI::STATE_FAILED(), + 'the replacement rejection fails the exchange', + ); + } +}; + subtest 'captured all-zero SHA256 RAKP2 retries once with suite 3' => sub { my @errors; my @sent; my $session = new_session(errors => \@errors); + my $original_localsid = $session->{localsid}; + my $original_tag = $session->{rmcptag}; local *xCAT::IPMI::sendpayload = sub { my ($self, %args) = @_; push @sent, \%args; @@ -142,6 +248,8 @@ subtest 'captured all-zero SHA256 RAKP2 retries once with suite 3' => sub { is(scalar(@sent), 1, 'one replacement Open Session request is sent'); is($session->{attempthash}, 1, 'retry selects SHA1'); ok($session->{zero_rakp2_fallback}, 'fallback is recorded for this login'); + is($session->{localsid}, $original_localsid + 1, 'replacement request uses a new console session ID'); + is($session->{rmcptag}, $original_tag + 1, 'replacement request uses a new message tag'); is( $session->{sessionestablishmentcontext}, xCAT::IPMI::STATE_OPENSESSION(),