Annotation of loncom/manage_ssl_certs.pl, revision 1.1

1.1     ! raeburn     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 19:00:00 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>