Annotation of loncom/CrGrant.pl, revision 1.3

1.1       foxr        1: #!/usr/bin/perl
                      2: # The LearningOnline Network
                      3: # CrGrant.pl  - Grant a loncapa SSL certificate.
                      4: #
1.3     ! foxr        5: # $Id: CrGrant.pl,v 1.2 2004/07/05 11:37:39 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)
        !            91: 
        !            92: #  Items I just need to know:
        !            93: 
        !            94: my $loncapa_config = "loncapa.conf";   # User's override config file.
        !            95: my $loncapa_apache_user = 'www';	# Name of apache daemon's user
        !            96: my $loncapa_apache_group = 'www';	# Name of apache daemon's group
        !            97: 
        !            98: 
1.1       foxr       99: 
                    100: # Debug/log support
                    101: 
                    102: my $DEBUG=1;
                    103: 
                    104: sub Debug {
                    105:     my $msg = shift;
                    106:     if($DEBUG) {
                    107: 	print STDERR "$msg\n";
                    108:     }
                    109: }
                    110: #  Support subs:
                    111: 
1.2       foxr      112: #
                    113: #   Print out program usage.
                    114: #
                    115: # Side effects:
                    116: #    Output goes to stderr.
                    117: #
                    118: sub Usage {
                    119:     print STDERR << "USAGE";
                    120: 
                    121: Usage:
                    122:    CrGrant.pl requestfile.pem
1.1       foxr      123: 
1.2       foxr      124: Where:
                    125:    requestfile.pem is a PEM formatted certificate extracted from an email 
                    126:                    to the LonCAPA certificate manager.
                    127: USAGE
                    128: 
                    129: }
1.3     ! foxr      130: #
        !           131: #  Read the loncapa configuration file and pull out the items
        !           132: #  we need:
        !           133: #
        !           134: # Implicit inputs:
        !           135: #   $loncapa_config   - The name of the auxilliary config file.
        !           136: # Side effects:
        !           137: #    - On failure exits with an error message.
        !           138: #    - On success set the following variables:
        !           139: #      o loncapa_cert_dir      - Path to certificates.
        !           140: #      o loncapa_hostcert_name - Name of host's cert file in that dir
        !           141: #      o loncapa_cacert_name   - Name of CA's cert file in that dir.
        !           142: #      o ssl_command           - Name of ssl utility command.
        !           143: sub ReadConfig {
        !           144:     Debug("Reading the config files");
        !           145:     my $perlvarref = LONCAPA::Configuration::read_conf($loncapa_config);
        !           146: 
        !           147:     #  Pull out the individual variables or die:
        !           148: 
        !           149:     # SSL Command:
        !           150: 
        !           151:     if($perlvarref->{SSLProgram}) {
        !           152: 	$ssl_command = $perlvarref->{SSLProgram};
        !           153: 	Debug("SSL utility program is $ssl_command");
        !           154:     } 
        !           155:     else {
        !           156: 	die "LonCAPA configuration errror: Can't read SSLProgram variable";
        !           157:     }
        !           158:     # Certificate directory:
        !           159:    
        !           160:     if($perlvarref->{lonCertificateDirectory}) {
        !           161: 	$loncapa_cert_dir = $perlvarref->{lonCertificateDirectory};
        !           162: 	Debug("Certificates will be installed in $loncapa_cert_dir");
        !           163:     } 
        !           164:     else {
        !           165: 	die "LonCAPA configuration error can't read lonCertificateDirectory variable";
        !           166: 
        !           167:     }
        !           168:     #  Get the name of the host's certificate:
        !           169: 
        !           170:     if($perlvarref->{lonnetCertificate}) {
        !           171: 	$loncapa_hostcert_name = $perlvarref->{lonnetCertificate};
        !           172: 	Debug("Host's certificate will be $loncapa_hostcert_name");
        !           173:     }
        !           174:     else {
        !           175: 	die "LonCAPA configuration error: Can't read lonnetCertificate variable";
        !           176:     }
        !           177:     #   Get the name of the certificate authority's certificate.
        !           178: 
        !           179:     if($perlvarref->{lonnetCertificateAuthority}) {
        !           180: 	$loncapa_cacert_name = $perlvarref->{lonnetCertificateAuthority};
        !           181: 	Debug("CA's certificate will be $loncapa_cacert_name");
        !           182:     }
        !           183:     else {
        !           184: 	die "LonCAPA configuration error: Can't read lonnetCertificateAuthority variable";
        !           185:     }
        !           186: 
        !           187: 
        !           188: }
        !           189: 
1.2       foxr      190: #  Create a certificate from the request file.  The certificate
                    191: #  is used, in conjunction with the openssl command with the 
                    192: #  certificate authority configuration to produce a certificate
                    193: #  file.
                    194: #
                    195: #  The certificate is parsed to determine the email address
                    196: #  of the requestor, which is returned to the caller.
                    197: #
                    198: #Parameters:
                    199: #     request_file   - Name of the file containing the certificate request.
                    200: #Returns:
                    201: #     If the request file exists and is able to produce a certificate
                    202: #     the email address of the requester is returned to the caller.
                    203: #     If not, undef is returned.
                    204: #
1.1       foxr      205: sub CreateCertificate {
1.2       foxr      206:     my ($request_file) = @_;
                    207: 
                    208:     Debug("CreateCertificate");
                    209: 
                    210:     if(!(-e $request_file)) {
                    211: 	Debug("Certificate file $request_file does not exist");
                    212: 	return undef;
                    213:     }
                    214:     Debug("Certificate file $request_file exists");
                    215: 
                    216:     # Create the certificate:  The status of the openssl command
                    217:     # is used to determine if the certificate succeeded:
                    218: 
                    219:     my $create_command = $ssl_command." ca -config ".$ca_config_file
                    220: 	                             ." -in ".$request_file
                    221: 				     ." -out hostCertificate.pem";
                    222:     my $status = system($create_command);
                    223:     if($status) {
                    224: 	Debug("openssl ca failed");
                    225: 	print STDERR "Certificate generation failed... probably bad";
                    226: 	print STDERR " request file!\n";
                    227: 	return undef;
                    228:     }
                    229:     Debug("openssl ca succeeded");
                    230: 
                    231:     #  Now we have a shining new signed certificate in ./hostCertificate.pem
                    232:     #  we parse it to get the email address to which the certificate should
                    233:     #  be emailed.
                    234:     #   The certificate's return email address will be in the Subject line:
                    235:     #
                    236: 
                    237:     Debug("Parsing certificate file for Subject:");
                    238:     open CERTIFICATE, "<hostCertificate.pem";
                    239:     my $line;
                    240:     my $subject_found = 0;
                    241:     while ($line = <CERTIFICATE>) {
                    242: 	Debug("Line = $line");
                    243: 	if($line =~ /Subject:/) {
                    244: 	    Debug("Found Subject: in $line");
                    245: 	    $subject_found =1;
                    246: 	    last;
                    247: 	}
                    248:     }
                    249:     close CERTIFICATE;
                    250: 
                    251:     if(!$subject_found) {
                    252: 	Debug("Did not find Subject line in cert");
                    253: 	print STDERR "Output certificate parse failed: no Subject:\n";
                    254: 	return undef;
                    255:     }
                    256:     #  The subject line contains an Email= string amidst the other stuff.
                    257:     #  First break in to comma separated stuff, then locate the piece that
                    258:     #  contains /Email=
                    259: 
                    260:     my @subject_fields = split(/,/, $line);
                    261:     my $email_found = 0;
                    262:     my $element;
                    263:     my $email_element;
                    264:     Debug("Parsing subject line for Email=");
                    265:     foreach $element (@subject_fields) {
                    266: 	$email_element = $element;
                    267: 	Debug("Parsing $element");
                    268: 	if($element =~ /\/Email=/) {
                    269: 	    Debug("Found /Email=");
                    270: 	    $email_found = 1;
                    271: 	    last;
                    272: 	}
                    273:     }
                    274:     if(!$email_found) {
                    275: 	Debug("Failed to fine Email=");
                    276: 	print STDERR "Unable to find line with /Email= in cert. Subject\n";
                    277: 	return undef;
                    278:     }
                    279: 
                    280:     #  The piece we found must first be split at the /
                    281:     #  to isolate the Email= part and then that part at the = to isolate
                    282:     #  the address:
                    283: 
                    284:     Debug("Splitting $email_element at /");
                    285:     my ($junk, $email) = split(/\//, $email_element);
                    286:     Debug("Email part is $email");
                    287:     my ($junk, $address) = split(/=/, $email);
                    288:     Debug("CreateCertificate Returning $address to caller");
                    289: 
                    290:     return $address;
1.1       foxr      291: 
                    292: }
1.3     ! foxr      293: #
        !           294: #   Create the installation script.  This will be  bash script
        !           295: #   that will install the certifiate and the CA's certificate with ownership
        !           296: #   WebUser:WebGroup and permissions 0400.  I thought about using a perl
        !           297: #   script in order to be able to get the certificate file/directory from
        !           298: #   the configuration files.  Unfortunately this is not as easy as it looks.
        !           299: #   Root has a chicken and egg problem.  In order to read the config file
        !           300: #   you need to have added the ..../lib/perl to the perl lib path. To do
        !           301: #   that correctly, you need to have read the config file to know where
        !           302: #   it is...What we will do is read our local configuration file and
        !           303: #   assume that our configuration is the same as the target's system in
        !           304: #   all respects we care about.
        !           305: # Implicit Inputs:
        !           306: #    - Bash is in /bin/bash
        !           307: #    - $loncapa_cert_dir             -  install target directory.
        !           308: #    - $loncapa_hostcert_name        -  Name of installed host cert file.
        !           309: #    - $loncapa_cacert_name          -  Name of installed ca cert file.
        !           310: #    - $loncapa_apache_user          -  username under which httpd runs.
        !           311: #    - $loncapa_apache_group         -  group under which httpd runs.
        !           312: #    - 0400                          -  install permissions.
        !           313: #    - The host's certificate is now in ./hostCertificate.pem
        !           314: #    - The CA's certificate is now in  $ca_cert_file
        !           315: #
        !           316: # Implicit Outputs:
        !           317: #    A file named CertInstall.sh
        !           318: #
        !           319: sub CreateInstallScript {
        !           320:     open INSTALLER,">CertInstall.sh";
        !           321:     print INSTALLER <<BASH_HEADER;
        !           322: #!/bin/bash
        !           323: #
        !           324: #    Installer for your lonCAPA certificates.  Please check the
        !           325: #    configuration variables to be sure they match your installation.
        !           326: #    Then run this script under a root shell to complete the 
        !           327: #    installation of the certificates.
        !           328: #
        !           329: # Configuration Variables:
        !           330: CERTDIR="$loncapa_cert_dir"        # Directory with your host key.
        !           331: HOSTCERT="$loncapa_hostcert_name"   # Name of host's certificate file.
        !           332: CACERT="$loncapa_cacert_name"     # Name of certifiate authority file.
        !           333: HTTPDUID="$loncapa_apache_user"     # UID of httpd.
        !           334: HTTPDGID="$loncapa_apache_group"    # GID of httpd.
        !           335: 
        !           336: #   End of configuration variables.
        !           337: 
        !           338: MODE=0444                           # certificates get this mode.
        !           339: HOSTCERTPATH="\$CERTDIR/\$HOSTCERT"
        !           340: CACERTPATH="\$CERTDIR/\$CACERT"
        !           341: 
        !           342: #  Create the host certificate file to install:
        !           343: 
        !           344: echo unpacking host certificate
        !           345: 
        !           346: cat <<-HOSTCERTTEXT   >\$HOSTCERT
        !           347: BASH_HEADER
        !           348: 
        !           349:     #   Now copy the host certificate into the script:
        !           350: 
        !           351:     open HOSTCERT, "<hostCertificate.pem";
        !           352:     while(my $line = <HOSTCERT>) {
        !           353: 	print INSTALLER $line;	# Line presumably has a \n.
        !           354:     }
        !           355:     close HOSTCERT;
        !           356: 
        !           357:     #  Close the here doc, and start up the cat of the ca cert:
        !           358: 
        !           359:     print INSTALLER "HOSTCERTTEXT\n";
        !           360:     print INSTALLER "echo unpacking CA certificate\n";
        !           361:     print INSTALLER "cat <<-CACERTTEXT >\$CACERT\n";
        !           362:     open  CACERT, "<$ca_cert_file";
        !           363:     while(my $line = <CACERT>) {
        !           364: 	print INSTALLER $line;
        !           365:     }
        !           366:     close CACERT;
        !           367:     print INSTALLER "CACERTTEXT\n";
        !           368: 
        !           369:     #  Ok, the script can create the two files, now it must install
        !           370:     # install them >and< clean up after itself.
        !           371: 
        !           372:     print INSTALLER <<BASH_TRAILER;
        !           373: 
        !           374: echo Installing certificates
        !           375: 
        !           376: install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$CACERT \$CACERTPATH
        !           377: install -m \$MODE -o \$HTTPDUID -g \$HTTPDGID \$HOSTCERT \$HOSTCERTPATH
        !           378: 
        !           379: echo done
        !           380: 
        !           381: # rm -f \$CACERT
        !           382: # rm -f \$HOSTCERT
        !           383: 
        !           384: #    Do they want to restart loncapa:
        !           385: #
        !           386: 
        !           387: echo In order to start running in secure mode you will need to start
        !           388: echo lonCAPA.  If you want I can do that now for you.  Otherwise,
        !           389: echo you will have to do it yourself later either by rebooting your
        !           390: echo system or by typing:
        !           391: echo
        !           392: echo /etc/init.d/loncontrol restart
        !           393: echo
        !           394: read -p "Restart loncapa now [yN]?"  yesno
        !           395: 
        !           396: if [ "{\$yesno:0:1}" = "Y" ] 
        !           397: then
        !           398:    /etc/init.d/loncontrol restart
        !           399: fi
        !           400: BASH_TRAILER
        !           401: 
        !           402:     close INSTALLER;
        !           403: }
1.1       foxr      404: 
                    405: sub CreateEmail {
                    406:     return "Dummy message";	# Stub.
                    407: }
                    408: 
                    409: sub SendEmail {
                    410:     my ($EmailAddress, $Message) = @_;
                    411: }
                    412: sub Cleanup {}
                    413: 
                    414: 
                    415: #  Program entry point
                    416: #   The usage is:
                    417: #     CrGrant.pl    {request_file}
                    418: #
                    419: 
                    420: my $argc = @ARGV;		# Count number of command parameters.
                    421: if($argc != 1) {
                    422:     Usage;
                    423:     exit -1;
                    424: }
                    425: my $CertificateRequest = $ARGV[0];
                    426: 
1.3     ! foxr      427: &ReadConfig;
        !           428: 
        !           429: my $email_address = &CreateCertificate($CertificateRequest);
        !           430: Debug("CreateCertificate returned: $email_address");
1.2       foxr      431: 
                    432: if(!defined $email_address) {
                    433:     print STDERR "Bad or missing certificate file!!";
                    434:     Usage;
                    435:     exit -1;
                    436: }
                    437: 
1.3     ! foxr      438: &CreateInstallScript;
        !           439: my $Message = &CreateEmail;
        !           440: &SendEmail($email_address, $Message);
        !           441: &Cleanup;
1.1       foxr      442: 
                    443: # POD documentation.

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