initcerts.in 11.4 KB
Newer Older
1
2
#!/usr/bin/perl -w
#
Leigh B Stoller's avatar
Leigh B Stoller committed
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#
use strict;
use English;
use Getopt::Std;

#
# Initialize an emulab to act as a protogeni emulab. Add optional -c
# option if this is a clearinghouse.
# 
sub usage()
{
    print "Usage: initcerts [-r [-k]]\n";
    print "Options:\n";
    print "  -r    - Regenerate certs, replacing existing keys/certs\n";
    print "  -k    - Reuse private keys when using -r option\n";
    exit(1);
}
my $optlist = "rk";
my $regen   = 0;
my $oldkeys = 0;
my $asch    = @PROTOGENI_ISCLEARINGHOUSE@;
my $cflag   = ($asch ? "-c" : "");

#
# Configure variables
#
my $TB		  = "@prefix@";
my $TBOPS         = "@TBOPSEMAIL@";
my $OURDOMAIN     = "@OURDOMAIN@";
my $PGENIDOMAIN   = "@PROTOGENI_DOMAIN@";
my $PGENISUPPORT  = @PROTOGENI_SUPPORT@;
my $PROTOGENI_RPCNAME = "@PROTOGENI_RPCNAME@";
my $PROTOGENI_RPCPORT = "@PROTOGENI_RPCPORT@";
my $PROTOGENI_WEBSITE = "@PROTOGENI_WEBSITE@";
my $PROTOGENI_URL = "@PROTOGENI_URL@";
my $PROTOUSER	  = "elabman";
my $ADDAUTHORITY  = "$TB/sbin/protogeni/addauthority";
my $GETCACERTS    = "$TB/sbin/protogeni/getcacerts";
my $POSTCRL       = "$TB/sbin/protogeni/postcrl";
my $GENCRL        = "$TB/sbin/protogeni/gencrl";
my $GENCRLBUNDLE  = "$TB/sbin/protogeni/gencrlbundle";
my $MKSYSCERT	  = "$TB/sbin/mksyscert";
my $WAP           = "$TB/sbin/withadminprivs";
my $CACERT	  = "$TB/etc/emulab.pem";
my $SACERT	  = "$TB/etc/genisa.pem";
my $CMCERT	  = "$TB/etc/genicm.pem";
my $CHCERT	  = "$TB/etc/genich.pem";
my $SESCERT	  = "$TB/etc/genises.pem";
my $RPCCERT	  = "$TB/etc/genirpc.pem";
my $SUDO	  = "/usr/local/bin/sudo";
my $FETCH	  = "/usr/bin/fetch";
my $OPENSSL       = "/usr/bin/openssl";

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

# Protos
sub fatal($);
sub UpdateCert($$$$);
sub BackUpFileFatal($);

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

# Load the Testbed support stuff.
use lib "@prefix@/lib";
use libtestbed;
use libdb qw(TBSetSiteVar TBOPSPID DBQueryFatal);
use Genixmlrpc;
use GeniRegistry;

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

#
# Check args.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"r"})) {
    $regen = 1;
}
if (defined($options{"k"})) {
    $oldkeys = 1;
}
usage()
    if (@ARGV);

#
# Have you sent in your certificate to the ClearingHouse?
#
if (!$asch && ! (-e "$TB/etc/.protogeni_federated" || -e "$TB/etc/.federated")){
    print "Sending your public certificate to the Clearing House.\n";
    print "Please be patient!\n";
    
    #
    # Send in the root CA certificate via curl. The clearinghouse will
    # enter a temporary entry so that site initialization can proceed.
    #
    system("curl -f --data-urlencode cert\@${CACERT} ".
	   "     https://$PROTOGENI_WEBSITE/protogeni/register_ca.php");

    fatal("Could not register CA certificate at $PROTOGENI_WEBSITE")
	if ($?);

    system("echo 'Do not remove this file' > $TB/etc/.protogeni_federated");

    # Delay a few second to give the server a chance to do its thing
    # and restart apache.
    print "Your public certificate has been provisionally accepted.\n";
    print "Waiting a bit for the Clearing House to finish swallowing.\n";
    print "Please be patient!\n";
147
    for (my $i = 0; $i < 30; $i++) {
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
	sleep(1);
	print ".";
    }
    print "\n";
    print "Continuing ...\n";
}

#
# Generate the certs we need.
#
if (! -e $CMCERT || $regen) {
    my $keyopt = ($oldkeys && -e $CMCERT ? "-k $CMCERT" : "");
    BackUpFileFatal($CMCERT)
	if (-e $CMCERT);
    
    print "Creating CM certificate ...\n";
    system("$SUDO -u $PROTOUSER $MKSYSCERT -o $CMCERT $keyopt ".
	   "  -u $PROTOGENI_URL/cm " .
	   "  -i urn:publicid:IDN+${OURDOMAIN}+authority+cm " .
	   "$PGENIDOMAIN.cm") == 0
	   or fatal("Could not generate $CMCERT");
}
if (! -e $SACERT || $regen) {
    my $keyopt = ($oldkeys && -e $SACERT ? "-k $SACERT" : "");
    BackUpFileFatal($SACERT)
	if (-e $SACERT);

    print "Creating SA certificate ...\n";
    system("$SUDO -u $PROTOUSER $MKSYSCERT -o $SACERT $keyopt ".
	   "  -u $PROTOGENI_URL/sa " .
	   "  -i urn:publicid:IDN+${OURDOMAIN}+authority+sa " .
	   "$PGENIDOMAIN.sa") == 0
	   or fatal("Could not generate $SACERT");
}
if (! -e $SESCERT || $regen) {
    my $keyopt = ($oldkeys && -e $SESCERT ? "-k $SESCERT" : "");
    BackUpFileFatal($SESCERT)
	if (-e $SESCERT);

    print "Creating SES certificate ...\n";
    system("$SUDO -u $PROTOUSER $MKSYSCERT -o $SESCERT $keyopt ".
	   "  -u $PROTOGENI_URL/ses " .
	   "  -i urn:publicid:IDN+${OURDOMAIN}+authority+ses " .
	   "$PGENIDOMAIN.ses") == 0
	   or fatal("Could not generate $SESCERT");
}
if (! -e $RPCCERT || $regen) {
    my $keyopt = ($oldkeys && -e $RPCCERT ? "-k $RPCCERT" : "");
    BackUpFileFatal($RPCCERT)
	if (-e $RPCCERT);

    print "Creating RPC server certificate ...\n";
    system("$SUDO -u $PROTOUSER $MKSYSCERT -o $RPCCERT $keyopt ".
	   "'ProtoGENI RPC Server' $PROTOGENI_RPCNAME") == 0
	   or fatal("Could not generate $SESCERT");
}
if ($asch) {
    if (! -e $CHCERT || $regen) {
	my $keyopt = ($oldkeys && -e $CHCERT ? "-k $CHCERT" : "");
	BackUpFileFatal($CHCERT)
	    if (-e $CHCERT);

	print "Creating CH certificate ...\n";
	system("$SUDO -u $PROTOUSER $MKSYSCERT -o $CHCERT $keyopt ".
	       "  -u $PROTOGENI_URL/ch " .
	       "  -i urn:publicid:IDN+${OURDOMAIN}+authority+ch " .
	       "$PGENIDOMAIN.ch") == 0
	       or fatal("Could not generate $CHCERT");
    }
    UpdateCert( $CHCERT, "$PROTOGENI_URL/ch",
		"urn:publicid:IDN+${OURDOMAIN}+authority+ch",
		"$PGENIDOMAIN.ch" );
    #
    # Copy the CH certificate out to the web interface, but only the public
    # key of course. 
    #
    my $chcertificate = GeniCertificate->LoadFromFile($CHCERT);
    fatal("Could not load certificate from $CHCERT")
	if (!defined($chcertificate));
    
    my $certfile = $chcertificate->WriteToFile();
    if (system("$SUDO /bin/mv $certfile $TB/www/genich.pem")) {
	$chcertificate->Delete();
	unlink($certfile);
	fatal("Could not mv $certfile to $TB/www/genich.pem");
    }
    chmod(0644, "$TB/www/genich.pem");

    #
    # Add the cert to the DB directly.
    #
    system("$ADDAUTHORITY -f -c $CHCERT ma") == 0
	or fatal("Could not add MA certificate");
}
else {
    #
    # Grab the CH certificate.
    #
    print "Fetching clearinghouse certificate from $PROTOGENI_WEBSITE ...\n";
    system("$FETCH -q -o $CHCERT http://$PROTOGENI_WEBSITE/genich.pem") == 0
	or fatal("Could not fetch clearinghouse certificate ".
		 "from $PROTOGENI_WEBSITE");
}

#
# Update obsolete (pre-URN) certificates.
#
UpdateCert( $CMCERT, "$PROTOGENI_URL/cm",
	    "urn:publicid:IDN+${OURDOMAIN}+authority+cm",
	    "$PGENIDOMAIN.cm" );
UpdateCert( $SACERT, "$PROTOGENI_URL/sa",
	    "urn:publicid:IDN+${OURDOMAIN}+authority+sa",
	    "$PGENIDOMAIN.sa" );
UpdateCert( $SESCERT, "$PROTOGENI_URL/ses",
	    "urn:publicid:IDN+${OURDOMAIN}+authority+ses",
	    "$PGENIDOMAIN.ses" );

#
# Load the SA cert to act as caller context.
#
my $certificate = GeniCertificate->LoadFromFile($SACERT);
if (!defined($certificate)) {
    fatal("Could not load certificate from $SACERT\n");
}
my $context = Genixmlrpc->Context($certificate);
if (!defined($context)) {
    fatal("Could not create context to talk to clearinghouse");
}
my $cmcert = GeniCertificate->LoadFromFile($CMCERT);
if (!defined($cmcert)) {
    fatal("Could not load certificate from $CMCERT\n");
}
my $sescert = GeniCertificate->LoadFromFile($SESCERT);
if (!defined($sescert)) {
    fatal("Could not load certificate from $SESCERT\n");
}

#
# Add certs to the local SA database.
#
system("$ADDAUTHORITY $SACERT sa") == 0
    or fatal("Could not add SA certificate");
system("$ADDAUTHORITY $CMCERT cm") == 0
    or fatal("Could not add CM certificate");
system("$ADDAUTHORITY $SESCERT ses") == 0
    or fatal("Could not add SES certificate");

#
# Add certs to the local CM database.
#
system("$ADDAUTHORITY -a $SACERT sa") == 0
    or fatal("Could not add SA certificate to CM DB");
system("$ADDAUTHORITY -a $CMCERT cm") == 0
    or fatal("Could not add CM certificate to CM DB");

#
# Register our certs at the clearinghouse or locally.
#
if ($asch) {
    system("$ADDAUTHORITY -c $SACERT sa") == 0
	or fatal("Could not add SA certificate");
    system("$ADDAUTHORITY -c $CMCERT cm") == 0
	or fatal("Could not add CM certificate");
    system("$ADDAUTHORITY -c $SESCERT ses") == 0
	or fatal("Could not add SES certificate");
}
Leigh B Stoller's avatar
Leigh B Stoller committed
314
315
system("$TB/sbin/openvpn-setup") == 0
    or fatal("Could not setup openvpn certificates");
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

#
# Local SiteVars to hold the UUIDs.
#
TBSetSiteVar('protogeni/sa_uuid', $certificate->uuid());
TBSetSiteVar('protogeni/cm_uuid', $cmcert->uuid());
TBSetSiteVar('protogeni/ses_uuid', $sescert->uuid());

exit(0);

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

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

# Add a URN to old certificates.  (This is horrible, mainly because
# we want to reuse the same private keys.)
sub UpdateCert($$$$)
{
    my ($cert, $url, $urn, $hrn) = @_;

    if( system( "$OPENSSL x509 -text -noout < $cert | " .
		"grep -q -i URI:urn:publicid:IDN" ) ) {
	my $extfile = "/tmp/$$.ext"; # not worth trying to be secure
	my $keyfile = "/tmp/$$.key";
	my $uuid = qx{$OPENSSL x509 -subject -noout < $cert};

	die "could not read subject from $cert"
	    unless defined( $uuid );
	die "no UUID found in subject"
	    unless $uuid =~ /CN=([-a-f0-9]+)/;
	$uuid = $1;

	open( OLDFILE, "< $cert" ) or die "can't open $cert";
	open( NEWFILE, "> $keyfile" ) or die "can't open $keyfile";
	while( <OLDFILE> ) {
	    print NEWFILE;
	    last if /-----END RSA PRIVATE KEY-----/;
	}
	close OLDFILE;
	close NEWFILE;

	print "Adding URN to $cert...\n";

	my $originalfile = "${cert}.orig";
	-f $originalfile and
	    die( "refusing to overwrite $originalfile" );
	rename( "$cert", "$originalfile" ) or
	    die( "could not rename $cert" );

	system("$SUDO -u $PROTOUSER $MKSYSCERT -o $cert ".
	       "  -u $url -i $urn -k $keyfile $hrn $uuid" ) == 0
	       or fatal("Could not generate $cert");
    }
}

#
# Backup a file or fail.
#
sub BackUpFileFatal($)
{
    my ($filename) = @_;
    my $suffix     = time();
    my $backup     = $filename . "-" . $suffix;

    fatal("$filename does not exist")
	if (! -e $filename);

    if (-e $backup) {
	sleep(1);
	$backup = $filename . "-" . time();
	fatal("$backup already exists")
	    if (-e $backup);
    }
    system("/bin/cp -p $filename $backup") == 0
	or fatal("Could not backup $filename to $backup");

    return 0;
}