Commit 2a534d88 authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

Move addpubkey from the utils to account directory. Note that I copied

the RCS control file in the repository so the history is left intact.

Two new modes, which used to be in the old mkacct. There is an init
mode, which is used on new users to create the initial pub key. There
is also a write mode, which is used regenerate the authkeys files for
people after they add/delete keys via the web interface. Used to be
that addpubkey wold add the key to the DB, but mkacct would deal with
creating the authkeys files. All this functionality is now localized
in this one script.
parent 292d16c8
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -16,17 +16,22 @@ use Getopt::Std;
#
sub usage()
{
print "Usage: addpubkeys [-n] [-a] [-k] <user> [<keyfile> | <key>]\n";
print "Usage: addpubkeys [-n] [-k] <user> [<keyfile> | <key>]\n";
print " addpubkeys [-i | -w] <user>\n";
print "Options:\n";
print " -k Indicates that key was passed in on the command line\n";
print " -n Verify key format only; do not enter into into DB\n";
print " -a Audit mode; send audit message to log file\n";
print " -w Generate new authkeys (protocol 1 and 2) file for user\n";
print " -i Initialize mode; generate initial key for user\n";
exit(-1);
}
my $optlist = "kna";
my $optlist = "knaiw";
my $iskey = 0;
my $verify = 0;
my $auditmode = 0;
my $initmode = 0;
my $genmode = 0;
my $nobody = 0;
my $noemail = 0;
#
# Configure variables
......@@ -34,11 +39,24 @@ my $auditmode = 0;
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $HOMEDIR = "/users";
my $KEYGEN = "/usr/bin/ssh-keygen";
my $USERUID;
# Locals
my $user;
my $keyfile;
my $keyline;
my $key;
my $comment;
my $user_name;
my $user_email;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
......@@ -53,6 +71,14 @@ $| = 1;
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# 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");
}
#
# Please do not run it as root. Hard to track what has happened.
#
......@@ -75,26 +101,66 @@ if (defined($options{"k"})) {
if (defined($options{"n"})) {
$verify = 1;
}
if (defined($options{"a"})) {
$auditmode = 1;
if (defined($options{"i"})) {
$initmode = 1;
}
if (defined($options{"w"})) {
$genmode = 1;
}
if (@ARGV != 2) {
if ($verify && $genmode) {
usage();
}
my $user = $ARGV[0];
my $keyfile = $ARGV[1];
my $keyline;
my $key;
my $comment;
my $db_uid;
my $db_name = "Testbed Operations";
my $db_email = $TBOPS;
my $user_name;
my $user_email;
if ($initmode || $genmode) {
if (@ARGV != 1) {
usage();
}
}
elsif (@ARGV == 2) {
$keyfile = $ARGV[1];
}
else {
usage();
}
$user = $ARGV[0];
#
# Untaint the arguments.
#
if ($user =~ /^([a-z0-9]+)$/i) {
$user = $1;
}
else {
fatal("Tainted username: $user");
}
#
# If invoked as "nobody" its for a user with no actual account.
#
if (getpwuid($UID) eq "nobody") {
if ($initmode || $genmode) {
fatal("Bad usage as 'nobody'");
}
$nobody = 1;
}
else {
$USERUID = getpwnam($user);
}
#
# Initmode or genmode, do it and exit.
#
if ($initmode) {
# Drop root privs, switch to target user.
$EUID = $USERUID;
exit InitUser();
}
if ($genmode) {
# Drop root privs, switch to target user.
$EUID = $USERUID;
exit GenerateKeyFiles();
}
# Else, key parse mode ...
if ($iskey) {
if ($keyfile =~ /^([-\w\s\.\@\+\/\=]*)$/) {
$keyfile = $1;
......@@ -107,74 +173,48 @@ if ($iskey) {
else {
if ($keyfile =~ /^([-\w\.\/]+)$/) {
$keyfile = $1;
}
}
else {
fatal("Tainted filename: $keyfile");
}
if (! -e $keyfile) {
fatal("*** $0\n".
" No such file: $keyfile\n");
fatal("No such file: $keyfile\n");
}
$keyline = `head -1 $keyfile`;
}
if ($user =~ /^([a-z0-9]+)$/i) {
$user = $1;
}
else {
fatal("Tainted username: $user");
}
#
# Check user and get his DB uid and email stuff. If invoked as "nobody" its
# for a user with no actual account. While we do not do anything to the
# account (other than inserting the new key into the DB) either way, we do
# lose the ability to fully audit what is happening (we will not know the
# invoker of the script from the web interface).
# Check user
#
if ($verify) {
#
# Silly, and complicates things.
#
$auditmode = 0;
}
else {
if (getpwuid($UID) eq "nobody") {
$auditmode = 1;
$db_uid = $user;
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");
}
else {
if (! UNIX2DBUID($UID, \$db_uid)) {
fatal("*** $0:\n".
" You do not exist in the Emulab Database.\n");
if (-d "$HOMEDIR/$user/.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;
}
if ($user ne $db_uid) {
#
# Only admins can set pubkeys for another user.
#
if (!TBAdmin($UID)) {
fatal("*** $0:\n".
" You are not allowed to set pubkeys for $user.\n");
}
# Always audit when setting other people's keys.
$auditmode = 1;
else {
fatal("No DB info for $user!");
}
}
}
#
# Okay, if the user record does not exist yet, then send the audit message
# to tbops only.
#
if ($auditmode) {
if (! UserDBInfo($user, \$user_name, \$user_email)) {
$user_name = "Testbed Operations";
$user_email = $TBOPS;
}
if (! UserDBInfo($db_uid, \$db_name, \$db_email)) {
$db_name = "Testbed Audit";
$db_email = $TBAUDIT;
}
# Drop root privs, switching to user.
if (!$nobody) {
$EUID = $USERUID;
}
#
......@@ -182,9 +222,6 @@ if ($auditmode) {
# format we like (openssh), either protocol 1 or 2.
#
if (ParseKey($keyline)) {
if ($auditmode) {
audit();
}
exit 0;
}
# If the key was entered on the command line, then nothing more to do.
......@@ -201,9 +238,6 @@ if (! open(KEYGEN, "ssh-keygen -i -f $keyfile 2>/dev/null |")) {
}
$keyline = <KEYGEN>;
if (close(KEYGEN) && ParseKey($keyline)) {
if ($auditmode) {
audit();
}
exit 0;
}
exit 1;
......@@ -258,14 +292,8 @@ sub ParseKey($) {
#
# Mark user record as modified so nodes are updated.
#
DBQueryFatal("update users set usr_modified=now() where uid='$user'");
TBNodeUpdateAccountsByUID($user);
return 1;
}
sub audit()
{
my $chunked = "";
while (length($key)) {
......@@ -274,29 +302,160 @@ sub audit()
$chunked .= "\n";
}
}
print "SSH Public Key for '$user' added:\n";
print "$chunked\n";
SENDMAIL("$user_name <$user_email>",
"SSH Public Key for '$user' Added",
"SSH Public Key for '$user' added by '$db_uid'.\n".
"\n".
"$chunked\n",
"$db_name <$db_email>",
"Bcc: $TBAUDIT");
# Generate new auth keys file.
if ($genmode) {
GenerateKeyFiles();
}
if (! $noemail) {
SENDMAIL("$user_name <$user_email>",
"SSH Public Key for '$user' Added",
"SSH Public Key for '$user' added:\n".
"\n".
"$chunked\n",
"$TBOPS");
}
return 1;
}
sub fatal($)
#
# Init function for new users. Generate the first key for the user (which
# is loaded into the DB), and then generate the keyfiles. Note that the
# user might have preloaded personal keys.
#
sub InitUser()
{
my($mesg) = $_[0];
my $sshdir = "$HOMEDIR/$user/.ssh";
print STDERR "$mesg\n";
#
# Send a message to the testbed list.
# Set up the ssh key, but only if not done so already.
#
SENDMAIL($TBOPS,
"SSH Public key insertion failed!",
$mesg,
"$db_name <$db_email>");
exit(-1);
if (! -e "$sshdir") {
mkdir("$sshdir", 0700) or
fatal("Could not mkdir $sshdir: $!");
}
if (! -f "$sshdir/identity") {
print "Setting up ssh configuration for $user.\n";
if (system("$KEYGEN -t rsa1 -P '' -f $sshdir/identity")) {
fatal("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 {
fatal("Bad emulab public key: $ident\n");
}
}
return GenerateKeyFiles();
}
#
# Generate the ssh key files for the user. The keys come from the DB, and
# are split into protocol 1 and protocol 2 keys. Then use the aux function
# to generate each file.
#
sub GenerateKeyFiles()
{
my @p1keys = ();
my @p2keys = ();
my $query_result =
DBQueryFatal("select * from user_pubkeys where uid='$user'");
while (my %row = $query_result->fetchhash()) {
my $key = $row{'pubkey'};
if ($key =~ /^\d+\s+.*$/) {
push(@p1keys, $key);
}
else {
push(@p2keys, $key);
}
}
GenerateKeyFile(1, @p1keys);
GenerateKeyFile(2, @p2keys);
return 0;
}
#
# Generate ssh authorized_keys files. Either protocol 1 or 2.
# Returns 0 on success, -1 on failure.
#
#
sub GenerateKeyFile($$)
{
my ($protocol, @pkeys) = @_;
my $sshdir = "$HOMEDIR/$user/.ssh";
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
if (! mkdir($sshdir, 0700)) {
warn("*** WARNING: Could not mkdir $sshdir: $!\n");
return -1;
}
}
if ($protocol == 2) {
$keyfile .= "2";
}
print "Generating $keyfile ...\n";
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 (!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");
}
return 0;
}
sub fatal($) {
my($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -20,6 +20,6 @@ my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/bin/addpubkey", @ARGV;
exec "$TB/sbin/addpubkey", @ARGV;
die("webaddpubkey: Could not exec addpubkey: $!");
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