[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/authldap/ -> auth.php (source)

   1  <?php
   2  
   3  /**
   4   * LDAP authentication backend
   5   *
   6   * @license   GPL 2 (http://www.gnu.org/licenses/gpl.html)
   7   * @author    Andreas Gohr <andi@splitbrain.org>
   8   * @author    Chris Smith <chris@jalakaic.co.uk>
   9   * @author    Jan Schumann <js@schumann-it.com>
  10   */
  11  class auth_plugin_authldap extends DokuWiki_Auth_Plugin
  12  {
  13      /* @var resource $con holds the LDAP connection */
  14      protected $con = null;
  15  
  16      /* @var int $bound What type of connection does already exist? */
  17      protected $bound = 0; // 0: anonymous, 1: user, 2: superuser
  18  
  19      /* @var array $users User data cache */
  20      protected $users = null;
  21  
  22      /* @var array $pattern User filter pattern */
  23      protected $pattern = null;
  24  
  25      /**
  26       * Constructor
  27       */
  28      public function __construct()
  29      {
  30          parent::__construct();
  31  
  32          // ldap extension is needed
  33          if (!function_exists('ldap_connect')) {
  34              $this->debug("LDAP err: PHP LDAP extension not found.", -1, __LINE__, __FILE__);
  35              $this->success = false;
  36              return;
  37          }
  38  
  39          // Add the capabilities to change the password
  40          $this->cando['modPass'] = $this->getConf('modPass');
  41      }
  42  
  43      /**
  44       * Check user+password
  45       *
  46       * Checks if the given user exists and the given
  47       * plaintext password is correct by trying to bind
  48       * to the LDAP server
  49       *
  50       * @param string $user
  51       * @param string $pass
  52       * @return  bool
  53       * @author  Andreas Gohr <andi@splitbrain.org>
  54       */
  55      public function checkPass($user, $pass)
  56      {
  57          // reject empty password
  58          if (empty($pass)) return false;
  59          if (!$this->openLDAP()) return false;
  60  
  61          // indirect user bind
  62          if ($this->getConf('binddn') && $this->getConf('bindpw')) {
  63              // use superuser credentials
  64              if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
  65                  $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
  66                  return false;
  67              }
  68              $this->bound = 2;
  69          } elseif ($this->getConf('binddn') &&
  70              $this->getConf('usertree') &&
  71              $this->getConf('userfilter')
  72          ) {
  73              // special bind string
  74              $dn = $this->makeFilter(
  75                  $this->getConf('binddn'),
  76                  array('user' => $user, 'server' => $this->getConf('server'))
  77              );
  78          } elseif (strpos($this->getConf('usertree'), '%{user}')) {
  79              // direct user bind
  80              $dn = $this->makeFilter(
  81                  $this->getConf('usertree'),
  82                  array('user' => $user, 'server' => $this->getConf('server'))
  83              );
  84          } else {
  85              // Anonymous bind
  86              if (!@ldap_bind($this->con)) {
  87                  msg("LDAP: can not bind anonymously", -1);
  88                  $this->debug('LDAP anonymous bind: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
  89                  return false;
  90              }
  91          }
  92  
  93          // Try to bind to with the dn if we have one.
  94          if (!empty($dn)) {
  95              // User/Password bind
  96              if (!@ldap_bind($this->con, $dn, $pass)) {
  97                  $this->debug("LDAP: bind with $dn failed", -1, __LINE__, __FILE__);
  98                  $this->debug('LDAP user dn bind: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
  99                  return false;
 100              }
 101              $this->bound = 1;
 102              return true;
 103          } else {
 104              // See if we can find the user
 105              $info = $this->fetchUserData($user, true);
 106              if (empty($info['dn'])) {
 107                  return false;
 108              } else {
 109                  $dn = $info['dn'];
 110              }
 111  
 112              // Try to bind with the dn provided
 113              if (!@ldap_bind($this->con, $dn, $pass)) {
 114                  $this->debug("LDAP: bind with $dn failed", -1, __LINE__, __FILE__);
 115                  $this->debug('LDAP user bind: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 116                  return false;
 117              }
 118              $this->bound = 1;
 119              return true;
 120          }
 121      }
 122  
 123      /**
 124       * Return user info
 125       *
 126       * Returns info about the given user needs to contain
 127       * at least these fields:
 128       *
 129       * name string  full name of the user
 130       * mail string  email addres of the user
 131       * grps array   list of groups the user is in
 132       *
 133       * This LDAP specific function returns the following
 134       * addional fields:
 135       *
 136       * dn     string  distinguished name (DN)
 137       * uid    string  Posix User ID
 138       * inbind bool    for internal use - avoid loop in binding
 139       *
 140       * @param string $user
 141       * @param bool $requireGroups (optional) - ignored, groups are always supplied by this plugin
 142       * @return  array containing user data or false
 143       * @author  <evaldas.auryla@pheur.org>
 144       * @author  Stephane Chazelas <stephane.chazelas@emerson.com>
 145       * @author  Steffen Schoch <schoch@dsb.net>
 146       *
 147       * @author  Andreas Gohr <andi@splitbrain.org>
 148       * @author  Trouble
 149       * @author  Dan Allen <dan.j.allen@gmail.com>
 150       */
 151      public function getUserData($user, $requireGroups = true)
 152      {
 153          return $this->fetchUserData($user);
 154      }
 155  
 156      /**
 157       * @param string $user
 158       * @param bool $inbind authldap specific, true if in bind phase
 159       * @return  array containing user data or false
 160       */
 161      protected function fetchUserData($user, $inbind = false)
 162      {
 163          global $conf;
 164          if (!$this->openLDAP()) return array();
 165  
 166          // force superuser bind if wanted and not bound as superuser yet
 167          if ($this->getConf('binddn') && $this->getConf('bindpw') && $this->bound < 2) {
 168              // use superuser credentials
 169              if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
 170                  $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 171                  return array();
 172              }
 173              $this->bound = 2;
 174          } elseif ($this->bound == 0 && !$inbind) {
 175              // in some cases getUserData is called outside the authentication workflow
 176              // eg. for sending email notification on subscribed pages. This data might not
 177              // be accessible anonymously, so we try to rebind the current user here
 178              list($loginuser, $loginsticky, $loginpass) = auth_getCookie();
 179              if ($loginuser && $loginpass) {
 180                  $loginpass = auth_decrypt($loginpass, auth_cookiesalt(!$loginsticky, true));
 181                  $this->checkPass($loginuser, $loginpass);
 182              }
 183          }
 184  
 185          $info = array();
 186          $info['user'] = $user;
 187          $this->debug('LDAP user to find: ' . hsc($info['user']), 0, __LINE__, __FILE__);
 188  
 189          $info['server'] = $this->getConf('server');
 190          $this->debug('LDAP Server: ' . hsc($info['server']), 0, __LINE__, __FILE__);
 191  
 192          //get info for given user
 193          $base = $this->makeFilter($this->getConf('usertree'), $info);
 194          if ($this->getConf('userfilter')) {
 195              $filter = $this->makeFilter($this->getConf('userfilter'), $info);
 196          } else {
 197              $filter = "(ObjectClass=*)";
 198          }
 199  
 200          $this->debug('LDAP Filter: ' . hsc($filter), 0, __LINE__, __FILE__);
 201  
 202          $this->debug('LDAP user search: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 203          $this->debug('LDAP search at: ' . hsc($base . ' ' . $filter), 0, __LINE__, __FILE__);
 204          $sr = $this->ldapSearch($this->con, $base, $filter, $this->getConf('userscope'), $this->getConf('attributes'));
 205  
 206  
 207          $result = @ldap_get_entries($this->con, $sr);
 208  
 209          // if result is not an array
 210          if (!is_array($result)) {
 211              // no objects found
 212              $this->debug('LDAP search returned non-array result: ' . hsc(print($result)), -1, __LINE__, __FILE__);
 213              return array();
 214          }
 215  
 216          // Don't accept more or less than one response
 217          if ($result['count'] != 1) {
 218              $this->debug(
 219                  'LDAP search returned ' . hsc($result['count']) . ' results while it should return 1!',
 220                  -1,
 221                  __LINE__,
 222                  __FILE__
 223              );
 224              //for($i = 0; $i < $result["count"]; $i++) {
 225              //$this->_debug('result: '.hsc(print_r($result[$i])), 0, __LINE__, __FILE__);
 226              //}
 227              return array();
 228          }
 229  
 230          $this->debug('LDAP search found single result !', 0, __LINE__, __FILE__);
 231  
 232          $user_result = $result[0];
 233          ldap_free_result($sr);
 234  
 235          // general user info
 236          $info['dn'] = $user_result['dn'];
 237          $info['gid'] = $user_result['gidnumber'][0];
 238          $info['mail'] = $user_result['mail'][0];
 239          $info['name'] = $user_result['cn'][0];
 240          $info['grps'] = array();
 241  
 242          // overwrite if other attribs are specified.
 243          if (is_array($this->getConf('mapping'))) {
 244              foreach ($this->getConf('mapping') as $localkey => $key) {
 245                  if (is_array($key)) {
 246                      // use regexp to clean up user_result
 247                      // $key = array($key=>$regexp), only handles the first key-value
 248                      $regexp = current($key);
 249                      $key = key($key);
 250                      if ($user_result[$key]) foreach ($user_result[$key] as $grpkey => $grp) {
 251                          if ($grpkey !== 'count' && preg_match($regexp, $grp, $match)) {
 252                              if ($localkey == 'grps') {
 253                                  $info[$localkey][] = $match[1];
 254                              } else {
 255                                  $info[$localkey] = $match[1];
 256                              }
 257                          }
 258                      }
 259                  } else {
 260                      $info[$localkey] = $user_result[$key][0];
 261                  }
 262              }
 263          }
 264          $user_result = array_merge($info, $user_result);
 265  
 266          //get groups for given user if grouptree is given
 267          if ($this->getConf('grouptree') || $this->getConf('groupfilter')) {
 268              $base = $this->makeFilter($this->getConf('grouptree'), $user_result);
 269              $filter = $this->makeFilter($this->getConf('groupfilter'), $user_result);
 270              $sr = $this->ldapSearch(
 271                  $this->con,
 272                  $base,
 273                  $filter,
 274                  $this->getConf('groupscope'),
 275                  array($this->getConf('groupkey'))
 276              );
 277              $this->debug('LDAP group search: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 278              $this->debug('LDAP search at: ' . hsc($base . ' ' . $filter), 0, __LINE__, __FILE__);
 279  
 280              if (!$sr) {
 281                  msg("LDAP: Reading group memberships failed", -1);
 282                  return array();
 283              }
 284              $result = ldap_get_entries($this->con, $sr);
 285              ldap_free_result($sr);
 286  
 287              if (is_array($result)) foreach ($result as $grp) {
 288                  if (!empty($grp[$this->getConf('groupkey')])) {
 289                      $group = $grp[$this->getConf('groupkey')];
 290                      if (is_array($group)) {
 291                          $group = $group[0];
 292                      } else {
 293                          $this->debug('groupkey did not return a detailled result', 0, __LINE__, __FILE__);
 294                      }
 295                      if ($group === '') continue;
 296  
 297                      $this->debug('LDAP usergroup: ' . hsc($group), 0, __LINE__, __FILE__);
 298                      $info['grps'][] = $group;
 299                  }
 300              }
 301          }
 302  
 303          // always add the default group to the list of groups
 304          if (!$info['grps'] or !in_array($conf['defaultgroup'], $info['grps'])) {
 305              $info['grps'][] = $conf['defaultgroup'];
 306          }
 307          return $info;
 308      }
 309  
 310      /**
 311       * Definition of the function modifyUser in order to modify the password
 312       *
 313       * @param string $user nick of the user to be changed
 314       * @param array $changes array of field/value pairs to be changed (password will be clear text)
 315       * @return  bool   true on success, false on error
 316       */
 317      public function modifyUser($user, $changes)
 318      {
 319  
 320          // open the connection to the ldap
 321          if (!$this->openLDAP()) {
 322              $this->debug('LDAP cannot connect: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 323              return false;
 324          }
 325  
 326          // find the information about the user, in particular the "dn"
 327          $info = $this->getUserData($user, true);
 328          if (empty($info['dn'])) {
 329              $this->debug('LDAP cannot find your user dn', 0, __LINE__, __FILE__);
 330              return false;
 331          }
 332          $dn = $info['dn'];
 333  
 334          // find the old password of the user
 335          list($loginuser, $loginsticky, $loginpass) = auth_getCookie();
 336          if ($loginuser !== null) { // the user is currently logged in
 337              $secret = auth_cookiesalt(!$loginsticky, true);
 338              $pass = auth_decrypt($loginpass, $secret);
 339  
 340              // bind with the ldap
 341              if (!@ldap_bind($this->con, $dn, $pass)) {
 342                  $this->debug(
 343                      'LDAP user bind failed: ' . hsc($dn) . ': ' . hsc(ldap_error($this->con)),
 344                      0,
 345                      __LINE__,
 346                      __FILE__
 347                  );
 348                  return false;
 349              }
 350          } elseif ($this->getConf('binddn') && $this->getConf('bindpw')) {
 351              // we are changing the password on behalf of the user (eg: forgotten password)
 352              // bind with the superuser ldap
 353              if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
 354                  $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 355                  return false;
 356              }
 357          } else {
 358              return false; // no otherway
 359          }
 360  
 361          // Generate the salted hashed password for LDAP
 362          $phash = new \dokuwiki\PassHash();
 363          $hash = $phash->hash_ssha($changes['pass']);
 364  
 365          // change the password
 366          if (!@ldap_mod_replace($this->con, $dn, array('userpassword' => $hash))) {
 367              $this->debug(
 368                  'LDAP mod replace failed: ' . hsc($dn) . ': ' . hsc(ldap_error($this->con)),
 369                  0,
 370                  __LINE__,
 371                  __FILE__
 372              );
 373              return false;
 374          }
 375  
 376          return true;
 377      }
 378  
 379      /**
 380       * Most values in LDAP are case-insensitive
 381       *
 382       * @return bool
 383       */
 384      public function isCaseSensitive()
 385      {
 386          return false;
 387      }
 388  
 389      /**
 390       * Bulk retrieval of user data
 391       *
 392       * @param int $start index of first user to be returned
 393       * @param int $limit max number of users to be returned
 394       * @param array $filter array of field/pattern pairs, null for no filter
 395       * @return  array of userinfo (refer getUserData for internal userinfo details)
 396       * @author  Dominik Eckelmann <dokuwiki@cosmocode.de>
 397       */
 398      public function retrieveUsers($start = 0, $limit = 0, $filter = array())
 399      {
 400          if (!$this->openLDAP()) return array();
 401  
 402          if (is_null($this->users)) {
 403              // Perform the search and grab all their details
 404              if ($this->getConf('userfilter')) {
 405                  $all_filter = str_replace('%{user}', '*', $this->getConf('userfilter'));
 406              } else {
 407                  $all_filter = "(ObjectClass=*)";
 408              }
 409              $sr = ldap_search($this->con, $this->getConf('usertree'), $all_filter);
 410              $entries = ldap_get_entries($this->con, $sr);
 411              $users_array = array();
 412              $userkey = $this->getConf('userkey');
 413              for ($i = 0; $i < $entries["count"]; $i++) {
 414                  array_push($users_array, $entries[$i][$userkey][0]);
 415              }
 416              asort($users_array);
 417              $result = $users_array;
 418              if (!$result) return array();
 419              $this->users = array_fill_keys($result, false);
 420          }
 421          $i = 0;
 422          $count = 0;
 423          $this->constructPattern($filter);
 424          $result = array();
 425  
 426          foreach ($this->users as $user => &$info) {
 427              if ($i++ < $start) {
 428                  continue;
 429              }
 430              if ($info === false) {
 431                  $info = $this->getUserData($user);
 432              }
 433              if ($this->filter($user, $info)) {
 434                  $result[$user] = $info;
 435                  if (($limit > 0) && (++$count >= $limit)) break;
 436              }
 437          }
 438          return $result;
 439      }
 440  
 441      /**
 442       * Make LDAP filter strings.
 443       *
 444       * Used by auth_getUserData to make the filter
 445       * strings for grouptree and groupfilter
 446       *
 447       * @param string $filter ldap search filter with placeholders
 448       * @param array $placeholders placeholders to fill in
 449       * @return  string
 450       * @author  Troels Liebe Bentsen <tlb@rapanden.dk>
 451       */
 452      protected function makeFilter($filter, $placeholders)
 453      {
 454          preg_match_all("/%{([^}]+)/", $filter, $matches, PREG_PATTERN_ORDER);
 455          //replace each match
 456          foreach ($matches[1] as $match) {
 457              //take first element if array
 458              if (is_array($placeholders[$match])) {
 459                  $value = $placeholders[$match][0];
 460              } else {
 461                  $value = $placeholders[$match];
 462              }
 463              $value = $this->filterEscape($value);
 464              $filter = str_replace('%{' . $match . '}', $value, $filter);
 465          }
 466          return $filter;
 467      }
 468  
 469      /**
 470       * return true if $user + $info match $filter criteria, false otherwise
 471       *
 472       * @param string $user the user's login name
 473       * @param array $info the user's userinfo array
 474       * @return bool
 475       * @author Chris Smith <chris@jalakai.co.uk>
 476       *
 477       */
 478      protected function filter($user, $info)
 479      {
 480          foreach ($this->pattern as $item => $pattern) {
 481              if ($item == 'user') {
 482                  if (!preg_match($pattern, $user)) return false;
 483              } elseif ($item == 'grps') {
 484                  if (!count(preg_grep($pattern, $info['grps']))) return false;
 485              } else {
 486                  if (!preg_match($pattern, $info[$item])) return false;
 487              }
 488          }
 489          return true;
 490      }
 491  
 492      /**
 493       * Set the filter pattern
 494       *
 495       * @param $filter
 496       * @return void
 497       * @author Chris Smith <chris@jalakai.co.uk>
 498       *
 499       */
 500      protected function constructPattern($filter)
 501      {
 502          $this->pattern = array();
 503          foreach ($filter as $item => $pattern) {
 504              $this->pattern[$item] = '/' . str_replace('/', '\/', $pattern) . '/i'; // allow regex characters
 505          }
 506      }
 507  
 508      /**
 509       * Escape a string to be used in a LDAP filter
 510       *
 511       * Ported from Perl's Net::LDAP::Util escape_filter_value
 512       *
 513       * @param string $string
 514       * @return string
 515       * @author Andreas Gohr
 516       */
 517      protected function filterEscape($string)
 518      {
 519          // see https://github.com/adldap/adLDAP/issues/22
 520          return preg_replace_callback(
 521              '/([\x00-\x1F\*\(\)\\\\])/',
 522              function ($matches) {
 523                  return "\\" . join("", unpack("H2", $matches[1]));
 524              },
 525              $string
 526          );
 527      }
 528  
 529      /**
 530       * Opens a connection to the configured LDAP server and sets the wanted
 531       * option on the connection
 532       *
 533       * @author  Andreas Gohr <andi@splitbrain.org>
 534       */
 535      protected function openLDAP()
 536      {
 537          if ($this->con) return true; // connection already established
 538  
 539          if ($this->getConf('debug')) {
 540              ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7);
 541          }
 542  
 543          $this->bound = 0;
 544  
 545          $port = $this->getConf('port');
 546          $bound = false;
 547          $servers = explode(',', $this->getConf('server'));
 548          foreach ($servers as $server) {
 549              $server = trim($server);
 550              $this->con = @ldap_connect($server, $port);
 551              if (!$this->con) {
 552                  continue;
 553              }
 554  
 555              /*
 556               * When OpenLDAP 2.x.x is used, ldap_connect() will always return a resource as it does
 557               * not actually connect but just initializes the connecting parameters. The actual
 558               * connect happens with the next calls to ldap_* funcs, usually with ldap_bind().
 559               *
 560               * So we should try to bind to server in order to check its availability.
 561               */
 562  
 563              //set protocol version and dependend options
 564              if ($this->getConf('version')) {
 565                  if (!@ldap_set_option(
 566                      $this->con,
 567                      LDAP_OPT_PROTOCOL_VERSION,
 568                      $this->getConf('version')
 569                  )
 570                  ) {
 571                      msg('Setting LDAP Protocol version ' . $this->getConf('version') . ' failed', -1);
 572                      $this->debug('LDAP version set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 573                  } else {
 574                      //use TLS (needs version 3)
 575                      if ($this->getConf('starttls')) {
 576                          if (!@ldap_start_tls($this->con)) {
 577                              msg('Starting TLS failed', -1);
 578                              $this->debug('LDAP TLS set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 579                          }
 580                      }
 581                      // needs version 3
 582                      if ($this->getConf('referrals') > -1) {
 583                          if (!@ldap_set_option(
 584                              $this->con,
 585                              LDAP_OPT_REFERRALS,
 586                              $this->getConf('referrals')
 587                          )
 588                          ) {
 589                              msg('Setting LDAP referrals failed', -1);
 590                              $this->debug('LDAP referal set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 591                          }
 592                      }
 593                  }
 594              }
 595  
 596              //set deref mode
 597              if ($this->getConf('deref')) {
 598                  if (!@ldap_set_option($this->con, LDAP_OPT_DEREF, $this->getConf('deref'))) {
 599                      msg('Setting LDAP Deref mode ' . $this->getConf('deref') . ' failed', -1);
 600                      $this->debug('LDAP deref set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
 601                  }
 602              }
 603              /* As of PHP 5.3.0 we can set timeout to speedup skipping of invalid servers */
 604              if (defined('LDAP_OPT_NETWORK_TIMEOUT')) {
 605                  ldap_set_option($this->con, LDAP_OPT_NETWORK_TIMEOUT, 1);
 606              }
 607  
 608              if ($this->getConf('binddn') && $this->getConf('bindpw')) {
 609                  $bound = @ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')));
 610                  $this->bound = 2;
 611              } else {
 612                  $bound = @ldap_bind($this->con);
 613              }
 614              if ($bound) {
 615                  break;
 616              }
 617          }
 618  
 619          if (!$bound) {
 620              msg("LDAP: couldn't connect to LDAP server", -1);
 621              $this->debug(ldap_error($this->con), 0, __LINE__, __FILE__);
 622              return false;
 623          }
 624  
 625          $this->cando['getUsers'] = true;
 626          return true;
 627      }
 628  
 629      /**
 630       * Wraps around ldap_search, ldap_list or ldap_read depending on $scope
 631       *
 632       * @param resource $link_identifier
 633       * @param string $base_dn
 634       * @param string $filter
 635       * @param string $scope can be 'base', 'one' or 'sub'
 636       * @param null|array $attributes
 637       * @param int $attrsonly
 638       * @param int $sizelimit
 639       * @return resource
 640       * @author Andreas Gohr <andi@splitbrain.org>
 641       */
 642      protected function ldapSearch(
 643          $link_identifier,
 644          $base_dn,
 645          $filter,
 646          $scope = 'sub',
 647          $attributes = null,
 648          $attrsonly = 0,
 649          $sizelimit = 0
 650      )
 651      {
 652          if (is_null($attributes)) $attributes = array();
 653  
 654          if ($scope == 'base') {
 655              return @ldap_read(
 656                  $link_identifier,
 657                  $base_dn,
 658                  $filter,
 659                  $attributes,
 660                  $attrsonly,
 661                  $sizelimit
 662              );
 663          } elseif ($scope == 'one') {
 664              return @ldap_list(
 665                  $link_identifier,
 666                  $base_dn,
 667                  $filter,
 668                  $attributes,
 669                  $attrsonly,
 670                  $sizelimit
 671              );
 672          } else {
 673              return @ldap_search(
 674                  $link_identifier,
 675                  $base_dn,
 676                  $filter,
 677                  $attributes,
 678                  $attrsonly,
 679                  $sizelimit
 680              );
 681          }
 682      }
 683  
 684      /**
 685       * Wrapper around msg() but outputs only when debug is enabled
 686       *
 687       * @param string $message
 688       * @param int $err
 689       * @param int $line
 690       * @param string $file
 691       * @return void
 692       */
 693      protected function debug($message, $err, $line, $file)
 694      {
 695          if (!$this->getConf('debug')) return;
 696          msg($message, $err, $line, $file);
 697      }
 698  }