# $Id: AuthenLDAP.pm,v 1.10 2001/07/12 14:14:15 cgilmore Exp $
#
# Author          : Jason Bodnar, Christian Gilmore
# Created On      : Dec 08 12:04:00 CDT 1999
# Status          : Functional
# 
# PURPOSE
#    LDAP User Authentication
#
###############################################################################
#
# IBM Public License Version 1.0 
# 
# THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS IBM
# PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
# DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF
# THIS AGREEMENT.
# 
# 1. DEFINITIONS 
# 
# "Contribution" means: 
# 
#   a) in the case of International Business Machines Corporation 
#   ("IBM"), the Original Program, and 
# 
#   b) in the case of each Contributor, 
# 
#   i) changes to the Program, and
# 
#   ii) additions to the Program;
# 
#   where such changes and/or additions to the Program originate from
#   and are distributed by that particular Contributor. A Contribution 
#   'originates' from a Contributor if it was added to the Program by 
#   such Contributor itself or anyone acting on such Contributor's 
#   behalf. Contributions do not include additions to the Program 
#   which: (i) are separate modules of software distributed in
#   conjunction with the Program under their own license agreement, 
#   and (ii) are not derivative works of the Program.
# 
# "Contributor" means IBM and any other entity that distributes the
# Program.
# 
# "Licensed Patents " mean patent claims licensable by a Contributor 
# which are necessarily infringed by the use or sale of its 
# Contribution alone or when combined with the Program. 
# 
# "Original Program" means the original version of the software 
# accompanying this Agreement as released by IBM, including source 
# code, object code and documentation, if any. 
# 
# "Program" means the Original Program and Contributions. 
# 
# "Recipient" means anyone who receives the Program under this 
# Agreement, including all Contributors. 
# 
# 2. GRANT OF RIGHTS 
# 
#   a) Subject to the terms of this Agreement, each Contributor hereby 
#   grants Recipient a non-exclusive, worldwide, royalty-free 
#   copyright license to reproduce, prepare derivative works of, 
#   publicly display, publicly perform, distribute and sublicense the 
#   Contribution of such Contributor, if any, and such derivative 
#   works, in source code and object code form.
# 
#   b) Subject to the terms of this Agreement, each Contributor hereby 
#   grants Recipient a non-exclusive, worldwide, royalty-free patent 
#   license under Licensed Patents to make, use, sell, offer to sell, 
#   import and otherwise transfer the Contribution of such 
#   Contributor, if any, in source code and object code form. This 
#   patent license shall apply to the combination of the Contribution 
#   and the Program if, at the time the Contribution is added by the 
#   Contributor, such addition of the Contribution causes such 
#   combination to be covered by the Licensed Patents. The patent 
#   license shall not apply to any other combinations which include 
#   the Contribution. No hardware per se is licensed hereunder. 
# 
#   c) Recipient understands that although each Contributor grants the 
#   licenses to its Contributions set forth herein, no assurances are 
#   provided by any Contributor that the Program does not infringe the 
#   patent or other intellectual property rights of any other entity. 
#   Each Contributor disclaims any liability to Recipient for claims 
#   brought by any other entity based on infringement of intellectual 
#   property rights or otherwise. As a condition to exercising the 
#   rights and licenses granted hereunder, each Recipient hereby 
#   assumes sole responsibility to secure any other intellectual 
#   property rights needed, if any. For example, if a third party 
#   patent license is required to allow Recipient to distribute the 
#   Program, it is Recipient's responsibility to acquire that license 
#   before distributing the Program.
# 
#   d) Each Contributor represents that to its knowledge it has 
#   sufficient copyright rights in its Contribution, if any, to grant 
#   the copyright license set forth in this Agreement. 
# 
# 3. REQUIREMENTS 
# 
# A Contributor may choose to distribute the Program in object code 
# form under its own license agreement, provided that: 
# 
#   a) it complies with the terms and conditions of this Agreement; 
#
# and
# 
#   b) its license agreement:
# 
#   i) effectively disclaims on behalf of all Contributors all 
#   warranties and conditions, express and implied, including 
#   warranties or conditions of title and non-infringement, and 
#   implied warranties or conditions of merchantability and fitness 
#   for a particular purpose; 
# 
#   ii) effectively excludes on behalf of all Contributors all 
#   liability for damages, including direct, indirect, special, 
#   incidental and consequential damages, such as lost profits; 
#   iii) states that any provisions which differ from this Agreement 
#   are offered by that Contributor alone and not by any other party; 
#   and
# 
#   iv) states that source code for the Program is available from such 
#   Contributor, and informs licensees how to obtain it in a 
#   reasonable manner on or through a medium customarily used for 
#   software exchange. 
# 
# When the Program is made available in source code form: 
# 
#   a) it must be made available under this Agreement; and 
# 
#   b) a copy of this Agreement must be included with each copy of the 
#   Program. 
# 
# Each Contributor must include the following in a conspicuous 
# location in the Program: 
# 
#   Copyright © {date here}, International Business Machines 
#   Corporation and others. All Rights Reserved. 
# 
# In addition, each Contributor must identify itself as the originator 
# of its Contribution, if any, in a manner that reasonably allows 
# subsequent Recipients to identify the originator of the 
# Contribution. 
# 
# 4. COMMERCIAL DISTRIBUTION 
# 
# Commercial distributors of software may accept certain 
# responsibilities with respect to end users, business partners and 
# the like. While this license is intended to facilitate the 
# commercial use of the Program, the Contributor who includes the 
# Program in a commercial product offering should do so in a manner 
# which does not create potential liability for other Contributors. 
# Therefore, if a Contributor includes the Program in a commercial 
# product offering, such Contributor ("Commercial Contributor") hereby 
# agrees to defend and indemnify every other Contributor ("Indemnified 
# Contributor") against any losses, damages and costs (collectively 
# "Losses") arising from claims, lawsuits and other legal actions 
# brought by a third party against the Indemnified Contributor to the 
# extent caused by the acts or omissions of such Commercial 
# Contributor in connection with its distribution of the Program in a 
# commercial product offering. The obligations in this section do not 
# apply to any claims or Losses relating to any actual or alleged 
# intellectual property infringement. In order to qualify, an 
# Indemnified Contributor must: a) promptly notify the Commercial 
# Contributor in writing of such claim, and b) allow the Commercial 
# Contributor to control, and cooperate with the Commercial 
# Contributor in, the defense and any related settlement negotiations. 
# The Indemnified Contributor may participate in any such claim at its 
# own expense. 
# 
# For example, a Contributor might include the Program in a commercial 
# product offering, Product X. That Contributor is then a Commercial 
# Contributor. If that Commercial Contributor then makes performance 
# claims, or offers warranties related to Product X, those performance 
# claims and warranties are such Commercial Contributor's 
# responsibility alone. Under this section, the Commercial Contributor 
# would have to defend claims against the other Contributors related 
# to those performance claims and warranties, and if a court requires 
# any other Contributor to pay any damages as a result, the Commercial 
# Contributor must pay those damages. 
# 
# 5. NO WARRANTY 
# 
# EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 
# PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 
# ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
# ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient 
# is solely responsible for determining the appropriateness of using 
# and distributing the Program and assumes all risks associated with 
# its exercise of rights under this Agreement, including but not 
# limited to the risks and costs of program errors, compliance with 
# applicable laws, damage to or loss of data, programs or equipment, 
# and unavailability or interruption of operations. 
# 
# 6. DISCLAIMER OF LIABILITY 
# 
# EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 
# NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, 
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
# (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON 
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
# THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 
# GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
# DAMAGES. 
# 
# 7. GENERAL 
# 
# If any provision of this Agreement is invalid or unenforceable under 
# applicable law, it shall not affect the validity or enforceability 
# of the remainder of the terms of this Agreement, and without further 
# action by the parties hereto, such provision shall be reformed to 
# the minimum extent necessary to make such provision valid and 
# enforceable. 
# 
# If Recipient institutes patent litigation against a Contributor with 
# respect to a patent applicable to software (including a cross-claim 
# or counterclaim in a lawsuit), then any patent licenses granted by 
# that Contributor to such Recipient under this Agreement shall 
# terminate as of the date such litigation is filed. In addition, If 
# Recipient institutes patent litigation against any entity (including 
# a cross-claim or counterclaim in a lawsuit) alleging that the 
# Program itself (excluding combinations of the Program with other 
# software or hardware) infringes such Recipient's patent(s), then 
# such Recipient's rights granted under Section 2(b) shall terminate 
# as of the date such litigation is filed. 
# 
# All Recipient's rights under this Agreement shall terminate if it 
# fails to comply with any of the material terms or conditions of this 
# Agreement and does not cure such failure in a reasonable period of 
# time after becoming aware of such noncompliance. If all Recipient's 
# rights under this Agreement terminate, Recipient agrees to cease use 
# and distribution of the Program as soon as reasonably practicable. 
# However, Recipient's obligations under this Agreement and any 
# licenses granted by Recipient relating to the Program shall continue 
# and survive. 
# 
# IBM may publish new versions (including revisions) of this Agreement 
# from time to time. Each new version of the Agreement will be given a 
# distinguishing version number. The Program (including Contributions) 
# may always be distributed subject to the version of the Agreement 
# under which it was received. In addition, after a new version of the 
# Agreement is published, Contributor may elect to distribute the 
# Program (including its Contributions) under the new version. No one 
# other than IBM has the right to modify this Agreement. Except as 
# expressly stated in Sections 2(a) and 2(b) above, Recipient receives 
# no rights or licenses to the intellectual property of any 
# Contributor under this Agreement, whether expressly, by implication, 
# estoppel or otherwise. All rights in the Program not expressly 
# granted under this Agreement are reserved. 
# 
# This Agreement is governed by the laws of the State of New York and 
# the intellectual property laws of the United States of America. No 
# party to this Agreement will bring a legal action under this 
# Agreement more than one year after the cause of action arose. Each 
# party waives its rights to a jury trial in any resulting litigation. 
#
###############################################################################


# Package name
package Apache::AuthenLDAP;


# Required libraries
use strict;
use mod_perl ();
use Apache::Constants ':common';
use Apache::Log ();
use Net::LDAP qw(:all);
use Data::Dumper;


# Global variables
$Apache::AuthenLDAP::VERSION = '0.61';


###############################################################################
###############################################################################
# handler: hook into Apache/mod_perl API
###############################################################################
###############################################################################
sub handler {
  my $r = shift;
  return OK unless $r->is_initial_req; # only the first internal request
  my ($res, $sent_pwd) = $r->get_basic_auth_pw;
  return $res if $res;
  
  my $name = $r->connection->user;
  unless ($name) {
    $r->note_basic_auth_failure;
    $r->log_reason("no username supplied", $r->uri);
    return AUTH_REQUIRED;
  }
  
  # The required patch was not introduced in 1.26. It is no longer
  # promised to be included in any timeframe. Commenting out.
  # if ($mod_perl::VERSION < 1.26) {
    # I shouldn't need to use the below lines as this module
    # should never be called if there was a cache hit.  Since
    # set_handlers() doesn't work properly until 1.26 (according
    # to Doug MacEachern) I have to work around it by cobbling
    # together cheat sheets for the previous and subsequent
    # handlers in this phase. I get the willies about the
    # security implications in a general environment where you
    # might be using someone else's handlers upstream or
    # downstream...
  my $cache_result = $r->notes('AuthenCache');
  if ($cache_result eq 'hit') {
    $r->log->debug("handler: upstream cache hit for username=$name");
    return OK;
  }
  # }

  my $basedn = $r->dir_config('AuthenBaseDN') || "";
  my $ldapserver = $r->dir_config('AuthenLDAPServer') ||
    $r->dir_config('LDAPServer') || "localhost";
  my $ldapport = $r->dir_config('AuthenLDAPPort') ||
    $r->dir_config('LDAPPort') || 389;
  my $uidattrtype = $r->dir_config('AuthenUidAttrType') ||
    $r->dir_config('UidAttrType') || "uid";

  $r->log->debug("AuthenBaseDN - $basedn; LDAPServer - $ldapserver; ", 
		 "LDAPPort - $ldapport; UiaDttrType - $uidattrtype");

  if ($sent_pwd eq "") {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: no password supplied", $r->uri);
    return AUTH_REQUIRED;
  }
  
  # Connect to the server
  my $ld;
  unless ($ld = new Net::LDAP($ldapserver, port => $ldapport)) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: LDAP Connection Failed", $r->uri);
    return SERVER_ERROR;
  }
  
  # Bind anonymously
  my $msg = $ld->bind;
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: LDAP Initial Bind Failed: " . $msg->code . 
		   " " . $msg->error, $r->uri);
    return SERVER_ERROR;
  }
  
  # Create the filter and search
  my $filter = "($uidattrtype=$name)";
  $r->log->debug("Using filter: $filter");
  $msg = $ld->search(base => $basedn, filter => $filter);
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: ldap search operation failed: " .
		    $msg->code . " " . $msg->error, $r->uri);
    return SERVER_ERROR;
  }

  # Did we receive any entries
  unless ($msg->count) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: username not found",$r->uri);
    return AUTH_REQUIRED;
  }
  
  # Only want the first if we've received more than one
  my $entry = $msg->first_entry;
  my $dn = $entry->dn;

  # Bind as the user we're authenticating
  $msg = $ld->bind($dn, password => $sent_pwd);
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: password mismatch", $r->uri);
    return AUTH_REQUIRED;
  }
  $ld->unbind;

  # Everything's A-OK
  return OK;
}

1;

__END__

# Documentation - try 'pod2text AuthenLDAP'

=head1 NAME

Apache::AuthenLDAP - mod_perl LDAP Authentication Module

=head1 SYNOPSIS

 <Directory /foo/bar>
 # Authentication Realm and Type (only Basic supported)
 AuthName "Foo Bar Authentication"
 AuthType Basic

 # Any of the following variables can be set.
 # Defaults are listed to the right.
 PerlSetVar AuthenBaseDN     o=Foo,c=Bar  # Default: Empty String ("")
 PerlSetVar AuthenLDAPServer ldap.foo.com # Default: localhost
 PerlSetVar AuthenLDAPPort   389          # Default: 389 (standard LDAP port)
 PerlSetVar UidattrType      userid       # Default: uid

 PerlAuthenHandler Apache::AuthenLDAP

 require valid-user                       # Any Valid LDAP User
                                          # Matching Attribute and Value
 </Directory>

=head1 DESCRIPTION

B<Apache::AuthenLDAP> is designed to work with mod_perl and
Net::LDAP. This module authenticates a user against an LDAP
backend. It can be combined with Apache::AuthzLDAP to provide
LDAP authorization as well.

=head1 CONFIGURATION OPTIONS

The following variables can be defined within the configuration
of Directory, Location, or Files blocks or within .htaccess
files.

=over 4

=item B<AuthenBaseDN>

The base distinguished name with which to query LDAP. By default,
the AuthenBaseDN is empty.

=back

=over 4

=item B<AuthenLDAPServer>

The hostname for the LDAP server to query. By default,
AuthenLDAPServer is set to localhost.

=back

=over 4

=item B<AuthenLDAPPort>

The port on which the LDAP server is listening. By default,
AuthenLDAPPort is set to 389.

=back

=over 4

=item B<AuthenUidAttrType>

The attribute type name that contains the user's
identification. By default, AuthenUidAttrType is set to uid.

=back

=head1 NOTES

This module has hooks built into it to handle Apache::AuthenCache
version 0.04 and higher passing notes to avoid bugs in the
set_handlers() method in mod_perl versions 1.2x.

=head1 AUTHORS

Jason Bodnar,
Christian Gilmore <cgilmore@tivoli.com>

=head1 See ALSO

httpd(8), ldap(3), mod_perl(1), slapd(8C)

=head1 COPYRIGHT
 
Copyright (C) 2001, International Business Machines Corporation
and others. All Rights Reserved.
 
This module is free software; you can redistribute it and/or
modify it under the terms of the IBM Public License.

=cut

###############################################################################
###############################################################################
# $Log: AuthenLDAP.pm,v $
# Revision 1.10  2001/07/12 14:14:15  cgilmore
# See ChangeLog
#
# Revision 1.9  2001/07/12 14:06:35  cgilmore
# see ChangeLog
#
# Revision 1.8  2001/05/27 19:38:24  cgilmore
# see ChangeLog
#
# Revision 1.7  2001/01/08 17:30:58  cgilmore
# added handling of blank userid and better handled set_handlers workaround
#
# Revision 1.6  2001/01/08 17:26:35  cgilmore
# released
#
# Revision 1.5  2000/09/26 18:26:54  cgilmore
# updated to Apache-general from Tivoli namespace. Updated pod.
#
# Revision 1.4  2000/08/26 15:33:04  cgilmore
# re-designed. See pod.
#
# Revision 1.3  2000/08/21 14:06:45  cgilmore
# *** empty log message ***
#
# Revision 1.2  2000/07/12 18:32:07  cgilmore
# Finally found a hack (using r->notes) around bugs in set_handlers()
# and lookup_uri(). See files for more.
#
# Revision 1.1  2000/06/01 16:19:04  cgilmore
# Initial revision
#
###############################################################################
###############################################################################


