From 3dac3cb8d75c55f702992faa2c3967b03799099c Mon Sep 17 00:00:00 2001 From: Leigh B Stoller <stoller@flux.utah.edu> Date: Mon, 30 Jan 2012 13:36:18 -0700 Subject: [PATCH] Changes to make it easier for ProtoGeni users! * When generating an encrypted SSL certificate, derive an SSH public key from the private key and store in the pubkeys table for the user. Note that SSH version 2 RSA keys are actually just openssl RSA keys, and that ssh-keygen can extract an ssh compatible public key from it. * Change getsslcert.php3 to return the ssh private and public key when give the "ssh" boolean argument. This is mostly for the benefit of Flack; we probably need a better UI for the user to get this stuff. * Remove the requirement that users must upload an SSH key to use protogeni, since we now create one for them when they create their encrypted SSL certificate. * Some cleanup; instead of looking at the comment field to determine what pubkeys are Emulab created (and should not be deleted), use new internal and nodelete flags. --- account/addpubkey.in | 46 ++++++++++++++++++++++++++++++++----------- account/mkusercert.in | 41 +++++++++++++++++++++++++++++++++++++- account/newuser.in | 7 +------ account/tbacct.in | 2 +- db/User.pm.in | 30 +++++++++++++++------------- www/dbdefs.php3.in | 5 ++++- www/deletepubkey.php3 | 22 +++++++++++++++++---- www/getsslcert.php3 | 46 +++++++++++++++++++++++++++++++++---------- www/joinproject.php3 | 12 +++++------ www/newproject.php3 | 12 +++++------ www/showpubkeys.php3 | 25 ++++++++++++++--------- 11 files changed, 176 insertions(+), 72 deletions(-) diff --git a/account/addpubkey.in b/account/addpubkey.in index f2a9057790..9cfd09bb23 100644 --- a/account/addpubkey.in +++ b/account/addpubkey.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -wT # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # use English; @@ -31,7 +31,7 @@ sub usage() print " -r Force a regenerate of initial key for user\n"; exit(-1); } -my $optlist = "dkniwfu:rX:s"; +my $optlist = "dkniwfu:rX:sRNC:S:"; my $iskey = 0; my $verify = 0; my $initmode = 0; @@ -39,6 +39,9 @@ my $force = 0; my $genmode = 0; my $nobody = 0; my $noemail = 0; +my $remove = 0; +my $nodelete = 0; +my $Comment; my $xmlfile; # @@ -147,9 +150,15 @@ if (defined($options{"n"})) { if (defined($options{"i"})) { $initmode = 1; } +if (defined($options{"N"})) { + $nodelete = 1; +} if (defined($options{"r"})) { $force = 1; } +if (defined($options{"R"})) { + $remove = 1; +} if (defined($options{"s"})) { $noemail = 1; } @@ -159,6 +168,9 @@ if (defined($options{"w"})) { if (defined($options{"u"})) { $user = $options{"u"}; } +if (defined($options{"C"})) { + $Comment = $options{"C"}; +} if (defined($options{"X"})) { $xmlfile = $options{"X"}; @@ -383,13 +395,21 @@ sub ParseKey($) { # Make up a comment field for the DB. # if (!defined($comment)) { - $comment = "$type-${user_email}"; + $comment = (defined($Comment) ? $Comment : "$type-${user_email}"); } $key = "$key $comment"; + my $safe_key = DBQuoteSpecial($key); + my $safe_comment = DBQuoteSpecial($comment); - DBQueryFatal("replace into user_pubkeys ". - "values ('$user_uid', '$user_dbid', ". - " 0, '$key', now(), '$comment')"); + if ($remove) { + DBQueryFatal("delete from user_pubkeys ". + "where uid_idx='$user_dbid' and comment=$safe_comment"); + } + DBQueryFatal("replace into user_pubkeys set ". + " uid='$user_uid', uid_idx='$user_dbid', ". + " internal='0', nodelete='$nodelete', ". + " idx=NULL, stamp=now(), ". + " pubkey=$safe_key, comment=$safe_comment"); # # Mark user record as modified so nodes are updated. @@ -469,9 +489,10 @@ sub InitUser() 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_uid', '$user_dbid', ". - " 0, '$1 $2', now(), '$2')"); + DBQueryFatal("replace into user_pubkeys set ". + " uid='$user_uid', uid_idx='$user_dbid', ". + " internal='1', nodelete='1', idx=NULL, stamp=now(), ". + " pubkey='$1 $2', comment='$2'"); } else { fatal("Bad protocol 1 public key: $ident\n"); @@ -512,9 +533,10 @@ sub InitUser() if ($ident =~ /^(ssh-rsa [-\w\.\@\+\/\=]*) ([-\w\@\.\ ]*)$/) { - DBQueryFatal("replace into user_pubkeys ". - "values ('$user_uid', '$user_dbid', ". - " 0, '$1 $2', now(), '$2')"); + DBQueryFatal("replace into user_pubkeys set ". + " uid='$user_uid', uid_idx='$user_dbid', ". + " internal='1', nodelete='1', idx=NULL, stamp=now(), ". + " pubkey='$1 $2', comment='$2'"); } else { fatal("Bad protocol 2 public key: $ident\n"); diff --git a/account/mkusercert.in b/account/mkusercert.in index 46e61a537a..e9dd39a2c3 100644 --- a/account/mkusercert.in +++ b/account/mkusercert.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -wT # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # use strict; @@ -53,6 +53,8 @@ my $CACONFIG = "$SSLDIR/ca.cnf"; my $EMULAB_CERT = "$TB/etc/emulab.pem"; my $EMULAB_KEY = "$TB/etc/emulab.key"; my $OPENSSL = "/usr/bin/openssl"; +my $KEYGEN = "/usr/bin/ssh-keygen"; +my $ADDKEY = "$TB/sbin/addpubkey"; my $WORKDIR = "$TB/ssl"; my $SAVEUID = $UID; @@ -471,6 +473,43 @@ if ($encrypted) { chown($user_number, $default_groupgid, "$target") or fatal("Could not chown $target: $!"); + + chmod(0600, $target) + or fatal("Could not chmod $target: $!"); + + # + # Create an SSH key from the private key. Mostly for geni users, + # who tend not to know how to do such things. + # + my $pemfile = "$ssldir/encrypted.pem"; + my $sshdir = "$USERDIR/$user_uid/.ssh"; + my $pphrase = User::escapeshellarg($password); + # This comment is special. It functions as a cross table reference + # between pubkeys and sslcerts. I might do this differently later. + my $comment = User::escapeshellarg("sslcert:${serial}"); + + # ssh-keygen whines and refuses to extract unless the mode is 600. + chmod(0600, $pemfile) + or fatal("Could not chmod $pemfile: $!"); + + system("$KEYGEN -P $pphrase -y -f $pemfile > $sshdir/encrypted.pub") == 0 + or fatal("Could not extract ssh pubkey from $pemfile"); + + # + # The key format is identical to openssh, so just copy it over. + # + system("/bin/cp usercert_key.pem $sshdir/encrypted.key") == 0 + or fatal("Could not copy private key to $sshdir/encrypted.key: $!"); + chmod(0600, "$sshdir/encrypted.key") + or fatal("Could not chmod $sshdir/encrypted.key: $!"); + + # + # And add the pubkey to the DB. Mark it as nodelete and that it should + # remove existing key with same comment. + # + $EUID = $UID; + system("$ADDKEY -s -N -R -C $comment -u $user_uid -f $sshdir/encrypted.pub") + == 0 or fatal("Could not add pubkey $sshdir/encrypted.pub"); } TBScriptUnlock(); diff --git a/account/newuser.in b/account/newuser.in index 485d6c3d5f..3e6d1f4391 100644 --- a/account/newuser.in +++ b/account/newuser.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # use English; @@ -342,11 +342,6 @@ if (exists($xmlparse->{'attribute'}->{'passphrase'}) && fatal("Checkpass failed with $?"); } $newuser_args{'initial_passphrase'} = $passphrase; - - if (! (exists($xmlparse->{'attribute'}->{'pubkey'}) || - exists($xmlparse->{'pubkeys'}))) { - UserError("Must provide SSH pubkey when requesting SSL Certificate") - } } # diff --git a/account/tbacct.in b/account/tbacct.in index 50c3274d71..b211b54b87 100644 --- a/account/tbacct.in +++ b/account/tbacct.in @@ -459,7 +459,7 @@ sub AddUser() if (defined($target_user->initial_passphrase())) { my $pphrase = User::escapeshellarg($target_user->initial_passphrase()); - system("$MKUSERCERT -p '$pphrase' $user"); + system("$MKUSERCERT -p $pphrase $user"); if ($?) { fatal("Could not create initial encrypted SSL certificate"); } diff --git a/db/User.pm.in b/db/User.pm.in index 7e1302f992..8bf70ead3a 100644 --- a/db/User.pm.in +++ b/db/User.pm.in @@ -1135,8 +1135,8 @@ sub SSLCert($$$;$) } # -# Get user ssh keys. This is bogus; I am making sure not to return the -# emulab key which is not encrypted. The keys table needs work. +# Get user ssh keys, but do not include the "internal" keys, which +# are the Emulab generated unencrypted keys. # sub GetSSHKeys($$) { @@ -1151,8 +1151,7 @@ sub GetSSHKeys($$) my $query_result = DBQueryWarn("select pubkey from user_pubkeys ". - "where uid_idx='$uid_idx' and ". - " comment not like '%${OURDOMAIN}'"); + "where uid_idx='$uid_idx' and internal=0"); return -1 if (!defined($query_result)); @@ -1179,8 +1178,7 @@ sub DeleteSSHKeys($) my $query_result = DBQueryWarn("delete from user_pubkeys ". - "where uid_idx='$uid_idx' and ". - " comment not like '%${OURDOMAIN}'"); + "where uid_idx='$uid_idx' and internal=0"); return -1 if (!defined($query_result)); @@ -1189,8 +1187,7 @@ sub DeleteSSHKeys($) } # -# Get (hopefully) unencrypted, locally-generated user ssh keys. This is -# bogus; I am making sure to only return locally-generated keys. +# Get (hopefully) unencrypted, locally-generated user ssh keys. # sub GetDefaultSSHKeys($$;$) { @@ -1210,8 +1207,7 @@ sub GetDefaultSSHKeys($$;$) my $query_result = DBQueryWarn("select pubkey from user_pubkeys ". - "where uid_idx='$uid_idx' and ". - " comment like '%\@${OURDOMAIN}' $extra"); + "where uid_idx='$uid_idx' and internal=1 $extra"); return -1 if (!defined($query_result)); @@ -1761,11 +1757,17 @@ sub HomeDir($) sub escapeshellarg($) { - my ($str) = @_; + my ($str) = @_; + my @chars = split('', $str); + my $result = ""; - $str =~ s/[^[:alnum:]]/\\$&/g; - $str =~ /^(.+)$/; # untaint the result - return $1; + foreach my $ch (@chars) { + if ($ch eq '\'') { + $result = $result . "\'\\\'"; + } + $result = $result . "$ch"; + } + return "'$result'"; } # diff --git a/www/dbdefs.php3.in b/www/dbdefs.php3.in index 72c9e2f1db..dd27208549 100644 --- a/www/dbdefs.php3.in +++ b/www/dbdefs.php3.in @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # # Database Constants @@ -33,6 +33,9 @@ $TBDB_MMLENGTH = 64; $TBDB_ARCHIVE_TAGLEN = 64; $TBDB_ARCHIVE_MSGLEN = 2048; +# Minimum length. +$TBDB_MINPASSPHRASE = 10; + # # Current policy is to prefix the EID with the PID. Make sure it is not # too long for the database. PID is 12, and the max is 32, so the user diff --git a/www/deletepubkey.php3 b/www/deletepubkey.php3 index fa13974471..a635e400f3 100644 --- a/www/deletepubkey.php3 +++ b/www/deletepubkey.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2006, 2007 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -47,9 +47,18 @@ if (! mysql_num_rows($query_result)) { USERERROR("Public 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"); +$row = mysql_fetch_array($query_result); +$pubkey = $row['pubkey']; +$chunky = chunk_split($pubkey, 70, "<br>\n"); +$internal = $row['internal']; +$nodelete = $row['nodelete']; + +# +# Internal keys cannot be deleted without admin. +# +if (($internal || $nodelete) && !$isadmin) { + USERERROR("You are not allowed to delete your system keys!", 1); +} # # We run this twice. The first time we are checking for a confirmation @@ -94,6 +103,11 @@ if (!isset($confirmed)) { <td>$chunky</td> </tr> </table>\n"; + + if ($internal || $nodelete) { + echo "<center><font color=red size=+1>"; + echo "This is an internal key!</font><center>"; + } PAGEFOOTER(); return; diff --git a/www/getsslcert.php3 b/www/getsslcert.php3 index ea8643e01d..fe88250658 100644 --- a/www/getsslcert.php3 +++ b/www/getsslcert.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2009 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -17,10 +17,14 @@ $isadmin = ISADMIN(); # Verify page arguments # $optargs = OptionalPageArguments("target_user", PAGEARG_USER, - "p12", PAGEARG_BOOLEAN); + "p12", PAGEARG_BOOLEAN, + "ssh", PAGEARG_BOOLEAN); if (!isset($p12)) { $p12 = 0; } +if (!isset($ssh)) { + $ssh = 0; +} # Default to current user if not provided. if (!isset($target_user)) { @@ -58,7 +62,7 @@ if ($p12) { } $query_result =& $target_user->TableLookUp("user_sslcerts", - "cert,privkey", + "cert,privkey,idx", "encrypted=1 and revoked is null"); if (!mysql_num_rows($query_result)) { @@ -69,12 +73,34 @@ $row = mysql_fetch_array($query_result); $cert = $row["cert"]; $key = $row["privkey"]; -header("Content-Type: text/plain"); -echo "-----BEGIN RSA PRIVATE KEY-----\n"; -echo $key; -echo "-----END RSA PRIVATE KEY-----\n"; -echo "-----BEGIN CERTIFICATE-----\n"; -echo $cert; -echo "-----END CERTIFICATE-----\n"; +if ($ssh) { + $serial = $row['idx']; + $comment = "sslcert:${serial}"; + $pubkey_result =& $target_user->TableLookUp("user_pubkeys", + "pubkey", + "comment='$comment'"); + if (!mysql_num_rows($query_result)) { + PAGEHEADER("Download SSL Certificate for $target_uid"); + USERERROR("There is no SSH pubkey for certificate!", 1); + } + $row = mysql_fetch_array($pubkey_result); + $pubkey = $row['pubkey']; + + header("Content-Type: text/plain"); + echo "-----BEGIN RSA PRIVATE KEY-----\n"; + echo $key; + echo "-----END RSA PRIVATE KEY-----\n"; + echo $pubkey; + echo "\n"; +} +else { + header("Content-Type: text/plain"); + echo "-----BEGIN RSA PRIVATE KEY-----\n"; + echo $key; + echo "-----END RSA PRIVATE KEY-----\n"; + echo "-----BEGIN CERTIFICATE-----\n"; + echo $cert; + echo "-----END CERTIFICATE-----\n"; +} ?> diff --git a/www/joinproject.php3 b/www/joinproject.php3 index 3eca95ce2b..0b5ab192fb 100644 --- a/www/joinproject.php3 +++ b/www/joinproject.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -738,6 +738,10 @@ if (! $returning) { elseif ($formfields["passphrase1"] != $formfields["passphrase2"]) { $errors["Confirm Pass Phrase"] = "Does not match Pass Phrase"; } + elseif (strlen($formfields["passphrase1"]) < $TBDB_MINPASSPHRASE) { + $errors["Pass Phrase"] = + "Too short; $TBDB_MINPASSPHRASE char minimum"; + } elseif (! CHECKPASSWORD(($USERSELECTUIDS ? $formfields["joining_uid"] : "ignored"), $formfields["passphrase1"], @@ -745,12 +749,6 @@ if (! $returning) { $formfields["usr_email"], $checkerror)) { $errors["Pass Phrase"] = "$checkerror"; } - if (! (isset($_FILES['usr_keyfile']) && - $_FILES['usr_keyfile']['name'] != "" && - $_FILES['usr_keyfile']['name'] != "none")) { - $errors["SSH Pub Key"] = - "You must provide an SSH pubkey to use Geni"; - } } } if (!$forwikionly) { diff --git a/www/newproject.php3 b/www/newproject.php3 index 8ea4360af3..aff553e406 100755 --- a/www/newproject.php3 +++ b/www/newproject.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2011 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -869,6 +869,10 @@ if (! $returning) { elseif ($formfields["passphrase1"] != $formfields["passphrase2"]) { $errors["Confirm Pass Phrase"] = "Does not match Pass Phrase"; } + elseif (strlen($formfields["passphrase1"]) < $TBDB_MINPASSPHRASE) { + $errors["Pass Phrase"] = + "Too short; $TBDB_MINPASSPHRASE char minimum"; + } elseif (! CHECKPASSWORD(($USERSELECTUIDS ? $formfields["proj_head_uid"] : "ignored"), $formfields["passphrase1"], @@ -876,12 +880,6 @@ if (! $returning) { $formfields["usr_email"], $checkerror)) { $errors["Pass Phrase"] = "$checkerror"; } - if (! (isset($_FILES['usr_keyfile']) && - $_FILES['usr_keyfile']['name'] != "" && - $_FILES['usr_keyfile']['name'] != "none")) { - $errors["SSH Pub Key"] = - "You must provide an SSH pubkey to use Geni"; - } } } diff --git a/www/showpubkeys.php3 b/www/showpubkeys.php3 index c1e1e22880..fe624bcc3a 100644 --- a/www/showpubkeys.php3 +++ b/www/showpubkeys.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2007 University of Utah and the Flux Group. +# Copyright (c) 2000-2012 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -75,21 +75,28 @@ function SPITFORM($formfields, $errors) $pubkey = $row['pubkey']; $date = $row['stamp']; $idx = $row['idx']; + $internal= $row['internal']; + $nodelete= $row['nodelete']; $fnote = ""; - if (strstr($comment, $BOSSNODE)) { + if ($internal || $nodelete) { $fnote = "[<b>1</b>]"; } $chunky = chunk_split("$pubkey $fnote", 75, "<br>\n"); $delurl = CreateURL("deletepubkey", $target_user, "key", $idx); - echo "<tr> - <td align=center> - <A href='$delurl'> - <img alt='Delete Key' src=redball.gif></A> - </td> - <td>$chunky</td> + echo "<tr>\n"; + if (($internal || $nodelete) && !$isadmin) { + echo "<td> </td>"; + } + else { + echo "<td align=center> + <A href='$delurl'> + <img alt='Delete Key' src=redball.gif></A> + </td>"; + } + echo " <td>$chunky</td> </tr>\n"; } echo "</table>\n"; @@ -101,7 +108,7 @@ function SPITFORM($formfields, $errors) } echo "<blockquote><blockquote><blockquote> <ol> - <li> Please do not delete your Emulab generated public key. + <li> Your Emulab generated public keys may not be deleted. </ol> </blockquote></blockquote></blockquote>\n"; -- GitLab