diff --git a/account/mkusercert.in b/account/mkusercert.in index 3755dad62bdac1c7d262fc54616dce1505228c40..6a61d23ec4a35228a9f7ada46adae5f58115ba14 100644 --- a/account/mkusercert.in +++ b/account/mkusercert.in @@ -1,7 +1,7 @@ #!/usr/bin/perl -wT # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2008 University of Utah and the Flux Group. +# Copyright (c) 2000-2011 University of Utah and the Flux Group. # All rights reserved. # use strict; @@ -22,14 +22,15 @@ use User; # sub usage() { - print("Usage: mkusercert [-d] [-o] [-g] [-p password] <user>\n"); + print("Usage: mkusercert [-d] [-o] [-r] [-g] [-p password] <user>\n"); exit(-1); } -my $optlist = "dp:og"; +my $optlist = "dp:ogr"; my $debug = 0; my $output = 0; my $password = ""; my $geniflag = 0; +my $reusekey = 0; # # Configure variables @@ -93,6 +94,7 @@ $| = 1; # Function prototypes # sub fatal($); +sub UserFatal($); # # Rewrite audit version of ARGV to prevent password in mail logs. @@ -116,6 +118,9 @@ if (! getopts($optlist, \%options)) { if (defined($options{"d"})) { $debug = 1; } +if (defined($options{"r"})) { + $reusekey = 1; +} if (defined($options{"g"})) { $geniflag = 1; } @@ -144,6 +149,9 @@ if (@ARGV != 1) { if ($geniflag && !$encrypted) { fatal("GENI certs must be encrypted (use -p password)."); } +if ($reusekey && !$encrypted) { + fatal("Cannot reuse the key for an unencrypted cert (use -p password)."); +} my $user = $ARGV[0]; # @@ -168,16 +176,6 @@ if (! defined($this_user)) { fatal("You ($UID) do not exist!"); } -# -# This script is always audited. Mail is sent automatically upon exit. -# -if (AuditStart(0)) { - # - # Parent exits normally - # - exit(0); -} - # # CD to the workdir, and then serialize on the lock file since there is # some shared goop that the ssl tools muck with (serial number, index, etc.). @@ -188,6 +186,54 @@ chdir("$WORKDIR") or TBScriptLock("mkusercert") == 0 or fatal("Could not get the lock!"); +# +# Create a client side cert. Reuse the original key if are told to, +# and it actually exists, and the password is valid. +# +# Do this before the AuditStart() so that user error goes back to web. +# +my $reqargs = ""; + +if ($reusekey) { + my $privkey; + my $cert; + if ($target_user->SSLCert(1, \$cert, \$privkey)) { + $reusekey = 0; + goto newkey; + } + + open(KEYF, "> usercert_key.pem") or + fatal("Could not create file to store existing private key"); + print KEYF "-----BEGIN RSA PRIVATE KEY-----\n"; + print KEYF $privkey; + print KEYF "-----END RSA PRIVATE KEY-----\n"; + close(KEYF); + + # + # Make sure the user provided the proper passphrase. + # + system("$OPENSSL rsa -check -in usercert_key.pem ". + " -passin 'pass:${sh_password}' -noout >/dev/null 2>&1") == 0 + or UserFatal("Cannot decrypt private key. Correct pass phrase?"); + + $reqargs = "-key usercert_key.pem -passin 'pass:${sh_password}' "; +} +else { + newkey: + $reqargs = "-keyout usercert_key.pem"; + $reqargs .= ($encrypted ? " -passout 'pass:${sh_password}' " : " -nodes ") +} + +# +# This script is always audited. Mail is sent automatically upon exit. +# +if (AuditStart(0)) { + # + # Parent exits normally + # + exit(0); +} + # # Get the user info (the user being operated on). # @@ -267,8 +313,7 @@ close(TEMP) # Create a client side private key and certificate request. # system("$OPENSSL req -new -config usercert.cnf ". - ($encrypted ? " -passout 'pass:${sh_password}' " : " -nodes ") . - " -keyout usercert_key.pem -out usercert_req.pem") == 0 + "$reqargs -out usercert_req.pem") == 0 or fatal("Could not create certificate request"); # @@ -421,3 +466,11 @@ sub fatal($) { die("*** $0:\n". " $mesg\n"); } +sub UserFatal($) { + my($mesg) = $_[0]; + + TBScriptUnlock(); + print STDERR $mesg; + # Tell web interface to tell user. + exit(1); +} diff --git a/www/gensslcert.php3 b/www/gensslcert.php3 index 9cc16cf70c956f70f42c060ca068b24bb0c170ad..048982e6a3305b4c2312d4445178a1511d1c99b7 100644 --- a/www/gensslcert.php3 +++ b/www/gensslcert.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2009 University of Utah and the Flux Group. +# Copyright (c) 2000-2011 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -128,12 +128,29 @@ function SPITFORM($target_user, $formfields, $errors) size=24></td> </tr>\n"; + if (1) { + echo "<tr> + <td>Reuse Private Key?[<b>3</b>]:</td> + <td class=left> + <input type=checkbox + name=\"formfields[reusekey]\" + value=Yep"; + + if (isset($formfields["reusekey"]) && + strcmp($formfields["reusekey"], "Yep") == 0) + echo " checked"; + + echo " > Yes + </td> + </tr>\n"; + } + # # Verify with password. # if (!$isadmin) { echo "<tr> - <td>Emulab Password[<b>3</b>]:</td> + <td>Emulab Password[<b>4</b>]:</td> <td class=left> <input type=password name=\"formfields[password]\" @@ -157,7 +174,9 @@ function SPITFORM($target_user, $formfields, $errors) <li> You must supply a passphrase to use when encrypting the private key for your SSL certificate. You will be prompted for this passphrase whenever you attempt to use it. Pick - a good one!"; + a good one! + <li> Reuse your existing private key unless you think it has been + compromised. Must provide correct passphrase for your key."; if (!$isadmin) { echo "<li> As a security precaution, you must supply your Emulab user password when creating new ssl certificates. "; @@ -171,6 +190,7 @@ function SPITFORM($target_user, $formfields, $errors) # if (! isset($_POST['submit'])) { $defaults = array(); + $defaults["reusekey"] = "Yep"; SPITFORM($target_user, $defaults, 0); PAGEFOOTER(); @@ -237,15 +257,43 @@ if (count($errors)) { return; } +$reusekey = ""; +if (isset($formfields["reusekey"]) && + strcmp($formfields["reusekey"], "Yep") == 0) { + $reusekey = "-r"; +} + # # Insert key, update authkeys files and nodes if appropriate. # STARTBUSY("Generating Certificate"); -SUEXEC($target_uid, "nobody", - "webmkusercert -p " . - escapeshellarg($formfields["passphrase1"]) . " $target_uid", - SUEXEC_ACTION_DIE); -STOPBUSY(); +$retval = SUEXEC($target_uid, "nobody", + "webmkusercert $reusekey -p " . + escapeshellarg($formfields["passphrase1"]) . " $target_uid", + SUEXEC_ACTION_IGNORE); +HIDEBUSY(); + +# +# Fatal Error. Report to tbops. +# +if ($retval < 0) { + SUEXECERROR(SUEXEC_ACTION_DIE); + # + # Never returns ... + # + die(""); +} + +# +# User Error. Report to user. +# +if ($retval > 0) { + $errors["PassPhrase"] = $suexec_output; + + SPITFORM($target_user, $formfields, $errors); + PAGEFOOTER(); + return; +} # # Redirect back, avoiding a POST in the history.