updatecert.in 5.73 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2008-2014 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# 
# {{{GENIPUBLIC-LICENSE
# 
# GENI Public License
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
# 
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
# 
# }}}
29 30 31 32
#
use strict;
use English;
use Getopt::Std;
33
use Cwd qw(realpath);
34 35

#
36
# Update a certificate using the existing private key.
37 38 39
# 
sub usage()
{
40
    print "Usage: updatecert [-d] [-o file | -w] [-k keyfile] <certfile.pem>\n";
41 42
    exit(1);
}
43 44 45 46 47
my $optlist   = "do:k:w";
my $debug     = 0;
my $overwrite = 0;
my $outfile;
my $keyfile;
48 49 50 51 52 53 54

#
# Configure variables
#
my $TB		  = "@prefix@";
my $TBOPS         = "@TBOPSEMAIL@";
my $OPENSSL       = "/usr/bin/openssl";
55 56 57 58 59
my $SSLDIR        = "$TB/lib/ssl";
my $CACONFIG      = "$SSLDIR/ca.cnf";
my $EMULAB_CERT   = "$TB/etc/emulab.pem";
my $EMULAB_KEY    = "$TB/etc/emulab.key";
my $WORKDIR       = "$TB/ssl";
60 61 62 63 64 65 66

# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/site/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

# Protos
sub fatal($);
67
sub UpdateCert($$);
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

#
# Turn off line buffering on output
#
$| = 1; 

# Load the Testbed support stuff.
use lib "@prefix@/lib";
use libtestbed;
use emutil qw(TBGetUniqueIndex);

if ($UID != 0) {
    fatal("Must be root to run this script\n");
}

#
# Check args.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug++;
}
93 94 95 96 97 98 99 100 101
if (defined($options{"o"})) {
    $outfile = $options{"o"};
}
elsif (defined($options{"w"})) {
    $overwrite = 1;
}
if (defined($options{"k"})) {
    $keyfile = $options{"k"};
}
102 103 104
usage()
    if (!@ARGV);
my $certfile = $ARGV[0];
105 106
$keyfile = $certfile
    if (!defined($keyfile));
107 108 109 110

fatal("No such file: $certfile")
    if (! -e $certfile);

111
exit(UpdateCert($certfile, $keyfile));
112 113 114 115

#
# Update a certificate using the installed CA.
#
116
sub UpdateCert($$)
117
{
118 119
    my ($certfile, $keyfile) = @_;

120
    $keyfile = realpath($keyfile);
121
    chomp($keyfile);
122
    $certfile = realpath($certfile);
123
    chomp($certfile);
124 125 126 127 128 129 130 131

    #
    # Make sure we can get find the private key in the file, and
    # save it for later.
    #
    my $privkey;
    my $string;
    
132 133
    open(CERT, $keyfile)
	or fatal("Could not open $keyfile");
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    while (<CERT>) {
	my $line = $_;
	if ($line =~ /^-----BEGIN RSA/) {
	    $string = $line;
	    next;
	}
	if ($line =~ /^-----END RSA/) {
	    $string  = $string .= $line;
	    $privkey = $string;
	    next;
	}
	$string .= $line
	    if (defined($string));
    }
    close(CERT);
    if (!defined($privkey)) {
150
	fatal("Could not find private key in $keyfile");
151
    }
152 153 154 155 156 157 158 159 160 161 162 163

    #
    # 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.).
    # 
    chdir("$WORKDIR") or
	fatal("Could not chdir to $WORKDIR: $!");

    TBScriptLock("mkusercert") == 0 or
	fatal("Could not get the lock!");

164 165 166
    # Update by changing serial.
    my $serial = TBGetUniqueIndex( "user_sslcerts" );

167 168 169 170 171 172 173 174
    #
    # Need an index file, which is the openssl version of the DB.
    #
    if (! -e "index.txt") {
	open(IND, ">index.txt")
	    or fatal("Could not create index.txt");
	close(IND);
    }
175 176 177 178 179 180
    
    #
    # Save the new certificate to a temporary file: OpenSSL will reuse the
    # plain text from the old certificate instead of the current version,
    # so we regenerate the whole thing to avoid confusion.
    #
181 182
    my $newcert = "/tmp/$$.pem";
    my $newreq  = "/tmp/$$.req";
183

184 185 186 187 188
    # Need a serial number file.
    open(SER, ">serial")
	or fatal("Could not open serial for writing");
    printf SER "%08x\n", $serial;
    close(SER);
189

190 191
    system("$OPENSSL x509 -x509toreq -in $certfile ".
	   "   -signkey $keyfile -out $newreq");
192 193 194 195 196
    if ($?) {
	fatal("Could not create new certificate request");
    }
    system("$OPENSSL ca -batch -policy policy_match -days 2000 ".
	   " -name CA_syscerts -config $CACONFIG -in $newreq ".
197
	   " -extensions typical_extensions -md sha1 ".
198
	   " -cert $EMULAB_CERT -keyfile $EMULAB_KEY -out $newcert");
199 200 201
    if ($?) {
	fatal("Could not create new certificate");
    }
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    # Put the private key back into the new file, if they were originally
    # in the same file.
    if ($certfile eq $keyfile) {
	open(CERT, ">>$newcert")
	    or fatal("Could not open $newcert for writing");
	print CERT $privkey;
	close(CERT);
    }
    if (defined($outfile) || $overwrite) {
	$outfile = $certfile
	    if ($overwrite);

	if ($overwrite) {
	    system("/bin/mv -f $outfile ${outfile}.bak");
	    fatal("Could not backup $outfile to ${outfile}.bak")
		if ($?);
	}
	system("/bin/mv -f $newcert $outfile");
	fatal("Could not rename $newcert to $outfile")
	    if ($?);
	
	print "New certificate written to $outfile\n";
    }
    else {
	system("/bin/cat $newcert");
	unlink($newcert);
    }
230
    TBScriptUnlock();
231 232 233 234 235 236 237 238 239 240 241

    return 0;
}

sub fatal($)
{
    my ($msg) = @_;

    die("*** $0:\n".
	"    $msg\n");
}