Diff for /loncom/CrGrant.pl between versions 1.1 and 1.6

version 1.1, 2004/07/02 10:51:18 version 1.6, 2009/02/17 21:18:07
Line 55 Line 55
 #      o The certificate authority files are in $SSLDir/loncapaca  #      o The certificate authority files are in $SSLDir/loncapaca
 #      o The certificate authority certificate is in:  #      o The certificate authority certificate is in:
 #         $SSLDir/loncapaca/cacert.pem  #         $SSLDir/loncapaca/cacert.pem
 #      o The certificate authority maintains a certificate index file  #      o Only one instance of this script will be run at a time in
 #        $SSLDIR/loncapaca/index.txt  #        this directory.
 #      o Only one instance of this script will be run at a time!!!!!  
 #        (otherwise the last line of the index file may not be the  
 #         index to our certificate.  We'll do some rudimentary  
 #         error checking, but have no idea how to recover in case  
 #         of problems).  
 #      o The generated certificates are stored in $SSLDIR/loncapaca/certs  
 #      o The person that runs this script knows the passphrase  #      o The person that runs this script knows the passphrase
 #        for the loncapa certificate authority's private key  #        for the loncapa certificate authority's private key
 #        which remains encrypted for security reasons.  #        which remains encrypted for security reasons.
Line 72 Line 66
 # Import section:  # Import section:
   
 use strict;  use strict;
 use lib '/home/httpd/lib/perl';  use lib '/home/httpd/lib/perl'; # An assumption!!!
 use MIME::Entity;  use MIME::Entity;
 use LONCAPA::Configuration;  use LONCAPA::Configuration;
   
Line 81  use LONCAPA::Configuration; Line 75  use LONCAPA::Configuration;
 # Global variable declarations  # Global variable declarations
   
   
   my $ssl_dir       = "/usr/share/ssl";    # Where ssl config files etc. live
   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  # Debug/log support
   
 my $DEBUG=1;  my $DEBUG=0;
   
 sub Debug {  sub Debug {
     my $msg = shift;      my $msg = shift;
Line 94  sub Debug { Line 110  sub Debug {
 }  }
 #  Support subs:  #  Support subs:
   
 sub Usage {}  #
   #   Print out program usage.
   #
   # Side effects:
   #    Output goes to stderr.
   #
   sub Usage {
       print STDERR << "USAGE";
   
   Usage:
      CrGrant.pl requestfile.pem
   
   Where:
      requestfile.pem is a PEM formatted certificate extracted from an email 
                      to the LonCAPA certificate manager.
   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
   #  file.
   #
   #  The certificate is parsed to determine the email address
   #  of the requestor, which is returned to the caller.
   #
   #Parameters:
   #     request_file   - Name of the file containing the certificate request.
   #Returns:
   #     If the request file exists and is able to produce a certificate
   #     the email address of the requester is returned to the caller.
   #     If not, undef is returned.
   #
 sub CreateCertificate {  sub CreateCertificate {
     my $RequestFile = shift;      my ($request_file) = @_;
   
       Debug("CreateCertificate");
   
       if(!(-e $request_file)) {
    Debug("Certificate file $request_file does not exist");
    return undef;
       }
       Debug("Certificate file $request_file exists");
   
       # Create the certificate:  The status of the openssl command
       # is used to determine if the certificate succeeded:
   
       my $create_command = $ssl_command." ca -config ".$ca_config_file
                                ." -in ".$request_file
        ." -out hostCertificate.pem";
       my $status = system($create_command);
       if($status) {
    Debug("openssl ca failed");
    print STDERR "Certificate generation failed... probably bad";
    print STDERR " request file!\n";
    return undef;
       }
       Debug("openssl ca succeeded");
   
       #  Now we have a shining new signed certificate in ./hostCertificate.pem
       #  we parse it to get the email address to which the certificate should
       #  be emailed.
       #   The certificate's return email address will be in the Subject line:
       #
   
       Debug("Parsing certificate file for Subject:");
       open CERTIFICATE, "<hostCertificate.pem";
       my $line;
       my $subject_found = 0;
       while ($line = <CERTIFICATE>) {
    Debug("Line = $line");
    if($line =~ /Subject:/) {
       Debug("Found Subject: in $line");
       $subject_found =1;
       last;
    }
       }
       close CERTIFICATE;
   
       if(!$subject_found) {
    Debug("Did not find Subject line in cert");
    print STDERR "Output certificate parse failed: no Subject:\n";
    return undef;
       }
       #  The subject line contains an Email= string amidst the other stuff.
       #  First break in to comma separated stuff, then locate the piece that
       #  contains /Email=
   
       my @subject_fields = split(/,/, $line);
       my $email_found = 0;
       my $element;
       my $email_element;
       Debug("Parsing subject line for Email=");
       foreach $element (@subject_fields) {
    $email_element = $element;
    Debug("Parsing $element");
    if($element =~ /\/Email=/) {
       Debug("Found /Email=");
       $email_found = 1;
       last;
    }
       }
       if(!$email_found) {
    Debug("Failed to fine Email=");
    print STDERR "Unable to find line with /Email= in cert. Subject\n";
    return undef;
       }
   
       #  The piece we found must first be split at the /
       #  to isolate the Email= part and then that part at the = to isolate
       #  the address:
   
       Debug("Splitting $email_element at /");
       my ($junk, $email) = split(/\//, $email_element);
       Debug("Email part is $email");
       my ($junk, $address) = split(/=/, $email);
       Debug("CreateCertificate Returning $address to caller");
   
       return $address;
   
     return 'fox@nscl.msu.edu';   # Stub..  
 }  }
 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 <<BASH_HEADER;
   #!/bin/bash
   #
   #    Installer for your lonCAPA certificates.  Please check the
   #    configuration variables to be sure they match your installation.
   #    Then run this script under a root shell to complete the 
   #    installation of the certificates.
   #
   # Configuration Variables:
   CERTDIR="$loncapa_cert_dir"        # Directory with your host key.
   HOSTCERT="$loncapa_hostcert_name"   # Name of host's certificate file.
   CACERT="$loncapa_cacert_name"     # Name of certifiate authority file.
   HTTPDUID="$loncapa_apache_user"     # UID of httpd.
   HTTPDGID="$loncapa_apache_group"    # GID of httpd.
   
   #   End of configuration variables.
   
   MODE=0444                           # certificates get this mode.
   HOSTCERTPATH="\$CERTDIR/\$HOSTCERT"
   CACERTPATH="\$CERTDIR/\$CACERT"
   
   #  Create the host certificate file to install:
   
   echo unpacking host certificate
   
   cat <<-HOSTCERTTEXT   >\$HOSTCERT
   BASH_HEADER
   
       #   Now copy the host certificate into the script:
   
       open HOSTCERT, "<hostCertificate.pem";
       while(my $line = <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 = <CACERT>) {
    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 <<BASH_TRAILER;
   
   echo Installing certificates
   
   install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$CACERT \$CACERTPATH
   install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$HOSTCERT \$HOSTCERTPATH
   
   echo done
   
   rm -f \$CACERT
   rm -f \$HOSTCERT
   
   #    Do they want to restart loncapa:
   #
   
   echo In order to start running in secure mode you will need to start
   echo lonCAPA.  If you want I can do that now for you.  Otherwise,
   echo you will have to do it yourself later either by rebooting your
   echo system or by typing:
   echo
   echo /etc/init.d/loncontrol restart
   echo
   read -p "Restart loncapa now [yN]?"  yesno
   
   if [ "\${yesno:0:1}" = "Y" -o "\${yesno:0:1}"  = "y" ] 
   then
      /etc/init.d/loncontrol restart
   fi
   BASH_TRAILER
   
       close INSTALLER;
   
       return "CertInstall.sh";
   }
   #
   #    Create a mime Email that consists of a cover letter of installation
   #    instructions and an attachment that is the installation script.
   # Parameters:
   #     script    - The name of the script that will be attached
   #                 to the email.
   #     send_address - Where the mail will be sent.
   # Returns:
   #     The MIME::Entity handle of the script.
   #
 sub CreateEmail {  sub CreateEmail {
     return "Dummy message"; # Stub.      Debug("Creating Email");
       my ($installer_file, $send_address) = @_;
   
       #  The top level mime entity is the mail headers and the
       #  cover letter:
   
       my $mime_message = MIME::Entity->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 {  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  #  Program entry point
Line 125  if($argc != 1) { Line 518  if($argc != 1) {
 }  }
 my $CertificateRequest = $ARGV[0];  my $CertificateRequest = $ARGV[0];
   
 my $EmailAddress = CreateCertificate($CertificateRequest);  &ReadConfig;
 CreateInstallScript;  
 my $Message = CreateEmail;  my $email_address = &CreateCertificate($CertificateRequest);
 SendEmail($EmailAddress, $Message);  Debug("CreateCertificate returned: $email_address");
 Cleanup;  
   if(!defined $email_address) {
       print STDERR "Bad or missing certificate file!!";
       Usage;
       exit -1;
   }
   
   my $script_name = &CreateInstallScript;
   my $Message = &CreateEmail($script_name, $email_address);
   &SendEmail($Message);
   &Cleanup($script_name);
   
 # POD documentation.  # POD documentation.

Removed from v.1.1  
changed lines
  Added in v.1.6


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