Commit 59bdb0b4 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint before starting on aggregate stuff

parent 2fd98548
......@@ -29,7 +29,6 @@ CREATE TABLE `geni_users` (
`name` tinytext,
`email` tinytext,
`sa_idx` int(10) unsigned NOT NULL default '0',
`cert` text,
PRIMARY KEY (`idx`),
KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
......@@ -50,13 +49,11 @@ CREATE TABLE `geni_users` (
#
DROP TABLE IF EXISTS `geni_components`;
CREATE TABLE `geni_components` (
`hrn` varchar(256) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`hrn` varchar(256) NOT NULL default '',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`name` tinytext,
`url` tinytext,
`cert` text,
PRIMARY KEY (`idx`),
UNIQUE KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
......@@ -76,16 +73,13 @@ CREATE TABLE `geni_components` (
#
DROP TABLE IF EXISTS `geni_sliceauthorities`;
CREATE TABLE `geni_sliceauthorities` (
`id` varchar(8) NOT NULL default '',
`id_idx` mediumint(8) unsigned NOT NULL default '0',
`hrn` varchar(256) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`uuid_prefix` varchar(8) NOT NULL default '',
`uuid_prefix` varchar(12) NOT NULL default '',
`created` datetime default NULL,
`name` tinytext,
`url` tinytext,
`pubkey` text,
PRIMARY KEY (`id_idx`),
UNIQUE KEY `id` (`id`),
PRIMARY KEY (`idx`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -126,7 +120,9 @@ CREATE TABLE `geni_slivers` (
`creator_uuid` varchar(40) NOT NULL default '',
`node_id` varchar(32) default NULL,
`created` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
`credential_idx` int(10) unsigned default NULL,
`ticket_idx` int(10) unsigned default NULL,
`component_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
UNIQUE KEY `uuid` (`uuid`),
INDEX `slice_uuid` (`slice_uuid`)
......@@ -143,9 +139,41 @@ CREATE TABLE `geni_tickets` (
`created` datetime default NULL,
`redeem_before` datetime default NULL,
`valid_until` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
`component_idx` int(10) unsigned NOT NULL default '0',
`ticket_string` text,
PRIMARY KEY (`idx`),
INDEX `owner_uuid` (`owner_uuid`),
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Table to remember credentials.
#
DROP TABLE IF EXISTS `geni_credentials`;
CREATE TABLE `geni_credentials` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`owner_uuid` varchar(40) NOT NULL default '',
`this_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`valid_until` datetime default NULL,
`credential_string` text,
PRIMARY KEY (`idx`),
INDEX `owner_uuid` (`owner_uuid`),
INDEX `this_uuid` (`this_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Table to hold uuid<->certificate bindings, keeping them out of the other
# tables above. We use this on both the client and server side (storing the
# private key), say for a sliver.
#
DROP TABLE IF EXISTS `geni_certificates`;
CREATE TABLE `geni_certificates` (
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`cert` text,
`privkey` text,
`revoked` datetime default NULL,
PRIMARY KEY (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -153,6 +153,25 @@ sub url($) { return field($_[0], "url"); }
sub hrn($) { return field($_[0], "hrn"); }
sub cert($) { return field($_[0], "cert"); }
#
# Does the uuid prefix match.
#
sub PrefixMatch($$)
{
my ($self, $uuid) = @_;
return 0
if (!ref($self));
my $uuid_prefix = $self->uuid_prefix();
if ($uuid =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/) {
return 1
if ("$uuid_prefix" eq "$1");
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -272,32 +272,18 @@ sub RegisterSlice($)
# The SA UUID comes from the SSL environment (certificate). Verify it
# and the prefix match for the uuid.
#
# Need to verify the UUID is permitted for the SA making the request.
#
my $sa_uuid = $ENV{'GENIUUID'};
my $query_result =
DBQueryWarn("select idx, uuid_prefix from geni_sliceauthorities ".
"where uuid='$sa_uuid'");
return GeniResponse->Create(GENIRESPONSE_DBERROR)
if (!defined($query_result));
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef, "Who are You?")
if (!$query_result->numrows);
my ($sa_idx, $uuid_prefix)= $query_result->fetchrow_array();
if ($uuid =~ /^\w+\-\w+\-\w+\-\w+\-(\w+)$/) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"uuid: Prefix mismatch")
if ("$uuid_prefix" ne "$1");
my $authority = GeniAuthority->Lookup($sa_uuid);
if (!defined($authority)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"No slice authority record for $sa_uuid");
}
else {
if (! $authority->PrefixMatch($uuid)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper format for uuid");
"uuid: Prefix mismatch");
}
my $newslice = GeniSlice->Create($hrn, $uuid, $creator_uuid, $cert,
$sa_idx);
$authority->idx());
if (!defined($newslice)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$hrn/$uuid could not be registered");
......
......@@ -24,6 +24,7 @@ use Genixmlrpc;
use GeniResponse;
use GeniTicket;
use GeniCredential;
use GeniCertificate;
use GeniSlice;
use GeniSliver;
use GeniUser;
......@@ -68,7 +69,7 @@ sub DiscoverResources($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
GeniCredential->CertificateInfo($slice, \$slice_uuid) == 0 or
GeniCertificate->CertificateInfo($slice, \$slice_uuid) == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
......@@ -142,7 +143,7 @@ sub GetTicket($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
GeniCredential->CertificateInfo($slice_cert, \$slice_uuid) == 0 or
GeniCertificate->CertificateInfo($slice_cert, \$slice_uuid) == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
......@@ -265,6 +266,10 @@ sub CreateSliver($)
my ($argref) = @_;
my $owner_uuid = $ENV{'GENIUSER'};
my $ticket = $argref->{'ticket'};
my $impotent = $argref->{'impotent'};
$impotent = 0
if (!defined($impotent));
if (! (defined($ticket) &&
!TBcheck_dbslot($ticket, "default", "text",
......@@ -289,6 +294,25 @@ sub CreateSliver($)
"No local experiment for slice");
}
#
# See if we have a record of this slice in the DB. If not, throw an
# error; might change later.
#
my $slice = GeniSlice->Lookup($ticket->slice_uuid());
if (!defined($slice)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No slice record for slice");
}
#
# Ditto the user.
#
my $owner = GeniUser->Lookup($owner_uuid);
if (!defined($owner)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No user record for $owner_uuid");
}
my $sliver = GeniSliver->Create($ticket);
if (!defined($sliver)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -300,7 +324,7 @@ sub CreateSliver($)
# so this should just work, unless the node has been released cause
# it has been too long.
#
if ($sliver->Provision() != 0) {
if (!$impotent && $sliver->Provision() != 0) {
$sliver->Delete();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not provision sliver");
......@@ -308,15 +332,12 @@ sub CreateSliver($)
#
# The API states we return a credential to control the sliver.
#
my $credential = GeniCredential->Create($sliver->uuid(),
$owner_uuid);
my $credential = $sliver->NewCredential($owner);
if (!defined($credential)) {
print STDERR "Could not create a credential for $sliver!\n";
return -1;
}
if ($credential->Sign()) {
print STDERR "Could not sign sliver credential!\n";
return -1;
$sliver->UnProvision();
$sliver->Delete();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create credential sliver");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $credential->asString());
}
......@@ -328,24 +349,22 @@ sub DestroySliver($)
{
my ($argref) = @_;
my $owner_uuid = $ENV{'GENIUSER'};
my $sliver_uuid = $argref->{'uuid'};
my $sliver_cert = $argref->{'sliver'};
my $credential = $argref->{'credential'};
my $sliver_uuid;
if (!defined($sliver_uuid)) {
if (!defined($sliver_cert) || !defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS);
}
GeniCertificate->CertificateInfo($sliver_cert, \$sliver_uuid) == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
my $sliver = GeniSliver->Lookup($sliver_uuid);
if (!defined($sliver)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"No such sliver $sliver_uuid");
}
if (! (defined($credential) &&
!TBcheck_dbslot($credential, "default", "text",
TBDB_CHECKDBSLOT_ERROR))) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"credential: ". TBFieldErrorString());
}
$credential = GeniCredential->CreateFromSigned($credential);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -357,7 +376,6 @@ sub DestroySliver($)
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Invalid credentials for operation");
}
$sliver->UnProvision() == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not unprovision sliver");
......
......@@ -41,45 +41,6 @@ my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "myboss.little-emulab-bsd61.testbed.emulab.net";
my $GENICENTRALURL = "https://$GENICENTRAL/protogeni/xmlrpc";
sub CreateSliver($$$)
{
my ($experiment, $ticket, $pref) = @_;
#
# XXX
#
my $this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
print STDERR "You ($UID) do not exist!\n";
return -1;
}
return $ticket->component()->CreateSliver($this_user, $ticket, $pref);
}
sub DestroySliver($$)
{
my ($experiment, $sliver) = @_;
#
# XXX
#
my $this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
print STDERR "You ($UID) do not exist!\n";
return -1;
}
my $component = $sliver->GetComponent();
return -1
if (!defined($component));
$component->DestroySliver($this_user, $sliver) == 0
or return -1;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -26,6 +26,7 @@ use XML::Simple;
use XML::LibXML;
use Data::Dumper;
use File::Temp qw(tempfile);
use overload ('""' => 'Stringify');
# Configure variables
my $TB = "@prefix@";
......@@ -38,6 +39,253 @@ my $SIGNCRED = "$TB/sbin/signgenicred";
my $VERIFYCRED = "$TB/sbin/verifygenicred";
my $NFREE = "$TB/bin/nfree";
my $OPENSSL = "/usr/bin/openssl";
my $MKCERT = "$TB/sbin/mksyscert";
# Cache of instances to avoid regenerating them.
my %certificates = ();
#
# Lookup by uuid only.
#
sub Lookup($$)
{
my ($class, $token) = @_;
my $query_result;
if (! ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
return undef;
}
# Look in cache first
return $certificates{"$token"}
if (exists($certificates{"$token"}));
$query_result =
DBQueryWarn("select * from geni_certificates where uuid='$token'");
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'CERT'} = $query_result->fetchrow_hashref();
bless($self, $class);
# Add to cache.
$certificates{$token} = $self;
return $self;
}
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $uuid = $self->uuid();
return "[GeniCertificate: $uuid]";
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'CERT'}->{$_[1]}); }
sub uuid($) { return field($_[0], "uuid"); }
sub created($) { return field($_[0], "created"); }
sub cert($) { return field($_[0], "cert"); }
sub privkey($) { return field($_[0], "privkey"); }
sub revoked($) { return field($_[0], "revoked"); }
#
# Create a certificate pair, which gives us a uuid to use for an object.
#
sub Create($$;$)
{
my ($class, $what, $uuid) = @_;
# Let mkcert generate a new one.
$uuid = ""
if (!defined($uuid));
if (! open(CERT, "$MKCERT $what $uuid |")) {
print STDERR "Could not start $MKCERT\n";
return undef;
}
my @certlines = ();
while (<CERT>) {
push(@certlines, $_);
}
if (!close(CERT)) {
print STDERR "Could not generate a new certificate with $MKCERT\n";
return undef;
}
my $cert;
my $privkey;
my $string;
foreach my $line (@certlines) {
if ($line =~ /^-----BEGIN CERT/ ||
$line =~ /^-----BEGIN RSA/) {
$string = "";
next;
}
if ($line =~ /^-----END CERT/) {
$cert = $string;
$string = undef;
next;
}
if ($line =~ /^-----END RSA/) {
$privkey = $string;
$string = undef;
next;
}
$string .= $line
if (defined($string));
}
if (! (defined($privkey) && defined($cert))) {
print STDERR "Could not generate a new certificate with $MKCERT\n";
return undef;
}
if (! ($cert =~ /^[\012\015\040-\176]*$/)) {
print STDERR "Improper chars in certificate string\n";
return undef;
}
if (GeniCertificate->CertificateInfo($cert, \$uuid) != 0){
print STDERR "Could not find uuid in certificate: $cert\n";
return undef;
}
my $safe_cert = DBQuoteSpecial($cert);
my $safe_uuid = DBQuoteSpecial($uuid);
my $safe_key = DBQuoteSpecial($privkey);
return undef
if (!DBQueryWarn("insert into geni_certificates set ".
" created=now(), uuid=$safe_uuid, ".
" cert=$safe_cert, privkey=$safe_key"));
return GeniCertificate->Lookup($uuid);
}
#
# Delete ...
#
sub Delete($)
{
my ($self) = @_;
my $uuid = $self->uuid();
return -1
if (!DBQueryWarn("delete from geni_certificates where uuid='$uuid'"));
return 0;
}
#
# Store a public key for a uuid. This is for something that is hosted
# elsewhere and we just want to save the pubkey away in case we need it.
#
sub StorePublic($$)
{
my ($class, $cert) = @_;
if (! ($cert =~ /^[\012\015\040-\176]*$/)) {
print STDERR "Improper chars in certificate string\n";
return undef;
}
my $uuid;
if (GeniCertificate->CertificateInfo($cert, \$uuid) != 0){
print STDERR "Could not find uuid in certificate: $cert\n";
return undef;
}
my $safe_cert = DBQuoteSpecial($cert);
my $safe_uuid = DBQuoteSpecial($uuid);
return undef
if (!DBQueryWarn("insert into geni_certificates set ".
" created=now(), uuid=$safe_uuid, cert=$safe_cert"));
return GeniCertificate->Lookup($uuid);
}
#
# Write a certificate and private key to a tempfile, as for signing with it.
#
sub WriteToFile($)
{
my ($self) = @_;
# Deleted when object is released
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";
print $tempfile "-----BEGIN RSA PRIVATE KEY-----\n";
print $tempfile $self->privkey();
print $tempfile "-----END RSA PRIVATE KEY-----\n";
return $filename;
}
#
# Convert a certificate to its uuid.
#
sub CertificateInfo($$$)
{
my ($class, $string, $pref) = @_;
# Deleted when scope is left.
my $tempfile = new File::Temp(UNLINK => 1);
my $filename = $tempfile->filename;
print $tempfile "-----BEGIN CERTIFICATE-----\n";
print $tempfile "$string";
print $tempfile "-----END CERTIFICATE-----\n";
my $dn = `$OPENSSL x509 -in $filename -noout -subject`;
if ($?) {
print STDERR "Cailed to convert $filename to uuid!\n";
return -1;
}
if ($dn =~ /CN=([-\w]*)$/) {
$$pref = $1;
}
else {
return -1;
}
return 0;
}
############################################################################
#
# Wrapper for local users.
#
package GeniCertificate::LocalUser;
use English;
use libdb;
#
# Create a wrapper, with the same access names.
#
sub Create($$)
{
my ($class, $user) = @_;
my $uid_idx = $user->uid_idx();
my $query_result =
DBQueryWarn("select * from user_sslcerts ".
"where uid_idx='$uid_idx' and encrypted=1 and ".
" revoked is null");
return undef
if (!defined($query_result) || !$query_result->numrows);
my $self = {};
$self->{'CERT'} = $query_result->fetchrow_hashref();
bless($self, $class);
return $self;
}
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'CERT'}->{$_[1]}); }
sub uuid($) { return field($_[0], "uuid"); }
sub created($) { return field($_[0], "created"); }
sub cert($) { return field($_[0], "cert"); }
sub privkey($) { return field($_[0], "privkey"); }
sub revoked($) { return field($_[0], "revoked"); }
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -40,6 +40,9 @@ my $OURDOMAIN = "@OURDOMAIN@";
my $SIGNCRED = "$TB/sbin/signgenicred";
my $VERIFYCRED = "$TB/sbin/verifygenicred";
# Do not allocated nodes.
my $impotent = 1;
# Cache of instances to avoid regenerating them.
my %components = ();
......@@ -268,7 +271,7 @@ sub GetTicket($$$$$)
"CM::GetTicket",
{ "slice" => $slice->cert(),
"credential" => $credential->asString(),
"impotent" => 1,
"impotent" => $impotent,
"rspec" => $rspec_xml });
return undef
......@@ -297,7 +300,8 @@ sub CreateSliver($$$$)
my $response =
Genixmlrpc::CallMethodHTTP($self->url(), $user,
"CM::CreateSliver",
{ "ticket" => $ticket->asString() });
{ "ticket" => $ticket->asString(),
"impotent" => $impotent });
return undef
if (!defined($response));
......@@ -327,7 +331,7 @@ sub CreateSliver($$$$)
#
sub DestroySliver($$$)
{
my ($self, $user, $sliver) = @_;
my ($self, $sliver, $user) = @_;
# Must be a real reference.
return -1
......@@ -340,7 +344,7 @@ sub DestroySliver($$$)
my $response =
Genixmlrpc::CallMethodHTTP($self->url(), $user,
"CM::DestroySliver",
{ "uuid" => $sliver->uuid(),
{ "sliver" => $sliver->cert(),
"credential" =>
$credential->asString() });
......@@ -348,12 +352,6 @@ sub DestroySliver($$$)
print STDERR "Could not destroy sliver $sliver\n";
return -1;
}
# Delete the local object from the DB.
$sliver->Delete() == 0
or return -1;
# And the credential since we no longer need to maintain it for
# this sliver.
return 0;
}
......
......@@ -19,6 +19,7 @@ use vars qw(@ISA @EXPORT);
# Must come after package declaration!
use lib '@prefix@/lib';
use GeniDB;
use GeniCertificate;
use libtestbed;
use Experiment;
use libdb qw(TBGetUniqueIndex);
......@@ -138,7 +139,7 @@ sub CreateFromSigned($$)
if (!defined($uuid_node));
my $this_cert = $uuid_node->to_literal();
my $this_uuid;
GeniCredential->CertificateInfo($this_cert, \$this_uuid) == 0
GeniCertificate->CertificateInfo($this_cert, \$this_uuid) == 0
or return undef;
if (! ($this_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
......@@ -153,7 +154,7 @@ sub CreateFromSigned($$)
if (!defined($uuid_node));
my $owner_cert = $uuid_node->to_literal();
my $owner_uuid;
GeniCredential->CertificateInfo($owner_cert, \$owner_uuid) == 0
GeniCertificate->CertificateInfo($owner_cert, \$owner_uuid) == 0
or return undef;
if (! ($owner_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
......@@ -199,7 +200,7 @@ sub Delete($)
#
sub Sign($$)
{
my ($self, $flag) = @_;
my ($self, $how) = @_;
return -1
if (!ref($self));
......@@ -239,11 +240,36 @@ sub Sign($$)
print $fh $template;
close($fh);
#
# Who signs the credential? $how is either a flag (CM or SA) or its
# a certificate object in the DB.
#
my $certificate;
if (ref($how)) {
# This will auto delete too.
my $certfile = $how->WriteToFile();
if (!defined($certfile)) {
print STDERR "Could not write $certificate to temp file\n";
return -1;
}
$certificate = "-c $certfile";
}
elsif ($how == $LOCALSA_FLAG) {
$certificate = "-c $TB/etc/genisa.pem";
}
elsif ($how == $LOCALCM_FLAG) {
$certificate = "-c $TB/etc/genicm.pem";
}
else {
print STDERR "Invalid 'how' argument to Sign()\n";
return -1;
}