1: #!/usr/bin/perl
2: use strict;
3: use Sys::Hostname::FQDN();
4: use Term::ReadKey;
5: use Locale::Country;
6: use Crypt::OpenSSL::X509;
7: use DateTime::Format::x509;
8: use File::Slurp;
9: use Cwd;
10:
11: #
12: # Expected structure
13: #
14: # /lonca
15: # opensslca.cnf
16: # cacert.pem
17: # index.txt
18: # /certs
19: # /crl
20: # /private
21: # /requests
22: #
23:
24: print(<<END);
25:
26: ****** SSL Certificate Authority *****
27:
28: If you are running your own cluster of LON-CAPA nodes you will need to
29: create a Certificate Authority (CA) for your cluster. You will then use
30: the CA to sign LON-CAPA SSL certificate signing requests generated by
31: the nodes in your cluster.
32:
33: LON-CAPA SSL Certificates can be used in two different contexts:
34: (a) if you configure LON-CAPA to use a secure channel for exchange of
35: the shared encryption key when establishing an "internal" LON-CAPA
36: connection between nodes in your cluster, and (b) if you configure
37: LON-CAPA to use client SSL certificate validation when one node replicates
38: content from library node(s) in your cluster.
39:
40: Although a LON-CAPA cluster may contain multiple domains and/or multiple
41: library nodes, there will only be one LON-CAPA certificate authority (CA)
42: for the cluster. The CA certificate signing infrastructure need not be
43: housed on a LON-CAPA node; it can instead be installed on a separate
44: Linux instance. The instance housing the CA infrastructure needs to
45: have the following Linux packages installed:
46:
47: openssl
48: perl
49:
50: and the following perl modules from CPAN installed:
51:
52: Term::ReadKey
53: Sys::Hostname::FQDN
54: Locale::Country
55: Crypt::OpenSSL::X509
56: DateTime::Format::x509
57: File::Slurp
58:
59: You need to decide on a directory you wish to use to hold the
60: CA infrastructure. If necessary you should create a new directory.
61: Then move this script (CrCA.pl) to that directory, and run it with
62: the command: perl CrCA.pl
63:
64: The script will create any required subdirectories (and files)
65: within that directory, if they do not already exist.
66:
67: You will need to provide a password to be used for the openssl CA key
68: which will be stored in the /private subdirectory, and will be used
69: when signing certificate signing requests to create LON-CAPA certificates
70: for use in the cluster.
71:
72: END
73:
74: #Proceed?
75:
76: my ($dir,$hostname,%data);
77:
78: # Check if required subdirectories exist in current directory.
79: $dir = Cwd::getcwd();
80:
81: if (-e "$dir/lonca") {
82: if ((!-d "$dir/lonca") && (-f "$dir/lonca")) {
83: print "A lonca directory is required, but there is an existing file of that name.\n".
84: "Please either delete the lonca file, or change to a different directory, and ".
85: "create the CA infrastructure there.\n";
86: exit;
87: }
88: } else {
89: mkdir("$dir/lonca",0700);
90: system('chown root:root '."$dir/lonca");
91: }
92: if (-d "$dir/lonca") {
93: foreach my $subdir ('certs','crl','private','requests') {
94: if (!-d "$dir/lonca/$subdir") {
95: if (-f "$dir/lonca/$subdir") {
96: print "A $subdir sub-directory is required, but there is an existing file of that name.\n".
97: "Please either delete or move the $subdir file, then run this script again.\n";
98: exit;
99: } else {
100: mkdir("$dir/lonca/$subdir",0700);
101: system('chown root:root '."$dir/lonca/$subdir");
102: }
103: }
104: }
105: } else {
106: print "A lonca directory is required, but no directory exists\n";
107: exit;
108: }
109: if (-e "$dir/lonca/opensslca.conf") {
110: # retrieve existing config file and verify that if contains the required fields.
111: %data = &parse_config("$dir/lonca/opensslca.conf");
112: my %update = &confirm_config(%data);
113: my %changes;
114: foreach my $field ('clustername','organization','email','country','state','city','days','crldays') {
115: if ($data{$field} ne $update{$field}) {
116: $changes{$field} = $update{$field};
117: }
118: }
119: if (keys(%changes)) {
120: &save_config_changes("$dir/lonca/opensslca.conf",\%changes);
121: }
122: } else {
123: print(<<END);
124: ****** Certificate Authority Configuration File *****
125:
126: A configuration file: $dir/lonca/opensslca.conf will be created.
127:
128: The following information will be included:
129: Country, State/Province, City, Cluster Name, Organizational Name, E-mail address, CA certificate lifetime (days), Default certificate lifetime (days), CRL re-creation interval (days)
130:
131: END
132: $hostname = Sys::Hostname::FQDN::fqdn();
133: if ($hostname eq '') {
134: $hostname =&get_hostname();
135: } else {
136: print "Hostname detected: $hostname. Is that correct? [Y/n]";
137: if (!&get_user_selection(1)) {
138: $hostname =&get_hostname();
139: }
140: }
141:
142: my %fieldname = (
143: city => 'City',
144: state => 'State or Province',
145: clustername => 'Cluster name',
146: organization => 'Organization name',
147: );
148: my ($clustername,$organization,$country,$state,$city,$email,$cadays,$clusterhostname,$days,$crldays);
149: $clusterhostname = $hostname;
150: $country = &get_country($hostname);
151: print "Enter state or province name\n";
152: $state = &get_info($fieldname{'state'});
153: print "Enter city name\n";
154: $city = &get_info($fieldname{'city'});
155: $email = &get_camail();
156: print 'Enter a name for this LON-CAPA cluster, e.g., "Lon-CAPA learning network"'."\n".
157: 'This name will be included as the Common Name for the CA certificate.'."\n";
158: $clustername = &get_info($fieldname{'clustername'});
159: print 'Enter the organization name for this LON-CAPA cluster, e.g., "Lon CAPA certification authority"'."\n".
160: 'This name will be included as the Oraganization for the CA certificate.'."\n";
161: $organization = &get_info($fieldname{'organization'});
162: print "Enter the lifetime (in days) for the CA root certificate distributed to all nodes, e.g., 3650\n";
163: $cadays = &get_days();
164: print "Enter the default lifetime (in days) for each certificate created/signed by the CA for individual nodes, e.g., 3650\n";
165: $days = &get_days();
166: print "Enter the re-creation interval (in days) for the CA's certificate revocation list (CRL), e.g., 180\n";
167: $crldays = &get_days();
168:
169: if (open(my $fh,'>',"$dir/lonca/opensslca.conf")) {
170: print $fh <<"END";
171: [ ca ]
172: default_ca = loncapa
173:
174: [ loncapa ]
175: dir = $dir/lonca
176: certificate = $dir/lonca/cacert.pem
177: database = $dir/lonca/index.txt
178: new_certs_dir = $dir/lonca/certs
179: private_key = $dir/lonca/private/cakey.pem
180: serial = $dir/lonca/serial
181:
182: default_crl_days = $crldays
183: default_days = $days
184: default_md = sha256
185:
186: policy = loncapa_policy
187: x509_extensions = certificate_extensions
188:
189: [ loncapa_policy ]
190:
191: commonName = supplied
192: stateOrProvinceName = supplied
193: countryName = supplied
194: emailAddress = supplied
195: organizationName = supplied
196: organizationalUnitName = optional
197:
198: [ certificate_extensions ]
199:
200: basicConstraints = CA:false
201: crlDistributionPoints = URI:http://$clusterhostname/adm/dns/loncapaCAcrl
202:
203: [ req ]
204:
205: default_bits = 2048
206: distinguished_name = loncapa_ca
207:
208: x509_extensions = loncapa_ca_extensions
209:
210: [ loncapa_ca ]
211:
212: commonName = $clustername
213: localityName = $city
214: stateOrProvinceName = $state
215: countryName = $country
216: emailAddress = $email
217: organizationName = $organization
218:
219: [ loncapa_ca_extensions ]
220: basicConstraints = CA:true
221:
222: [ crl_ext ]
223:
224: authorityKeyIdentifier=keyid:always,issuer:always
225:
226:
227: END
228:
229: } else {
230: print 'Error: failed to wtite to '."$dir/lonca/opensslca.conf. Exiting.\n";
231: exit;
232: }
233: %data = &parse_config("$dir/lonca/opensslca.conf");
234: my %update = &confirm_config(%data);
235: my %changes;
236: foreach my $field ('clustername','organization','email','country','state','city','days','crldays') {
237: if ($data{$field} ne $update{$field}) {
238: $changes{$field} = $update{$field};
239: }
240: }
241: if (keys(%changes)) {
242: &save_config_changes("$dir/lonca/opensslca.conf",\%changes);
243: }
244: }
245:
246: my $sslkeypass;
247: if (-e "$dir/lonca/private/cakey.pem") {
248: my ($keyok,$try);
249: print "CA key aleady exists\n";
250: $try = 1;
251: while (!$keyok && $try) {
252: $sslkeypass = &get_password('Enter the password for the CA key');
253: if ($sslkeypass ne '') {
254: open(PIPE,"openssl rsa -noout -in lonca/private/cakey.pem -passin pass:$sslkeypass -check |");
255: my $check = <PIPE>;
256: close(PIPE);
257: chomp($check);
258: if ($check eq 'RSA key ok') {
259: $keyok = 1;
260: last;
261: } else {
262: print "CA key check failed. Try again? [Y/n]";
263: if (!&get_user_selection(1)) {
264: $try = 0;
265: }
266: }
267: }
268: }
269: unless ($keyok) {
270: print "CA key check failed. Create a new key? [Y/n]";
271: if (&get_user_selection(1)) {
272: $sslkeypass = &get_new_sslkeypass();
273: # generate SSL key
274: unless (&make_key("$dir/lonca/private",$sslkeypass)) {
275: print "Failed to create CA key\n";
276: exit;
277: }
278: } else {
279: exit;
280: }
281: }
282: } else {
283: $sslkeypass = &get_new_sslkeypass();
284: # generate SSL key
285: unless (&make_key("$dir/lonca/private",$sslkeypass)) {
286: print "Failed to create CA key\n";
287: exit;
288: }
289: }
290: if (-e "$dir/lonca/cacert.pem") {
291: print "A CA certificate exists\n";
292: open(PIPE,"openssl pkey -in $dir/lonca/private/cakey.pem -passin pass:$sslkeypass -pubout -outform der | sha256sum |");
293: my $hashfromkey = <PIPE>;
294: close(PIPE);
295: chomp($hashfromkey);
296: open(PIPE,"openssl x509 -in $dir/lonca/cacert.pem -pubkey | openssl pkey -pubin -pubout -outform der | sha256sum |");
297: my $hashfromcert = <PIPE>;
298: close(PIPE);
299: chomp($hashfromcert);
300: if ($hashfromkey eq $hashfromcert) {
301: my ($now,$starttime,$endtime,$status,%cert);
302: my $x509 = Crypt::OpenSSL::X509->new_from_file("$dir/lonca/cacert.pem");
303: my @items = split(/,\s+/,$x509->subject());
304: foreach my $item (@items) {
305: my ($name,$value) = split(/=/,$item);
306: if ($name eq 'CN') {
307: $cert{'cn'} = $value;
308: }
309: }
310: $cert{'start'} = $x509->notBefore();
311: $cert{'end'} = $x509->notAfter();
312: $cert{'alg'} = $x509->sig_alg_name();
313: $cert{'size'} = $x509->bit_length();
314: $cert{'email'} = $x509->email();
315: my $dt = DateTime::Format::x509->parse_datetime($cert{'start'});
316: if (ref($dt)) {
317: $starttime = $dt->epoch;
318: }
319: $dt = DateTime::Format::x509->parse_datetime($cert{'end'});
320: if (ref($dt)) {
321: $endtime = $dt->epoch;
322: }
323: $now = time;
324: if (($starttime ne '') && ($endtime ne '')) {
325: if ($endtime <= $now) {
326: $status = 'previous';
327: print "Current CA certificate expired $cert{'end'}\n";
328: } elsif ($starttime > $now) {
329: $status = 'future';
330: print "Current CA certificate will be valid after $cert{'start'}\n";
331: } else {
332: $status eq 'active';
333: print "Current CA certificate valid until $cert{'end'}".' '.
334: "Signature Algorithm: $cert{'alg'}; Public Key size: $cert{'size'}\n";
335: }
336: if ($status eq 'previous') {
337: print 'Create a new certificate? [Y/n]';
338: if (&get_user_selection(1)) {
339: unless (&make_ca_cert("$dir/lonca/private","$dir/lonca",$sslkeypass)) {
340: print "Failed to create CA cert\n";
341: exit;
342: }
343: }
344: }
345: } else {
346: print "Could not determine validity of current CA certificate\n";
347: exit;
348: }
349: }
350: } else {
351: unless (&make_ca_cert("$dir/lonca/private","$dir/lonca",$sslkeypass)) {
352: print "Failed to create CA cert\n";
353: exit;
354: }
355: }
356:
357: if (!-e "$dir/lonca/index.txt") {
358: File::Slurp::write_file("$dir/lonca/index.txt");
359: }
360: if (-e "$dir/lonca/index.txt") {
361: my $mode = 0600;
362: chmod $mode, "$dir/lonca/index.txt";
363: } else {
364: print "lonca/index.txt file is missing\n";
365: exit;
366: }
367: # echo 1000 > serial
368:
369:
370: unless (-e "$dir/lonca/crl/loncapaCAcrl.pem") {
371: open(PIPE,"openssl ca -gencrl -keyfile $dir/lonca/private/cakey.pem -cert $dir/lonca/cacert.pem -out $dir".
372: "/lonca/crl/loncapaCAcrl.pem -config $dir/lonca/opensslca.conf -passin pass:$sslkeypass |");
373: close(PIPE);
374: if (-e "$dir/lonca/crl/loncapaCAcrl.pem") {
375: print "Certificate Revocation List created\n";
376: }
377: }
378: if (-e "$dir/lonca/crl/loncapaCAcrl.pem") {
379: open(PIPE,"openssl crl -in $dir/lonca/crl/loncapaCAcrl.pem -inform pem -CAfile $dir/lonca/cacert.pem -noout 2>&1 |");
380: my $revoked = <PIPE>;
381: close(PIPE);
382: chomp($revoked);
383: print "Revocation certificate status: $revoked\n";
384: # Create a new one?
385: }
386:
387: sub cafield_to_key {
388: my %mapping = (
389: city => 'localityName',
390: state => 'stateOrProvinceName',
391: country => 'countryName',
392: email => 'emailAddress',
393: organization => 'organizationName',
394: clustername => 'commonName',
395: );
396: return %mapping;
397: }
398:
399: sub field_to_key {
400: my %mapping = (
401: days => 'default_days',
402: crldays => 'default_crl_days',
403: );
404: }
405:
406: sub parse_config {
407: my ($filepath) = @_;
408: my (%fields,%data);
409: if (open(my $fh,'<',$filepath)) {
410: my $currsection;
411: while(<$fh>) {
412: chomp();
413: s/(^\s+|\s+$)//g;
414: if (/^\[\s*([^\s]+)\s*\]/) {
415: $currsection = $1;
416: } elsif (/^([^=]+)=([^=]+)$/) {
417: my ($key,$value) = ($1,$2);
418: $key =~ s/\s+$//;
419: $value =~ s/^\s+//;
420: if ($currsection ne '') {
421: $fields{$currsection}{$key} = $value;
422: }
423: }
424: }
425: close($fh);
426: }
427: if (ref($fields{'loncapa_ca'}) eq 'HASH') {
428: my %ca_mapping = &cafield_to_key();
429: foreach my $key (keys(%ca_mapping)) {
430: $data{$key} = $fields{'loncapa_ca'}{$ca_mapping{$key}};
431: }
432: }
433: if (ref($fields{'loncapa'}) eq 'HASH') {
434: my %mapping = &field_to_key();
435: foreach my $key (keys(%mapping)) {
436: $data{$key} = $fields{'loncapa'}{$mapping{$key}};
437: }
438: }
439: return %data;
440: }
441:
442: sub save_config_changes {
443: my ($filepath,$updated) = @_;
444: return unless (ref($updated) eq 'HASH');
445: my %mapping = &field_to_key();
446: my %ca_mapping = &cafield_to_key();
447: my %revmapping = reverse(%mapping);
448: my %rev_ca_mapping = reverse(%ca_mapping);
449: my $lines;
450: if (open(my $fh,'<',$filepath)) {
451: my $currsection;
452: while(<$fh>) {
453: my $line = $_;
454: chomp();
455: s/(^\s+|\s+$)//g;
456: my $newline;
457: if (/^\[\s*([^\s]+)\s*\]/) {
458: $currsection = $1;
459: } elsif (/^([^=]+)=([^=]*)$/) {
460: my ($origkey,$origvalue) = ($1,$2);
461: my ($key,$value) = ($origkey,$origvalue);
462: $key =~ s/\s+$//;
463: $value =~ s/^\s+//;
464: if ($currsection eq 'loncapa_ca') {
465: if ((exists($rev_ca_mapping{$key})) && (exists($updated->{$rev_ca_mapping{$key}}))) {
466: if ($value eq '') {
467: if ($origvalue eq '') {
468: $origvalue = ' ';
469: }
470: $origvalue .= $updated->{$rev_ca_mapping{$key}};
471: } else {
472: $origvalue =~ s/\Q$value\E/$updated->{$rev_ca_mapping{$key}}/;
473: }
474: $newline = $origkey.'='.$origvalue."\n";
475: }
476: } elsif ($currsection eq 'loncapa') {
477: if ((exists($revmapping{$key})) && (exists($updated->{$revmapping{$key}}))) {
478: if ($value eq '') {
479: if ($origvalue eq '') {
480: $origvalue = ' ';
481: }
482: $origvalue .= $updated->{$revmapping{$key}};
483: } else {
484: $origvalue =~ s/\Q$value\E/$updated->{$revmapping{$key}}/;
485: }
486: $newline = $origkey.'='.$origvalue."\n";
487: }
488: }
489: }
490: if ($newline) {
491: $lines .= $newline;
492: } else {
493: $lines .= $line;
494: }
495: }
496: close($fh);
497: if (open(my $fout,'>',$filepath)) {
498: print $fout $lines;
499: close($fout);
500: } else {
501: print "Error: failed to open '$filepath' for writing\n";
502: }
503: }
504: return;
505: }
506:
507: #
508: # get_hostname() prompts the user to provide the server's hostname.
509: #
510: # If invalid input is provided, the routine is called recursively
511: # until, a valid hostname is provided.
512: #
513:
514: sub get_hostname {
515: my $hostname;
516: print 'Enter the hostname of this server, e.g., loncapa.somewhere.edu'."\n";
517: my $choice = <STDIN>;
518: chomp($choice);
519: $choice =~ s/(^\s+|\s+$)//g;
520: if ($choice eq '') {
521: print "Hostname you entered was either blank or contanied only white space.\n";
522: } elsif ($choice =~ /^[\w\.\-]+$/) {
523: $hostname = $choice;
524: } else {
525: print "Hostname you entered was invalid -- a hostname may only contain letters, numbers, - and .\n";
526: }
527: while ($hostname eq '') {
528: $hostname = &get_hostname();
529: }
530: print "\n";
531: return $hostname;
532: }
533:
534: sub get_new_sslkeypass {
535: my $sslkeypass;
536: my $flag=0;
537: # get password for SSL key
538: while (!$flag) {
539: $sslkeypass = &make_passphrase();
540: if ($sslkeypass) {
541: $flag = 1;
542: } else {
543: print "Invalid input (a password is required for the CA key).\n";
544: }
545: }
546: return $sslkeypass;
547: }
548:
549: sub make_passphrase {
550: my ($got_passwd,$firstpass,$secondpass,$passwd);
551: my $maxtries = 10;
552: my $trial = 0;
553: while ((!$got_passwd) && ($trial < $maxtries)) {
554: $firstpass = &get_password('Enter a password for the CA key (at least 6 characters long)');
555: if (length($firstpass) < 6) {
556: print('Password too short.'."\n".
557: 'Please choose a password with at least six characters.'."\n".
558: 'Please try again.'."\n");
559: } elsif (length($firstpass) > 30) {
560: print('Password too long.'."\n".
561: 'Please choose a password with no more than thirty characters.'."\n".
562: 'Please try again.'."\n");
563: } else {
564: my $pbad=0;
565: foreach (split(//,$firstpass)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
566: if ($pbad) {
567: print('Password contains invalid characters.'."\n".
568: 'Password must consist of standard ASCII characters.'."\n".
569: 'Please try again.'."\n");
570: } else {
571: $secondpass = &get_password('Enter password a second time');
572: if ($firstpass eq $secondpass) {
573: $got_passwd = 1;
574: $passwd = $firstpass;
575: } else {
576: print('Passwords did not match.'."\n".
577: 'Please try again.'."\n");
578: }
579: }
580: }
581: $trial ++;
582: }
583: return $passwd;
584: }
585:
586: sub get_password {
587: my ($prompt) = @_;
588: local $| = 1;
589: print $prompt.': ';
590: my $newpasswd = '';
591: ReadMode 'raw';
592: my $key;
593: while(ord($key = ReadKey(0)) != 10) {
594: if(ord($key) == 127 || ord($key) == 8) {
595: chop($newpasswd);
596: print "\b \b";
597: } elsif(!ord($key) < 32) {
598: $newpasswd .= $key;
599: print '*';
600: }
601: }
602: ReadMode 'normal';
603: print "\n";
604: return $newpasswd;
605: }
606:
607: #
608: # make_key() generates CA root key
609: #
610:
611: sub make_key {
612: my ($keydir,$sslkeypass) = @_;
613: # generate SSL key
614: my $created;
615: if (($keydir ne '') && ($sslkeypass ne '')) {
616: if (-f "$keydir/cakey.pem") {
617: my $mode = 0600;
618: chmod $mode, "$keydir/cakey.pem";
619: }
620: open(PIPE,"openssl genrsa -aes256 -passout pass:$sslkeypass -out $keydir/cakey.pem 2048 2>&1 |");
621: close(PIPE);
622: if (-f "$keydir/cakey.pem") {
623: my $mode = 0400;
624: chmod $mode, "$keydir/cakey.pem";
625: $created= 1;
626: }
627: } else {
628: print "Key creation failed. Missing one or more of: certificates directory, key name\n";
629: }
630: return $created;
631: }
632:
633: #
634: # make_ca_cert() generates CA root certificate
635: #
636:
637: sub make_ca_cert {
638: my ($keydir,$certdir,$sslkeypass) = @_;
639: # generate SSL cert for CA
640: my $created;
641: if ((-d $keydir) && (-d $certdir) && ($sslkeypass ne '')) {
642: my $cmd = "openssl req -x509 -key $keydir/cakey.pem -passin pass:$sslkeypass -new -batch -config $certdir/opensslca.conf -out $certdir/cacert.pem";
643: print "Calling ||$cmd||\n";
644: open(PIPE,"openssl req -x509 -key $keydir/cakey.pem -passin pass:$sslkeypass -new -batch -config $certdir/opensslca.conf -out $certdir/cacert.pem |");
645: close(PIPE);
646: if (-f "$certdir/cacert.pem") {
647: my $mode = 0600;
648: chmod $mode, "$certdir/cacert.pem";
649: # chmod $mode, "$certdir/careq.pem";
650: # open(PIPE,"openssl ca -create_serial -out $certdir/cacert.pem -days 3650 -keyfile $keydir/cakey.pem -selfsign -config ./openssl.cnf -infiles $certdir/careq.pem |");
651: # close(PIPE);
652: # if (-f "$certdir/cacert.pem") {
653: # my $mode = 0600;
654: # chmod $mode, "$certdir/cacert.pem";
655: # }
656: $created= 1;
657: }
658: } else {
659: print "Creation of CA root certificate failed. Missing one or more of: CA directory, CA key directory, or CA passphrase.\n";
660: }
661: return $created;
662: }
663:
664: sub get_camail {
665: my $camail;
666: my $flag=0;
667: # get Certificate Authority E-mail
668: while (!$flag) {
669: print(<<END);
670:
671: Enter e-mail address of Certificate Authority.
672: END
673:
674: my $choice=<>;
675: chomp($choice);
676: if (($choice ne '') && ($choice =~ /^[^\@]+\@[^\@]+$/)) {
677: $camail=$choice;
678: $flag=1;
679: } else {
680: print "Invalid input (a valid email address is required).\n";
681: }
682: }
683: return $camail;
684: }
685:
686: sub ssl_info {
687: print(<<END);
688:
689: ****** Information about Country, State or Province and City *****
690:
691: A two-letter country code, e.g., US, CA, DE etc. as defined by ISO 3166,
692: is required. A state or province, and a city are also required.
693: This locality information is included in two SSL certificates used internally
694: by LON-CAPA, unless you are running standalone.
695:
696: If your server will be part of either the production or development
697: clusters, then the certificate will need to be signed by the official
698: LON-CAPA Certificate Authority (CA). If you will be running your own
699: cluster then the cluster will need to create its own CA.
700:
701: END
702: }
703:
704: sub get_country {
705: my ($desiredhostname) = @_;
706: # get Country
707: my ($posscountry,$country);
708: if ($desiredhostname =~ /\.(edu|com|org)$/) {
709: $posscountry = 'us';
710: } else {
711: ($posscountry) = ($desiredhostname =~ /\.(a-z){2}$/);
712: }
713: if ($posscountry) {
714: my $countrydesc = &Locale::Country::code2country($posscountry);
715: if ($countrydesc eq '') {
716: undef($posscountry);
717: }
718: }
719:
720: my $flag=0;
721: while (!$flag) {
722: if ($posscountry) {
723: $posscountry = uc($posscountry);
724: print "Enter Two-Letter Country Code [$posscountry]:\n";
725: } else {
726: print "Enter the Two-Letter Country Code:\n";
727: }
728: my $choice=<STDIN>;
729: chomp($choice);
730: if ($choice ne '') {
731: if (&Locale::Country::code2country(lc($choice))) {
732: $country=uc($choice);
733: $flag=1;
734: } else {
735: print "Invalid input -- a valid two letter country code is required\n";
736: }
737: } elsif (($choice eq '') && ($posscountry ne '')) {
738: $country = $posscountry;
739: $flag = 1;
740: } else {
741: print "Invalid input -- a country code is required\n";
742: }
743: }
744: return $country;
745: }
746:
747: sub get_info {
748: my ($typename) = @_;
749: my $value;
750: my $choice = <STDIN>;
751: chomp($choice);
752: $choice =~ s/(^\s+|\s+$)//g;
753: if ($choice eq '') {
754: print "$typename you entered was either blank or contained only white space.\n";
755: } else {
756: $value = $choice;
757: }
758: while ($value eq '') {
759: $value = &get_info($typename);
760: }
761: print "\n";
762: return $value;
763: }
764:
765: sub get_days {
766: my $value;
767: my $choice = <STDIN>;
768: chomp($choice);
769: $choice =~ s/(^\s+|\s+$)//g;
770: if ($choice eq '') {
771: print "The value you entered was either blank or contained only white space.\n";
772: } elsif ($choice !~ /^\d+$/) {
773: print "The value you entered contained invalid characters -- you must enter just an integer.\n";
774: } else {
775: $value = $choice;
776: }
777: while ($value eq '') {
778: $value = &get_days();
779: }
780: print "\n";
781: return $value;
782: }
783:
784: sub confirm_config {
785: my (%data) = @_;
786: my $flag = 0;
787: while (!$flag) {
788: print(<<END);
789:
790: The cluster name, organization name, country, state and city will be
791: included in the CA certificate
792:
793: 1) Cluster Name: $data{'clustername'}
794: 2) Organization Name: $data{'organization'}
795: 3) Country: $data{'country'}
796: 4) State or Province: $data{'state'}
797: 5) City: $data{'city'}
798: 6) E-mail: $data{'email'}
799: 7) CA certificate lifetime (days): $data{'cadays'}
800: 8) Default certificate lifetime for issued certs (days): $data{'days'}
801: 9) CRL recreation interval (days): $data{'crldays'}
802: 10) Everything is correct up above
803:
804: Enter a choice of 1-9 to change, otherwise enter 10:
805: END
806: my $choice=<STDIN>;
807: chomp($choice);
808: if ($choice == 1) {
809: print(<<END);
810: 1) Cluster Name: $data{'clustername'}
811: Enter new value:
812: END
813: my $choice2=<STDIN>;
814: chomp($choice2);
815: $data{'clustername'}=$choice2;
816: chomp($choice2);
817: $data{'organization'}=$choice2;
818: } elsif ($choice == 3) {
819: print(<<END);
820: 3) Country: $data{'country'}
821: Enter new value (this should be a two-character code, e,g, US, CA, DE):
822: END
823: my $choice2=<STDIN>;
824: chomp($choice2);
825: $data{'country'} = uc($choice2);
826: } elsif ($choice == 4) {
827: print(<<END);
828: 4) State or Province: $data{'state'}
829: Enter new value:
830: END
831: my $choice2=<>;
832: chomp($choice2);
833: $data{'state'}=$choice2;
834: } elsif ($choice == 5) {
835: print(<<END);
836: 5) City: $data{'city'}
837: Enter new value:
838: END
839: my $choice2=<>;
840: chomp($choice2);
841: $data{'city'}=$choice2;
842: } elsif ($choice == 6) {
843: print(<<END);
844: 6) E-mail: $data{'email'}
845: Enter new value:
846: END
847: my $choice2=<>;
848: chomp($choice2);
849: $data{'email'}=$choice2;
850: } elsif ($choice == 7) {
851: print(<<END);
852: 7) CA Root Certificate lifetime: $data{'cadays'}
853: Enter new value:
854: END
855: my $choice2=<>;
856: chomp($choice2);
857: $choice2 =~ s/\D//g;
858: $data{'cadays'}=$choice2;
859: } elsif ($choice == 8) {
860: print(<<END);
861: 8) Default certificate lifetime: $data{'days'}
862: Enter new value:
863: END
864: my $choice2=<>;
865: chomp($choice2);
866: $choice2 =~ s/\D//g;
867: $data{'days'}=$choice2;
868: } elsif ($choice == 9) {
869: print(<<END);
870: 9) CRL re-creation interval: $data{'crldays'}
871: Enter new value:
872: END
873: my $choice2=<>;
874: chomp($choice2);
875: $choice2 =~ s/\D//g;
876: $data{'crldays'}=$choice2;
877: } elsif ($choice == 10) {
878: $flag=1;
879: foreach my $key (keys(%data)) {
880: $data{$key} =~ s{/}{ }g;
881: }
882: } else {
883: print "Invalid input.\n";
884: }
885: }
886: return %data;
887: }
888:
889: sub get_user_selection {
890: my ($defaultrun) = @_;
891: my $do_action = 0;
892: my $choice = <STDIN>;
893: chomp($choice);
894: $choice =~ s/(^\s+|\s+$)//g;
895: my $yes = 'y';
896: if ($defaultrun) {
897: if (($choice eq '') || ($choice =~ /^\Q$yes\E/i)) {
898: $do_action = 1;
899: }
900: } else {
901: if ($choice =~ /^\Q$yes\E/i) {
902: $do_action = 1;
903: }
904: }
905: return $do_action;
906: }
907:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>