File:  [LON-CAPA] / loncom / CrGrant.pl
Revision 1.5: download - view: text, annotated - select for diffs
Fri Jul 9 09:16:09 2004 UTC (19 years, 11 months ago) by foxr
Branches: MAIN
CVS tags: version_2_8_X, version_2_8_2, version_2_8_1, version_2_8_0, version_2_7_X, version_2_7_99_1, version_2_7_99_0, version_2_7_1, version_2_7_0, version_2_6_X, version_2_6_99_1, version_2_6_99_0, version_2_6_3, version_2_6_2, version_2_6_1, version_2_6_0, version_2_5_X, version_2_5_99_1, version_2_5_99_0, version_2_5_2, version_2_5_1, version_2_5_0, version_2_4_X, version_2_4_99_0, version_2_4_2, version_2_4_1, version_2_4_0, version_2_3_X, version_2_3_99_0, version_2_3_2, version_2_3_1, version_2_3_0, version_2_2_X, version_2_2_99_1, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, version_2_1_X, version_2_1_99_3, version_2_1_99_2, version_2_1_99_1, version_2_1_99_0, version_2_1_3, version_2_1_2, version_2_1_1, version_2_1_0, version_2_0_X, version_2_0_99_1, version_2_0_2, version_2_0_1, version_2_0_0, version_1_99_3, version_1_99_2, version_1_99_1_tmcc, version_1_99_1, version_1_99_0_tmcc, version_1_99_0, version_1_3_X, version_1_3_3, version_1_3_2, version_1_3_1, version_1_3_0, version_1_2_X, version_1_2_99_1, version_1_2_99_0, version_1_2_1, version_1_2_0, version_1_1_99_5, version_1_1_99_4, version_1_1_99_3, version_1_1_99_2, HEAD, GCI_1
Fix a case problem on the cleanup of the generated certificate file

    1: #!/usr/bin/perl
    2: # The LearningOnline Network
    3: # CrGrant.pl  - Grant a loncapa SSL certificate.
    4: #
    5: # $Id: CrGrant.pl,v 1.5 2004/07/09 09:16:09 foxr Exp $
    6: #
    7: # Copyright Michigan State University Board of Trustees
    8: #
    9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   10: #
   11: # LON-CAPA is free software; you can redistribute it and/or modify
   12: # it under the terms of the GNU General Public License as published by
   13: # the Free Software Foundation; either version 2 of the License, or 
   14: # (at your option) any later version.
   15: #
   16: # LON-CAPA is distributed in the hope that it will be useful,
   17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19: # GNU General Public License for more details.
   20: #
   21: # You should have received a copy of the GNU General Public License
   22: # along with LON-CAPA; if not, write to the Free Software
   23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   24: #
   25: # /home/httpd/html/adm/gpl.txt
   26: #
   27: 
   28: 
   29: # http://www.lon-capa.org/
   30: #
   31: # This script operates on a certificate request that has been
   32: # extracted from the attachment sent to the loncapa certificate 
   33: # administrator and:
   34: #
   35: #  1. Creates an ssl certificate corresponding to the request.
   36: #  2. Constructs an installation script that will install
   37: #     the certificate along with the certificate authority's
   38: #     certificate in a loncapa system.
   39: #  3. Constructs an email which contains a cover letter 
   40: #     describing what to do with the attachment, and an
   41: #     attachment that consists of the installation script
   42: #     created in step 2.
   43: #  4. Emails the message to the email address in the certificate
   44: #     request.
   45: #
   46: #  There are some assumptions we need to make in order to
   47: #  get this all to work:
   48: #    - The certificate authority is installed on a 
   49: #      loncapa system with configuration files that specify
   50: #      the same certificate directory and certificate filenames
   51: #      as the target system (otherwise we can't generate the
   52: #      installation script).
   53: #    - The loncapa certificate authority configuration file is
   54: #      $SSLDir/loncapaca.cnf and that it specifies that:
   55: #      o The certificate authority files are in $SSLDir/loncapaca
   56: #      o The certificate authority certificate is in:
   57: #         $SSLDir/loncapaca/cacert.pem
   58: #      o Only one instance of this script will be run at a time in
   59: #        this directory.
   60: #      o The person that runs this script knows the passphrase
   61: #        for the loncapa certificate authority's private key
   62: #        which remains encrypted for security reasons.
   63: #
   64: #
   65: 
   66: # Import section:
   67: 
   68: use strict;
   69: use lib '/home/httpd/lib/perl';	# An assumption!!!
   70: use MIME::Entity;
   71: use LONCAPA::Configuration;
   72: 
   73: 
   74: 
   75: # Global variable declarations
   76: 
   77: 
   78: my $ssl_dir       = "/usr/share/ssl";    # Where ssl config files etc. live
   79: my $ca_cert_file  = $ssl_dir."/loncapaca/cacert.pem"; # CA's certificate file.
   80: my $ca_config_file= $ssl_dir."/loncapaca.cnf";      # CA's config file. 
   81: 
   82: 
   83: #   LONCAPA Configuration global variables:
   84: 
   85: # Items read from our configuration file.
   86: 
   87: my $ssl_command   = "/usr/bin/openssl "; # Command to run openssl.
   88: my $loncapa_cert_dir;		# Name of target cert dir (from config)
   89: my $loncapa_hostcert_name;	# Name of host's signed cert file (config)
   90: my $loncapa_cacert_name;        # Name of the CA's certificate file (config)
   91: my $return_address;		# Email return address.
   92: 
   93: #  Items I just need to know:
   94: 
   95: my $loncapa_config = "loncapa.conf";   # User's override config file.
   96: my $loncapa_apache_user = 'www';	# Name of apache daemon's user
   97: my $loncapa_apache_group = 'www';	# Name of apache daemon's group
   98: 
   99: 
  100: 
  101: # Debug/log support
  102: 
  103: my $DEBUG=0;
  104: 
  105: sub Debug {
  106:     my $msg = shift;
  107:     if($DEBUG) {
  108: 	print STDERR "$msg\n";
  109:     }
  110: }
  111: #  Support subs:
  112: 
  113: #
  114: #   Print out program usage.
  115: #
  116: # Side effects:
  117: #    Output goes to stderr.
  118: #
  119: sub Usage {
  120:     print STDERR << "USAGE";
  121: 
  122: Usage:
  123:    CrGrant.pl requestfile.pem
  124: 
  125: Where:
  126:    requestfile.pem is a PEM formatted certificate extracted from an email 
  127:                    to the LonCAPA certificate manager.
  128: USAGE
  129: 
  130: }
  131: #
  132: #  Read the loncapa configuration file and pull out the items
  133: #  we need:
  134: #
  135: # Implicit inputs:
  136: #   $loncapa_config   - The name of the auxilliary config file.
  137: # Side effects:
  138: #    - On failure exits with an error message.
  139: #    - On success set the following variables:
  140: #      o loncapa_cert_dir      - Path to certificates.
  141: #      o loncapa_hostcert_name - Name of host's cert file in that dir
  142: #      o loncapa_cacert_name   - Name of CA's cert file in that dir.
  143: #      o ssl_command           - Name of ssl utility command.
  144: sub ReadConfig {
  145:     Debug("Reading the config files");
  146:     my $perlvarref = LONCAPA::Configuration::read_conf($loncapa_config);
  147: 
  148:     #  Pull out the individual variables or die:
  149: 
  150:     # SSL Command:
  151: 
  152:     if($perlvarref->{SSLProgram}) {
  153: 	$ssl_command = $perlvarref->{SSLProgram};
  154: 	Debug("SSL utility program is $ssl_command");
  155:     } 
  156:     else {
  157: 	die "LonCAPA configuration errror: Can't read SSLProgram variable";
  158:     }
  159:     # Certificate directory:
  160:    
  161:     if($perlvarref->{lonCertificateDirectory}) {
  162: 	$loncapa_cert_dir = $perlvarref->{lonCertificateDirectory};
  163: 	Debug("Certificates will be installed in $loncapa_cert_dir");
  164:     } 
  165:     else {
  166: 	die "LonCAPA configuration error can't read lonCertificateDirectory variable";
  167: 
  168:     }
  169:     #  Get the name of the host's certificate:
  170: 
  171:     if($perlvarref->{lonnetCertificate}) {
  172: 	$loncapa_hostcert_name = $perlvarref->{lonnetCertificate};
  173: 	Debug("Host's certificate will be $loncapa_hostcert_name");
  174:     }
  175:     else {
  176: 	die "LonCAPA configuration error: Can't read lonnetCertificate variable";
  177:     }
  178:     #   Get the name of the certificate authority's certificate.
  179: 
  180:     if($perlvarref->{lonnetCertificateAuthority}) {
  181: 	$loncapa_cacert_name = $perlvarref->{lonnetCertificateAuthority};
  182: 	Debug("CA's certificate will be $loncapa_cacert_name");
  183:     }
  184:     else {
  185: 	die "LonCAPA configuration error: Can't read lonnetCertificateAuthority variable";
  186:     }
  187:     #  Get the email address of the certificate manager:
  188:     #  this is the email return address:
  189: 
  190:     if($perlvarref->{SSLEmail}) {
  191: 	$return_address = $perlvarref->{SSLEmail};
  192: 	Debug("Return address will be $return_address");
  193:     }
  194:     else {
  195: 	die "LonCAPA configuration error can't read SSLEmail configuration item";
  196:     }
  197: 
  198: }
  199: 
  200: #  Create a certificate from the request file.  The certificate
  201: #  is used, in conjunction with the openssl command with the 
  202: #  certificate authority configuration to produce a certificate
  203: #  file.
  204: #
  205: #  The certificate is parsed to determine the email address
  206: #  of the requestor, which is returned to the caller.
  207: #
  208: #Parameters:
  209: #     request_file   - Name of the file containing the certificate request.
  210: #Returns:
  211: #     If the request file exists and is able to produce a certificate
  212: #     the email address of the requester is returned to the caller.
  213: #     If not, undef is returned.
  214: #
  215: sub CreateCertificate {
  216:     my ($request_file) = @_;
  217: 
  218:     Debug("CreateCertificate");
  219: 
  220:     if(!(-e $request_file)) {
  221: 	Debug("Certificate file $request_file does not exist");
  222: 	return undef;
  223:     }
  224:     Debug("Certificate file $request_file exists");
  225: 
  226:     # Create the certificate:  The status of the openssl command
  227:     # is used to determine if the certificate succeeded:
  228: 
  229:     my $create_command = $ssl_command." ca -config ".$ca_config_file
  230: 	                             ." -in ".$request_file
  231: 				     ." -out hostCertificate.pem";
  232:     my $status = system($create_command);
  233:     if($status) {
  234: 	Debug("openssl ca failed");
  235: 	print STDERR "Certificate generation failed... probably bad";
  236: 	print STDERR " request file!\n";
  237: 	return undef;
  238:     }
  239:     Debug("openssl ca succeeded");
  240: 
  241:     #  Now we have a shining new signed certificate in ./hostCertificate.pem
  242:     #  we parse it to get the email address to which the certificate should
  243:     #  be emailed.
  244:     #   The certificate's return email address will be in the Subject line:
  245:     #
  246: 
  247:     Debug("Parsing certificate file for Subject:");
  248:     open CERTIFICATE, "<hostCertificate.pem";
  249:     my $line;
  250:     my $subject_found = 0;
  251:     while ($line = <CERTIFICATE>) {
  252: 	Debug("Line = $line");
  253: 	if($line =~ /Subject:/) {
  254: 	    Debug("Found Subject: in $line");
  255: 	    $subject_found =1;
  256: 	    last;
  257: 	}
  258:     }
  259:     close CERTIFICATE;
  260: 
  261:     if(!$subject_found) {
  262: 	Debug("Did not find Subject line in cert");
  263: 	print STDERR "Output certificate parse failed: no Subject:\n";
  264: 	return undef;
  265:     }
  266:     #  The subject line contains an Email= string amidst the other stuff.
  267:     #  First break in to comma separated stuff, then locate the piece that
  268:     #  contains /Email=
  269: 
  270:     my @subject_fields = split(/,/, $line);
  271:     my $email_found = 0;
  272:     my $element;
  273:     my $email_element;
  274:     Debug("Parsing subject line for Email=");
  275:     foreach $element (@subject_fields) {
  276: 	$email_element = $element;
  277: 	Debug("Parsing $element");
  278: 	if($element =~ /\/Email=/) {
  279: 	    Debug("Found /Email=");
  280: 	    $email_found = 1;
  281: 	    last;
  282: 	}
  283:     }
  284:     if(!$email_found) {
  285: 	Debug("Failed to fine Email=");
  286: 	print STDERR "Unable to find line with /Email= in cert. Subject\n";
  287: 	return undef;
  288:     }
  289: 
  290:     #  The piece we found must first be split at the /
  291:     #  to isolate the Email= part and then that part at the = to isolate
  292:     #  the address:
  293: 
  294:     Debug("Splitting $email_element at /");
  295:     my ($junk, $email) = split(/\//, $email_element);
  296:     Debug("Email part is $email");
  297:     my ($junk, $address) = split(/=/, $email);
  298:     Debug("CreateCertificate Returning $address to caller");
  299: 
  300:     return $address;
  301: 
  302: }
  303: #
  304: #   Create the installation script.  This will be  bash script
  305: #   that will install the certifiate and the CA's certificate with ownership
  306: #   WebUser:WebGroup and permissions 0400.  I thought about using a perl
  307: #   script in order to be able to get the certificate file/directory from
  308: #   the configuration files.  Unfortunately this is not as easy as it looks.
  309: #   Root has a chicken and egg problem.  In order to read the config file
  310: #   you need to have added the ..../lib/perl to the perl lib path. To do
  311: #   that correctly, you need to have read the config file to know where
  312: #   it is...What we will do is read our local configuration file and
  313: #   assume that our configuration is the same as the target's system in
  314: #   all respects we care about.
  315: # Implicit Inputs:
  316: #    - Bash is in /bin/bash
  317: #    - $loncapa_cert_dir             -  install target directory.
  318: #    - $loncapa_hostcert_name        -  Name of installed host cert file.
  319: #    - $loncapa_cacert_name          -  Name of installed ca cert file.
  320: #    - $loncapa_apache_user          -  username under which httpd runs.
  321: #    - $loncapa_apache_group         -  group under which httpd runs.
  322: #    - 0400                          -  install permissions.
  323: #    - The host's certificate is now in ./hostCertificate.pem
  324: #    - The CA's certificate is now in  $ca_cert_file
  325: #
  326: # Implicit Outputs:
  327: #    A file named CertInstall.sh
  328: # Return
  329: #    Name of the file we created.
  330: #
  331: sub CreateInstallScript {
  332:     open INSTALLER,">CertInstall.sh";
  333:     print INSTALLER <<BASH_HEADER;
  334: #!/bin/bash
  335: #
  336: #    Installer for your lonCAPA certificates.  Please check the
  337: #    configuration variables to be sure they match your installation.
  338: #    Then run this script under a root shell to complete the 
  339: #    installation of the certificates.
  340: #
  341: # Configuration Variables:
  342: CERTDIR="$loncapa_cert_dir"        # Directory with your host key.
  343: HOSTCERT="$loncapa_hostcert_name"   # Name of host's certificate file.
  344: CACERT="$loncapa_cacert_name"     # Name of certifiate authority file.
  345: HTTPDUID="$loncapa_apache_user"     # UID of httpd.
  346: HTTPDGID="$loncapa_apache_group"    # GID of httpd.
  347: 
  348: #   End of configuration variables.
  349: 
  350: MODE=0444                           # certificates get this mode.
  351: HOSTCERTPATH="\$CERTDIR/\$HOSTCERT"
  352: CACERTPATH="\$CERTDIR/\$CACERT"
  353: 
  354: #  Create the host certificate file to install:
  355: 
  356: echo unpacking host certificate
  357: 
  358: cat <<-HOSTCERTTEXT   >\$HOSTCERT
  359: BASH_HEADER
  360: 
  361:     #   Now copy the host certificate into the script:
  362: 
  363:     open HOSTCERT, "<hostCertificate.pem";
  364:     while(my $line = <HOSTCERT>) {
  365: 	print INSTALLER $line;	# Line presumably has a \n.
  366:     }
  367:     close HOSTCERT;
  368: 
  369:     #  Close the here doc, and start up the cat of the ca cert:
  370: 
  371:     print INSTALLER "HOSTCERTTEXT\n";
  372:     print INSTALLER "echo unpacking CA certificate\n";
  373:     print INSTALLER "cat <<-CACERTTEXT >\$CACERT\n";
  374:     open  CACERT, "<$ca_cert_file";
  375:     while(my $line = <CACERT>) {
  376: 	print INSTALLER $line;
  377:     }
  378:     close CACERT;
  379:     print INSTALLER "CACERTTEXT\n";
  380: 
  381:     #  Ok, the script can create the two files, now it must install
  382:     # install them >and< clean up after itself.
  383: 
  384:     print INSTALLER <<BASH_TRAILER;
  385: 
  386: echo Installing certificates
  387: 
  388: install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$CACERT \$CACERTPATH
  389: install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$HOSTCERT \$HOSTCERTPATH
  390: 
  391: echo done
  392: 
  393: rm -f \$CACERT
  394: rm -f \$HOSTCERT
  395: 
  396: #    Do they want to restart loncapa:
  397: #
  398: 
  399: echo In order to start running in secure mode you will need to start
  400: echo lonCAPA.  If you want I can do that now for you.  Otherwise,
  401: echo you will have to do it yourself later either by rebooting your
  402: echo system or by typing:
  403: echo
  404: echo /etc/init.d/loncontrol restart
  405: echo
  406: read -p "Restart loncapa now [yN]?"  yesno
  407: 
  408: if [ "\${yesno:0:1}" = "Y" -o "\${yesno:0:1}"  = "y" ] 
  409: then
  410:    /etc/init.d/loncontrol restart
  411: fi
  412: BASH_TRAILER
  413: 
  414:     close INSTALLER;
  415: 
  416:     return "CertInstall.sh";
  417: }
  418: #
  419: #    Create a mime Email that consists of a cover letter of installation
  420: #    instructions and an attachment that is the installation script.
  421: # Parameters:
  422: #     script    - The name of the script that will be attached
  423: #                 to the email.
  424: #     send_address - Where the mail will be sent.
  425: # Returns:
  426: #     The MIME::Entity handle of the script.
  427: #
  428: sub CreateEmail {
  429:     Debug("Creating Email");
  430:     my ($installer_file, $send_address) = @_;
  431: 
  432:     #  The top level mime entity is the mail headers and the
  433:     #  cover letter:
  434: 
  435:     my $mime_message = MIME::Entity->build(Type    => "multipart/mixed",
  436: 					   From    => $return_address,
  437: 					   To      => $send_address,
  438: 					   Subject =>"LonCAPA certificates");
  439:     if(!$mime_message) {
  440: 	die "Unable to create top level MIME Message";
  441:     }
  442: 
  443:      $mime_message->attach(Data =>["  This email contains your lonCAPA SSL certificates.  These\n",
  444:      "certificates allow your system to interact with the world wide\n",
  445:      "cluster of LonCAPA systems, and allow you to access and share\n",
  446:      "public resources for courses you host.\n\n",
  447:      "   The certificates are shipped as a self installing shell script\n",
  448:      "To install these certificates:\n\n",
  449:      "1. Extract the attachment to this email message\n",
  450:      "2. Save the attachment where it can be recovered in case you need\n",
  451:      "   to re-install these certificates later on for some reason\n",
  452:      "3. As root execute the certificate request file:
  453:            . $installer_file\n",
  454:      "   (Note: If you used a Windows based email program to extract the\n",
  455:      "   this file and then tranferred it to your unix lonCAPA system you \n",
  456:      "   Will probably need to convert the file first e.g.: \n",
  457:      "     dos2unix $installer_file\n",
  458:      "     . $installer_file\n",
  459:      "   The installer file will install the certificates and ask you\n",
  460:      "   if you want to restart the LonCAPA system.  You must restart the\n",
  461:      "   LonCAPA system for it to use the new certificates.\n\n",
  462:      "      Thank you for choosing LonCAPA for your course delivery needs,\n",
  463:      "      The LonCAPA team.\n"]);
  464: 
  465:     Debug("Main message body created");
  466: 
  467: 
  468:     #  Attach the certificate intaller:
  469: 
  470:     $mime_message->attach(Type    => "text/plain",
  471: 			  Path    => $installer_file);
  472:     Debug("Installer attached");
  473: 
  474:     return $mime_message;
  475: 
  476: }
  477: 
  478: #
  479: #   Sends a mime message to an email address.
  480: # Parameters:
  481: #    message   - A MIME::Entity containing the message.
  482: # Implicit inputs:
  483: #   Mail is sent via /usr/lib/sendmail -t -oi -oem"
  484: #   This should work on all systems with a properly configured
  485: #   sendmail or compatible mail transfer agent.
  486: sub SendEmail {
  487:     my ($message) =  @_;
  488: 
  489:     Debug("Mailing");
  490: 
  491:     open MAILPIPE, "| /usr/lib/sendmail -t -oi -oem" or 
  492: 	die "Failed to open pipe to sendmail: $!";
  493: 
  494:     $message->print(\*MAILPIPE);
  495:     Debug("Submitted to sendmail");
  496:     close MAILPIPE;
  497: }
  498: #
  499: #  Cleanup destroys the certificate file and its installer.
  500: #
  501: #
  502: sub Cleanup {
  503:     my ($installer) = @_;
  504:     unlink($installer);
  505:     unlink("hostCertificate.pem");
  506: }
  507: 
  508: 
  509: #  Program entry point
  510: #   The usage is:
  511: #     CrGrant.pl    {request_file}
  512: #
  513: 
  514: my $argc = @ARGV;		# Count number of command parameters.
  515: if($argc != 1) {
  516:     Usage;
  517:     exit -1;
  518: }
  519: my $CertificateRequest = $ARGV[0];
  520: 
  521: &ReadConfig;
  522: 
  523: my $email_address = &CreateCertificate($CertificateRequest);
  524: Debug("CreateCertificate returned: $email_address");
  525: 
  526: if(!defined $email_address) {
  527:     print STDERR "Bad or missing certificate file!!";
  528:     Usage;
  529:     exit -1;
  530: }
  531: 
  532: my $script_name = &CreateInstallScript;
  533: my $Message = &CreateEmail($script_name, $email_address);
  534: &SendEmail($Message);
  535: &Cleanup($script_name);
  536: 
  537: # POD documentation.

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.