File:  [LON-CAPA] / loncom / manage_ssl_certs.pl
Revision 1.1: download - view: text, annotated - select for diffs
Sat Aug 18 23:33:30 2018 UTC (5 years, 7 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, HEAD
- LON-CAPA SSL PKI for connections to other nodes, and replication of
  published content from other nodes.
  - script to create new SSL key and/or SSL certs (host cert and/or
    hostname certs

#!/usr/bin/perl
$|=1;
# Displays status of LON-CAPA SSL certs, and allows new certificate
# signing requests to be created and e-mailed to CA for cluster to
# which server/VM belongs. 
# $Id: manage_ssl_certs.pl,v 1.1 2018/08/18 23:33:30 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

use strict;
use lib '/home/httpd/lib/perl/';
use LONCAPA::Configuration;
use LONCAPA::Lond;
use LONCAPA::SSL;
use LONCAPA;
use Term::ReadKey;
use Locale::Country;

my ($lonCluster,$domainDescription,$hostname,$certmail,
    $certsdir,$privkey,$connectcsr,$replicatecsr);

print(<<END);

===============================================================================

Which type of LON-CAPA cluster does this server/VM belong to?
IMPORTANT: to take advantage of the cluster options 1) and 3),
you must have been or expect to be accepted into the cluster.
Contact loncapa\@loncapa.org for access.

1) PRODUCTION - you have (or will) connect this machine to the
                LON-CAPA content sharing network. This setting is for
                schools, colleges, and universities, that currently
                are running - or in the future will run - courses.
2) STAND-ALONE - you want this machine to run in 'stand-alone' mode and
                 not be connected to other LON-CAPA machines.
3) DEVELOPMENT - you want to do software (not content!) development with
                 this workstation and eventually link it with 
                 workstations of other LON-CAPA software developers.
4) RUNNING YOUR OWN CLUSTER - this machine is not in the standard LON-CAPA
                 cluster and won't be in the future.
                 (This choice is unlikely what you want to select.)
END

my $flag=0;
while (!$flag) {
    print "ENTER 1, 2, 3, or 4:\n";
    my $choice=<>;
    chomp($choice);
    if ($choice==1) {
        $lonCluster='production'; $flag=1;
    } elsif ($choice==2) {
        $lonCluster='standalone'; $flag=1;
    } elsif ($choice==3) {
        $lonCluster='development'; $flag=1;
    } elsif ($choice==4) {
        $lonCluster='existing'; $flag=1;
        my $earlyout;
        foreach my $file ('hosts.tab','dns_hosts.tab',
                          'domain.tab','dns_domain.tab') {
            unless (-e '/home/httpd/lonTabs/'.$file) {
                print <<END;
There is no existing /home/httpd/lonTabs/$file
END
                $earlyout = 1;
            }
        }
        if ($earlyout) {
            exit;
        }
    }
}

$flag = 0;
my $dist=`/home/httpd/perl/distprobe`;
my $confdir = '/etc/httpd/conf/';
if ($dist =~ /^(sles|suse|ubuntu|debian)/) {
    $confdir = '/etc/apache2/';
}

my $filename='loncapa.conf';
my %perlvar;
if (-e "$confdir$filename") {
    if (open(CONFIG,'<',$confdir.$filename) or die("Can't read $confdir$filename")) {
        while (my $configline=<CONFIG>) {
            if ($configline =~ /^[^\#]*PerlSetVar/) {
                my ($unused,$varname,$varvalue)=split(/\s+/,$configline);
                chomp($varvalue);
                $perlvar{$varname}=$varvalue if $varvalue!~/^\{\[\[\[\[/;
            }
        }
        close(CONFIG);
    }
}

my $perlstaticref = &get_static_config($confdir);
if (ref($perlstaticref) ne 'HASH') {
    exit;
}

if (open(IN,'<','/home/httpd/lonTabs/domain.tab')) {
    while(my $line = <IN>) {
       if ($line =~ /^\Q$perlvar{'lonDefDomain'}\E\:/) {
           (undef,$domainDescription)=split(/:/,$line);
           chomp($domainDescription);
           last;
        }
    }
    close(IN);
}

if (open(IN,'<','/home/httpd/lonTabs/hosts.tab')) {
    while(my $line = <IN>) {
       if ($line =~ /^\Q$perlvar{'lonHostID'}\E\:/) {
           (undef,undef,undef,$hostname)=split(/:/,$line);
           last;
        }
    }
    close(IN);
}

$certsdir = $perlstaticref->{'lonCertificateDirectory'};
$privkey = $perlstaticref->{'lonnetPrivateKey'};
$connectcsr = $perlstaticref->{'lonnetCertificate'};
$connectcsr =~ s/\.pem$/.csr/;
$replicatecsr = $perlstaticref->{'lonnetHostnameCertificate'};
$replicatecsr =~ s/\.pem$/.csr/;


$certmail = &get_mail();

print "\nRetrieving status information for SSL key and certificates ...\n\n";
my ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
    &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
print $certinfo;
my %sslstatus;
if (ref($sslref) eq 'HASH') {
    %sslstatus = %{$sslref};
}
while (!$flag) {
    print(<<END);

===============================================================================

This is now the current configuration of your machine.
1) Private Key for SSL: $lonkeystatus
2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
3) SSL Certificate for Content Replication: $lonhostnamecertstatus
4) Everything is correct up above

ENTER A CHOICE OF 1 TO 3 TO CHANGE, OTHERWISE ENTER 4:
END

    my $choice=<>;
    chomp($choice);
    if ($choice==1) {
        if ($sslstatus{'key'} == 1) {
            print(<<END);
1) Private Key for SSL: $lonkeystatus

POSSIBLE CHOICES:
1) overwrite existing key
2) make no change
ENTER NEW VALUE
END
            my $choice2=<>;
            chomp($choice2);
            if ($choice2 eq '1') {
                my $sslkeypass = &get_new_sslkeypass();
                &make_key($certsdir,$privkey,$sslkeypass);
            }
        } elsif ($sslstatus{'key'} == 0) {
            print(<<END);
1) Private Key for SSL: $lonkeystatus
END
            my $sslkeypass = &get_new_sslkeypass();
            &make_key($certsdir,$privkey,$sslkeypass);
            print "\nRetrieving status information for SSL key and certificates ...\n\n";
            ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
                &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
            if (ref($sslref) eq 'HASH') {
                %sslstatus = %{$sslref};
            }
        }
    } elsif ($choice==2) {
        if (($sslstatus{'host'} == 1) || ($sslstatus{'host'} == 2) || ($sslstatus{'host'} == 3)) {
            print(<<END);
2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus

POSSIBLE CHOICES:
1) create new certificate signing request with new key
2) create new certificate signing request with existing key
3) resend current certificate signing request
4) make no change
ENTER NEW VALUE
END

            my $choice2=<>;
            chomp($choice2);
            if (($choice2 eq '1') || ($choice2 eq '2')) {
                &ssl_info();
                my $country = &get_country($hostname);
                my $state = &get_state();
                my $city = &get_city();
                my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$certmail";
                ($domainDescription,$country,$state,$city) = &confirm_locality($domainDescription,$country,$state,$city);
                my $sslkeypass;
                if ($choice2 eq '1') {
                    $sslkeypass = &get_new_sslkeypass();
                    &make_key($certsdir,$privkey,$sslkeypass);
                } elsif ($choice2 eq '2') {
                    $sslkeypass = &get_password('Enter existing password for SSL key');
                    &encrypt_key($certsdir,$privkey,$sslkeypass);
                }
                &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj);
                &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
                print "\nRetrieving status information for SSL key and certificates ...\n\n";
                ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
                    &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
                if (ref($sslref) eq 'HASH') {
                    %sslstatus = %{$sslref};
                }
            } elsif ($choice2 eq '3') {
                if (-e "$certsdir/$connectcsr") {
                    &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
                }
            }
        } elsif (($sslstatus{'host'} == 0) || ($sslstatus{'host'} == 4) || ($sslstatus{'host'} == 5)) {
            my $sslkeypass;
            if ($sslstatus{'key'} == 1) {
                print(<<END);
2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus

POSSIBLE CHOICES:
1) create new certificate signing request with new key
2) create new certificate signing request with existing key
3) make no change
ENTER NEW VALUE
END
                my $choice2=<>;
                chomp($choice2);
                if ($choice2 eq '1') {
                    $sslkeypass = &get_new_sslkeypass();
                    &make_key($certsdir,$privkey,$sslkeypass);
                } elsif ($choice2 eq '2') {
                    $sslkeypass = &get_password('Enter existing password for SSL key');
                    &encrypt_key($certsdir,$privkey,$sslkeypass);
                }
            } else {
                print(<<END);
2) SSL Certificate for LON-CAPA server connections: $lonhostcertstatus
END
                $sslkeypass = &get_new_sslkeypass();
            }
            &ssl_info();
            my $country = &get_country($hostname);
            my $state = &get_state();
            my $city = &get_city();
            my $connectsubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=$perlvar{'lonHostID'}/OU=LONCAPA/emailAddress=$certmail";
            &make_host_csr($certsdir,$sslkeypass,$connectcsr,$connectsubj);
            &mail_csr('host',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
            print "\nRetrieving status information for SSL key and certificates ...\n\n";
            ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
                &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
            if (ref($sslref) eq 'HASH') {
                %sslstatus = %{$sslref};
            }
        }
    } elsif ($choice==3) {
        if (($sslstatus{'hostname'} == 1) || ($sslstatus{'hostname'} == 2) || ($sslstatus{'hostname'} == 3)) {
            print(<<END);
3) SSL Certificate for Content Replication: $lonhostnamecertstatus

POSSIBLE CHOICES:
1) create new certificate signing request with new key
2) create new certificate signing request with existing key
3) resend current certificate signing request
4) make no change
ENTER NEW VALUE
END
            my $choice2=<>;
            chomp($choice2);
            if (($choice2 eq '1') || ($choice2 eq '2')) {
                &ssl_info();
                my $country = &get_country($hostname);
                my $state = &get_state();
                my $city = &get_city();
                my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$hostname/OU=LONCAPA/emailAddress=$certmail";
                my $sslkeypass;
                if ($choice2 eq '1') {
                    $sslkeypass = &get_new_sslkeypass();
                    &make_key($certsdir,$privkey,$sslkeypass);
                } elsif ($choice2 eq '2') {
                    $sslkeypass = &get_password('Enter existing password for SSL key');
                    &encrypt_key($certsdir,$privkey,$sslkeypass);
                }
                &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj);
                &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
                print "\nRetrieving status information for SSL key and certificates ...\n\n";
                ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
                    &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
                if (ref($sslref) eq 'HASH') {
                    %sslstatus = %{$sslref};
                }
            } elsif ($choice2 eq '3') {
                if (-e "$certsdir/$replicatecsr") {
                    &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
                }
            }
        } elsif (($sslstatus{'hostname'} == 0) || ($sslstatus{'hostname'} == 4) || ($sslstatus{'hostname'} == 5)) {
            my $sslkeypass;
            if ($sslstatus{'key'} == 1) {
                print(<<END);
3) SSL Certificate for Content Replication: $lonhostnamecertstatus

POSSIBLE CHOICES:
1) create new certificate signing request with new key
2) create new certificate signing request with existing key
3) make no change
ENTER NEW VALUE
END
                my $choice2=<>;
                chomp($choice2);
                if ($choice2 eq '1') {
                    $sslkeypass = &get_new_sslkeypass();
                    &make_key($certsdir,$privkey,$sslkeypass);
                } elsif ($choice2 eq '2') {
                    $sslkeypass = &get_password('Enter existing password for SSL key');
                    &encrypt_key($certsdir,$privkey,$sslkeypass);
                }
            } else {
                print(<<END);
3) SSL Certificate for Content Replication: $lonhostnamecertstatus
END
                $sslkeypass = &get_new_sslkeypass();
            }
            &ssl_info();
            my $country = &get_country($hostname);
            my $state = &get_state();
            my $city = &get_city();
            my $replicatesubj = "/C=$country/ST=$state/O=$domainDescription/L=$city/CN=internal-$hostname/OU=LONCAPA/emailAddress=$certmail";
            &make_hostname_csr($certsdir,$sslkeypass,$replicatecsr,$replicatesubj);
            &mail_csr('hostname',$lonCluster,$perlvar{'lonHostID'},$hostname,$certsdir,$connectcsr,$replicatecsr,$perlstaticref);
            print "\nRetrieving status information for SSL key and certificates ...\n\n";
            ($certinfo,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,$sslref) =
                &get_cert_status($perlvar{'lonHostID'},$hostname,$perlstaticref);
            if (ref($sslref) eq 'HASH') {
                %sslstatus = %{$sslref};
            }
        }
    } elsif ($choice==4) {
        $flag=1;
    } else {
       print "Invalid input.\n";
    }
}

exit;

sub get_static_config {
    my ($confdir) = @_;
    my $filename='loncapa_apache.conf';
    my %LCperlvar;
    if (-e "$confdir$filename") {
        open(CONFIG,'<',$confdir.$filename) or die("Can't read $confdir$filename");
        while (my $configline=<CONFIG>) {
            if ($configline =~ /^[^\#]?PerlSetVar/) {
                my ($unused,$varname,$varvalue)=split(/\s+/,$configline);
                chomp($varvalue);
                $LCperlvar{$varname}=$varvalue;
            }
        }
        close(CONFIG);
    }
    return \%LCperlvar;
}

sub get_sslnames {
    my %sslnames = (
                      key      => 'lonnetPrivateKey',
                      host     => 'lonnetCertificate',
                      hostname => 'lonnetHostnameCertificate',
                      ca       => 'lonnetCertificateAuthority',
                   );
    return %sslnames;
}

sub get_ssldesc {
    my %ssldesc = (
                    key      => 'Private Key',
                    host     => 'Connections Certificate',
                    hostname => 'Replication Certificate',
                    ca       => 'LON-CAPA CA Certificate',
                  );
    return %ssldesc;
}

sub get_cert_status {
    my ($lonHostID,$hostname,$perlvarstatic) = @_;
    my $currcerts = &LONCAPA::SSL::print_certstatus({$lonHostID => $hostname,},'text','cgi');
    my ($lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,%sslstatus);
    my $output = '';
    if ($currcerts eq "$lonHostID:error") {
        $output .= "No information available for SSL certificates\n";
        $sslstatus{'key'} = -1;
        $sslstatus{'host'} = -1;
        $sslstatus{'hostname'} = -1;
        $sslstatus{'ca'} = -1;
        $lonkeystatus = 'unknown status';
        $lonhostcertstatus = 'unknown status';
        $lonhostnamecertstatus = 'unknown status';
    } else {
        my %sslnames = &get_sslnames();
        my %ssldesc = &get_ssldesc();
        my %csr;
        my ($lonhost,$info) = split(/\:/,$currcerts,2);
        if ($lonhost eq $lonHostID) {
            my @items = split(/\&/,$info);
            foreach my $item (@items) {
                my ($key,$value) = split(/=/,$item,2);
                if ($key =~ /^(host(?:|name))\-csr$/) {
                    $csr{$1} = $value;
                }
                my @data = split(/,/,$value);
                if (grep(/^\Q$key\E$/,keys(%sslnames))) {
                    my ($checkcsr,$comparecsr);
                    if (lc($data[0]) eq 'yes') {
                        $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." available with status = $data[1]\n";
                        if ($key eq 'key') {
                            $lonkeystatus = "status: $data[1]";
                            if ($data[1] =~ /ok$/) {
                                $sslstatus{$key} = 1;
                            }
                        } else {
                            my $setstatus;
                            if (($key eq 'host') || ($key eq 'hostname')) {
                                if ($data[1] eq 'otherkey') {
                                    $sslstatus{$key} = 4;
                                    $setstatus = 1;
                                    if ($key eq 'host') {
                                        $lonhostcertstatus = "status: created with different key";
                                    } elsif ($key eq 'hostname') {
                                        $lonhostnamecertstatus = "status: created with different key";
                                    }
                                } elsif ($data[1] eq 'nokey') {
                                    $sslstatus{$key} = 5;
                                    $setstatus = 1;
                                    if ($key eq 'host') {
                                        $lonhostcertstatus = "status: created with missing key";
                                    } elsif ($key eq 'hostname') {
                                        $lonhostnamecertstatus = "status: created with missing key";
                                    }
                                }
                                if ($setstatus) {
                                    $comparecsr = 1;
                                }
                            }
                            unless ($setstatus) {
                                if ($data[1] eq 'expired') {
                                    $sslstatus{$key} = 2;
                                    if (($key eq 'host') || ($key eq 'hostname')) {
                                        $comparecsr = 1;
                                    }
                                } elsif ($data[1] eq 'future') {
                                    $sslstatus{$key} = 3;
                                    $sslstatus{$key} = 3;
                                } else {
                                    $sslstatus{$key} = 1;
                                }
                                if ($key eq 'host') {
                                    $lonhostcertstatus = "status: $data[1]";
                                } elsif ($key eq 'hostname') {
                                    $lonhostnamecertstatus = "status: $data[1]";
                                }
                            }
                        }
                    } else {
                        $sslstatus{$key} = 0;
                        $output .= "$ssldesc{$key} ".$perlvarstatic->{$sslnames{$key}}." not available\n";
                        if ($key eq 'key') {
                            $lonkeystatus = 'still needed';
                        } elsif (($key eq 'host') || ($key eq 'hostname')) {
                            $checkcsr = 1;
                        }
                    }
                    if (($checkcsr) || ($comparecsr)) {
                        my $csrfile = $perlvarstatic->{$sslnames{$key}};
                        $csrfile =~s /\.pem$/.csr/;
                        my $csrstatus;
                        if (-e $perlvarstatic->{'lonCertificateDirectory'}."/$csrfile") {
                            if (open(PIPE,"openssl req -text -noout -verify -in ".$perlvarstatic->{'lonCertificateDirectory'}."/$csrfile 2>&1 |")) {
                                while(<PIPE>) {
                                    chomp();
                                    $csrstatus = $_;
                                    last;
                                }
                                close(PIPE);
                                if ($comparecsr) {
                                    my $csrhash;
                                    if (open(PIPE,"openssl x509 -in certificate.crt -pubkey -noout -outform pem | sha256sum")) {
                                        $csrhash = <PIPE>;
                                        close(PIPE);
                                    }
                                }
                            }
                            $output .= "Certificate signing request for $ssldesc{$key} available with status = $csrstatus\n\n";
                            if ($key eq 'host') {
                                $lonhostcertstatus = 'awaiting signature';
                            } else {
                                $lonhostnamecertstatus = 'awaiting signature';
                            }
                            $sslstatus{$key} = 3;
                        } elsif ($checkcsr) {
                            $output .= "No certificate signing request available for $ssldesc{$key}\n\n";
                            if ($key eq 'host') {
                                $lonhostcertstatus = 'still needed';
                            } else {
                                $lonhostnamecertstatus = 'still needed';
                            }
                        }
                    }
                }
            }
# 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.
        }
    }
    return ($output,$lonkeystatus,$lonhostcertstatus,$lonhostnamecertstatus,\%sslstatus);
}

sub get_new_sslkeypass {
   my $sslkeypass;
    my $flag=0;
# get Password for SSL key
    while (!$flag) {
        $sslkeypass = &make_passphrase();
        if ($sslkeypass) {
            $flag = 1;
        } else {
            print "Invalid input (a password is required for the SSL key).\n";
        }
    }
    return $sslkeypass;
}

sub make_passphrase {
    my ($got_passwd,$firstpass,$secondpass,$passwd);
    my $maxtries = 10;
    my $trial = 0;
    while ((!$got_passwd) && ($trial < $maxtries)) {
        $firstpass = &get_password('Enter a password for the SSL key (at least 6 characters long)');
        if (length($firstpass) < 6) {
            print('Password too short.'."\n".
              'Please choose a password with at least six characters.'."\n".
              'Please try again.'."\n");
        } elsif (length($firstpass) > 30) {
            print('Password too long.'."\n".
                  'Please choose a password with no more than thirty characters.'."\n".
                  'Please try again.'."\n");
        } else {
            my $pbad=0;
            foreach (split(//,$firstpass)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
            if ($pbad) {
                print('Password contains invalid characters.'."\n".
                      'Password must consist of standard ASCII characters.'."\n".
                      'Please try again.'."\n");
            } else {
                $secondpass = &get_password('Enter password a second time');
                if ($firstpass eq $secondpass) {
                    $got_passwd = 1;
                    $passwd = $firstpass;
                } else {
                    print('Passwords did not match.'."\n".
                          'Please try again.'."\n");
                }
            }
        }
        $trial ++;
    }
    return $passwd;
}

sub get_password {
    my ($prompt) = @_;
    local $| = 1;
    print $prompt.': ';
    my $newpasswd = '';
    ReadMode 'raw';
    my $key;
    while(ord($key = ReadKey(0)) != 10) {
        if(ord($key) == 127 || ord($key) == 8) {
            chop($newpasswd);
            print "\b \b";
        } elsif(!ord($key) < 32) {
            $newpasswd .= $key;
            print '*';
        }
    }
    ReadMode 'normal';
    print "\n";
    return $newpasswd;
}

sub send_mail {
    my ($hostname,$recipient,$subj,$file) = @_;
    my $from = 'www@'.$hostname;
    my $certmail = "To: $recipient\n".
                   "From: $from\n".
                   "Subject: ".$subj."\n".
                   "Content-type: text/plain\; charset=UTF-8\n".
                   "MIME-Version: 1.0\n\n";
    if (open(my $fh,"<$file")) {
        while (<$fh>) {
            $certmail .= $_;
        }
        close($fh);
        $certmail .= "\n\n";
        if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) {
            print $mailh $certmail;
            close($mailh);
            print "Mail sent ($subj) to $recipient\n";
        } else {
            print "Sending mail ($subj) to $recipient failed.\n";
        }
    }
    return;
}

sub mail_csr {
    my ($types,$lonCluster,$lonHostID,$hostname,$certsdir,$connectcsr,$replicatecsr,$perlvarref) = @_;
    my ($camail,$flag);
    if ($lonCluster eq 'production' || $lonCluster eq 'development') {
        $camail = $perlvarref->{'SSLEmail'};
    } else {
        $flag=0;
# get Certificate Authority E-mail
        while (!$flag) {
            print(<<END);

ENTER EMAIL ADDRESS TO SEND CERTIFICATE SIGNING REQUESTS
END

            my $choice=<>;
            chomp($choice);
            if ($choice ne '') {
                open(OUT,'>>/tmp/loncapa_updatequery.out');
                print(OUT 'Certificate Authority Email Address'."\t".$choice."\n");
                close(OUT);
                $camail=$choice;
                $flag=1;
            } else {
                print "Invalid input (an email address is required).\n";
            }
        }
    }
    if ($camail) {
        my $subj;
        if (($types eq 'both') || ($types = 'host')) {
            if (-e "$certsdir/$connectcsr") {
                $subj = "Certificate Request ($lonHostID)";
                print(&send_mail($hostname,$camail,$subj,"$certsdir/$connectcsr"));
            }
        }
        if (($types eq 'both') || ($types = 'hostname')) {
            if (-e "$certsdir/$replicatecsr") {
                $subj = "Certificate Request (internal-$hostname)";
                print(&send_mail($hostname,$camail,$subj,"$certsdir/$replicatecsr"));
            }
        }
    }
}

sub ssl_info {
    print(<<END);

****** Information about Country, State or Province and City *****

A two-letter country code, e.g., US, CA, DE etc. as defined by ISO 3166,
is required. A state or province, and a city are also required.
This locality information is included in two SSL certificates used internally
by LON-CAPA, unless you are running standalone.

If your server will be part of either the production or development
clusters, then the certificate will need to be signed by the official
LON-CAPA Certificate Authority (CA).  If you will be running your own
cluster then the cluster will need to create its own CA.

END
}

sub get_country {
    my ($hostname) = @_;
# get Country
    my ($posscountry,$country);
    if ($hostname =~ /\.(edu|com|org)$/) {
        $posscountry = 'us';
    } else {
        ($posscountry) = ($hostname =~ /\.(a-z){2}$/);
    }
    if ($posscountry) {
        my $countrydesc = &Locale::Country::code2country($posscountry);
        if ($countrydesc eq '') {
            undef($posscountry);
        }
    }

    my $flag=0;
    while (!$flag) {
        if ($posscountry) {
            $posscountry = uc($posscountry);
            print "ENTER TWO-LETTER COUNTRY CODE [$posscountry]:\n";
        } else {
            print "ENTER TWO-LETTER COUNTRY CODE:\n";
        }
        my $choice=<>;
        chomp($choice);
        if ($choice ne '') {
            if (&Locale::Country::code2country(lc($choice))) {
                open(OUT,'>>/tmp/loncapa_updatequery.out');
                print(OUT 'country'."\t".uc($choice)."\n");
                close(OUT);
                $country=uc($choice);
                $flag=1;
            } else {
                print "Invalid input -- a valid two letter country code is required\n";
            }
        } elsif (($choice eq '') && ($posscountry ne '')) {
            open(OUT,'>>/tmp/loncapa_updatequery.out');
            print(OUT 'country'."\t".$posscountry."\n");
            close(OUT);
            $country = $posscountry;
            $flag = 1;
        } else {
            print "Invalid input -- a country code is required\n";
        }
    }
    return $country;
}

sub get_state {
# get State or Province
    my $flag=0;
    my $state = '';
    while (!$flag) {
        print(<<END);

ENTER STATE OR PROVINCE NAME:
END

        my $choice=<>;
        chomp($choice);
        if ($choice ne '') {
            open(OUT,'>>/tmp/loncapa_updatequery.out');
            print(OUT 'state'."\t".$choice."\n");
            close(OUT);
            $state=$choice;
            $flag=1;
        } else {
            print "Invalid input (a state or province name is required).\n";
        }
    }
    return $state;
}

sub get_city {
# get City
    my $flag=0;
    my $city = '';
    while (!$flag) {
        print(<<END);

ENTER CITY NAME:
END

        my $choice=<>;
        chomp($choice);
        if ($choice ne '') {
            open(OUT,'>>/tmp/loncapa_updatequery.out');
            print(OUT 'city'."\t".$choice."\n");
            close(OUT);
            $city=$choice;
            $flag=1;
        } else {
            print "Invalid input (a city is required).\n";
        }
    }
    return $city;
}

sub confirm_locality {
    my ($domainDescription,$country,$state,$city) = @_;
    my $flag = 0;
    while (!$flag) {
        print(<<END);

The domain description, country, state and city will be
used in the SSL certificates

1) Domain Description: $domainDescription
2) Country: $country
3) State or Province: $state
4) City: $city
5) Everything is correct up above

ENTER A CHOICE OF 1-4 TO CHANGE, otherwise ENTER 5:
END
        my $choice=<>;
        chomp($choice);
        if ($choice == 1) {
            print(<<END);
1) Domain Description: $domainDescription
ENTER NEW VALUE
END
            my $choice2=<>;
            chomp($choice2);
            $domainDescription=$choice2;
        } elsif ($choice == 2) {
            print(<<END);
2) Country: $country
ENTER NEW VALUE (this should be a two-character code, e,g, US, CA, DE)
END
            my $choice2=<>;
            chomp($choice2);
            $country = uc($choice2);
        } elsif ($choice == 3) {
            print(<<END);
3) State or Province: $state
ENTER NEW VALUE:
END
            my $choice2=<>;
            chomp($choice2);
            $state=$choice2;
        } elsif ($choice == 4) {
            print(<<END);
4) City: $city
ENTER NEW VALUE:
END
            my $choice2=<>;
            chomp($choice2);
            $city=$choice2;
        } elsif ($choice == 5) {
            $flag=1;
            $state =~ s{/}{ }g;
            $city =~ s{/}{ }g;
            $domainDescription =~ s{/}{ }g;
        } else {
            print "Invalid input.\n";
        }
    }
    return ($domainDescription,$country,$state,$city);
}

sub make_key {
    my ($certsdir,$privkey,$sslkeypass) = @_;
# generate SSL key
    if ($certsdir && $privkey) {
        if (-f "$certsdir/lonKey.enc") {
            my $mode = 0600;
            chmod $mode, "$certsdir/lonKey.enc";
        }
        open(PIPE,"openssl genrsa -des3 -passout pass:$sslkeypass -out $certsdir/lonKey.enc 2048 2>&1 |");
        close(PIPE);
        if (-f "$certsdir/$privkey") {
            my $mode = 0600;
            chmod $mode, "$certsdir/$privkey";
        }
        open(PIPE,"openssl rsa -in $certsdir/lonKey.enc -passin pass:$sslkeypass -out $certsdir/$privkey -outform PEM |");
        close(PIPE);
        if (-f "$certsdir/lonKey.enc") {
            my $mode = 0400;
            chmod $mode, "$certsdir/lonKey.enc";
        }
        if (-f "$certsdir/$privkey") {
            my $mode = 0400;
            chmod $mode, "$certsdir/$privkey";
        }
    } else {
        print "Key creation failed.  Missing one or more of: certificates directory, key name\n";
    }
}

sub encrypt_key {
    my ($certsdir,$privkey,$sslkeypass) = @_;
    if ($certsdir && $privkey) {
        if ((-f "$certsdir/$privkey") && (!-f "$certsdir/lonKey.enc")) {
            open(PIPE,"openssl rsa -des3 -in $certsdir/$privkey -out $certsdir/lonKey.enc |");
        }
    }
    return;
}

sub make_host_csr {
    my ($certsdir,$sslkeypass,$connectcsr,$connectsubj) = @_;
# generate SSL csr for hostID
    if ($certsdir && $connectcsr && $connectsubj) {
        open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$connectsubj\" -out $certsdir/$connectcsr |");
        close(PIPE);
    } else {
        print "Creation of certificate signing request failed.  Missing one or more of: certificates directory, CSR name, or locality information.\n";
    }
}

sub make_hostname_csr {
    my ($certsdir,$sslkeypass,$replicatecsr,$replicatesubj) = @_;
# generate SSL csr for internal hostname
    if ($certsdir && $replicatecsr && $replicatesubj) {
        open(PIPE,"openssl req -key $certsdir/lonKey.enc -passin pass:$sslkeypass -new -batch -subj \"$replicatesubj\" -out $certsdir/$replicatecsr |");
        close(PIPE);
    } else {
        print "Creation of certificate signing request failed.  Missing one or more of: certificates directory, CSR name, or locality information.\n";
    }
}

sub get_mail {
    my $email;
    my $flag=0;
# get E-mail Address 
    while (!$flag) {
        print(<<END);

An e-mail address to be included with certificate signing requests is needed.
After signing by the Certificate Authority, the signed certificate(s) will
be returned to this e-mail address.
ENTER E-MAIL ADDRESS
END
        my $choice=<>;
        chomp($choice);
        if (($choice ne '') && ($choice =~ /^[^\@]+\@[^\@]+$/)) {
            $email=$choice;
            $flag=1;
        } else {
            print "Invalid input (a valid email address is required).\n";
        }
    }
    return $email;
}



FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>