Commit 980f6cbd authored by Leigh Stoller's avatar Leigh Stoller

Large set of changes for using the Geni trusted signer tool, to

authenticate Geni users to CloudLab (who do not have Emulab accounts).
CloudLab users must have an account to do anything (unlike APT which allows
guest users). But instead of requiring them to go through the Emulab
account creation (high bar), let then use their Geni credentials to prove
who they are. We then build a local account for that new user, and save off
the speaksfor credential so that we can act on their behalf when talking to
the backend clusters (and their MA to get their ssh keys).

These users do not have a local account password, so they cannot log into
the web interface using the Emulab login page, nor do they have a shell on
ops.

Once authenticated, we put the appropriate cookies into the browser via
javascript, so they can use the Cloud (okay, APT) web interface (they
appear logged in).

I make use of the nonlocal_id field of the users table, which was not being
used for anything else. Officially, these are "nonlocal" users in the code
(IsNonLocal()).

When a nonlocal user instantiates a profile, we use their speaksfor
credential to ask their home MA for their ssh keys, which we then store in
the DB, and then provide to the aggregate via the CreateSliver call.
Note that no provision has been made for users who edit their profile and
add keys; I am not currently expecting these users to stumble into the web
interface (yet).
parent b8fb1917
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -619,6 +619,9 @@ if ($encrypted) {
chmod(0600, $target)
or fatal("Could not chmod $target: $!");
goto skipssh
if ($target_user->IsNonLocal());
#
# Create an SSH key from the private key. Mostly for geni users,
# who tend not to know how to do such things.
......@@ -662,6 +665,7 @@ if ($encrypted) {
" -f $sshdir/encrypted.pub")
== 0 or fatal("Could not add pubkey $sshdir/encrypted.pub");
}
skipssh:
}
TBScriptUnlock();
......
......@@ -38,7 +38,7 @@ use File::Basename;
use overload ('""' => 'Stringify');
use vars qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$NEWUSER_FLAGS_NOUUID $NEWUSER_FLAGS_NONLOCAL $NEWUSER_FLAGS_VIAAPT
$NEWUSER_FLAGS_NOUUID $NEWUSER_FLAGS_NONLOCAL
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED $USERSTATUS_NONLOCAL
......@@ -66,7 +66,6 @@ $NEWUSER_FLAGS_WEBONLY = 0x04;
$NEWUSER_FLAGS_ARCHIVED = 0x08;
$NEWUSER_FLAGS_NOUUID = 0x80;
$NEWUSER_FLAGS_NONLOCAL = 0x40;
$NEWUSER_FLAGS_VIAAPT = 0x20;
# Status values.
$USERSTATUS_ACTIVE = "active";
......@@ -80,7 +79,7 @@ $USERSTATUS_NONLOCAL = "nonlocal";
# Why, why, why?
@EXPORT_OK = qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$NEWUSER_FLAGS_NOUUID $NEWUSER_FLAGS_VIAAPT
$NEWUSER_FLAGS_NOUUID
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED $USERSTATUS_NONLOCAL);
......@@ -110,7 +109,6 @@ sub Lookup($$)
{
my ($class, $token) = @_;
my $status_archived = $USERSTATUS_ARCHIVED;
my $status_nonlocal = $USERSTATUS_NONLOCAL;
my $query_result;
# Look in cache first
......@@ -133,8 +131,7 @@ sub Lookup($$)
$query_result =
DBQueryWarn("select * from users ".
"where uid='$token' and ".
" status!='$status_archived' and ".
" status!='$status_nonlocal'");
" status!='$status_archived'");
}
else {
return undef;
......@@ -294,13 +291,11 @@ sub LookupByWikiName($$)
{
my ($class, $wikiname) = @_;
my $status_archived = $USERSTATUS_ARCHIVED;
my $status_nonlocal = $USERSTATUS_NONLOCAL;
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where wikiname='$wikiname' and ".
" status!='$status_archived' and ".
" status!='$status_nonlocal'");
" status!='$status_archived'");
return undef
if (! $query_result || !$query_result->numrows);
......@@ -318,13 +313,11 @@ sub LookupByEmail($$)
{
my ($class, $email) = @_;
my $status_archived = $USERSTATUS_ARCHIVED;
my $status_nonlocal = $USERSTATUS_NONLOCAL;
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where LCASE(usr_email)=LCASE('$email') and ".
" status!='$status_archived' and ".
" status!='$status_nonlocal'");
" status!='$status_archived'");
return undef
......@@ -342,14 +335,12 @@ sub LookupByUUID($$)
{
my ($class, $uuid) = @_;
my $status_archived = $USERSTATUS_ARCHIVED;
my $status_nonlocal = $USERSTATUS_NONLOCAL;
my $safe_uuid = DBQuoteSpecial($uuid);
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where uid_uuid=$safe_uuid and ".
" status!='$status_archived' and ".
" status!='$status_nonlocal'");
" status!='$status_archived'");
return undef
if (! $query_result || !$query_result->numrows);
......@@ -365,13 +356,13 @@ sub LookupByUUID($$)
sub LookupNonLocal($$)
{
my ($class, $urn) = @_;
my $status_nonlocal = $USERSTATUS_NONLOCAL;
my $status_archived = $USERSTATUS_ARCHIVED;
my $safe_urn = DBQuoteSpecial($urn);
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where nonlocal_id=$safe_urn and ".
" status='$status_nonlocal'");
" status!='$status_archived'");
return undef
if (! $query_result || !$query_result->numrows);
......@@ -395,7 +386,6 @@ sub Create($$$$)
my $archived = ($flags & $NEWUSER_FLAGS_ARCHIVED ? 1 : 0);
my $nonlocal = ($flags & $NEWUSER_FLAGS_NONLOCAL ? 1 : 0);
my $nouuid = ($flags & $NEWUSER_FLAGS_NOUUID ? 1 : 0);
my $viaapt = ($flags & $NEWUSER_FLAGS_VIAAPT ? 1 : 0);
#
# If no uid, we need to generate a unique one for the user.
......@@ -576,15 +566,10 @@ sub Create($$$$)
"Must provide nonlocal_id and nonlocal_type!\n";
return undef;
}
push(@insert_data, "status='$USERSTATUS_NONLOCAL'");
push(@insert_data, "status='$USERSTATUS_ACTIVE'");
push(@insert_data, "pswd_expires=now()");
push(@insert_data, "usr_pswd='*'");
}
elsif ($viaapt) {
push(@insert_data, "status='$USERSTATUS_UNAPPROVED'");
push(@insert_data, "pswd_expires=date_add(now(), interval 5 year)");
push(@insert_data, "viaAPT='1'");
}
else {
push(@insert_data, "status='$USERSTATUS_NEWUSER'");
push(@insert_data, "pswd_expires=date_add(now(), interval 1 year)");
......@@ -620,6 +605,8 @@ sub Delete($)
my $uid_idx = $self->uid_idx();
DBQueryWarn("delete from user_credentials where uid_idx='$uid_idx'")
or return -1;
DBQueryWarn("delete from user_pubkeys where uid_idx='$uid_idx'")
or return -1;
DBQueryWarn("delete from user_sslcerts where uid_idx='$uid_idx'")
......@@ -1861,6 +1848,15 @@ sub ValidUID($$)
TBDB_CHECKDBSLOT_ERROR());
}
sub ValidEmail($$)
{
my ($class, $email) = @_;
return TBcheck_dbslot($email, "users", "usr_email",
TBDB_CHECKDBSLOT_WARN()|
TBDB_CHECKDBSLOT_ERROR());
}
#
# Default project. If not set in the users table, then look at the
# project membership, and if only one project then use that.
......@@ -1977,6 +1973,48 @@ sub HomeDirOkay($;$)
return 0;
}
#
# Set/Get credential for a user. These are used by APT to store a speaksfor
# credential for a nonlocal user, but might also use it later to
#
sub StoreCredential($$$$)
{
my ($self, $cred, $expires, $cert) = @_;
my $uid = $self->uid();
my $uid_idx = $self->uid_idx();
my $safe_credential = DBQuoteSpecial($cred);
my $safe_certificate = DBQuoteSpecial($cert);
return -1
if (!DBQueryWarn("replace into user_credentials set ".
" uid='$uid', uid_idx='$uid_idx',created=now(), ".
" expires='$expires', ".
" credential_string=$safe_credential, ".
" certificate_string=$safe_certificate"));
return 0;
}
sub GetStoredCredential($)
{
my ($self) = @_;
my $uid = $self->uid();
my $uid_idx = $self->uid_idx();
my $query_result =
DBQueryWarn("select credential_string,certificate_string ".
" from user_credentials ".
"where uid_idx='$uid_idx'");
return undef
if (!$query_result || !$query_result->numrows);
my ($cred, $cert) = $query_result->fetchrow_array();
return ($cred, $cert);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -49,7 +49,8 @@ PSBIN_STUFF = register_resources expire_daemon gencrl postcrl \
updatecert fixcerts initcerts cacontrol webcacontrol \
genextendcred rspeclint chstats listactive \
maptoslice webmaptoslice setexpiration quickvm webquickvm \
mondbd
mondbd parsecert creategeniuser webcreategeniuser \
updategeniuser webupdategeniuser
ifeq ($(ISCLEARINGHOUSE),1)
PSBIN_STUFF += ch_daemon
......@@ -76,6 +77,8 @@ install: apt-install \
$(addprefix $(INSTALL_SBINDIR)/protogeni/, $(PSBIN_STUFF)) \
$(INSTALL_LIBEXECDIR)/webquickvm \
$(INSTALL_LIBEXECDIR)/webcacontrol \
$(INSTALL_LIBEXECDIR)/webcreategeniuser \
$(INSTALL_LIBEXECDIR)/webupdategeniuser \
$(INSTALL_LIBEXECDIR)/webmaptoslice
-rm -f $(INSTALL_SBINDIR)/protogeni/cleanupticket
......
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2014 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
use strict;
use English;
use Getopt::Std;
use Data::Dumper;
#
# Create a geni user. This is a nonlocal user, derived from the
# only info we have which is the urn/email. We use this from CloudLab
# web interface (geni-login) to create a stub local account for a
# geni user.
#
sub usage()
{
print STDERR "Usage: $0 [-n] <urn> <email>\n";
exit(1);
}
my $optlist = "nr";
my $impotent = 0;
my $delete = 0;
# Configure ...
my $TB = "@prefix@";
my $TBACCT = "$TB/sbin/tbacct";
my $MKUSERCERT = "$TB/sbin/mkusercert";
my $MODGROUPS = "$TB/sbin/modgroups";
use lib '@prefix@/lib';
use emutil;
use User;
use Project;
use GeniCertificate;
use GeniHRN;
use EmulabConstants;
# Protos.
sub DeleteGeniUser($);
sub fatal($)
{
my ($msg) = @_;
die("*** $0:\n".
" $msg\n");
}
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"n"})) {
$impotent = 1;
}
if (defined($options{"r"})) {
usage()
if (@ARGV != 1);
$delete = 1;
}
else {
usage()
if (@ARGV != 2);
}
my $urn = $ARGV[0];
fatal("Invalid urn")
if (! GeniHRN::IsValid($urn));
# Make sure we can get this project.
my $project = Project->Lookup("CloudLab");
if (!defined($project)) {
fatal("Cannot find the project.");
}
if ($delete) {
exit(DeleteGeniUser($urn));
}
my $email = $ARGV[1];
my $usr_uid;
fatal("Invalid email")
if (! User->ValidEmail($email));
# Must not be a user with same nonlocal ID.
if (User->LookupNonLocal($urn)) {
fatal("We already have a user with that nonlocal ID (urn)");
}
#
# Parse urn and email, maybe we can get a unique uid out of one.
#
my (undef,undef,$uid) = GeniHRN::Parse($urn);
fatal("Could not parse urn")
if (!defined($uid));
if (User->ValidUID($uid) && !User->Lookup($uid)) {
$usr_uid = $uid;
}
else {
#
# Split email and try that.
#
my ($token) = split("@", $email);
if (defined($token) &&
User->ValidUID($token) && !User->Lookup($token)) {
$usr_uid = $token;
}
}
#
# Neither worked, so need to generate something. Ick.
#
if (!defined($usr_uid)) {
if (!User->ValidUID($uid)) {
# Random
$usr_uid = "g" . substr(lc(emutil::GenHash()), 0, 6);
}
else {
my $i;
$uid = substr($uid, 0, 7);
for ($i = 0 ; $i <= 9; $i++) {
if (!User->Lookup("${uid}${i}")) {
$usr_uid = "${uid}${i}";
last;
}
}
if ($i > 9) {
$usr_uid = "g" . substr(lc(emutil::GenHash()), 0, 6);
}
}
}
if ($impotent) {
print "Would create nolocal user '$usr_uid' ...\n";
exit(0);
}
#
# Okay, create new account in the DB.
#
my $user = User->Create($usr_uid,
$User::NEWUSER_FLAGS_NONLOCAL,
{"usr_name" => "Geni User $usr_uid",
"usr_email" => $email,
"nonlocal_id" => $urn,
"nonlocal_type" => "geni",
});
fatal("Could not create user!")
if (!defined($user));
#
# Add them to the holding project. This will need more thought.
#
if ($project->AddMemberShip($user, $Group::MemberShip::TRUSTSTRING_LOCALROOT)) {
$user->Delete();
fatal("Could not add new user to project");
}
# And then instantiate the user.
system("$TBACCT add $usr_uid");
if ($?) {
$project->DeleteMemberShip($user);
$user->Delete();
fatal("Could not instantiate user account!")
}
# We need to generate the encrypted ssl certificate to keep
# things happy.
my $certpass = substr(lc(emutil::GenHash()), 0, 10);
system("$MKUSERCERT -p $certpass $usr_uid");
if ($?) {
$project->DeleteMemberShip($user);
$user->Delete();
fatal("Could not create local SSL certificate");
}
exit(0);
#
# Delete (purge!) geni user. Not to be used generally, please use
# the normal archive path. This is for debugging.
#
sub DeleteGeniUser($)
{
my ($urn) = @_;
my $user = User->LookupNonLocal($urn);
if (!defined($user)) {
fatal("No such local user!");
}
my $uid = $user->uid();
my $pid = $project->pid();
system("$MODGROUPS -r $pid:$pid $uid");
system("$TBACCT -f del $uid") == 0 or
fatal("$TBACCT $uid failed!");
$user->Delete();
return 0;
}
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2014 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
use strict;
use English;
use Getopt::Std;
use Data::Dumper;
#
# Parse a certificate and print out useful info. Used by the web interface
# so that it does not need to duplicate code in GeniCertificate.
#
sub usage()
{
print STDERR "Usage: $0 [-a] <cert file> <outfile>\n";
exit(1);
}
my $optlist = "a";
my $showall = 0;
# Configure ...
my $TB = "@prefix@";
use lib '@prefix@/lib';
use GeniCertificate;
use GeniHRN;
sub fatal($)
{
my ($msg) = @_;
die("*** $0:\n".
" $msg\n");
}
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"a"})) {
$showall = 1;
}
usage()
if (@ARGV != 2);
my $certfile = $ARGV[0];
my $outfile = $ARGV[1];
my $certificate = GeniCertificate->LoadFromFile($certfile);
if (!defined($certificate)) {
fatal("Could not parse certificate");
}
if (!open(OUT, ">$outfile")) {
fatal("Could not open file for output");
}
print OUT "<attributes>\n";
print OUT "<attribute name='urn'>" . $certificate->urn();
print OUT "</attribute>\n";
print OUT "<attribute name='email'>" . $certificate->email();
print OUT "</attribute>\n";
print OUT "<attribute name='uuid'>" . $certificate->uuid();
print OUT "</attribute>\n";
print OUT "</attributes>\n";
close(OUT);
exit(0);
......@@ -40,27 +40,28 @@ use Cwd qw(realpath);
#
sub usage()
{
print "Usage: quickvm [-l] [-u uuid] [-a aggregate] <xmlfile>\n";
print "Usage: quickvm [-u uuid] [-a aggregate] <xmlfile>\n";
print "Usage: quickvm -k <uuid>\n";
print "Usage: quickvm -e <seconds> <uuid>\n";
print "Usage: quickvm -s <uuid> <sliver_urn> <imagename>\n";
exit(1);
}
my $optlist = "dkve:lu:a:st:f";
my $optlist = "dkve:u:a:st:f";
my $debug = 0;
my $verbose = 1;
my $killit = 0;
my $utahddc = 1;
my $DDCURN = "urn:publicid:IDN+utahddc.geniracks.net+authority+cm";
my $localuser = 0;
my $xmlfile;
my $extend;
my $webtask;
my $webtask_id;
my $snapshot;
my $foreground = 0;
my $localuser = 0;
my $quickuuid;
my $aggregate;
my $this_user;
# Protos
sub fatal($);
......@@ -68,6 +69,7 @@ sub UserError($);
sub Terminate($);
sub Extend($$);
sub SnapShot($$$);
sub GenCredentials($$$$);
#
# Configure variables
......@@ -82,6 +84,7 @@ my $SACERT = "$TB/etc/genisa.pem";
my $CMCERT = "$TB/etc/genicm.pem";
my $SSHKEYGEN = "/usr/bin/ssh-keygen";
my $SSHSETUP = "$TB/sbin/aptssh-setup";
my $UPDATEGENIUSER= "$TB/sbin/protogeni/updategeniuser";
my $VERSIONING = @PROFILEVERSIONS@;
# un-taint path
......@@ -131,9 +134,6 @@ if (defined($options{"d"})) {
if (defined($options{"v"})) {
$verbose = 1;
}
if (defined($options{"l"})) {
$localuser = 1;
}
if (defined($options{"f"})) {
$foreground = 1;
}
......@@ -178,15 +178,14 @@ else {
# file in /tmp.
#
if (getpwuid($UID) ne "nobody") {
my $this_user = User->ThisUser();
$this_user = User->ThisUser();
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
fatal("Only admins can run this script.")
if (!$this_user->IsAdmin());
$localuser = 1;
}
else {
if (!defined($this_user) || !$this_user->IsAdmin()) {
if ($xmlfile =~ /^([-\w\.\/]+)$/) {
$xmlfile = $1;
}
......@@ -443,10 +442,21 @@ if (!$localuser && $MAINSITE) {
$speaker_signer = "/usr/testbed/etc/utah-apt.sa";
}
# Remember key. For now we accept only one key. We store it simply
# so we can display it again for the user in the web interface.
# We allow key reuse for existing users, see above.
if (!$localuser && defined($sshkey)) {
if ($localuser) {
my $emulab_user = $geniuser->emulab_user();
if ($emulab_user->IsNonLocal()) {
system("$UPDATEGENIUSER -s " . $emulab_user->uid());
if ($?) {
fatal("Could not update ssh keys for nonlocal user");
}
}
}
elsif (!$localuser && defined($sshkey)) {
#
# Remember key. For now we accept only one key. We store it simply
# so we can display it again for the user in the web interface.
# We allow key reuse for existing users, see above.
#
$geniuser->DeleteKeys();
$geniuser->AddKey($sshkey);
}
......@@ -501,40 +511,17 @@ if ($slice->SetExpiration(time() + (($localuser ? 16 : 3) * 3600)) != 0) {
}
my $slice_uuid = $slice->uuid();
# Create a slice credential
my $slice_credential =
GeniCredential->Create($slice,
$geniuser);
if (!defined($slice_credential)) {
$slice->Delete();
fatal("Could not create credential for $slice_urn");
}
#
# Need to set the credential expiration to match the slice expiration,
# before we sign it.
# Generate credentials we need.
#
$slice_credential->SetExpiration(time() + (($localuser ? 16 : 3) * 3600));
# And sign it.
if ($slice_credential->Sign($GeniCredential::LOCALSA_FLAG) != 0) {
$slice_credential->Delete();
my ($slice_credential, $speaksfor_credential) =
GenCredentials($slice, $geniuser, $sa_authority, $speaker_signer);
if (! (defined($speaksfor_credential) &&
defined($slice_credential))) {
$slice->Delete();
fatal("Could not sign credential");
fatal("Could not generate credentials");
}
#
# In order to connect as the SA instead of the user we just created,
# lets generate a speaksfor credential that allows the SA to speakfor
# for the new user. Fancy, eh?
#
my $speaksfor_credential = GeniCredential->Create($geniuser, $sa_authority);
fatal("Could not create speaksfor credential")
if (!defined($speaksfor_credential));
$speaksfor_credential->SetType("speaksfor");
fatal("Could not sign speaksfor credential")
if ($speaksfor_credential->Sign($speaker_signer));
#
# Got this far, lets create a quickvm record.
#
......@@ -688,6 +675,68 @@ sub UserError($) {
exit(1);
}
#
# Generate or yank the speaks for credential out of the DB.
#
sub GenCredentials($$$$)
{
my ($target, $geniuser, $authority, $signer) = @_;
my ($speaksfor, $credential);
#
# If a local user account, but a nonlocal id, then we should
# have a speaksfor credential stored, as well as a certificate
# for the user.
#
if ($geniuser->IsLocal() && $geniuser->emulab_user()->IsNonLocal()) {
my ($speaksfor_string, $certificate_string) =
$geniuser->emulab_user()->GetStoredCredential();
if (! (defined($speaksfor_string) &&
defined($certificate_string))) {
print STDERR "No stored speaksfor/certificate for $geniuser\n";
goto bad;
}
$speaksfor = GeniCredential->CreateFromSigned($speaksfor_string);
if (!defined($speaksfor)) {
print STDERR "Could not create speaksfor credential\n";