All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

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) {