Commit ceeede28 authored by Mike Hibler's avatar Mike Hibler

Make the secure boot path work with PXEWAIT.

When a node with the secure boot dongle is freed, it goes into PXEWAIT in
the context of the secure MFS. Previously we remained in "secure mode"
(i.e., did not terminate with a TPMSIGNOFF) while a node was in this state.
If the next use of the node, just booted from the OS that was already on
the disk, then we never signed off properly.

Now we sign off before entering PXEWAIT. I thought that this would be the
easiest alternative to fixing the problem..HaHaHa..not! Because now we have
to restart the secure boot path (i.e., reboot) if the result of coming out
of PXEWAIT is a request to reload the disk (i.e., if we are continuing the
secure disk load path).

Ideally this would have required only modifications to the state machines
for SECUREBOOT/LOAD, but as you can see by the presence of stated.in in the
modified files, this was not the case. The change required some additional
"finesse" to get it working. See the comments in stated.in and bootinfo_mysql.c
if you really care.
parent 3ca3abf6
......@@ -798,9 +798,13 @@ sub stateTransition($$) {
# To differentiate BOOT from LOAD:
# if next_op_mode is SECURELOAD, goto SECURELOAD
# else if mode/state is SECURELOAD/SHUTDOWN, goto SECURELOAD
# else if mode/state is SECURELOAD/REBOOTING, goto SECURELOAD
# else goto SECUREBOOT.
#
debug("Running $SECUREBOOT trigger\n");
# The SHUTDOWN case handles os_load.
# The REBOOT case handles the forced reboot following PXEWAIT
# (inflicted by bootinfo--see bootinfo_mysql.c).
#
my $query_result =
DBQueryWarn("select next_op_mode from nodes ".
"where node_id='$node'");
......@@ -808,8 +812,10 @@ sub stateTransition($$) {
if (!$nextmode) {
$nextmode = $mode;
}
info("Running $SECUREBOOT trigger with $nextmode/$oldstate\n");
if ($nextmode ne TBDB_NODEOPMODE_SECURELOAD ||
$oldstate ne TBDB_NODESTATE_SHUTDOWN) {
($oldstate ne TBDB_NODESTATE_SHUTDOWN &&
$oldstate ne TBDB_NODESTATE_REBOOTING)) {
$nextmode = TBDB_NODEOPMODE_SECUREBOOT;
}
if ($mode ne $nextmode) {
......@@ -1282,11 +1288,23 @@ sub handleCommand($$;$$) {
my $nodelist=join(" ",@nodes);
info("Rebooting nodes: $nodelist\n");
#
# XXX for reboot of nodes in PXEWAIT:
# force == 0,1 -> wake them up, let them re-query
# force == 2 -> wake them up, make them reboot
# force == 3 -> just power cycle em
#
my $opt = "-r";
if ($force == 2) {
$opt .= " -b";
} elsif ($force == 3) {
$opt .= " -k";
}
# Permissions were checked in order to send the message,
# so we don't need to do any fancy stuff here.
my $cmd = "$nodereboot -r $nodelist";
my $cmd = "$nodereboot $opt $nodelist";
debug("$cmd\n");
system("(date; $cmd) >>$rebootlog 2>&1 &") and
system("(echo \"`date`: $cmd\"; $cmd) >>$rebootlog 2>&1 &") and
notify("$params/$command: ".
"Command '$cmd' failed, error $?: $!\n");
......@@ -1313,6 +1331,10 @@ sub handleCommand($$;$$) {
if ($soft_secviolation && $func eq "off" && @nodes > 0 &&
$nodes{$nodes[0]}{state} eq TBDB_NODESTATE_SECVIOLATION) {
info("soft SECVIOLATION: NOT powering off $params\n");
# XXX don't get stuck reloading disk over and over...
system("$osselect -d -c -1 $nodelist");
next;
}
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -87,4 +87,4 @@ client-install: bootinfoclient
$(INSTALL_PROGRAM) bootinfoclient $(DESTDIR)$(CLIENT_BINDIR)
clean:
rm -f *.o core bootinfo bootinfosend testmysql bootinfo_version.c
rm -f *.o core bootinfo bootinfosend bootinfoclient testmysql bootinfo_version.c
......@@ -404,6 +404,41 @@ query_bootinfo_db(struct in_addr ipaddr, char *node_id, int version,
}
rval = 1;
done:
/*
* XXX horrific state-specific hack.
*
* Currently, for nodes that use the gPXE boot dongle, they exit
* secure mode (do a TPM "sign-off") before entering PXEWAIT when
* freed. When such a node is allocated and it has to reload its
* disk, we must reboot the node so that the TPM sign off PCR is
* cleared and the secure boot path is cleanly restarted.
*
* For reasons too bogus to talk about, we cannot currently do
* this from stated. So here we detect a wakeup from PXEWAIT for
* the purposes of reloading the disk via the "secure MFS".
*/
if (!rval && !node_id && info->type == BIBOOTWHAT_TYPE_MFS &&
(info->flags & BIBOOTWHAT_FLAGS_SECURE) != 0) {
res2 = mydb_query("select op_mode,eventstate from "
" nodes as n, interfaces as i "
" where n.node_id=i.node_id and i.IP='%s'",
2, ipstr);
if (res2) {
if (mysql_num_rows(res2)) {
row2 = mysql_fetch_row(res2);
error("Secure booting node %s in %s/%s\n",
ipstr, row2[0], row2[1]);
if (strcmp(row2[0], "PXEKERNEL") == 0 &&
strcmp(row2[1], "PXEBOOTING") == 0) {
info->type = BIBOOTWHAT_TYPE_REBOOT;
error("Forcing reboot of %s\n", ipstr);
}
}
mysql_free_result(res2);
}
}
mysql_free_result(res);
return rval;
}
......
......@@ -420,6 +420,7 @@ REPLACE INTO state_timeouts VALUES ('SECUREBOOT','GPXEBOOTING',60,'STATE:SECVIOL
REPLACE INTO state_timeouts VALUES ('SECUREBOOT','PXEBOOTING',60,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECUREBOOT','SHUTDOWN',300,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECUREBOOT','TPMSIGNOFF',60,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECUREBOOT','PXEWAIT',10,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECURELOAD','BOOTING',300,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECURELOAD','GPXEBOOTING',60,'STATE:SECVIOLATION');
REPLACE INTO state_timeouts VALUES ('SECURELOAD','PXEBOOTING',60,'STATE:SECVIOLATION');
......@@ -641,6 +642,9 @@ REPLACE INTO state_transitions VALUES ('SECUREBOOT','BOOTING','TPMSIGNOFF','Quot
REPLACE INTO state_transitions VALUES ('SECUREBOOT','BOOTING','PXEBOOTING','re-BootInfo');
REPLACE INTO state_transitions VALUES ('SECUREBOOT','GPXEBOOTING','PXEBOOTING','DHCP');
REPLACE INTO state_transitions VALUES ('SECUREBOOT','PXEBOOTING','BOOTING','BootInfo');
REPLACE INTO state_transitions VALUES ('SECUREBOOT','PXEBOOTING','PXEWAIT','BootInfoFree');
REPLACE INTO state_transitions VALUES ('SECUREBOOT','PXEWAIT','SECVIOLATION','QuoteFailed');
REPLACE INTO state_transitions VALUES ('SECUREBOOT','PXEWAIT','TPMSIGNOFF','QuoteOK');
REPLACE INTO state_transitions VALUES ('SECURELOAD','BOOTING','PXEBOOTING','re-BootInfo');
REPLACE INTO state_transitions VALUES ('SECURELOAD','BOOTING','RELOADSETUP','QuoteOK');
REPLACE INTO state_transitions VALUES ('SECURELOAD','BOOTING','SECVIOLATION','QuoteFailed');
......@@ -673,6 +677,7 @@ REPLACE INTO state_transitions VALUES ('WIMRELOAD','SHUTDOWN','SHUTDOWN','Retry'
REPLACE INTO state_transitions VALUES ('WIMRELOAD','SHUTDOWN','PXEBOOTING','WrongPXEboot');
REPLACE INTO state_transitions VALUES ('WIMRELOAD','RELOADSETUP','SHUTDOWN','Error');
REPLACE INTO state_transitions VALUES ('WIMRELOAD','RELOADING','SHUTDOWN','Error');
REPLACE INTO state_transitions VALUES ('PXEKERNEL','PXEBOOTING','REBOOTING','ForcedReboot');
--
-- Dumping data for table `state_triggers`
......
#
# More state management for secure boot.
# These handle a node going into/out-of PXEWAIT.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
my @mode_transitions = (
);
my @timeouts = (
["SECUREBOOT","PXEWAIT",10,"STATE:SECVIOLATION"],
);
my @transitions = (
["PXEKERNEL","PXEBOOTING","REBOOTING","ForcedReboot"],
["SECUREBOOT","PXEBOOTING","PXEWAIT","BootInfoFree"],
["SECUREBOOT","PXEWAIT","SECVIOLATION","QuoteFailed"],
["SECUREBOOT","PXEWAIT","TPMSIGNOFF","QuoteOK"],
);
my @triggers = (
);
foreach my $row (@mode_transitions) {
my ($opm1,$s1,$opm2,$s2,$lab) = @$row;
my $query_result =
DBQueryFatal("SELECT op_mode1 FROM mode_transitions WHERE ".
"op_mode1='$opm1' AND state1='$s1' AND ".
"op_mode2='$opm2' AND state2='$s2'");
if ($query_result->numrows == 0) {
DBQueryFatal("INSERT INTO mode_transitions VALUES ".
"('$opm1','$s1','$opm2', '$s2','$lab')");
}
}
foreach my $row (@timeouts) {
my ($opm,$s,$to,$act) = @$row;
my $query_result =
DBQueryFatal("SELECT op_mode FROM state_timeouts WHERE ".
"op_mode='$opm' AND state='$s'");
if ($query_result->numrows == 0) {
DBQueryFatal("INSERT INTO state_timeouts VALUES ".
"('$opm','$s','$to', '$act')");
}
}
foreach my $row (@transitions) {
my ($opm,$s1,$s2,$lab) = @$row;
my $query_result =
DBQueryFatal("SELECT op_mode FROM state_transitions WHERE ".
"op_mode='$opm' AND state1='$s1' AND state2='$s2'");
if ($query_result->numrows == 0) {
DBQueryFatal("INSERT INTO state_transitions VALUES ".
"('$opm','$s1','$s2','$lab')");
}
}
foreach my $row (@triggers) {
my ($node,$opm,$s,$trig) = @$row;
my $query_result =
DBQueryFatal("SELECT node_id FROM state_triggers WHERE ".
"node_id='$node' AND op_mode='$opm' AND state='$s'");
if ($query_result->numrows == 0) {
DBQueryFatal("INSERT INTO state_triggers VALUES ".
"('$node','$opm','$s','$trig')");
}
}
# tweak a previous transition that wasn't quite right
DBQueryFatal("UPDATE mode_transitions SET state2='SHUTDOWN' WHERE ".
" state1='TPMSIGNOFF' AND op_mode2='PXEKERNEL' AND ".
" state2='BOOTING'");
# no need to BOOTING, CHECKGENISUP as those will happen in next BOOTING
DBQueryFatal("UPDATE state_triggers SET `trigger`='PXEBOOT' WHERE ".
" op_mode='SECUREBOOT' AND state='TPMSIGNOFF'");
return 0;
}
1;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment