37 |
|
$self->{CALLER_URL}=""; # true URL (including any proxy transform.) |
38 |
|
|
39 |
|
$self->{ERRMSG} = ""; |
40 |
< |
$self->{LOGFILE} = "/tmp/SecMod.log"; #maybe default to apache log |
40 |
> |
$self->{LOGFILE} = undef; # defaults to stderr (apache err log) |
41 |
|
$self->{LOGH} = undef; # Log filehandle |
42 |
|
$self->{LOGLEVEL} = 1; |
43 |
|
|
316 |
|
} |
317 |
|
|
318 |
|
my ($state,$user,$time,$orighash) = $self->_decryptCookie($cookie); |
319 |
< |
#TODO test cookie time in addition to key validity time? |
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; |
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 "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 |
< |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); |
356 |
< |
$year+=1900; |
357 |
< |
$mon++; |
366 |
> |
|
367 |
|
#TODO: better log date formatting |
368 |
< |
print {$self->{LOGH}} "$year-$mon-$mday $hour:$min:$sec (L$level)" . $msg . "\n"; |
368 |
> |
print {$self->{LOGH}} "$self->{REMOTE_ADDR} $date (L$level)" . $msg . "\n"; |
369 |
|
|
370 |
|
return 0; |
371 |
|
} |
508 |
|
if ($dbpasswd eq $hash) { |
509 |
|
$self->{AUTHNSTATE} = "passwd"; |
510 |
|
if ($self->{USERDN} = $self->_getDNfromUsername($username)) { |
511 |
< |
#TODO: set correct expires value |
511 |
> |
# for now we use no 'expires' value -expires =>'', |
512 |
|
$self->{COOKIE} = cookie(-name => "SecMod", |
513 |
|
-value => $self->_prepareCookie(), |
505 |
– |
-expires =>'+5h', |
514 |
|
-secure => 1 |
515 |
|
); |
516 |
+ |
$self->_log(4,"Successful password authentication by $username for $self->{USERDN}"); |
517 |
|
return(1); |
518 |
|
} |
519 |
|
|
520 |
< |
|
512 |
< |
#TODO: if the entry is not present we should probably set it based |
513 |
< |
# on the current USERDN from the SSL authentication |
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); |
583 |
|
if ($authnstate eq "passwd") { |
584 |
|
print " (and you are logged in via password)"; |
585 |
|
} else { |
586 |
< |
print $q->p,"Your Browser presented: " . $userdn if $userdn; |
586 |
> |
print $q->p,"Your Browser presented this certificate: " . $userdn if $userdn; |
587 |
|
} |
588 |
|
print $q->end_html; |
589 |
|
exit(0); |
617 |
|
|
618 |
|
$errmsg = $sec->getErrMsg(); # returns error message |
619 |
|
|
620 |
< |
# if getCookie() is defined, your page needs to make sure that this |
621 |
< |
# cookie will be set using CGI.pm's header(-cookie => $cookie ) |
620 |
> |
# 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 |
|
if( ($cookie=$sec->getCookie) ) { |
624 |
|
print header(-cookie => $cookie ); |
625 |
|
} else { |
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 |
< |
$roles = $sec->getRoles(); # returns a hash of roles, each role pointing to a |
633 |
> |
$roles = $sec->getRoles(); # returns a hash of roles, each role mapping to a |
634 |
|
# list of scopes |
635 |
|
|
636 |
|
|
647 |
|
=head1 DESCRIPTION |
648 |
|
|
649 |
|
The SecurityModule handles authentication and authorization to a web site. Users |
650 |
< |
are identified via a certificate loaded in their browser or via a previously |
650 |
> |
are identified by a certificate loaded in their browser or by a previously |
651 |
|
set cookie that was issued upon a successful password authentication. |
652 |
|
|
653 |
+ |
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 |
|
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 |
< |
the SSL authentication and sets the following request headers to |
660 |
> |
the SSL authentication and is required to set the following request headers to |
661 |
|
the values of the respective environment variables for this request: |
662 |
|
|
663 |
|
B<SSL_CLIENT_VERIFY>, |
683 |
|
|
684 |
|
=item * |
685 |
|
|
686 |
< |
B<CALLER_URL>: URL of the current page that was invoked by the browser, i.e. it |
687 |
< |
must contain the URL which the reverse proxy got before redirecting to the backend |
688 |
< |
server. |
686 |
> |
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 |
|
|
690 |
|
=item * |
691 |
|
|
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 |
< |
Status messages: password authentication required, reauthentication, invalid cookie |
727 |
> |
Status messages: password authentication required |
728 |
> |
reauthentication |
729 |
> |
invalid cookie |
730 |
|
|
731 |
|
=item * |
732 |
|
|
740 |
|
|
741 |
|
=item * |
742 |
|
|
743 |
< |
B<LOGLEVEL>: (from 1 to 5) |
743 |
> |
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 |
|
|
750 |
|
=item * |
751 |
|
|
759 |
|
|
760 |
|
=back |
761 |
|
|
762 |
< |
=head2 Calling the password form via a page "Login" link: |
762 |
> |
=head2 Calling the password form via a web page "Login" link: |
763 |
|
|
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 |
785 |
|
|
786 |
|
=item * |
787 |
|
|
788 |
< |
The mapping from username to certificate distinguished name needs to be established, |
789 |
< |
either by letting a cert authenticated user log in and storing his username, or by |
769 |
< |
providing a special page where the user can password authenticate and establish the |
770 |
< |
mapping. |
788 |
> |
The mapping from username to certificate distinguished name needs to be established |
789 |
> |
separately. |
790 |
|
|
791 |
|
=item * |
792 |
|
|