<?php
/**
* LDAP
*
* This module implements several functions to work with LDAP servers.
*
* @package Engine
* @subpackage LDAP
*/
//--------------------------------------------------------------------------------------------------
//
// eTraxis - Records tracking web-based system.
// Copyright (C) 2005-2009 by Artem Rodygin
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//--------------------------------------------------------------------------------------------------
// Author Date Description of modifications
//--------------------------------------------------------------------------------------------------
// Artem Rodygin 2005-08-14 new-003: Authentication with Active Directory.
// Artem Rodygin 2005-08-22 new-040: LDAP: list of Organization Units should be supported.
// Artem Rodygin 2005-08-29 bug-067: AD user cannot login using second BASEDN from BASEDN list.
// Artem Rodygin 2005-08-29 new-070: The 'ldap_finduser' should not generate 'ERROR' debug log when LDAP server returns no results.
// Artem Rodygin 2005-08-29 new-071: The 'ldap_login' should not generate 'ERROR' debug log when user credentials are invalid.
// Artem Rodygin 2005-09-01 bug-079: String database columns are not enough to store UTF-8 values.
// Artem Rodygin 2005-08-29 new-088: The 'ldap_finduser' should not generate 'ERROR' debug log when LDAP server returns no results.
// Artem Rodygin 2005-09-22 new-141: Source code review.
// Artem Rodygin 2005-11-13 bug-177: Multibyte string functions should be used instead of 'eregi' and 'split'.
// Artem Rodygin 2006-01-24 new-204: Active Directory Support functionality (new-003) should be conditionally "compiled".
// Artem Rodygin 2006-07-14 new-206: User password should not be stored in client cookies.
// Artem Rodygin 2006-08-07 bug-300: Cannot login with Active Directory credentials.
// Artem Rodygin 2006-10-17 new-363: Multiple BASE DNs are obsolete.
// Artem Rodygin 2006-11-11 bug-378: LDAP: BASE DN is not multiple anymore (new-363).
// Artem Rodygin 2006-11-18 bug-389: Motorola LDAP server returns "Insufficient rights" error.
// Artem Rodygin 2006-12-27 bug-471: The 'displayName' LDAP attribute should be used instead of 'CN' one.
// Daniel Jungbluth 2007-10-08 new-594: [SF1809444] Assigning users to groups via listbox
// Artem Rodygin 2007-10-12 bug-598: PHP Notice: Undefined index: mail
// Artem Rodygin 2008-10-12 new-751: LDAP // Multiple Base DN support.
// Artem Rodygin 2009-03-11 bug-799: eTraxis doesn't work with XAMPP on Windows.
// Artem Rodygin 2009-07-29 new-832: Required LDAP attributes should be configurable.
// Artem Rodygin 2009-10-12 new-848: LDAP TLS support.
//--------------------------------------------------------------------------------------------------
/**#@+
* Dependency.
*/
require_once('../engine/config.php');
require_once('../engine/debug.php');
require_once('../engine/utility.php');
/**#@-*/
//--------------------------------------------------------------------------------------------------
// Definitions.
//--------------------------------------------------------------------------------------------------
/**#@+
* LDAP error code.
*/
define('LDAP_SUCCESS', 0x00);
define('LDAP_OPERATIONS_ERROR', 0x01);
define('LDAP_PROTOCOL_ERROR', 0x02);
define('LDAP_TIMELIMIT_EXCEEDED', 0x03);
define('LDAP_SIZELIMIT_EXCEEDED', 0x04);
define('LDAP_COMPARE_FALSE', 0x05);
define('LDAP_COMPARE_TRUE', 0x06);
define('LDAP_AUTH_METHOD_NOT_SUPPORTED', 0x07);
define('LDAP_STRONG_AUTH_REQUIRED', 0x08);
define('LDAP_PARTIAL_RESULTS', 0x09);
define('LDAP_REFERRAL', 0x0A);
define('LDAP_ADMINLIMIT_EXCEEDED', 0x0B);
define('LDAP_UNAVAILABLE_CRITICAL_EXTENSION', 0x0C);
define('LDAP_CONFIDENTIALITY_REQUIRED', 0x0D);
define('LDAP_SASL_BIND_INPROGRESS', 0x0E);
define('LDAP_NO_SUCH_ATTRIBUTE', 0x10);
define('LDAP_UNDEFINED_TYPE', 0x11);
define('LDAP_INAPPROPRIATE_MATCHING', 0x12);
define('LDAP_CONSTRAINT_VIOLATION', 0x13);
define('LDAP_TYPE_OR_VALUE_EXISTS', 0x14);
define('LDAP_INVALID_SYNTAX', 0x15);
define('LDAP_NO_SUCH_OBJECT', 0x20);
define('LDAP_ALIAS_PROBLEM', 0x21);
define('LDAP_INVALID_DN_SYNTAX', 0x22);
define('LDAP_IS_LEAF', 0x23);
define('LDAP_ALIAS_DEREF_PROBLEM', 0x24);
define('LDAP_INAPPROPRIATE_AUTH', 0x30);
define('LDAP_INVALID_CREDENTIALS', 0x31);
define('LDAP_INSUFFICIENT_ACCESS', 0x32);
define('LDAP_BUSY', 0x33);
define('LDAP_UNAVAILABLE', 0x34);
define('LDAP_UNWILLING_TO_PERFORM', 0x35);
define('LDAP_LOOP_DETECT', 0x36);
define('LDAP_SORT_CONTROL_MISSING', 0x3C);
define('LDAP_INDEX_RANGE_ERROR', 0x3D);
define('LDAP_NAMING_VIOLATION', 0x40);
define('LDAP_OBJECT_CLASS_VIOLATION', 0x41);
define('LDAP_NOT_ALLOWED_ON_NONLEAF', 0x42);
define('LDAP_NOT_ALLOWED_ON_RDN', 0x43);
define('LDAP_ALREADY_EXISTS', 0x44);
define('LDAP_NO_OBJECT_CLASS_MODS', 0x45);
define('LDAP_RESULTS_TOO_LARGE', 0x46);
define('LDAP_AFFECTS_MULTIPLE_DSAS', 0x47);
define('LDAP_OTHER', 0x50);
define('LDAP_SERVER_DOWN', 0x51);
define('LDAP_LOCAL_ERROR', 0x52);
define('LDAP_ENCODING_ERROR', 0x53);
define('LDAP_DECODING_ERROR', 0x54);
define('LDAP_TIMEOUT', 0x55);
define('LDAP_AUTH_UNKNOWN', 0x56);
define('LDAP_FILTER_ERROR', 0x57);
define('LDAP_USER_CANCELLED', 0x58);
define('LDAP_PARAM_ERROR', 0x59);
define('LDAP_NO_MEMORY', 0x5A);
define('LDAP_CONNECT_ERROR', 0x5B);
define('LDAP_NOT_SUPPORTED', 0x5C);
define('LDAP_CONTROL_NOT_FOUND', 0x5D);
define('LDAP_NO_RESULTS_RETURNED', 0x5E);
define('LDAP_MORE_RESULTS_TO_RETURN', 0x5F);
define('LDAP_CLIENT_LOOP', 0x60);
define('LDAP_REFERRAL_LIMIT_EXCEEDED', 0x61);
/**#@-*/
//--------------------------------------------------------------------------------------------------
// Functions.
//--------------------------------------------------------------------------------------------------
/**
* Searches for specified username on LDAP server.
*
* The function searches for specified <i>username</i>.
* If user is found, then his display name and email address are returned, otherwise NULL is returned.
* If <i>password</i> is specified, then function also tries to authorize on LDAP server using specified <i>username</i> and <i>password</i>.
* If authorization is failed, NULL is returned, even when user with specified <i>username</i> was successfully found.
*
* @param string $username Login of user to be found.
* @param string $password Password of user.
* @return array The array which contains two items: first item is display name of user, second one - his email.
* If user was not found, or cannot be authorized with specified password, then NULL is returned.
*/
function ldap_finduser ($username, $password = NULL)
{
debug_write_log(DEBUG_TRACE, '[ldap_finduser]');
debug_write_log(DEBUG_DUMP, '[ldap_finduser] $username = ' . $username);
$link = @ldap_connect(LDAP_HOST, LDAP_PORT);
if (!$link)
{
debug_write_log(DEBUG_ERROR, '[ldap_finduser] ldap_connect() error.');
return NULL;
}
$retval = NULL;
if (!@ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, 3))
{
debug_write_log(DEBUG_ERROR, '[ldap_finduser] ldap_set_option(LDAP_OPT_PROTOCOL_VERSION) error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (!@ldap_set_option($link, LDAP_OPT_REFERRALS, 0))
{
debug_write_log(DEBUG_ERROR, '[ldap_finduser] ldap_set_option(LDAP_OPT_REFERRALS) error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (LDAP_USE_TLS && !@ldap_start_tls($link))
{
debug_write_log(DEBUG_ERROR, '[ldap_finduser] ldap_start_tls() error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (!@ldap_bind($link, LDAP_USERNAME, LDAP_PASSWORD))
{
debug_write_log(DEBUG_WARNING, '[ldap_finduser] ldap_bind(anonymous) error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
$attrs = array('dn', LDAP_ATTR_FULLNAME, LDAP_ATTR_EMAIL);
$basedn = mb_split(';', LDAP_BASEDN);
for ($i = 0; $i < count($basedn) && is_null($retval); $i++)
{
debug_write_log(DEBUG_DUMP, '[ldap_finduser] $basedn = ' . $basedn[$i]);
$result = @ldap_search($link, $basedn[$i], sprintf("(%s=%s)", LDAP_ATTR_LOGIN, $username), $attrs);
if (!$result)
{
debug_write_log(DEBUG_WARNING, '[ldap_finduser] ldap_search() error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
$entries = @ldap_get_entries($link, $result);
if (!$entries || count($entries) <= 1)
{
debug_write_log(DEBUG_WARNING, '[ldap_finduser] ldap_get_entries() error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (!is_null($password) && !@ldap_bind($link, $entries[0]['dn'], $password))
{
debug_write_log(DEBUG_WARNING, '[ldap_finduser] ldap_bind(username) error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
if (empty($entries[0][LDAP_ATTR_FULLNAME][0]) ||
empty($entries[0][LDAP_ATTR_EMAIL ][0]))
{
debug_write_log(DEBUG_NOTICE, '[ldap_finduser] Found entries are empty.');
}
else
{
debug_write_log(DEBUG_DUMP, '[ldap_finduser] LDAP(displayname) = ' . $entries[0][LDAP_ATTR_FULLNAME][0]);
debug_write_log(DEBUG_DUMP, '[ldap_finduser] LDAP(mail) = ' . $entries[0][LDAP_ATTR_EMAIL ][0]);
$retval = array($entries[0][LDAP_ATTR_FULLNAME][0],
$entries[0][LDAP_ATTR_EMAIL ][0]);
}
}
}
}
}
ldap_close($link);
return $retval;
}
/**
* Searches for all users of LDAP server and returns array with all findings.
*
* If login, display name, or email of some LDAP user is empty, it will not be returned.
*
* @return array Array, where each item is associative array with two items.
* First item is user's login and accessable via "username" index.
* Second item is user's display name and accessable via "fullname" index.
*/
function ldap_findallusers ()
{
debug_write_log(DEBUG_TRACE, '[ldap_findallusers]');
$link = @ldap_connect(LDAP_HOST, LDAP_PORT);
if (!$link)
{
debug_write_log(DEBUG_ERROR, '[ldap_findallusers] ldap_connect() error.');
return NULL;
}
$retval = array();
if (!@ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, 3))
{
debug_write_log(DEBUG_ERROR, '[ldap_findallusers] ldap_set_option(LDAP_OPT_PROTOCOL_VERSION) error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (!@ldap_set_option($link, LDAP_OPT_REFERRALS, 0))
{
debug_write_log(DEBUG_ERROR, '[ldap_findallusers] ldap_set_option(LDAP_OPT_REFERRALS) error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (LDAP_USE_TLS && !@ldap_start_tls($link))
{
debug_write_log(DEBUG_ERROR, '[ldap_finduser] ldap_start_tls() error: ' . ldap_err2str(ldap_errno($link)));
}
elseif (!@ldap_bind($link, LDAP_USERNAME, LDAP_PASSWORD))
{
debug_write_log(DEBUG_WARNING, '[ldap_findallusers] ldap_bind(anonymous) error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
$attrs = array(LDAP_ATTR_LOGIN, LDAP_ATTR_FULLNAME, LDAP_ATTR_EMAIL);
$basedn = mb_split(';', LDAP_BASEDN);
for ($i = 0; $i < count($basedn); $i++)
{
debug_write_log(DEBUG_DUMP, '[ldap_findallusers] $basedn = ' . $basedn[$i]);
$result = @ldap_search($link, $basedn[$i], sprintf("(&(objectcategory=person)(objectclass=user)(%s=*))", LDAP_ATTR_LOGIN), $attrs);
if (!$result)
{
debug_write_log(DEBUG_WARNING, '[ldap_findallusers] ldap_search() error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
$entries = @ldap_get_entries($link, $result);
if (!$entries || count($entries) <= 1)
{
debug_write_log(DEBUG_WARNING, '[ldap_findallusers] ldap_get_entries() error: ' . ldap_err2str(ldap_errno($link)));
}
else
{
for ($i = 0; $i < count($entries) - 1; $i++)
{
if (!empty($entries[$i][LDAP_ATTR_LOGIN ][0]) &&
!empty($entries[$i][LDAP_ATTR_FULLNAME][0]) &&
!empty($entries[$i][LDAP_ATTR_EMAIL ][0]))
{
$entry = array('username' => $entries[$i][LDAP_ATTR_LOGIN ][0],
'fullname' => $entries[$i][LDAP_ATTR_FULLNAME][0]);
array_push($retval, $entry);
}
}
}
}
}
}
ldap_close($link);
return $retval;
}
?>