Commit 4a060fda authored by Leigh Stoller's avatar Leigh Stoller

More nails in the NFS access to /users; use dropfile to generate

.forward files. Change addpubkeys to use dropfile for the auth keys
file, and add createsshkey method to generate the key on ops, and
send the public key back via stdout.
parent 0086b6fa
......@@ -46,6 +46,8 @@ sub usage()
print " accountsetup addgroup ...\n";
print " accountsetup delproject ...\n";
print " accountsetup delgroup ...\n";
print " accountsetup checkdotfiles ...\n";
print " accountsetup createsshkey ...\n";
print " accountsetup dropfile ...\n";
exit(1);
}
......@@ -61,7 +63,9 @@ my $RENAMEDIRS = 1;
# Configure variables
#
my $TB = "@prefix@";
my $USERPATH = "$TB/bin";
my $WITHZFS = @WITHZFS@;
my $OURDOMAIN = "@OURDOMAIN@";
my $ZFS_ROOT = "@ZFS_ROOT@";
my $ZFS_QUOTA_USER = "@ZFS_QUOTA_USER@";
my $ZFS_QUOTA_PROJECT = "@ZFS_QUOTA_PROJECT@";
......@@ -77,6 +81,7 @@ my $CHMOD = "/bin/chmod";
my $MKDIR = "/bin/mkdir";
my $MV = "/bin/mv";
my $ZFS = "/sbin/zfs";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $SKEL = "/usr/share/skel";
my $PIDFILE = "/var/run/mountd.pid";
......@@ -119,6 +124,8 @@ sub AddGroup();
sub DelProject();
sub DelGroup();
sub DropFile();
sub CheckDotFiles();
sub CreateSSHKey();
sub fatal($);
sub ZFSexists($);
sub MakeDir($$);
......@@ -182,6 +189,14 @@ SWITCH: for ($cmd) {
DropFile();
last SWITCH;
};
/^checkdotfiles$/ && do {
CheckDotFiles();
last SWITCH;
};
/^createsshkey$/ && do {
CreateSSHKey();
last SWITCH;
};
# Default
usage();
}
......@@ -570,6 +585,105 @@ sub DropFile()
return 0;
}
#
# Check the dot files.
#
sub CheckDotFiles()
{
if (@ARGV != 3) {
fatal("checkdotfiles: Wrong number of arguments");
}
my $user = shift(@ARGV);
my $gid = shift(@ARGV);
my $email = shift(@ARGV);
my $forward = "$USERROOT/$user/.forward";
my $cshrc = "$USERROOT/$user/.cshrc";
my $profile = "$USERROOT/$user/.profile";
# Just in case we got called before account created.
return 0
if (! -d "$USERROOT/$user");
#
# Set up a .forward file so that any email to them gets forwarded off.
#
print "Setting up .forward file for $user.\n";
sysopen(HANDLE, $forward, O_WRONLY|O_CREAT|O_TRUNC, 0600)
or fatal("sysopen $forward: $!");
print HANDLE "$email\n";
close(HANDLE);
mysystem("$CHOWN $user:$gid $forward") == 0
or fatal("Could not chown $forward to $user:$gid");
mysystem("$CHMOD 644 $forward") == 0
or fatal("Could not chmod '$forward' to 644");
#
# Add testbed path to .cshrc and .profile.
# Plus a conditional Cygwin section for the Windows system path.
#
my $cpathstr = "set path = ($USERPATH \$path)\n" .
'if ( `uname -s` =~ CYGWIN* ) then' . "\n" .
' setenv PATH "${PATH}:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS"' . "\n" .
'endif';
if (-e $cshrc && system("egrep -q -s '$USERPATH' $cshrc")) {
system("echo '$cpathstr' >> $cshrc");
}
my $spathstr = "PATH=$USERPATH:\$PATH\n" .
'if [[ `uname -s` == CYGWIN* ]]; then' . "\n" .
' PATH="$PATH":/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS' . "\n" .
'fi';
if (-e $profile && system("egrep -q -s '$USERPATH' $profile")) {
system("echo '$spathstr' >> $profile");
}
return 0;
}
#
# Create ssh keys for user, sending back the pub part. This is a little
# incovenient since we generate two keys (V1 and V2) but can send back
# just one at a time via STDOUT (do not want to parse anything).
#
sub CreateSSHKey()
{
if (@ARGV != 3) {
fatal("createsshkey: Wrong number of arguments");
}
my $user = shift(@ARGV);
my $gid = shift(@ARGV);
my $type = shift(@ARGV);
my $sshdir = "$USERROOT/$user/.ssh";
my $sshkey = "$sshdir/";
if ($type eq "rsa") {
$sshkey .= "identity";
}
elsif ($type eq "rsa1") {
$sshkey .= "id_rsa";
}
else {
fatal("Bad key type: $type");
}
unlink($sshkey)
if (-e $sshkey);
#
# Since we send the key back via STDOUT, make sure all output
# goes to STDERR.
#
mysystem("$KEYGEN -t $type -P '' -f $sshkey ".
"-C '${type}" . "\@" . ${OURDOMAIN} . "' 1>&2") == 0
or fatal("Failure in ssh-keygen!");
mysystem("$CHOWN $user:$gid $sshkey ${sshkey}.pub") == 0
or fatal("Could not chown $sshkey to $user:$gid");
# Return the key via STDOUT to boss.
my $ident = `cat ${sshkey}.pub`;
print STDOUT $ident;
return 0;
}
#
# Check for ZFS existence.
#
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -24,6 +24,7 @@
use English;
use Getopt::Std;
use XML::Simple;
use File::Temp qw(tempfile :POSIX );
#
# Parse ssh public keys and enter into the DB. The default format is
......@@ -70,7 +71,11 @@ my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@";
my $CONTROL = "@USERNODE@";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $ACCOUNTPROXY= "$TB/sbin/accountsetup";
my $SSH = "$TB/bin/sshtb";
my $SAVEUID = $UID;
my $USERUID;
# Locals
......@@ -281,18 +286,25 @@ else {
}
#
# Initmode or genmode, do it and exit. Eventually get rid of the switch
# to the target user.
# Initmode or genmode, do it and exit.
#
if ($initmode) {
# Drop root privs, switch to target user.
$EUID = $UID = $USERUID;
exit InitUser();
}
if ($genmode) {
# Drop root privs, switch to target user.
$EUID = $UID = $USERUID;
exit GenerateKeyFile();
if ($initmode || $genmode) {
my $firstproject;
my $default_groupgid;
if ($target_user->FirstApprovedProject(\$firstproject) < 0 ||
!defined($firstproject)) {
fatal("Could not determine first approved project");
}
$user_gid = $firstproject->unix_gid();
if ($initmode) {
exit(InitUser());
}
if ($genmode) {
exit(GenerateKeyFile());
}
exit(1);
}
# Else, key parse mode ...
......@@ -482,98 +494,74 @@ sub ParseKey($) {
#
sub InitUser()
{
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
#
# Set up the ssh key, but only if not done so already.
# Want to delete existing keys from DB, but not the sslcert key.
#
if (! -e "$sshdir") {
mkdir("$sshdir", 0700) or
fatal("Could not mkdir $sshdir: $!");
}
if (! -e "$sshdir/identity" || $force) {
print "Creating ssh protocol 1 key for $user.\n";
#
# Want to delete existing key from DB.
#
if (-e "$sshdir/identity") {
my $ident = `cat $sshdir/identity.pub`;
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/identity");
}
# Hmm, need to use -C option so comment field makes sense.
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and ".
" (internal=1 or comment like '%\@${OURDOMAIN}' or ".
" comment like '%\@boss.${OURDOMAIN}') and ".
" isaptkey=0 and comment not like 'sslcert:%'");
# Redirect pub key to file, redirect STDERR to STDIN for display.
my $outfile = tmpnam();
my $command = "$ACCOUNTPROXY createsshkey $user_uid $user_gid ";
$UID = 0;
open ERR, "$SSH -host $CONTROL '$command rsa1' 2>&1 > $outfile |";
$UID = $SAVEUID;
my $errs = "";
while (<ERR>) {
$errs .= $_;
}
close(ERR);
print STDERR $errs;
if ($?) {
unlink($outfile);
fatal("Could not create rsa1 key");
}
my $pubkey = `cat $outfile`;
chomp($pubkey);
my $safe_pubkey = DBQuoteSpecial($pubkey);
my $comment = "rsa\@${OURDOMAIN}";
if (system("$KEYGEN -t rsa1 -P '' ".
"-C '${user}" . "\@" . ${OURDOMAIN} . "' ".
"-f $sshdir/identity")) {
fatal("Failure in ssh-keygen!");
}
#
# Grab a copy for the DB.
#
my $ident = `cat $sshdir/identity.pub`;
if (! DBQueryWarn("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey=$safe_pubkey, comment='$comment'")) {
unlink($outfile);
fatal("Could not add rsa1 key to database");
}
$UID = 0;
open ERR, "$SSH -host $CONTROL '$command rsa' 2>&1 > $outfile |";
$UID = $SAVEUID;
if ($ident =~ /(\d*\s\d*\s[0-9a-zA-Z]*)\s([-\w\@\.]*)/) {
DBQueryFatal("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey='$1 $2', comment='$2'");
}
else {
fatal("Bad protocol 1 public key: $ident\n");
}
$errs = "";
while (<ERR>) {
$errs .= $_;
}
close(ERR);
print STDERR $errs;
if ($?) {
unlink($outfile);
fatal("Could not create rsa key");
}
#
# Moving to V2 keys ...
#
if (! -e "$sshdir/id_rsa" || $force) {
print "Creating ssh protocol 2 key for $user.\n";
#
# Want to delete existing key from DB.
#
if (-e "$sshdir/id_rsa") {
my $ident = `cat $sshdir/id_rsa.pub`;
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("delete from user_pubkeys ".
"where uid_idx='$user_dbid' and pubkey='$1 $2'");
}
unlink("$sshdir/id_rsa");
}
# Hmm, need to use -C option so comment field makes sense.
$pubkey = `cat $outfile`;
chomp($pubkey);
$safe_pubkey = DBQuoteSpecial($pubkey);
$comment = "rsa1\@${OURDOMAIN}";
if (system("$KEYGEN -t rsa -P '' ".
"-C '${user}" . "\@" . ${OURDOMAIN} . "' ".
"-f $sshdir/id_rsa")) {
fatal("Failure in ssh-keygen!");
}
#
# Grab a copy for the DB.
#
my $ident = `cat $sshdir/id_rsa.pub`;
if ($ident =~
/^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) {
DBQueryFatal("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey='$1 $2', comment='$2'");
}
else {
fatal("Bad protocol 2 public key: $ident\n");
}
}
if (! DBQueryWarn("replace into user_pubkeys set ".
" uid='$user_uid', uid_idx='$user_dbid', ".
" internal='1', nodelete='1', idx=NULL, stamp=now(), ".
" pubkey=$safe_pubkey, comment='$comment'")) {
unlink($outfile);
fatal("Could not add rsa key to database");
}
unlink($outfile);
return GenerateKeyFile();
}
......@@ -584,15 +572,9 @@ sub InitUser()
sub GenerateKeyFile()
{
my @pkeys = ();
my $outfile = tmpnam();
my $sshdir = "$HOMEDIR/$user_uid/.ssh";
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
if (! mkdir($sshdir, 0700)) {
warn("*** WARNING: Could not mkdir $sshdir: $!\n");
return -1;
}
}
my $query_result =
DBQueryFatal("select pubkey from user_pubkeys ".
"where uid_idx='$user_dbid'");
......@@ -601,15 +583,14 @@ sub GenerateKeyFile()
push(@pkeys, $key);
}
print "Generating $keyfile ...\n";
if (!open(AUTHKEYS, "> ${keyfile}.new")) {
warn("*** WARNING: Could not open ${keyfile}.new: $!\n");
print "Generating authorized_keys ...\n";
if (!open(AUTHKEYS, "> $outfile")) {
warn("*** WARNING: Could not open $outfile: $!\n");
return -1;
}
print AUTHKEYS "#\n";
print AUTHKEYS "# DO NOT EDIT! This file auto generated by ".
"Emulab.Net account software.\n";
"Emulab account software.\n";
print AUTHKEYS "#\n";
print AUTHKEYS "# Please use the web interface to edit your ".
"public key list.\n";
......@@ -620,27 +601,15 @@ sub GenerateKeyFile()
}
close(AUTHKEYS);
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 (!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");
}
elsif (-e "$sshdir/authorized_keys2") {
#
# Save to remove deprecated authorized_keys2 file at this point.
#
unlink("$sshdir/authorized_keys2");
$UID = 0;
system("$SSH -host $CONTROL ".
"'$ACCOUNTPROXY dropfile $user_uid $user_gid 0600 $sshdir ".
"authorized_keys' < $outfile");
$UID = $SAVEUID;
if ($?) {
unlink($outfile);
fatal("Could not copy authorized_keys file to $CONTROL");
}
return 0;
}
......
......@@ -43,7 +43,7 @@ use Getopt::Std;
sub usage()
{
print("Usage: tbacct [-f] [-b] [-u] ".
"<add|del|mod|passwd|wpasswd|email|freeze|thaw|verify|revoke> ".
"<add|del|mod|passwd|wpasswd|email|freeze|thaw|verify|revoke|dots> ".
"<user> [args]\n");
exit(-1);
}
......@@ -218,7 +218,8 @@ if ($user =~ /^([-\w]+)$/i) {
else {
die("Tainted argument: $user\n");
}
if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw|email|verify|revoke)$/) {
if ($cmd =~
/^(add|del|mod|freeze|passwd|wpasswd|thaw|email|verify|revoke|dots)$/) {
$cmd = $1;
}
else {
......@@ -351,6 +352,10 @@ SWITCH: for ($cmd) {
VerifyUser();
last SWITCH;
};
/^dots$/ && do {
CheckDotFiles();
last SWITCH;
};
}
#
......@@ -922,8 +927,6 @@ sub UpdateUser(;$)
#
sub UpdateEmail()
{
my $forward = "$HOMEDIR/$user/.forward";
#
# Only admin people can do this.
#
......@@ -990,11 +993,7 @@ sub UpdateEmail()
system("$GENELISTS -m -u $user");
$EUID = 0;
# Remove the users current .forward file to force regen.
unlink($forward)
if (-e $forward);
CheckDotFiles();
return 0;
}
......@@ -1129,69 +1128,14 @@ sub RevokeUser()
#
sub CheckDotFiles()
{
my $forward = "$HOMEDIR/$user/.forward";
my $cshrc = "$HOMEDIR/$user/.cshrc";
my $profile = "$HOMEDIR/$user/.profile";
# No home dirs for these.
return 0
if ($webonly || $isnonlocal);
if (! -d "$HOMEDIR/$user") {
return 0;
}
# As the user.
$UID = $user_number;
#
# Set up a .forward file so that any email to them gets forwarded off.
#
if (! -e $forward) {
print "Setting up .forward file for $user.\n";
if (system("echo \"$user_email\" > $forward")) {
fatal("Could not create $forward!");
}
chmod(0644, "$HOMEDIR/$user/.forward") or
fatal("Could not chmod $forward: $!");
$fileowner= (stat($forward))[4];
$dochown=0;
if ($fileowner==0) {
chown($user_number,$default_groupgid,"$HOMEDIR/$user/.forward") or
do {
warn("Could not chown $forward: $!");
$dochown=1;
};
}
}
#
# Add testbed path to .cshrc and .profile.
# Plus a conditional Cygwin section for the Windows system path.
#
my $cpathstr = "set path = ($USERPATH \$path)\n" .
'if ( `uname -s` =~ CYGWIN* ) then' . "\n" .
' setenv PATH "${PATH}:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS"' . "\n" .
'endif';
if (-e $cshrc && system("egrep -q -s '$USERPATH' $cshrc")) {
system("echo '$cpathstr' >> $cshrc");
}
my $spathstr = "PATH=$USERPATH:\$PATH\n" .
'if [[ `uname -s` == CYGWIN* ]]; then' . "\n" .
' PATH="$PATH":/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS' . "\n" .
'fi';
if (-e $profile && system("egrep -q -s '$USERPATH' $profile")) {
system("echo '$spathstr' >> $profile");
$UID = 0;
system("$SSH -host $CONTROL ".
" '$ACCOUNTPROXY checkdotfiles $user $default_groupgid ".
" $user_email'");
if ($?) {
fatal("Could not check dotfiles for user $user on $CONTROL.");
}
$UID = $SAVEUID;
if (defined($dochown) && $dochown!=0) {
chown($user_number,$default_groupgid,"$HOMEDIR/$user/.forward") or
warn("Could not chown $forward: $!");
}
return 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment