Commit ae77bdb6 authored by Leigh Stoller's avatar Leigh 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], "")) {