#!/usr/bin/perl
# The LearningOnline Network
# CrGenerate - Generate a loncapa certificate request.
#
# $Id: CrGenerate.pl,v 1.4 2004/06/30 11:14:35 foxr 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/
#
#
#  This script:
#  1. Generates a private host key and certificate request/
#  2. Decodes the private host key
#  3. Installs the private host key with appropriate permissions
#     in the  appropriate directory (sorry to be vague about this, but
#     the installation directory is determined by external configuration
#     info).
# 4. Constructs an email to the loncapa cluster administrator
#    consisting of a generic heading and the certificate request as a MIME
#    attachment.
# 5. Sends the email and
# 6. Cleans up after itself by removing any temp files generated.
#
#


# Import section:

use strict;
use MIME::Entity;
use Mail::Mailer;
use LONCAPA::Configuration;
use File::Copy;

#  Global variable declarations:4

my $SSLCommand;			  # Full path to openssl command.
my $CertificateDirectory;	  # LONCAPA Certificate directory.
my $KeyFilename;	          # Key filename (within CertificateDirectory).
my $RequestEmail;		  # Email address of loncapa cert admin.
my $WebUID;			# UID of web user.
my $WebGID;			# GID of web user.

my $Passphrase="loncapawhatever";      # Initial passphrase for keyfile
my $RequestFile="loncapaRequest.pem";  # Name of Certificate request file.
my $EncodedKey="hostkey.pem";	       # Name of encoded key file.

my $WebUser="www";		# Username running the web server.
my $WebGroup="www";		# Group name running the web server.

#   Debug/log support:
#
my $DEBUG = 1;			# 1 for on, 0 for off.

# Send debugging to stderr.
# Parameters:
#     msg   - Message to send to stderr.
# Implicit Inputs:
#    $DEBUG - message is only written if this is true.
#
sub Debug {
    my $msg  = shift;
    if($DEBUG) {
	print STDERR "$msg\n";
    }
}

#
#   Read the LonCAPA web config files to get the values of the 
#   configuration global variables we need:
# Implicit inputs:
#   loncapa.conf   - configuration file to read (user specific).
# Implicit outputs (see global variables section):
#   SSLCommand,
#   CertificateDirectory
#   KeyfileName
#   RequestEmail
# Side-Effects:
#   Exit with error if cannot complete.
#
sub ReadConfig {

    Debug("Reading configuration");
    my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf');
    
    # Name of the SSL Program

    if($perlvarref->{SSLProgram}) {
	$SSLCommand = $perlvarref->{SSLProgram};
	Debug("SSL Command: $SSLCommand");
    }
    else {
	die "Unable to read the SSLCommand configuration option\n";
    }

    # Where the certificates, and host key are installed:

    if($perlvarref->{lonCertificateDirectory}) {
	$CertificateDirectory = $perlvarref->{lonCertificateDirectory};
	Debug("Local certificate Directory: $CertificateDirectory");
    }
    else {
	die "Unable to read SSLDirectory configuration option\n";
    }
    # The name of the host key file (to be installed in SSLDirectory).
    #
    if($perlvarref->{lonnetPrivateKey}) {
	$KeyFilename  = $perlvarref->{lonnetPrivateKey};
	Debug("Private key will be installed as $KeyFilename");
    } 
    else {
	die "Unable to read lonnetPrivateKey conrig paraemter\n";
    }
    #  The email address to which the certificate request is sent:

    if($perlvarref->{SSLEmail}) {
	$RequestEmail = $perlvarref->{SSLEmail};
	Debug("Certificate request will be sent to $RequestEmail");
    }
    else {
	die "Could not read SSLEmail coniguration key";
    }
    #  The UID/GID of the web user: It's possible the web user's
    #  GID is not its primary, so we'll translate that form the
    #  group file separately.

    my ($login, $pass, $uid, $gid) = getpwnam($WebUser);
    if($uid) {
	$WebUID = $uid;
	Debug("Web user: $WebUser -> UID: $WebUID");
    }
    else {
	die "Could not translate web user: $WebUser to a uid.";
    }
    my $gid = getgrnam($WebGroup);
    if($gid) {
	$WebGID = $gid;
	Debug("Web group: $WebGroup -> GID $WebGID");
    }
    else {
	die "Unable to translate web group $WebGroup to a gid.";
    }
}
#
#   Generate a certificate request.
#   The openssl command is issued to create a local host key and
#   a certificate request.  The key is initially encoded.
#   We will eventually decode this, however, since the key
#   passphrase is open source we'll protect even the initial 
#   encoded key file too.  We'll need to decode the keyfile since
#   otherwise, openssl will need a passphrase everytime an ssl connection
#   is created (ouch).
# Implicit Inputs:
#    Passphrase   - Initial passphrase for the encoded key.
#    RequestFile  - Filename of the certificate request.
#    EncodedKey   - Filename of the encoded key file.
#
# Side-Effects:
#
sub GenerateRequest {
    Debug("Generating the request and key");

    print "We are now going to generate the certificate request\n";
    print "You will be prompted by openssl for several pieces of \n";
    print "information.  Most of this information is for documentation\n";
    print "purposes only, so it's not critical if you make a mistake.\n";
    print "However:  The generated certificate will be sent to the \n";
    print "Email address you provide, and you should leave the optional\n";
    print "Challenge password blank.\n";

    my $requestcmd = $SSLCommand." req -newkey rsa:1024 "
                                ." -keyout hostkey.pem "
                                ." -keyform PEM "
                                ." -out request.pem "
                                ." -outform PEM "
                                ." -passout pass:$Passphrase";
    my $status = system($requestcmd);
    if($status) {
	die "Certificate request generation failed: $status";
    }

    chmod(0600, "hostkey.pem");	# Protect key since passphrase is opensrc.

    Debug("Decoding the key");
    my $decodecmd = $SSLCommand." rsa -in  hostkey.pem"
                               ."     -out hostkey.dec"
                               ."     -passin pass:$Passphrase";
    my $status = system($decodecmd);
    if($status) {
	die "Host key decode failed";
    }

    chmod(0600, "hostkey.dec");	# Protect the decoded hostkey.
    Debug("Done");
}
#
#  Installs the decoded host key (hostkey.dec) in the 
#  certificate directory with the correct permissions.
#
# Implicit Inputs:
#    hostkey.dec           - the name of the host key file.
#    $CertificateDirectory - where the key file gets installed
#    $KeyFilename          - Final name of the key file.
#    $WebUser              - User who should own the key file.
#    $WebGroup             - Group who should own the key file.
#    0400                  - Permissions to give to the installed key
#                            file.
#    0700                  - Permissions given to the certificate
#                            directory if created.
# Side-Effects:
#    If necessary, $CertificateDirectory is created.
#    $CertificateDirectory/$KeyFilename is ovewritten with the
#          contents of hostkey.dec in the cwd.
#
sub InstallKey {
    Debug("InstallKey");

    Debug("Need to create certificate directory?");
    if(!(-d $CertificateDirectory)) {
	
	Debug("Creating");
	mkdir($CertificateDirectory, 0700);
	chown($WebUID, $WebGID, $CertificateDirectory);
    }
    else {
	Debug("Exists");
    }

    Debug("Installing the key file:");
    my $FullKeyPath = $CertificateDirectory."/".$KeyFilename;
    copy("hostkey.dec", $FullKeyPath);

    Debug("Setting ownership and permissions");
    chmod(0400, $FullKeyPath);
    chown($WebUID, $WebGID, $FullKeyPath);

    Debug("Done");
}
sub MailRequest {}
sub Cleanup {}



#  Entry point:

Debug("Starting program");
ReadConfig;			# Read loncapa apache config file.
GenerateRequest;		# Generate certificate request.
InstallKey;			# Install the user's key.
MailRequest;			# Mail certificate request to loncapa 
Cleanup;			# Cleanup temp files created.

Debug("Done");
