[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

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

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