Commit 9bb38fee authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

This script is dead; redone and moved to the account directory,

renamed to tbacct.
parent 35f5a781
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Create a user account. All this script does is create the account
# if it does not exist, or update the password and gecos fields.
# No groups processing is done here. The initial account is created in
# the "guest" group; use the setgroups command to set a users groups.
#
# XXX - /users wired in.
#
sub usage()
{
print STDOUT "Usage: mkacct [-f] <name>\n";
exit(-1);
}
my $optlist = "f";
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $CONTROL = "@USERNODE@";
my $BOSSNODE= "@BOSSNODE@";
my $WITHSFS = @SFSSUPPORT@;
my $HOMEDIR = "/users";
my $USERPATH= "$TB/bin";
my $PBAG = "$TB/sbin/paperbag";
my $SSH = "$TB/bin/sshtb";
my $USERADD = "/usr/sbin/pw useradd";
my $USERDEL = "/usr/sbin/pw userdel";
my $USERMOD = "/usr/sbin/pw usermod";
my $CHPASS = "/usr/bin/chpass";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $SFSKEYGEN = "/usr/local/bin/sfskey gen";
my $SFSKEYREG = "/usr/local/bin/sfskey register";
my $SETGROUPS = "$TB/sbin/setgroups";
my $GENELISTS = "$TB/sbin/genelists";
my $SFSUPDATE = "$TB/sbin/sfskey_update";
my $NOLOGIN = "/sbin/nologin";
my $sfschange = 0;
my $errors = 0;
my $wasfrozen = 0;
my $user;
my @db_row;
my $query_result;
#
# Note hardwired control node.
#
my $control_node = $CONTROL;
#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
die("*** $0:\n".
" Must be setuid! Maybe its a development version?\n");
}
#
# This script is setuid, so please do not run it as root. Hard to track
# what has happened.
#
if ($UID == 0) {
die("*** $0:\n".
" Please do not run this as root! Its already setuid!\n");
}
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/usr/bin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Turn off line buffering on output
#
$| = 1;
#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"f"})) {
$sfschange = 1;
}
if (@ARGV != 1) {
usage();
}
$user = $ARGV[0];
#
# Untaint the argument.
#
if ($user =~ /^([a-z0-9]+)$/i) {
$user = $1;
}
else {
die("Invalid uid '$user' contains illegal characters.\n");
}
my $SSHDIR = "$HOMEDIR/$user/.ssh";
my $SFSDIR = "$HOMEDIR/$user/.sfs";
#
# This script always does the right thing ...
#
#
# Get the user info (the user being created). This join picks out the
# user's earliest project membership to use for the default group.
#
$query_result =
DBQueryFatal("select u.usr_pswd,u.unix_uid,u.usr_name, ".
" u.usr_email,u.status,u.webonly from users as u ".
"where u.uid='$user'");
if ($query_result->numrows == 0) {
fatal("$user is not in the DB. This is bad.\n");
}
@db_row = $query_result->fetchrow_array();
my $pswd = $db_row[0];
my $user_number = $db_row[1];
my $fullname = $db_row[2];
my $user_email = $db_row[3];
my $status = $db_row[4];
my $webonly = $db_row[5];
#
# This script is always audited. Mail is sent automatically upon exit.
#
if (AuditStart(0)) {
#
# Parent exits normally
#
exit(0);
}
if ($webonly ||
$status eq USERSTATUS_FROZEN ||
$status eq USERSTATUS_NEWUSER ||
$status eq USERSTATUS_UNVERIFIED ||
$status eq USERSTATUS_UNAPPROVED) {
print "User $user status indicates he/she should not have an account!\n";
#
# All this stuff must be done as root (ssh).
#
$UID = $EUID;
if ($status eq USERSTATUS_FROZEN) {
print "Freezing user $user on local node.\n";
system("$USERMOD $user -s $NOLOGIN");
}
else {
print "Removing user $user from local node.\n";
system("$USERDEL $user");
}
if ($? && (($? >> 8) != 67)) {
$errors++;
print("Error operating on user $user on local node!\n");
}
if ($control_node ne $BOSSNODE) {
if ($status eq USERSTATUS_FROZEN) {
print "Freezing user $user on $control_node.\n";
system("$SSH -host $control_node ".
"'$USERMOD $user -s $NOLOGIN'");
}
else {
print "Removing user $user from $control_node.\n";
system("$SSH -host $control_node '$USERDEL $user'");
}
if ($? && (($? >> 8) != 67)) {
$errors++;
print("Error operating on user $user on $control_node!\n");
}
}
if ($errors) {
fatal("Error clearing account for $user!");
}
exit(0);
}
#
# 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_groupgid;
$query_result =
DBQueryFatal("select m.pid from group_membership as m ".
"where m.uid='$user' and m.pid=m.gid ".
"order by date_approved asc limit 1");
if (my ($defpid) = $query_result->fetchrow_array) {
if (! TBGroupUnixInfo($defpid, $defpid,
\$default_groupgid, \$default_groupname)) {
fatal("No info for default project $defpid!");
}
}
else {
print "No group membership for $user; using the guest group!\n";
($default_groupname,undef,$default_groupgid,undef) = getgrnam("guest");
}
#
# All this stuff must be done as root (ssh).
#
$UID = $EUID;
#
# Run genelists to update the email lists. This is a convenient
# spot to do this. Errors are non-fatal; the testbed list will
# will find out about problems via email from genelists. Note that
# this command must be run when EUID==UID==0 because its a setuid
# PERL script.
#
system("$GENELISTS -n $user");
#
# Make user on local. We don't give them a password since they are not
# allowed to log in, except via paperbag.
#
if (system("egrep -q -s '^${user}:' /etc/passwd")) {
print "Adding user $user ($user_number) to local node.\n";
if (system("$USERADD $user -u $user_number -c \"$fullname\" ".
"-k /usr/share/skel -m -d $HOMEDIR/$user ".
"-g $default_groupname -s $PBAG")) {
fatal("Could not add user $user to local node.");
}
}
else {
#
# Get the current login shell. This is how we determine if the user
# was frozen, which tells us what shell argument to provide. Otherwise
# we leave the shell alone since the user may have changed it. Maybe
# we should put the shell in the DB instead?
#
my $oldshell;
my $shellarg = "";
(undef,undef,undef,undef,undef,undef,undef,undef,$oldshell) =
getpwnam($user) or
fatal("Could not determine oldshell for $user");
if ($oldshell eq $NOLOGIN) {
$wasfrozen = 1;
$shellarg = "-s $PBAG";
}
#
# MAKE SURE not to update anything else!
#
print "Updating user $user ($user_number) on local node.\n";
if (system("$USERMOD $user $shellarg -c \"$fullname\" ")) {
fatal("Could not modify user $user on local node.");
}
}
#
# Make user account on control node. We do the password setup as separate
# step since thats easier than trying to do both via ssh.
#
# Quote special chars for ssh and the shell on the other side
#
$fullname =~ s/\"/\'/g;
$fullname =~ s/([^\\])([\'\"\(\)])/$1\\$2/g;
if ($control_node ne $BOSSNODE) {
if (system("$SSH -host $control_node ".
"egrep -q -s '^${user}:' /etc/passwd")) {
print "Adding user $user ($user_number) to $control_node.\n";
if (system("$SSH -host $control_node ".
"'$USERADD $user -u $user_number -c \\\"$fullname\\\" ".
"-k /usr/share/skel -m -d $HOMEDIR/$user ".
"-g $default_groupname -s /bin/tcsh'")) {
fatal("Could not add user $user ($user_number) ".
"to $control_node.\n");
}
}
else {
my $shellarg = ($wasfrozen ? "-s /bin/tcsh" : "");
#
# MAKE SURE not to update anything else!
#
print "Updating user $user ($user_number) on $control_node.\n";
if (system("$SSH -host $control_node ".
"'$USERMOD $user $shellarg -c \\\"$fullname\\\"'")) {
fatal("Could not modify user $user record on $control_node.");
}
}
print "Updating user $user password on $control_node.\n";
$pswd =~ s/\$/\\\\\\\$/g;
if (system("$SSH -host $control_node $CHPASS -p '$pswd' $user")) {
fatal("Could not change password for user $user on $control_node.\n");
}
}
#
# Do "first time" stuff.
#
FirstTime();
#
# Create a new authorized keys file from DB.
#
# Grab pub keys and split into P1 and P2.
#
$query_result =
DBQueryFatal("select * from user_pubkeys where uid='$user'");
my @p1keys = ();
my @p2keys = ();
while (my %row = $query_result->fetchhash()) {
my $key = $row{'pubkey'};
if ($key =~ /^\d+\s+.*$/) {
push(@p1keys, $key);
}
else {
push(@p2keys, $key);
}
}
NewsshKeyfile($SSHDIR, $user_number, $default_groupgid, 1, @p1keys);
NewsshKeyfile($SSHDIR, $user_number, $default_groupgid, 2, @p2keys);
#
# Now schedule account updates on all the nodes that this person has
# an account on.
#
TBNodeUpdateAccountsByUID($user);
#
# If an SFS change was requested (or a new user), then must update.
#
if ($WITHSFS && $sfschange) {
#
# Note that this command must be run when EUID==UID==0 because its
# a setuid PERL script.
#
system($SFSUPDATE) == 0
or fatal("$SFSUPDATE failed!");
}
exit(0);
#
# Do some new account stuff.
#
sub FirstTime()
{
my $dossh = 0;
#
# Set up the sfs key, but only if not done so already.
# This has to be done from root because the sfs_users file needs
# to be updated (and "sfskey register" won't work because it
# prompts for the user's UNIX password if not run from root.)
#
if ($WITHSFS &&
! -e "$SFSDIR/identity") {
if (! -e "$SFSDIR" ) {
print "Setting up sfs configuration for $user.\n";
mkdir("$SFSDIR", 0700) or
fatal("Could not mkdir $SFSDIR: $!");
chown($user_number, $default_groupgid, "$SFSDIR") or
fatal("Could not chown $SFSDIR: $!");
}
print "Generating sfs key\n";
if (system("$SSH -host $control_node '$SFSKEYGEN -KPn ".
"$user\@ops.emulab.net $SFSDIR/identity'")) {
fatal("Failure in sfskey gen!");
}
$sfschange = 1;
chown($user_number, $default_groupgid, "$SFSDIR/identity") or
fatal("Could not chown $SFSDIR/identity: $!");
chmod(0600, "$SFSDIR/identity") or
fatal("Could not chmod $SFSDIR/identity: $!");
#
# Grab a copy for the DB. Causes an SFS update key to run so
# that key is inserted into the files.
#
my $ident = `cat $SFSDIR/identity`;
if ($ident =~ /.*,.*,.*,(.*),(.*)/) {
DBQueryFatal("replace into user_sfskeys ".
"values ('$user', '$2', '${user}:${1}:${user}::', ".
"now())");
}
else {
warn("*** $0:\n".
" Bad emulab SFS public key\n");
}
}
#
# Set up the ssh key, but only if not done so already.
#
if (! -e "$SSHDIR" ) {
print "Setting up ssh configuration for $user.\n";
mkdir("$SSHDIR", 0700) or
fatal("Could not mkdir $SSHDIR: $!");
chown($user_number, $default_groupgid, "$SSHDIR") or
fatal("Could not chown $SSHDIR: $!");
$dossh = 1;
}
#
# Check for missing identity file
#
if (! -e "$SSHDIR/identity") {
$dossh = 1;
}
#
# The rest of this needs to be done as the user, so fork a child.
#
$mypid = fork();
if ($mypid) {
waitpid($mypid, 0);
if ($?) {
fatal("Failed to do firsttime user stuff!");
}
return;
}
# Avoid dups.
AuditFork();
$EUID = $user_number;
$UID = $EUID;
TBdbfork();
#
# Below here, use die() instead of fatal().
#
if ($dossh) {
if (system("$KEYGEN -t rsa1 -P '' -f $SSHDIR/identity")) {
die("Failure in ssh-keygen");
}
#
# Grab a copy for the DB.
#
my $ident = `cat $SSHDIR/identity.pub`;
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([\w\@\.]*)/) {
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', '$2', '$1 $2', now())");
#
# Backwards compat. Remove later.
#
DBQueryFatal("update users set emulab_pubkey='$1 $2' ".
"where uid='$user'");
}
else {
warn("*** $0:\n".
" Bad emulab public key: $ident\n");
}
}
#
# Set up a .forward file so that any email to them gets forwarded off.
#
if (! -e "$HOMEDIR/$user/.forward" ) {
print "Setting up .forward file for $user.\n";
if (system("echo \"$user_email\" > $HOMEDIR/$user/.forward")) {
die("Could not create $HOMEDIR/$user/.forward");
}
chmod(0644, "$HOMEDIR/$user/.forward") or
die("Could not chmod $HOMEDIR/$user/.forward");
}
#
# Add testbed path to .cshrc and .profile.
#
my $cpathstr = "set path = ($USERPATH \$path)";
if (-e "$HOMEDIR/$user/.cshrc" &&
system("egrep -q -s '$USERPATH' $HOMEDIR/$user/.cshrc")) {
system("echo '$cpathstr' >> $HOMEDIR/$user/.cshrc");
}
my $spathstr = "PATH=$USERPATH:\$PATH";
if (-e "$HOMEDIR/$user/.profile" &&
system("egrep -q -s '$USERPATH' $HOMEDIR/$user/.profile")) {
system("echo '$spathstr' >> $HOMEDIR/$user/.profile");
}
exit(0);
}
#
# Generate ssh authorized_keys files. Either protocol 1 or 2.
# Returns 0 on success, -1 on failure.
#
sub NewsshKeyfile($$$$$)
{
my ($sshdir, $uid, $gid, $protocol, @pkeys) = @_;
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
if (! mkdir($sshdir, 0700)) {
warn("*** WARNING: Could not mkdir $sshdir: $!\n");
return -1;
}
if (!chown($uid, $gid, $sshdir)) {
warn("*** WARNING: Could not chown $sshdir: $!\n");
return -1;
}
}
if ($protocol == 2) {
$keyfile .= "2";
}
if (!open(AUTHKEYS, "> ${keyfile}.new")) {
warn("*** WARNING: Could not open ${keyfile}.new: $!\n");
return -1;
}
print AUTHKEYS "#\n";
print AUTHKEYS "# DO NOT EDIT! This file auto generated by ".
"Emulab.Net account software.\n";
print AUTHKEYS "#\n";
print AUTHKEYS "# Please use the web interface to edit your ".
"public key list.\n";
print AUTHKEYS "#\n";
foreach my $key (@pkeys) {
print AUTHKEYS "$key\n";
}
close(AUTHKEYS);
if (!chown($uid, $gid, "${keyfile}.new")) {
warn("*** WARNING: Could not chown ${keyfile}.new: $!\n");
return -1;
}
if (!chmod(0600, "${keyfile}.new")) {
warn("*** WARNING: Could not chmod ${keyfile}.new: $!\n");
return -1;
}
if (-e "${keyfile}") {
if (system("cp -p -f ${keyfile} ${keyfile}.old")) {
warn("*** Could not save off ${keyfile}: $!\n");
return -1;
}
if (!chown($uid, $gid, "${keyfile}.old")) {
warn("*** Could not chown ${keyfile}.old: $!\n");
}
if (!chmod(0600, "${keyfile}.old")) {
warn("*** Could not chmod ${keyfile}.old: $!\n");
}
}
if (system("mv -f ${keyfile}.new ${keyfile}")) {
warn("*** Could not mv ${keyfile} to ${keyfile}.new: $!\n");
}
return 0;
}
sub fatal($) {
my($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
#
# This gets invoked from the Web interface. Simply a wrapper ...
#
# usage: webmkacct arguments ...
#
#
# Configure variables
#
my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/sbin/mkacct", @ARGV;