File:  [LON-CAPA] / loncom / CrGrant.pl
Revision 1.3: download - view: text, annotated - select for diffs
Tue Jul 6 11:05:45 2004 UTC (19 years, 9 months ago) by foxr
Branches: MAIN
CVS tags: HEAD
- Add read of config file to figure out where stuff goes.
- Code and test install script creation.

    1: #!/usr/bin/perl
    2: # The LearningOnline Network
    3: # CrGrant.pl  - Grant a loncapa SSL certificate.
    4: #
    5: # $Id: CrGrant.pl,v 1.3 2004/07/06 11:05:45 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: 
   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: 
   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: 
  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
  123: 
  124: Where:
  125:    requestfile.pem is a PEM formatted certificate extracted from an email 
  126:                    to the LonCAPA certificate manager.
  127: USAGE
  128: 
  129: }
  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: 
  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: #
  205: sub CreateCertificate {
  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;
  291: 
  292: }
  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: }
  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: 
  427: &ReadConfig;
  428: 
  429: my $email_address = &CreateCertificate($CertificateRequest);
  430: Debug("CreateCertificate returned: $email_address");
  431: 
  432: if(!defined $email_address) {
  433:     print STDERR "Bad or missing certificate file!!";
  434:     Usage;
  435:     exit -1;
  436: }
  437: 
  438: &CreateInstallScript;
  439: my $Message = &CreateEmail;
  440: &SendEmail($email_address, $Message);
  441: &Cleanup;
  442: 
  443: # POD documentation.

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