2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-09 05:00:44 +00:00

fix(credentials): serve the passwd hash of a node's configured sudoer

getcredentials answered xcat_secure_pw only for root, so a postscript
had no way to get the password of another node account from the passwd
table.

xcat_secure_pw:<user> now returns the password field of the passwd row
key=system,username=<user> when <user> is root or a sudoer named in the
postscripts or postbootscripts of the requesting node, its osimage, or
xcatdefaults. A sudoer without a row or without a password gets the
locked field "!", so the node applies the reply as is. Any other user,
an invalid user name, or a failed hash answers with an error instead of
an empty reply. The root request reads the same row as before and keeps
the error reply for a missing row.
This commit is contained in:
Vinícius Ferrão
2026-09-03 18:25:12 -03:00
parent a631bc6fba
commit a712ec33d9
+64 -6
View File
@@ -328,12 +328,13 @@ sub process_request
next;
} elsif ($parm =~ /xcat_secure_pw:/) {
xCAT::MsgUtils->trace(0, 'I', "credentials: sending $parm to $client");
my @users=split(/:/,$parm);
if (defined($users[1]) and $users[1] eq 'root') {
my $pass = xCAT::PasswordUtils::crypt_system_password();
if ($pass) {
push @{$rsp->{'data'}}, { content => [ $pass ], desc => [ $parm ] };
}
my (undef, $user) = split(/:/, $parm);
my ($hash, $error) = system_password_hash($client, $user);
if ($hash) {
push @{ $rsp->{'data'} }, { content => [$hash], desc => [$parm] };
} else {
push @{ $rsp->{'error'} }, "Unable to get the password hash for $parm: $error";
xCAT::MsgUtils->trace(0, 'W', "credentials: Unable to get the password hash for $parm: $error");
}
next;
} else {
@@ -550,6 +551,63 @@ sub _sign_x509_certificate {
return $certificate;
}
# A sudoer without a passwd row gets the locked field so the node applies the reply as is.
sub system_password_hash {
my ($node, $user) = @_;
unless (defined($user) and $user =~ /^[A-Za-z_][A-Za-z0-9_.-]{0,31}$/) {
return (undef, 'invalid user name');
}
unless ($user eq 'root' or configured_sudoers($node)->{$user}) {
return (undef, "$user is not a configured sudoer of $node");
}
my %key = (key => 'system', username => $user);
my $passwd = xCAT::Table->new('passwd', -create => 0);
my $entry = $passwd ? $passwd->getAttribs(\%key, 'password') : undef;
$passwd->close() if $passwd;
unless ($entry and defined($entry->{password})) {
return ('!', undef) unless $user eq 'root';
return (undef, 'no password in the passwd table for root');
}
my $hash = xCAT::PasswordUtils::crypt_system_password('passwd', \%key, [ 'password', 'cryptmethod' ]);
return ($hash, $hash ? undef : "unable to hash the password of $user");
}
# The sudoer postscript entries of a node, from the same three sources
# Postage.pm uses: xcatdefaults, the osimage of provmethod, and the node.
sub configured_sudoers {
my $node = shift;
my @lists;
my $posttab = xCAT::Table->new('postscripts', -create => 0);
if ($posttab) {
my $defaults = $posttab->getAttribs({ node => 'xcatdefaults' }, 'postscripts', 'postbootscripts');
my $own = $posttab->getNodeAttribs($node, [ 'postscripts', 'postbootscripts' ]);
push @lists, map { ($_->{postscripts}, $_->{postbootscripts}) } grep { $_ } ($defaults, $own);
$posttab->close();
}
my $typetab = xCAT::Table->new('nodetype', -create => 0);
my $type = $typetab ? $typetab->getNodeAttribs($node, ['provmethod']) : undef;
$typetab->close() if $typetab;
if ($type and $type->{provmethod} and $type->{provmethod} !~ /^(?:install|netboot|statelite)$/) {
my $imagetab = xCAT::Table->new('osimage', -create => 0);
my $image = $imagetab ? $imagetab->getAttribs({ imagename => $type->{provmethod} }, 'postscripts', 'postbootscripts') : undef;
$imagetab->close() if $imagetab;
push @lists, ($image->{postscripts}, $image->{postbootscripts}) if $image;
}
my %sudoers;
foreach my $entry (map { split /,/, $_ } grep { defined } @lists) {
next unless $entry =~ /^\s*sudoer(?:\s+(.*?))?\s*$/;
my $args = defined $1 ? $1 : '';
my $name = $args =~ /(?:^|\s)-u\s*(\S+)/ ? $1 : 'xcat';
$sudoers{$name} = 1;
}
return \%sudoers;
}
sub ok_with_node {
my $node = shift;