Annotation of loncom/CrGrant.pl, revision 1.5

1.1       foxr        1: #!/usr/bin/perl
                      2: # The LearningOnline Network
                      3: # CrGrant.pl  - Grant a loncapa SSL certificate.
                      4: #
1.5     ! foxr        5: # $Id: CrGrant.pl,v 1.4 2004/07/09 09:11:48 foxr Exp $
1.1       foxr        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
1.2       foxr       58: #      o Only one instance of this script will be run at a time in
                     59: #        this directory.
1.1       foxr       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;
1.3       foxr       69: use lib '/home/httpd/lib/perl';	# An assumption!!!
1.1       foxr       70: use MIME::Entity;
                     71: use LONCAPA::Configuration;
                     72: 
                     73: 
                     74: 
                     75: # Global variable declarations
                     76: 
1.3       foxr       77: 
1.2       foxr       78: my $ssl_dir       = "/usr/share/ssl";    # Where ssl config files etc. live
1.3       foxr       79: my $ca_cert_file  = $ssl_dir."/loncapaca/cacert.pem"; # CA's certificate file.
1.2       foxr       80: my $ca_config_file= $ssl_dir."/loncapaca.cnf";      # CA's config file. 
1.1       foxr       81: 
1.3       foxr       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)
1.4       foxr       91: my $return_address;		# Email return address.
1.3       foxr       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: 
1.1       foxr      100: 
                    101: # Debug/log support
                    102: 
1.4       foxr      103: my $DEBUG=0;
1.1       foxr      104: 
                    105: sub Debug {
                    106:     my $msg = shift;
                    107:     if($DEBUG) {
                    108: 	print STDERR "$msg\n";
                    109:     }
                    110: }
                    111: #  Support subs:
                    112: 
1.2       foxr      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
1.1       foxr      124: 
1.2       foxr      125: Where:
                    126:    requestfile.pem is a PEM formatted certificate extracted from an email 
                    127:                    to the LonCAPA certificate manager.
                    128: USAGE
                    129: 
                    130: }
1.3       foxr      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:     }
1.4       foxr      187:     #  Get the email address of the certificate manager:
                    188:     #  this is the email return address:
1.3       foxr      189: 
1.4       foxr      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:     }
1.3       foxr      197: 
                    198: }
                    199: 
1.2       foxr      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: #
1.1       foxr      215: sub CreateCertificate {
1.2       foxr      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;
1.1       foxr      301: 
                    302: }
1.3       foxr      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
1.4       foxr      328: # Return
                    329: #    Name of the file we created.
1.3       foxr      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: 
1.4       foxr      393: rm -f \$CACERT
                    394: rm -f \$HOSTCERT
1.3       foxr      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: 
1.4       foxr      408: if [ "\${yesno:0:1}" = "Y" -o "\${yesno:0:1}"  = "y" ] 
1.3       foxr      409: then
                    410:    /etc/init.d/loncontrol restart
                    411: fi
                    412: BASH_TRAILER
                    413: 
                    414:     close INSTALLER;
1.4       foxr      415: 
                    416:     return "CertInstall.sh";
1.3       foxr      417: }
1.4       foxr      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;
1.1       foxr      475: 
                    476: }
                    477: 
1.4       foxr      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.
1.1       foxr      486: sub SendEmail {
1.4       foxr      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);
1.5     ! foxr      505:     unlink("hostCertificate.pem");
1.1       foxr      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: 
1.3       foxr      521: &ReadConfig;
                    522: 
                    523: my $email_address = &CreateCertificate($CertificateRequest);
                    524: Debug("CreateCertificate returned: $email_address");
1.2       foxr      525: 
                    526: if(!defined $email_address) {
                    527:     print STDERR "Bad or missing certificate file!!";
                    528:     Usage;
                    529:     exit -1;
                    530: }
                    531: 
1.4       foxr      532: my $script_name = &CreateInstallScript;
                    533: my $Message = &CreateEmail($script_name, $email_address);
                    534: &SendEmail($Message);
                    535: &Cleanup($script_name);
1.1       foxr      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.