All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 2181fc33 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Next step in the locally unique uid project. This commits add the luid to

most of the rest of the tables in the system (still a few exceptions).
Bound to be some bugs ...
parent d243779f
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2006 University of Utah and the Flux Group.
# Copyright (c) 2000-2007 University of Utah and the Flux Group.
# All rights reserved.
#
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2006 University of Utah and the Flux Group.
# Copyright (c) 2000-2007 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -48,12 +48,16 @@ my $USERUID;
# Locals
my $user;
my $this_user;
my $target_user;
my $keyfile;
my $keyline;
my $key;
my $comment;
my $user_name;
my $user_email;
my $user_dbid;
my $user_uid;
#
# Testbed Support libraries
......@@ -151,12 +155,22 @@ else {
# Untaint the arguments.
#
if (defined($user)) {
if ($user =~ /^([-\w_]+)$/i) {
if ($user =~ /^([-\w]+)$/i) {
$user = $1;
}
else {
fatal("Tainted username: $user");
}
# Map user to object.
$target_user = User->Lookup($user);
if (! defined($target_user)) {
fatal("$user does not exist!")
}
$user_name = $target_user->name();
$user_email = $target_user->email();
$user_dbid = $target_user->dbid();
$user_uid = $target_user->uid();
}
#
......@@ -170,6 +184,13 @@ if (getpwuid($UID) eq "nobody") {
}
else {
$USERUID = getpwnam($user);
# Map invoking user to object.
$this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
}
#
......@@ -214,28 +235,19 @@ else {
#
if (!$verify) {
# If its the user himself, then we can generate a new authkeys file.
if (!$nobody && getpwuid($UID) ne "$user" && !TBAdmin($UID)) {
fatal("You are not allowed to set pubkeys for $user.\n");
if (!$nobody && !TBAdmin() && !$target_user->SameUser($this_user)) {
fatal("You are not allowed to set pubkeys for $target_user\n");
}
if (-d "$HOMEDIR/$user/.ssh") {
if (-d "$HOMEDIR/$user_uid/.ssh") {
$genmode = 1;
}
#
# This script is audited when not in verify mode. Since all keys are first
# checked with verify mode, this should not cause any extra email from bad
# keys.
#
AuditStart(0);
if (! UserDBInfo($user, \$user_name, \$user_email)) {
if ($nobody) {
$noemail = 1;
}
else {
fatal("No DB info for $user!");
}
}
}
# Drop root privs, switching to user.
......@@ -323,12 +335,13 @@ sub ParseKey($) {
$key = "$key $comment";
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', 0, '$key', now(), '$comment')");
"values ('$user_uid', '$user_dbid', ".
" 0, '$key', now(), '$comment')");
#
# Mark user record as modified so nodes are updated.
#
TBNodeUpdateAccountsByUID($user);
TBNodeUpdateAccountsByUID($user_uid);
my $chunked = "";
......@@ -348,8 +361,8 @@ sub ParseKey($) {
if (! $noemail) {
SENDMAIL("$user_name <$user_email>",
"SSH Public Key for '$user' Added",
"SSH Public Key for '$user' added:\n".
"SSH Public Key for '$user_uid' Added",
"SSH Public Key for '$user_uid' added:\n".
"\n".
"$chunked\n",
"$TBOPS");
......@@ -364,7 +377,7 @@ sub ParseKey($) {
#
sub InitUser()
{
my $sshdir = "$HOMEDIR/$user/.ssh";
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
#
# Set up the ssh key, but only if not done so already.
......@@ -384,7 +397,7 @@ sub InitUser()
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid='$user' and pubkey='$1 $2'");
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/identity");
}
......@@ -404,7 +417,8 @@ sub InitUser()
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', 0, '$1 $2', now(), '$2')");
"values ('$user_uid', '$user_dbid', ".
" 0, '$1 $2', now(), '$2')");
}
else {
fatal("Bad protocol 1 public key: $ident\n");
......@@ -425,7 +439,7 @@ sub InitUser()
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid='$user' and pubkey='$1 $2'");
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/id_rsa");
}
......@@ -446,13 +460,8 @@ sub InitUser()
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', 0, '$1 $2', now(), '$2')");
#
# Backwards compat. Remove later.
#
DBQueryFatal("update users set emulab_pubkey='$1 $2' ".
"where uid='$user'");
"values ('$user_uid', '$user_dbid', ".
" 0, '$1 $2', now(), '$2')");
}
else {
fatal("Bad protocol 2 public key: $ident\n");
......@@ -468,7 +477,7 @@ sub InitUser()
sub GenerateKeyFile()
{
my @pkeys = ();
my $sshdir = "$HOMEDIR/$user/.ssh";
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
......@@ -478,7 +487,8 @@ sub GenerateKeyFile()
}
}
my $query_result =
DBQueryFatal("select pubkey from user_pubkeys where uid='$user'");
DBQueryFatal("select pubkey from user_pubkeys ".
"where uid_idx='$user_dbid'");
while (my ($key) = $query_result->fetchrow_array()) {
push(@pkeys, $key);
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2004, 2006 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -16,6 +16,7 @@ use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
use User;
#
# Create user SSL certificates.
......@@ -48,7 +49,6 @@ my $CACONFIG = "$SSLDIR/ca.cnf";
my $EMULAB_CERT = "$TB/etc/emulab.pem";
my $EMULAB_KEY = "$TB/etc/emulab.key";
my $OPENSSL = "/usr/bin/openssl";
my $lockfile = "/var/tmp/testbed_mkusercert_lockfile";
my $WORKDIR = "$TB/ssl";
my $SAVEUID = $UID;
......@@ -137,6 +137,18 @@ else {
die("Tainted argument: $user\n");
}
# Map target user to object.
my $target_user = User->Lookup($user);
if (! defined($target_user)) {
fatal("$user does not exist!");
}
# Map invoking user to object.
my $this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
#
# This script is always audited. Mail is sent automatically upon exit.
#
......@@ -154,103 +166,38 @@ if (AuditStart(0)) {
chdir("$WORKDIR") or
fatal("Could not chdir to $WORKDIR: $!");
open(LOCK, ">>$lockfile") || fatal("Couldn't open $lockfile\n");
$count = 0;
if (flock(LOCK, LOCK_EX|LOCK_NB) == 0) {
#
# If we don't get it the first time, we wait for:
# 1) The lock to become free, in which case we do our thing
# 2) The time on the lock to change, in which case we wait for that process
# to finish
#
my $oldlocktime = (stat(LOCK))[9];
my $gotlock = 0;
while (1) {
print "Another exports update in progress, waiting for it to finish\n";
if (flock(LOCK, LOCK_EX|LOCK_NB) != 0) {
# OK, got the lock, we can do what we're supposed to
$gotlock = 1;
last;
}
$locktime = (stat(LOCK))[9];
if ($locktime != $oldlocktime) {
$oldlocktime = $locktime;
last;
}
if ($count++ > 60) {
fatal("Could not get the lock after a long time!\n");
}
sleep(1);
}
$count = 0;
#
# If we didn't get the lock, wait for the processes that did to finish
#
if (!$gotlock) {
while (1) {
if ((stat(LOCK))[9] != $oldlocktime) {
exit(0);
}
if (flock(LOCK, LOCK_EX|LOCK_NB) != 0) {
close(LOCK);
exit(0);
}
if ($count++ > 20) {
fatal("Process with the lock did not finish after ".
"a long time!\n");
}
sleep(1);
}
}
}
#
# Perl-style touch(1)
#
my $now = time;
utime $now, $now, $lockfile;
TBScriptLock("mkusercert") == 0 or
fatal("Could not get the lock!");
#
# Get the user info (the user being operated on).
#
my $query_result =
DBQueryFatal("select u.usr_name,u.usr_email,u.admin,u.unix_uid ".
"from users as u ".
"where u.uid='$user'");
if ($query_result->numrows == 0) {
fatal("$user is not in the DB. This is bad.\n");
}
my @row = $query_result->fetchrow_array();
my $fullname = $row[0];
my $user_email = $row[1];
my $usr_admin = $row[2];
my $user_number = $row[3];
my $fullname = $target_user->name();
my $user_email = $target_user->email();
my $usr_admin = $target_user->admin();
my $user_number = $target_user->uid_idx();
my $user_uid = $target_user->uid();
my $user_dbid = $target_user->dbid();
#
# Get the users earliest project membership to use as the default group
# for the case that the account is being (re)created. We convert that to
# the unix info.
#
my $default_groupname;
my $default_project;
my $default_groupgid;
$query_result =
DBQueryFatal("select m.pid from group_membership as m ".
"where m.uid='$user' and m.pid=m.gid and m.trust!='none' ".
"order by date_approved asc limit 1");
if ($target_user->FirstApprovedProject(\$default_project) < 0) {
fatal("Could not locate default project for $target_user");
}
if (my ($defpid) = $query_result->fetchrow_array) {
if (! TBGroupUnixInfo($defpid, $defpid,
\$default_groupgid, \$default_groupname)) {
fatal("No info for default project $defpid!");
}
if (defined($default_project)) {
$default_groupgid = $default_project->unix_gid();
}
else {
print "No group membership for $user; using the guest group!\n";
print "No group membership for $target_user; using the guest group!\n";
($default_groupname,undef,$default_groupgid,undef) = getgrnam("guest");
(undef,undef,$default_groupgid,undef) = getgrnam("guest");
}
#
......@@ -264,8 +211,8 @@ open(TEMP, ">>usercert.cnf")
or fatal("Could not open $TEMPLATE for append: $!");
print TEMP "OU\t\t= sslxmlrpc\n";
print TEMP "CN\t\t= $user\n";
print TEMP "emailAddress\t= $user" . "\@" . "$OURDOMAIN\n";
print TEMP "CN\t\t= $user_uid\n";
print TEMP "emailAddress\t= $user_uid" . "\@" . "$OURDOMAIN\n";
close(TEMP)
or fatal("Could not close usercert.cnf: $!");
......@@ -310,14 +257,15 @@ $UID = $SAVEUID;
# unencrypted). Might change that at some point.
#
DBQueryFatal("delete from user_sslcerts ".
"where uid='$user' and ".
"where uid_idx='$user_dbid' and ".
" encrypted=" . (defined($password) ? 1 : 0));
#
# Create a new entry in the table.
#
DBQueryFatal("insert into user_sslcerts (uid, idx, created, encrypted) ".
" values ('$user', $curidx, now(), ".
DBQueryFatal("insert into user_sslcerts ".
"(uid, uid_idx, idx, created, encrypted) values ".
"('$user_uid', '$user_dbid', $curidx, now(), ".
(defined($password) ? 1 : 0) . ")");
#
......@@ -353,7 +301,7 @@ close(PKEY);
$pkeystring = DBQuoteSpecial($pkeystring);
$certstring = DBQuoteSpecial($certstring);
DBQueryFatal("update user_sslcerts set cert=$certstring,privkey=$pkeystring ".
"where uid='$user' and idx=$curidx");
"where uid_idx='$user_dbid' and idx=$curidx");
#
# Combine the key and the certificate into one file which is installed
......@@ -366,7 +314,7 @@ system("cat usercert_key.pem usercert_cert.pem > usercert.pem") == 0
#
# Copy the certificate to the users .ssl directory.
#
my $ssldir = "$USERDIR/$user/.ssl";
my $ssldir = "$USERDIR/$user_uid/.ssl";
if (! -d $ssldir) {
mkdir($ssldir, 0700) or
fatal("Could not mkdir $ssldir: $!");
......@@ -390,12 +338,13 @@ system("cp -f usercert.pem $target") == 0
chown($user_number, $default_groupgid, "$target")
or fatal("Could not chown $target: $!");
close(LOCK);
TBScriptUnlock();
exit(0);
sub fatal($) {
my($mesg) = $_[0];
TBScriptUnlock();
die("*** $0:\n".
" $mesg\n");
}
......@@ -145,7 +145,7 @@ my $HOMEDIR = USERROOT();
#
# Rewrite audit version of ARGV to prevent password in mail logs.
#
if ($ARGV[0] eq "passwd" && scalar(@ARGV) == 3) {
if (scalar(@ARGV) == 3 && $ARGV[0] eq "passwd") {
my @NEWARGV = @ARGV;
$NEWARGV[scalar(@NEWARGV) - 1] = "**********";
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005, 2006 University of Utah and the Flux Group.
# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
# All rights reserved.
#
package Experiment;
......@@ -130,6 +130,8 @@ sub dpdb($) { return field($_[0], 'dpdb');}
sub dpdbname($) { return field($_[0], 'dpdbname');}
sub dpdbpassword($) { return field($_[0], 'dpdbpassword');}
sub instance_idx($) { return field($_[0], 'instance_idx'); }
sub creator_idx($) { return field($_[0], 'creator_idx');}
sub swapper_idx($) { return field($_[0], 'swapper_idx');}
#
# Lookup an experiment given an experiment index.
......@@ -335,20 +337,21 @@ sub Create($$$$)
tberror("Error inserting experiment resources record for $pid/$eid!");
return undef;
}
my $rsrcidx = $query_result->insertid;
my $creator = $argref->{'expt_head_uid'};
my $gid = $argref->{'gid'};
my $batchmode = $argref->{'batchmode'};
my $rsrcidx = $query_result->insertid;
my $creator_uid = $argref->{'expt_head_uid'};
my $creator_idx = $argref->{'creator_idx'};
my $gid = $argref->{'gid'};
my $batchmode = $argref->{'batchmode'};
#
# Now create an experiment_stats record to match.
#
if (! DBQueryWarn("insert into experiment_stats ".
"(eid, pid, creator, gid, created, ".
"(eid, pid, creator, creator_idx, gid, created, ".
" batch, exptidx, rsrcidx) ".
"values('$eid', '$pid', '$creator', '$gid', ".
"FROM_UNIXTIME('$now'), ".
"$batchmode, $exptidx, $rsrcidx)")) {
"values('$eid', '$pid', '$creator_uid', '$creator_idx',".
" '$gid', FROM_UNIXTIME('$now'), ".
" $batchmode, $exptidx, $rsrcidx)")) {
DBQueryWarn("delete from experiments where pid='$pid' and eid='$eid'");
DBQueryWarn("delete from experiment_resources where idx=$rsrcidx");
DBQueryWarn("unlock tables");
......@@ -1103,7 +1106,7 @@ sub BackupUserData($)
#
sub SetSwapInfo($$)
{
my ($self, $dbuid) = @_;
my ($self, $user) = @_;
# Must be a real reference.
return -1
......@@ -1113,25 +1116,30 @@ sub SetSwapInfo($$)
my $eid = $self->eid();
TBSetExpSwapTime($pid, $eid);
TBExptSetSwapUID($pid, $eid, $dbuid);
$self->SetSwapper($user);
return $self->Refresh();
}
#
# Just the swap uid,
# Just the swap uid.
#
sub SetSwapper($$)
{
my ($self, $dbuid) = @_;
my ($self, $user) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $pid = $self->pid();
my $eid = $self->eid();
TBExptSetSwapUID($pid, $eid, $dbuid);
my $pid = $self->pid();
my $eid = $self->eid();
my $uid = $user->uid();
my $dbid = $user->dbid();
DBQueryWarn("update experiments set ".
" expt_swap_uid='$uid', swapper_idx='$dbid' ".
"where pid='$pid' and eid='$eid'");
return $self->Refresh();
}
......
......@@ -22,11 +22,41 @@ use Data::Dumper;
use File::Basename;
use overload ('""' => 'Stringify');
use Project;
use vars qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED
@EXPORT_OK);
# Configure variables
my $TB = "@prefix@";
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $TB = "@prefix@";
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $MIN_UNIX_UID = @MIN_UNIX_UID@;
my $MIN_UNIX_GID = @MIN_UNIX_GID@;
# Create() flags.
$NEWUSER_FLAGS_PROJLEADER = 0x01;
$NEWUSER_FLAGS_WIKIONLY = 0x02;
$NEWUSER_FLAGS_WEBONLY = 0x04;
$NEWUSER_FLAGS_ARCHIVED = 0x08;
# Status values.
$USERSTATUS_ACTIVE = "active";
$USERSTATUS_FROZEN = "frozen";
$USERSTATUS_UNAPPROVED = "unapproved";
$USERSTATUS_UNVERIFIED = "unverified";
$USERSTATUS_NEWUSER = "newuser";
$USERSTATUS_ARCHIVED = "archived";
# Why, why, why?
@EXPORT_OK = qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED);
# Cache of instances to avoid regenerating them.
my %users = ();
......@@ -171,6 +201,237 @@ sub LookupByUnixId($$)
return User->Lookup($uid_idx);
}
#
# Lookup user given a wikiname. This is just to make sure the wikiname
# the user picked is unique.
#
sub LookupByWikiName($$)
{
my ($class, $wikiname) = @_;
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where wikiname='$wikiname'");
return undef
if (! $query_result || !$query_result->numrows);
my ($uid_idx) = $query_result->fetchrow_array();
return User->Lookup($uid_idx);
}
#
# Lookup user given a wikiname. This is just to make sure the wikiname
# the user picked is unique.
#
sub LookupByEmail($$)
{
my ($class, $email) = @_;
my $query_result =
DBQueryFatal("select uid_idx from users ".
"where LCASE(usr_email)=LCASE('$email')");
return undef
if (! $query_result || !$query_result->numrows);
my ($uid_idx) = $query_result->fetchrow_array();
return User->Lookup($uid_idx);
}
#
# Class function to create new user and return object.
#
sub Create($$$$)
{
my ($class, $uid, $flags, $argref) = @_;
my $isleader = ($flags & $NEWUSER_FLAGS_PROJLEADER ? 1 : 0);
my $wikionly = ($flags & $NEWUSER_FLAGS_WIKIONLY ? 1 : 0);
my $webonly = ($flags & $NEWUSER_FLAGS_WEBONLY ? 1 : 0);
my $archived = ($flags & $NEWUSER_FLAGS_ARCHIVED ? 1 : 0);
#
# If no uid, we need to generate a unique one for the user.
#
if (! $uid) {
#
# Take the first 5 letters of the email to form a root. That gives
# us 3 digits to make it unique, since unix uids are limited to 8
# chars, sheesh!
#
my $email = $argref->{'usr_email'};
my $token;