mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 07:08:12 +00:00
178 lines
7.5 KiB
Perl
178 lines
7.5 KiB
Perl
#!/usr/bin/env perl
|
|
use strict;
|
|
use warnings;
|
|
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/../lib";
|
|
use File::Spec;
|
|
use File::Slurper qw(read_text write_text);
|
|
use File::Temp qw(tempdir);
|
|
use Test::More;
|
|
|
|
use XCAT::Test::File qw(repo_path);
|
|
|
|
my $bash = qx(command -v bash 2>/dev/null);
|
|
chomp($bash);
|
|
my $sh = qx(command -v sh 2>/dev/null);
|
|
chomp($sh);
|
|
my $openssl = qx(command -v openssl 2>/dev/null);
|
|
chomp($openssl);
|
|
my $host_fips = 0;
|
|
if (-r '/proc/sys/crypto/fips_enabled') {
|
|
$host_fips = read_text('/proc/sys/crypto/fips_enabled') =~ /^1\s*$/;
|
|
}
|
|
|
|
SKIP: {
|
|
skip 'sh, bash, and openssl are required', 40
|
|
unless $sh && -x $sh && $bash && -x $bash && $openssl && -x $openssl;
|
|
|
|
foreach my $runtime (
|
|
['xCAT-genesis-scripts/usr/lib/xcat/fips.sh', $sh],
|
|
['xCAT/postscripts/xcatlib.sh', $bash],
|
|
) {
|
|
my ($relative, $shell) = @{$runtime};
|
|
my $library = repo_path($relative);
|
|
my $tmpdir = tempdir(CLEANUP => 1);
|
|
my $status = File::Spec->catfile($tmpdir, 'fips_enabled');
|
|
my $missing = File::Spec->catfile($tmpdir, 'missing');
|
|
my $packet = File::Spec->catfile($tmpdir, 'discovery-packet');
|
|
write_text($packet, "<xcatrequest><command>findme</command></xcatrequest>\n");
|
|
|
|
write_text($status, "1\n");
|
|
is(shell_output($shell, $library, 'xcat_fips_state "$2"', $status), '1',
|
|
"$relative recognizes enabled FIPS state");
|
|
write_text($status, "0\n");
|
|
is(shell_output($shell, $library, 'xcat_fips_state "$2"', $status), '0',
|
|
"$relative recognizes disabled FIPS state");
|
|
write_text($status, "1 0\n");
|
|
is(shell_output($shell, $library, 'xcat_fips_state "$2"', $status), '0',
|
|
"$relative rejects malformed FIPS state");
|
|
is(shell_output($shell, $library, 'xcat_fips_state "$2"', $missing), '0',
|
|
"$relative treats missing FIPS state as disabled");
|
|
is(shell_output($shell, $library, 'set -u; xcat_fips_state'),
|
|
$host_fips ? '1' : '0',
|
|
"$relative accepts its default status path with nounset");
|
|
my $invalid_key = File::Spec->catfile($tmpdir, 'invalid-key.pem');
|
|
isnt(shell_status($shell, $library,
|
|
'xcat_generate_discovery_private_key invalid "$2"', $invalid_key), 0,
|
|
"$relative rejects invalid private-key policy");
|
|
isnt(shell_status($shell, $library,
|
|
'xcat_discovery_public_key invalid "$2"', $invalid_key), 0,
|
|
"$relative rejects invalid public-key policy");
|
|
|
|
my $fips_key = File::Spec->catfile($tmpdir, 'fips-key.pem');
|
|
my $fips_public = File::Spec->catfile($tmpdir, 'fips-key.pub');
|
|
my $fips_signature = File::Spec->catfile($tmpdir, 'fips-signature');
|
|
write_text($status, "1\n");
|
|
is(run_key_cycle($shell, $library, $status, 0, $fips_key, $fips_public), 0,
|
|
"$relative reuses enabled state for key generation and extraction");
|
|
my $fips_key_text = command_output(
|
|
$openssl, 'ec', '-in', $fips_key, '-text', '-noout'
|
|
);
|
|
like($fips_key_text, qr/Private-Key: \(256 bit\)/,
|
|
"$relative generates a P-256 FIPS key");
|
|
is(shell_output($shell, $library, 'xcat_discovery_public_key 0 "$2"', $fips_key),
|
|
read_text($fips_public), "$relative exports an EC key after FIPS is disabled");
|
|
my $public_body = read_text($fips_public);
|
|
$public_body =~ s/-----[^-]+-----//g;
|
|
$public_body =~ s/\s+//g;
|
|
# lldpad reads the system description into a 256-byte buffer.
|
|
cmp_ok(length($public_body), '<=', 255,
|
|
"$relative FIPS public key fits the lldpad description buffer");
|
|
is(system($openssl, 'dgst', '-sha512', '-sign', $fips_key,
|
|
'-out', $fips_signature, $packet), 0,
|
|
"$relative FIPS key signs discovery data");
|
|
is(system($openssl, 'dgst', '-sha512', '-verify', $fips_public,
|
|
'-signature', $fips_signature, $packet), 0,
|
|
"$relative FIPS public key verifies discovery data");
|
|
|
|
SKIP: {
|
|
skip 'legacy RSA is unavailable on a FIPS-enabled test host', 4
|
|
if $host_fips;
|
|
|
|
my $legacy_key = File::Spec->catfile($tmpdir, 'legacy-key.pem');
|
|
my $legacy_public = File::Spec->catfile($tmpdir, 'legacy-key.pub');
|
|
my $legacy_signature = File::Spec->catfile($tmpdir, 'legacy-signature');
|
|
write_text($status, "0\n");
|
|
is(run_key_cycle($shell, $library, $status, 1, $legacy_key, $legacy_public), 0,
|
|
"$relative reuses disabled state for key generation and extraction");
|
|
my $legacy_key_text = command_output(
|
|
$openssl, 'rsa', '-in', $legacy_key, '-text', '-noout'
|
|
);
|
|
like($legacy_key_text, qr/Private-Key: \(1024 bit/,
|
|
"$relative preserves the legacy RSA key size");
|
|
is(system($openssl, 'dgst', '-sha512', '-sign', $legacy_key,
|
|
'-out', $legacy_signature, $packet), 0,
|
|
"$relative legacy key signs discovery data");
|
|
is(system($openssl, 'dgst', '-sha512', '-verify', $legacy_public,
|
|
'-signature', $legacy_signature, $packet), 0,
|
|
"$relative legacy public key verifies discovery data");
|
|
}
|
|
}
|
|
|
|
my $genesis_library = repo_path('xCAT-genesis-scripts/usr/lib/xcat/fips.sh');
|
|
is(shell_status($sh, $genesis_library, 'xcat_dsa_allowed 0'), 0,
|
|
'Genesis permits DSA outside FIPS mode');
|
|
isnt(shell_status($sh, $genesis_library, 'xcat_dsa_allowed 1'), 0,
|
|
'Genesis rejects DSA in FIPS mode');
|
|
|
|
my $cumulus_library = repo_path('xCAT/postscripts/xcatlib.sh');
|
|
my $key_dir = tempdir(CLEANUP => 1);
|
|
my $existing_key = "$key_dir/existing.pem";
|
|
system($openssl, 'genrsa', '-out', $existing_key, 2048) == 0
|
|
or die 'prepare existing RSA key';
|
|
my $original_key = read_text($existing_key);
|
|
is(shell_status($bash, $cumulus_library,
|
|
'xcat_prepare_discovery_key 0 "$2"', $existing_key), 0,
|
|
'non-FIPS Cumulus discovery accepts an existing key');
|
|
is(read_text($existing_key), $original_key, 'non-FIPS discovery keeps the existing key');
|
|
is(shell_status($bash, $cumulus_library,
|
|
'xcat_prepare_discovery_key 1 "$2"', $existing_key), 0,
|
|
'FIPS Cumulus discovery replaces an existing bootstrap key');
|
|
like(command_output($openssl, 'ec', '-in', $existing_key, '-text', '-noout'),
|
|
qr/Private-Key: \(256 bit\)/, 'the replacement key uses P-256');
|
|
}
|
|
|
|
done_testing();
|
|
|
|
sub shell_output {
|
|
my ($shell, $library, $command, @arguments) = @_;
|
|
return command_output(
|
|
$shell, '-c', '. "$1"; ' . $command,
|
|
'xcat-fips-test', $library, @arguments,
|
|
);
|
|
}
|
|
|
|
sub shell_status {
|
|
my ($shell, $library, $command, @arguments) = @_;
|
|
system(
|
|
$shell, '-c', '. "$1"; ' . $command,
|
|
'xcat-fips-test', $library, @arguments,
|
|
);
|
|
return $? >> 8;
|
|
}
|
|
|
|
sub run_key_cycle {
|
|
my ($shell, $library, $status, $next_state, $key, $public) = @_;
|
|
my $command = <<'EOF';
|
|
. "$1"
|
|
fips_state=$(xcat_fips_state "$2")
|
|
printf '%s\n' "$3" > "$2"
|
|
xcat_generate_discovery_private_key "$fips_state" "$4" 2>/dev/null || exit
|
|
xcat_discovery_public_key "$fips_state" "$4" > "$5" 2>/dev/null
|
|
EOF
|
|
system(
|
|
$shell, '-c', $command,
|
|
'xcat-fips-test', $library, $status, $next_state, $key, $public,
|
|
);
|
|
return $? >> 8;
|
|
}
|
|
|
|
sub command_output {
|
|
my @command = @_;
|
|
open(my $fh, '-|', @command) or die "run @command: $!";
|
|
my $output = do { local $/; <$fh> };
|
|
close($fh) or die "close @command: $!";
|
|
return $output;
|
|
}
|