2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-05 12:37:54 +00:00
Files
xcat-core/xCAT-server/lib/xcat/plugins/bmcconfig.pm
T
Vinícius Ferrão 0d6929c427 fix(plugins): mask passwords in plugin log messages
Six modules wrote passwords to their own log and diagnostic messages,
outside the daemon redaction pipeline. The z/VM plugin logged each
smcli command line through printSyslog, with the disk read, write and
multi passwords, the image password, the provision root password and
the page volume parm disk password, passed the real disk passwords to
checkSSH_Rc, which echoes the command to syslog and to the client on
failure, and logged raw directory entries whose USER and MDISK
statements carry the logon and disk passwords. The bmcconfig plugin
logged the BMC password in its attribute report, in syslog and in the
command response. The energy plugin logged the HCP password in a
verbose message, and the CIM utilities dumped the whole HTTP request,
with its basic authorization header, to the verbose callback. The PPC
configuration module logged the HMC, FSP and BPA passwords in its
verbose credential reports.

Mask the passwords in the logged text. The executed commands keep the
real values. The page volume log string is built by operand position,
so a decoy value in another operand cannot divert the mask. The
checkSSH_Rc calls receive the masked command string, as the routine
documentation asks. Add redact_directory_entry to the z/VM utilities.
The routine masks the USER, IDENTITY and IDENT logon password, the
MDISK passwords after the access mode in the range form and in the
DEVNO, V-DISK and T-DISK forms, the APPCPASS statement, and the
keyword password assignments in the short and the full spelling. The
match separators stay on one line, so a record without passwords never
masks the record below it, and one or more comment stars do not hide a
credential record from the rules. The COMMAND statement masks whole,
because it can start any CP command with an inline password. Every directory query sink logs
through it,
and the clone loops redact the query output at the source, because the
failure checker and the retained disk list reuse the text. The
directory helpers keep their raw return value for the callers and hand
a redacted copy to the failure checker. Every error branch that echoes
a fetched record after the output check does so through the redactor,
because a password can spell an error word and trip the check: the
directory fetch, the mini disk keyword fetch, and the four disk list
callers. The CIM dump masks
the authorization header. The bmcconfig report now names the password
state, set or missing, which the report needs for diagnosis.
2026-09-02 01:28:19 -03:00

191 lines
7.0 KiB
Perl

# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::bmcconfig;
use Data::Dumper;
use xCAT::Table;
use xCAT::MsgUtils;
use xCAT::Utils;
use xCAT::PasswordUtils;
use xCAT::IMMUtils;
use xCAT::TableUtils;
use IO::Select;
use Socket;
sub handled_commands {
return {
getbmcconfig => 'bmcconfig',
remoteimmsetup => 'bmcconfig',
};
}
sub genpassword {
my $length = shift;
my $password = '';
my $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890';
srand; #have to reseed, rand is not rand otherwise
while (length($password) < $length) {
$password .= substr($characters, int(rand 63), 1);
}
return $password;
}
sub net_parms {
my $ip = shift;
if (inet_aton($ip)) {
$ip = inet_ntoa(inet_aton($ip));
} else {
xCAT::MsgUtils->message("S", "Unable to resolve $ip");
return undef;
}
my $nettab = xCAT::Table->new('networks');
unless ($nettab) { return undef }
my @nets = $nettab->getAllAttribs('net', 'mask', 'gateway');
foreach (@nets) {
my $net = $_->{'net'};
my $mask = $_->{'mask'};
my $gw = $_->{'gateway'};
$ip =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/;
my $ipnum = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
$mask =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/;
my $masknum = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
$net =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ or next; #next if ipv6, TODO: IPv6 support
my $netnum = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
if ($gw eq '<xcatmaster>') {
my @gwd = xCAT::NetworkUtils->my_ip_facing($ip);
unless ($gwd[0]) { $gw = $gwd[1]; }
}
if (($ipnum & $masknum) == $netnum) {
return ($ip, $mask, $gw);
}
}
xCAT::MsgUtils->message("S", "xCAT BMC configuration error, no appropriate network for $ip found in networks, unable to determine netmask");
}
sub ok_with_node {
my $node = shift;
#Here we connect to the node on a privileged port (in the clear) and ask the
#node if it just asked us for credential. It's convoluted, but it is
#a convenient way to see if root on the ip has approved requests for
#credential retrieval. Given the nature of the situation, it is only ok
#to assent to such requests before users can log in. During postscripts
#stage in stateful nodes and during the rc scripts of stateless boot
my $select = new IO::Select;
#sleep 0.5; # gawk script race condition might exist, try to lose just in case
my $sock = new IO::Socket::INET(PeerAddr => $node,
Proto => "tcp",
PeerPort => shift);
my $rsp;
unless ($sock) { return 0 }
$select->add($sock);
print $sock "CREDOKBYYOU?\n";
unless ($select->can_read(5)) { #wait for data for up to five seconds
return 0;
}
my $response = <$sock>;
chomp($response);
if ($response eq "CREDOKBYME") {
return 1;
}
return 0;
}
sub process_request {
my $request = shift;
my $callback = shift;
my $node = $request->{'_xcat_clienthost'}->[0];
my $bmc_mgmt_type = "ipmi";
if ($request->{isopenbmc}->[0]) {
$bmc_mgmt_type = "openbmc";
}
unless (ok_with_node($node, 300)) {
$callback->({ error => ["Unable to prove root on your IP approves of this request"], errorcode => [1] });
return;
}
my $ipmitable = xCAT::Table->new("$bmc_mgmt_type");
my $tmphash;
my $username;
my $gennedpassword = 0;
my $bmc;
my $password;
$tmphash = $ipmitable->getNodesAttribs([$node], [ 'bmc', 'username', 'bmcport', 'password', 'taggedvlan' ]);
my $authmap = xCAT::PasswordUtils::getIPMIAuth(noderange => [$node], ipmihash => $tmphash, keytype => $bmc_mgmt_type);
if ($::XCATSITEVALS{genpasswords} eq "1" or $::XCATSITEVALS{genpasswords} =~ /y(es)?/i) {
$password = genpassword(10) . "1cA!";
$gennedpassword = 1;
} else {
$password = $authmap->{$node}->{password};
}
my $bmcport;
if (defined $tmphash->{$node}->[0]->{bmcport}) {
$bmcport = $tmphash->{$node}->[0]->{bmcport};
}
if ($tmphash->{$node}->[0]->{bmc}) {
$bmc = $tmphash->{$node}->[0]->{bmc};
}
$username = $authmap->{$node}->{username};
my $cliusername;
if ($authmap->{$node}->{cliusername}) {
$cliusername = $authmap->{$node}->{cliusername};
} else {
$cliusername = $username;
}
my $clipassword;
if ($authmap->{$node}->{clipassword}) {
$clipassword = $authmap->{$node}->{clipassword};
} else {
$clipassword = $password;
}
unless (defined $bmc) {
xCAT::MsgUtils->message('S', "Received request from host=$node but unable to determine the $bmc_mgmt_type.bmc value for the node. Verify the node.mgt attribute is configured and the node.bmc is defined.");
$callback->({ error => ["No value specified for '$node.bmc'. Unable to configure the BMC, check the node definition."], errorcode => [1] });
return 1;
}
my $bmcport_counter = 0;
foreach my $sbmc (split /,/, $bmc) {
(my $ip, my $mask, my $gw) = net_parms($sbmc);
unless ($ip and $mask and $username and $password) {
my $passtate = defined($password) && length($password) ? "set" : "missing";
xCAT::MsgUtils->message('S', "Unable to determine IP, Netmask, Username, or Password for $sbmc. Ensure that hostname resolution is working. [IP=$ip Netmask=$mask User=$username Pass=$passtate]",);
$callback->({ error => ["Invalid/Missing BMC related attributes in the node defintion (IP=$ip Netmask=$mask User=$username Pass=$passtate). Unable to configure the BMC, check the node definition."], errorcode => [1] });
return 1;
}
if ($request->{command}->[0] eq 'remoteimmsetup') {
xCAT::IMMUtils::setupIMM($node, cliusername => $cliusername, clipassword => $clipassword, username => $username, password => $password, callback => $callback);
return;
}
my $response = { bmcip => $ip, netmask => $mask, gateway => $gw, username => $username, password => $password };
if (defined $bmcport) {
if ($bmcport =~ /,/) {
my @sbmcport = (split /,/, $bmcport);
$response->{bmcport} = $sbmcport[$bmcport_counter];
} else {
$response->{bmcport} = $bmcport;
}
}
if (defined $tmphash->{$node}->[0]->{taggedvlan}) {
if ($tmphash->{$node}->[0]->{taggedvlan} =~ /,/) {
my @staggedvlan = (split /,/, $tmphash->{$node}->[0]->{taggedvlan});
$response->{taggedvlan} = $staggedvlan[$bmcport_counter];
} else {
$response->{taggedvlan} = $tmphash->{$node}->[0]->{taggedvlan};
}
}
$callback->($response);
$bmcport_counter += 1;
}
if ($gennedpassword) { # save generated password
$ipmitable->setNodeAttribs($node, { password => $password });
}
return 1;
}
1;