Issues (847)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

lib/plugins/authldap/auth.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
use dokuwiki\Utf8\Sort;
3
4
/**
5
 * LDAP authentication backend
6
 *
7
 * @license   GPL 2 (http://www.gnu.org/licenses/gpl.html)
8
 * @author    Andreas Gohr <[email protected]>
9
 * @author    Chris Smith <[email protected]>
10
 * @author    Jan Schumann <[email protected]>
11
 */
12
class auth_plugin_authldap extends DokuWiki_Auth_Plugin
13
{
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
    {
31
        parent::__construct();
32
33
        // ldap extension is needed
34
        if (!function_exists('ldap_connect')) {
35
            $this->debug("LDAP err: PHP LDAP extension not found.", -1, __LINE__, __FILE__);
36
            $this->success = false;
37
            return;
38
        }
39
40
        // Add the capabilities to change the password
41
        $this->cando['modPass'] = $this->getConf('modPass');
42
    }
43
44
    /**
45
     * Check user+password
46
     *
47
     * Checks if the given user exists and the given
48
     * plaintext password is correct by trying to bind
49
     * to the LDAP server
50
     *
51
     * @param string $user
52
     * @param string $pass
53
     * @return  bool
54
     * @author  Andreas Gohr <[email protected]>
55
     */
56
    public function checkPass($user, $pass)
57
    {
58
        // reject empty password
59
        if (empty($pass)) return false;
60
        if (!$this->openLDAP()) return false;
61
62
        // indirect user bind
63
        if ($this->getConf('binddn') && $this->getConf('bindpw')) {
64
            // use superuser credentials
65
            if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
66
                $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
67
                return false;
68
            }
69
            $this->bound = 2;
70
        } elseif ($this->getConf('binddn') &&
71
            $this->getConf('usertree') &&
72
            $this->getConf('userfilter')
73
        ) {
74
            // special bind string
75
            $dn = $this->makeFilter(
76
                $this->getConf('binddn'),
77
                array('user' => $user, 'server' => $this->getConf('server'))
78
            );
79
        } elseif (strpos($this->getConf('usertree'), '%{user}')) {
80
            // direct user bind
81
            $dn = $this->makeFilter(
82
                $this->getConf('usertree'),
83
                array('user' => $user, 'server' => $this->getConf('server'))
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: ' . hsc(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: ' . hsc(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->fetchUserData($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: ' . hsc(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
     * @param string $user
142
     * @param bool $requireGroups (optional) - ignored, groups are always supplied by this plugin
143
     * @return  array containing user data or false
144
     * @author  <[email protected]>
145
     * @author  Stephane Chazelas <[email protected]>
146
     * @author  Steffen Schoch <[email protected]>
147
     *
148
     * @author  Andreas Gohr <[email protected]>
149
     * @author  Trouble
150
     * @author  Dan Allen <[email protected]>
151
     */
152
    public function getUserData($user, $requireGroups = true)
153
    {
154
        return $this->fetchUserData($user);
155
    }
156
157
    /**
158
     * @param string $user
159
     * @param bool $inbind authldap specific, true if in bind phase
160
     * @return  array containing user data or false
161
     */
162
    protected function fetchUserData($user, $inbind = false)
163
    {
164
        global $conf;
165
        if (!$this->openLDAP()) return array();
166
167
        // force superuser bind if wanted and not bound as superuser yet
168
        if ($this->getConf('binddn') && $this->getConf('bindpw') && $this->bound < 2) {
169
            // use superuser credentials
170
            if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
171
                $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
172
                return array();
173
            }
174
            $this->bound = 2;
175
        } elseif ($this->bound == 0 && !$inbind) {
176
            // in some cases getUserData is called outside the authentication workflow
177
            // eg. for sending email notification on subscribed pages. This data might not
178
            // be accessible anonymously, so we try to rebind the current user here
179
            list($loginuser, $loginsticky, $loginpass) = auth_getCookie();
180
            if ($loginuser && $loginpass) {
181
                $loginpass = auth_decrypt($loginpass, auth_cookiesalt(!$loginsticky, true));
0 ignored issues
show
It seems like auth_cookiesalt(!$loginsticky, true) targeting auth_cookiesalt() can also be of type boolean; however, auth_decrypt() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
182
                $this->checkPass($loginuser, $loginpass);
183
            }
184
        }
185
186
        $info = array();
187
        $info['user'] = $user;
188
        $this->debug('LDAP user to find: ' . hsc($info['user']), 0, __LINE__, __FILE__);
189
190
        $info['server'] = $this->getConf('server');
191
        $this->debug('LDAP Server: ' . hsc($info['server']), 0, __LINE__, __FILE__);
192
193
        //get info for given user
194
        $base = $this->makeFilter($this->getConf('usertree'), $info);
195
        if ($this->getConf('userfilter')) {
196
            $filter = $this->makeFilter($this->getConf('userfilter'), $info);
197
        } else {
198
            $filter = "(ObjectClass=*)";
199
        }
200
201
        $this->debug('LDAP Filter: ' . hsc($filter), 0, __LINE__, __FILE__);
202
203
        $this->debug('LDAP user search: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
204
        $this->debug('LDAP search at: ' . hsc($base . ' ' . $filter), 0, __LINE__, __FILE__);
205
        $sr = $this->ldapSearch($this->con, $base, $filter, $this->getConf('userscope'), $this->getConf('attributes'));
206
207
208
        $result = @ldap_get_entries($this->con, $sr);
209
210
        // if result is not an array
211
        if (!is_array($result)) {
212
            // no objects found
213
            $this->debug('LDAP search returned non-array result: ' . hsc(print($result)), -1, __LINE__, __FILE__);
214
            return array();
215
        }
216
217
        // Don't accept more or less than one response
218
        if ($result['count'] != 1) {
219
            $this->debug(
220
                'LDAP search returned ' . hsc($result['count']) . ' results while it should return 1!',
221
                -1,
222
                __LINE__,
223
                __FILE__
224
            );
225
            //for($i = 0; $i < $result["count"]; $i++) {
226
            //$this->_debug('result: '.hsc(print_r($result[$i])), 0, __LINE__, __FILE__);
227
            //}
228
            return array();
229
        }
230
231
        $this->debug('LDAP search found single result !', 0, __LINE__, __FILE__);
232
233
        $user_result = $result[0];
234
        ldap_free_result($sr);
235
236
        // general user info
237
        $info['dn'] = $user_result['dn'];
238
        $info['gid'] = $user_result['gidnumber'][0];
239
        $info['mail'] = $user_result['mail'][0];
240
        $info['name'] = $user_result['cn'][0];
241
        $info['grps'] = array();
242
243
        // overwrite if other attribs are specified.
244
        if (is_array($this->getConf('mapping'))) {
245
            foreach ($this->getConf('mapping') as $localkey => $key) {
246
                if (is_array($key)) {
247
                    // use regexp to clean up user_result
248
                    // $key = array($key=>$regexp), only handles the first key-value
249
                    $regexp = current($key);
250
                    $key = key($key);
251
                    if ($user_result[$key]) foreach ($user_result[$key] as $grpkey => $grp) {
252
                        if ($grpkey !== 'count' && preg_match($regexp, $grp, $match)) {
253
                            if ($localkey == 'grps') {
254
                                $info[$localkey][] = $match[1];
255
                            } else {
256
                                $info[$localkey] = $match[1];
257
                            }
258
                        }
259
                    }
260
                } else {
261
                    $info[$localkey] = $user_result[$key][0];
262
                }
263
            }
264
        }
265
        $user_result = array_merge($info, $user_result);
266
267
        //get groups for given user if grouptree is given
268
        if ($this->getConf('grouptree') || $this->getConf('groupfilter')) {
269
            $base = $this->makeFilter($this->getConf('grouptree'), $user_result);
270
            $filter = $this->makeFilter($this->getConf('groupfilter'), $user_result);
271
            $sr = $this->ldapSearch(
272
                $this->con,
273
                $base,
274
                $filter,
275
                $this->getConf('groupscope'),
276
                array($this->getConf('groupkey'))
277
            );
278
            $this->debug('LDAP group search: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
279
            $this->debug('LDAP search at: ' . hsc($base . ' ' . $filter), 0, __LINE__, __FILE__);
280
281
            if (!$sr) {
282
                msg("LDAP: Reading group memberships failed", -1);
283
                return array();
284
            }
285
            $result = ldap_get_entries($this->con, $sr);
286
            ldap_free_result($sr);
287
288
            if (is_array($result)) foreach ($result as $grp) {
289
                if (!empty($grp[$this->getConf('groupkey')])) {
290
                    $group = $grp[$this->getConf('groupkey')];
291
                    if (is_array($group)) {
292
                        $group = $group[0];
293
                    } else {
294
                        $this->debug('groupkey did not return a detailled result', 0, __LINE__, __FILE__);
295
                    }
296
                    if ($group === '') continue;
297
298
                    $this->debug('LDAP usergroup: ' . hsc($group), 0, __LINE__, __FILE__);
299
                    $info['grps'][] = $group;
300
                }
301
            }
302
        }
303
304
        // always add the default group to the list of groups
305
        if (!$info['grps'] or !in_array($conf['defaultgroup'], $info['grps'])) {
306
            $info['grps'][] = $conf['defaultgroup'];
307
        }
308
        return $info;
309
    }
310
311
    /**
312
     * Definition of the function modifyUser in order to modify the password
313
     *
314
     * @param string $user nick of the user to be changed
315
     * @param array $changes array of field/value pairs to be changed (password will be clear text)
316
     * @return  bool   true on success, false on error
317
     */
318
    public function modifyUser($user, $changes)
319
    {
320
321
        // open the connection to the ldap
322
        if (!$this->openLDAP()) {
323
            $this->debug('LDAP cannot connect: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
324
            return false;
325
        }
326
327
        // find the information about the user, in particular the "dn"
328
        $info = $this->getUserData($user, true);
329
        if (empty($info['dn'])) {
330
            $this->debug('LDAP cannot find your user dn', 0, __LINE__, __FILE__);
331
            return false;
332
        }
333
        $dn = $info['dn'];
334
335
        // find the old password of the user
336
        list($loginuser, $loginsticky, $loginpass) = auth_getCookie();
337
        if ($loginuser !== null) { // the user is currently logged in
338
            $secret = auth_cookiesalt(!$loginsticky, true);
339
            $pass = auth_decrypt($loginpass, $secret);
340
341
            // bind with the ldap
342
            if (!@ldap_bind($this->con, $dn, $pass)) {
343
                $this->debug(
344
                    'LDAP user bind failed: ' . hsc($dn) . ': ' . hsc(ldap_error($this->con)),
345
                    0,
346
                    __LINE__,
347
                    __FILE__
348
                );
349
                return false;
350
            }
351
        } elseif ($this->getConf('binddn') && $this->getConf('bindpw')) {
352
            // we are changing the password on behalf of the user (eg: forgotten password)
353
            // bind with the superuser ldap
354
            if (!@ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')))) {
355
                $this->debug('LDAP bind as superuser: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
356
                return false;
357
            }
358
        } else {
359
            return false; // no otherway
360
        }
361
362
        // Generate the salted hashed password for LDAP
363
        $phash = new \dokuwiki\PassHash();
364
        $hash = $phash->hash_ssha($changes['pass']);
365
366
        // change the password
367
        if (!@ldap_mod_replace($this->con, $dn, array('userpassword' => $hash))) {
368
            $this->debug(
369
                'LDAP mod replace failed: ' . hsc($dn) . ': ' . hsc(ldap_error($this->con)),
370
                0,
371
                __LINE__,
372
                __FILE__
373
            );
374
            return false;
375
        }
376
377
        return true;
378
    }
379
380
    /**
381
     * Most values in LDAP are case-insensitive
382
     *
383
     * @return bool
384
     */
385
    public function isCaseSensitive()
386
    {
387
        return false;
388
    }
389
390
    /**
391
     * Bulk retrieval of user data
392
     *
393
     * @param int $start index of first user to be returned
394
     * @param int $limit max number of users to be returned
395
     * @param array $filter array of field/pattern pairs, null for no filter
396
     * @return  array of userinfo (refer getUserData for internal userinfo details)
397
     * @author  Dominik Eckelmann <[email protected]>
398
     */
399
    public function retrieveUsers($start = 0, $limit = 0, $filter = array())
400
    {
401
        if (!$this->openLDAP()) return array();
402
403
        if (is_null($this->users)) {
404
            // Perform the search and grab all their details
405
            if ($this->getConf('userfilter')) {
406
                $all_filter = str_replace('%{user}', '*', $this->getConf('userfilter'));
407
            } else {
408
                $all_filter = "(ObjectClass=*)";
409
            }
410
            $sr = ldap_search($this->con, $this->getConf('usertree'), $all_filter);
411
            $entries = ldap_get_entries($this->con, $sr);
412
            $users_array = array();
413
            $userkey = $this->getConf('userkey');
414
            for ($i = 0; $i < $entries["count"]; $i++) {
415
                array_push($users_array, $entries[$i][$userkey][0]);
416
            }
417
            Sort::asort($users_array);
418
            $result = $users_array;
419
            if (!$result) return array();
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
420
            $this->users = array_fill_keys($result, false);
421
        }
422
        $i = 0;
423
        $count = 0;
424
        $this->constructPattern($filter);
425
        $result = array();
426
427
        foreach ($this->users as $user => &$info) {
428
            if ($i++ < $start) {
429
                continue;
430
            }
431
            if ($info === false) {
432
                $info = $this->getUserData($user);
433
            }
434
            if ($this->filter($user, $info)) {
435
                $result[$user] = $info;
436
                if (($limit > 0) && (++$count >= $limit)) break;
437
            }
438
        }
439
        return $result;
440
    }
441
442
    /**
443
     * Make LDAP filter strings.
444
     *
445
     * Used by auth_getUserData to make the filter
446
     * strings for grouptree and groupfilter
447
     *
448
     * @param string $filter ldap search filter with placeholders
449
     * @param array $placeholders placeholders to fill in
450
     * @return  string
451
     * @author  Troels Liebe Bentsen <[email protected]>
452
     */
453
    protected function makeFilter($filter, $placeholders)
454
    {
455
        preg_match_all("/%{([^}]+)/", $filter, $matches, PREG_PATTERN_ORDER);
456
        //replace each match
457
        foreach ($matches[1] as $match) {
458
            //take first element if array
459
            if (is_array($placeholders[$match])) {
460
                $value = $placeholders[$match][0];
461
            } else {
462
                $value = $placeholders[$match];
463
            }
464
            $value = $this->filterEscape($value);
465
            $filter = str_replace('%{' . $match . '}', $value, $filter);
466
        }
467
        return $filter;
468
    }
469
470
    /**
471
     * return true if $user + $info match $filter criteria, false otherwise
472
     *
473
     * @param string $user the user's login name
474
     * @param array $info the user's userinfo array
475
     * @return bool
476
     * @author Chris Smith <[email protected]>
477
     *
478
     */
479
    protected function filter($user, $info)
480
    {
481
        foreach ($this->pattern as $item => $pattern) {
482
            if ($item == 'user') {
483
                if (!preg_match($pattern, $user)) return false;
484
            } elseif ($item == 'grps') {
485
                if (!count(preg_grep($pattern, $info['grps']))) return false;
486
            } else {
487
                if (!preg_match($pattern, $info[$item])) return false;
488
            }
489
        }
490
        return true;
491
    }
492
493
    /**
494
     * Set the filter pattern
495
     *
496
     * @param $filter
497
     * @return void
498
     * @author Chris Smith <[email protected]>
499
     *
500
     */
501
    protected function constructPattern($filter)
502
    {
503
        $this->pattern = array();
504
        foreach ($filter as $item => $pattern) {
505
            $this->pattern[$item] = '/' . str_replace('/', '\/', $pattern) . '/i'; // allow regex characters
506
        }
507
    }
508
509
    /**
510
     * Escape a string to be used in a LDAP filter
511
     *
512
     * Ported from Perl's Net::LDAP::Util escape_filter_value
513
     *
514
     * @param string $string
515
     * @return string
516
     * @author Andreas Gohr
517
     */
518
    protected function filterEscape($string)
519
    {
520
        // see https://github.com/adldap/adLDAP/issues/22
521
        return preg_replace_callback(
522
            '/([\x00-\x1F\*\(\)\\\\])/',
523
            function ($matches) {
524
                return "\\" . join("", unpack("H2", $matches[1]));
525
            },
526
            $string
527
        );
528
    }
529
530
    /**
531
     * Opens a connection to the configured LDAP server and sets the wanted
532
     * option on the connection
533
     *
534
     * @author  Andreas Gohr <[email protected]>
535
     */
536
    protected function openLDAP()
537
    {
538
        if ($this->con) return true; // connection already established
539
540
        if ($this->getConf('debug')) {
541
            ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7);
542
        }
543
544
        $this->bound = 0;
545
546
        $port = $this->getConf('port');
547
        $bound = false;
548
        $servers = explode(',', $this->getConf('server'));
549
        foreach ($servers as $server) {
550
            $server = trim($server);
551
            $this->con = @ldap_connect($server, $port);
552
            if (!$this->con) {
553
                continue;
554
            }
555
556
            /*
557
             * When OpenLDAP 2.x.x is used, ldap_connect() will always return a resource as it does
558
             * not actually connect but just initializes the connecting parameters. The actual
559
             * connect happens with the next calls to ldap_* funcs, usually with ldap_bind().
560
             *
561
             * So we should try to bind to server in order to check its availability.
562
             */
563
564
            //set protocol version and dependend options
565
            if ($this->getConf('version')) {
566
                if (!@ldap_set_option(
567
                    $this->con,
568
                    LDAP_OPT_PROTOCOL_VERSION,
569
                    $this->getConf('version')
570
                )
571
                ) {
572
                    msg('Setting LDAP Protocol version ' . $this->getConf('version') . ' failed', -1);
573
                    $this->debug('LDAP version set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
574
                } else {
575
                    //use TLS (needs version 3)
576
                    if ($this->getConf('starttls')) {
577
                        if (!@ldap_start_tls($this->con)) {
578
                            msg('Starting TLS failed', -1);
579
                            $this->debug('LDAP TLS set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
580
                        }
581
                    }
582
                    // needs version 3
583
                    if ($this->getConf('referrals') > -1) {
584
                        if (!@ldap_set_option(
585
                            $this->con,
586
                            LDAP_OPT_REFERRALS,
587
                            $this->getConf('referrals')
588
                        )
589
                        ) {
590
                            msg('Setting LDAP referrals failed', -1);
591
                            $this->debug('LDAP referal set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
592
                        }
593
                    }
594
                }
595
            }
596
597
            //set deref mode
598
            if ($this->getConf('deref')) {
599
                if (!@ldap_set_option($this->con, LDAP_OPT_DEREF, $this->getConf('deref'))) {
600
                    msg('Setting LDAP Deref mode ' . $this->getConf('deref') . ' failed', -1);
601
                    $this->debug('LDAP deref set: ' . hsc(ldap_error($this->con)), 0, __LINE__, __FILE__);
602
                }
603
            }
604
            /* As of PHP 5.3.0 we can set timeout to speedup skipping of invalid servers */
605
            if (defined('LDAP_OPT_NETWORK_TIMEOUT')) {
606
                ldap_set_option($this->con, LDAP_OPT_NETWORK_TIMEOUT, 1);
607
            }
608
609
            if ($this->getConf('binddn') && $this->getConf('bindpw')) {
610
                $bound = @ldap_bind($this->con, $this->getConf('binddn'), conf_decodeString($this->getConf('bindpw')));
611
                $this->bound = 2;
612
            } else {
613
                $bound = @ldap_bind($this->con);
614
            }
615
            if ($bound) {
616
                break;
617
            }
618
        }
619
620
        if (!$bound) {
621
            msg("LDAP: couldn't connect to LDAP server", -1);
622
            $this->debug(ldap_error($this->con), 0, __LINE__, __FILE__);
623
            return false;
624
        }
625
626
        $this->cando['getUsers'] = true;
627
        return true;
628
    }
629
630
    /**
631
     * Wraps around ldap_search, ldap_list or ldap_read depending on $scope
632
     *
633
     * @param resource $link_identifier
634
     * @param string $base_dn
635
     * @param string $filter
636
     * @param string $scope can be 'base', 'one' or 'sub'
637
     * @param null|array $attributes
638
     * @param int $attrsonly
639
     * @param int $sizelimit
640
     * @return resource
641
     * @author Andreas Gohr <[email protected]>
642
     */
643
    protected function ldapSearch(
644
        $link_identifier,
645
        $base_dn,
646
        $filter,
647
        $scope = 'sub',
648
        $attributes = null,
649
        $attrsonly = 0,
650
        $sizelimit = 0
651
    )
652
    {
653
        if (is_null($attributes)) $attributes = array();
654
655
        if ($scope == 'base') {
656
            return @ldap_read(
657
                $link_identifier,
658
                $base_dn,
659
                $filter,
660
                $attributes,
661
                $attrsonly,
662
                $sizelimit
663
            );
664
        } elseif ($scope == 'one') {
665
            return @ldap_list(
666
                $link_identifier,
667
                $base_dn,
668
                $filter,
669
                $attributes,
670
                $attrsonly,
671
                $sizelimit
672
            );
673
        } else {
674
            return @ldap_search(
675
                $link_identifier,
676
                $base_dn,
677
                $filter,
678
                $attributes,
679
                $attrsonly,
680
                $sizelimit
681
            );
682
        }
683
    }
684
685
    /**
686
     * Wrapper around msg() but outputs only when debug is enabled
687
     *
688
     * @param string $message
689
     * @param int $err
690
     * @param int $line
691
     * @param string $file
692
     * @return void
693
     */
694
    protected function debug($message, $err, $line, $file)
695
    {
696
        if (!$this->getConf('debug')) return;
697
        msg($message, $err, $line, $file);
698
    }
699
}
700