Commit 7d2e31a9 authored by Jonathon Duerig's avatar Jonathon Duerig
Browse files

Merge branch 'master' of git-public.flux.utah.edu:/flux/git/emulab-devel

parents 99b526d0 c8f06bef
......@@ -9,7 +9,7 @@ use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(SiteVarExists GetSiteVar SetSiteVar NoLogins);
@EXPORT = qw(SiteVarExists GetSiteVar SetSiteVar NoLogins IsShutDown);
use emdb;
......@@ -123,6 +123,10 @@ sub SetSiteVar($$)
#
sub NoLogins()
{
my $shutdown = IsShutDown();
return 1
if ($shutdown);
my $query_result =
DBQueryWarn("select value from sitevariables ".
"where name='web/nologins'");
......@@ -136,4 +140,20 @@ sub NoLogins()
return ($value ? 1 : 0);
}
# Ditto shutdown.
sub IsShutDown()
{
my $query_result =
DBQueryWarn("select value from sitevariables ".
"where name='general/testbed_shutdown'");
return 1
if (!$query_result);
return 0
if (!$query_result->numrows);
my ($value) = $query_result->fetchrow_array();
return ($value ? 1 : 0);
}
1;
......@@ -31,6 +31,7 @@ my $ELABINELAB = @ELABINELAB@;
my $WINSUPPORT = @WINSUPPORT@;
my $QUOTA_FSLIST= '@FS_WITH_QUOTAS@';
my $SCRATCHDIR = '@FSDIR_SCRATCH@';
my $NOSHAREDFS = @NOSHAREDFS@;
#
# Fixed paths for clients
......@@ -381,14 +382,17 @@ Phase "exports", "Setting up exports", sub {
# /share is special. We want to export to boss/ops read-write,
# but to the control network read-only.
#
my ($a,$b,$c,$d) = ($CONTROL_NETWORK =~ /^(\d*)\.(\d*)\.(\d*)\.(\d*)/);
my $realdir = `realpath $SHAREROOT`;
chomp($realdir);
push(@exports_lines,
"$realdir\t$BOSSNODE $USERNODE -maproot=root");
push(@exports_lines,
"$realdir\t-network ${a}.${b}.${c} -mask $CONTROL_NETMASK ".
"-maproot=root -ro -alldirs");
if (!$NOSHAREDFS) {
my ($a,$b,$c,$d) =
($CONTROL_NETWORK =~ /^(\d*)\.(\d*)\.(\d*)\.(\d*)/);
push(@exports_lines,
"$realdir\t-network ${a}.${b}.${c} ".
"-mask $CONTROL_NETMASK -maproot=root -ro -alldirs");
}
#
# Put them in exports.head, and copy that to /etc/exports
......
......@@ -45,6 +45,7 @@ my $QUOTA_FSLIST= '@FS_WITH_QUOTAS@';
my $ETCDIR = "$PREFIX/etc";
my $LIBDIR = "$PREFIX/lib";
my $SCRATCHDIR = '@FSDIR_SCRATCH@';
my $NOSHAREDFS = @NOSHAREDFS@;
my $OUTER_BOSS = '@OUTERBOSS_NODENAME@';
if ($OUTER_BOSS eq '')
{ $OUTER_BOSS = "www.emulab.net"; }
......@@ -770,15 +771,17 @@ Phase "exports", "Setting up exports", sub {
# /share is special. We want to export to boss read-write,
# but to the control network read-only.
#
my ($a,$b,$c,$d) =
($CONTROL_NETWORK =~ /^(\d*)\.(\d*)\.(\d*)\.(\d*)/);
my $realdir = `realpath $SHAREROOT`;
chomp($realdir);
push(@exports_lines,
"$realdir\t$BOSSNODE -maproot=root");
push(@exports_lines,
"$realdir\t-network ${a}.${b}.${c} -mask $CONTROL_NETMASK ".
"-maproot=root -ro -alldirs");
if (!$NOSHAREDFS) {
my ($a,$b,$c,$d) =
($CONTROL_NETWORK =~ /^(\d*)\.(\d*)\.(\d*)\.(\d*)/);
push(@exports_lines,
"$realdir\t-network ${a}.${b}.${c} ".
"-mask $CONTROL_NETMASK -maproot=root -ro -alldirs");
}
}
#
......
......@@ -83,6 +83,7 @@ BEGIN
}
}
use emdb;
use libEmulab;
use libtestbed;
#
......@@ -141,6 +142,14 @@ my @INCDIRS = ("-I${objdir}", "-I${objdir}/../tbsetup",
"-I@prefix@/lib"
);
# Chicken or Egg.
DBQueryFatal("INSERT INTO sitevariables VALUES ".
" ('general/testbed_shutdown',NULL,'0', ".
" 'Non-zero value indicates that the testbed is shutdown ".
"and scripts should not do anything when they run. ".
"DO NOT SET THIS BY HAND!', 0)")
if (!SiteVarExists("general/testbed_shutdown"));
#
# Dump the testbed version numbers in all modes.
#
......@@ -216,7 +225,8 @@ sub Restart()
}
if ($nologins) {
print "Turning on the web interface and allowing swaps\n";
if (system("$SETSITEVAR web/nologins -") ||
if (system("$SETSITEVAR general/testbed_shutdown -") ||
system("$SETSITEVAR web/nologins -") ||
system("$SETSITEVAR web/message -")) {
print "*** Could not renable the web interface and swapping!\n";
exit(1);
......@@ -250,6 +260,10 @@ if ($stopbed) {
if ($?) {
Fatal("Could not disable web logins and experiment swaps");
}
system("$SETSITEVAR general/testbed_shutdown 1");
if ($?) {
Fatal("Could not set general/testbed_shutdown");
}
$nologins = 1;
print "-> Waiting a few seconds for testbed to quiet down ...\n";
......
#
# Note that all actions *MUST* be idempotent; if this script is run
# again, it should always do the right thing, not break if something
# was already done. See boss-install for lots of example of how to use
# libinstall to help with this.
#
use strict;
use libinstall;
my $NEWSYSLOG_CONF = "/etc/newsyslog.conf";
my $REBOOTLOG = "$LOGDIR/reboot.log";
my $POWERLOG = "$LOGDIR/power.log";
my $CACONF = "$TBROOT/lib/ssl/ca.cnf";
sub InstallUpdate($$)
{
my ($version, $phase) = @_;
#
# If something should run in the pre-install phase.
#
if ($phase eq "pre") {
Phase "newsyslog", "Updating $NEWSYSLOG_CONF", sub {
DoneIfEdited($NEWSYSLOG_CONF);
BackUpFileFatal($NEWSYSLOG_CONF);
AppendToFileFatal($NEWSYSLOG_CONF, "$REBOOTLOG 640 14 300 * Z");
};
Phase "ca.cnf", "Updating ssl CA config file", sub {
PhaseSkip("Already updated")
if (`grep 'Added for update 5.10' $CACONF`);
BackUpFileFatal($CACONF);
ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl ca.cnf");
# For impotent mode.
DiffFiles("$TOP_OBJDIR/ssl/ca.cnf", $CACONF);
ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl install-conf");
};
}
#
# If something should run in the post-install phase.
#
if ($phase eq "post") {
#
# Rename the older power log to reboot log.
#
Phase "newsyslog", "Renaming power log", sub {
DoneIfExists($REBOOTLOG);
DoneIfDoesntExist($POWERLOG);
ExecQuietFatal("/bin/mv $POWERLOG $REBOOTLOG");
};
}
return 0;
}
1;
......@@ -2462,10 +2462,20 @@ sub SliverWorkAux($$$$$$$)
# The API states we return a credential to control the aggregate.
#
my $sliver_credential = $aggregate->NewCredential($owner);
if (!defined($sliver_credential)) {
$message = "Could not create credential";
goto bad;
}
#
# Cache the credential for subsequent requests. See GetSliver().
# Okay if this fails.
#
if ($isupdate) {
GeniCredential->DeleteForTarget($aggregate);
}
$sliver_credential->Store();
if (!$isupdate) {
$experiment->SetState(EXPTSTATE_ACTIVE());
......@@ -2673,6 +2683,13 @@ sub RenewSliverAux($$)
$message = "Could not set expiration time";
goto bad;
}
#
# Need to delete any cached credentials.
#
my $aggregate = GeniAggregate->SliceAggregate($slice);
if (defined($aggregate)) {
GeniCredential->DeleteForTarget($aggregate);
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
......@@ -3032,15 +3049,31 @@ sub GetSliverAux($)
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"No slivers here for slice");
}
my $new_credential = $aggregate->NewCredential($user);
if (!defined($new_credential)) {
my $agg_credential = GeniCredential->Lookup($aggregate, $user);
if (defined($agg_credential)) {
#
# Check for expiration and for changed certificate.
#
if ($agg_credential->IsExpired() ||
!$agg_credential->SameCerts($aggregate, $user)) {
$agg_credential->Delete();
$agg_credential = undef;
}
}
if (!defined($agg_credential)) {
$agg_credential = $aggregate->NewCredential($user);
# Okay if this fails.
$agg_credential->Store()
if (defined($agg_credential));
}
if (!defined($agg_credential)) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
$new_credential->asString());
$agg_credential->asString());
}
#
......@@ -3912,6 +3945,8 @@ sub CleanupDeadSlice($;$)
# Unregister the slice at the SA.
UnRegisterSliver($slice);
}
GeniCredential->DeleteForTarget($topaggregate)
if (defined($topaggregate));
#
# Find any aggregates and tear them down.
......
......@@ -296,6 +296,16 @@ sub Flush($)
delete($certificates{$self->uuid()});
}
#
# Compare two certs.
#
sub SameCert($$)
{
my ($self, $other) = @_;
return $self->GetCertificate()->cert() eq $other->GetCertificate()->cert();
}
#
# Load a certificate from a string. This creates an object, but does
# not store it in the DB.
......@@ -308,25 +318,64 @@ sub LoadFromString($$)
print STDERR "Improper chars in certificate string\n";
return undef;
}
my ($tempfile, $filename) = tempfile(UNLINK => 1);
if (!$tempfile) {
print STDERR "Could not create tempfile for cert string\n";
require Socket;
import Socket qw(:DEFAULT);
require IO::Handle; # thousands of lines just for autoflush :-(
if (! socketpair(CHILD, PARENT, AF_UNIX(), SOCK_STREAM(), PF_UNSPEC())) {
print STDERR "LoadFromString: Could not create socketpair\n";
return undef;
}
CHILD->autoflush(1);
PARENT->autoflush(1);
my $childpid = fork();
if (! $childpid) {
close CHILD;
#
# Dup our descriptors to the parent, and exec the program.
# The parent then talks to it read/write.
#
open(STDIN, "<&PARENT") || die "Cannot redirect stdin";
open(STDOUT, ">&PARENT") || die "Cannot redirect stdout";
open(STDERR, ">&PARENT") || die "Cannot redirect stderr";
exec("$OPENSSL x509 -subject -text");
die("*** $0:\n".
" exec openssl x509 failed: $!\n");
}
close PARENT;
#
# Write the certificate to the child.
#
# The certificate might already have the header and footer
# so only add them if needed.
#
if ($string =~ /^-----BEGIN CERTIFICATE-----/) {
print $tempfile $string;
} else {
print $tempfile "-----BEGIN CERTIFICATE-----\n";
print $tempfile $string;
print $tempfile "\n" if $string !~ /\n$/;
print $tempfile "-----END CERTIFICATE-----\n";
print CHILD $string;
}
my $certificate = GeniCertificate->LoadFromFile($filename);
unlink($filename);
else {
print CHILD "-----BEGIN CERTIFICATE-----\n";
print CHILD $string;
print CHILD "\n" if $string !~ /\n$/;
print CHILD "-----END CERTIFICATE-----\n";
}
# Tell the process we are done writing. ie: Send it an EOF.
shutdown(CHILD,1);
my @certlines = ();
while (<CHILD>) {
push(@certlines, $_);
}
close(CHILD);
waitpid($childpid, 0);
if ($? || !@certlines) {
print STDERR "openssl x509 failed to parse certificate\n";
return undef;
}
my $certificate = GeniCertificate->LoadFromArray(@certlines);
return undef
if (!defined($certificate));
......@@ -341,8 +390,6 @@ sub LoadFromString($$)
sub LoadFromFile($$)
{
my ($class, $filename) = @_;
my $url;
my $urn;
if (! open(X509, "$OPENSSL x509 -in $filename -subject -text |")) {
print STDERR "Could not start $OPENSSL on $filename\n";
......@@ -356,7 +403,21 @@ sub LoadFromFile($$)
print STDERR "Could not load certificate from $filename\n";
return undef;
}
my $certificate = GeniCertificate->LoadFromArray(@certlines);
return undef
if (!defined($certificate));
$certificate->{'CERT'}->{'certfile'} = $filename;
return $certificate;
}
sub LoadFromArray($@)
{
my $class = shift();
my @certlines = @_;
my $url;
my $urn;
#
# The first line is the DN (subject).
#
......@@ -390,7 +451,7 @@ sub LoadFromFile($$)
}
}
if (!@certlines) {
print STDERR "Could not parse certificate from $filename\n";
print STDERR "Could not parse certificate!\n";
return undef;
}
......@@ -437,12 +498,77 @@ sub LoadFromFile($$)
$self->{'CERT'}->{'privkey'} = undef;
$self->{'CERT'}->{'revoked'} = undef;
$self->{'CERT'}->{'created'} = undef;
$self->{'CERT'}->{'certfile'} = $filename;
$self->{'CERT'}->{'uri'} = $url;
$self->{'CERT'}->{'urn'} = $urn;
return $self;
}
#
# Pipe a certificate (and maybe key) to a command and read back results
# for the caller.
#
sub PipeTo($$$)
{
my ($self, $withkey, $string) = @_;
print STDERR "PipeTo: $self, '$string'\n";
require Socket;
import Socket qw(:DEFAULT);
require IO::Handle; # thousands of lines just for autoflush :-(
if (! socketpair(CHILD, PARENT, AF_UNIX(), SOCK_STREAM(), PF_UNSPEC())) {
print STDERR "*** PipeTo: Could not create socketpair\n";
return undef;
}
CHILD->autoflush(1);
PARENT->autoflush(1);
my $childpid = fork();
if (! $childpid) {
close CHILD;
#
# Dup our descriptors to the parent, and exec the program.
# The parent then talks to it read/write.
#
open(STDIN, "<&PARENT") || die "Cannot redirect stdin";
open(STDOUT, ">&PARENT") || die "Cannot redirect stdout";
open(STDERR, ">&PARENT") || die "Cannot redirect stderr";
exec($string);
die("*** $0:\n".
" exec '$string' failed: $!\n");
}
close PARENT;
#
# Write the certificate to the child.
#
print CHILD "-----BEGIN CERTIFICATE-----\n";
print CHILD $self->cert();
print CHILD "-----END CERTIFICATE-----\n";
if ($withkey && $self->privkey()) {
print CHILD "-----BEGIN RSA PRIVATE KEY-----\n";
print CHILD $self->privkey();
print CHILD "-----END RSA PRIVATE KEY-----\n";
}
# Tell the process we are done writing. ie: Send it an EOF.
shutdown(CHILD,1);
my @certlines = ();
while (<CHILD>) {
push(@certlines, $_);
}
close(CHILD);
waitpid($childpid, 0);
if ($? || !@certlines) {
print STDERR "*** Failed to parse certificate: '$string'\n";
return undef;
}
return @certlines;
}
#
# Store a certificate that was loaded from a string/file.
#
......@@ -507,11 +633,12 @@ sub URL($)
return $url
if (defined($url));
my $filename = $self->WriteToFile();
if (! open(X509, "$OPENSSL x509 -in $filename -text -noout |")) {
print STDERR "Could not start $OPENSSL on $filename\n";
my @certlines = $self->PipeTo(0, "$OPENSSL x509 -text -noout");
if (! @certlines) {
print STDERR "Could not get text from $self\n";
return undef;
}
# Note that we really want to put only URNs in the subjectAltName,
# and all URLs in the subjectInfoAccess. However, old certificates
# used subjectAltName for URLs, so for temporary backward compatibility
......@@ -519,7 +646,7 @@ sub URL($)
my ($alturl,$accessurl);
my $altname = 0;
my $accessinfo = 0;
while (<X509>) {
for (@certlines) {
if( /^\s+X509v3 Subject Alternative Name:\s*$/ ) {
$altname = 1;
} elsif( /^\s+Authority Information Access:\s*$/ ) {
......@@ -543,11 +670,10 @@ sub URL($)
}
$url = defined( $accessurl ) ? $accessurl :
defined( $alturl ) ? $alturl : undef;
if (!close(X509) || !defined($url)) {
print STDERR "Could not find url in certificate from $filename\n";
if (!defined($url)) {
print STDERR "Could not find url in $self\n";
return undef;
}
unlink($filename);
$self->{'CERT'}->{'uri'} = $url;
$self->{'URL'} = $url;
return $url;
......@@ -566,26 +692,26 @@ sub URN($)
return $urn
if (defined($urn));
my $filename = $self->WriteToFile();
if (! open(X509, "$OPENSSL x509 -in $filename -text -noout |")) {
print STDERR "Could not start $OPENSSL on $filename\n";
my @certlines = $self->PipeTo(0, "$OPENSSL x509 -text -noout");
if (! @certlines) {
print STDERR "Could not get text from $self\n";
return undef;
}
my $altname = 0;
while (<X509>) {
for (@certlines) {
if( /^\s+X509v3 Subject Alternative Name:\s*$/ ) {
$altname = 1;
} elsif( $altname ) {
}
elsif ($altname) {
m'^\s*URI:(urn:publicid:[-!#$%()*+,./0-9:;=?@A-Z_a-z~]+)\s*$' and $urn = $1
foreach split( /, / );
$altname = 0;
}
}
if (!close(X509) || !defined($urn)) {
print STDERR "Could not find URN in certificate from $filename\n";
if (!defined($urn)) {
print STDERR "Could not find URN in $self\n";
return undef;
}
unlink($filename);
$self->{'URN'} = $urn;
return $urn;
}
......@@ -593,22 +719,13 @@ sub URN($)
sub asText($)
{
my ($self) = @_;
my $text = "";
my $filename = $self->WriteToFile();
if (! open(X509, "$OPENSSL x509 -in $filename -text |")) {
print STDERR "Could not start $OPENSSL on $filename\n";
return undef;
}
while (<X509>) {
$text .= $_;
}
if (!close(X509) || $text eq "") {
print STDERR "Could not dump text of certificate from $filename\n";
my @certlines = $self->PipeTo(0, "$OPENSSL x509 -text");
if (! @certlines) {
print STDERR "Could not convert $self to text\n";
return undef;
}
unlink($filename);
return $text;
return join("", @certlines);
}
#
......@@ -710,6 +827,7 @@ sub uri($) { return undef; }
sub urn($) { return field($_[0], "urn"); }
sub URL($) { return undef; }
sub URN($) { return field($_[0], "urn"); }
sub GetCertificate($) { return $_[0]; }
#
# Need to add DN to the emulab table.
......@@ -720,22 +838,16 @@ sub DN($)
return $self->{'CERT'}->{'DN'}
if (exists($self->{'CERT'}->{'DN'}));
# Deleted when scope is left.
my $tempfile = new File::Temp(UNLINK => 1);
my $filename = $tempfile->filename;
print $tempfile "-----BEGIN CERTIFICATE-----\n";
print $tempfile "$self->cert()";
print $tempfile "-----END CERTIFICATE-----\n";
my $dn = `$OPENSSL x509 -in $filename -noout -subject`;
if ($?) {
print STDERR "Failed to convert $filename to uuid!\n";
my @certlines =
GeniCertificate::PipeTo($self, 0, "$OPENSSL x509 -noout -subject");
if (!@certlines) {
print STDERR "Failed to get DN from $self!\n";
return undef;
}
my ($dn) = @cer