1: #!/usr/bin/perl
2: $|=1;
3: # Displays status of LON-CAPA SSL certs, and allows new certificate
4: # signing requests to be created and e-mailed to CA for cluster to
5: # which server/VM belongs.
6: # $Id: manage_ssl_certs.pl,v 1.1 2018/08/18 23:33:30 raeburn Exp $
7: #
8: # Copyright Michigan State University Board of Trustees
9: #
10: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
11: #
12: # LON-CAPA is free software; you can redistribute it and/or modify
13: # it under the terms of the GNU General Public License as published by
14: # the Free Software Foundation; either version 2 of the License, or
15: # (at your option) any later version.
16: #
17: # LON-CAPA is distributed in the hope that it will be useful,
18: # but WITHOUT ANY WARRANTY; without even the implied warranty of
19: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20: # GNU General Public License for more details.
21: #
22: # You should have received a copy of the GNU General Public License
23: # along with LON-CAPA; if not, write to the Free Software
24: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25: #
26: # /home/httpd/html/adm/gpl.txt
27: #
28: # http://www.lon-capa.org/
29: #
30:
31: use strict;
32: use lib '/home/httpd/lib/perl/';
33: use LONCAPA::Configuration;
34: use LONCAPA::Lond;
35: use LONCAPA::SSL;
36: use LONCAPA;
37: use Term::ReadKey;
38: use Locale::Country;
39:
40: my ($lonCluster,$domainDescription,$hostname,$certmail,
41: $certsdir,$privkey,$connectcsr,$replicatecsr);
42:
43: print(<<END);
44:
45: ===============================================================================
46:
47: Which type of LON-CAPA cluster does this server/VM belong to?
48: IMPORTANT: to take advantage of the cluster options 1) and 3),
49: you must have been or expect to be accepted into the cluster.
50: Contact loncapa\@loncapa.org for access.
51:
52: 1) PRODUCTION - you have (or will) connect this machine to the
53: LON-CAPA content sharing network. This setting is for
54: schools, colleges, and universities, that currently
55: are running - or in the future will run - courses.
56: 2) STAND-ALONE - you want this machine to run in 'stand-alone' mode and
57: not be connected to other LON-CAPA machines.
58: 3) DEVELOPMENT - you want to do software (not content!) development with
59: this workstation and eventually link it with
60: workstations of other LON-CAPA software developers.
61: 4) RUNNING YOUR OWN CLUSTER - this machine is not in the standard LON-CAPA
62: cluster and won't be in the future.
63: (This choice is unlikely what you want to select.)
64: END
65:
66: my $flag=0;
67: while (!$flag) {
68: print "ENTER 1, 2, 3, or 4:\n";
69: my $choice=<>;
70: chomp($choice);
71: if ($choice==1) {
72: $lonCluster='production'; $flag=1;
73: } elsif ($choice==2) {
74: $lonCluster='standalone'; $flag=1;
75: } elsif ($choice==3) {
76: $lonCluster='development'; $flag=1;
77: } elsif ($choice==4) {
78: $lonCluster='existing'; $flag=1;
79: my $earlyout;
80: foreach my $file ('hosts.tab','dns_hosts.tab',
81: 'domain.tab','dns_domain.tab') {
82: unless (-e '/home/httpd/lonTabs/'.$file) {
83: print <<END;
84: There is no existing /home/httpd/lonTabs/$file
85: END
86: $earlyout = 1;
87: }
88: }
89: if ($earlyout) {
90: exit;
91: }
92: }
93: }
94:
95: $flag = 0;
96: my $dist=`/home/httpd/perl/distprobe`;
97: my $confdir = '/etc/httpd/conf/';
98: if ($dist =~ /^(sles|suse|ubuntu|debian)/) {
99: $confdir = '/etc/apache2/';
100: }
101:
102: my $filename='loncapa.conf';
103: my %perlvar;
104: if (-e "$confdir$filename") {
105: if (open(CONFIG,'<',$confdir.$filename) or die("Can't read $confdir$filename")) {
106: while (my $configline=<CONFIG>) {
107: if ($configline =~ /^[^\#]*PerlSetVar/) {
108: my ($unused,$varname,$varvalue)=split(/\s+/,$configline);
109: chomp($varvalue);
110: $perlvar{$varname}=$varvalue if $varvalue!~/^\{\[\[\[\[/;
111: }
112: }
113: close(CONFIG);
114: }
115: }
116:
117: my $perlstaticref = &get_static_config($confdir);
118: if (ref($perlstaticref) ne 'HASH') {
119: exit;
120: }
121:
122: if (open(IN,'<','/home/httpd/lonTabs/domain.tab')) {
123: while(my $line = <IN>) {
124: if ($line =~ /^\Q$perlvar{'lonDefDomain'}\E\:/) {
125: (undef,$domainDescription)=split(/:/,$line);
126: chomp($domainDescription);
127: last;
128: }
129: }
130: close(IN);
131: }
132:
133: if (open(IN,'<','/home/httpd/lonTabs/hosts.tab')) {
134: while(my $line = <IN>) {
135: if ($line =~ /^\Q$perlvar{'lonHostID'}\E\:/) {
136: (undef,undef,undef,$hostname)=split(/:/,$line);
137: last;
138: }
139: }
140: close(IN);
141: }
142:
143: $certsdir = $perlstaticref->{'lonCertificateDirectory'};
144: $privkey = $perlstaticref->{'lonnetPrivateKey'};
145: $connectcsr = $perlstaticref->{'lonnetCertificate'};
146: $connectcsr =~ s/\.pem$/.csr/;
147: $replicatecsr = $perlstaticref->{'lonnetHostnameCertificate'};
148: $replicatecsr =~ s/\.pem$/.csr/;
149:
150:
151: $certmail = &get_mail();
152:
153: print "\nRetrieving status information for SSL key and certificates ...\n\n";
154: my ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
155: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
156: print $certinfo;
157: my %sslstatus;
158: if (ref($sslref) eq 'HASH') {
159: %sslstatus = %{$sslref};
160: }
161: while (!$flag) {
162: print(<<END);
163:
164: ===============================================================================
165:
166: This is now the current configuration of your machine.
167: 1) Private Key for SSL: $lonkeystatus
168: 2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
169: 3) SSL Certificate for Content Replication: $lonhostnamecertstatus
170: 4) Everything is correct up above
171:
172: ENTER A CHOICE OF 1 TO 3 TO CHANGE, OTHERWISE ENTER 4:
173: END
174:
175: my $choice=<>;
176: chomp($choice);
177: if ($choice==1) {
178: if ($sslstatus{'key'} == 1) {
179: print(<<END);
180: 1) Private Key for SSL: $lonkeystatus
181:
182: POSSIBLE CHOICES:
183: 1) overwrite existing key
184: 2) make no change
185: ENTER NEW VALUE
186: END
187: my $choice2=<>;
188: chomp($choice2);
189: if ($choice2 eq '1') {
190: my $sslkeypass = &get_new_sslkeypass();
191: &make_key($certsdir,$privkey,$sslkeypass);
192: }
193: } elsif ($sslstatus{'key'} == 0) {
194: print(<<END);
195: 1) Private Key for SSL: $lonkeystatus
196: END
197: my $sslkeypass = &get_new_sslkeypass();
198: &make_key($certsdir,$privkey,$sslkeypass);
199: print "\nRetrieving status information for SSL key and certificates ...\n\n";
200: ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
201: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
202: if (ref($sslref) eq 'HASH') {
203: %sslstatus = %{$sslref};
204: }
205: }
206: } elsif ($choice==2) {
207: if (($sslstatus{'host'} == 1) || ($sslstatus{'host'} == 2) || ($sslstatus{'host'} == 3)) {
208: print(<<END);
209: 2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
210:
211: POSSIBLE CHOICES:
212: 1) create new certificate signing request with new key
213: 2) create new certificate signing request with existing key
214: 3) resend current certificate signing request
215: 4) make no change
216: ENTER NEW VALUE
217: END
218:
219: my $choice2=<>;
220: chomp($choice2);
221: if (($choice2 eq '1') || ($choice2 eq '2')) {
222: &ssl_info();
223: my $country = &get_country($hostname);
224: my $state = &get_state();
225: my $city = &get_city();
226: my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$certmail";
227: ($domainDescription,$country,$state,$city) = &confirm_locality($domainDescription,$country,$state,$city);
228: my $sslkeypass;
229: if ($choice2 eq '1') {
230: $sslkeypass = &get_new_sslkeypass();
231: &make_key($certsdir,$privkey,$sslkeypass);
232: } elsif ($choice2 eq '2') {
233: $sslkeypass = &get_password('Enter existing password for SSL key');
234: &encrypt_key($certsdir,$privkey,$sslkeypass);
235: }
236: &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj);
237: &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
238: print "\nRetrieving status information for SSL key and certificates ...\n\n";
239: ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
240: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
241: if (ref($sslref) eq 'HASH') {
242: %sslstatus = %{$sslref};
243: }
244: } elsif ($choice2 eq '3') {
245: if (-e "$certsdir/$connectcsr") {
246: &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
247: }
248: }
249: } elsif (($sslstatus{'host'} == 0) || ($sslstatus{'host'} == 4) || ($sslstatus{'host'} == 5)) {
250: my $sslkeypass;
251: if ($sslstatus{'key'} == 1) {
252: print(<<END);
253: 2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
254:
255: POSSIBLE CHOICES:
256: 1) create new certificate signing request with new key
257: 2) create new certificate signing request with existing key
258: 3) make no change
259: ENTER NEW VALUE
260: END
261: my $choice2=<>;
262: chomp($choice2);
263: if ($choice2 eq '1') {
264: $sslkeypass = &get_new_sslkeypass();
265: &make_key($certsdir,$privkey,$sslkeypass);
266: } elsif ($choice2 eq '2') {
267: $sslkeypass = &get_password('Enter existing password for SSL key');
268: &encrypt_key($certsdir,$privkey,$sslkeypass);
269: }
270: } else {
271: print(<<END);
272: 2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
273: END
274: $sslkeypass = &get_new_sslkeypass();
275: }
276: &ssl_info();
277: my $country = &get_country($hostname);
278: my $state = &get_state();
279: my $city = &get_city();
280: my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$certmail";
281: &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj);
282: &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
283: print "\nRetrieving status information for SSL key and certificates ...\n\n";
284: ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
285: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
286: if (ref($sslref) eq 'HASH') {
287: %sslstatus = %{$sslref};
288: }
289: }
290: } elsif ($choice==3) {
291: if (($sslstatus{'hostname'} == 1) || ($sslstatus{'hostname'} == 2) || ($sslstatus{'hostname'} == 3)) {
292: print(<<END);
293: 3) SSL Certificate for Content Replication: $lonhostnamecertstatus
294:
295: POSSIBLE CHOICES:
296: 1) create new certificate signing request with new key
297: 2) create new certificate signing request with existing key
298: 3) resend current certificate signing request
299: 4) make no change
300: ENTER NEW VALUE
301: END
302: my $choice2=<>;
303: chomp($choice2);
304: if (($choice2 eq '1') || ($choice2 eq '2')) {
305: &ssl_info();
306: my $country = &get_country($hostname);
307: my $state = &get_state();
308: my $city = &get_city();
309: my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$hostname/OU=LONCAPA/emailAddress=$certmail";
310: my $sslkeypass;
311: if ($choice2 eq '1') {
312: $sslkeypass = &get_new_sslkeypass();
313: &make_key($certsdir,$privkey,$sslkeypass);
314: } elsif ($choice2 eq '2') {
315: $sslkeypass = &get_password('Enter existing password for SSL key');
316: &encrypt_key($certsdir,$privkey,$sslkeypass);
317: }
318: &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj);
319: &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
320: print "\nRetrieving status information for SSL key and certificates ...\n\n";
321: ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
322: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
323: if (ref($sslref) eq 'HASH') {
324: %sslstatus = %{$sslref};
325: }
326: } elsif ($choice2 eq '3') {
327: if (-e "$certsdir/$replicatecsr") {
328: &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
329: }
330: }
331: } elsif (($sslstatus{'hostname'} == 0) || ($sslstatus{'hostname'} == 4) || ($sslstatus{'hostname'} == 5)) {
332: my $sslkeypass;
333: if ($sslstatus{'key'} == 1) {
334: print(<<END);
335: 3) SSL Certificate for Content Replication: $lonhostnamecertstatus
336:
337: POSSIBLE CHOICES:
338: 1) create new certificate signing request with new key
339: 2) create new certificate signing request with existing key
340: 3) make no change
341: ENTER NEW VALUE
342: END
343: my $choice2=<>;
344: chomp($choice2);
345: if ($choice2 eq '1') {
346: $sslkeypass = &get_new_sslkeypass();
347: &make_key($certsdir,$privkey,$sslkeypass);
348: } elsif ($choice2 eq '2') {
349: $sslkeypass = &get_password('Enter existing password for SSL key');
350: &encrypt_key($certsdir,$privkey,$sslkeypass);
351: }
352: } else {
353: print(<<END);
354: 3) SSL Certificate for Content Replication: $lonhostnamecertstatus
355: END
356: $sslkeypass = &get_new_sslkeypass();
357: }
358: &ssl_info();
359: my $country = &get_country($hostname);
360: my $state = &get_state();
361: my $city = &get_city();
362: my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$hostname/OU=LONCAPA/emailAddress=$certmail";
363: &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj);
364: &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
365: print "\nRetrieving status information for SSL key and certificates ...\n\n";
366: ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
367: &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
368: if (ref($sslref) eq 'HASH') {
369: %sslstatus = %{$sslref};
370: }
371: }
372: } elsif ($choice==4) {
373: $flag=1;
374: } else {
375: print "Invalid input.\n";
376: }
377: }
378:
379: exit;
380:
381: sub get_static_config {
382: my ($confdir) = @_;
383: my $filename='loncapa_apache.conf';
384: my %LCperlvar;
385: if (-e "$confdir$filename") {
386: open(CONFIG,'<',$confdir.$filename) or die("Can't read $confdir$filename");
387: while (my $configline=<CONFIG>) {
388: if ($configline =~ /^[^\#]?PerlSetVar/) {
389: my ($unused,$varname,$varvalue)=split(/\s+/,$configline);
390: chomp($varvalue);
391: $LCperlvar{$varname}=$varvalue;
392: }
393: }
394: close(CONFIG);
395: }
396: return \%LCperlvar;
397: }
398:
399: sub get_sslnames {
400: my %sslnames = (
401: key => 'lonnetPrivateKey',
402: host => 'lonnetCertificate',
403: hostname => 'lonnetHostnameCertificate',
404: ca => 'lonnetCertificateAuthority',
405: );
406: return %sslnames;
407: }
408:
409: sub get_ssldesc {
410: my %ssldesc = (
411: key => 'Private Key',
412: host => 'Connections Certificate',
413: hostname => 'Replication Certificate',
414: ca => 'LON-CAPA CA Certificate',
415: );
416: return %ssldesc;
417: }
418:
419: sub get_cert_status {
420: my ($lonHostID,$hostname,$perlvarstatic) = @_;
421: my $currcerts = &LONCAPA::SSL::print_certstatus({$lonHostID => $hostname,},'text','cgi');
422: my ($lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,%sslstatus);
423: my $output = '';
424: if ($currcerts eq "$lonHostID:error") {
425: $output .= "No information available for SSL certificates\n";
426: $sslstatus{'key'} = -1;
427: $sslstatus{'host'} = -1;
428: $sslstatus{'hostname'} = -1;
429: $sslstatus{'ca'} = -1;
430: $lonkeystatus = 'unknown status';
431: $lonhostcertstatus = 'unknown status';
432: $lonhostnamecertstatus = 'unknown status';
433: } else {
434: my %sslnames = &get_sslnames();
435: my %ssldesc = &get_ssldesc();
436: my %csr;
437: my ($lonhost,$info) = split(/\:/,$currcerts,2);
438: if ($lonhost eq $lonHostID) {
439: my @items = split(/\&/,$info);
440: foreach my $item (@items) {
441: my ($key,$value) = split(/=/,$item,2);
442: if ($key =~ /^(host(?:|name))\-csr$/) {
443: $csr{$1} = $value;
444: }
445: my @data = split(/,/,$value);
446: if (grep(/^\Q$key\E$/,keys(%sslnames))) {
447: my ($checkcsr,$comparecsr);
448: if (lc($data[0]) eq 'yes') {
449: $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." available with status = $data[1]\n";
450: if ($key eq 'key') {
451: $lonkeystatus = "status: $data[1]";
452: if ($data[1] =~ /ok$/) {
453: $sslstatus{$key} = 1;
454: }
455: } else {
456: my $setstatus;
457: if (($key eq 'host') || ($key eq 'hostname')) {
458: if ($data[1] eq 'otherkey') {
459: $sslstatus{$key} = 4;
460: $setstatus = 1;
461: if ($key eq 'host') {
462: $lonhostcertstatus = "status: created with different key";
463: } elsif ($key eq 'hostname') {
464: $lonhostnamecertstatus = "status: created with different key";
465: }
466: } elsif ($data[1] eq 'nokey') {
467: $sslstatus{$key} = 5;
468: $setstatus = 1;
469: if ($key eq 'host') {
470: $lonhostcertstatus = "status: created with missing key";
471: } elsif ($key eq 'hostname') {
472: $lonhostnamecertstatus = "status: created with missing key";
473: }
474: }
475: if ($setstatus) {
476: $comparecsr = 1;
477: }
478: }
479: unless ($setstatus) {
480: if ($data[1] eq 'expired') {
481: $sslstatus{$key} = 2;
482: if (($key eq 'host') || ($key eq 'hostname')) {
483: $comparecsr = 1;
484: }
485: } elsif ($data[1] eq 'future') {
486: $sslstatus{$key} = 3;
487: $sslstatus{$key} = 3;
488: } else {
489: $sslstatus{$key} = 1;
490: }
491: if ($key eq 'host') {
492: $lonhostcertstatus = "status: $data[1]";
493: } elsif ($key eq 'hostname') {
494: $lonhostnamecertstatus = "status: $data[1]";
495: }
496: }
497: }
498: } else {
499: $sslstatus{$key} = 0;
500: $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." not available\n";
501: if ($key eq 'key') {
502: $lonkeystatus = 'still needed';
503: } elsif (($key eq 'host') || ($key eq 'hostname')) {
504: $checkcsr = 1;
505: }
506: }
507: if (($checkcsr) || ($comparecsr)) {
508: my $csrfile = $perlvarstatic->{$sslnames{$key}};
509: $csrfile =~s /\.pem$/.csr/;
510: my $csrstatus;
511: if (-e $perlvarstatic->{'lonCertificateDirectory'}."/$csrfile") {
512: if (open(PIPE,"openssl req -text -noout -verify -in ".$perlvarstatic->{'lonCertificateDirectory'}."/$csrfile 2>&1 |")) {
513: while(<PIPE>) {
514: chomp();
515: $csrstatus = $_;
516: last;
517: }
518: close(PIPE);
519: if ($comparecsr) {
520: my $csrhash;
521: if (open(PIPE,"openssl x509 -in certificate.crt -pubkey -noout -outform pem | sha256sum")) {
522: $csrhash = <PIPE>;
523: close(PIPE);
524: }
525: }
526: }
527: $output .= "Certificate signing request for $ssldesc{$key} available with status = $csrstatus\n\n";
528: if ($key eq 'host') {
529: $lonhostcertstatus = 'awaiting signature';
530: } else {
531: $lonhostnamecertstatus = 'awaiting signature';
532: }
533: $sslstatus{$key} = 3;
534: } elsif ($checkcsr) {
535: $output .= "No certificate signing request available for $ssldesc{$key}\n\n";
536: if ($key eq 'host') {
537: $lonhostcertstatus = 'still needed';
538: } else {
539: $lonhostnamecertstatus = 'still needed';
540: }
541: }
542: }
543: }
544: }
545: # FIXME If different key, or missing key, or expired, check if there is a csr that does not match the cert, and that may be awaiting signature.
546: }
547: }
548: return ($output,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,\%sslstatus);
549: }
550:
551: sub get_new_sslkeypass {
552: my $sslkeypass;
553: my $flag=0;
554: # get Password for SSL key
555: while (!$flag) {
556: $sslkeypass = &make_passphrase();
557: if ($sslkeypass) {
558: $flag = 1;
559: } else {
560: print "Invalid input (a password is required for the SSL key).\n";
561: }
562: }
563: return $sslkeypass;
564: }
565:
566: sub make_passphrase {
567: my ($got_passwd,$firstpass,$secondpass,$passwd);
568: my $maxtries = 10;
569: my $trial = 0;
570: while ((!$got_passwd) && ($trial < $maxtries)) {
571: $firstpass = &get_password('Enter a password for the SSL key (at least 6 characters long)');
572: if (length($firstpass) < 6) {
573: print('Password too short.'."\n".
574: 'Please choose a password with at least six characters.'."\n".
575: 'Please try again.'."\n");
576: } elsif (length($firstpass) > 30) {
577: print('Password too long.'."\n".
578: 'Please choose a password with no more than thirty characters.'."\n".
579: 'Please try again.'."\n");
580: } else {
581: my $pbad=0;
582: foreach (split(//,$firstpass)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
583: if ($pbad) {
584: print('Password contains invalid characters.'."\n".
585: 'Password must consist of standard ASCII characters.'."\n".
586: 'Please try again.'."\n");
587: } else {
588: $secondpass = &get_password('Enter password a second time');
589: if ($firstpass eq $secondpass) {
590: $got_passwd = 1;
591: $passwd = $firstpass;
592: } else {
593: print('Passwords did not match.'."\n".
594: 'Please try again.'."\n");
595: }
596: }
597: }
598: $trial ++;
599: }
600: return $passwd;
601: }
602:
603: sub get_password {
604: my ($prompt) = @_;
605: local $| = 1;
606: print $prompt.': ';
607: my $newpasswd = '';
608: ReadMode 'raw';
609: my $key;
610: while(ord($key = ReadKey(0)) != 10) {
611: if(ord($key) == 127 || ord($key) == 8) {
612: chop($newpasswd);
613: print "\b \b";
614: } elsif(!ord($key) < 32) {
615: $newpasswd .= $key;
616: print '*';
617: }
618: }
619: ReadMode 'normal';
620: print "\n";
621: return $newpasswd;
622: }
623:
624: sub send_mail {
625: my ($hostname,$recipient,$subj,$file) = @_;
626: my $from = 'www@'.$hostname;
627: my $certmail = "To: $recipient\n".
628: "From: $from\n".
629: "Subject: ".$subj."\n".
630: "Content-type: text/plain\; charset=UTF-8\n".
631: "MIME-Version: 1.0\n\n";
632: if (open(my $fh,"<$file")) {
633: while (<$fh>) {
634: $certmail .= $_;
635: }
636: close($fh);
637: $certmail .= "\n\n";
638: if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) {
639: print $mailh $certmail;
640: close($mailh);
641: print "Mail sent ($subj) to $recipient\n";
642: } else {
643: print "Sending mail ($subj) to $recipient failed.\n";
644: }
645: }
646: return;
647: }
648:
649: sub mail_csr {
650: my ($types,$lonCluster,$lonHostID,$hostname,$certsdir,$connectcsr,$replicatecsr,$perlvarref) = @_;
651: my ($camail,$flag);
652: if ($lonCluster eq 'production' || $lonCluster eq 'development') {
653: $camail = $perlvarref->{'SSLEmail'};
654: } else {
655: $flag=0;
656: # get Certificate Authority E-mail
657: while (!$flag) {
658: print(<<END);
659:
660: ENTER EMAIL ADDRESS TO SEND CERTIFICATE SIGNING REQUESTS
661: END
662:
663: my $choice=<>;
664: chomp($choice);
665: if ($choice ne '') {
666: open(OUT,'>>/tmp/loncapa_updatequery.out');
667: print(OUT 'Certificate Authority Email Address'."\t".$choice."\n");
668: close(OUT);
669: $camail=$choice;
670: $flag=1;
671: } else {
672: print "Invalid input (an email address is required).\n";
673: }
674: }
675: }
676: if ($camail) {
677: my $subj;
678: if (($types eq 'both') || ($types = 'host')) {
679: if (-e "$certsdir/$connectcsr") {
680: $subj = "Certificate Request ($lonHostID)";
681: print(&send_mail($hostname,$camail,$subj,"$certsdir/$connectcsr"));
682: }
683: }
684: if (($types eq 'both') || ($types = 'hostname')) {
685: if (-e "$certsdir/$replicatecsr") {
686: $subj = "Certificate Request (internal-$hostname)";
687: print(&send_mail($hostname,$camail,$subj,"$certsdir/$replicatecsr"));
688: }
689: }
690: }
691: }
692:
693: sub ssl_info {
694: print(<<END);
695:
696: ****** Information about Country, State or Province and City *****
697:
698: A two-letter country code, e.g., US, CA, DE etc. as defined by ISO 3166,
699: is required. A state or province, and a city are also required.
700: This locality information is included in two SSL certificates used internally
701: by LON-CAPA, unless you are running standalone.
702:
703: If your server will be part of either the production or development
704: clusters, then the certificate will need to be signed by the official
705: LON-CAPA Certificate Authority (CA). If you will be running your own
706: cluster then the cluster will need to create its own CA.
707:
708: END
709: }
710:
711: sub get_country {
712: my ($hostname) = @_;
713: # get Country
714: my ($posscountry,$country);
715: if ($hostname =~ /\.(edu|com|org)$/) {
716: $posscountry = 'us';
717: } else {
718: ($posscountry) = ($hostname =~ /\.(a-z){2}$/);
719: }
720: if ($posscountry) {
721: my $countrydesc = &Locale::Country::code2country($posscountry);
722: if ($countrydesc eq '') {
723: undef($posscountry);
724: }
725: }
726:
727: my $flag=0;
728: while (!$flag) {
729: if ($posscountry) {
730: $posscountry = uc($posscountry);
731: print "ENTER TWO-LETTER COUNTRY CODE [$posscountry]:\n";
732: } else {
733: print "ENTER TWO-LETTER COUNTRY CODE:\n";
734: }
735: my $choice=<>;
736: chomp($choice);
737: if ($choice ne '') {
738: if (&Locale::Country::code2country(lc($choice))) {
739: open(OUT,'>>/tmp/loncapa_updatequery.out');
740: print(OUT 'country'."\t".uc($choice)."\n");
741: close(OUT);
742: $country=uc($choice);
743: $flag=1;
744: } else {
745: print "Invalid input -- a valid two letter country code is required\n";
746: }
747: } elsif (($choice eq '') && ($posscountry ne '')) {
748: open(OUT,'>>/tmp/loncapa_updatequery.out');
749: print(OUT 'country'."\t".$posscountry."\n");
750: close(OUT);
751: $country = $posscountry;
752: $flag = 1;
753: } else {
754: print "Invalid input -- a country code is required\n";
755: }
756: }
757: return $country;
758: }
759:
760: sub get_state {
761: # get State or Province
762: my $flag=0;
763: my $state = '';
764: while (!$flag) {
765: print(<<END);
766:
767: ENTER STATE OR PROVINCE NAME:
768: END
769:
770: my $choice=<>;
771: chomp($choice);
772: if ($choice ne '') {
773: open(OUT,'>>/tmp/loncapa_updatequery.out');
774: print(OUT 'state'."\t".$choice."\n");
775: close(OUT);
776: $state=$choice;
777: $flag=1;
778: } else {
779: print "Invalid input (a state or province name is required).\n";
780: }
781: }
782: return $state;
783: }
784:
785: sub get_city {
786: # get City
787: my $flag=0;
788: my $city = '';
789: while (!$flag) {
790: print(<<END);
791:
792: ENTER CITY NAME:
793: END
794:
795: my $choice=<>;
796: chomp($choice);
797: if ($choice ne '') {
798: open(OUT,'>>/tmp/loncapa_updatequery.out');
799: print(OUT 'city'."\t".$choice."\n");
800: close(OUT);
801: $city=$choice;
802: $flag=1;
803: } else {
804: print "Invalid input (a city is required).\n";
805: }
806: }
807: return $city;
808: }
809:
810: sub confirm_locality {
811: my ($domainDescription,$country,$state,$city) = @_;
812: my $flag = 0;
813: while (!$flag) {
814: print(<<END);
815:
816: The domain description, country, state and city will be
817: used in the SSL certificates
818:
819: 1) Domain Description: $domainDescription
820: 2) Country: $country
821: 3) State or Province: $state
822: 4) City: $city
823: 5) Everything is correct up above
824:
825: ENTER A CHOICE OF 1-4 TO CHANGE, otherwise ENTER 5:
826: END
827: my $choice=<>;
828: chomp($choice);
829: if ($choice == 1) {
830: print(<<END);
831: 1) Domain Description: $domainDescription
832: ENTER NEW VALUE
833: END
834: my $choice2=<>;
835: chomp($choice2);
836: $domainDescription=$choice2;
837: } elsif ($choice == 2) {
838: print(<<END);
839: 2) Country: $country
840: ENTER NEW VALUE (this should be a two-character code, e,g, US, CA, DE)
841: END
842: my $choice2=<>;
843: chomp($choice2);
844: $country = uc($choice2);
845: } elsif ($choice == 3) {
846: print(<<END);
847: 3) State or Province: $state
848: ENTER NEW VALUE:
849: END
850: my $choice2=<>;
851: chomp($choice2);
852: $state=$choice2;
853: } elsif ($choice == 4) {
854: print(<<END);
855: 4) City: $city
856: ENTER NEW VALUE:
857: END
858: my $choice2=<>;
859: chomp($choice2);
860: $city=$choice2;
861: } elsif ($choice == 5) {
862: $flag=1;
863: $state =~ s{/}{ }g;
864: $city =~ s{/}{ }g;
865: $domainDescription =~ s{/}{ }g;
866: } else {
867: print "Invalid input.\n";
868: }
869: }
870: return ($domainDescription,$country,$state,$city);
871: }
872:
873: sub make_key {
874: my ($certsdir,$privkey,$sslkeypass) = @_;
875: # generate SSL key
876: if ($certsdir && $privkey) {
877: if (-f "$certsdir/lonKey.enc") {
878: my $mode = 0600;
879: chmod $mode, "$certsdir/lonKey.enc";
880: }
881: open(PIPE,"openssl genrsa -des3 -passout pass:$sslkeypass -out $certsdir/lonKey.enc 2048 2>&1 |");
882: close(PIPE);
883: if (-f "$certsdir/$privkey") {
884: my $mode = 0600;
885: chmod $mode, "$certsdir/$privkey";
886: }
887: open(PIPE,"openssl rsa -in $certsdir/lonKey.enc -passin pass:$sslkeypass -out $certsdir/$privkey -outform PEM |");
888: close(PIPE);
889: if (-f "$certsdir/lonKey.enc") {
890: my $mode = 0400;
891: chmod $mode, "$certsdir/lonKey.enc";
892: }
893: if (-f "$certsdir/$privkey") {
894: my $mode = 0400;
895: chmod $mode, "$certsdir/$privkey";
896: }
897: } else {
898: print "Key creation failed. Missing one or more of: certificates directory, key name\n";
899: }
900: }
901:
902: sub encrypt_key {
903: my ($certsdir,$privkey,$sslkeypass) = @_;
904: if ($certsdir && $privkey) {
905: if ((-f "$certsdir/$privkey") && (!-f "$certsdir/lonKey.enc")) {
906: open(PIPE,"openssl rsa -des3 -in $certsdir/$privkey -out $certsdir/lonKey.enc |");
907: }
908: }
909: return;
910: }
911:
912: sub make_host_csr {
913: my ($certsdir,$sslkeypass,$connectcsr,$connectsubj) = @_;
914: # generate SSL csr for hostID
915: if ($certsdir && $connectcsr && $connectsubj) {
916: open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$connectsubj\" -out $certsdir/$connectcsr |");
917: close(PIPE);
918: } else {
919: print "Creation of certificate signing request failed. Missing one or more of: certificates directory, CSR name, or locality information.\n";
920: }
921: }
922:
923: sub make_hostname_csr {
924: my ($certsdir,$sslkeypass,$replicatecsr,$replicatesubj) = @_;
925: # generate SSL csr for internal hostname
926: if ($certsdir && $replicatecsr && $replicatesubj) {
927: open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$replicatesubj\" -out $certsdir/$replicatecsr |");
928: close(PIPE);
929: } else {
930: print "Creation of certificate signing request failed. Missing one or more of: certificates directory, CSR name, or locality information.\n";
931: }
932: }
933:
934: sub get_mail {
935: my $email;
936: my $flag=0;
937: # get E-mail Address
938: while (!$flag) {
939: print(<<END);
940:
941: An e-mail address to be included with certificate signing requests is needed.
942: After signing by the Certificate Authority, the signed certificate(s) will
943: be returned to this e-mail address.
944: ENTER E-MAIL ADDRESS
945: END
946: my $choice=<>;
947: chomp($choice);
948: if (($choice ne '') && ($choice =~ /^[^\@]+\@[^\@]+$/)) {
949: $email=$choice;
950: $flag=1;
951: } else {
952: print "Invalid input (a valid email address is required).\n";
953: }
954: }
955: return $email;
956: }
957:
958:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>