mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-07-31 18:19:40 +00:00
Merge pull request #7678 from VersatusHPC/refactor/ipmi-sha1-fallback
refactor(ipmi): centralize RMCP+ SHA1 fallback
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user