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>