Commit ae77bdb6 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Rework all of the ssh key handling. Moved the parsing and verification

to an external perl script, and use ssh-keygen to attempt conversion
off SSH2/SECSH key formats. This is actually a simplification of the
php code, which is not generally very good at this kind of thing (or
maybe I mean perl is just better at it). The parsing and error
handling it also much improved.
parent 541a3586
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Parse ssh public keys and enter into the DB. The default format is
# openssh, but if the key is not in that format, then use ssh-keygen
# to see if it can be converted from either SSH2 or SECSH format into
# openssh format. This gets called from the webpage to parse keys
# uploaded by users.
#
sub usage()
{
print "Usage: addpubkeys [-n] [-a] [-k] <user> [<keyfile> | <key>]\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";
exit(-1);
}
my $optlist = "kna";
my $iskey = 0;
my $verify = 0;
my $auditmode = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# 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!\n");
}
#
# 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{"k"})) {
$iskey = 1;
}
if (defined($options{"n"})) {
$verify = 1;
}
if (defined($options{"a"})) {
$auditmode = 1;
}
if (@ARGV != 2) {
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;
#
# Untaint the arguments.
#
if ($iskey) {
if ($keyfile =~ /^([-\w\s\.\@\+\/\=]*)$/) {
$keyfile = $1;
}
else {
fatal("Tainted key: $keyfile");
}
$keyline = $keyfile;
print "$keyline\n";
}
else {
if ($keyfile =~ /^([-\w\.\/]+)$/) {
$keyfile = $1;
}
else {
fatal("Tainted filename: $keyfile");
}
if (! -e $keyfile) {
fatal("*** $0\n".
" 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, unless only verfying
# the format of the key, or if its a new user and the caller is "nobody".
# In that case, the user better not exist.
#
if (getpwuid($UID) eq "nobody") {
if (getpwnam($user) ||
UserDBInfo($user, \$user_name, \$user_email)) {
fatal("*** $0:\n".
" Attempt to insert first key for existing user!\n");
}
}
elsif (! $verify) {
if (! UNIX2DBUID($UID, \$db_uid)) {
fatal("*** $0:\n".
" You do not exist in the Emulab Database.\n");
}
if (! UserDBInfo($db_uid, \$db_name, \$db_email)) {
fatal("*** $0:\n".
" Cannot determine your name and email address.\n");
}
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");
}
if (! UserDBInfo($user, \$user_name, \$user_email)) {
fatal("*** $0:\n".
" Cannot determine name and email address for $user.\n");
}
# Always audit when setting other people's keys.
$auditmode = 1;
}
else {
$user_name = $db_name;
$user_email = $db_email;
}
}
#
# Grab the first line of the file. Parse it to see if its in the
# 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.
if ($iskey) {
exit 1;
}
#
# Run ssh-keygen over it and see if it can convert it.
#
if (! open(KEYGEN, "ssh-keygen -i -f $keyfile 2>/dev/null |")) {
fatal("*** $0:\n".
" Could not start ssh-keygen\n");
}
$keyline = <KEYGEN>;
if (close(KEYGEN) && ParseKey($keyline)) {
if ($auditmode) {
audit();
}
exit 0;
}
exit 1;
sub ParseKey($) {
my ($keyline) = @_;
if ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*) ([-\w\@\.]*)$/) {
# Protocol 1
$type = "ssh-rsa1";
$key = $1;
$comment = $2;
}
elsif ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*)\s*$/) {
# Protocol 1 but no comment field.
$type = "ssh-rsa1";
$key = $1;
}
elsif ($keyline =~
/^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=]*) ([-\w\@\.]*)$/) {
# Protocol 2
$type = $1;
$key = "$1 $2";
$comment = $3;
}
elsif ($keyline =~ /^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=]*)$/) {
# Protocol 2 but no comment field
$type = $1;
$key = "$1 $2";
}
if (!defined($key)) {
return 0;
}
# Do not enter into DB if in verify mode.
if ($verify) {
return 1;
}
#
# Make up a comment field for the DB index. Need something.
#
if (!defined($comment)) {
$comment = "$type-${user_email}";
}
$key = "$key $comment";
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', '$comment', '$key', now())");
return 1;
}
sub audit()
{
my $chunked = "";
while (length($key)) {
$chunked .= substr($key, 0, 65, "");
if (length($key)) {
$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");
}
sub fatal($)
{
my($mesg) = $_[0];
print STDERR "$mesg\n";
#
# Send a message to the testbed list.
#
SENDMAIL($TBOPS,
"SSH Public key insertion failed!",
$mesg,
"$db_name <$db_email>");
exit(-1);
}
#!/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 ...
#
#
# Configure variables
#
my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/bin/addpubkey", @ARGV;
die("webaddpubkey: Could not exec addpubkey: $!");
......@@ -12,7 +12,7 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@
include $(OBJDIR)/Makeconf
BIN_SCRIPTS = delay_config sshtb create_image node_admin
BIN_SCRIPTS = delay_config sshtb create_image node_admin addpubkey
SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl
LIBEXEC_SCRIPTS = webcreateimage webaddpubkey
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Parse ssh public keys and enter into the DB. The default format is
# openssh, but if the key is not in that format, then use ssh-keygen
# to see if it can be converted from either SSH2 or SECSH format into
# openssh format. This gets called from the webpage to parse keys
# uploaded by users.
#
sub usage()
{
print "Usage: addpubkeys [-n] [-a] [-k] <user> [<keyfile> | <key>]\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";
exit(-1);
}
my $optlist = "kna";
my $iskey = 0;
my $verify = 0;
my $auditmode = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# 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!\n");
}
#
# 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{"k"})) {
$iskey = 1;
}
if (defined($options{"n"})) {
$verify = 1;
}
if (defined($options{"a"})) {
$auditmode = 1;
}
if (@ARGV != 2) {
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;
#
# Untaint the arguments.
#
if ($iskey) {
if ($keyfile =~ /^([-\w\s\.\@\+\/\=]*)$/) {
$keyfile = $1;
}
else {
fatal("Tainted key: $keyfile");
}
$keyline = $keyfile;
print "$keyline\n";
}
else {
if ($keyfile =~ /^([-\w\.\/]+)$/) {
$keyfile = $1;
}
else {
fatal("Tainted filename: $keyfile");
}
if (! -e $keyfile) {
fatal("*** $0\n".
" 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, unless only verfying
# the format of the key, or if its a new user and the caller is "nobody".
# In that case, the user better not exist.
#
if (getpwuid($UID) eq "nobody") {
if (getpwnam($user) ||
UserDBInfo($user, \$user_name, \$user_email)) {
fatal("*** $0:\n".
" Attempt to insert first key for existing user!\n");
}
}
elsif (! $verify) {
if (! UNIX2DBUID($UID, \$db_uid)) {
fatal("*** $0:\n".
" You do not exist in the Emulab Database.\n");
}
if (! UserDBInfo($db_uid, \$db_name, \$db_email)) {
fatal("*** $0:\n".
" Cannot determine your name and email address.\n");
}
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");
}
if (! UserDBInfo($user, \$user_name, \$user_email)) {
fatal("*** $0:\n".
" Cannot determine name and email address for $user.\n");
}
# Always audit when setting other people's keys.
$auditmode = 1;
}
else {
$user_name = $db_name;
$user_email = $db_email;
}
}
#
# Grab the first line of the file. Parse it to see if its in the
# 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.
if ($iskey) {
exit 1;
}
#
# Run ssh-keygen over it and see if it can convert it.
#
if (! open(KEYGEN, "ssh-keygen -i -f $keyfile 2>/dev/null |")) {
fatal("*** $0:\n".
" Could not start ssh-keygen\n");
}
$keyline = <KEYGEN>;
if (close(KEYGEN) && ParseKey($keyline)) {
if ($auditmode) {
audit();
}
exit 0;
}
exit 1;
sub ParseKey($) {
my ($keyline) = @_;
if ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*) ([-\w\@\.]*)$/) {
# Protocol 1
$type = "ssh-rsa1";
$key = $1;
$comment = $2;
}
elsif ($keyline =~ /^(\d*\s\d*\s[0-9a-zA-Z]*)\s*$/) {
# Protocol 1 but no comment field.
$type = "ssh-rsa1";
$key = $1;
}
elsif ($keyline =~
/^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=]*) ([-\w\@\.]*)$/) {
# Protocol 2
$type = $1;
$key = "$1 $2";
$comment = $3;
}
elsif ($keyline =~ /^(ssh-rsa|ssh-dss) ([-\w\.\@\+\/\=]*)$/) {
# Protocol 2 but no comment field
$type = $1;
$key = "$1 $2";
}
if (!defined($key)) {
return 0;
}
# Do not enter into DB if in verify mode.
if ($verify) {
return 1;
}
#
# Make up a comment field for the DB index. Need something.
#
if (!defined($comment)) {
$comment = "$type-${user_email}";
}
$key = "$key $comment";
DBQueryFatal("replace into user_pubkeys ".
"values ('$user', '$comment', '$key', now())");
return 1;
}
sub audit()
{
my $chunked = "";
while (length($key)) {
$chunked .= substr($key, 0, 65, "");
if (length($key)) {
$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");
}
sub fatal($)
{
my($mesg) = $_[0];
print STDERR "$mesg\n";
#
# Send a message to the testbed list.
#
SENDMAIL($TBOPS,
"SSH Public key insertion failed!",
$mesg,
"$db_name <$db_email>");
exit(-1);
}
#!/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 ...
#
#
# Configure variables
#
my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/bin/addpubkey", @ARGV;
die("webaddpubkey: Could not exec addpubkey: $!");
......@@ -54,7 +54,8 @@ if (! mysql_num_rows($query_result)) {
SPITSTATUS(CDROMSTATUS_BADCDKEY);
return;
}
$row = mysql_fetch_array($query_result);
$row = mysql_fetch_array($query_result);
$cdvers = $row[version];
#
# Grab the privkey record. First squeeze out any spaces.
......@@ -148,15 +149,7 @@ DBQueryFatal("update widearea_privkeys ".
header("Content-Type: text/plain");
echo "privkey=$newkey\n";
if (0) {
echo "fdisk=http://${WWWHOST}/images/image.fdisk\n";
echo "slice1_image=http://${WWWHOST}/images/slice1.ndz\n";
echo "slice1_md5=2970e2cf045f5872c6728eeea3b51dae\n";
echo "slicex_mount=/users\n";
echo "slicex_tarball=http://${WWWHOST}/images/slicex.tar.gz\n";
echo "slicex_md5=1f84fbc3434d174151ac3a2b8389799a\n";
}
else {
if ($cdvers == 1) {
echo "fdisk=image.fdisk\n";
echo "slice1_image=slice1.ndz\n";
echo "slice1_md5=cb810b43f49d15b3ac4122ff42f8925d\n";
......@@ -164,5 +157,13 @@ else {
echo "slicex_tarball=slicex.tar.gz\n";
echo "slicex_md5=1f84fbc3434d174151ac3a2b8389799a\n";
}
else {
echo "fdisk=http://${WWWHOST}/images/image.fdisk\n";
echo "slice1_image=http://${WWWHOST}/images/slice1-v2.ndz\n";
echo "slice1_md5=5389a2687cee16ff212bbd842585a5d5\n";
echo "slicex_mount=/users\n";
echo "slicex_tarball=http://${WWWHOST}/images/slicex.tar.gz\n";
echo "slicex_md5=1f84fbc3434d174151ac3a2b8389799a\n";
}
echo "emulab_status=0\n";
......@@ -169,6 +169,19 @@ function SUEXEC($uid, $gid, $cmdandargs, $die) {
return $retval;
}
function ADDPUBKEY($uid, $cmdandargs) {
global $TBSUEXEC_PATH;
ignore_user_abort(1);
$output = array();
$retval = 0;
$result = exec("$TBSUEXEC_PATH $uid nobody $cmdandargs",
$output, $retval);
return $retval;
}
#
# Verify a URL.
#
......
......@@ -44,9 +44,10 @@ function SPITFORM($formfields, $returning, $errors)
PAGEHEADER("Apply for Project Membership");
if ($errors) {
echo "<table align=center border=0 cellpadding=0 cellspacing=2>
echo "<table class=stealth
align=center border=0 cellpadding=0 cellspacing=2>
<tr>
<td nowrap align=center colspan=3>
<td class=stealth nowrap align=center colspan=3>
<font size=+1 color=red>
Oops, please fix the following errors!
</font>
......@@ -55,9 +56,11 @@ function SPITFORM($formfields, $returning, $errors)
while (list ($name, $message) = each ($errors)) {
echo "<tr>
<td align=right><font color=red>$name:</font></td>
<td>&nbsp &nbsp</td>
<td align=left><font color=red>$message</font></td>
<td class=stealth align=right>
<font color=red>$name:</font></td>
<td class=stealth>&nbsp &nbsp</td>
<td class=stealth align=left>
<font color=red>$message</font></td>
</tr>\n";
}
echo "</table><br>\n";
......@@ -204,6 +207,7 @@ function SPITFORM($formfields, $returning, $errors)
<br>
<input type=text
name=\"formfields[usr_key]\"
value=\"$formfields[usr_key]\"
size=50
maxlength=1024>
</td>
......@@ -275,7 +279,15 @@ function SPITFORM($formfields, $returning, $errors)
if (! $returning) {
echo "<li> If you want us to use your existing ssh public key,
then either paste it in or specify the path to your
your identity.pub file.
your identity.pub file. <font color=red>NOTE:</font>
We use the <a href=www.openssh.org>OpenSSH</a> key format,
which has a slightly different protocol 2 public key format
than some of the commercial vendors such as
<a href=www.ssh.com>SSH Communications</a>. If you
use one of these commercial vendors, then please
upload the public key file and we will convert it
for you. <i>Please do not paste it in.</i>\n
<li> Note to <a href=http://www.opera.com><b>Opera 5</b></a>
users: The file upload mechanism is broken in Opera, so
you cannot specify a local file for upload. Instead,
......@@ -451,55 +463,41 @@ if (!$returning) {
}
#
# Pasted in key.
#
# Pub Key.
#
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "")) {
#