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/authad/auth.php (5 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
use dokuwiki\Logger;
4
5
/**
6
 * Active Directory authentication backend for DokuWiki
7
 *
8
 * This makes authentication with a Active Directory server much easier
9
 * than when using the normal LDAP backend by utilizing the adLDAP library
10
 *
11
 * Usage:
12
 *   Set DokuWiki's local.protected.php auth setting to read
13
 *
14
 *   $conf['authtype']       = 'authad';
15
 *
16
 *   $conf['plugin']['authad']['account_suffix']     = '@my.domain.org';
17
 *   $conf['plugin']['authad']['base_dn']            = 'DC=my,DC=domain,DC=org';
18
 *   $conf['plugin']['authad']['domain_controllers'] = 'srv1.domain.org,srv2.domain.org';
19
 *
20
 *   //optional:
21
 *   $conf['plugin']['authad']['sso']                = 1;
22
 *   $conf['plugin']['authad']['admin_username']     = 'root';
23
 *   $conf['plugin']['authad']['admin_password']     = 'pass';
24
 *   $conf['plugin']['authad']['real_primarygroup']  = 1;
25
 *   $conf['plugin']['authad']['use_ssl']            = 1;
26
 *   $conf['plugin']['authad']['use_tls']            = 1;
27
 *   $conf['plugin']['authad']['debug']              = 1;
28
 *   // warn user about expiring password this many days in advance:
29
 *   $conf['plugin']['authad']['expirywarn']         = 5;
30
 *
31
 *   // get additional information to the userinfo array
32
 *   // add a list of comma separated ldap contact fields.
33
 *   $conf['plugin']['authad']['additional'] = 'field1,field2';
34
 *
35
 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
36
 * @author  James Van Lommel <[email protected]>
37
 * @link    http://www.nosq.com/blog/2005/08/ldap-activedirectory-and-dokuwiki/
38
 * @author  Andreas Gohr <[email protected]>
39
 * @author  Jan Schumann <[email protected]>
40
 */
41
class auth_plugin_authad extends DokuWiki_Auth_Plugin
42
{
43
44
    /**
45
     * @var array hold connection data for a specific AD domain
46
     */
47
    protected $opts = array();
48
49
    /**
50
     * @var array open connections for each AD domain, as adLDAP objects
51
     */
52
    protected $adldap = array();
53
54
    /**
55
     * @var bool message state
56
     */
57
    protected $msgshown = false;
58
59
    /**
60
     * @var array user listing cache
61
     */
62
    protected $users = array();
63
64
    /**
65
     * @var array filter patterns for listing users
66
     */
67
    protected $pattern = array();
68
69
    protected $grpsusers = array();
70
71
    /**
72
     * Constructor
73
     */
74
    public function __construct()
75
    {
76
        global $INPUT;
77
        parent::__construct();
78
79
        require_once(DOKU_PLUGIN.'authad/adLDAP/adLDAP.php');
80
        require_once(DOKU_PLUGIN.'authad/adLDAP/classes/adLDAPUtils.php');
81
82
        // we load the config early to modify it a bit here
83
        $this->loadConfig();
84
85
        // additional information fields
86
        if (isset($this->conf['additional'])) {
87
            $this->conf['additional'] = str_replace(' ', '', $this->conf['additional']);
88
            $this->conf['additional'] = explode(',', $this->conf['additional']);
89
        } else $this->conf['additional'] = array();
90
91
        // ldap extension is needed
92
        if (!function_exists('ldap_connect')) {
93
            if ($this->conf['debug'])
94
                msg("AD Auth: PHP LDAP extension not found.", -1);
95
            $this->success = false;
96
            return;
97
        }
98
99
        // Prepare SSO
100
        if (!empty($_SERVER['REMOTE_USER'])) {
101
            // make sure the right encoding is used
102
            if ($this->getConf('sso_charset')) {
103
                $_SERVER['REMOTE_USER'] = iconv($this->getConf('sso_charset'), 'UTF-8', $_SERVER['REMOTE_USER']);
104
            } elseif (!\dokuwiki\Utf8\Clean::isUtf8($_SERVER['REMOTE_USER'])) {
105
                $_SERVER['REMOTE_USER'] = utf8_encode($_SERVER['REMOTE_USER']);
106
            }
107
108
            // trust the incoming user
109
            if ($this->conf['sso']) {
110
                $_SERVER['REMOTE_USER'] = $this->cleanUser($_SERVER['REMOTE_USER']);
111
112
                // we need to simulate a login
113
                if (empty($_COOKIE[DOKU_COOKIE])) {
114
                    $INPUT->set('u', $_SERVER['REMOTE_USER']);
115
                    $INPUT->set('p', 'sso_only');
116
                }
117
            }
118
        }
119
120
        // other can do's are changed in $this->_loadServerConfig() base on domain setup
121
        $this->cando['modName'] = (bool)$this->conf['update_name'];
122
        $this->cando['modMail'] = (bool)$this->conf['update_mail'];
123
        $this->cando['getUserCount'] = true;
124
    }
125
126
    /**
127
     * Load domain config on capability check
128
     *
129
     * @param string $cap
130
     * @return bool
131
     */
132
    public function canDo($cap)
133
    {
134
        //capabilities depend on config, which may change depending on domain
135
        $domain = $this->getUserDomain($_SERVER['REMOTE_USER']);
136
        $this->loadServerConfig($domain);
137
        return parent::canDo($cap);
138
    }
139
140
    /**
141
     * Check user+password [required auth function]
142
     *
143
     * Checks if the given user exists and the given
144
     * plaintext password is correct by trying to bind
145
     * to the LDAP server
146
     *
147
     * @author  James Van Lommel <[email protected]>
148
     * @param string $user
149
     * @param string $pass
150
     * @return  bool
151
     */
152
    public function checkPass($user, $pass)
153
    {
154
        if ($_SERVER['REMOTE_USER'] &&
155
            $_SERVER['REMOTE_USER'] == $user &&
156
            $this->conf['sso']
157
        ) return true;
158
159
        $adldap = $this->initAdLdap($this->getUserDomain($user));
160
        if (!$adldap) return false;
161
162
        try {
163
            return $adldap->authenticate($this->getUserName($user), $pass);
164
        } catch (adLDAPException $e) {
0 ignored issues
show
The class adLDAPException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
165
            // shouldn't really happen
166
            return false;
167
        }
168
    }
169
170
    /**
171
     * Return user info [required auth function]
172
     *
173
     * Returns info about the given user needs to contain
174
     * at least these fields:
175
     *
176
     * name    string  full name of the user
177
     * mail    string  email address of the user
178
     * grps    array   list of groups the user is in
179
     *
180
     * This AD specific function returns the following
181
     * addional fields:
182
     *
183
     * dn         string    distinguished name (DN)
184
     * uid        string    samaccountname
185
     * lastpwd    int       timestamp of the date when the password was set
186
     * expires    true      if the password expires
187
     * expiresin  int       seconds until the password expires
188
     * any fields specified in the 'additional' config option
189
     *
190
     * @author  James Van Lommel <[email protected]>
191
     * @param string $user
192
     * @param bool $requireGroups (optional) - ignored, groups are always supplied by this plugin
193
     * @return array
194
     */
195
    public function getUserData($user, $requireGroups = true)
196
    {
197
        global $conf;
198
        global $lang;
199
        global $ID;
200
        $adldap = $this->initAdLdap($this->getUserDomain($user));
201
        if (!$adldap) return array();
202
203
        if ($user == '') return array();
204
205
        $fields = array('mail', 'displayname', 'samaccountname', 'lastpwd', 'pwdlastset', 'useraccountcontrol');
206
207
        // add additional fields to read
208
        $fields = array_merge($fields, $this->conf['additional']);
209
        $fields = array_unique($fields);
210
        $fields = array_filter($fields);
211
212
        //get info for given user
213
        $result = $adldap->user()->info($this->getUserName($user), $fields);
214
        if ($result == false) {
215
            return array();
216
        }
217
218
        //general user info
219
        $info = array();
220
        $info['name'] = $result[0]['displayname'][0];
221
        $info['mail'] = $result[0]['mail'][0];
222
        $info['uid']  = $result[0]['samaccountname'][0];
223
        $info['dn']   = $result[0]['dn'];
224
        //last password set (Windows counts from January 1st 1601)
225
        $info['lastpwd'] = $result[0]['pwdlastset'][0] / 10000000 - 11644473600;
226
        //will it expire?
227
        $info['expires'] = !($result[0]['useraccountcontrol'][0] & 0x10000); //ADS_UF_DONT_EXPIRE_PASSWD
228
229
        // additional information
230
        foreach ($this->conf['additional'] as $field) {
231
            if (isset($result[0][strtolower($field)])) {
232
                $info[$field] = $result[0][strtolower($field)][0];
233
            }
234
        }
235
236
        // handle ActiveDirectory memberOf
237
        $info['grps'] = $adldap->user()->groups($this->getUserName($user), (bool) $this->opts['recursive_groups']);
238
239
        if (is_array($info['grps'])) {
240
            foreach ($info['grps'] as $ndx => $group) {
241
                $info['grps'][$ndx] = $this->cleanGroup($group);
242
            }
243
        }
244
245
        // always add the default group to the list of groups
246
        if (!is_array($info['grps']) || !in_array($conf['defaultgroup'], $info['grps'])) {
247
            $info['grps'][] = $conf['defaultgroup'];
248
        }
249
250
        // add the user's domain to the groups
251
        $domain = $this->getUserDomain($user);
252
        if ($domain && !in_array("domain-$domain", (array) $info['grps'])) {
253
            $info['grps'][] = $this->cleanGroup("domain-$domain");
254
        }
255
256
        // check expiry time
257
        if ($info['expires'] && $this->conf['expirywarn']) {
258
            try {
259
                $expiry = $adldap->user()->passwordExpiry($user);
260
                if (is_array($expiry)) {
261
                    $info['expiresat'] = $expiry['expiryts'];
262
                    $info['expiresin'] = round(($info['expiresat'] - time())/(24*60*60));
263
264
                    // if this is the current user, warn him (once per request only)
265
                    if (($_SERVER['REMOTE_USER'] == $user) &&
266
                        ($info['expiresin'] <= $this->conf['expirywarn']) &&
267
                        !$this->msgshown
268
                    ) {
269
                        $msg = sprintf($this->getLang('authpwdexpire'), $info['expiresin']);
270
                        if ($this->canDo('modPass')) {
271
                            $url = wl($ID, array('do'=> 'profile'));
272
                            $msg .= ' <a href="'.$url.'">'.$lang['btn_profile'].'</a>';
273
                        }
274
                        msg($msg);
275
                        $this->msgshown = true;
276
                    }
277
                }
278
            } catch (adLDAPException $e) {
0 ignored issues
show
The class adLDAPException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
279
                // ignore. should usually not happen
280
            }
281
        }
282
283
        return $info;
284
    }
285
286
    /**
287
     * Make AD group names usable by DokuWiki.
288
     *
289
     * Removes backslashes ('\'), pound signs ('#'), and converts spaces to underscores.
290
     *
291
     * @author  James Van Lommel ([email protected])
292
     * @param string $group
293
     * @return string
294
     */
295
    public function cleanGroup($group)
296
    {
297
        $group = str_replace('\\', '', $group);
298
        $group = str_replace('#', '', $group);
299
        $group = preg_replace('[\s]', '_', $group);
300
        $group = \dokuwiki\Utf8\PhpString::strtolower(trim($group));
301
        return $group;
302
    }
303
304
    /**
305
     * Sanitize user names
306
     *
307
     * Normalizes domain parts, does not modify the user name itself (unlike cleanGroup)
308
     *
309
     * @author Andreas Gohr <[email protected]>
310
     * @param string $user
311
     * @return string
312
     */
313
    public function cleanUser($user)
314
    {
315
        $domain = '';
316
317
        // get NTLM or Kerberos domain part
318
        list($dom, $user) = explode('\\', $user, 2);
319
        if (!$user) $user = $dom;
320
        if ($dom) $domain = $dom;
321
        list($user, $dom) = explode('@', $user, 2);
322
        if ($dom) $domain = $dom;
323
324
        // clean up both
325
        $domain = \dokuwiki\Utf8\PhpString::strtolower(trim($domain));
326
        $user   = \dokuwiki\Utf8\PhpString::strtolower(trim($user));
327
328
        // is this a known, valid domain or do we work without account suffix? if not discard
329
        if (!is_array($this->conf[$domain]) && $this->conf['account_suffix'] !== '') {
330
            $domain = '';
331
        }
332
333
        // reattach domain
334
        if ($domain) $user = "$user@$domain";
335
        return $user;
336
    }
337
338
    /**
339
     * Most values in LDAP are case-insensitive
340
     *
341
     * @return bool
342
     */
343
    public function isCaseSensitive()
344
    {
345
        return false;
346
    }
347
348
    /**
349
     * Create a Search-String useable by adLDAPUsers::all($includeDescription = false, $search = "*", $sorted = true)
350
     *
351
     * @param array $filter
352
     * @return string
353
     */
354
    protected function constructSearchString($filter)
355
    {
356
        if (!$filter) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filter 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...
357
            return '*';
358
        }
359
        $adldapUtils = new adLDAPUtils($this->initAdLdap(null));
360
        $result = '*';
361
        if (isset($filter['name'])) {
362
            $result .= ')(displayname=*' . $adldapUtils->ldapSlashes($filter['name']) . '*';
363
            unset($filter['name']);
364
        }
365
366
        if (isset($filter['user'])) {
367
            $result .= ')(samAccountName=*' . $adldapUtils->ldapSlashes($filter['user']) . '*';
368
            unset($filter['user']);
369
        }
370
371
        if (isset($filter['mail'])) {
372
            $result .= ')(mail=*' . $adldapUtils->ldapSlashes($filter['mail']) . '*';
373
            unset($filter['mail']);
374
        }
375
        return $result;
376
    }
377
378
    /**
379
     * Return a count of the number of user which meet $filter criteria
380
     *
381
     * @param array $filter  $filter array of field/pattern pairs, empty array for no filter
382
     * @return int number of users
383
     */
384
    public function getUserCount($filter = array())
385
    {
386
        $adldap = $this->initAdLdap(null);
387
        if (!$adldap) {
388
            Logger::debug("authad/auth.php getUserCount(): _adldap not set.");
389
            return -1;
390
        }
391
        if ($filter == array()) {
392
            $result = $adldap->user()->all();
393
        } else {
394
            $searchString = $this->constructSearchString($filter);
395
            $result = $adldap->user()->all(false, $searchString);
396
            if (isset($filter['grps'])) {
397
                $this->users = array_fill_keys($result, false);
398
                /** @var admin_plugin_usermanager $usermanager */
399
                $usermanager = plugin_load("admin", "usermanager", false);
400
                $usermanager->setLastdisabled(true);
401
                if (!isset($this->grpsusers[$this->filterToString($filter)])) {
402
                    $this->fillGroupUserArray($filter, $usermanager->getStart() + 3*$usermanager->getPagesize());
403
                } elseif (count($this->grpsusers[$this->filterToString($filter)]) <
404
                    $usermanager->getStart() + 3*$usermanager->getPagesize()
405
                ) {
406
                    $this->fillGroupUserArray(
407
                        $filter,
408
                        $usermanager->getStart() +
409
                        3*$usermanager->getPagesize() -
410
                        count($this->grpsusers[$this->filterToString($filter)])
411
                    );
412
                }
413
                $result = $this->grpsusers[$this->filterToString($filter)];
414
            } else {
415
                /** @var admin_plugin_usermanager $usermanager */
416
                $usermanager = plugin_load("admin", "usermanager", false);
417
                $usermanager->setLastdisabled(false);
418
            }
419
        }
420
421
        if (!$result) {
422
            return 0;
423
        }
424
        return count($result);
425
    }
426
427
    /**
428
     *
429
     * create a unique string for each filter used with a group
430
     *
431
     * @param array $filter
432
     * @return string
433
     */
434
    protected function filterToString($filter)
435
    {
436
        $result = '';
437
        if (isset($filter['user'])) {
438
            $result .= 'user-' . $filter['user'];
439
        }
440
        if (isset($filter['name'])) {
441
            $result .= 'name-' . $filter['name'];
442
        }
443
        if (isset($filter['mail'])) {
444
            $result .= 'mail-' . $filter['mail'];
445
        }
446
        if (isset($filter['grps'])) {
447
            $result .= 'grps-' . $filter['grps'];
448
        }
449
        return $result;
450
    }
451
452
    /**
453
     * Create an array of $numberOfAdds users passing a certain $filter, including belonging
454
     * to a certain group and save them to a object-wide array. If the array
455
     * already exists try to add $numberOfAdds further users to it.
456
     *
457
     * @param array $filter
458
     * @param int $numberOfAdds additional number of users requested
459
     * @return int number of Users actually add to Array
460
     */
461
    protected function fillGroupUserArray($filter, $numberOfAdds)
462
    {
463
        if (isset($this->grpsusers[$this->filterToString($filter)])) {
464
            $actualstart = count($this->grpsusers[$this->filterToString($filter)]);
465
        } else {
466
            $this->grpsusers[$this->filterToString($filter)] = [];
467
            $actualstart = 0;
468
        }
469
470
        $i=0;
471
        $count = 0;
472
        $this->constructPattern($filter);
473
        foreach ($this->users as $user => &$info) {
474
            if ($i++ < $actualstart) {
475
                continue;
476
            }
477
            if ($info === false) {
478
                $info = $this->getUserData($user);
479
            }
480
            if ($this->filter($user, $info)) {
481
                $this->grpsusers[$this->filterToString($filter)][$user] = $info;
482
                if (($numberOfAdds > 0) && (++$count >= $numberOfAdds)) break;
483
            }
484
        }
485
        return $count;
486
    }
487
488
    /**
489
     * Bulk retrieval of user data
490
     *
491
     * @author  Dominik Eckelmann <[email protected]>
492
     *
493
     * @param   int $start index of first user to be returned
494
     * @param   int $limit max number of users to be returned
495
     * @param   array $filter array of field/pattern pairs, null for no filter
496
     * @return array userinfo (refer getUserData for internal userinfo details)
497
     */
498
    public function retrieveUsers($start = 0, $limit = 0, $filter = array())
499
    {
500
        $adldap = $this->initAdLdap(null);
501
        if (!$adldap) return array();
502
503
        //if (!$this->users) {
504
            //get info for given user
505
            $result = $adldap->user()->all(false, $this->constructSearchString($filter));
506
            if (!$result) return array();
507
            $this->users = array_fill_keys($result, false);
508
        //}
509
510
        $i     = 0;
511
        $count = 0;
512
        $result = array();
513
514
        if (!isset($filter['grps'])) {
515
            /** @var admin_plugin_usermanager $usermanager */
516
            $usermanager = plugin_load("admin", "usermanager", false);
517
            $usermanager->setLastdisabled(false);
518
            $this->constructPattern($filter);
519
            foreach ($this->users as $user => &$info) {
520
                if ($i++ < $start) {
521
                    continue;
522
                }
523
                if ($info === false) {
524
                    $info = $this->getUserData($user);
525
                }
526
                $result[$user] = $info;
527
                if (($limit > 0) && (++$count >= $limit)) break;
528
            }
529
        } else {
530
            /** @var admin_plugin_usermanager $usermanager */
531
            $usermanager = plugin_load("admin", "usermanager", false);
532
            $usermanager->setLastdisabled(true);
533
            if (!isset($this->grpsusers[$this->filterToString($filter)]) ||
534
                count($this->grpsusers[$this->filterToString($filter)]) < ($start+$limit)
535
            ) {
536
                if(!isset($this->grpsusers[$this->filterToString($filter)])) {
537
                    $this->grpsusers[$this->filterToString($filter)] = [];
538
                }
539
540
                $this->fillGroupUserArray(
541
                    $filter,
542
                    $start+$limit - count($this->grpsusers[$this->filterToString($filter)]) +1
543
                );
544
            }
545
            if (!$this->grpsusers[$this->filterToString($filter)]) return array();
546
            foreach ($this->grpsusers[$this->filterToString($filter)] as $user => &$info) {
547
                if ($i++ < $start) {
548
                    continue;
549
                }
550
                $result[$user] = $info;
551
                if (($limit > 0) && (++$count >= $limit)) break;
552
            }
553
        }
554
        return $result;
555
    }
556
557
    /**
558
     * Modify user data
559
     *
560
     * @param   string $user      nick of the user to be changed
561
     * @param   array  $changes   array of field/value pairs to be changed
562
     * @return  bool
563
     */
564
    public function modifyUser($user, $changes)
565
    {
566
        $return = true;
567
        $adldap = $this->initAdLdap($this->getUserDomain($user));
568
        if (!$adldap) {
569
            msg($this->getLang('connectfail'), -1);
570
            return false;
571
        }
572
573
        // password changing
574
        if (isset($changes['pass'])) {
575
            try {
576
                $return = $adldap->user()->password($this->getUserName($user), $changes['pass']);
577
            } catch (adLDAPException $e) {
0 ignored issues
show
The class adLDAPException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
578
                if ($this->conf['debug']) msg('AD Auth: '.$e->getMessage(), -1);
579
                $return = false;
580
            }
581
            if (!$return) msg($this->getLang('passchangefail'), -1);
582
        }
583
584
        // changing user data
585
        $adchanges = array();
586
        if (isset($changes['name'])) {
587
            // get first and last name
588
            $parts                     = explode(' ', $changes['name']);
589
            $adchanges['surname']      = array_pop($parts);
590
            $adchanges['firstname']    = join(' ', $parts);
591
            $adchanges['display_name'] = $changes['name'];
592
        }
593
        if (isset($changes['mail'])) {
594
            $adchanges['email'] = $changes['mail'];
595
        }
596
        if (count($adchanges)) {
597
            try {
598
                $return = $return & $adldap->user()->modify($this->getUserName($user), $adchanges);
599
            } catch (adLDAPException $e) {
0 ignored issues
show
The class adLDAPException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
600
                if ($this->conf['debug']) msg('AD Auth: '.$e->getMessage(), -1);
601
                $return = false;
602
            }
603
            if (!$return) msg($this->getLang('userchangefail'), -1);
604
        }
605
606
        return $return;
607
    }
608
609
    /**
610
     * Initialize the AdLDAP library and connect to the server
611
     *
612
     * When you pass null as domain, it will reuse any existing domain.
613
     * Eg. the one of the logged in user. It falls back to the default
614
     * domain if no current one is available.
615
     *
616
     * @param string|null $domain The AD domain to use
617
     * @return adLDAP|bool true if a connection was established
618
     */
619
    protected function initAdLdap($domain)
620
    {
621
        if (is_null($domain) && is_array($this->opts)) {
622
            $domain = $this->opts['domain'];
623
        }
624
625
        $this->opts = $this->loadServerConfig((string) $domain);
626
        if (isset($this->adldap[$domain])) return $this->adldap[$domain];
627
628
        // connect
629
        try {
630
            $this->adldap[$domain] = new adLDAP($this->opts);
631
            return $this->adldap[$domain];
632
        } catch (Exception $e) {
633
            if ($this->conf['debug']) {
634
                msg('AD Auth: '.$e->getMessage(), -1);
635
            }
636
            $this->success         = false;
637
            $this->adldap[$domain] = null;
638
        }
639
        return false;
640
    }
641
642
    /**
643
     * Get the domain part from a user
644
     *
645
     * @param string $user
646
     * @return string
647
     */
648
    public function getUserDomain($user)
649
    {
650
        list(, $domain) = explode('@', $user, 2);
651
        return $domain;
652
    }
653
654
    /**
655
     * Get the user part from a user
656
     *
657
     * When an account suffix is set, we strip the domain part from the user
658
     *
659
     * @param string $user
660
     * @return string
661
     */
662
    public function getUserName($user)
663
    {
664
        if ($this->conf['account_suffix'] !== '') {
665
            list($user) = explode('@', $user, 2);
666
        }
667
        return $user;
668
    }
669
670
    /**
671
     * Fetch the configuration for the given AD domain
672
     *
673
     * @param string $domain current AD domain
674
     * @return array
675
     */
676
    protected function loadServerConfig($domain)
677
    {
678
        // prepare adLDAP standard configuration
679
        $opts = $this->conf;
680
681
        $opts['domain'] = $domain;
682
683
        // add possible domain specific configuration
684
        if ($domain && is_array($this->conf[$domain])) foreach ($this->conf[$domain] as $key => $val) {
685
            $opts[$key] = $val;
686
        }
687
688
        // handle multiple AD servers
689
        $opts['domain_controllers'] = explode(',', $opts['domain_controllers']);
690
        $opts['domain_controllers'] = array_map('trim', $opts['domain_controllers']);
691
        $opts['domain_controllers'] = array_filter($opts['domain_controllers']);
692
693
        // compatibility with old option name
694
        if (empty($opts['admin_username']) && !empty($opts['ad_username'])) {
695
            $opts['admin_username'] = $opts['ad_username'];
696
        }
697
        if (empty($opts['admin_password']) && !empty($opts['ad_password'])) {
698
            $opts['admin_password'] = $opts['ad_password'];
699
        }
700
        $opts['admin_password'] = conf_decodeString($opts['admin_password']); // deobfuscate
701
702
        // we can change the password if SSL is set
703
        if ($opts['use_ssl'] || $opts['use_tls']) {
704
            $this->cando['modPass'] = true;
705
        } else {
706
            $this->cando['modPass'] = false;
707
        }
708
709
        // adLDAP expects empty user/pass as NULL, we're less strict FS#2781
710
        if (empty($opts['admin_username'])) $opts['admin_username'] = null;
711
        if (empty($opts['admin_password'])) $opts['admin_password'] = null;
712
713
        // user listing needs admin priviledges
714
        if (!empty($opts['admin_username']) && !empty($opts['admin_password'])) {
715
            $this->cando['getUsers'] = true;
716
        } else {
717
            $this->cando['getUsers'] = false;
718
        }
719
720
        return $opts;
721
    }
722
723
    /**
724
     * Returns a list of configured domains
725
     *
726
     * The default domain has an empty string as key
727
     *
728
     * @return array associative array(key => domain)
729
     */
730
    public function getConfiguredDomains()
731
    {
732
        $domains = array();
733
        if (empty($this->conf['account_suffix'])) return $domains; // not configured yet
734
735
        // add default domain, using the name from account suffix
736
        $domains[''] = ltrim($this->conf['account_suffix'], '@');
737
738
        // find additional domains
739
        foreach ($this->conf as $key => $val) {
740
            if (is_array($val) && isset($val['account_suffix'])) {
741
                $domains[$key] = ltrim($val['account_suffix'], '@');
742
            }
743
        }
744
        Sort::ksort($domains);
745
746
        return $domains;
747
    }
748
749
    /**
750
     * Check provided user and userinfo for matching patterns
751
     *
752
     * The patterns are set up with $this->_constructPattern()
753
     *
754
     * @author Chris Smith <[email protected]>
755
     *
756
     * @param string $user
757
     * @param array  $info
758
     * @return bool
759
     */
760
    protected function filter($user, $info)
761
    {
762
        foreach ($this->pattern as $item => $pattern) {
763
            if ($item == 'user') {
764
                if (!preg_match($pattern, $user)) return false;
765
            } elseif ($item == 'grps') {
766
                if (!count(preg_grep($pattern, $info['grps']))) return false;
767
            } else {
768
                if (!preg_match($pattern, $info[$item])) return false;
769
            }
770
        }
771
        return true;
772
    }
773
774
    /**
775
     * Create a pattern for $this->_filter()
776
     *
777
     * @author Chris Smith <[email protected]>
778
     *
779
     * @param array $filter
780
     */
781
    protected function constructPattern($filter)
782
    {
783
        $this->pattern = array();
784
        foreach ($filter as $item => $pattern) {
785
            $this->pattern[$item] = '/'.str_replace('/', '\/', $pattern).'/i'; // allow regex characters
786
        }
787
    }
788
}
789