--- loncom/CrGrant.pl 2004/07/05 11:37:39 1.2 +++ loncom/CrGrant.pl 2009/02/17 21:18:07 1.6 @@ -2,7 +2,7 @@ # The LearningOnline Network # CrGrant.pl - Grant a loncapa SSL certificate. # -# $Id: CrGrant.pl,v 1.2 2004/07/05 11:37:39 foxr Exp $ +# $Id: CrGrant.pl,v 1.6 2009/02/17 21:18:07 schafran Exp $ # # Copyright Michigan State University Board of Trustees # @@ -66,7 +66,7 @@ # Import section: use strict; -use lib '/home/httpd/lib/perl'; +use lib '/home/httpd/lib/perl'; # An assumption!!! use MIME::Entity; use LONCAPA::Configuration; @@ -74,16 +74,33 @@ use LONCAPA::Configuration; # Global variable declarations -my $ssl_command = "/usr/bin/openssl "; # Command to run openssl. + my $ssl_dir = "/usr/share/ssl"; # Where ssl config files etc. live -my $ca_cert_file = $ssl_dir."/loncapa/cacert.pem"; # CA's certificate file. +my $ca_cert_file = $ssl_dir."/loncapaca/cacert.pem"; # CA's certificate file. my $ca_config_file= $ssl_dir."/loncapaca.cnf"; # CA's config file. - + +# LONCAPA Configuration global variables: + +# Items read from our configuration file. + +my $ssl_command = "/usr/bin/openssl "; # Command to run openssl. +my $loncapa_cert_dir; # Name of target cert dir (from config) +my $loncapa_hostcert_name; # Name of host's signed cert file (config) +my $loncapa_cacert_name; # Name of the CA's certificate file (config) +my $return_address; # Email return address. + +# Items I just need to know: + +my $loncapa_config = "loncapa.conf"; # User's override config file. +my $loncapa_apache_user = 'www'; # Name of apache daemon's user +my $loncapa_apache_group = 'www'; # Name of apache daemon's group + + # Debug/log support -my $DEBUG=1; +my $DEBUG=0; sub Debug { my $msg = shift; @@ -111,6 +128,75 @@ Where: USAGE } +# +# Read the loncapa configuration file and pull out the items +# we need: +# +# Implicit inputs: +# $loncapa_config - The name of the auxilliary config file. +# Side effects: +# - On failure exits with an error message. +# - On success set the following variables: +# o loncapa_cert_dir - Path to certificates. +# o loncapa_hostcert_name - Name of host's cert file in that dir +# o loncapa_cacert_name - Name of CA's cert file in that dir. +# o ssl_command - Name of ssl utility command. +sub ReadConfig { + Debug("Reading the config files"); + my $perlvarref = LONCAPA::Configuration::read_conf($loncapa_config); + + # Pull out the individual variables or die: + + # SSL Command: + + if($perlvarref->{SSLProgram}) { + $ssl_command = $perlvarref->{SSLProgram}; + Debug("SSL utility program is $ssl_command"); + } + else { + die "LonCAPA configuration errror: Can't read SSLProgram variable"; + } + # Certificate directory: + + if($perlvarref->{lonCertificateDirectory}) { + $loncapa_cert_dir = $perlvarref->{lonCertificateDirectory}; + Debug("Certificates will be installed in $loncapa_cert_dir"); + } + else { + die "LonCAPA configuration error can't read lonCertificateDirectory variable"; + + } + # Get the name of the host's certificate: + + if($perlvarref->{lonnetCertificate}) { + $loncapa_hostcert_name = $perlvarref->{lonnetCertificate}; + Debug("Host's certificate will be $loncapa_hostcert_name"); + } + else { + die "LonCAPA configuration error: Can't read lonnetCertificate variable"; + } + # Get the name of the certificate authority's certificate. + + if($perlvarref->{lonnetCertificateAuthority}) { + $loncapa_cacert_name = $perlvarref->{lonnetCertificateAuthority}; + Debug("CA's certificate will be $loncapa_cacert_name"); + } + else { + die "LonCAPA configuration error: Can't read lonnetCertificateAuthority variable"; + } + # Get the email address of the certificate manager: + # this is the email return address: + + if($perlvarref->{SSLEmail}) { + $return_address = $perlvarref->{SSLEmail}; + Debug("Return address will be $return_address"); + } + else { + die "LonCAPA configuration error can't read SSLEmail configuration item"; + } + +} + # Create a certificate from the request file. The certificate # is used, in conjunction with the openssl command with the # certificate authority configuration to produce a certificate @@ -214,16 +300,210 @@ sub CreateCertificate { return $address; } -sub CreateInstallScript {} +# +# Create the installation script. This will be bash script +# that will install the certifiate and the CA's certificate with ownership +# WebUser:WebGroup and permissions 0400. I thought about using a perl +# script in order to be able to get the certificate file/directory from +# the configuration files. Unfortunately this is not as easy as it looks. +# Root has a chicken and egg problem. In order to read the config file +# you need to have added the ..../lib/perl to the perl lib path. To do +# that correctly, you need to have read the config file to know where +# it is...What we will do is read our local configuration file and +# assume that our configuration is the same as the target's system in +# all respects we care about. +# Implicit Inputs: +# - Bash is in /bin/bash +# - $loncapa_cert_dir - install target directory. +# - $loncapa_hostcert_name - Name of installed host cert file. +# - $loncapa_cacert_name - Name of installed ca cert file. +# - $loncapa_apache_user - username under which httpd runs. +# - $loncapa_apache_group - group under which httpd runs. +# - 0400 - install permissions. +# - The host's certificate is now in ./hostCertificate.pem +# - The CA's certificate is now in $ca_cert_file +# +# Implicit Outputs: +# A file named CertInstall.sh +# Return +# Name of the file we created. +# +sub CreateInstallScript { + open INSTALLER,">CertInstall.sh"; + print INSTALLER <\$HOSTCERT +BASH_HEADER + + # Now copy the host certificate into the script: + + open HOSTCERT, ") { + print INSTALLER $line; # Line presumably has a \n. + } + close HOSTCERT; + + # Close the here doc, and start up the cat of the ca cert: + + print INSTALLER "HOSTCERTTEXT\n"; + print INSTALLER "echo unpacking CA certificate\n"; + print INSTALLER "cat <<-CACERTTEXT >\$CACERT\n"; + open CACERT, "<$ca_cert_file"; + while(my $line = ) { + print INSTALLER $line; + } + close CACERT; + print INSTALLER "CACERTTEXT\n"; + + # Ok, the script can create the two files, now it must install + # install them >and< clean up after itself. + + print INSTALLER <build(Type => "multipart/mixed", + From => $return_address, + To => $send_address, + Subject =>"LonCAPA certificates"); + if(!$mime_message) { + die "Unable to create top level MIME Message"; + } + + $mime_message->attach(Data =>[" This e-mail contains your lonCAPA SSL certificates. These\n", + "certificates allow your system to interact with the world wide\n", + "cluster of LonCAPA systems, and allow you to access and share\n", + "public resources for courses you host.\n\n", + " The certificates are shipped as a self installing shell script\n", + "To install these certificates:\n\n", + "1. Extract the attachment to this e-mail message\n", + "2. Save the attachment where it can be recovered in case you need\n", + " to re-install these certificates later on for some reason\n", + "3. As root execute the certificate request file: + . $installer_file\n", + " (Note: If you used a Windows based e-mail program to extract the\n", + " this file and then tranferred it to your unix lonCAPA system you \n", + " Will probably need to convert the file first e.g.: \n", + " dos2unix $installer_file\n", + " . $installer_file\n", + " The installer file will install the certificates and ask you\n", + " if you want to restart the LonCAPA system. You must restart the\n", + " LonCAPA system for it to use the new certificates.\n\n", + " Thank you for choosing LonCAPA for your course delivery needs,\n", + " The LonCAPA team.\n"]); + + Debug("Main message body created"); + + + # Attach the certificate intaller: + + $mime_message->attach(Type => "text/plain", + Path => $installer_file); + Debug("Installer attached"); + + return $mime_message; + } +# +# Sends a mime message to an email address. +# Parameters: +# message - A MIME::Entity containing the message. +# Implicit inputs: +# Mail is sent via /usr/lib/sendmail -t -oi -oem" +# This should work on all systems with a properly configured +# sendmail or compatible mail transfer agent. sub SendEmail { - my ($EmailAddress, $Message) = @_; + my ($message) = @_; + + Debug("Mailing"); + + open MAILPIPE, "| /usr/lib/sendmail -t -oi -oem" or + die "Failed to open pipe to sendmail: $!"; + + $message->print(\*MAILPIPE); + Debug("Submitted to sendmail"); + close MAILPIPE; +} +# +# Cleanup destroys the certificate file and its installer. +# +# +sub Cleanup { + my ($installer) = @_; + unlink($installer); + unlink("hostCertificate.pem"); } -sub Cleanup {} # Program entry point @@ -238,7 +518,10 @@ if($argc != 1) { } my $CertificateRequest = $ARGV[0]; -my $email_address = CreateCertificate($CertificateRequest); +&ReadConfig; + +my $email_address = &CreateCertificate($CertificateRequest); +Debug("CreateCertificate returned: $email_address"); if(!defined $email_address) { print STDERR "Bad or missing certificate file!!"; @@ -246,9 +529,9 @@ if(!defined $email_address) { exit -1; } -CreateInstallScript; -my $Message = CreateEmail; -SendEmail($email_address, $Message); -Cleanup; +my $script_name = &CreateInstallScript; +my $Message = &CreateEmail($script_name, $email_address); +&SendEmail($Message); +&Cleanup($script_name); # POD documentation.