Commit c85367b4 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint

parent 3e2702be
......@@ -24,6 +24,8 @@ CREATE TABLE `geni_users` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`expires` datetime default NULL,
`locked` datetime default NULL,
`archived` datetime default NULL,
`status` enum('active','archived','frozen') NOT NULL default 'frozen',
`name` tinytext,
......@@ -100,6 +102,8 @@ CREATE TABLE `geni_slices` (
`uuid` varchar(40) NOT NULL default '',
`exptidx` int(11) default NULL,
`created` datetime default NULL,
`expires` datetime default NULL,
`locked` datetime default NULL,
`creator_uuid` varchar(40) NOT NULL default '',
`name` tinytext,
`sa_uuid` varchar(40) NOT NULL default '',
......@@ -123,6 +127,8 @@ CREATE TABLE `geni_slivers` (
`resource_uuid` varchar(40) NOT NULL default '',
`resource_type` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`expires` datetime default NULL,
`locked` datetime default NULL,
`credential_idx` int(10) unsigned default NULL,
`component_uuid` varchar(40) default NULL,
`aggregate_uuid` varchar(40) default NULL,
......@@ -146,6 +152,8 @@ CREATE TABLE `geni_aggregates` (
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`expires` datetime default NULL,
`locked` datetime default NULL,
`credential_idx` int(10) unsigned default NULL,
`component_idx` int(10) unsigned NOT NULL default '0',
`aggregate_idx` int(10) unsigned default NULL,
......@@ -166,6 +174,7 @@ CREATE TABLE `geni_tickets` (
`slice_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`redeem_before` datetime default NULL,
`locked` datetime default NULL,
`valid_until` datetime default NULL,
`component_uuid` varchar(40) NOT NULL default '',
`seqno` int(10) unsigned NOT NULL default '0',
......
......@@ -19,7 +19,7 @@ LIB_SCRIPTS = GeniDB.pm GeniUser.pm GeniSAClient.pm \
GeniUtil.pm GeniRegistry.pm
SBIN_SCRIPTS = plabnodewrapper plabslicewrapper
SCRIPTS = test.pl addnode.pl test.pl addauthority
SCRIPTS = test.pl addnode.pl
#OPS_LIBS = GeniCMClient.pm GeniSAClient.pm
# These scripts installed setuid, with sudo.
......
This diff is collapsed.
......@@ -230,6 +230,7 @@ sub LoadFromString($$)
my $certificate = GeniCertificate->LoadFromFile($filename);
unlink($filename);
$certificate->{'CERT'}->{'certfile'} = undef;
return $certificate;
}
......
......@@ -399,7 +399,7 @@ sub Sign($$)
if (ref($how)) {
# This will auto delete too.
my $certfile = $how->certfile() || $how->WriteToFile();
my $certfile = $how->certfile() || $how->WriteToFile(1);
if (!defined($certfile)) {
print STDERR "Could not write $how to temp file\n";
return -1;
......
......@@ -133,7 +133,8 @@ sub GetCredential($)
if (!defined($slice));
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Not your slice!")
if ($slice->creator_uuid() ne $this_user->uuid());
if ($slice->creator_uuid() ne $this_user->uuid() &&
!$slice->IsBound($this_user));
#
# Return a credential for the slice.
......@@ -250,7 +251,7 @@ sub Resolve($)
"hrn" => $geniuser->hrn(),
"uuid" => $geniuser->uuid(),
"email" => $geniuser->email(),
"cert" => $geniuser->cert(),
"gid" => $geniuser->cert(),
"name" => $geniuser->name(),
};
#$blob->{'sliverkeys'} = \@sliverkeys
......@@ -620,5 +621,111 @@ sub DiscoverResources($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS, 0);
}
#
# Return ssh keys.
#
sub GetKeys($)
{
my ($argref) = @_;
my $cred = $argref->{'credential'};
if (! defined($cred)) {
return GeniResponse->MalformedArgsResponse();
}
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
#
# And that the target of the credential is this registry.
#
my $authority = GeniAuthority->Lookup($ENV{'MYUUID'});
if (!defined($authority)) {
print STDERR "Could not find local authority object\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if ($credential->target_uuid() ne $authority->uuid()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN,
undef, "This is not your registry!");
}
my $this_user = GeniUser->Lookup($credential->owner_uuid(), 1);
if (!defined($this_user)) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN,
undef, "Who are you?");
}
my @keys;
if ($this_user->GetKeys(\@keys) != 0) {
print STDERR "Could not get keys for $this_user\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, @keys);
}
#
# Bind a user to a slice. The slice creator does this so that the target
# user can request his own credential to manipulate the slice. This is in
# leu of delegation.
#
sub BindToSlice($)
{
my ($argref) = @_;
my $cred = $argref->{'credential'};
my $uuid = $argref->{'uuid'};
if (!defined($uuid) || !defined($cred)) {
return GeniResponse->MalformedArgsResponse();
}
if (!($uuid =~ /^[-\w]*$/)) {
return GeniResponse->MalformedArgsResponse();
}
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
my $this_user = GeniUser->Lookup($credential->owner_uuid(), 1);
if (!defined($this_user)) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN,
undef, "Who are you?");
}
my $slice = GeniSlice->Lookup($credential->target_uuid());
if (!defined($slice)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Unknown slice for this credential");
}
#
# Locate the target user; must exist locally.
#
my $target_user = GeniUser->Lookup($uuid, 1);
if (!defined($target_user)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "No such user here");
}
if ($slice->BindUser($target_user) != 0) {
print STDERR "Could not bind $target_user to $slice\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -111,6 +111,7 @@ sub Lookup($$)
}
$self->{'CERT'} = $certificate;
$self->{'BINDINGS'} = undef;
$self->{'LOCKED'} = 0;
# Add to cache.
$slices{$self->{'SLICE'}->{'idx'}} = $self;
......@@ -124,10 +125,12 @@ sub hrn($) { return field($_[0], "hrn"); }
sub uuid($) { return field($_[0], "uuid"); }
sub creator_uuid($) { return field($_[0], "creator_uuid"); }
sub created($) { return field($_[0], "created"); }
sub expires($) { return field($_[0], "expires"); }
sub sa_uuid($) { return field($_[0], "sa_uuid"); }
sub exptidx($) { return field($_[0], "exptidx"); }
sub cert($) { return $_[0]->{'CERT'}->cert(); }
sub GetCertificate($) { return $_[0]->{'CERT'}; }
sub LOCKED($) { return $_[0]->{'LOCKED'}; }
#
# Stringify for output.
......@@ -212,6 +215,7 @@ sub Create($$$$;$)
# Now tack on other stuff we need.
push(@insert_data, "created=now()");
push(@insert_data, "expires=DATE_ADD(now(), INTERVAL 1 HOUR)");
push(@insert_data, "idx='$idx'");
push(@insert_data, "sa_uuid='$sa_uuid'");
push(@insert_data, "exptidx=$exptidx")
......@@ -237,6 +241,53 @@ sub Create($$$$;$)
return GeniSlice->Lookup($idx);
}
#
# We lock at a very coarse grain, mostly in the CM. When a slice is busy
# we cannot expire things from it.
#
sub Lock($)
{
my ($self) = @_;
my $idx = $self->idx();
# We already have it locked.
return 0
if ($self->LOCKED());
DBQueryWarn("lock tables geni_slices write")
or return -1;
my $query_result =
DBQueryWarn("select locked from geni_slices ".
"where idx='$idx' and locked is null");
if (!$query_result || !$query_result->numrows) {
DBQueryWarn("unlock tables");
return 1;
}
$query_result =
DBQueryWarn("update geni_slices set locked=now() where idx='$idx'");
DBQueryWarn("unlock tables");
return 1
if (!$query_result);
$self->{'LOCKED'} = $$;
return 0;
}
sub UnLock($)
{
my ($self) = @_;
my $idx = $self->idx();
return 1
if (!$self->LOCKED());
DBQueryWarn("update geni_slices set locked=NULL where idx='$idx'")
or return -1;
$self->{'LOCKED'} = 0;
return 0;
}
#
# Class function to create new Geni slice from a local experiment.
# We want to create the key pair so that we can sign credentials.
......@@ -344,6 +395,16 @@ sub Delete($)
return 0;
}
#
# Flush from our little cache, as for the expire daemon.
#
sub Flush($)
{
my ($self) = @_;
delete($slices{$self->idx()});
}
#
# Return the emulab experiment for this slice.
#
......@@ -462,5 +523,52 @@ sub UserBindings($$)
return 0;
}
#
# Is the user bound to a slice.
#
sub IsBound($$)
{
my ($self, $user) = @_;
return -1
if (! (ref($self) && ref($user)));
my $slice_uuid = $self->uuid();
my $user_uuid = $user->uuid();
my $query_result =
DBQueryWarn("select user_uuid from geni_bindings ".
"where slice_uuid='$slice_uuid' and ".
" user_uuid='$user_uuid'");
return 0
if (!$query_result);
return $query_result->numrows();
}
#
# Set the expiration time for a slice.
#
sub SetExpiration($$)
{
my ($self, $expires) = @_;
my $uuid = $self->uuid();
if ($expires =~ /^\d+$/) {
$expires = "FROM_UNIXTIME($expires)";
}
else {
$expires = "'$expires'";
}
my $query_result =
DBQueryWarn("update geni_slices set expires=$expires " .
"where uuid='$uuid'");
return -1
if (!$query_result);
$self->{'SLICE'}->{'expires'} = $expires;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -28,6 +28,9 @@ use English;
use XML::Simple;
use XML::LibXML;
use Data::Dumper;
use Date::Parse;
use POSIX qw(strftime);
use Time::Local;
use File::Temp qw(tempfile);
use overload ('""' => 'Stringify');
......@@ -78,6 +81,10 @@ sub Lookup($$)
return undef
if (!defined($ticket));
# We ignore this in the ticket. In fact, we need to change how we bring
# tickets back into the system.
$ticket->{'redeem_before'} = $row->{'redeem_before'};
# Mark as coming from the DB.
$ticket->{'idx'} = $idx;
$ticket->{'stored'} = 1;
......@@ -109,6 +116,12 @@ sub Create($$$$)
$self->{'component'} = undef;
$self->{'stored'} = 0; # Stored to the DB.
#
# For now, all tickets expire very quickly ...
#
$self->{'redeem_before'} =
POSIX::strftime("20%y-%m-%dT%H:%M:%S", localtime(time() + (2*60)));
#
# Locally generated tickets need a local DB index, which can be the
# same as the sequence number. A ticket from a remote component will
......@@ -136,6 +149,7 @@ sub ticket_uuid($) { return field($_[0], "ticket_uuid"); }
sub ticket($) { return field($_[0], "ticket"); }
sub asString($) { return field($_[0], "ticket_string"); }
sub ticket_string($) { return field($_[0], "ticket_string"); }
sub redeem_before($) { return field($_[0], "redeem_before"); }
sub component_uuid($) { return field($_[0], "component_uuid"); }
sub component($) { return field($_[0], "component"); }
sub stored($) { return field($_[0], "stored"); }
......@@ -157,6 +171,16 @@ sub Stringify($)
return "[GeniTicket: $idx, slice:$slice_uuid]";
}
#
# Flush from our little cache, as for the expire daemon.
#
sub Flush($)
{
my ($self) = @_;
delete($tickets{$self->idx()});
}
#
# Create a ticket object from a signed ticket string.
#
......@@ -288,6 +312,8 @@ sub CreateFromSignedTicket($$;$$)
print STDERR "Could not find the ticket $seqno in the DB\n";
return undef;
}
my $row = $query_result->fetchrow_hashref();
$self->{'redeem_before'} = $row->{'redeem_before'};
$self->{'idx'} = $seqno;
$self->{'stored'} = 1;
}
......@@ -349,6 +375,7 @@ sub Store($)
my $slice_uuid = $self->slice_uuid();
my $owner_uuid = $self->owner_uuid();
my $ticket_uuid= $self->ticket_uuid();
my $expires = $self->redeem_before();
#
# For a locally created/signed ticket, seqno=idx. For a ticket from
......@@ -370,6 +397,7 @@ sub Store($)
push(@insert_data, "ticket_uuid='$ticket_uuid'");
push(@insert_data, "slice_uuid='$slice_uuid'");
push(@insert_data, "owner_uuid='$owner_uuid'");
push(@insert_data, "redeem_before='$expires'");
my $safe_ticket = DBQuoteSpecial($self->ticket_string());
push(@insert_data, "ticket_string=$safe_ticket");
......@@ -395,6 +423,7 @@ sub Sign($)
if (!ref($self));
my $idx = $self->seqno();
my $expires = $self->redeem_before();
my $slice_cert = $self->slice_cert()->cert();
my $owner_cert = $self->owner_cert()->cert();
my $rspec_xml = XMLout($self->rspec(), "NoAttr" => 1);
......@@ -406,6 +435,10 @@ sub Sign($)
my $ticket_uuid = NewUUID();
$self->{'ticket_uuid'} = $ticket_uuid;
# Convert to GMT.
$expires = POSIX::strftime("20%y-%m-%dT%H:%M:%S",
gmtime(str2time($expires)));
#
# Create a template xml file to sign.
#
......@@ -417,10 +450,10 @@ sub Sign($)
" <owner_gid>$owner_cert</owner_gid>\n".
" <target_gid>$slice_cert</target_gid>\n".
" <uuid>$ticket_uuid</uuid>\n".
" <expires>2008-05-10T09:00:00</expires>\n".
" <expires>$expires</expires>\n".
" <ticket>\n".
" <can_delegate>1</can_delegate>\n".
" <redeem_before>2008-05-10T09:00:00</redeem_before>\n".
" <redeem_before>$expires</redeem_before>\n".
" $rspec_xml\n".
" </ticket>\n".
"</credential>\n";
......@@ -553,5 +586,22 @@ sub SameTicket($$)
return $self->idx() == $other->idx();
}
#
# Check if ticket has expired. Use the DB directly.
#
sub Expired($)
{
my ($self) = @_;
my $idx = $self->idx();
my $query_result =
DBQueryWarn("select idx from geni_tickets ".
"where idx='$idx' and ".
" (UNIX_TIMESTAMP(now()) > ".
" UNIX_TIMESTAMP(redeem_before))");
return $query_result->numrows;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -286,8 +286,8 @@ sub Modify($$$$)
my $idx = $self->idx();
my $uuid = $self->uuid();
my $safe_name = DBQuoteSpecial($name);
my $safe_email = DBQuoteSpecial($email);
my $safe_name = DBQuoteSpecial($name || $self->name());
my $safe_email = DBQuoteSpecial($email || $self->email());
return -1
if (!DBQueryWarn("update geni_users set ".
......@@ -484,6 +484,16 @@ sub Archive($)
return 0;
}
#
# Is this a local user.
#
sub IsLocal($)
{
my ($self) = @_;
return ref($self) eq "GeniUser::LocalUser";
}
############################################################################
#
# Wrapper for local users.
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Data::Dumper;
use Getopt::Std;
my $optlist = "c";
my $asch = 0;
#
# Check args.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"c"})) {
$asch = 1;
}
use vars qw($GENI_DBNAME);
if ($asch) {
$GENI_DBNAME = "geni-ch";
}
# Now we can load the libraries after setting the proper DB.
use lib '@prefix@/lib';
require GeniDB;
require GeniCertificate;
require GeniAuthority;
require GeniComponent;
my $type = $ARGV[0];
my $url = $ARGV[1];
my $hrn = $ARGV[2];
my $filename = $ARGV[3];
sub AddAuthority($$$)
{
my ($type, $url, $filename) = @_;
my $prefix;
my $certificate = GeniCertificate->LoadFromFile($filename);
if (!defined($certificate)) {
die("Could not get certificate from $filename\n");
}
if ($certificate->uuid() =~ /\w*-(\w*)$/) {
$prefix = $1;
}
else {
die("Could not get prefix from uuid\n");
}
GeniAuthority->Create($certificate, $url, $prefix, $type)
or die("Could not add new authority\n");
return 0;
}
sub AddComponent($$)
{
my ($url, $filename) = @_;
my $certificate = GeniCertificate->LoadFromFile($filename);
if (!defined($certificate)) {
die("Could not get certificate from $filename\n");
}
GeniComponent->Create($certificate, $url)
or die("Could not add new component\n");
return 0;
}
if (0) {
#
# On real boss.
#
$type = "sa";
$url = "https://www.emulab.net/protogeni/xmlrpc/sa";
$hrn = "emulab.sa";
$filename = "@prefix@/etc/genisa.pem";
AddAuthority($type, $url, $filename);
}
else {
$type = "sa";
$url = "https://myboss.myelab.testbed.emulab.net/protogeni/xmlrpc/sa";
$hrn = "myelab.sa";
$filename = "@prefix@/etc/genisa.pem";
AddAuthority($type, $url, $filename);
if ($asch) {
$type = "sa";
$url = "https://www.emulab.net/protogeni/xmlrpc/sa";
$filename = "@prefix@/etc/emulabsa.pem";
AddAuthority($type, $url, $filename);
$type = "ma";
$url = "https://myboss.myelab.testbed.emulab.net/protogeni/xmlrpc/ch";
$filename = "@prefix@/etc/genima.pem";
AddAuthority($type, $url, $filename);
$type = "ma";
$url = "https://boss.emulab.net/protogeni/xmlrpc/cm";
$filename = "@prefix@/etc/emulabcm.pem";
AddAuthority($type, $url, $filename);
$url = "https://myboss.myelab.testbed.emulab.net/protogeni/xmlrpc/cm";
$filename = "@prefix@/etc/genicm.pem";
AddComponent($url, $filename);
$url = "https://boss.emulab.net/protogeni/xmlrpc/cm";
$filename = "@prefix@/etc/emulabcm.pem";
AddComponent($url, $filename);
}
}
......@@ -12,7 +12,7 @@ SUBDIR = protogeni/scripts
include $(OBJDIR)/Makeconf
SBIN_STUFF = cleanupslice
PSBIN_STUFF = register_resources \
PSBIN_STUFF = register_resources expire_daemon \
createcerts initsite addauthority getcacerts
# These scripts installed setuid, with sudo.
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
#
# Look for things that need to be expired and resources released. This is
# incredibly primitive at the moment.
#
sub usage()
{
print "Usage: expire_daemon [-d] [-c | -s]\n";
exit(1);
}
my $optlist = "d";
my $debug = 1;
my $asch = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $PGENIDOMAIN = "@PROTOGENI_DOMAIN@";
my $PGENISUPPORT = @PROTOGENI_SUPPORT@;
my $LOGFILE = "$TB/log/geniexpire";
my $IDLETIMES = "$TB/sbin/idletimes";
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Protos
sub fatal($);
#
# Turn off line buffering on output
#
$| = 1;
if ($UID != 0) {
fatal("Must be root to run this script\n");
}
#
# Check args early so we get the right DB.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
# Do this early so that we talk to the right DB.
use vars qw($GENI_DBNAME);
$GENI_DBNAME = ($asch ? "geni-ch" : "geni-cm");
$LOGFILE .= ($asch ? ".ch" : ".cm");
# Load the Testbed support stuff.
use lib "@prefix@/lib";
require GeniDB;
require GeniUtil;
require GeniTicket;
require GeniSlice;
require GeniCM;
use libtestbed;
GeniUtil::FlipToGeniUser();
# Go to ground.