ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/WEBTOOLS/SecurityModule/SecurityModule.pm
Revision: 1.2
Committed: Fri Feb 16 10:25:08 2007 UTC (18 years, 2 months ago) by dfeichti
Content type: text/plain
Branch: MAIN
Changes since 1.1: +47 -28 lines
Log Message:
- added note about current import routine doing a merge (i.e. not removing
  entries which are not present in the HN password file)

File Contents

# User Rev Content
1 dfeichti 1.1 ################################################################
2     # SecurityModule
3     #
4     # Version info: $Id: SecurityModule.pm,v 1.20 2007/02/07 11:20:09 dfeich Exp $
5     ################################################################
6     package SecurityModule;
7    
8     use IO::File;
9     use Crypt::CBC;
10     use MIME::Base64;
11     use Digest::MD5 qw(md5_base64);
12     use Data::Dumper;
13     use CGI qw(cookie param url_param redirect);
14     #use Carp;
15     use strict;
16    
17     sub new {
18     my $class = shift;
19     my $options = shift;
20    
21     my @settable = qw(CALLER_URL CONFIG LOGFILE LOGLEVEL PWDFORM_HANDLER KEYVALIDTIME
22     REQCERT_FAIL_HANDLER REVPROXY_MODE);
23    
24     my $self = {};
25     $self->{AUTHNSTATE} = "failed";
26     $self->{USERDN} = undef;
27     $self->{ROLES} = {};
28    
29     $self->{REVPROXY_MODE} = 1; # reverse proxy mode
30    
31     $self->{CIPHER} = undef; # cipher object
32     $self->{CIPHERKEY} = undef; # currently active key
33     $self->{KEYID} = undef; # id of current key
34     $self->{KEYVALIDTIME} = 3600; # for how long keys remain valid (in s)
35     $self->{COOKIE} = undef; # holds a cookie to be set
36    
37     $self->{CALLER_URL}=""; # true URL (including any proxy transform.)
38    
39     $self->{ERRMSG} = "";
40 dfeichti 1.2 $self->{LOGFILE} = undef; # defaults to stderr (apache err log)
41 dfeichti 1.1 $self->{LOGH} = undef; # Log filehandle
42     $self->{LOGLEVEL} = 1;
43    
44     $self->{CONFIG} = undef; # configuration file
45    
46     # handler for password page
47     # $self->{PWDFORM_HANDLER} = sub { die "Not implemented"; };
48     $self->{PWDFORM_HANDLER} = \&SecurityModule::_passwordForm;
49    
50     # handler for reqAuthnCert failures
51     $self->{REQCERT_FAIL_HANDLER} = \&SecurityModule::_reqcertFailHandler;
52    
53    
54     bless ($self, $class);
55    
56     # treat logfile options seperately first
57     if(exists $options->{LOGFILE}) {
58     $self->{LOGFILE} = $options->{LOGFILE};
59     }
60     if(exists $options->{LOGLEVEL}) {
61     $self->{LOGLEVEL} = $options->{LOGLEVEL};
62     }
63    
64     # read the config file and let command line given options override
65     # the config file supplied ones
66     if(exists $options->{CONFIG}) {
67     $self->{CONFIG} = $options->{CONFIG};
68     my $confopts = $self->_readConfigFile();
69     while( my ($opt,$val) = each %$confopts) {
70     $options->{$opt} = $val if ! exists $options->{$opt};
71     }
72     }
73    
74     while( my ($opt,$val) = each %$options) {
75     next if ! grep (/$opt/,@settable);
76     $self->{$opt} = $val;
77     delete $options->{$opt};
78     }
79    
80     # depending whether we are in reverse proxy mode, choose the
81     # appropriate environment variables for the SSL authentication results
82     if ($self->{REVPROXY_MODE}) {
83     $self->{HTTPS} = $ENV{HTTP_HTTPS};
84     $self->{SSL_CLIENT_VERIFY} = $ENV{HTTP_SSL_CLIENT_VERIFY};
85     $self->{SSL_CLIENT_S_DN} = $ENV{HTTP_SSL_CLIENT_S_DN};
86     $self->{REMOTE_ADDR} = $ENV{HTTP_X_FORWARDED_FOR};
87     } else {
88     $self->{HTTPS} = $ENV{HTTPS};
89     $self->{SSL_CLIENT_VERIFY} = $ENV{SSL_CLIENT_VERIFY};
90     $self->{SSL_CLIENT_S_DN} = $ENV{SSL_CLIENT_S_DN};
91     $self->{REMOTE_ADDR} = $ENV{REMOTE_ADDR};
92     }
93    
94     return $self;
95     }
96    
97     sub init {
98     my $self = shift;
99    
100     return 1 if $self->{HTTPS} ne "on";
101    
102     if ($self->{SSL_CLIENT_VERIFY} eq "SUCCESS") {
103     $self->{AUTHNSTATE} = "cert";
104     $self->{USERDN} = $self->{SSL_CLIENT_S_DN};
105     }
106    
107     # return if this is the password form
108     return 1 if $self->{CALLER_URL} =~ /^$self->{PWDFORM_HANDLER}.*/;
109    
110     my $show_pwdform = 0;
111     # Cookies can override a SSL/cert authentication
112     if((my $cookie = cookie(-name => "SecMod"))) {
113     $self->_log(5,"Found cookie: $cookie");
114     # do cookie authentication
115     $show_pwdform =1 if ! $self->_cookieAuthen($cookie);
116     } elsif (url_param("SecModLogout")){
117     # if there is no cookie, but we still have a Logout directive in the URL
118     # clean it and redirect to self
119     (my $url = $self->{CALLER_URL}) =~ s/[?&]SecModLogout=1//;
120     print redirect($url);
121     }
122    
123     # do password authentication of a submitted password form
124     $show_pwdform = $self->_passwordAuthen() ? 0 : 1 if(param("SecModLogin"));
125    
126     # don't show form if we are already passwd authenticated
127     $show_pwdform = 1 if (param("SecModPwd") && $self->{AUTHNSTATE} ne "passwd");
128    
129     # display the password form
130     if($show_pwdform) {
131     $self->_showPasswdForm($self->{ERRMSG});
132     }
133    
134     $self->{ROLES}=$self->_getRolesFromDN($self->{USERDN}) if $self->{AUTHNSTATE} ne "failed";
135    
136     return 1;
137     }
138    
139     sub reqAuthnPasswd {
140     my $self = shift;
141    
142     if ($ENV{HTTP_HTTPS} ne "on") {
143     (my $red = $self->{CALLER_URL}) =~ s!^http:!^https:!;
144     print redirect($red);
145     }
146    
147     return if $self->{AUTHNSTATE} eq "cert" || $self->{AUTHNSTATE} eq "passwd";
148     $self->_showPasswdForm("password authentication required");
149     exit(0);
150     }
151    
152     sub reqAuthnCert {
153     my $self = shift;
154    
155     if ($ENV{HTTP_HTTPS} ne "on") {
156     (my $red = $self->{CALLER_URL}) =~ s!^http:!^https:!;
157     print redirect($red);
158     }
159    
160     return if $self->{AUTHNSTATE} eq "cert";
161    
162     if (ref($self->{REQCERT_FAIL_HANDLER}) eq "CODE") {
163     &{$self->{REQCERT_FAIL_HANDLER}}($self->{AUTHNSTATE},$self->{USERDN});
164     exit 1;
165     }
166     print redirect($self->{REQCERT_FAIL_HANDLER} ."?caller_url=" .
167     urlencode($self->{CALLER_URL}));
168     exit 1;
169     }
170    
171     sub getAuthnState {
172     my $self = shift;
173    
174     return $self->{AUTHNSTATE};
175     }
176    
177     sub getCookie {
178     my $self = shift;
179     return $self->{COOKIE};
180     }
181    
182     sub getDN {
183     my $self = shift;
184     return $self->{USERDN};
185     }
186    
187     sub getRoles {
188     my $self = shift;
189     return $self->{ROLES};
190     }
191    
192    
193     sub getErrMsg {
194     my $self = shift;
195     return $self->{ERRMSG};
196     }
197    
198     sub setKeyValidTime {
199     my $self = shift;
200     $self->{KEYVALIDTIME} = shift;
201     }
202    
203     sub setLogFile {
204     my $self = shift;
205     $self->{LOGFILE} = shift;
206     }
207    
208     sub setLogLevel {
209     my $self = shift;
210     $self->{LOGLEVEL} = shift;
211     }
212    
213     sub setPwdHandler {
214     my $self = shift;
215     $self->{PWDFORM_HANDLER} = shift;
216     }
217    
218    
219     sub DESTROY {
220    
221     }
222    
223     # CLASS METHODS
224    
225     sub urlencode {
226     (my $str = shift) =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
227     return $str;
228     }
229    
230     sub urldecode {
231     (my $str = shift) =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
232     return $str;
233     }
234    
235    
236     ############################################
237     # VIRTUAL METHODS TO BE IMPLEMENTED BY A
238     # SUBCLASS
239    
240     # If argument keyid is given, fetches the appropriate key. If
241     # keyid is omitted, returns the a valid key from the DB (if no
242     # valid keys are found, a new one will be generated).
243     # @param: keyid ID of key to be retrieved
244     # @return: 0 if ok, 1 if no such key in DB, 2 if key is too old
245     sub _getCipherKey {
246     die "virtual function _getCipherKey not implemented";
247     }
248    
249     # @param: user's DN
250     # @return: hash mapping roles to array of scopes
251     sub _getRolesFromDN {
252     die "virtual function _getRolesFromDN not implemented";
253     }
254    
255     # @param: short user name as used in passwd authentication
256     # @return: user's DN
257     sub _getDNfromUsername {
258     die "virtual function _getDNfromUsername not implemented";
259     }
260    
261     # @param: short user name as used in passwd authentication
262     # @return: password hash
263     sub _getUserPasswd {
264     die "virtual function _getUserPasswd not implemented";
265     }
266    
267     ############################################
268     # PRIVATE METHODS
269    
270    
271     sub _getOriginatorHash {
272     my $self = shift;
273     $self->_log(5,"_getOriginatorHash: REMOTE_ADDR=".$self->{REMOTE_ADDR});
274     return md5_base64($self->{REMOTE_ADDR},
275     $ENV{HTTP_USER_AGENT});
276     }
277    
278     # very primitive configuration file reader
279     sub _readConfigFile {
280     my $self = shift;
281    
282     my $conf={};
283    
284     open(CONF,"<$self->{CONFIG}") or die "Failed to open config file " . $self->{CONFIG};
285    
286     while(my $line = <CONF>) {
287     next if $line =~ /^\s*#/ || $line =~ /^\s*$/;
288    
289     if( my ($opt,$val) = $line =~ m/^\s*([A-Z_]+)\s*=\s*([^\s]+)\s*$/) {
290     $conf->{$opt} = $val;
291     }
292     }
293     close CONF;
294     $self->_log(5,"Config File: " . Dumper($conf));
295     return $conf;
296     }
297    
298     # Does authentication of a presented cookie. Also implements a
299     # logout feature by setting an obsolete cookie
300     #
301     # @return 1 for a correct cookie, 0 if an invalid cookie was found
302     # also returns 1 if a "Logout" directive was found
303     sub _cookieAuthen {
304     my $self = shift;
305     my $cookie = shift;
306    
307     # A 'logout' is performed if a parameter SecModLogout is found
308     # basically resulting in destroying the cookie
309     if(param("SecModLogout")) {
310     $self->{COOKIE} = cookie(-name => "SecMod",
311     -value =>"",
312     -expires => "-1h",
313     -secure => 1
314     );
315     return(1);
316     }
317    
318     my ($state,$user,$time,$orighash) = $self->_decryptCookie($cookie);
319 dfeichti 1.2 #TODO test cookie creation time in addition to key validity time?
320 dfeichti 1.1 if($state==0 && $orighash eq $self->_getOriginatorHash()) {
321     $self->{AUTHNSTATE} = "passwd";
322     $self->{USERDN} = $user;
323     return(1);
324     }
325     $self->_log(5,"_cookieAuthen: Failed: orighash=" . $self->_getOriginatorHash());
326    
327     if ($state == 2) {
328     $self->{ERRMSG}="reauthentication";
329     return(0);
330     }
331    
332     $self->{ERRMSG}="invalid cookie";
333     $self->_log(1,"INVALID COOKIE from .$self->{REMOTE_ADDR}: $cookie");
334     return(0)
335     }
336    
337     # @return: 0 if all went correct
338     sub _log {
339     my $self = shift;
340     my $level = shift;
341     my $msg = shift;
342    
343     return if($level>$self->{LOGLEVEL});
344    
345 dfeichti 1.2 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
346     $year+=1900;
347     $mon++;
348     my $date = sprintf("$year-%02d-%02d %02d:%02d:%02d",
349     $mon,$mday,$hour,$min,$sec);
350    
351     if (! $self->{LOGFILE}) {
352     print STDERR "$self->{REMOTE_ADDR} $date SecurityModule: $msg\n";
353     return 0;
354     }
355    
356 dfeichti 1.1 if( !$self->{LOGH} ) {
357     if(! ($self->{LOGH} = IO::File->new(">>$self->{LOGFILE}")) ) {
358     $self->{ERRMSG} = "Failed to open logfile " . $self->{LOGFILE};
359 dfeichti 1.2 print STDERR "$date SecurityModule: Failed to open logfile " . $self->{LOGFILE}
360 dfeichti 1.1 . ": $msg\n";
361     return 1;
362     }
363     chmod 0600,$self->{LOGFILE};
364     }
365    
366 dfeichti 1.2
367 dfeichti 1.1 #TODO: better log date formatting
368 dfeichti 1.2 print {$self->{LOGH}} "$self->{REMOTE_ADDR} $date (L$level)" . $msg . "\n";
369 dfeichti 1.1
370     return 0;
371     }
372    
373    
374     sub _createCryptKey {
375     return Crypt::CBC->random_bytes(56);
376     }
377    
378     # if $keyid is given the CIPHER will be initialised with
379     # the key matching this id (if available and still valid)
380     # @return: 0 if ok, 1 for invalid key, 2 for key too old,
381     # 3 for failure to create cipher object
382     sub _initCipher {
383     my $self = shift;
384     my $keyid = shift;
385    
386     my $rv;
387     if ($keyid) {
388     if ($rv = $self->_getCipherKey($keyid)) {
389     if($rv == 1) {
390     $self->{ERRMSG}="invalid cipher key ID $keyid";
391     $self->_log(1,"WARNING: Failed to get cypher keys for ID $keyid");
392     } elsif ($rv == 2) {
393     $self->{ERRMSG}="old cipher key ID $keyid";
394     $self->_log(3,"Refusing old cypher key (ID $keyid)");
395     }
396     return $rv;
397     }
398     } else {
399     $self->_getCipherKey();
400     }
401    
402     $self->{CIPHER} = Crypt::CBC->new( -cipher => 'Blowfish',
403     -literal_key => 1,
404     -key => $self->{CIPHERKEY},
405     -iv => "dummyabc",
406     -header => 'none',
407     -blocksize => 8,
408     -keysize => 56
409     -padding => 'standard'
410     );
411    
412     return 0 if $self->{CIPHER};
413    
414     $self->_log(1,"Error: Failed to create cipher object: keyID $keyid, cipherkey:" .
415     Dumper($self->{CIPHERKEY}));
416     $self->{ERRMSG} = "failed to create cipher object";
417     return 3;
418     }
419    
420     # The random generated initialization vector is prepended to the
421     # encrypted text
422     sub _encrypt {
423     my $self = shift;
424     my $msg = shift;
425    
426     $self->_log(5,"msg to encrypt is >$msg< CIPHER: $self->{CIPHER}");
427     #return "" if $msg == undef or $msg eq "";
428     my $iv = Crypt::CBC->random_bytes(8);
429     $self->{CIPHER}->set_initialization_vector($iv);
430    
431     my $encr = $iv . $self->{CIPHER}->encrypt($msg);
432     my $encr64 = encode_base64($encr);
433    
434     return $encr64;
435     }
436    
437     sub _decrypt{
438     my $self = shift;
439     my $encr64 = shift;
440    
441     my $encr = decode_base64($encr64);
442     my $iv = substr($encr,0,8,"");
443     $self->{CIPHER}->set_initialization_vector($iv);
444    
445     my $decr;
446     eval {
447     $decr = $self->{CIPHER}->decrypt($encr);
448     };
449     if($@) {
450     $self->_log(1,"Error: _decrypt: Failed to decrypt: $@");
451     return undef;
452     }
453    
454     return $decr;
455     }
456    
457     #
458     #
459     # @return: (0,...) for ok, 1 for failed decryption or invalid key, 2 for key too old
460     #
461     sub _decryptCookie {
462     my $self = shift;
463     my $encrcookie = shift;
464    
465     my ($keyid,$encr) = $encrcookie =~ m/^([^|]+)\|(.+)$/s;
466     return 1 if ! $keyid;
467    
468     my $rv;
469     return $rv if ($rv = $self->_initCipher($keyid));
470    
471     my $decr = $self->_decrypt($encr);
472     return 1 if ! $decr;
473     my ($user,$time,$orighash) = split(/\|/,$decr);
474    
475     $self->_log(5,"_decryptCookie: $user | $time | $orighash");
476     return (0,$user,$time,$orighash);
477     }
478    
479     # prepares the cookie payload
480     sub _prepareCookie {
481     my $self = shift;
482    
483     my $cleartxt = join("|",($self->{USERDN},time(),$self->_getOriginatorHash()));
484     my $encr = $self->_encrypt($cleartxt);
485     my $cookie = $self->{KEYID} . "|" . $encr;
486    
487     return $cookie;
488     }
489    
490    
491     #
492     # Authenticate a user based on a password entered in the password form
493     #
494     # @return: 1 for authenticated, 0 for not authenticated
495     sub _passwordAuthen {
496     my $self = shift;
497    
498     return 0 if $self->_initCipher();
499    
500     $self->{ERRMSG} = "";
501    
502     my $username = param("SecModLogin");
503     my $pass = param('SecModPwd');
504    
505     my $dbpasswd = $self->_getUserPasswd($username);
506     my $hash = crypt($pass,substr($dbpasswd,0,2));
507     $self->_log(5,"_passwordAuthen: user $username, pass: $hash, dbpass: $dbpasswd");
508     if ($dbpasswd eq $hash) {
509     $self->{AUTHNSTATE} = "passwd";
510     if ($self->{USERDN} = $self->_getDNfromUsername($username)) {
511 dfeichti 1.2 # for now we use no 'expires' value -expires =>'',
512 dfeichti 1.1 $self->{COOKIE} = cookie(-name => "SecMod",
513     -value => $self->_prepareCookie(),
514     -secure => 1
515     );
516 dfeichti 1.2 $self->_log(4,"Successful password authentication by $username for $self->{USERDN}");
517 dfeichti 1.1 return(1);
518     }
519    
520 dfeichti 1.2 $self->_log(3,"No distinguished name known for user $username");
521 dfeichti 1.1 $self->{ERRMSG} = "No distinguished name known for user $username";
522     return(0);
523     }
524    
525     $self->{ERRMSG} = "Password verification failed";
526 dfeichti 1.2 $self->_log(3,"Password verification failed for user $username");
527 dfeichti 1.1 # TODO: pass on amount of failed attempts in hidden field and redirect after certain
528     # number of failures?
529     return(0);
530     }
531    
532     sub _showPasswdForm {
533     my $self = shift;
534     my $msg = shift;
535    
536     # get rid of any unwanted url parameters
537     (my $url = $self->{CALLER_URL}) =~ s/[&?]SecModLogout=1//;
538     $url =~ s/[&?]SecModPwd=1//;
539    
540     if (ref($self->{PWDFORM_HANDLER}) eq "CODE") {
541     &{$self->{PWDFORM_HANDLER}}($url,$msg);
542     #TODO: should pass all params except SecMod* through to the next page
543     exit 1;
544     }
545     print redirect($self->{PWDFORM_HANDLER} ."?caller_url=" .
546     urlencode($url) . "&msg=" . urlencode($msg));
547     exit 1;
548    
549     }
550    
551     # A default password form
552     sub _passwordForm {
553     my $caller_url = shift;
554     my $msg=shift;
555    
556     my $q = new CGI;
557    
558     print $q->header,$q->start_html;
559     print $q->start_form("POST","$caller_url","application/x-www-form-urlencoded");
560     if ($msg) {
561     print $q->h1("Error: " . $msg);
562     } else {
563     print $q->h1("Log in:");
564     }
565     print $q->p,"Login Name: ",$q->textfield('SecModLogin','',12,20);
566     print $q->p,"Password: ",$q->password_field('SecModPwd','',20,30);
567     print $q->p,$q->submit(-name=>'Submit',
568     -value=>'submit');
569     print $q->endform;
570    
571     print $q->end_html;
572     exit(0);
573     }
574    
575     # A default page for failed certificate access
576     sub _reqcertFailHandler {
577     my $authnstate = shift;
578     my $userdn = shift;
579    
580     my $q = new CGI;
581     print $q->header,$q->start_html;
582     print "Access only with a valid certificate";
583     if ($authnstate eq "passwd") {
584     print " (and you are logged in via password)";
585     } else {
586 dfeichti 1.2 print $q->p,"Your Browser presented this certificate: " . $userdn if $userdn;
587 dfeichti 1.1 }
588     print $q->end_html;
589     exit(0);
590    
591     }
592    
593     1;
594    
595    
596     =pod
597    
598     =head1 NAME
599    
600     SecurityModule.pm - Web Security Module
601    
602     =head1 SYNOPSIS
603    
604     Note: This applies specifically to the MySQL implementation. There is also
605     a SecurityModule::SQLite implementation.
606    
607     use SecurityModule::MySQL;
608     $sec = new SecurityModule::MySQL;
609     my $sec = new SecurityModule::MySQL({CALLER_URL => $myurl,
610     CONFIG => "/etc/sec/SecMod.conf";
611     LOGLEVEL => 5,
612     KEYVALIDTIME => 7200,
613     PWDFORM_HANDLER => \&myPasswordForm
614     });
615    
616     $ret = $sec->init(); # returns 0 in case of failure
617    
618     $errmsg = $sec->getErrMsg(); # returns error message
619    
620 dfeichti 1.2 # if getCookie() returns a defined value, your page needs to make
621     # sure that this cookie will be set using CGI.pm's
622     # header(-cookie => $cookie ) command
623 dfeichti 1.1 if( ($cookie=$sec->getCookie) ) {
624     print header(-cookie => $cookie );
625     } else {
626     print header();
627     }
628    
629    
630     # Access to authentication / authorization information
631     $state = $sec->getAuthnState(); # returns (failed | cert | passwd)
632     $user_dn = $sec->getDN(); # returns user's distinguished name
633 dfeichti 1.2 $roles = $sec->getRoles(); # returns a hash of roles, each role mapping to a
634 dfeichti 1.1 # list of scopes
635    
636    
637     # Protecting functions: reqAuthnCert() and reqAuthnPasswd()
638     sub my_certificate_protected_function {
639     $sec->reqAuthnCert();
640     ...
641     }
642     sub my_password_protected_function {
643     $sec->reqAuthnPasswd();
644     ...
645     }
646    
647     =head1 DESCRIPTION
648    
649     The SecurityModule handles authentication and authorization to a web site. Users
650 dfeichti 1.2 are identified by a certificate loaded in their browser or by a previously
651 dfeichti 1.1 set cookie that was issued upon a successful password authentication.
652    
653 dfeichti 1.2 Certificate based authentication is the strongest authentication type,
654     so functions protected by the reqAuthnPasswd() method will allow
655     access to certificate authenticated users, but reqAuthnCert() will deny
656     access to password authenticated users.
657    
658 dfeichti 1.1 The SecurityModule was written for a setup where a B<remote Proxy> mediates access
659     to a number of backend servers. The remote proxy handles
660 dfeichti 1.2 the SSL authentication and is required to set the following request headers to
661 dfeichti 1.1 the values of the respective environment variables for this request:
662    
663     B<SSL_CLIENT_VERIFY>,
664     B<SSL_CLIENT_S_DN>,
665     B<HTTPS>.
666    
667     On the backend servers these must be available as environment variables of identical names
668     except for the prefix B<HTTP_>, e.g. B<HTTP_SSL_CLIENT_S_DN>.
669    
670     Since all backend servers are hidden behind the reverse proxy, an authentication
671     cookie is set restrictively to only be sent back to the issueing server. The
672     necessary translation for the proxy is handled transparently by apache's mod_proxy
673     module (needs >= apache-2.2).
674    
675     The SecurityModule can also be run without a reverse proxy in pure SSL mode (by
676     setting the REVPROXY_MODE option to 0), mainly for testing.
677    
678     =head2 Initialization
679    
680     Arguments which can be passed to the constructor:
681    
682     =over 4
683    
684     =item *
685    
686 dfeichti 1.2 B<CALLER_URL>: (Required) URL of the current page that was invoked by
687     the browser, i.e. it must contain the URL which the reverse proxy got
688     before redirecting to the backend server.
689 dfeichti 1.1
690     =item *
691    
692     B<REVPROXY_MODE>: 1 for reverse proxy mode, 0 for basic SSL mode. Default is 1.
693    
694     =item *
695    
696     B<CONFIG>: Filename of a configuration file. The file must contain "option = value"
697     lines as in this example:
698    
699     LOGFILE = /tmp/SecMod.log
700     LOGLEVEL = 5
701     KEYVALIDTIME = 1500
702     # Comments and empty lines are allowed
703     DBHOST = localhost
704     DBPORT = 3306
705     DBNAME = secmod
706     DBUSER = smwriter
707     DBPASS = mypasswd
708     REQCERT_FAIL_HANDLER = https://localhost/bclear/testSecMod/nopermission
709     PWDFORM_HANDLER = https://localhost/bclear/testSecMod/passform
710    
711     B<Note>: The configuration options specified in the constructor will override
712     any options specified in the configuration file.
713    
714     =item *
715    
716     B<KEYVALIDTIME>: Validity time in seconds of generated keys
717    
718     =item *
719    
720     B<PWDFORM_HANDLER>: URL or reference to a function generating a
721     password page. If a function reference is given, two values will be
722     passed into the function: The URL of the present page (so we can get
723     back) and a status message describing why the password form was
724     called. If an URL is given, the two values will be passed using a
725     query string (?caller_url=...&msg=...) in the redirection.
726    
727 dfeichti 1.2 Status messages: password authentication required
728     reauthentication
729     invalid cookie
730 dfeichti 1.1
731     =item *
732    
733     B<REQCERT_FAIL_HANDLER>: URL or reference to a function to call when a page secured by
734     reqAuthnCert() is encountered and the client is not certificate authenticated.
735     Typically, to display some diagnostic message.
736    
737     =item *
738    
739     B<LOGFILE>: Filename of the logfile
740    
741     =item *
742    
743 dfeichti 1.2 B<LOGLEVEL>: Integer value from 0-5
744    
745     0: no log messages at all
746     1: error and security relevant messages only
747     3: Logs password authentications (standard log level)
748     5: debugging messages
749 dfeichti 1.1
750     =item *
751    
752     For the MySQL implementation you can also supply the DB connection parameters
753     B<DBHOST, DBPORT, DBNAME, DBUSER, DBPASS>
754    
755     =item *
756    
757     For the SQLite implementation you only need to supply a B<DBFILE> parameter
758     with the location of the data base file.
759    
760     =back
761    
762 dfeichti 1.2 =head2 Calling the password form via a web page "Login" link:
763 dfeichti 1.1
764     You can pass B<SecModPwd=1> as a GET variable to any page using the
765     SecurityModule. This will call the handler for / redirect to the password form
766     and insure that the user can return to the same page.
767    
768    
769     =head1 AUTHOR
770    
771     Derek Feichtinger <derek.feichtinger@psi.ch>
772    
773     CMS web interfaces group <hn-cms-webInterfaces@cern.ch>
774    
775    
776     =head1 ISSUES / TODO
777    
778     List of issues to resolve:
779    
780     =over 4
781    
782     =item *
783    
784     POST arguments are not carried across the password form
785    
786     =item *
787    
788 dfeichti 1.2 The mapping from username to certificate distinguished name needs to be established
789     separately.
790 dfeichti 1.1
791     =item *
792    
793     Look at "TODO" comments in the code.
794    
795     =back