ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/WEBTOOLS/SecurityModule/SecurityModule.pm
Revision: 1.4
Committed: Mon Feb 26 15:23:35 2007 UTC (18 years, 2 months ago) by dfeichti
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +1 -1 lines
State: FILE REMOVED
Log Message:
- missed migrating these two files

File Contents

# Content
1 ################################################################
2 # SecurityModule
3 #
4 # Version info: $Id: SecurityModule.pm,v 1.3 2007/02/22 12:49:06 dfeichti 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 $self->{LOGFILE} = undef; # defaults to stderr (apache err log)
41 $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 #TODO test cookie creation time in addition to key validity time?
320 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 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 if( !$self->{LOGH} ) {
357 if(! ($self->{LOGH} = IO::File->new(">>$self->{LOGFILE}")) ) {
358 $self->{ERRMSG} = "Failed to open logfile " . $self->{LOGFILE};
359 print STDERR "$date SecurityModule: Failed to open logfile " . $self->{LOGFILE}
360 . ": $msg\n";
361 return 1;
362 }
363 chmod 0600,$self->{LOGFILE};
364 }
365
366
367 #TODO: better log date formatting
368 print {$self->{LOGH}} "$self->{REMOTE_ADDR} $date (L$level)" . $msg . "\n";
369
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 # for now we use no 'expires' value -expires =>'',
512 $self->{COOKIE} = cookie(-name => "SecMod",
513 -value => $self->_prepareCookie(),
514 -secure => 1
515 );
516 $self->_log(4,"Successful password authentication by $username for $self->{USERDN}");
517 return(1);
518 }
519
520 $self->_log(3,"No distinguished name known for user $username");
521 $self->{ERRMSG} = "No distinguished name known for user $username";
522 return(0);
523 }
524
525 $self->{ERRMSG} = "Password verification failed";
526 $self->_log(3,"Password verification failed for user $username");
527 # 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 print $q->p,"Your Browser presented this certificate: " . $userdn if $userdn;
587 }
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({CALLER_URL => $myurl,
609 CONFIG => "/etc/sec/SecMod.conf";
610 LOGLEVEL => 5,
611 KEYVALIDTIME => 7200,
612 PWDFORM_HANDLER => \&myPasswordForm
613 });
614
615 $ret = $sec->init(); # returns 0 in case of failure
616
617 $errmsg = $sec->getErrMsg(); # returns error message
618
619 # if getCookie() returns a defined value, your page needs to make
620 # sure that this cookie will be set using CGI.pm's
621 # header(-cookie => $cookie ) command
622 if( ($cookie=$sec->getCookie) ) {
623 print header(-cookie => $cookie );
624 } else {
625 print header();
626 }
627
628
629 # Access to authentication / authorization information
630 $state = $sec->getAuthnState(); # returns (failed | cert | passwd)
631 $user_dn = $sec->getDN(); # returns user's distinguished name
632 $roles = $sec->getRoles(); # returns a hash of roles, each role mapping to a
633 # list of scopes
634
635
636 # Protecting functions: reqAuthnCert() and reqAuthnPasswd()
637 sub my_certificate_protected_function {
638 $sec->reqAuthnCert();
639 ...
640 }
641 sub my_password_protected_function {
642 $sec->reqAuthnPasswd();
643 ...
644 }
645
646 =head1 DESCRIPTION
647
648 The SecurityModule handles authentication and authorization to a web site. Users
649 are identified by a certificate loaded in their browser or by a previously
650 set cookie that was issued upon a successful password authentication.
651
652 Certificate based authentication is the strongest authentication type,
653 so functions protected by the reqAuthnPasswd() method will allow
654 access to certificate authenticated users, but reqAuthnCert() will deny
655 access to password authenticated users.
656
657 The SecurityModule was written for a setup where a B<remote Proxy> mediates access
658 to a number of backend servers. The remote proxy handles
659 the SSL authentication and is required to set the following request headers to
660 the values of the respective environment variables for this request:
661
662 B<SSL_CLIENT_VERIFY>,
663 B<SSL_CLIENT_S_DN>,
664 B<HTTPS>.
665
666 On the backend servers these must be available as environment variables of identical names
667 except for the prefix B<HTTP_>, e.g. B<HTTP_SSL_CLIENT_S_DN>.
668
669 Since all backend servers are hidden behind the reverse proxy, an authentication
670 cookie is set restrictively to only be sent back to the issueing server. The
671 necessary translation for the proxy is handled transparently by apache's mod_proxy
672 module (needs >= apache-2.2).
673
674 The SecurityModule can also be run without a reverse proxy in pure SSL mode (by
675 setting the REVPROXY_MODE option to 0), mainly for testing.
676
677 =head2 Initialization
678
679 Arguments which can be passed to the constructor:
680
681 =over 4
682
683 =item *
684
685 B<CALLER_URL>: (Required) URL of the current page that was invoked by
686 the browser, i.e. it must contain the URL which the reverse proxy got
687 before redirecting to the backend server.
688
689 =item *
690
691 B<REVPROXY_MODE>: 1 for reverse proxy mode, 0 for basic SSL mode. Default is 1.
692
693 =item *
694
695 B<CONFIG>: Filename of a configuration file. The file must contain "option = value"
696 lines as in this example:
697
698 LOGFILE = /tmp/SecMod.log
699 LOGLEVEL = 5
700 KEYVALIDTIME = 1500
701 # Comments and empty lines are allowed
702 DBHOST = localhost
703 DBPORT = 3306
704 DBNAME = secmod
705 DBUSER = smwriter
706 DBPASS = mypasswd
707 REQCERT_FAIL_HANDLER = https://localhost/bclear/testSecMod/nopermission
708 PWDFORM_HANDLER = https://localhost/bclear/testSecMod/passform
709
710 B<Note>: The configuration options specified in the constructor will override
711 any options specified in the configuration file.
712
713 =item *
714
715 B<KEYVALIDTIME>: Validity time in seconds of generated keys
716
717 =item *
718
719 B<PWDFORM_HANDLER>: URL or reference to a function generating a
720 password page. If a function reference is given, two values will be
721 passed into the function: The URL of the present page (so we can get
722 back) and a status message describing why the password form was
723 called. If an URL is given, the two values will be passed using a
724 query string (?caller_url=...&msg=...) in the redirection.
725
726 Status messages: password authentication required
727 reauthentication
728 invalid cookie
729
730 =item *
731
732 B<REQCERT_FAIL_HANDLER>: URL or reference to a function to call when a page secured by
733 reqAuthnCert() is encountered and the client is not certificate authenticated.
734 Typically, to display some diagnostic message.
735
736 =item *
737
738 B<LOGFILE>: Filename of the logfile
739
740 =item *
741
742 B<LOGLEVEL>: Integer value from 0-5
743
744 0: no log messages at all
745 1: error and security relevant messages only
746 3: Logs password authentications (standard log level)
747 5: debugging messages
748
749 =item *
750
751 For the MySQL implementation you can also supply the DB connection parameters
752 B<DBHOST, DBPORT, DBNAME, DBUSER, DBPASS>
753
754 =item *
755
756 For the SQLite implementation you only need to supply a B<DBFILE> parameter
757 with the location of the data base file.
758
759 =back
760
761 =head2 Calling the password form via a web page 'Login' link:
762
763 You can pass B<SecModPwd=1> as a GET variable to any page using the
764 SecurityModule. This will call the handler for / redirect to the password form
765 and insure that the user can return to the same page.
766
767
768 =head1 AUTHOR
769
770 Derek Feichtinger <derek.feichtinger@psi.ch>
771
772 CMS web interfaces group <hn-cms-webInterfaces@cern.ch>
773
774
775 =head1 ISSUES / TODO
776
777 List of issues to resolve:
778
779 =over 4
780
781 =item *
782
783 POST arguments are not carried across the password form
784
785 =item *
786
787 The mapping from username to certificate distinguished name needs to be established
788 separately.
789
790 =item *
791
792 Look at "TODO" comments in the code.
793
794 =back