Commit 6c6f8baf authored by Leigh Stoller's avatar Leigh Stoller

The big key changes ... Deprecate the two pubkey slots in the users

table and create a new table to hold user_pubkeys, indexed by the
comment field of the key. Change mkacct to insert newly created Emulab
keys into that table, and to regen the users authorized_keys file
from the DB. Users should no longer edit their own authorized_keys
file or the changes will be lost (I put a comment in their files).

Change the three pages that deal with keys. join/new project can now
take a file of multiple keys; each is inserted. Moved the key stuff
that was in the update user info page into a new pubkeys page that
allows users to add/sub keys easily. New key additions are password
protected.

Unrelated change: Add an audit mode to mkacct to log its output and
send it to the tblogs email. Previously, warnings and errors tended to
get lost.
parent 8f0d50f4
#!/usr/bin/perl -wT
use English;
use Getopt::Std;
#
# Create a user account. All this script does is create the account
# if it does not exist, or update the password and gecos fields.
......
......@@ -492,7 +492,7 @@ while (list ($header, $value) = each ($HTTP_POST_VARS)) {
#
# Create user account on control node.
#
SUEXEC($uid, "flux", "webmkacct $user", 0);
SUEXEC($uid, "flux", "webmkacct -a $user", 0);
SUEXEC($uid, "flux", "websetgroups $user", 0);
continue;
......
......@@ -1141,6 +1141,19 @@ function TBCvswebAllowed($uid) {
#
# DB Interface.
#
$maxtries = 3;
$DBlinkid = 0;
while ($maxtries && !$DBlinkid) {
$DBlinkid = mysql_connect();
$maxtries--;
sleep(1);
}
if (! $DBlinkid) {
TBERROR("Could not connect to mysqld after several attempts!", 1);
}
if (!mysql_select_db($TBDBNAME)) {
TBERROR("Could not select DB after connecting!", 1);
}
#
# Record last DB error string.
......
<?php
include("defs.php3");
include("showstuff.php3");
#
# No PAGEHEADER since we spit out a redirect later.
#
#
# Only known and logged in users can do this.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
#
# Verify form arguments.
#
if (!isset($target_uid) ||
strcmp($target_uid, "") == 0) {
USERERROR("Improper form arguments!", 1);
}
if (!isset($key) ||
strcmp($key, "") == 0) {
USERERROR("Improper form arguments!", 1);
}
#
# Check to make sure thats this is a valid UID.
#
if (! TBCurrentUser($target_uid)) {
USERERROR("The user $target_uid is not a valid user", 1);
}
#
# Verify that this uid is a member of one of the projects that the
# target_uid is in. Must have proper permission in that group too.
#
if (!$isadmin &&
strcmp($uid, $target_uid)) {
if (! TBUserInfoAccessCheck($uid, $target_uid, $TB_USERINFO_MODIFYINFO)) {
USERERROR("You do not have permission to change ${user}'s keys!", 1);
}
}
#
# Get the actual key.
#
$query_result =
DBQueryFatal("select * from user_pubkeys ".
"where uid='$target_uid' and comment='$key'");
if (! mysql_num_rows($query_result)) {
USERERROR("Public Key '$key' for user '$target_uid' does not exist!", 1);
}
$row = mysql_fetch_array($query_result);
$pubkey = $row[pubkey];
$chunky = chunk_split($pubkey, 70, "<br>\n");
#
# We run this twice. The first time we are checking for a confirmation
# by putting up a form. The next time through the confirmation will be
# set. Or, the user can hit the cancel button, in which case we should
# probably redirect the browser back up a level.
#
if ($canceled) {
PAGEHEADER("SSH Public Key Maintenance");
echo "<center><h2><br>
SSH Public Key deletion canceled!
</h2></center>\n";
echo "<br>
Back to <a href='showpubkeys.php3?target_uid=$target_uid'>
ssh public keys</a> for user '$uid'.\n";
PAGEFOOTER();
return;
}
if (!$confirmed) {
PAGEHEADER("SSH Public Key Maintenance");
echo "<center><h2><br>
Are you <b>REALLY</b>
sure you want to delete SSH Public Key '$key' for user '$target_uid'?
</h2>\n";
echo "<form action='deletepubkey.php3?target_uid=$target_uid&key=$key'
method=post>";
echo "<b><input type=submit name=confirmed value=Confirm></b>\n";
echo "<b><input type=submit name=canceled value=Cancel></b>\n";
echo "</form>\n";
echo "</center>\n";
echo "<table align=center border=1 cellpadding=2 cellspacing=2>
<tr>
<td>$chunky</td>
</tr>
</table>\n";
PAGEFOOTER();
return;
}
#
# Audit
#
TBUserInfo($uid, $uid_name, $uid_email);
TBUserInfo($target_uid, $targuid_name, $targuid_email);
TBMAIL("$targuid_name <$targuid_email>",
"SSH Public Key for '$target_uid' Deleted",
"\n".
"SSH Public Key for '$target_uid' deleted by '$uid'.\n".
"\n".
"$chunky\n".
"\n".
"Thanks,\n".
"Testbed Ops\n".
"Utah Network Testbed\n",
"From: $uid_name <$uid_email>\n".
"Cc: $TBMAIL_AUDIT\n".
"Errors-To: $TBMAIL_WWW");
DBQueryFatal("delete from user_pubkeys ".
"where uid='$target_uid' and comment='$key'");
#
# mkacct updates the user pubkeys.
#
SUEXEC($uid, "flux", "webmkacct -a $target_uid", 0);
header("Location: showpubkeys.php3?target_uid=$target_uid");
?>
......@@ -105,6 +105,12 @@ SUEXEC($uid, "flux", "rmacct-ctrl $target_uid", 0);
$query_result =
DBQueryFatal("delete FROM users where uid='$target_uid'");
#
# Then the pubkey table,
#
$query_result =
DBQueryFatal("delete FROM user_pubkeys where uid='$target_uid'");
#
# Warm fuzzies.
#
......
......@@ -199,7 +199,6 @@ function SPITFORM($formfields, $returning, $errors)
<br>
<input type=text
name=\"formfields[usr_key]\"
value=\"" . $formfields[usr_key] . "\"
size=50
maxlength=1024>
</td>
......@@ -456,7 +455,7 @@ if (!$returning) {
#
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "")) {
$usr_key = $formfields[usr_key];
$usr_key[] = $formfields[usr_key];
}
#
......@@ -470,16 +469,19 @@ if (!$returning) {
if (! ($fp = fopen($usr_keyfile, "r"))) {
TBERROR("Could not open $usr_keyfile", 1);
}
$buffer = fgets($fp, 4096);
if (! ereg("^[0-9a-zA-Z\@\. ]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
SPITFORM($formfields, $returning, $errors);
PAGEFOOTER();
return;
while (!feof($fp)) {
$buffer = fgets($fp, 4096);
if (! ereg("^[0-9a-zA-Z\@\. \n]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
fclose($fp);
SPITFORM($formfields, $returning, $errors);
PAGEFOOTER();
return;
}
$usr_key[] = Chop($buffer);
}
$usr_key = Chop($buffer);
fclose($fp);
}
}
......@@ -549,8 +551,25 @@ if (! $returning) {
"date_add(now(), interval 1 year))");
if (isset($usr_key)) {
DBQueryFatal("update users set home_pubkey='$usr_key' ".
"where uid='$joining_uid'");
while (list ($idx, $stuff) = each ($usr_key)) {
#
# Need to separate out the comment field.
#
$pieces = explode(" ", $stuff);
if (count($pieces) != 4) {
if (count($pieces) != 1) {
TBERROR("Bad Key for $joining_uid: $stuff", 0);
}
continue;
}
# These have already been tested for bad chars above (ereg).
$key = "$pieces[0] $pieces[1] $pieces[2] $pieces[3]";
$comment = $pieces[3];
DBQueryFatal("replace into user_pubkeys ".
"values ('$joining_uid', '$comment', '$key', now())");
}
}
$key = GENKEY($joining_uid);
......
......@@ -24,7 +24,7 @@ $isadmin = ISADMIN($uid);
function SPITFORM($formfields, $errors)
{
global $TBDB_UIDLEN, $TBDB_PIDLEN, $TBDB_GIDLEN, $isadmin;
global $usr_keyfile;
global $target_uid;
#
# Standard Testbed Header. Written late cause of password
......@@ -60,8 +60,7 @@ function SPITFORM($formfields, $errors)
</td>
</tr>\n
<form enctype=multipart/form-data
action=moduserinfo.php3 method=post>\n";
<form action=moduserinfo.php3 method=post>\n";
#
# UserName. This is a constant field.
......@@ -174,36 +173,6 @@ function SPITFORM($formfields, $errors)
</td>
</tr>\n";
#
# SSH public key
#
echo "<tr>
<td rowspan><center>
Your SSH Pub Key: &nbsp<br>
[<b>2</b>]
</center></td>
<td rowspan><center>Upload (1K max)[<b>3</b>]<br>
<b>Or</b><br>
Insert Key
</center></td>
<td rowspan>
<input type=hidden name=MAX_FILE_SIZE value=1024>
<input type=file
name=usr_keyfile
value=\"" . $usr_keyfile . "\"
size=50>
<br>
<br>
<input type=text
name=\"formfields[usr_key]\"
value=\"" . $formfields[usr_key] . "\"
size=50
maxlength=1024>
</td>
</tr>\n";
#
# Password. Note that we do not resend the password. User
# must retype on error.
......@@ -239,13 +208,9 @@ function SPITFORM($formfields, $errors)
<a href = 'docwrapper.php3?docname=security.html'>
security policies</a> for information
regarding passwords and email addresses.
<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.
<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, please paste your
key in.
<li> You can also
<a href='showpubkeys.php3?target_uid=$target_uid'>
edit your ssh public keys.</a>
</ol>
</blockquote></blockquote>
</h4>\n";
......@@ -307,7 +272,7 @@ if (mysql_num_rows($query_result) == 0) {
# On first load, display a form consisting of current user values, and exit.
#
if (! isset($submit)) {
$formfields = array();
$defaults = array();
#
# Construct a defaults array based on current DB info.
......@@ -321,7 +286,6 @@ if (! isset($submit)) {
$defaults[usr_phone] = $row[usr_phone];
$defaults[usr_title] = $row[usr_title];
$defaults[usr_affil] = $row[usr_affil];
$defaults[usr_key] = $row[home_pubkey];
SPITFORM($defaults, 0);
PAGEFOOTER();
......@@ -396,12 +360,6 @@ if (isset($formfields[password1]) &&
$errors["Password"] = "$checkerror";
}
}
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "") &&
! ereg("^[0-9a-zA-Z\@\. ]*$", $formfields[usr_key])) {
$errors["PubKey"] = "Invalid characters";
}
if (count($errors)) {
SPITFORM($formfields, $errors);
PAGEFOOTER();
......@@ -426,40 +384,6 @@ else {
$usr_URL = $formfields[usr_URL];
}
#
# Pasted key.
#
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "")) {
$usr_key = $formfields[usr_key];
}
#
# If usr provided a file for the key, it overrides the paste in text.
# Must read and check it.
#
# XXX I allow only a single line of stuff. The rest is ignored for now.
#
if (isset($usr_keyfile) &&
strcmp($usr_keyfile, "") &&
strcmp($usr_keyfile, "none")) {
if (! ($fp = fopen($usr_keyfile, "r"))) {
TBERROR("Could not open $usr_keyfile", 1);
}
$buffer = fgets($fp, 4096);
if (! ereg("^[0-9a-zA-Z\@\. \n]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
SPITFORM($formfields, $errors);
PAGEFOOTER();
return;
}
$usr_key = Chop($buffer);
fclose($fp);
}
#
# Check that email address looks reasonable. Only admin types can change
# the email address. If its different, the user circumvented the form,
......@@ -533,11 +457,6 @@ DBQueryFatal("UPDATE users SET ".
"usr_affil=\"$usr_affil\" ".
"WHERE uid=\"$target_uid\"");
if (isset($usr_key)) {
DBQueryFatal("update users set home_pubkey='$usr_key' ".
"where uid='$target_uid'");
}
#
# Audit
#
......@@ -567,7 +486,7 @@ TBMAIL("$usr_name <$usr_email>",
#
# mkacct updates the user gecos and password.
#
SUEXEC($uid, "flux", "webmkacct $target_uid", 0);
SUEXEC($uid, "flux", "webmkacct -a $target_uid", 0);
#
# Spit out a redirect so that the history does not include a post
......
......@@ -215,7 +215,6 @@ function SPITFORM($formfields, $returning, $errors)
<br>
<input type=text
name=\"formfields[usr_key]\"
value=\"" . $formfields[usr_key] . "\"
size=50
maxlength=1024>
</td>
......@@ -669,7 +668,7 @@ if (!$returning) {
#
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "")) {
$usr_key = $formfields[usr_key];
$usr_key[] = $formfields[usr_key];
}
#
......@@ -683,16 +682,19 @@ if (!$returning) {
if (! ($fp = fopen($usr_keyfile, "r"))) {
TBERROR("Could not open $usr_keyfile", 1);
}
$buffer = fgets($fp, 4096);
if (! ereg("^[0-9a-zA-Z\@\. \n]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
SPITFORM($formfields, $errors);
PAGEFOOTER();
return;
while (!feof($fp)) {
$buffer = fgets($fp, 4096);
if (! ereg("^[0-9a-zA-Z\@\. \n]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
fclose($fp);
SPITFORM($formfields, $returning, $errors);
PAGEFOOTER();
return;
}
$usr_key[] = Chop($buffer);
}
$usr_key = Chop($buffer);
fclose($fp);
}
}
......@@ -770,8 +772,26 @@ if (! $returning) {
"date_add(now(), interval 1 year))");
if (isset($usr_key)) {
DBQueryFatal("update users set home_pubkey='$usr_key' ".
"where uid='$proj_head_uid'");
while (list ($idx, $stuff) = each ($usr_key)) {
#
# Need to separate out the comment field.
#
$pieces = explode(" ", $stuff);
if (count($pieces) != 4) {
if (count($pieces) != 1) {
TBERROR("Bad Key for $proj_head_uid: $stuff", 0);
}
continue;
}
# These have already been tested for bad chars above (ereg).
$key = "$pieces[0] $pieces[1] $pieces[2] $pieces[3]";
$comment = $pieces[3];
DBQueryFatal("replace into user_pubkeys ".
"values ('$proj_head_uid', ".
" '$comment', '$key', now())");
}
}
$key = GENKEY($proj_head_uid);
......
<?php
include("defs.php3");
include("showstuff.php3");
#
# Only known and logged in users can do this.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
#
# Verify form arguments.
#
if (!isset($target_uid) ||
strcmp($target_uid, "") == 0) {
$target_uid = $uid;
}
#
# Check to make sure thats this is a valid UID.
#
if (! TBCurrentUser($target_uid)) {
USERERROR("The user $target_uid is not a valid user", 1);
}
#
# Verify that this uid is a member of one of the projects that the
# target_uid is in. Must have proper permission in that group too.
#
if (!$isadmin &&
strcmp($uid, $target_uid)) {
if (! TBUserInfoAccessCheck($uid, $target_uid, $TB_USERINFO_READINFO)) {
USERERROR("You do not have permission to view ${user}'s keys!", 1);
}
}
function SPITFORM($formfields, $errors)
{
global $isadmin, $usr_keyfile_name, $target_uid;
#
# Standard Testbed Header, now that we know what we want to say.
#
if (strcmp($uid, $target_uid)) {
PAGEHEADER("SSH Public Keys for user: $target_uid");
}
else {
PAGEHEADER("My SSH Public Keys");
}
#
# Get the list and show it.
#
$query_result =
DBQueryFatal("select * from user_pubkeys where uid='$target_uid'");
if (mysql_num_rows($query_result)) {
echo "<table align=center border=1 cellpadding=2 cellspacing=2>\n";
echo "<center>
Current ssh public keys for user $target_uid.
</center><br>\n";
echo "<tr>
<td>Delete?</td>
<td>Key</td>
</tr>\n";
while ($row = mysql_fetch_array($query_result)) {
$comment = $row[comment];
$pubkey = $row[pubkey];
$date = $row[stamp];
$chunky = chunk_split("$pubkey", 75, "<br>\n");
echo "<tr>
<td align=center>
<A href='deletepubkey.php3?target_uid=$target_uid" .
"&key=$comment'><img alt=X src=redball.gif></A>
</td>
<td>$chunky</td>
</tr>\n";
}
echo "</table>\n";
}
else {
echo "<center>
There are no public keys on file for user $target_uid!
</center>\n";
}
echo "<br><hr size=4>\n";
echo "<center>
Enter new ssh public keys for user ${target_uid}[<b>1,2</b>].
</center><br>\n";
if ($errors) {
echo "<table align=center border=0 cellpadding=0 cellspacing=2>
<tr>
<td align=center colspan=3>
<font size=+1 color=red>
Oops, please fix the following errors!
</font>
</td>
</tr>\n";
while (list ($name, $message) = each ($errors)) {
echo "<tr>
<td align=right><font color=red>$name:</font></td>
<td>&nbsp</td>
<td align=left><font color=red>$message</font></td>
</tr>\n";
}
echo "</table><br>\n";
}
echo "<table align=center border=1>
<form enctype=multipart/form-data
action=showpubkeys.php3?target_uid=$target_uid method=post>\n";
#
# SSH public key
#
echo "<tr>
<td rowspan><center>Upload (4K max)[<b>3,4</b>]<br>
<b>Or</b><br>
Insert Key
</center></td>
<td rowspan>
<input type=hidden name=MAX_FILE_SIZE value=4096>
<input type=file
name=usr_keyfile
size=50>
<br>
<br>
<input type=text
name=\"formfields[usr_key]\"
size=50
maxlength=1024>
</td>
</tr>\n";
#
# Verify with password.
#
if (!$isadmin) {
echo "<tr>
<td>Password[<b>5</b>]:</td>
<td class=left>
<input type=password
name=\"formfields[password]\"
size=8></td>
</tr>\n";
}
echo "<tr>
<td colspan=2 align=center>
<b><input type=submit name=submit value='Add New Keys'></b>
</td>
</tr>\n";
echo "</form>
</table>\n";
echo "<h4><blockquote><blockquote>
<ol>
<li> Please consult our
<a href = 'docwrapper.php3?docname=security.html#SSH'>
security policies</a> for information
regarding ssh public keys.
<li> You should not hand edit your your authorized_keys file on
Emulab (.ssh/authorized_keys) since modifications via this
page will overwrite the file.
<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, please paste your
key in.
<li> Typically, the file you want to upload is your
identity.pub, contained in your .ssh directory.
<li> As a security precaution, you must supply your password
when adding new ssh public keys.
</ol>
</blockquote></blockquote>
</h4>\n";
}
#
# On first load, display a form of current values.
#
if (! isset($submit) || isset($finished)) {
$defaults = array();
SPITFORM($defaults, 0);
PAGEFOOTER();
return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
if (isset($formfields[usr_key]) &&
strcmp($formfields[usr_key], "")) {
#
# Replace any embedded newlines first.
#
$formfields[usr_key] = ereg_replace("[\n]", "", $formfields[usr_key]);
if (! ereg("^[0-9a-zA-Z\@\. ]*$", $formfields[usr_key])) {
$errors["PubKey"] = "Invalid characters";
SPITFORM($formfields, $errors);
PAGEFOOTER();
return;
}
else {
$usr_key[] = $formfields[usr_key];
}
}
#
# If usr provided a file for the key, it overrides the paste in text.
# Must read and check it.
#
if (isset($usr_keyfile) &&
strcmp($usr_keyfile, "") &&
strcmp($usr_keyfile, "none")) {
if (! ($fp = fopen($usr_keyfile, "r"))) {
TBERROR("Could not open $usr_keyfile", 1);
}
while (!feof($fp)) {
$buffer = fgets($fp, 4096);
if (ereg("^[\n\#]", $buffer))
continue;
if (! ereg("^[0-9a-zA-Z\@\.[:space:]\r\n]*$", $buffer)) {
$errors["PubKey File Contents"] = "Invalid characters";
fclose($fp);
SPITFORM($formfields, $errors);
PAGEFOOTER();
return;
}
$usr_key[] = Chop($buffer);
}
fclose($fp);
}
#
# Insert each key. The comment field serves as the secondary key