--- loncom/CrGenerate.pl 2004/06/30 11:14:35 1.4 +++ loncom/CrGenerate.pl 2004/07/05 11:36:52 1.8 @@ -2,7 +2,7 @@ # The LearningOnline Network # CrGenerate - Generate a loncapa certificate request. # -# $Id: CrGenerate.pl,v 1.4 2004/06/30 11:14:35 foxr Exp $ +# $Id: CrGenerate.pl,v 1.8 2004/07/05 11:36:52 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -48,8 +48,8 @@ # Import section: use strict; +use lib '/home/httpd/lib/perl'; use MIME::Entity; -use Mail::Mailer; use LONCAPA::Configuration; use File::Copy; @@ -71,7 +71,7 @@ my $WebGroup="www"; # Group name runnin # Debug/log support: # -my $DEBUG = 1; # 1 for on, 0 for off. +my $DEBUG = 0; # 1 for on, 0 for off. # Send debugging to stderr. # Parameters: @@ -80,13 +80,87 @@ my $DEBUG = 1; # 1 for on, 0 for off. # $DEBUG - message is only written if this is true. # sub Debug { - my $msg = shift; + my ($msg) = @_; if($DEBUG) { print STDERR "$msg\n"; } } # +# Decodes the email address from a textual certificate request +# file: +# Parameters: +# $RequestFile - Name of the file containing the textual +# version of the certificate request. +# Returns: +# Email address contained in the request. +# Failure: +# If unable to open or unable to fine an email address in the file, +# dies with a message. +# +sub DecodeEmailFromRequest { + Debug("DecodeEmailFromRequest"); + + my ($RequestFile) = @_; + Debug("Request file is called $RequestFile"); + + # We need to look for the line that has a "/Email=" in it. + + Debug("opening $RequestFile"); + open REQUEST, "< $RequestFile" or + die "Unable to open $RequestFile to parse return email address"; + + Debug("Parsing request file"); + my $line; + my $found = 0; + while($line = ) { + chomp($line); # Never a bad idea. + if($line =~ /\/Email=/) { + $found = 1; + last; + } + } + if(!$found) { + die "There does not appear to be an email address in $RequestFile"; + } + + close REQUEST; + + Debug("Found /Email in $line"); + + # $line contains a bunch of comma separated key=value pairs. + # The problem is that after these is a /Email= + # first we'll split the line up at the commas. + # Then we'll look for the entity with the /Email in it. + # That line will get split at the / and then the Email= + # gets split at the =. I'm sure there's some clever regular expression + # substitution that will get it all in a single line, but I think + # this approach is gonna be much easier to understand than punctuation + # sneezed all over the page: + + my @commalist = split(/,/, $line); + my $item; + my $emailequals = ""; + foreach $item (@commalist) { + if($item =~ /\/Email=/) { # gotcha... + $emailequals = $item; + last; + } + } + + Debug("Pulled out $emailequals from $line"); + my ($trash, $addressequals) = split(/\//, $emailequals); + Debug("Futher pulled out $addressequals"); + + my ($junk, $address) = split(/=/, $addressequals); + Debug("Parsed final email addresss as $address"); + + + + return $address; +} + +# # Read the LonCAPA web config files to get the values of the # configuration global variables we need: # Implicit inputs: @@ -206,12 +280,24 @@ sub GenerateRequest { my $decodecmd = $SSLCommand." rsa -in hostkey.pem" ." -out hostkey.dec" ." -passin pass:$Passphrase"; - my $status = system($decodecmd); + $status = system($decodecmd); if($status) { die "Host key decode failed"; } chmod(0600, "hostkey.dec"); # Protect the decoded hostkey. + + # Create the textual version of the request too: + + Debug("Creating textual version of the request for users."); + my $textcmd = $SSLCommand." req -in request.pem -text " + ." -out request.txt"; + $status = system($textcmd); + if($status) { + die "Textualization of the certificate request failed"; + } + + Debug("Done"); } # @@ -257,8 +343,90 @@ sub InstallKey { Debug("Done"); } -sub MailRequest {} -sub Cleanup {} +# +# Package up a certificate request and email it to the loncapa +# admin. The email sent: +# - Has the subject: "LonCAPA certificate request for hostname +# - Has, as the body, the text version of the certificate. +# This can be inspected by the human issuing the certificate +# to decide if they want to really grant it... it will +# have the return email and all the documentation fields. +# - Has a text attachment that consists of the .pem version of the +# request. This is extracted by the human granting the +# certificate and used as input to the CrGrant.pl script. +# +# +# Implicit inputs: +# request.pem - The certificate request file. +# request.txt - Textual version of the request file. +# $RequestEmail - Email address to which the key is sent. +# +sub MailRequest { + Debug("Mailing request"); + + # First we need to pull out the return address from the textual + # form of the certificate request: + + my $FromEmail = DecodeEmailFromRequest("request.txt"); + if(!$FromEmail) { + die "From email address cannot be decoded from certificate request"; + } + Debug("Certificate will be sent back to $FromEmail"); + + # Create the email message headers and all: + # + Debug("Creating top...level..."); + my $top = MIME::Entity->build(Type => "multipart/mixed", + From => $FromEmail, + To => $RequestEmail, + Subject => "LonCAPA certificate request"); + if(!$top) { + die "Unable to create top level mime document"; + } + Debug("Attaching Text formatted certificate request"); + $top->attach(Path => "request.txt"); + + + Debug("Attaching PEM formatted certificate request..."); + $top->attach(Type => "text/plain", + Path => "request.pem"); + + # Now send the email via sendmail this should work as long as + # sendmail or postfix are configured properly. Most other mailers + # define the sendmail command too for compatibility with what + # we're trying to do. I decided to use sendmail directly because + # otherwise I'm not sure the mail headers I created in $top + # will get properly passed as headers to other mailer thingies. + # + + Debug("Mailing.."); + + open MAILPIPE, "| /usr/lib/sendmail -t -oi -oem" or + die "Failed to open pipe to sendmail: $!"; + $top->print(\*MAILPIPE); + close MAILPIPE; + + + + Debug("Done"); +} + +# +# Cleans up the detritus that's been created by this +# script (see Implicit inputs below). +# Implicit inputs: +# request.pem - Name of certificate request file in PEM format +# which will be deleted. +# request.txt - Name of textual equivalent of request file +# which will also be deleted. +# hostkey.pem - Encrypted host key which will be deleted. +# hostkey.dec - Decoded host key, which will be deleted. +# +sub Cleanup { + Debug("Cleaning up generated, temporary files"); + unlink("request.pem", "request.txt", "hostkey.pem", "hostkey.dec"); + Debug("done!"); +} @@ -272,3 +440,124 @@ MailRequest; # Mail certificate reques Cleanup; # Cleanup temp files created. Debug("Done"); + +#---------------------- POD documentatio -------------------- + +=head1 NAME + + CrGenerate - Generate a loncapa certificate request. + +=head1 SYNOPSIS + +Usage: B + +This should probably be run automatically at system +installation time. Root must run this as write access is +required to /home/httpd. + +This is a command line script that: + + - Generates a hostkey and certificate request. + - Installs the protected/decoded host key where + secure lond/lonc can find it. + - Emails the certificate request to the loncapa certificate + manager. + +In due course if all is legitimate, the loncapa certificate +manager will email a certificate installation script to +the local loncapa system administrator. + +=head1 DESCRIPTION + +Using the default openssl configuration file, a certificate +request and local hostkey are created in the current working +directory. The local host key is decoded and installed in the +loncapa certificate directory. This allows the secure versions +of lonc and lond to locate them when attempting to form +external connections. The key file is given mode +0400 to secure it from prying eyes. + +The certificate request in PEM form is attached to an email that +contains the textual equivalent of the certificate request +and sent to the loncapa certificate manager. All temporary +files (certificate request, keys etc.) are removed from the +current working directory. + +It is recommended that the directory this script is run in have +permission mask 0700 to ensure that there are no timing holes +during which the decoded host key file can be stolen. + +During certificate generation, the user will receive several +prompts. For the default LonCAPA openssl configuration, +these prompts, and documentation and sample responses +in angle brackets (<>) are shown below: + + Country Name (2 letter code) [GB]: + State or Province Name (full name) [Berkshire]: + Locality Name (eg, city) [Newbury]: + Organization Name (eg, company) [My Company Ltd]: + Organizational Unit Name (eg, section) []: + Common Name (eg, your name or your server's host name) [] + Email Address []:
+ + Please enter the following 'extra' attributes + to be sent with your certificate request + A challenge password []: + An optional company name []: + + +=head1 DEPENDENCIES + + - MIME::Entity Used to create the email message. + - LONCAPA::Configuration Used to parse the loncapa configuration files. + - File::Copy Used to install the key file. + - /usr/lib/sendmail Properly configured sendmail, used to send the + certificate request email to the loncapa + certificate administrator. + - /etc/httpd/conf/* Loncapa configuration files read to locate + the certificate directory etc. + +=head1 FILES + + The following temporary files are created in the cwd + + hostkey.pem - PEM formatted version of the encrypted host key. + hostkey.dec - PEM formatted decrypted version of the host key. + request.pem - PEM formatted certificate request. + request.txt - Textual rendering of the certificate request. + + The following permanent file is created: + + $CertDir/$Keyfile - The installed decoded host key file. $CertDir + is defined by the Perl variable lonCertificateDirectory + in /etc/loncapa_apache.conf while $Keyfile is + defined by the perl variable lonnetPrivateKey in the + same configuration file. + +=head1 COPYRIGHT: + + 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 + + +=cut + + +